summaryrefslogtreecommitdiffstatshomepage
path: root/cgit.h
blob: b7f8827f3e581b1d9de4aa9f98ebdad299e07c41 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#ifndef CGIT_H
#define CGIT_H


#include <git-compat-util.h>
#include <cache.h>
#include <grep.h>
#include <object.h>
#include <tree.h>
#include <commit.h>
#include <tag.h>
#include <diff.h>
#include <diffcore.h>
#include <refs.h>
#include <revision.h>
#include <log-tree.h>
#include <archive.h>
#include <xdiff/xdiff.h>


typedef void (*configfn)(const char *name, const char *value);

struct cacheitem {
	char *name;
	struct stat st;
	int ttl;
	int fd;
};

struct repoinfo {
	char *url;
	char *name;
	char *path;
	char *desc;
	char *owner;
	char *module_link;
	int snapshots;
};

struct repolist {
	int length;
	int count;
	struct repoinfo *repos;
};

struct commitinfo {
	struct commit *commit;
	char *author;
	char *author_email;
	unsigned long author_date;
	char *committer;
	char *committer_email;
	unsigned long committer_date;
	char *subject;
	char *msg;
};

struct taginfo {
	char *tagger;
	char *tagger_email;
	int tagger_date;
	char *msg;
};

extern const char cgit_version[];

extern struct repolist cgit_repolist;
extern struct repoinfo *cgit_repo;

extern char *cgit_root_title;
extern char *cgit_css;
extern char *cgit_logo;
extern char *cgit_logo_link;
extern char *cgit_module_link;
extern char *cgit_virtual_root;
extern char *cgit_cache_root;

extern int cgit_nocache;
extern int cgit_snapshots;
extern int cgit_max_lock_attempts;
extern int cgit_cache_root_ttl;
extern int cgit_cache_repo_ttl;
extern int cgit_cache_dynamic_ttl;
extern int cgit_cache_static_ttl;
extern int cgit_cache_max_create_time;

extern int cgit_max_msg_len;

extern char *cgit_repo_name;
extern char *cgit_repo_desc;
extern char *cgit_repo_owner;

extern int cgit_query_has_symref;
extern int cgit_query_has_sha1;

extern char *cgit_querystring;
extern char *cgit_query_repo;
extern char *cgit_query_page;
extern char *cgit_query_search;
extern char *cgit_query_head;
extern char *cgit_query_sha1;
extern char *cgit_query_sha2;
extern char *cgit_query_path;
extern char *cgit_query_name;
extern int   cgit_query_ofs;

extern int htmlfd;

extern void cgit_global_config_cb(const char *name, const char *value);
extern void cgit_repo_config_cb(const char *name, const char *value);
extern void cgit_querystring_cb(const char *name, const char *value);

extern int chk_zero(int result, char *msg);
extern int chk_positive(int result, char *msg);

extern int hextoint(char c);

extern void *cgit_free_commitinfo(struct commitinfo *info);

extern char *fmt(const char *format,...);

extern void html(const char *txt);
extern void htmlf(const char *format,...);
extern void html_txt(char *txt);
extern void html_ntxt(int len, char *txt);
extern void html_attr(char *txt);
extern void html_hidden(char *name, char *value);
extern void html_link_open(char *url, char *title, char *class);
extern void html_link_close(void);
extern void html_filemode(unsigned short mode);

extern int cgit_read_config(const char *filename, configfn fn);
extern int cgit_parse_query(char *txt, configfn fn);
extern struct commitinfo *cgit_parse_commit(struct commit *commit);
extern struct taginfo *cgit_parse_tag(struct tag *tag);

extern char *cache_safe_filename(const char *unsafe);
extern int cache_lock(struct cacheitem *item);
extern int cache_unlock(struct cacheitem *item);
extern int cache_cancel_lock(struct cacheitem *item);
extern int cache_exist(struct cacheitem *item);
extern int cache_expired(struct cacheitem *item);

extern char *cgit_repourl(const char *reponame);
extern char *cgit_pageurl(const char *reponame, const char *pagename, 
			  const char *query);

extern void cgit_print_error(char *msg);
extern void cgit_print_date(unsigned long secs);
extern void cgit_print_docstart(char *title, struct cacheitem *item);
extern void cgit_print_docend();
extern void cgit_print_pageheader(char *title, int show_search);
extern void cgit_print_snapshot_start(const char *mimetype, 
				      const char *filename, 
				      struct cacheitem *item);

extern void cgit_print_repolist(struct cacheitem *item);
extern void cgit_print_summary();
extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep);
extern void cgit_print_view(const char *hex, char *path);
extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path);
extern void cgit_print_tree(const char *hex, char *path);
extern void cgit_print_commit(const char *hex);
extern void cgit_print_diff(const char *old_hex, const char *new_hex);
extern void cgit_print_snapshot(struct cacheitem *item, const char *hex, 
				const char *format, const char *prefix,
				const char *filename);

#endif /* CGIT_H */
class="w"> { if (period) *period = &periods[i]; return i+1; } return 0; } const char *cgit_find_stats_periodname(int idx) { if (idx > 0 && idx < 4) return periods[idx - 1].name; else return ""; } static void add_commit(struct string_list *authors, struct commit *commit, struct cgit_period *period) { struct commitinfo *info; struct string_list_item *author, *item; struct authorstat *authorstat; struct string_list *items; char *tmp; struct tm *date; time_t t; info = cgit_parse_commit(commit); tmp = xstrdup(info->author); author = string_list_insert(authors, tmp); if (!author->util) author->util = xcalloc(1, sizeof(struct authorstat)); else free(tmp); authorstat = author->util; items = &authorstat->list; t = info->committer_date; date = gmtime(&t); period->trunc(date); tmp = xstrdup(period->pretty(date)); item = string_list_insert(items, tmp); if (item->util) free(tmp); item->util++; authorstat->total++; cgit_free_commitinfo(info); } static int cmp_total_commits(const void *a1, const void *a2) { const struct string_list_item *i1 = a1; const struct string_list_item *i2 = a2; const struct authorstat *auth1 = i1->util; const struct authorstat *auth2 = i2->util; return auth2->total - auth1->total; } /* Walk the commit DAG and collect number of commits per author per * timeperiod into a nested string_list collection. */ struct string_list collect_stats(struct cgit_context *ctx, struct cgit_period *period) { struct string_list authors; struct rev_info rev; struct commit *commit; const char *argv[] = {NULL, ctx->qry.head, NULL, NULL, NULL, NULL}; int argc = 3; time_t now; long i; struct tm *tm; char tmp[11]; time(&now); tm = gmtime(&now); period->trunc(tm); for (i = 1; i < period->count; i++) period->dec(tm); strftime(tmp, sizeof(tmp), "%Y-%m-%d", tm); argv[2] = xstrdup(fmt("--since=%s", tmp)); if (ctx->qry.path) { argv[3] = "--"; argv[4] = ctx->qry.path; argc += 2; } init_revisions(&rev, NULL); rev.abbrev = DEFAULT_ABBREV; rev.commit_format = CMIT_FMT_DEFAULT; rev.no_merges = 1; rev.verbose_header = 1; rev.show_root_diff = 0; setup_revisions(argc, argv, &rev, NULL); prepare_revision_walk(&rev); memset(&authors, 0, sizeof(authors)); while ((commit = get_revision(&rev)) != NULL) { add_commit(&authors, commit, period); free(commit->buffer); free_commit_list(commit->parents); } return authors; } void print_combined_authorrow(struct string_list *authors, int from, int to, const char *name, const char *leftclass, const char *centerclass, const char *rightclass, struct cgit_period *period) { struct string_list_item *author; struct authorstat *authorstat; struct string_list *items; struct string_list_item *date; time_t now; long i, j, total, subtotal; struct tm *tm; char *tmp; time(&now); tm = gmtime(&now); period->trunc(tm); for (i = 1; i < period->count; i++) period->dec(tm); total = 0; htmlf("<tr><td class='%s'>%s</td>", leftclass, fmt(name, to - from + 1)); for (j = 0; j < period->count; j++) { tmp = period->pretty(tm); period->inc(tm); subtotal = 0; for (i = from; i <= to; i++) { author = &authors->items[i]; authorstat = author->util; items = &authorstat->list; date = string_list_lookup(items, tmp); if (date) subtotal += (size_t)date->util; } htmlf("<td class='%s'>%ld</td>", centerclass, subtotal); total += subtotal; } htmlf("<td class='%s'>%ld</td></tr>", rightclass, total); } void print_authors(struct string_list *authors, int top, struct cgit_period *period) { struct string_list_item *author; struct authorstat *authorstat; struct string_list *items; struct string_list_item *date; time_t now; long i, j, total; struct tm *tm; char *tmp; time(&now); tm = gmtime(&now); period->trunc(tm); for (i = 1; i < period->count; i++) period->dec(tm); html("<table class='stats'><tr><th>Author</th>"); for (j = 0; j < period->count; j++) { tmp = period->pretty(tm); htmlf("<th>%s</th>", tmp); period->inc(tm); } html("<th>Total</th></tr>\n"); if (top <= 0 || top > authors->nr) top = authors->nr; for (i = 0; i < top; i++) { author = &authors->items[i]; html("<tr><td class='left'>"); html_txt(author->string); html("</td>"); authorstat = author->util; items = &authorstat->list; total = 0; for (j = 0; j < period->count; j++) period->dec(tm); for (j = 0; j < period->count; j++) { tmp = period->pretty(tm); period->inc(tm); date = string_list_lookup(items, tmp); if (!date) html("<td>0</td>"); else { htmlf("<td>"SZ_FMT"</td>", (size_t)date->util); total += (size_t)date->util; } } htmlf("<td class='sum'>%ld</td></tr>", total); } if (top < authors->nr) print_combined_authorrow(authors, top, authors->nr - 1, "Others (%ld)", "left", "", "sum", period); print_combined_authorrow(authors, 0, authors->nr - 1, "Total", "total", "sum", "sum", period); html("</table>"); } /* Create a sorted string_list with one entry per author. The util-field * for each author is another string_list which is used to calculate the * number of commits per time-interval. */ void cgit_show_stats(struct cgit_context *ctx) { struct string_list authors; struct cgit_period *period; int top, i; const char *code = "w"; if (ctx->qry.period) code = ctx->qry.period; i = cgit_find_stats_period(code, &period); if (!i) { cgit_print_error(fmt("Unknown statistics type: %c", code[0])); return; } if (i > ctx->repo->max_stats) { cgit_print_error(fmt("Statistics type disabled: %s", period->name)); return; } authors = collect_stats(ctx, period); qsort(authors.items, authors.nr, sizeof(struct string_list_item), cmp_total_commits); top = ctx->qry.ofs; if (!top) top = 10; html("<div class='cgit-panel'>"); html("<b>stat options</b>"); html("<form method='get' action=''>"); cgit_add_hidden_formfields(1, 0, "stats"); html("<table><tr><td colspan='2'/></tr>"); if (ctx->repo->max_stats > 1) { html("<tr><td class='label'>Period:</td>"); html("<td class='ctrl'><select name='period' onchange='this.form.submit();'>"); for (i = 0; i < ctx->repo->max_stats; i++) html_option(fmt("%c", periods[i].code), periods[i].name, fmt("%c", period->code)); html("</select></td></tr>"); } html("<tr><td class='label'>Authors:</td>"); html("<td class='ctrl'><select name='ofs' onchange='this.form.submit();'>"); html_intoption(10, "10", top); html_intoption(25, "25", top); html_intoption(50, "50", top); html_intoption(100, "100", top); html_intoption(-1, "all", top); html("</select></td></tr>"); html("<tr><td/><td class='ctrl'>"); html("<noscript><input type='submit' value='Reload'/></noscript>"); html("</td></tr></table>"); html("</form>"); html("</div>"); htmlf("<h2>Commits per author per %s", period->name); if (ctx->qry.path) { html(" (path '"); html_txt(ctx->qry.path); html("')"); } html("</h2>"); print_authors(&authors, top, period); }