CGIT_CONFIG = /etc/cgitrc
CACHE_ROOT = /var/cache/cgit
SHA1_HEADER = <openssl/sha.h>
-GIT_VER = 1.6.4.3
+GIT_VER = 1.7.2.2
GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2
INSTALL = install
# Define NO_STRCASESTR if you don't have strcasestr.
#
+# Define NO_OPENSSL to disable linking with OpenSSL and use bundled SHA1
+# implementation (slower).
+#
# Define NEEDS_LIBICONV if linking with libc is not enough (eg. Darwin).
#
$(QUIET_CC)$(CC) -o $*.o -c $(CFLAGS) $<
-EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
+EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lpthread
OBJECTS =
OBJECTS += cache.o
OBJECTS += cgit.o
OBJECTS += ui-repolist.o
OBJECTS += ui-shared.o
OBJECTS += ui-snapshot.o
+OBJECTS += ui-ssdiff.o
OBJECTS += ui-stats.o
OBJECTS += ui-summary.o
OBJECTS += ui-tag.o
ifdef NO_STRCASESTR
CFLAGS += -DNO_STRCASESTR
endif
+ifdef NO_OPENSSL
+ CFLAGS += -DNO_OPENSSL
+ GIT_OPTIONS += NO_OPENSSL=1
+else
+ EXTLIBS += -lcrypto
+endif
cgit: $(OBJECTS) libgit
$(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o cgit $(OBJECTS) $(EXTLIBS)
cgit.o: VERSION
--include $(OBJECTS:.o=.d)
+ifneq "$(MAKECMDGOALS)" "clean"
+ -include $(OBJECTS:.o=.d)
+endif
libgit:
- $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 libgit.a
- $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 xdiff/lib.a
+ $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 $(GIT_OPTIONS) libgit.a
+ $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 $(GIT_OPTIONS) xdiff/lib.a
test: all
$(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all
/* cgit.c: cgi for the git scm
*
* Copyright (C) 2006 Lars Hjemli
+ * Copyright (C) 2010 Jason A. Donenfeld <Jason@zx2c4.com>
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
{
struct string_list_item *item;
- item = string_list_insert(xstrdup(name), &ctx.cfg.mimetypes);
+ item = string_list_insert(&ctx.cfg.mimetypes, xstrdup(name));
item->util = xstrdup(value);
}
repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
else if (!strcmp(name, "enable-log-linecount"))
repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
+ else if (!strcmp(name, "enable-remote-branches"))
+ repo->enable_remote_branches = atoi(value);
+ else if (!strcmp(name, "enable-subject-links"))
+ repo->enable_subject_links = atoi(value);
else if (!strcmp(name, "max-stats"))
repo->max_stats = cgit_find_stats_period(value, NULL);
else if (!strcmp(name, "module-link"))
else if (!strcmp(name, "section"))
repo->section = xstrdup(value);
else if (!strcmp(name, "readme") && value != NULL) {
- if (*value == '/')
+ char *colon;
+ if (*value == '/' || ((colon = strchr(value, ':')) != NULL && colon != value && *(colon + 1) != '\0'))
repo->readme = xstrdup(value);
else
repo->readme = xstrdup(fmt("%s/%s", repo->path, value));
ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
else if (!strcmp(name, "enable-filter-overrides"))
ctx.cfg.enable_filter_overrides = atoi(value);
+ else if (!strcmp(name, "enable-gitweb-owner"))
+ ctx.cfg.enable_gitweb_owner = atoi(value);
else if (!strcmp(name, "enable-index-links"))
ctx.cfg.enable_index_links = atoi(value);
else if (!strcmp(name, "enable-log-filecount"))
ctx.cfg.enable_log_filecount = atoi(value);
else if (!strcmp(name, "enable-log-linecount"))
ctx.cfg.enable_log_linecount = atoi(value);
+ else if (!strcmp(name, "enable-remote-branches"))
+ ctx.cfg.enable_remote_branches = atoi(value);
+ else if (!strcmp(name, "enable-subject-links"))
+ ctx.cfg.enable_subject_links = atoi(value);
else if (!strcmp(name, "enable-tree-linenumbers"))
ctx.cfg.enable_tree_linenumbers = atoi(value);
else if (!strcmp(name, "max-stats"))
else if (!strcmp(name, "cache-size"))
ctx.cfg.cache_size = atoi(value);
else if (!strcmp(name, "cache-root"))
- ctx.cfg.cache_root = xstrdup(value);
+ ctx.cfg.cache_root = xstrdup(expand_macros(value));
else if (!strcmp(name, "cache-root-ttl"))
ctx.cfg.cache_root_ttl = atoi(value);
else if (!strcmp(name, "cache-repo-ttl"))
ctx.cfg.commit_filter = new_filter(value, 0);
else if (!strcmp(name, "embedded"))
ctx.cfg.embedded = atoi(value);
+ else if (!strcmp(name, "max-atom-items"))
+ ctx.cfg.max_atom_items = atoi(value);
else if (!strcmp(name, "max-message-length"))
ctx.cfg.max_msg_len = atoi(value);
else if (!strcmp(name, "max-repodesc-length"))
ctx.cfg.max_repodesc_len = atoi(value);
+ else if (!strcmp(name, "max-blob-size"))
+ ctx.cfg.max_blob_size = atoi(value);
else if (!strcmp(name, "max-repo-count"))
ctx.cfg.max_repo_count = atoi(value);
else if (!strcmp(name, "max-commit-count"))
ctx.cfg.max_commit_count = atoi(value);
+ else if (!strcmp(name, "project-list"))
+ ctx.cfg.project_list = xstrdup(expand_macros(value));
else if (!strcmp(name, "scan-path"))
if (!ctx.cfg.nocache && ctx.cfg.cache_size)
- process_cached_repolist(value);
+ process_cached_repolist(expand_macros(value));
+ else if (ctx.cfg.project_list)
+ scan_projects(expand_macros(value),
+ ctx.cfg.project_list, repo_config);
else
- scan_tree(value, repo_config);
+ scan_tree(expand_macros(value), repo_config);
else if (!strcmp(name, "source-filter"))
ctx.cfg.source_filter = new_filter(value, 1);
else if (!strcmp(name, "summary-log"))
ctx.cfg.summary_branches = atoi(value);
else if (!strcmp(name, "summary-tags"))
ctx.cfg.summary_tags = atoi(value);
+ else if (!strcmp(name, "side-by-side-diffs"))
+ ctx.cfg.ssdiff = atoi(value);
else if (!strcmp(name, "agefile"))
ctx.cfg.agefile = xstrdup(value);
else if (!strcmp(name, "renamelimit"))
ctx.cfg.renamelimit = atoi(value);
+ else if (!strcmp(name, "remove-suffix"))
+ ctx.cfg.remove_suffix = atoi(value);
else if (!strcmp(name, "robots"))
ctx.cfg.robots = xstrdup(value);
else if (!strcmp(name, "clone-prefix"))
else if (!prefixcmp(name, "mimetype."))
add_mimetype(name + 9, value);
else if (!strcmp(name, "include"))
- parse_configfile(value, config_cb);
+ parse_configfile(expand_macros(value), config_cb);
}
static void querystring_cb(const char *name, const char *value)
} else if (!strcmp(name, "p")) {
ctx.qry.page = xstrdup(value);
} else if (!strcmp(name, "url")) {
+ if (*value == '/')
+ value++;
ctx.qry.url = xstrdup(value);
cgit_parse_url(value);
} else if (!strcmp(name, "qt")) {
ctx.qry.showmsg = atoi(value);
} else if (!strcmp(name, "period")) {
ctx.qry.period = xstrdup(value);
+ } else if (!strcmp(name, "ss")) {
+ ctx.qry.ssdiff = atoi(value);
+ } else if (!strcmp(name, "all")) {
+ ctx.qry.show_all = atoi(value);
+ } else if (!strcmp(name, "context")) {
+ ctx.qry.context = atoi(value);
+ } else if (!strcmp(name, "ignorews")) {
+ ctx.qry.ignorews = atoi(value);
}
}
ctx->cfg.css = "/cgit.css";
ctx->cfg.logo = "/cgit.png";
ctx->cfg.local_time = 0;
+ ctx->cfg.enable_gitweb_owner = 1;
ctx->cfg.enable_tree_linenumbers = 1;
ctx->cfg.max_repo_count = 50;
ctx->cfg.max_commit_count = 50;
ctx->cfg.max_lock_attempts = 5;
ctx->cfg.max_msg_len = 80;
ctx->cfg.max_repodesc_len = 80;
+ ctx->cfg.max_blob_size = 0;
ctx->cfg.max_stats = 0;
ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
+ ctx->cfg.project_list = NULL;
ctx->cfg.renamelimit = -1;
+ ctx->cfg.remove_suffix = 0;
ctx->cfg.robots = "index, nofollow";
ctx->cfg.root_title = "Git repository browser";
ctx->cfg.root_desc = "a fast webinterface for the git dscm";
ctx->cfg.summary_branches = 10;
ctx->cfg.summary_log = 10;
ctx->cfg.summary_tags = 10;
+ ctx->cfg.max_atom_items = 10;
+ ctx->cfg.ssdiff = 0;
ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
ctx->env.https = xstrdupn(getenv("HTTPS"));
return;
}
+ /* If cmd->want_vpath is set, assume ctx->qry.path contains a "virtual"
+ * in-project path limit to be made available at ctx->qry.vpath.
+ * Otherwise, no path limit is in effect (ctx->qry.vpath = NULL).
+ */
+ ctx->qry.vpath = cmd->want_vpath ? ctx->qry.path : NULL;
+
if (cmd->want_repo && !ctx->repo) {
cgit_print_http_headers(ctx);
cgit_print_docstart(ctx);
return errno;
}
idx = cgit_repolist.count;
- scan_tree(path, repo_config);
+ if (ctx.cfg.project_list)
+ scan_projects(path, ctx.cfg.project_list, repo_config);
+ else
+ scan_tree(path, repo_config);
print_repolist(f, &cgit_repolist, idx);
if (rename(locked_rc, cached_rc))
fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n",
struct stat st;
char *cached_rc;
time_t age;
+ unsigned long hash;
- cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root,
- hash_str(path)));
+ hash = hash_str(path);
+ if (ctx.cfg.project_list)
+ hash += hash_str(ctx.cfg.project_list);
+ cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root, hash));
if (stat(cached_rc, &st)) {
/* Nothing is cached, we need to scan without forking. And
* if we fail to generate a cached repolist, we need to
* invoke scan_tree manually.
*/
- if (generate_cached_repolist(path, cached_rc))
- scan_tree(path, repo_config);
+ if (generate_cached_repolist(path, cached_rc)) {
+ if (ctx.cfg.project_list)
+ scan_projects(path, ctx.cfg.project_list,
+ repo_config);
+ else
+ scan_tree(path, repo_config);
+ }
return;
}
cgit_repolist.repos = NULL;
cgit_parse_args(argc, argv);
- parse_configfile(ctx.env.cgit_config, config_cb);
+ parse_configfile(expand_macros(ctx.env.cgit_config), config_cb);
ctx.repo = NULL;
http_parse_querystring(ctx.qry.raw, querystring_cb);
}
table.tabs {
- /* border-bottom: solid 2px #ccc; */
+ border-bottom: solid 3px #ccc;
border-collapse: collapse;
margin-top: 2em;
margin-bottom: 0px;
font-size: 90%;
}
+div.path {
+ margin: 0px;
+ padding: 5px 2em 2px 2em;
+ color: #000;
+ background-color: #eee;
+}
+
div.content {
margin: 0px;
padding: 2em;
- border-top: solid 3px #ccc;
border-bottom: solid 3px #ccc;
}
padding: 1em 0.5em 2em 0.5em;
}
+table.list td.lognotes-label {
+ text-align:right;
+ vertical-align:top;
+}
+
+table.list td.lognotes {
+ font-family: monospace;
+ white-space: pre;
+ padding: 0em 0.5em 2em 0.5em;
+}
+
table.list td a {
color: black;
}
+table.list td a.ls-dir {
+ font-weight: bold;
+ color: #00f;
+}
+
table.list td a:hover {
color: #00f;
}
font-family: monospace;
}
+div.notes-header {
+ font-weight: bold;
+ padding-top: 1.5em;
+}
+
+div.notes {
+ white-space: pre;
+ font-family: monospace;
+ border: solid 1px #ee9;
+ background-color: #ffd;
+ padding: 0.3em 2em 0.3em 1em;
+ float: left;
+}
+
+div.notes-footer {
+ clear: left;
+}
+
div.diffstat-header {
font-weight: bold;
padding-top: 1.5em;
border: solid 1px #770000;
}
-div.commit-subject a {
+div.commit-subject a.branch-deco,
+div.commit-subject a.tag-deco,
+div.commit-subject a.remote-deco,
+div.commit-subject a.deco {
margin-left: 1em;
font-size: 75%;
}
background-color: #eee;
height: 1em;
}
+
+table.ssdiff {
+ width: 100%;
+}
+
+table.ssdiff td {
+ font-size: 75%;
+ font-family: monospace;
+ white-space: pre;
+ padding: 1px 4px 1px 4px;
+ border-left: solid 1px #aaa;
+ border-right: solid 1px #aaa;
+}
+
+table.ssdiff td.add {
+ color: black;
+ background: #cfc;
+ min-width: 50%;
+}
+
+table.ssdiff td.add_dark {
+ color: black;
+ background: #aca;
+ min-width: 50%;
+}
+
+table.ssdiff span.add {
+ background: #cfc;
+ font-weight: bold;
+}
+
+table.ssdiff td.del {
+ color: black;
+ background: #fcc;
+ min-width: 50%;
+}
+
+table.ssdiff td.del_dark {
+ color: black;
+ background: #caa;
+ min-width: 50%;
+}
+
+table.ssdiff span.del {
+ background: #fcc;
+ font-weight: bold;
+}
+
+table.ssdiff td.changed {
+ color: black;
+ background: #ffc;
+ min-width: 50%;
+}
+
+table.ssdiff td.changed_dark {
+ color: black;
+ background: #cca;
+ min-width: 50%;
+}
+
+table.ssdiff td.lineno {
+ color: black;
+ background: #eee;
+ text-align: right;
+ width: 3em;
+ min-width: 3em;
+}
+
+table.ssdiff td.hunk {
+ color: #black;
+ background: #ccf;
+ border-top: solid 1px #aaa;
+ border-bottom: solid 1px #aaa;
+}
+
+table.ssdiff td.head {
+ border-top: solid 1px #aaa;
+ border-bottom: solid 1px #aaa;
+}
+
+table.ssdiff td.head div.head {
+ font-weight: bold;
+ color: black;
+}
+
+table.ssdiff td.foot {
+ border-top: solid 1px #aaa;
+ border-left: none;
+ border-right: none;
+ border-bottom: none;
+}
+
+table.ssdiff td.space {
+ border: none;
+}
+
+table.ssdiff td.space div {
+ min-height: 3em;
+}
\ No newline at end of file
#include <xdiff-interface.h>
#include <xdiff/xdiff.h>
#include <utf8.h>
+#include <notes.h>
/*
int snapshots;
int enable_log_filecount;
int enable_log_linecount;
+ int enable_remote_branches;
+ int enable_subject_links;
int max_stats;
time_t mtime;
struct cgit_filter *about_filter;
int nohead;
char *sort;
int showmsg;
+ int ssdiff;
+ int show_all;
+ int context;
+ int ignorews;
+ char *vpath;
};
struct cgit_config {
char *logo;
char *logo_link;
char *module_link;
+ char *project_list;
char *robots;
char *root_title;
char *root_desc;
int cache_static_ttl;
int embedded;
int enable_filter_overrides;
+ int enable_gitweb_owner;
int enable_index_links;
int enable_log_filecount;
int enable_log_linecount;
+ int enable_remote_branches;
+ int enable_subject_links;
int enable_tree_linenumbers;
int local_time;
+ int max_atom_items;
int max_repo_count;
int max_commit_count;
int max_lock_attempts;
int max_msg_len;
int max_repodesc_len;
+ int max_blob_size;
int max_stats;
int nocache;
int noplainemail;
int noheader;
int renamelimit;
+ int remove_suffix;
int snapshots;
int summary_branches;
int summary_log;
int summary_tags;
+ int ssdiff;
struct string_list mimetypes;
struct cgit_filter *about_filter;
struct cgit_filter *commit_filter;
extern int cgit_diff_files(const unsigned char *old_sha1,
const unsigned char *new_sha1,
unsigned long *old_size, unsigned long *new_size,
- int *binary, linediff_fn fn);
+ int *binary, int context, int ignorews,
+ linediff_fn fn);
extern void cgit_diff_tree(const unsigned char *old_sha1,
const unsigned char *new_sha1,
- filepair_fn fn, const char *prefix);
+ filepair_fn fn, const char *prefix, int ignorews);
extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
extern int readfile(const char *path, char **buf, size_t *size);
+extern char *expand_macros(const char *txt);
+
#endif /* CGIT_H */
Flag which, when set to "1", allows all filter settings to be
overridden in repository-specific cgitrc files. Default value: none.
+enable-gitweb-owner::
+ If set to "1" and scan-path is enabled, we first check each repository
+ for the git config value "gitweb.owner" to determine the owner.
+ Default value: "1". See also: scan-path.
+
enable-index-links::
Flag which, when set to "1", will make cgit generate extra links for
each repo in the repository index (specifically, to the "summary",
and removed lines for each commit on the repository log page. Default
value: "0".
+enable-remote-branches::
+ Flag which, when set to "1", will make cgit display remote branches
+ in the summary and refs views. Default value: "0". See also:
+ "repo.enable-remote-branches".
+
+enable-subject-links::
+ Flag which, when set to "1", will make cgit use the subject of the
+ parent commit as link text when generating links to parent commits
+ in commit view. Default value: "0". See also:
+ "repo.enable-subject-links".
+
enable-tree-linenumbers::
Flag which, when set to "1", will make cgit generate linenumber links
for plaintext blobs printed in the tree view. Default value: "1".
calculated url of the repository index page will be used. Default
value: none.
+max-atom-items::
+ Specifies the number of items to display in atom feeds view. Default
+ value: "10".
+
max-commit-count::
Specifies the number of entries to list per page in "log" view. Default
value: "50".
Specifies the maximum number of repo description characters to display
on the repository index page. Default value: "80".
+max-blob-size::
+ Specifies the maximum size of a blob to display HTML for in KBytes.
+ Default value: "0" (limit disabled).
+
max-stats::
Set the default maximum statistics period. Valid values are "week",
"month", "quarter" and "year". If unspecified, statistics are
Flag which, when set to "1", will make cgit omit the standard header
on all pages. Default value: none. See also: "embedded".
+project-list::
+ A list of subdirectories inside of scan-path, relative to it, that
+ should loaded as git repositories. This must be defined prior to
+ scan-path. Default value: none. See also: scan-path.
+
+remove-suffix::
+ If set to "1" and scan-path is enabled, if any repositories are found
+ with a suffix of ".git", this suffix will be removed for the url and
+ name. Default value: "0". See also: scan-path.
+
renamelimit::
Maximum number of files to consider when detecting renames. The value
"-1" uses the compiletime value in git (for further info, look at
scan-path::
A path which will be scanned for repositories. If caching is enabled,
the result will be cached as a cgitrc include-file in the cache
- directory. Default value: none. See also: cache-scanrc-ttl.
+ directory. If project-list has been defined prior to scan-path,
+ scan-path loads only the directories listed in the file pointed to by
+ project-list. Default value: none. See also: cache-scanrc-ttl,
+ project-list.
section::
The name of the current repository section - all repositories defined
after this option will inherit the current section name. Default value:
none.
+side-by-side-diffs::
+ If set to "1" shows side-by-side diffs instead of unidiffs per
+ default. Default value: "0".
+
snapshots::
Text which specifies the default set of snapshot formats generated by
cgit. The value is a space-separated list of zero or more of the
A flag which can be used to disable the global setting
`enable-log-linecount'. Default value: none.
+repo.enable-remote-branches::
+ Flag which, when set to "1", will make cgit display remote branches
+ in the summary and refs views. Default value: <enable-remote-branches>.
+
+repo.enable-subject-links::
+ A flag which can be used to override the global setting
+ `enable-subject-links'. Default value: none.
+
repo.max-stats::
Override the default maximum statistics period. Valid values are equal
to the values specified for the global "max-stats" setting. Default
repo.readme::
A path (relative to <repo.path>) which specifies a file to include
- verbatim as the "About" page for this repo. Default value: none.
+ verbatim as the "About" page for this repo. You may also specify a
+ git refspec by head or by hash by prepending the refspec followed by
+ a colon. For example, "master:docs/readme.mkd" Default value: none.
repo.snapshots::
A mask of allowed snapshot-formats for this repo, restricted by the
## List of common mimetypes
##
-mimetype.git=image/git
+mimetype.gif=image/gif
mimetype.html=text/html
mimetype.jpg=image/jpeg
mimetype.jpeg=image/jpeg
AUTHOR
------
Lars Hjemli <hjemli@gmail.com>
+Jason A. Donenfeld <Jason@zx2c4.com>
static void atom_fn(struct cgit_context *ctx)
{
- cgit_print_atom(ctx->qry.head, ctx->qry.path, 10);
+ cgit_print_atom(ctx->qry.head, ctx->qry.path, ctx->cfg.max_atom_items);
}
static void about_fn(struct cgit_context *ctx)
static void commit_fn(struct cgit_context *ctx)
{
- cgit_print_commit(ctx->qry.sha1);
+ cgit_print_commit(ctx->qry.sha1, ctx->qry.path);
}
static void diff_fn(struct cgit_context *ctx)
static void patch_fn(struct cgit_context *ctx)
{
- cgit_print_patch(ctx->qry.sha1);
+ cgit_print_patch(ctx->qry.sha1, ctx->qry.path);
}
static void plain_fn(struct cgit_context *ctx)
cgit_print_tree(ctx->qry.sha1, ctx->qry.path);
}
-#define def_cmd(name, want_repo, want_layout) \
- {#name, name##_fn, want_repo, want_layout}
+#define def_cmd(name, want_repo, want_layout, want_vpath) \
+ {#name, name##_fn, want_repo, want_layout, want_vpath}
struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx)
{
static struct cgit_cmd cmds[] = {
- def_cmd(HEAD, 1, 0),
- def_cmd(atom, 1, 0),
- def_cmd(about, 0, 1),
- def_cmd(blob, 1, 0),
- def_cmd(commit, 1, 1),
- def_cmd(diff, 1, 1),
- def_cmd(info, 1, 0),
- def_cmd(log, 1, 1),
- def_cmd(ls_cache, 0, 0),
- def_cmd(objects, 1, 0),
- def_cmd(patch, 1, 0),
- def_cmd(plain, 1, 0),
- def_cmd(refs, 1, 1),
- def_cmd(repolist, 0, 0),
- def_cmd(snapshot, 1, 0),
- def_cmd(stats, 1, 1),
- def_cmd(summary, 1, 1),
- def_cmd(tag, 1, 1),
- def_cmd(tree, 1, 1),
+ def_cmd(HEAD, 1, 0, 0),
+ def_cmd(atom, 1, 0, 0),
+ def_cmd(about, 0, 1, 0),
+ def_cmd(blob, 1, 0, 0),
+ def_cmd(commit, 1, 1, 1),
+ def_cmd(diff, 1, 1, 1),
+ def_cmd(info, 1, 0, 0),
+ def_cmd(log, 1, 1, 1),
+ def_cmd(ls_cache, 0, 0, 0),
+ def_cmd(objects, 1, 0, 0),
+ def_cmd(patch, 1, 0, 1),
+ def_cmd(plain, 1, 0, 0),
+ def_cmd(refs, 1, 1, 0),
+ def_cmd(repolist, 0, 0, 0),
+ def_cmd(snapshot, 1, 0, 0),
+ def_cmd(stats, 1, 1, 1),
+ def_cmd(summary, 1, 1, 0),
+ def_cmd(tag, 1, 1, 0),
+ def_cmd(tree, 1, 1, 1),
};
int i;
const char *name;
cgit_cmd_fn fn;
unsigned int want_repo:1,
- want_layout:1;
+ want_layout:1,
+ want_vpath:1;
};
extern struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx);
#!/bin/sh
-# This script can be used to generate links in commit messages - the first
-# sed expression generates links to commits referenced by their SHA1, while
-# the second expression generates links to a fictional bugtracker.
+# This script can be used to generate links in commit messages.
#
# To use this script, refer to this file with either the commit-filter or the
# repo.commit-filter options in cgitrc.
-sed -re '
-s|\b([0-9a-fA-F]{8,40})\b|<a href="./?id=\1">\1</a>|g
-s| #([0-9]+)\b|<a href="http://bugs.example.com/?bug=\1">#\1</a>|g
-'
+# This expression generates links to commits referenced by their SHA1.
+regex=$regex'
+s|\b([0-9a-fA-F]{8,40})\b|<a href="./?id=\1">\1</a>|g'
+# This expression generates links to a fictional bugtracker.
+regex=$regex'
+s| #([0-9]+)\b|<a href="http://bugs.example.com/?bug=\1">#\1</a>|g'
+
+sed -re "$regex"
# tree-view by refering to this file with the source-filter or repo.source-
# filter options in cgitrc.
#
+# This script requires a shell supporting the ${var##pattern} syntax.
+# It is supported by at least dash and bash, however busybox environments
+# might have to use an external call to sed instead.
+#
# Note: the highlight command (http://www.andre-simon.de/) uses css for syntax
# highlighting, so you'll probably want something like the following included
# in your css file (generated by highlight 2.4.8 and adapted for cgit):
# table.blob .kwc { color:#000000; font-weight:bold; }
# table.blob .kwd { color:#010181; }
-case "$1" in
- *.c)
- highlight -f -I -X -S c
- ;;
- *.h)
- highlight -f -I -X -S c
- ;;
- *.sh)
- highlight -f -I -X -S sh
- ;;
- *.css)
- highlight -f -I -X -S css
- ;;
- *)
- highlight -f -I -X -S txt
- ;;
-esac
+# store filename and extension in local vars
+BASENAME="$1"
+EXTENSION="${BASENAME##*.}"
+
+# map Makefile and Makefile.* to .mk
+[ "${BASENAME%%.*}" == "Makefile" ] && EXTENSION=mk
+
+exec highlight --force -f -I -X -S $EXTENSION 2>/dev/null
-Subproject commit 7fb6bcff2dece2ff9fbc5ebfe526d9b2a7e764c4
+Subproject commit 8c67c392e1620fc3b749aa9e0b8da13bd84226f8
#include <string.h>
#include <errno.h>
+/* Percent-encoding of each character, except: a-zA-Z0-9!$()*,./:;@- */
+static const char* url_escape_table[256] = {
+ "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", "%08", "%09",
+ "%0a", "%0b", "%0c", "%0d", "%0e", "%0f", "%10", "%11", "%12", "%13",
+ "%14", "%15", "%16", "%17", "%18", "%19", "%1a", "%1b", "%1c", "%1d",
+ "%1e", "%1f", "%20", 0, "%22", "%23", 0, "%25", "%26", "%27", 0, 0, 0,
+ "%2b", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "%3c", "%3d",
+ "%3e", "%3f", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, "%5c", 0, "%5e", 0, "%60", 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "%7b",
+ "%7c", "%7d", 0, "%7f", "%80", "%81", "%82", "%83", "%84", "%85",
+ "%86", "%87", "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f",
+ "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97", "%98", "%99",
+ "%9a", "%9b", "%9c", "%9d", "%9e", "%9f", "%a0", "%a1", "%a2", "%a3",
+ "%a4", "%a5", "%a6", "%a7", "%a8", "%a9", "%aa", "%ab", "%ac", "%ad",
+ "%ae", "%af", "%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7",
+ "%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf", "%c0", "%c1",
+ "%c2", "%c3", "%c4", "%c5", "%c6", "%c7", "%c8", "%c9", "%ca", "%cb",
+ "%cc", "%cd", "%ce", "%cf", "%d0", "%d1", "%d2", "%d3", "%d4", "%d5",
+ "%d6", "%d7", "%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df",
+ "%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7", "%e8", "%e9",
+ "%ea", "%eb", "%ec", "%ed", "%ee", "%ef", "%f0", "%f1", "%f2", "%f3",
+ "%f4", "%f5", "%f6", "%f7", "%f8", "%f9", "%fa", "%fb", "%fc", "%fd",
+ "%fe", "%ff"
+};
+
int htmlfd = STDOUT_FILENO;
char *fmt(const char *format, ...)
html("\n");
}
-void html_txt(char *txt)
+void html_txt(const char *txt)
{
- char *t = txt;
+ const char *t = txt;
while(t && *t){
int c = *t;
if (c=='<' || c=='>' || c=='&') {
html(txt);
}
-void html_ntxt(int len, char *txt)
+void html_ntxt(int len, const char *txt)
{
- char *t = txt;
+ const char *t = txt;
while(t && *t && len--){
int c = *t;
if (c=='<' || c=='>' || c=='&') {
html("...");
}
-void html_attr(char *txt)
+void html_attr(const char *txt)
{
- char *t = txt;
+ const char *t = txt;
while(t && *t){
int c = *t;
if (c=='<' || c=='>' || c=='\'' || c=='\"') {
html(txt);
}
-void html_url_path(char *txt)
+void html_url_path(const char *txt)
{
- char *t = txt;
+ const char *t = txt;
while(t && *t){
int c = *t;
- if (c=='"' || c=='#' || c=='\'' || c=='?') {
+ const char *e = url_escape_table[c];
+ if (e && c!='+' && c!='&' && c!='+') {
write(htmlfd, txt, t - txt);
- write(htmlfd, fmt("%%%2x", c), 3);
+ write(htmlfd, e, 3);
txt = t+1;
}
t++;
html(txt);
}
-void html_url_arg(char *txt)
+void html_url_arg(const char *txt)
{
- char *t = txt;
+ const char *t = txt;
while(t && *t){
int c = *t;
- if (c=='"' || c=='#' || c=='%' || c=='&' || c=='\'' || c=='+' || c=='?') {
+ const char *e = url_escape_table[c];
+ if (e) {
write(htmlfd, txt, t - txt);
- write(htmlfd, fmt("%%%2x", c), 3);
+ write(htmlfd, e, 3);
txt = t+1;
}
t++;
html(txt);
}
-void html_hidden(char *name, char *value)
+void html_hidden(const char *name, const char *value)
{
html("<input type='hidden' name='");
html_attr(name);
html("'/>");
}
-void html_option(char *value, char *text, char *selected_value)
+void html_option(const char *value, const char *text, const char *selected_value)
{
html("<option value='");
html_attr(value);
html("</option>\n");
}
-void html_link_open(char *url, char *title, char *class)
+void html_link_open(const char *url, const char *title, const char *class)
{
html("<a href='");
html_attr(url);
}
}
-int http_parse_querystring(char *txt, void (*fn)(const char *name, const char *value))
+int http_parse_querystring(const char *txt_, void (*fn)(const char *name, const char *value))
{
- char *t, *value = NULL, c;
+ char *t, *txt, *value = NULL, c;
- if (!txt)
+ if (!txt_)
return 0;
- t = txt = strdup(txt);
+ t = txt = strdup(txt_);
if (t == NULL) {
printf("Out of memory\n");
exit(1);
extern void html(const char *txt);
extern void htmlf(const char *format,...);
extern void html_status(int code, const char *msg, int more_headers);
-extern void html_txt(char *txt);
-extern void html_ntxt(int len, char *txt);
-extern void html_attr(char *txt);
-extern void html_url_path(char *txt);
-extern void html_url_arg(char *txt);
-extern void html_hidden(char *name, char *value);
-extern void html_option(char *value, char *text, char *selected_value);
-extern void html_link_open(char *url, char *title, char *class);
+extern void html_txt(const char *txt);
+extern void html_ntxt(int len, const char *txt);
+extern void html_attr(const char *txt);
+extern void html_url_path(const char *txt);
+extern void html_url_arg(const char *txt);
+extern void html_hidden(const char *name, const char *value);
+extern void html_option(const char *value, const char *text, const char *selected_value);
+extern void html_link_open(const char *url, const char *title, const char *class);
extern void html_link_close(void);
extern void html_fileperm(unsigned short mode);
extern int html_include(const char *filename);
-extern int http_parse_querystring(char *txt, void (*fn)(const char *name, const char *value));
+extern int http_parse_querystring(const char *txt, void (*fn)(const char *name, const char *value));
#endif /* HTML_H */
+/* scan-tree.c
+ *
+ * Copyright (C) 2008-2009 Lars Hjemli
+ * Copyright (C) 2010 Jason A. Donenfeld <Jason@zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ * (see COPYING for full license text)
+ */
+
#include "cgit.h"
#include "configfile.h"
#include "html.h"
struct cgit_repo *repo;
repo_config_fn config_fn;
+char *owner;
static void repo_config(const char *name, const char *value)
{
config_fn(repo, name, value);
}
+static int git_owner_config(const char *key, const char *value, void *cb)
+{
+ if (!strcmp(key, "gitweb.owner"))
+ owner = xstrdup(value);
+ return 0;
+}
+
static void add_repo(const char *base, const char *path, repo_config_fn fn)
{
struct stat st;
path, strerror(errno), errno);
return;
}
- if ((pwd = getpwuid(st.st_uid)) == NULL) {
- fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
- path, strerror(errno), errno);
+ if (!stat(fmt("%s/noweb", path), &st))
return;
- }
+
+ owner = NULL;
+ if (ctx.cfg.enable_gitweb_owner)
+ git_config_from_file(git_owner_config, fmt("%s/config", path), NULL);
if (base == path)
p = fmt("%s", path);
else
p[strlen(p) - 5] = '\0';
repo = cgit_add_repo(xstrdup(p));
+ if (ctx.cfg.remove_suffix)
+ if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git"))
+ *p = '\0';
repo->name = repo->url;
repo->path = xstrdup(path);
- p = (pwd && pwd->pw_gecos) ? strchr(pwd->pw_gecos, ',') : NULL;
- if (p)
- *p = '\0';
- repo->owner = (pwd ? xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name) : "");
+ while (!owner) {
+ if ((pwd = getpwuid(st.st_uid)) == NULL) {
+ fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
+ path, strerror(errno), errno);
+ break;
+ }
+ if (pwd->pw_gecos)
+ if ((p = strchr(pwd->pw_gecos, ',')))
+ *p = '\0';
+ owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name);
+ }
+ repo->owner = owner;
p = fmt("%s/description", path);
if (!stat(p, &st))
closedir(dir);
}
+#define lastc(s) s[strlen(s) - 1]
+
+void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn)
+{
+ char line[MAX_PATH * 2], *z;
+ FILE *projects;
+ int err;
+
+ projects = fopen(projectsfile, "r");
+ if (!projects) {
+ fprintf(stderr, "Error opening projectsfile %s: %s (%d)\n",
+ projectsfile, strerror(errno), errno);
+ }
+ while (fgets(line, sizeof(line), projects) != NULL) {
+ for (z = &lastc(line);
+ strlen(line) && strchr("\n\r", *z);
+ z = &lastc(line))
+ *z = '\0';
+ if (strlen(line))
+ scan_path(path, fmt("%s/%s", path, line), fn);
+ }
+ if ((err = ferror(projects))) {
+ fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n",
+ projectsfile, strerror(err), err);
+ }
+ fclose(projects);
+}
+
void scan_tree(const char *path, repo_config_fn fn)
{
scan_path(path, path, fn);
-
-
+extern void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn);
extern void scan_tree(const char *path, repo_config_fn fn);
struct cgit_repolist cgit_repolist;
struct cgit_context ctx;
-int cgit_cmd;
int chk_zero(int result, char *msg)
{
ret->snapshots = ctx.cfg.snapshots;
ret->enable_log_filecount = ctx.cfg.enable_log_filecount;
ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
+ ret->enable_remote_branches = ctx.cfg.enable_remote_branches;
+ ret->enable_subject_links = ctx.cfg.enable_subject_links;
ret->max_stats = ctx.cfg.max_stats;
ret->module_link = ctx.cfg.module_link;
ret->readme = NULL;
int cgit_diff_files(const unsigned char *old_sha1,
const unsigned char *new_sha1, unsigned long *old_size,
- unsigned long *new_size, int *binary, linediff_fn fn)
+ unsigned long *new_size, int *binary, int context,
+ int ignorews, linediff_fn fn)
{
mmfile_t file1, file2;
xpparam_t diff_params;
memset(&emit_params, 0, sizeof(emit_params));
memset(&emit_cb, 0, sizeof(emit_cb));
diff_params.flags = XDF_NEED_MINIMAL;
- emit_params.ctxlen = 3;
+ if (ignorews)
+ diff_params.flags |= XDF_IGNORE_WHITESPACE;
+ emit_params.ctxlen = context > 0 ? context : 3;
emit_params.flags = XDL_EMIT_FUNCNAMES;
emit_cb.outf = filediff_cb;
emit_cb.priv = fn;
void cgit_diff_tree(const unsigned char *old_sha1,
const unsigned char *new_sha1,
- filepair_fn fn, const char *prefix)
+ filepair_fn fn, const char *prefix, int ignorews)
{
struct diff_options opt;
int ret;
opt.detect_rename = 1;
opt.rename_limit = ctx.cfg.renamelimit;
DIFF_OPT_SET(&opt, RECURSIVE);
+ if (ignorews)
+ DIFF_XDL_SET(&opt, IGNORE_WHITESPACE);
opt.format_callback = cgit_diff_tree_cb;
opt.format_callback_data = fn;
if (prefix) {
if (commit->parents)
old_sha1 = commit->parents->item->object.sha1;
- cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL);
+ cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL,
+ ctx.qry.ignorews);
}
int cgit_parse_snapshots_mask(const char *str)
close(fd);
return (*size == st.st_size ? 0 : e);
}
+
+int is_token_char(char c)
+{
+ return isalnum(c) || c == '_';
+}
+
+/* Replace name with getenv(name), return pointer to zero-terminating char
+ */
+char *expand_macro(char *name, int maxlength)
+{
+ char *value;
+ int len;
+
+ len = 0;
+ value = getenv(name);
+ if (value) {
+ len = strlen(value);
+ if (len > maxlength)
+ len = maxlength;
+ strncpy(name, value, len);
+ }
+ return name + len;
+}
+
+#define EXPBUFSIZE (1024 * 8)
+
+/* Replace all tokens prefixed by '$' in the specified text with the
+ * value of the named environment variable.
+ * NB: the return value is a static buffer, i.e. it must be strdup'd
+ * by the caller.
+ */
+char *expand_macros(const char *txt)
+{
+ static char result[EXPBUFSIZE];
+ char *p, *start;
+ int len;
+
+ p = result;
+ start = NULL;
+ while (p < result + EXPBUFSIZE - 1 && txt && *txt) {
+ *p = *txt;
+ if (start) {
+ if (!is_token_char(*txt)) {
+ if (p - start > 0) {
+ *p = '\0';
+ len = result + EXPBUFSIZE - start - 1;
+ p = expand_macro(start, len) - 1;
+ }
+ start = NULL;
+ txt--;
+ }
+ p++;
+ txt++;
+ continue;
+ }
+ if (*txt == '$') {
+ start = p;
+ txt++;
+ continue;
+ }
+ p++;
+ txt++;
+ }
+ *p = '\0';
+ if (start && p - start > 0) {
+ len = result + EXPBUFSIZE - start - 1;
+ p = expand_macro(start, len);
+ *p = '\0';
+ }
+ return result;
+}
struct rev_info rev;
int argc = 2;
- if (!tip)
+ if (ctx.qry.show_all)
+ argv[1] = "--all";
+ else if (!tip)
argv[1] = ctx.qry.head;
if (path) {
/* ui-blob.c: show blob content
*
* Copyright (C) 2008 Lars Hjemli
+ * Copyright (C) 2010 Jason A. Donenfeld <Jason@zx2c4.com>
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
static char *match_path;
static unsigned char *matched_sha1;
+static int found_path;
static int walk_tree(const unsigned char *sha1, const char *base,int baselen,
const char *pathname, unsigned mode, int stage, void *cbdata) {
|| strcmp(match_path+baselen,pathname) )
return READ_TREE_RECURSIVE;
memmove(matched_sha1,sha1,20);
+ found_path = 1;
return 0;
}
-void cgit_print_blob(const char *hex, char *path, const char *head)
+int cgit_print_file(char *path, const char *head)
{
+ unsigned char sha1[20];
+ enum object_type type;
+ char *buf;
+ unsigned long size;
+ struct commit *commit;
+ const char *paths[] = {path, NULL};
+ if (get_sha1(head, sha1))
+ return -1;
+ type = sha1_object_info(sha1, &size);
+ if(type == OBJ_COMMIT && path) {
+ commit = lookup_commit_reference(sha1);
+ match_path = path;
+ matched_sha1 = sha1;
+ found_path = 0;
+ read_tree_recursive(commit->tree, "", 0, 0, paths, walk_tree, NULL);
+ if (!found_path)
+ return -1;
+ type = sha1_object_info(sha1, &size);
+ }
+ if (type == OBJ_BAD)
+ return -1;
+ buf = read_sha1_file(sha1, &type, &size);
+ if (!buf)
+ return -1;
+ buf[size] = '\0';
+ write(htmlfd, buf, size);
+ return 0;
+}
+void cgit_print_blob(const char *hex, char *path, const char *head)
+{
unsigned char sha1[20];
enum object_type type;
char *buf;
#ifndef UI_BLOB_H
#define UI_BLOB_H
+extern int cgit_print_file(char *path, const char *head);
extern void cgit_print_blob(const char *hex, char *path, const char *head);
#endif /* UI_BLOB_H */
#include "ui-diff.h"
#include "ui-log.h"
-void cgit_print_commit(char *hex)
+void cgit_print_commit(char *hex, const char *prefix)
{
struct commit *commit, *parent;
- struct commitinfo *info;
+ struct commitinfo *info, *parent_info;
struct commit_list *p;
+ struct strbuf notes = STRBUF_INIT;
unsigned char sha1[20];
- char *tmp;
+ char *tmp, *tmp2;
int parents = 0;
if (!hex)
}
info = cgit_parse_commit(commit);
+ format_note(NULL, sha1, ¬es, PAGE_ENCODING, 0);
+
load_ref_decorations(DECORATE_FULL_REFS);
html("<table summary='commit info' class='commit-info'>\n");
html("</td></tr>\n");
html("<tr><th>commit</th><td colspan='2' class='sha1'>");
tmp = sha1_to_hex(commit->object.sha1);
- cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp);
+ cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp, prefix, 0);
html(" (");
- cgit_patch_link("patch", NULL, NULL, NULL, tmp);
+ cgit_patch_link("patch", NULL, NULL, NULL, tmp, prefix);
+ html(") (");
+ if ((ctx.qry.ssdiff && !ctx.cfg.ssdiff) || (!ctx.qry.ssdiff && ctx.cfg.ssdiff))
+ cgit_commit_link("unidiff", NULL, NULL, ctx.qry.head, tmp, prefix, 1);
+ else
+ cgit_commit_link("side-by-side diff", NULL, NULL, ctx.qry.head, tmp, prefix, 1);
html(")</td></tr>\n");
html("<tr><th>tree</th><td colspan='2' class='sha1'>");
tmp = xstrdup(hex);
cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL,
ctx.qry.head, tmp, NULL);
+ if (prefix) {
+ html(" /");
+ cgit_tree_link(prefix, NULL, NULL, ctx.qry.head, tmp, prefix);
+ }
html("</td></tr>\n");
for (p = commit->parents; p ; p = p->next) {
parent = lookup_commit_reference(p->item->object.sha1);
}
html("<tr><th>parent</th>"
"<td colspan='2' class='sha1'>");
- cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL,
- ctx.qry.head, sha1_to_hex(p->item->object.sha1));
+ tmp = tmp2 = sha1_to_hex(p->item->object.sha1);
+ if (ctx.repo->enable_subject_links) {
+ parent_info = cgit_parse_commit(parent);
+ tmp2 = parent_info->subject;
+ }
+ cgit_commit_link(tmp2, NULL, NULL, ctx.qry.head, tmp, prefix, 0);
html(" (");
cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex,
- sha1_to_hex(p->item->object.sha1), NULL);
+ sha1_to_hex(p->item->object.sha1), prefix, 0);
html(")</td></tr>");
parents++;
}
if (ctx.repo->commit_filter)
cgit_close_filter(ctx.repo->commit_filter);
html("</div>");
+ if (notes.len != 0) {
+ html("<div class='notes-header'>Notes</div>");
+ html("<div class='notes'>");
+ if (ctx.repo->commit_filter)
+ cgit_open_filter(ctx.repo->commit_filter);
+ html_txt(notes.buf);
+ if (ctx.repo->commit_filter)
+ cgit_close_filter(ctx.repo->commit_filter);
+ html("</div>");
+ html("<div class='notes-footer'></div>");
+ }
if (parents < 3) {
if (parents)
tmp = sha1_to_hex(commit->parents->item->object.sha1);
else
tmp = NULL;
- cgit_print_diff(ctx.qry.sha1, tmp, NULL);
+ cgit_print_diff(ctx.qry.sha1, tmp, prefix);
}
+ strbuf_release(¬es);
cgit_free_commitinfo(info);
}
#ifndef UI_COMMIT_H
#define UI_COMMIT_H
-extern void cgit_print_commit(char *hex);
+extern void cgit_print_commit(char *hex, const char *prefix);
#endif /* UI_COMMIT_H */
#include "cgit.h"
#include "html.h"
#include "ui-shared.h"
+#include "ui-ssdiff.h"
unsigned char old_rev_sha1[20];
unsigned char new_rev_sha1[20];
int binary:1;
} *items;
+static int use_ssdiff = 0;
static void print_fileinfo(struct fileinfo *info)
{
}
htmlf("</td><td class='%s'>", class);
cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, ctx.qry.sha1,
- ctx.qry.sha2, info->new_path);
+ ctx.qry.sha2, info->new_path, 0);
if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED)
htmlf(" (%s from %s)",
info->status == DIFF_STATUS_COPIED ? "copied" : "renamed",
lines_added = 0;
lines_removed = 0;
cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, &new_size,
- &binary, count_diff_lines);
+ &binary, 0, ctx.qry.ignorews, count_diff_lines);
if (files >= slots) {
if (slots == 0)
slots = 4;
}
void cgit_print_diffstat(const unsigned char *old_sha1,
- const unsigned char *new_sha1)
+ const unsigned char *new_sha1, const char *prefix)
{
- int i;
+ int i, save_context = ctx.qry.context;
html("<div class='diffstat-header'>");
cgit_diff_link("Diffstat", NULL, NULL, ctx.qry.head, ctx.qry.sha1,
- ctx.qry.sha2, NULL);
+ ctx.qry.sha2, NULL, 0);
+ if (prefix)
+ htmlf(" (limited to '%s')", prefix);
+ html(" (");
+ ctx.qry.context = (save_context > 0 ? save_context : 3) << 1;
+ cgit_self_link("more", NULL, NULL, &ctx);
+ html("/");
+ ctx.qry.context = (save_context > 3 ? save_context : 3) >> 1;
+ cgit_self_link("less", NULL, NULL, &ctx);
+ ctx.qry.context = save_context;
+ html(" context)");
+ html(" (");
+ ctx.qry.ignorews = (ctx.qry.ignorews + 1) % 2;
+ cgit_self_link(ctx.qry.ignorews ? "ignore" : "show", NULL, NULL, &ctx);
+ ctx.qry.ignorews = (ctx.qry.ignorews + 1) % 2;
+ html(" whitespace changes)");
html("</div>");
html("<table summary='diffstat' class='diffstat'>");
max_changes = 0;
- cgit_diff_tree(old_sha1, new_sha1, inspect_filepair, NULL);
+ cgit_diff_tree(old_sha1, new_sha1, inspect_filepair, prefix,
+ ctx.qry.ignorews);
for(i = 0; i<files; i++)
print_fileinfo(&items[i]);
html("</table>");
html("</div>");
}
+static void print_ssdiff_link()
+{
+ if (!strcmp(ctx.qry.page, "diff")) {
+ if (use_ssdiff)
+ cgit_diff_link("Unidiff", NULL, NULL, ctx.qry.head,
+ ctx.qry.sha1, ctx.qry.sha2, ctx.qry.path, 1);
+ else
+ cgit_diff_link("Side-by-side diff", NULL, NULL,
+ ctx.qry.head, ctx.qry.sha1,
+ ctx.qry.sha2, ctx.qry.path, 1);
+ }
+}
+
static void filepair_cb(struct diff_filepair *pair)
{
unsigned long old_size = 0;
unsigned long new_size = 0;
int binary = 0;
+ linediff_fn print_line_fn = print_line;
+ if (use_ssdiff) {
+ cgit_ssdiff_header_begin();
+ print_line_fn = cgit_ssdiff_line_cb;
+ }
header(pair->one->sha1, pair->one->path, pair->one->mode,
pair->two->sha1, pair->two->path, pair->two->mode);
+ if (use_ssdiff)
+ cgit_ssdiff_header_end();
if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
if (S_ISGITLINK(pair->one->mode))
- print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52);
+ print_line_fn(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52);
if (S_ISGITLINK(pair->two->mode))
- print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52);
+ print_line_fn(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52);
+ if (use_ssdiff)
+ cgit_ssdiff_footer();
return;
}
- if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size,
- &new_size, &binary, print_line))
+ if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size,
+ &new_size, &binary, ctx.qry.context,
+ ctx.qry.ignorews, print_line_fn))
cgit_print_error("Error running diff");
- if (binary)
- html("Binary files differ");
+ if (binary) {
+ if (use_ssdiff)
+ html("<tr><td colspan='4'>Binary files differ</td></tr>");
+ else
+ html("Binary files differ");
+ }
+ if (use_ssdiff)
+ cgit_ssdiff_footer();
}
void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefix)
if (!commit2 || parse_commit(commit2))
cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(old_rev_sha1)));
}
- cgit_print_diffstat(old_rev_sha1, new_rev_sha1);
- html("<table summary='diff' class='diff'>");
- html("<tr><td>");
- cgit_diff_tree(old_rev_sha1, new_rev_sha1, filepair_cb, prefix);
- html("</td></tr>");
+ if ((ctx.qry.ssdiff && !ctx.cfg.ssdiff) || (!ctx.qry.ssdiff && ctx.cfg.ssdiff))
+ use_ssdiff = 1;
+
+ print_ssdiff_link();
+ cgit_print_diffstat(old_rev_sha1, new_rev_sha1, prefix);
+
+ if (use_ssdiff) {
+ html("<table summary='ssdiff' class='ssdiff'>");
+ } else {
+ html("<table summary='diff' class='diff'>");
+ html("<tr><td>");
+ }
+ cgit_diff_tree(old_rev_sha1, new_rev_sha1, filepair_cb, prefix,
+ ctx.qry.ignorews);
+ if (!use_ssdiff)
+ html("</td></tr>");
html("</table>");
}
files++;
if (ctx.repo->enable_log_linecount)
cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size,
- &new_size, &binary, count_lines);
+ &new_size, &binary, 0, ctx.qry.ignorews,
+ count_lines);
}
void show_commit_decorations(struct commit *commit)
while (deco) {
if (!prefixcmp(deco->name, "refs/heads/")) {
strncpy(buf, deco->name + 11, sizeof(buf) - 1);
- cgit_log_link(buf, NULL, "branch-deco", buf, NULL, NULL,
- 0, NULL, NULL, ctx.qry.showmsg);
+ cgit_log_link(buf, NULL, "branch-deco", buf, NULL,
+ ctx.qry.vpath, 0, NULL, NULL,
+ ctx.qry.showmsg);
}
else if (!prefixcmp(deco->name, "tag: refs/tags/")) {
strncpy(buf, deco->name + 15, sizeof(buf) - 1);
else if (!prefixcmp(deco->name, "refs/remotes/")) {
strncpy(buf, deco->name + 13, sizeof(buf) - 1);
cgit_log_link(buf, NULL, "remote-deco", NULL,
- sha1_to_hex(commit->object.sha1), NULL,
- 0, NULL, NULL, ctx.qry.showmsg);
+ sha1_to_hex(commit->object.sha1),
+ ctx.qry.vpath, 0, NULL, NULL,
+ ctx.qry.showmsg);
}
else {
strncpy(buf, deco->name, sizeof(buf) - 1);
cgit_commit_link(buf, NULL, "deco", ctx.qry.head,
- sha1_to_hex(commit->object.sha1));
+ sha1_to_hex(commit->object.sha1),
+ ctx.qry.vpath, 0);
}
deco = deco->next;
}
htmlf("<tr%s><td>",
ctx.qry.showmsg ? " class='logheader'" : "");
tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1));
- tmp = cgit_pageurl(ctx.repo->url, "commit", tmp);
+ tmp = cgit_fileurl(ctx.repo->url, "commit", ctx.qry.vpath, tmp);
html_link_open(tmp, NULL, NULL);
cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
html_link_close();
htmlf("</td><td%s>",
ctx.qry.showmsg ? " class='logsubject'" : "");
cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
- sha1_to_hex(commit->object.sha1));
+ sha1_to_hex(commit->object.sha1), ctx.qry.vpath, 0);
show_commit_decorations(commit);
html("</td><td>");
html_txt(info->author);
}
html("</td></tr>\n");
if (ctx.qry.showmsg) {
+ struct strbuf notes = STRBUF_INIT;
+ format_note(NULL, commit->object.sha1, ¬es, PAGE_ENCODING, 0);
+
if (ctx.repo->enable_log_filecount) {
cols++;
if (ctx.repo->enable_log_linecount)
cols);
html_txt(info->msg);
html("</td></tr>\n");
+ if (notes.len != 0) {
+ html("<tr class='nohover'>");
+ html("<td class='lognotes-label'>Notes:</td>");
+ htmlf("<td colspan='%d' class='lognotes'>",
+ cols);
+ html_txt(notes.buf);
+ html("</td></tr>\n");
+ }
+ strbuf_release(¬es);
}
cgit_free_commitinfo(info);
}
argv[1] = disambiguate_ref(tip);
- if (grep && pattern && (!strcmp(grep, "grep") ||
- !strcmp(grep, "author") ||
- !strcmp(grep, "committer")))
- argv[argc++] = fmt("--%s=%s", grep, pattern);
+ if (grep && pattern) {
+ if (!strcmp(grep, "grep") || !strcmp(grep, "author") ||
+ !strcmp(grep, "committer"))
+ argv[argc++] = fmt("--%s=%s", grep, pattern);
+ if (!strcmp(grep, "range"))
+ argv[1] = pattern;
+ }
if (path) {
argv[argc++] = "--";
html(" (");
cgit_log_link(ctx.qry.showmsg ? "Collapse" : "Expand", NULL,
NULL, ctx.qry.head, ctx.qry.sha1,
- ctx.qry.path, ctx.qry.ofs, ctx.qry.grep,
+ ctx.qry.vpath, ctx.qry.ofs, ctx.qry.grep,
ctx.qry.search, ctx.qry.showmsg ? 0 : 1);
html(")");
}
columns);
if (ofs > 0) {
cgit_log_link("[prev]", NULL, NULL, ctx.qry.head,
- ctx.qry.sha1, ctx.qry.path,
+ ctx.qry.sha1, ctx.qry.vpath,
ofs - cnt, ctx.qry.grep,
ctx.qry.search, ctx.qry.showmsg);
html(" ");
}
if ((commit = get_revision(&rev)) != NULL) {
cgit_log_link("[next]", NULL, NULL, ctx.qry.head,
- ctx.qry.sha1, ctx.qry.path,
+ ctx.qry.sha1, ctx.qry.vpath,
ofs + cnt, ctx.qry.grep,
ctx.qry.search, ctx.qry.showmsg);
}
html("</div>");
} else if ((commit = get_revision(&rev)) != NULL) {
html("<tr class='nohover'><td colspan='3'>");
- cgit_log_link("[...]", NULL, NULL, ctx.qry.head, NULL, NULL, 0,
- NULL, NULL, ctx.qry.showmsg);
+ cgit_log_link("[...]", NULL, NULL, ctx.qry.head, NULL,
+ ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg);
html("</td></tr>\n");
}
}
return;
}
if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size,
- &new_size, &binary, print_line))
+ &new_size, &binary, 0, 0, print_line))
html("Error running diff");
if (binary)
html("Binary files differ\n");
}
-void cgit_print_patch(char *hex)
+void cgit_print_patch(char *hex, const char *prefix)
{
struct commit *commit;
struct commitinfo *info;
html("\n");
}
html("---\n");
- cgit_diff_tree(old_sha1, sha1, filepair_cb, NULL);
+ if (prefix)
+ htmlf("(limited to '%s')\n\n", prefix);
+ cgit_diff_tree(old_sha1, sha1, filepair_cb, prefix, 0);
html("--\n");
htmlf("cgit %s\n", CGIT_VERSION);
cgit_free_commitinfo(info);
#ifndef UI_PATCH_H
#define UI_PATCH_H
-extern void cgit_print_patch(char *hex);
+extern void cgit_print_patch(char *hex, const char *prefix);
#endif /* UI_PATCH_H */
#include "html.h"
#include "ui-shared.h"
-char *curr_rev;
-char *match_path;
+int match_baselen;
int match;
static void print_object(const unsigned char *sha1, const char *path)
ctx.page.mimetype = NULL;
ext = strrchr(path, '.');
if (ext && *(++ext)) {
- mime = string_list_lookup(ext, &ctx.cfg.mimetypes);
+ mime = string_list_lookup(&ctx.cfg.mimetypes, ext);
if (mime)
ctx.page.mimetype = (char *)mime->util;
}
match = 1;
}
+static void print_dir(const unsigned char *sha1, const char *path,
+ const char *base)
+{
+ char *fullpath;
+ if (path[0] || base[0])
+ fullpath = fmt("/%s%s/", base, path);
+ else
+ fullpath = "/";
+ ctx.page.etag = sha1_to_hex(sha1);
+ cgit_print_http_headers(&ctx);
+ htmlf("<html><head><title>%s</title></head>\n<body>\n"
+ " <h2>%s</h2>\n <ul>\n", fullpath, fullpath);
+ if (path[0] || base[0])
+ html(" <li><a href=\"../\">../</a></li>\n");
+ match = 2;
+}
+
+static void print_dir_entry(const unsigned char *sha1, const char *path,
+ unsigned mode)
+{
+ const char *sep = "";
+ if (S_ISDIR(mode))
+ sep = "/";
+ htmlf(" <li><a href=\"%s%s\">%s%s</a></li>\n", path, sep, path, sep);
+ match = 2;
+}
+
+static void print_dir_tail(void)
+{
+ html(" </ul>\n</body></html>\n");
+}
+
static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
const char *pathname, unsigned mode, int stage,
void *cbdata)
{
- if (S_ISDIR(mode))
+ if (baselen == match_baselen) {
+ if (S_ISREG(mode))
+ print_object(sha1, pathname);
+ else if (S_ISDIR(mode)) {
+ print_dir(sha1, pathname, base);
+ return READ_TREE_RECURSIVE;
+ }
+ }
+ else if (baselen > match_baselen)
+ print_dir_entry(sha1, pathname, mode);
+ else if (S_ISDIR(mode))
return READ_TREE_RECURSIVE;
- if (S_ISREG(mode) && !strncmp(base, match_path, baselen) &&
- !strcmp(pathname, match_path + baselen))
- print_object(sha1, pathname);
+ return 0;
+}
+static int basedir_len(const char *path)
+{
+ char *p = strrchr(path, '/');
+ if (p)
+ return p - path + 1;
return 0;
}
if (!rev)
rev = ctx->qry.head;
- curr_rev = xstrdup(rev);
if (get_sha1(rev, sha1)) {
html_status(404, "Not found", 0);
return;
html_status(404, "Not found", 0);
return;
}
- match_path = ctx->qry.path;
+ if (!paths[0]) {
+ paths[0] = "";
+ match_baselen = -1;
+ print_dir(commit->tree->object.sha1, "", "");
+ }
+ else
+ match_baselen = basedir_len(paths[0]);
read_tree_recursive(commit->tree, "", 0, 0, paths, walk_tree, NULL);
if (!match)
html_status(404, "Not found", 0);
+ else if (match == 2)
+ print_dir_tail();
}
html("</td><td>");
if (ref->object->type == OBJ_COMMIT) {
- cgit_commit_link(info->subject, NULL, NULL, name, NULL);
+ cgit_commit_link(info->subject, NULL, NULL, name, NULL, NULL, 0);
html("</td><td>");
html_txt(info->author);
html("</td><td colspan='2'>");
list.refs = NULL;
list.alloc = list.count = 0;
for_each_branch_ref(cgit_refs_cb, &list);
+ if (ctx.repo->enable_remote_branches)
+ for_each_remote_ref(cgit_refs_cb, &list);
if (maxcount == 0 || maxcount > list.count)
maxcount = list.count;
tm->tm_hour, tm->tm_min, tm->tm_sec);
}
-void cgit_print_error(char *msg)
+void cgit_print_error(const char *msg)
{
html("<div class='error'>");
html_txt(msg);
return fmt("%s/", ctx.cfg.virtual_root);
}
-static void site_url(char *page, char *search, int ofs)
+static void site_url(const char *page, const char *search, int ofs)
{
char *delim = "?";
}
}
-static void site_link(char *page, char *name, char *title, char *class,
- char *search, int ofs)
+static void site_link(const char *page, const char *name, const char *title,
+ const char *class, const char *search, int ofs)
{
html("<a");
if (title) {
html("</a>");
}
-void cgit_index_link(char *name, char *title, char *class, char *pattern,
- int ofs)
+void cgit_index_link(const char *name, const char *title, const char *class,
+ const char *pattern, int ofs)
{
site_link(NULL, name, title, class, pattern, ofs);
}
-static char *repolink(char *title, char *class, char *page, char *head,
- char *path)
+static char *repolink(const char *title, const char *class, const char *page,
+ const char *head, const char *path)
{
char *delim = "?";
return fmt("%s", delim);
}
-static void reporevlink(char *page, char *name, char *title, char *class,
- char *head, char *rev, char *path)
+static void reporevlink(const char *page, const char *name, const char *title,
+ const char *class, const char *head, const char *rev,
+ const char *path)
{
char *delim;
html("</a>");
}
-void cgit_summary_link(char *name, char *title, char *class, char *head)
+void cgit_summary_link(const char *name, const char *title, const char *class,
+ const char *head)
{
reporevlink(NULL, name, title, class, head, NULL, NULL);
}
-void cgit_tag_link(char *name, char *title, char *class, char *head,
- char *rev)
+void cgit_tag_link(const char *name, const char *title, const char *class,
+ const char *head, const char *rev)
{
reporevlink("tag", name, title, class, head, rev, NULL);
}
-void cgit_tree_link(char *name, char *title, char *class, char *head,
- char *rev, char *path)
+void cgit_tree_link(const char *name, const char *title, const char *class,
+ const char *head, const char *rev, const char *path)
{
reporevlink("tree", name, title, class, head, rev, path);
}
-void cgit_plain_link(char *name, char *title, char *class, char *head,
- char *rev, char *path)
+void cgit_plain_link(const char *name, const char *title, const char *class,
+ const char *head, const char *rev, const char *path)
{
reporevlink("plain", name, title, class, head, rev, path);
}
-void cgit_log_link(char *name, char *title, char *class, char *head,
- char *rev, char *path, int ofs, char *grep, char *pattern,
- int showmsg)
+void cgit_log_link(const char *name, const char *title, const char *class,
+ const char *head, const char *rev, const char *path,
+ int ofs, const char *grep, const char *pattern, int showmsg)
{
char *delim;
html("</a>");
}
-void cgit_commit_link(char *name, char *title, char *class, char *head,
- char *rev)
+void cgit_commit_link(char *name, const char *title, const char *class,
+ const char *head, const char *rev, const char *path,
+ int toggle_ssdiff)
{
if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) {
name[ctx.cfg.max_msg_len] = '\0';
name[ctx.cfg.max_msg_len - 2] = '.';
name[ctx.cfg.max_msg_len - 3] = '.';
}
- reporevlink("commit", name, title, class, head, rev, NULL);
+
+ char *delim;
+
+ delim = repolink(title, class, "commit", head, path);
+ if (rev && strcmp(rev, ctx.qry.head)) {
+ html(delim);
+ html("id=");
+ html_url_arg(rev);
+ delim = "&";
+ }
+ if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) {
+ html(delim);
+ html("ss=1");
+ delim = "&";
+ }
+ if (ctx.qry.context > 0 && ctx.qry.context != 3) {
+ html(delim);
+ html("context=");
+ htmlf("%d", ctx.qry.context);
+ delim = "&";
+ }
+ if (ctx.qry.ignorews) {
+ html(delim);
+ html("ignorews=1");
+ delim = "&";
+ }
+ html("'>");
+ html_txt(name);
+ html("</a>");
}
-void cgit_refs_link(char *name, char *title, char *class, char *head,
- char *rev, char *path)
+void cgit_refs_link(const char *name, const char *title, const char *class,
+ const char *head, const char *rev, const char *path)
{
reporevlink("refs", name, title, class, head, rev, path);
}
-void cgit_snapshot_link(char *name, char *title, char *class, char *head,
- char *rev, char *archivename)
+void cgit_snapshot_link(const char *name, const char *title, const char *class,
+ const char *head, const char *rev,
+ const char *archivename)
{
reporevlink("snapshot", name, title, class, head, rev, archivename);
}
-void cgit_diff_link(char *name, char *title, char *class, char *head,
- char *new_rev, char *old_rev, char *path)
+void cgit_diff_link(const char *name, const char *title, const char *class,
+ const char *head, const char *new_rev, const char *old_rev,
+ const char *path, int toggle_ssdiff)
{
char *delim;
html(delim);
html("id2=");
html_url_arg(old_rev);
+ delim = "&";
+ }
+ if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) {
+ html(delim);
+ html("ss=1");
+ delim = "&";
+ }
+ if (ctx.qry.context > 0 && ctx.qry.context != 3) {
+ html(delim);
+ html("context=");
+ htmlf("%d", ctx.qry.context);
+ delim = "&";
+ }
+ if (ctx.qry.ignorews) {
+ html(delim);
+ html("ignorews=1");
+ delim = "&";
}
html("'>");
html_txt(name);
html("</a>");
}
-void cgit_patch_link(char *name, char *title, char *class, char *head,
- char *rev)
+void cgit_patch_link(const char *name, const char *title, const char *class,
+ const char *head, const char *rev, const char *path)
{
- reporevlink("patch", name, title, class, head, rev, NULL);
+ reporevlink("patch", name, title, class, head, rev, path);
}
-void cgit_stats_link(char *name, char *title, char *class, char *head,
- char *path)
+void cgit_stats_link(const char *name, const char *title, const char *class,
+ const char *head, const char *path)
{
reporevlink("stats", name, title, class, head, NULL, path);
}
+void cgit_self_link(char *name, const char *title, const char *class,
+ struct cgit_context *ctx)
+{
+ if (!strcmp(ctx->qry.page, "repolist"))
+ return cgit_index_link(name, title, class, ctx->qry.search,
+ ctx->qry.ofs);
+ else if (!strcmp(ctx->qry.page, "summary"))
+ return cgit_summary_link(name, title, class, ctx->qry.head);
+ else if (!strcmp(ctx->qry.page, "tag"))
+ return cgit_tag_link(name, title, class, ctx->qry.head,
+ ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL);
+ else if (!strcmp(ctx->qry.page, "tree"))
+ return cgit_tree_link(name, title, class, ctx->qry.head,
+ ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
+ ctx->qry.path);
+ else if (!strcmp(ctx->qry.page, "plain"))
+ return cgit_plain_link(name, title, class, ctx->qry.head,
+ ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
+ ctx->qry.path);
+ else if (!strcmp(ctx->qry.page, "log"))
+ return cgit_log_link(name, title, class, ctx->qry.head,
+ ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
+ ctx->qry.path, ctx->qry.ofs,
+ ctx->qry.grep, ctx->qry.search,
+ ctx->qry.showmsg);
+ else if (!strcmp(ctx->qry.page, "commit"))
+ return cgit_commit_link(name, title, class, ctx->qry.head,
+ ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
+ ctx->qry.path, 0);
+ else if (!strcmp(ctx->qry.page, "patch"))
+ return cgit_patch_link(name, title, class, ctx->qry.head,
+ ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
+ ctx->qry.path);
+ else if (!strcmp(ctx->qry.page, "refs"))
+ return cgit_refs_link(name, title, class, ctx->qry.head,
+ ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
+ ctx->qry.path);
+ else if (!strcmp(ctx->qry.page, "snapshot"))
+ return cgit_snapshot_link(name, title, class, ctx->qry.head,
+ ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
+ ctx->qry.path);
+ else if (!strcmp(ctx->qry.page, "diff"))
+ return cgit_diff_link(name, title, class, ctx->qry.head,
+ ctx->qry.sha1, ctx->qry.sha2,
+ ctx->qry.path, 0);
+ else if (!strcmp(ctx->qry.page, "stats"))
+ return cgit_stats_link(name, title, class, ctx->qry.head,
+ ctx->qry.path);
+
+ /* Don't known how to make link for this page */
+ repolink(title, class, ctx->qry.page, ctx->qry.head, ctx->qry.path);
+ html("><!-- cgit_self_link() doesn't know how to make link for page '");
+ html_txt(ctx->qry.page);
+ html("' -->");
+ html_txt(name);
+ html("</a>");
+}
+
void cgit_object_link(struct object *obj)
{
char *page, *shortrev, *fullrev, *name;
shortrev[10] = '\0';
if (obj->type == OBJ_COMMIT) {
cgit_commit_link(fmt("commit %s...", shortrev), NULL, NULL,
- ctx.qry.head, fullrev);
+ ctx.qry.head, fullrev, NULL, 0);
return;
} else if (obj->type == OBJ_TREE)
page = "tree";
reporevlink(page, name, NULL, NULL, ctx.qry.head, fullrev, NULL);
}
-void cgit_print_date(time_t secs, char *format, int local_time)
+void cgit_print_date(time_t secs, const char *format, int local_time)
{
char buf[64];
struct tm *time;
html_txt(buf);
}
-void cgit_print_age(time_t t, time_t max_relative, char *format)
+void cgit_print_age(time_t t, time_t max_relative, const char *format)
{
time_t now, secs;
html("<link rel='alternate' title='Atom feed' href='");
html(cgit_httpscheme());
html_attr(cgit_hosturl());
- html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.path,
+ html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.vpath,
fmt("h=%s", ctx->qry.head)));
html("' type='application/atom+xml'/>\n");
}
return 0;
}
-void cgit_add_hidden_formfields(int incl_head, int incl_search, char *page)
+void cgit_add_hidden_formfields(int incl_head, int incl_search,
+ const char *page)
{
char *url;
if (!ctx.cfg.virtual_root) {
url = fmt("%s/%s", ctx.qry.repo, page);
- if (ctx.qry.path)
- url = fmt("%s/%s", url, ctx.qry.path);
+ if (ctx.qry.vpath)
+ url = fmt("%s/%s", url, ctx.qry.vpath);
html_hidden("url", url);
}
}
}
-const char *fallback_cmd = "repolist";
+static const char *hc(struct cgit_context *ctx, const char *page)
+{
+ return strcmp(ctx->qry.page, page) ? NULL : "active";
+}
-char *hc(struct cgit_cmd *cmd, const char *page)
+static void cgit_print_path_crumbs(struct cgit_context *ctx, char *path)
{
- return (strcmp(cmd ? cmd->name : fallback_cmd, page) ? NULL : "active");
+ char *old_path = ctx->qry.path;
+ char *p = path, *q, *end = path + strlen(path);
+
+ ctx->qry.path = NULL;
+ cgit_self_link("root", NULL, NULL, ctx);
+ ctx->qry.path = p = path;
+ while (p < end) {
+ if (!(q = strchr(p, '/')))
+ q = end;
+ *q = '\0';
+ html_txt("/");
+ cgit_self_link(p, NULL, NULL, ctx);
+ if (q < end)
+ *q = '/';
+ p = q + 1;
+ }
+ ctx->qry.path = old_path;
}
static void print_header(struct cgit_context *ctx)
void cgit_print_pageheader(struct cgit_context *ctx)
{
- struct cgit_cmd *cmd = cgit_get_cmd(ctx);
-
- if (!cmd && ctx->repo)
- fallback_cmd = "summary";
-
html("<div id='cgit'>");
if (!ctx->cfg.noheader)
print_header(ctx);
html("<table class='tabs'><tr><td>\n");
if (ctx->repo) {
- cgit_summary_link("summary", NULL, hc(cmd, "summary"),
+ cgit_summary_link("summary", NULL, hc(ctx, "summary"),
ctx->qry.head);
- cgit_refs_link("refs", NULL, hc(cmd, "refs"), ctx->qry.head,
+ cgit_refs_link("refs", NULL, hc(ctx, "refs"), ctx->qry.head,
ctx->qry.sha1, NULL);
- cgit_log_link("log", NULL, hc(cmd, "log"), ctx->qry.head,
- NULL, NULL, 0, NULL, NULL, ctx->qry.showmsg);
- cgit_tree_link("tree", NULL, hc(cmd, "tree"), ctx->qry.head,
- ctx->qry.sha1, NULL);
- cgit_commit_link("commit", NULL, hc(cmd, "commit"),
- ctx->qry.head, ctx->qry.sha1);
- cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head,
- ctx->qry.sha1, ctx->qry.sha2, NULL);
+ cgit_log_link("log", NULL, hc(ctx, "log"), ctx->qry.head,
+ NULL, ctx->qry.vpath, 0, NULL, NULL,
+ ctx->qry.showmsg);
+ cgit_tree_link("tree", NULL, hc(ctx, "tree"), ctx->qry.head,
+ ctx->qry.sha1, ctx->qry.vpath);
+ cgit_commit_link("commit", NULL, hc(ctx, "commit"),
+ ctx->qry.head, ctx->qry.sha1, ctx->qry.vpath, 0);
+ cgit_diff_link("diff", NULL, hc(ctx, "diff"), ctx->qry.head,
+ ctx->qry.sha1, ctx->qry.sha2, ctx->qry.vpath, 0);
if (ctx->repo->max_stats)
- cgit_stats_link("stats", NULL, hc(cmd, "stats"),
- ctx->qry.head, NULL);
+ cgit_stats_link("stats", NULL, hc(ctx, "stats"),
+ ctx->qry.head, ctx->qry.vpath);
if (ctx->repo->readme)
reporevlink("about", "about", NULL,
- hc(cmd, "about"), ctx->qry.head, NULL,
+ hc(ctx, "about"), ctx->qry.head, NULL,
NULL);
html("</td><td class='form'>");
html("<form class='right' method='get' action='");
if (ctx->cfg.virtual_root)
html_url_path(cgit_fileurl(ctx->qry.repo, "log",
- ctx->qry.path, NULL));
+ ctx->qry.vpath, NULL));
html("'>\n");
cgit_add_hidden_formfields(1, 0, "log");
html("<select name='qt'>\n");
html_option("grep", "log msg", ctx->qry.grep);
html_option("author", "author", ctx->qry.grep);
html_option("committer", "committer", ctx->qry.grep);
+ html_option("range", "range", ctx->qry.grep);
html("</select>\n");
html("<input class='txt' type='text' size='10' name='q' value='");
html_attr(ctx->qry.search);
html("<input type='submit' value='search'/>\n");
html("</form>\n");
} else {
- site_link(NULL, "index", NULL, hc(cmd, "repolist"), NULL, 0);
+ site_link(NULL, "index", NULL, hc(ctx, "repolist"), NULL, 0);
if (ctx->cfg.root_readme)
- site_link("about", "about", NULL, hc(cmd, "about"),
+ site_link("about", "about", NULL, hc(ctx, "about"),
NULL, 0);
html("</td><td class='form'>");
html("<form method='get' action='");
html("</form>");
}
html("</td></tr></table>\n");
+ if (ctx->qry.vpath) {
+ html("<div class='path'>");
+ html("path: ");
+ cgit_print_path_crumbs(ctx, ctx->qry.vpath);
+ html("</div>");
+ }
html("<div class='content'>");
}
const char *hex, int snapshots)
{
const struct cgit_snapshot_format* f;
+ char *prefix;
char *filename;
+ unsigned char sha1[20];
+ if (get_sha1(fmt("refs/tags/%s", hex), sha1) == 0 &&
+ (hex[0] == 'v' || hex[0] == 'V') && isdigit(hex[1]))
+ hex++;
+ prefix = xstrdup(fmt("%s-%s", cgit_repobasename(repo), hex));
for (f = cgit_snapshot_formats; f->suffix; f++) {
if (!(snapshots & f->bit))
continue;
- filename = fmt("%s-%s%s", cgit_repobasename(repo), hex,
- f->suffix);
+ filename = fmt("%s%s", prefix, f->suffix);
cgit_snapshot_link(filename, NULL, NULL, NULL, NULL, filename);
html("<br/>");
}
extern char *cgit_pageurl(const char *reponame, const char *pagename,
const char *query);
-extern void cgit_index_link(char *name, char *title, char *class,
- char *pattern, int ofs);
-extern void cgit_summary_link(char *name, char *title, char *class, char *head);
-extern void cgit_tag_link(char *name, char *title, char *class, char *head,
- char *rev);
-extern void cgit_tree_link(char *name, char *title, char *class, char *head,
- char *rev, char *path);
-extern void cgit_plain_link(char *name, char *title, char *class, char *head,
- char *rev, char *path);
-extern void cgit_log_link(char *name, char *title, char *class, char *head,
- char *rev, char *path, int ofs, char *grep,
- char *pattern, int showmsg);
-extern void cgit_commit_link(char *name, char *title, char *class, char *head,
- char *rev);
-extern void cgit_patch_link(char *name, char *title, char *class, char *head,
- char *rev);
-extern void cgit_refs_link(char *name, char *title, char *class, char *head,
- char *rev, char *path);
-extern void cgit_snapshot_link(char *name, char *title, char *class,
- char *head, char *rev, char *archivename);
-extern void cgit_diff_link(char *name, char *title, char *class, char *head,
- char *new_rev, char *old_rev, char *path);
-extern void cgit_stats_link(char *name, char *title, char *class, char *head,
- char *path);
+extern void cgit_index_link(const char *name, const char *title,
+ const char *class, const char *pattern, int ofs);
+extern void cgit_summary_link(const char *name, const char *title,
+ const char *class, const char *head);
+extern void cgit_tag_link(const char *name, const char *title,
+ const char *class, const char *head,
+ const char *rev);
+extern void cgit_tree_link(const char *name, const char *title,
+ const char *class, const char *head,
+ const char *rev, const char *path);
+extern void cgit_plain_link(const char *name, const char *title,
+ const char *class, const char *head,
+ const char *rev, const char *path);
+extern void cgit_log_link(const char *name, const char *title,
+ const char *class, const char *head, const char *rev,
+ const char *path, int ofs, const char *grep,
+ const char *pattern, int showmsg);
+extern void cgit_commit_link(char *name, const char *title,
+ const char *class, const char *head,
+ const char *rev, const char *path,
+ int toggle_ssdiff);
+extern void cgit_patch_link(const char *name, const char *title,
+ const char *class, const char *head,
+ const char *rev, const char *path);
+extern void cgit_refs_link(const char *name, const char *title,
+ const char *class, const char *head,
+ const char *rev, const char *path);
+extern void cgit_snapshot_link(const char *name, const char *title,
+ const char *class, const char *head,
+ const char *rev, const char *archivename);
+extern void cgit_diff_link(const char *name, const char *title,
+ const char *class, const char *head,
+ const char *new_rev, const char *old_rev,
+ const char *path, int toggle_ssdiff);
+extern void cgit_stats_link(const char *name, const char *title,
+ const char *class, const char *head,
+ const char *path);
+extern void cgit_self_link(char *name, const char *title,
+ const char *class, struct cgit_context *ctx);
extern void cgit_object_link(struct object *obj);
-extern void cgit_print_error(char *msg);
-extern void cgit_print_date(time_t secs, char *format, int local_time);
-extern void cgit_print_age(time_t t, time_t max_relative, char *format);
+extern void cgit_print_error(const char *msg);
+extern void cgit_print_date(time_t secs, const char *format, int local_time);
+extern void cgit_print_age(time_t t, time_t max_relative, const char *format);
extern void cgit_print_http_headers(struct cgit_context *ctx);
extern void cgit_print_docstart(struct cgit_context *ctx);
extern void cgit_print_docend();
extern void cgit_print_snapshot_links(const char *repo, const char *head,
const char *hex, int snapshots);
extern void cgit_add_hidden_formfields(int incl_head, int incl_search,
- char *page);
+ const char *page);
#endif /* UI_SHARED_H */
return write_compressed_tar_archive(args,"bzip2");
}
+static int write_tar_xz_archive(struct archiver_args *args)
+{
+ return write_compressed_tar_archive(args,"xz");
+}
+
const struct cgit_snapshot_format cgit_snapshot_formats[] = {
- { ".zip", "application/x-zip", write_zip_archive, 0x1 },
- { ".tar.gz", "application/x-gzip", write_tar_gzip_archive, 0x2 },
- { ".tar.bz2", "application/x-bzip2", write_tar_bzip2_archive, 0x4 },
- { ".tar", "application/x-tar", write_tar_archive, 0x8 },
+ { ".zip", "application/x-zip", write_zip_archive, 0x01 },
+ { ".tar.gz", "application/x-gzip", write_tar_gzip_archive, 0x02 },
+ { ".tar.bz2", "application/x-bzip2", write_tar_bzip2_archive, 0x04 },
+ { ".tar", "application/x-tar", write_tar_archive, 0x08 },
+ { ".tar.xz", "application/x-xz", write_tar_xz_archive, 0x10 },
{}
};
--- /dev/null
+#include "cgit.h"
+#include "html.h"
+#include "ui-shared.h"
+
+extern int use_ssdiff;
+
+static int current_old_line, current_new_line;
+
+struct deferred_lines {
+ int line_no;
+ char *line;
+ struct deferred_lines *next;
+};
+
+static struct deferred_lines *deferred_old, *deferred_old_last;
+static struct deferred_lines *deferred_new, *deferred_new_last;
+
+static char *longest_common_subsequence(char *A, char *B)
+{
+ int i, j, ri;
+ int m = strlen(A);
+ int n = strlen(B);
+ int L[m + 1][n + 1];
+ int tmp1, tmp2;
+ int lcs_length;
+ char *result;
+
+ for (i = m; i >= 0; i--) {
+ for (j = n; j >= 0; j--) {
+ if (A[i] == '\0' || B[j] == '\0') {
+ L[i][j] = 0;
+ } else if (A[i] == B[j]) {
+ L[i][j] = 1 + L[i + 1][j + 1];
+ } else {
+ tmp1 = L[i + 1][j];
+ tmp2 = L[i][j + 1];
+ L[i][j] = (tmp1 > tmp2 ? tmp1 : tmp2);
+ }
+ }
+ }
+
+ lcs_length = L[0][0];
+ result = xmalloc(lcs_length + 2);
+ memset(result, 0, sizeof(*result) * (lcs_length + 2));
+
+ ri = 0;
+ i = 0;
+ j = 0;
+ while (i < m && j < n) {
+ if (A[i] == B[j]) {
+ result[ri] = A[i];
+ ri += 1;
+ i += 1;
+ j += 1;
+ } else if (L[i + 1][j] >= L[i][j + 1]) {
+ i += 1;
+ } else {
+ j += 1;
+ }
+ }
+ return result;
+}
+
+static int line_from_hunk(char *line, char type)
+{
+ char *buf1, *buf2;
+ int len;
+
+ buf1 = strchr(line, type);
+ if (buf1 == NULL)
+ return 0;
+ buf1 += 1;
+ buf2 = strchr(buf1, ',');
+ if (buf2 == NULL)
+ return 0;
+ len = buf2 - buf1;
+ buf2 = xmalloc(len + 1);
+ strncpy(buf2, buf1, len);
+ buf2[len] = '\0';
+ int res = atoi(buf2);
+ free(buf2);
+ return res;
+}
+
+static char *replace_tabs(char *line)
+{
+ char *prev_buf = line;
+ char *cur_buf;
+ int linelen = strlen(line);
+ int n_tabs = 0;
+ int i;
+ char *result;
+ char *spaces = " ";
+
+ if (linelen == 0) {
+ result = xmalloc(1);
+ result[0] = '\0';
+ return result;
+ }
+
+ for (i = 0; i < linelen; i++)
+ if (line[i] == '\t')
+ n_tabs += 1;
+ result = xmalloc(linelen + n_tabs * 8 + 1);
+ result[0] = '\0';
+
+ while (1) {
+ cur_buf = strchr(prev_buf, '\t');
+ if (!cur_buf) {
+ strcat(result, prev_buf);
+ break;
+ } else {
+ strcat(result, " ");
+ strncat(result, spaces, 8 - (strlen(result) % 8));
+ strncat(result, prev_buf, cur_buf - prev_buf);
+ }
+ prev_buf = cur_buf + 1;
+ }
+ return result;
+}
+
+static int calc_deferred_lines(struct deferred_lines *start)
+{
+ struct deferred_lines *item = start;
+ int result = 0;
+ while (item) {
+ result += 1;
+ item = item->next;
+ }
+ return result;
+}
+
+static void deferred_old_add(char *line, int line_no)
+{
+ struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines));
+ item->line = xstrdup(line);
+ item->line_no = line_no;
+ item->next = NULL;
+ if (deferred_old) {
+ deferred_old_last->next = item;
+ deferred_old_last = item;
+ } else {
+ deferred_old = deferred_old_last = item;
+ }
+}
+
+static void deferred_new_add(char *line, int line_no)
+{
+ struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines));
+ item->line = xstrdup(line);
+ item->line_no = line_no;
+ item->next = NULL;
+ if (deferred_new) {
+ deferred_new_last->next = item;
+ deferred_new_last = item;
+ } else {
+ deferred_new = deferred_new_last = item;
+ }
+}
+
+static void print_part_with_lcs(char *class, char *line, char *lcs)
+{
+ int line_len = strlen(line);
+ int i, j;
+ char c[2] = " ";
+ int same = 1;
+
+ j = 0;
+ for (i = 0; i < line_len; i++) {
+ c[0] = line[i];
+ if (same) {
+ if (line[i] == lcs[j])
+ j += 1;
+ else {
+ same = 0;
+ htmlf("<span class='%s'>", class);
+ }
+ } else if (line[i] == lcs[j]) {
+ same = 1;
+ htmlf("</span>");
+ j += 1;
+ }
+ html_txt(c);
+ }
+}
+
+static void print_ssdiff_line(char *class,
+ int old_line_no,
+ char *old_line,
+ int new_line_no,
+ char *new_line, int individual_chars)
+{
+ char *lcs = NULL;
+ if (old_line)
+ old_line = replace_tabs(old_line + 1);
+ if (new_line)
+ new_line = replace_tabs(new_line + 1);
+ if (individual_chars && old_line && new_line)
+ lcs = longest_common_subsequence(old_line, new_line);
+ html("<tr>");
+ if (old_line_no > 0)
+ htmlf("<td class='lineno'>%d</td><td class='%s'>",
+ old_line_no, class);
+ else if (old_line)
+ htmlf("<td class='lineno'></td><td class='%s'>", class);
+ else
+ htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
+ if (old_line) {
+ if (lcs)
+ print_part_with_lcs("del", old_line, lcs);
+ else
+ html_txt(old_line);
+ }
+
+ html("</td>");
+ if (new_line_no > 0)
+ htmlf("<td class='lineno'>%d</td><td class='%s'>",
+ new_line_no, class);
+ else if (new_line)
+ htmlf("<td class='lineno'></td><td class='%s'>", class);
+ else
+ htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
+ if (new_line) {
+ if (lcs)
+ print_part_with_lcs("add", new_line, lcs);
+ else
+ html_txt(new_line);
+ }
+
+ html("</td></tr>");
+ if (lcs)
+ free(lcs);
+ if (new_line)
+ free(new_line);
+ if (old_line)
+ free(old_line);
+}
+
+static void print_deferred_old_lines()
+{
+ struct deferred_lines *iter_old, *tmp;
+ iter_old = deferred_old;
+ while (iter_old) {
+ print_ssdiff_line("del", iter_old->line_no,
+ iter_old->line, -1, NULL, 0);
+ tmp = iter_old->next;
+ free(iter_old);
+ iter_old = tmp;
+ }
+}
+
+static void print_deferred_new_lines()
+{
+ struct deferred_lines *iter_new, *tmp;
+ iter_new = deferred_new;
+ while (iter_new) {
+ print_ssdiff_line("add", -1, NULL,
+ iter_new->line_no, iter_new->line, 0);
+ tmp = iter_new->next;
+ free(iter_new);
+ iter_new = tmp;
+ }
+}
+
+static void print_deferred_changed_lines()
+{
+ struct deferred_lines *iter_old, *iter_new, *tmp;
+ int n_old_lines = calc_deferred_lines(deferred_old);
+ int n_new_lines = calc_deferred_lines(deferred_new);
+ int individual_chars = (n_old_lines == n_new_lines ? 1 : 0);
+
+ iter_old = deferred_old;
+ iter_new = deferred_new;
+ while (iter_old || iter_new) {
+ if (iter_old && iter_new)
+ print_ssdiff_line("changed", iter_old->line_no,
+ iter_old->line,
+ iter_new->line_no, iter_new->line,
+ individual_chars);
+ else if (iter_old)
+ print_ssdiff_line("changed", iter_old->line_no,
+ iter_old->line, -1, NULL, 0);
+ else if (iter_new)
+ print_ssdiff_line("changed", -1, NULL,
+ iter_new->line_no, iter_new->line, 0);
+ if (iter_old) {
+ tmp = iter_old->next;
+ free(iter_old);
+ iter_old = tmp;
+ }
+
+ if (iter_new) {
+ tmp = iter_new->next;
+ free(iter_new);
+ iter_new = tmp;
+ }
+ }
+}
+
+void cgit_ssdiff_print_deferred_lines()
+{
+ if (!deferred_old && !deferred_new)
+ return;
+ if (deferred_old && !deferred_new)
+ print_deferred_old_lines();
+ else if (!deferred_old && deferred_new)
+ print_deferred_new_lines();
+ else
+ print_deferred_changed_lines();
+ deferred_old = deferred_old_last = NULL;
+ deferred_new = deferred_new_last = NULL;
+}
+
+/*
+ * print a single line returned from xdiff
+ */
+void cgit_ssdiff_line_cb(char *line, int len)
+{
+ char c = line[len - 1];
+ line[len - 1] = '\0';
+ if (line[0] == '@') {
+ current_old_line = line_from_hunk(line, '-');
+ current_new_line = line_from_hunk(line, '+');
+ }
+
+ if (line[0] == ' ') {
+ if (deferred_old || deferred_new)
+ cgit_ssdiff_print_deferred_lines();
+ print_ssdiff_line("ctx", current_old_line, line,
+ current_new_line, line, 0);
+ current_old_line += 1;
+ current_new_line += 1;
+ } else if (line[0] == '+') {
+ deferred_new_add(line, current_new_line);
+ current_new_line += 1;
+ } else if (line[0] == '-') {
+ deferred_old_add(line, current_old_line);
+ current_old_line += 1;
+ } else if (line[0] == '@') {
+ html("<tr><td colspan='4' class='hunk'>");
+ html_txt(line);
+ html("</td></tr>");
+ } else {
+ html("<tr><td colspan='4' class='ctx'>");
+ html_txt(line);
+ html("</td></tr>");
+ }
+ line[len - 1] = c;
+}
+
+void cgit_ssdiff_header_begin()
+{
+ current_old_line = -1;
+ current_new_line = -1;
+ html("<tr><td class='space' colspan='4'><div></div></td></tr>");
+ html("<tr><td class='head' colspan='4'>");
+}
+
+void cgit_ssdiff_header_end()
+{
+ html("</td><tr>");
+}
+
+void cgit_ssdiff_footer()
+{
+ if (deferred_old || deferred_new)
+ cgit_ssdiff_print_deferred_lines();
+ html("<tr><td class='foot' colspan='4'></td></tr>");
+}
--- /dev/null
+#ifndef UI_SSDIFF_H
+#define UI_SSDIFF_H
+
+extern void cgit_ssdiff_print_deferred_lines();
+
+extern void cgit_ssdiff_line_cb(char *line, int len);
+
+extern void cgit_ssdiff_header_begin();
+extern void cgit_ssdiff_header_end();
+
+extern void cgit_ssdiff_footer();
+
+#endif /* UI_SSDIFF_H */
info = cgit_parse_commit(commit);
tmp = xstrdup(info->author);
- author = string_list_insert(tmp, authors);
+ author = string_list_insert(authors, tmp);
if (!author->util)
author->util = xcalloc(1, sizeof(struct authorstat));
else
date = gmtime(&t);
period->trunc(date);
tmp = xstrdup(period->pretty(date));
- item = string_list_insert(tmp, items);
+ item = string_list_insert(items, tmp);
if (item->util)
free(tmp);
item->util++;
author = &authors->items[i];
authorstat = author->util;
items = &authorstat->list;
- date = string_list_lookup(tmp, items);
+ date = string_list_lookup(items, tmp);
if (date)
subtotal += (size_t)date->util;
}
for (j = 0; j < period->count; j++) {
tmp = period->pretty(tm);
period->inc(tm);
- date = string_list_lookup(tmp, items);
+ date = string_list_lookup(items, tmp);
if (!date)
html("<td>0</td>");
else {
/* ui-summary.c: functions for generating repo summary page
*
* Copyright (C) 2006 Lars Hjemli
+ * Copyright (C) 2010 Jason A. Donenfeld <Jason@zx2c4.com>
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
#include "html.h"
#include "ui-log.h"
#include "ui-refs.h"
+#include "ui-blob.h"
int urls = 0;
void cgit_print_repo_readme(char *path)
{
- char *slash, *tmp;
+ char *slash, *tmp, *colon, *ref = 0;
if (!ctx.repo->readme)
return;
if (path) {
slash = strrchr(ctx.repo->readme, '/');
- if (!slash)
- return;
+ if (!slash) {
+ slash = strchr(ctx.repo->readme, ':');
+ if (!slash)
+ return;
+ }
tmp = xmalloc(slash - ctx.repo->readme + 1 + strlen(path) + 1);
strncpy(tmp, ctx.repo->readme, slash - ctx.repo->readme + 1);
strcpy(tmp + (slash - ctx.repo->readme + 1), path);
} else
tmp = ctx.repo->readme;
+ colon = strchr(tmp, ':');
+ if (colon && strlen(colon) > 1) {
+ *colon = '\0';
+ ref = tmp;
+ tmp = colon + 1;
+ while ((*tmp == '/' || *tmp == ':') && *tmp != '\0')
+ ++tmp;
+ if (!(*tmp))
+ return;
+ }
html("<div id='summary'>");
if (ctx.repo->about_filter)
cgit_open_filter(ctx.repo->about_filter);
- html_include(tmp);
+ if (ref)
+ cgit_print_file(tmp, ref);
+ else
+ html_include(tmp);
if (ctx.repo->about_filter)
cgit_close_filter(ctx.repo->about_filter);
html("</div>");
}
}
+void print_download_links(char *revname)
+{
+ html("<tr><th>download</th><td class='sha1'>");
+ cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head,
+ revname, ctx.repo->snapshots);
+ html("</td></tr>");
+}
+
void cgit_print_tag(char *revname)
{
unsigned char sha1[20];
return;
}
html("<table class='commit-info'>\n");
- htmlf("<tr><td>Tag name</td><td>");
+ htmlf("<tr><td>tag name</td><td>");
html_txt(revname);
htmlf(" (%s)</td></tr>\n", sha1_to_hex(sha1));
if (info->tagger_date > 0) {
- html("<tr><td>Tag date</td><td>");
+ html("<tr><td>tag date</td><td>");
cgit_print_date(info->tagger_date, FMT_LONGDATE, ctx.cfg.local_time);
html("</td></tr>\n");
}
if (info->tagger) {
- html("<tr><td>Tagged by</td><td>");
+ html("<tr><td>tagged by</td><td>");
html_txt(info->tagger);
if (info->tagger_email && !ctx.cfg.noplainemail) {
html(" ");
}
html("</td></tr>\n");
}
- html("<tr><td>Tagged object</td><td>");
+ html("<tr><td>tagged object</td><td class='sha1'>");
cgit_object_link(tag->tagged);
html("</td></tr>\n");
+ if (ctx.repo->snapshots)
+ print_download_links(revname);
html("</table>\n");
print_tag_content(info->msg);
} else {
html("<table class='commit-info'>\n");
- htmlf("<tr><td>Tag name</td><td>");
+ htmlf("<tr><td>tag name</td><td>");
html_txt(revname);
html("</td></tr>\n");
- html("<tr><td>Tagged object</td><td>");
+ html("<tr><td>Tagged object</td><td class='sha1'>");
cgit_object_link(obj);
html("</td></tr>\n");
+ if (ctx.repo->snapshots)
+ print_download_links(revname);
html("</table>\n");
}
return;
return;
}
- html(" (");
+ htmlf("blob: %s (", sha1_to_hex(sha1));
cgit_plain_link("plain", NULL, NULL, ctx.qry.head,
curr_rev, path);
- htmlf(")<br/>blob: %s\n", sha1_to_hex(sha1));
+ html(")\n");
+
+ if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) {
+ htmlf("<div class='error'>blob size (%dKB) exceeds display size limit (%dKB).</div>",
+ size / 1024, ctx.cfg.max_blob_size);
+ return;
+ }
if (buffer_is_binary(buf, size))
print_binary_buffer(buf, size);
if (ctx.repo->max_stats)
cgit_stats_link("stats", NULL, "button", ctx.qry.head,
fullpath);
+ cgit_plain_link("plain", NULL, "button", ctx.qry.head, curr_rev,
+ fullpath);
html("</td></tr>\n");
free(name);
return 0;
{
static int state;
static char buffer[PATH_MAX];
- char *url;
if (state == 0) {
memcpy(buffer, base, baselen);
strcpy(buffer+baselen, pathname);
- url = cgit_pageurl(ctx.qry.repo, "tree",
- fmt("h=%s&path=%s", curr_rev, buffer));
- html("/");
- cgit_tree_link(xstrdup(pathname), NULL, NULL, ctx.qry.head,
- curr_rev, buffer);
-
if (strcmp(match_path, buffer))
return READ_TREE_RECURSIVE;
return;
}
- html("path: <a href='");
- html_attr(cgit_pageurl(ctx.qry.repo, "tree", fmt("h=%s", rev)));
- html("'>root</a>");
-
if (path == NULL) {
ls_tree(commit->tree->object.sha1, NULL);
return;