summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c4
-rw-r--r--cgit.h2
-rw-r--r--cgitrc.5.txt9
-rw-r--r--shared.c1
-rw-r--r--ui-refs.c2
5 files changed, 18 insertions, 0 deletions
diff --git a/cgit.c b/cgit.c
index 4f68a4b..e46c00a 100644
--- a/cgit.c
+++ b/cgit.c
@@ -15,173 +15,177 @@
15#include "ui-stats.h" 15#include "ui-stats.h"
16#include "scan-tree.h" 16#include "scan-tree.h"
17 17
18const char *cgit_version = CGIT_VERSION; 18const char *cgit_version = CGIT_VERSION;
19 19
20void add_mimetype(const char *name, const char *value) 20void add_mimetype(const char *name, const char *value)
21{ 21{
22 struct string_list_item *item; 22 struct string_list_item *item;
23 23
24 item = string_list_insert(xstrdup(name), &ctx.cfg.mimetypes); 24 item = string_list_insert(xstrdup(name), &ctx.cfg.mimetypes);
25 item->util = xstrdup(value); 25 item->util = xstrdup(value);
26} 26}
27 27
28struct cgit_filter *new_filter(const char *cmd, int extra_args) 28struct cgit_filter *new_filter(const char *cmd, int extra_args)
29{ 29{
30 struct cgit_filter *f; 30 struct cgit_filter *f;
31 31
32 if (!cmd || !cmd[0]) 32 if (!cmd || !cmd[0])
33 return NULL; 33 return NULL;
34 34
35 f = xmalloc(sizeof(struct cgit_filter)); 35 f = xmalloc(sizeof(struct cgit_filter));
36 f->cmd = xstrdup(cmd); 36 f->cmd = xstrdup(cmd);
37 f->argv = xmalloc((2 + extra_args) * sizeof(char *)); 37 f->argv = xmalloc((2 + extra_args) * sizeof(char *));
38 f->argv[0] = f->cmd; 38 f->argv[0] = f->cmd;
39 f->argv[1] = NULL; 39 f->argv[1] = NULL;
40 return f; 40 return f;
41} 41}
42 42
43static void process_cached_repolist(const char *path); 43static void process_cached_repolist(const char *path);
44 44
45void repo_config(struct cgit_repo *repo, const char *name, const char *value) 45void repo_config(struct cgit_repo *repo, const char *name, const char *value)
46{ 46{
47 if (!strcmp(name, "name")) 47 if (!strcmp(name, "name"))
48 repo->name = xstrdup(value); 48 repo->name = xstrdup(value);
49 else if (!strcmp(name, "clone-url")) 49 else if (!strcmp(name, "clone-url"))
50 repo->clone_url = xstrdup(value); 50 repo->clone_url = xstrdup(value);
51 else if (!strcmp(name, "desc")) 51 else if (!strcmp(name, "desc"))
52 repo->desc = xstrdup(value); 52 repo->desc = xstrdup(value);
53 else if (!strcmp(name, "owner")) 53 else if (!strcmp(name, "owner"))
54 repo->owner = xstrdup(value); 54 repo->owner = xstrdup(value);
55 else if (!strcmp(name, "defbranch")) 55 else if (!strcmp(name, "defbranch"))
56 repo->defbranch = xstrdup(value); 56 repo->defbranch = xstrdup(value);
57 else if (!strcmp(name, "snapshots")) 57 else if (!strcmp(name, "snapshots"))
58 repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); 58 repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value);
59 else if (!strcmp(name, "enable-log-filecount")) 59 else if (!strcmp(name, "enable-log-filecount"))
60 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 60 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
61 else if (!strcmp(name, "enable-log-linecount")) 61 else if (!strcmp(name, "enable-log-linecount"))
62 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 62 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
63 else if (!strcmp(name, "enable-remote-branches"))
64 repo->enable_remote_branches = atoi(value);
63 else if (!strcmp(name, "max-stats")) 65 else if (!strcmp(name, "max-stats"))
64 repo->max_stats = cgit_find_stats_period(value, NULL); 66 repo->max_stats = cgit_find_stats_period(value, NULL);
65 else if (!strcmp(name, "module-link")) 67 else if (!strcmp(name, "module-link"))
66 repo->module_link= xstrdup(value); 68 repo->module_link= xstrdup(value);
67 else if (!strcmp(name, "section")) 69 else if (!strcmp(name, "section"))
68 repo->section = xstrdup(value); 70 repo->section = xstrdup(value);
69 else if (!strcmp(name, "readme") && value != NULL) { 71 else if (!strcmp(name, "readme") && value != NULL) {
70 if (*value == '/') 72 if (*value == '/')
71 repo->readme = xstrdup(value); 73 repo->readme = xstrdup(value);
72 else 74 else
73 repo->readme = xstrdup(fmt("%s/%s", repo->path, value)); 75 repo->readme = xstrdup(fmt("%s/%s", repo->path, value));
74 } else if (ctx.cfg.enable_filter_overrides) { 76 } else if (ctx.cfg.enable_filter_overrides) {
75 if (!strcmp(name, "about-filter")) 77 if (!strcmp(name, "about-filter"))
76 repo->about_filter = new_filter(value, 0); 78 repo->about_filter = new_filter(value, 0);
77 else if (!strcmp(name, "commit-filter")) 79 else if (!strcmp(name, "commit-filter"))
78 repo->commit_filter = new_filter(value, 0); 80 repo->commit_filter = new_filter(value, 0);
79 else if (!strcmp(name, "source-filter")) 81 else if (!strcmp(name, "source-filter"))
80 repo->source_filter = new_filter(value, 1); 82 repo->source_filter = new_filter(value, 1);
81 } 83 }
82} 84}
83 85
84void config_cb(const char *name, const char *value) 86void config_cb(const char *name, const char *value)
85{ 87{
86 if (!strcmp(name, "section") || !strcmp(name, "repo.group")) 88 if (!strcmp(name, "section") || !strcmp(name, "repo.group"))
87 ctx.cfg.section = xstrdup(value); 89 ctx.cfg.section = xstrdup(value);
88 else if (!strcmp(name, "repo.url")) 90 else if (!strcmp(name, "repo.url"))
89 ctx.repo = cgit_add_repo(value); 91 ctx.repo = cgit_add_repo(value);
90 else if (ctx.repo && !strcmp(name, "repo.path")) 92 else if (ctx.repo && !strcmp(name, "repo.path"))
91 ctx.repo->path = trim_end(value, '/'); 93 ctx.repo->path = trim_end(value, '/');
92 else if (ctx.repo && !prefixcmp(name, "repo.")) 94 else if (ctx.repo && !prefixcmp(name, "repo."))
93 repo_config(ctx.repo, name + 5, value); 95 repo_config(ctx.repo, name + 5, value);
94 else if (!strcmp(name, "root-title")) 96 else if (!strcmp(name, "root-title"))
95 ctx.cfg.root_title = xstrdup(value); 97 ctx.cfg.root_title = xstrdup(value);
96 else if (!strcmp(name, "root-desc")) 98 else if (!strcmp(name, "root-desc"))
97 ctx.cfg.root_desc = xstrdup(value); 99 ctx.cfg.root_desc = xstrdup(value);
98 else if (!strcmp(name, "root-readme")) 100 else if (!strcmp(name, "root-readme"))
99 ctx.cfg.root_readme = xstrdup(value); 101 ctx.cfg.root_readme = xstrdup(value);
100 else if (!strcmp(name, "css")) 102 else if (!strcmp(name, "css"))
101 ctx.cfg.css = xstrdup(value); 103 ctx.cfg.css = xstrdup(value);
102 else if (!strcmp(name, "favicon")) 104 else if (!strcmp(name, "favicon"))
103 ctx.cfg.favicon = xstrdup(value); 105 ctx.cfg.favicon = xstrdup(value);
104 else if (!strcmp(name, "footer")) 106 else if (!strcmp(name, "footer"))
105 ctx.cfg.footer = xstrdup(value); 107 ctx.cfg.footer = xstrdup(value);
106 else if (!strcmp(name, "head-include")) 108 else if (!strcmp(name, "head-include"))
107 ctx.cfg.head_include = xstrdup(value); 109 ctx.cfg.head_include = xstrdup(value);
108 else if (!strcmp(name, "header")) 110 else if (!strcmp(name, "header"))
109 ctx.cfg.header = xstrdup(value); 111 ctx.cfg.header = xstrdup(value);
110 else if (!strcmp(name, "logo")) 112 else if (!strcmp(name, "logo"))
111 ctx.cfg.logo = xstrdup(value); 113 ctx.cfg.logo = xstrdup(value);
112 else if (!strcmp(name, "index-header")) 114 else if (!strcmp(name, "index-header"))
113 ctx.cfg.index_header = xstrdup(value); 115 ctx.cfg.index_header = xstrdup(value);
114 else if (!strcmp(name, "index-info")) 116 else if (!strcmp(name, "index-info"))
115 ctx.cfg.index_info = xstrdup(value); 117 ctx.cfg.index_info = xstrdup(value);
116 else if (!strcmp(name, "logo-link")) 118 else if (!strcmp(name, "logo-link"))
117 ctx.cfg.logo_link = xstrdup(value); 119 ctx.cfg.logo_link = xstrdup(value);
118 else if (!strcmp(name, "module-link")) 120 else if (!strcmp(name, "module-link"))
119 ctx.cfg.module_link = xstrdup(value); 121 ctx.cfg.module_link = xstrdup(value);
120 else if (!strcmp(name, "virtual-root")) { 122 else if (!strcmp(name, "virtual-root")) {
121 ctx.cfg.virtual_root = trim_end(value, '/'); 123 ctx.cfg.virtual_root = trim_end(value, '/');
122 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 124 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
123 ctx.cfg.virtual_root = ""; 125 ctx.cfg.virtual_root = "";
124 } else if (!strcmp(name, "nocache")) 126 } else if (!strcmp(name, "nocache"))
125 ctx.cfg.nocache = atoi(value); 127 ctx.cfg.nocache = atoi(value);
126 else if (!strcmp(name, "noplainemail")) 128 else if (!strcmp(name, "noplainemail"))
127 ctx.cfg.noplainemail = atoi(value); 129 ctx.cfg.noplainemail = atoi(value);
128 else if (!strcmp(name, "noheader")) 130 else if (!strcmp(name, "noheader"))
129 ctx.cfg.noheader = atoi(value); 131 ctx.cfg.noheader = atoi(value);
130 else if (!strcmp(name, "snapshots")) 132 else if (!strcmp(name, "snapshots"))
131 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 133 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
132 else if (!strcmp(name, "enable-filter-overrides")) 134 else if (!strcmp(name, "enable-filter-overrides"))
133 ctx.cfg.enable_filter_overrides = atoi(value); 135 ctx.cfg.enable_filter_overrides = atoi(value);
134 else if (!strcmp(name, "enable-index-links")) 136 else if (!strcmp(name, "enable-index-links"))
135 ctx.cfg.enable_index_links = atoi(value); 137 ctx.cfg.enable_index_links = atoi(value);
136 else if (!strcmp(name, "enable-log-filecount")) 138 else if (!strcmp(name, "enable-log-filecount"))
137 ctx.cfg.enable_log_filecount = atoi(value); 139 ctx.cfg.enable_log_filecount = atoi(value);
138 else if (!strcmp(name, "enable-log-linecount")) 140 else if (!strcmp(name, "enable-log-linecount"))
139 ctx.cfg.enable_log_linecount = atoi(value); 141 ctx.cfg.enable_log_linecount = atoi(value);
142 else if (!strcmp(name, "enable-remote-branches"))
143 ctx.cfg.enable_remote_branches = atoi(value);
140 else if (!strcmp(name, "enable-tree-linenumbers")) 144 else if (!strcmp(name, "enable-tree-linenumbers"))
141 ctx.cfg.enable_tree_linenumbers = atoi(value); 145 ctx.cfg.enable_tree_linenumbers = atoi(value);
142 else if (!strcmp(name, "max-stats")) 146 else if (!strcmp(name, "max-stats"))
143 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 147 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
144 else if (!strcmp(name, "cache-size")) 148 else if (!strcmp(name, "cache-size"))
145 ctx.cfg.cache_size = atoi(value); 149 ctx.cfg.cache_size = atoi(value);
146 else if (!strcmp(name, "cache-root")) 150 else if (!strcmp(name, "cache-root"))
147 ctx.cfg.cache_root = xstrdup(value); 151 ctx.cfg.cache_root = xstrdup(value);
148 else if (!strcmp(name, "cache-root-ttl")) 152 else if (!strcmp(name, "cache-root-ttl"))
149 ctx.cfg.cache_root_ttl = atoi(value); 153 ctx.cfg.cache_root_ttl = atoi(value);
150 else if (!strcmp(name, "cache-repo-ttl")) 154 else if (!strcmp(name, "cache-repo-ttl"))
151 ctx.cfg.cache_repo_ttl = atoi(value); 155 ctx.cfg.cache_repo_ttl = atoi(value);
152 else if (!strcmp(name, "cache-scanrc-ttl")) 156 else if (!strcmp(name, "cache-scanrc-ttl"))
153 ctx.cfg.cache_scanrc_ttl = atoi(value); 157 ctx.cfg.cache_scanrc_ttl = atoi(value);
154 else if (!strcmp(name, "cache-static-ttl")) 158 else if (!strcmp(name, "cache-static-ttl"))
155 ctx.cfg.cache_static_ttl = atoi(value); 159 ctx.cfg.cache_static_ttl = atoi(value);
156 else if (!strcmp(name, "cache-dynamic-ttl")) 160 else if (!strcmp(name, "cache-dynamic-ttl"))
157 ctx.cfg.cache_dynamic_ttl = atoi(value); 161 ctx.cfg.cache_dynamic_ttl = atoi(value);
158 else if (!strcmp(name, "about-filter")) 162 else if (!strcmp(name, "about-filter"))
159 ctx.cfg.about_filter = new_filter(value, 0); 163 ctx.cfg.about_filter = new_filter(value, 0);
160 else if (!strcmp(name, "commit-filter")) 164 else if (!strcmp(name, "commit-filter"))
161 ctx.cfg.commit_filter = new_filter(value, 0); 165 ctx.cfg.commit_filter = new_filter(value, 0);
162 else if (!strcmp(name, "embedded")) 166 else if (!strcmp(name, "embedded"))
163 ctx.cfg.embedded = atoi(value); 167 ctx.cfg.embedded = atoi(value);
164 else if (!strcmp(name, "max-message-length")) 168 else if (!strcmp(name, "max-message-length"))
165 ctx.cfg.max_msg_len = atoi(value); 169 ctx.cfg.max_msg_len = atoi(value);
166 else if (!strcmp(name, "max-repodesc-length")) 170 else if (!strcmp(name, "max-repodesc-length"))
167 ctx.cfg.max_repodesc_len = atoi(value); 171 ctx.cfg.max_repodesc_len = atoi(value);
168 else if (!strcmp(name, "max-blob-size")) 172 else if (!strcmp(name, "max-blob-size"))
169 ctx.cfg.max_blob_size = atoi(value); 173 ctx.cfg.max_blob_size = atoi(value);
170 else if (!strcmp(name, "max-repo-count")) 174 else if (!strcmp(name, "max-repo-count"))
171 ctx.cfg.max_repo_count = atoi(value); 175 ctx.cfg.max_repo_count = atoi(value);
172 else if (!strcmp(name, "max-commit-count")) 176 else if (!strcmp(name, "max-commit-count"))
173 ctx.cfg.max_commit_count = atoi(value); 177 ctx.cfg.max_commit_count = atoi(value);
174 else if (!strcmp(name, "scan-path")) 178 else if (!strcmp(name, "scan-path"))
175 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 179 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
176 process_cached_repolist(value); 180 process_cached_repolist(value);
177 else 181 else
178 scan_tree(value, repo_config); 182 scan_tree(value, repo_config);
179 else if (!strcmp(name, "source-filter")) 183 else if (!strcmp(name, "source-filter"))
180 ctx.cfg.source_filter = new_filter(value, 1); 184 ctx.cfg.source_filter = new_filter(value, 1);
181 else if (!strcmp(name, "summary-log")) 185 else if (!strcmp(name, "summary-log"))
182 ctx.cfg.summary_log = atoi(value); 186 ctx.cfg.summary_log = atoi(value);
183 else if (!strcmp(name, "summary-branches")) 187 else if (!strcmp(name, "summary-branches"))
184 ctx.cfg.summary_branches = atoi(value); 188 ctx.cfg.summary_branches = atoi(value);
185 else if (!strcmp(name, "summary-tags")) 189 else if (!strcmp(name, "summary-tags"))
186 ctx.cfg.summary_tags = atoi(value); 190 ctx.cfg.summary_tags = atoi(value);
187 else if (!strcmp(name, "side-by-side-diffs")) 191 else if (!strcmp(name, "side-by-side-diffs"))
diff --git a/cgit.h b/cgit.h
index 5941ec0..cd4af72 100644
--- a/cgit.h
+++ b/cgit.h
@@ -27,96 +27,97 @@
27#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)" 27#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)"
28#define FMT_SHORTDATE "%Y-%m-%d" 28#define FMT_SHORTDATE "%Y-%m-%d"
29#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ" 29#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ"
30 30
31 31
32/* 32/*
33 * Limits used for relative dates 33 * Limits used for relative dates
34 */ 34 */
35#define TM_MIN 60 35#define TM_MIN 60
36#define TM_HOUR (TM_MIN * 60) 36#define TM_HOUR (TM_MIN * 60)
37#define TM_DAY (TM_HOUR * 24) 37#define TM_DAY (TM_HOUR * 24)
38#define TM_WEEK (TM_DAY * 7) 38#define TM_WEEK (TM_DAY * 7)
39#define TM_YEAR (TM_DAY * 365) 39#define TM_YEAR (TM_DAY * 365)
40#define TM_MONTH (TM_YEAR / 12.0) 40#define TM_MONTH (TM_YEAR / 12.0)
41 41
42 42
43/* 43/*
44 * Default encoding 44 * Default encoding
45 */ 45 */
46#define PAGE_ENCODING "UTF-8" 46#define PAGE_ENCODING "UTF-8"
47 47
48typedef void (*configfn)(const char *name, const char *value); 48typedef void (*configfn)(const char *name, const char *value);
49typedef void (*filepair_fn)(struct diff_filepair *pair); 49typedef void (*filepair_fn)(struct diff_filepair *pair);
50typedef void (*linediff_fn)(char *line, int len); 50typedef void (*linediff_fn)(char *line, int len);
51 51
52struct cgit_filter { 52struct cgit_filter {
53 char *cmd; 53 char *cmd;
54 char **argv; 54 char **argv;
55 int old_stdout; 55 int old_stdout;
56 int pipe_fh[2]; 56 int pipe_fh[2];
57 int pid; 57 int pid;
58 int exitstatus; 58 int exitstatus;
59}; 59};
60 60
61struct cgit_repo { 61struct cgit_repo {
62 char *url; 62 char *url;
63 char *name; 63 char *name;
64 char *path; 64 char *path;
65 char *desc; 65 char *desc;
66 char *owner; 66 char *owner;
67 char *defbranch; 67 char *defbranch;
68 char *module_link; 68 char *module_link;
69 char *readme; 69 char *readme;
70 char *section; 70 char *section;
71 char *clone_url; 71 char *clone_url;
72 int snapshots; 72 int snapshots;
73 int enable_log_filecount; 73 int enable_log_filecount;
74 int enable_log_linecount; 74 int enable_log_linecount;
75 int enable_remote_branches;
75 int max_stats; 76 int max_stats;
76 time_t mtime; 77 time_t mtime;
77 struct cgit_filter *about_filter; 78 struct cgit_filter *about_filter;
78 struct cgit_filter *commit_filter; 79 struct cgit_filter *commit_filter;
79 struct cgit_filter *source_filter; 80 struct cgit_filter *source_filter;
80}; 81};
81 82
82typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name, 83typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name,
83 const char *value); 84 const char *value);
84 85
85struct cgit_repolist { 86struct cgit_repolist {
86 int length; 87 int length;
87 int count; 88 int count;
88 struct cgit_repo *repos; 89 struct cgit_repo *repos;
89}; 90};
90 91
91struct commitinfo { 92struct commitinfo {
92 struct commit *commit; 93 struct commit *commit;
93 char *author; 94 char *author;
94 char *author_email; 95 char *author_email;
95 unsigned long author_date; 96 unsigned long author_date;
96 char *committer; 97 char *committer;
97 char *committer_email; 98 char *committer_email;
98 unsigned long committer_date; 99 unsigned long committer_date;
99 char *subject; 100 char *subject;
100 char *msg; 101 char *msg;
101 char *msg_encoding; 102 char *msg_encoding;
102}; 103};
103 104
104struct taginfo { 105struct taginfo {
105 char *tagger; 106 char *tagger;
106 char *tagger_email; 107 char *tagger_email;
107 unsigned long tagger_date; 108 unsigned long tagger_date;
108 char *msg; 109 char *msg;
109}; 110};
110 111
111struct refinfo { 112struct refinfo {
112 const char *refname; 113 const char *refname;
113 struct object *object; 114 struct object *object;
114 union { 115 union {
115 struct taginfo *tag; 116 struct taginfo *tag;
116 struct commitinfo *commit; 117 struct commitinfo *commit;
117 }; 118 };
118}; 119};
119 120
120struct reflist { 121struct reflist {
121 struct refinfo **refs; 122 struct refinfo **refs;
122 int alloc; 123 int alloc;
@@ -134,96 +135,97 @@ struct cgit_query {
134 char *head; 135 char *head;
135 char *sha1; 136 char *sha1;
136 char *sha2; 137 char *sha2;
137 char *path; 138 char *path;
138 char *name; 139 char *name;
139 char *mimetype; 140 char *mimetype;
140 char *url; 141 char *url;
141 char *period; 142 char *period;
142 int ofs; 143 int ofs;
143 int nohead; 144 int nohead;
144 char *sort; 145 char *sort;
145 int showmsg; 146 int showmsg;
146 int ssdiff; 147 int ssdiff;
147}; 148};
148 149
149struct cgit_config { 150struct cgit_config {
150 char *agefile; 151 char *agefile;
151 char *cache_root; 152 char *cache_root;
152 char *clone_prefix; 153 char *clone_prefix;
153 char *css; 154 char *css;
154 char *favicon; 155 char *favicon;
155 char *footer; 156 char *footer;
156 char *head_include; 157 char *head_include;
157 char *header; 158 char *header;
158 char *index_header; 159 char *index_header;
159 char *index_info; 160 char *index_info;
160 char *logo; 161 char *logo;
161 char *logo_link; 162 char *logo_link;
162 char *module_link; 163 char *module_link;
163 char *robots; 164 char *robots;
164 char *root_title; 165 char *root_title;
165 char *root_desc; 166 char *root_desc;
166 char *root_readme; 167 char *root_readme;
167 char *script_name; 168 char *script_name;
168 char *section; 169 char *section;
169 char *virtual_root; 170 char *virtual_root;
170 int cache_size; 171 int cache_size;
171 int cache_dynamic_ttl; 172 int cache_dynamic_ttl;
172 int cache_max_create_time; 173 int cache_max_create_time;
173 int cache_repo_ttl; 174 int cache_repo_ttl;
174 int cache_root_ttl; 175 int cache_root_ttl;
175 int cache_scanrc_ttl; 176 int cache_scanrc_ttl;
176 int cache_static_ttl; 177 int cache_static_ttl;
177 int embedded; 178 int embedded;
178 int enable_filter_overrides; 179 int enable_filter_overrides;
179 int enable_index_links; 180 int enable_index_links;
180 int enable_log_filecount; 181 int enable_log_filecount;
181 int enable_log_linecount; 182 int enable_log_linecount;
183 int enable_remote_branches;
182 int enable_tree_linenumbers; 184 int enable_tree_linenumbers;
183 int local_time; 185 int local_time;
184 int max_repo_count; 186 int max_repo_count;
185 int max_commit_count; 187 int max_commit_count;
186 int max_lock_attempts; 188 int max_lock_attempts;
187 int max_msg_len; 189 int max_msg_len;
188 int max_repodesc_len; 190 int max_repodesc_len;
189 int max_blob_size; 191 int max_blob_size;
190 int max_stats; 192 int max_stats;
191 int nocache; 193 int nocache;
192 int noplainemail; 194 int noplainemail;
193 int noheader; 195 int noheader;
194 int renamelimit; 196 int renamelimit;
195 int snapshots; 197 int snapshots;
196 int summary_branches; 198 int summary_branches;
197 int summary_log; 199 int summary_log;
198 int summary_tags; 200 int summary_tags;
199 int ssdiff; 201 int ssdiff;
200 struct string_list mimetypes; 202 struct string_list mimetypes;
201 struct cgit_filter *about_filter; 203 struct cgit_filter *about_filter;
202 struct cgit_filter *commit_filter; 204 struct cgit_filter *commit_filter;
203 struct cgit_filter *source_filter; 205 struct cgit_filter *source_filter;
204}; 206};
205 207
206struct cgit_page { 208struct cgit_page {
207 time_t modified; 209 time_t modified;
208 time_t expires; 210 time_t expires;
209 size_t size; 211 size_t size;
210 char *mimetype; 212 char *mimetype;
211 char *charset; 213 char *charset;
212 char *filename; 214 char *filename;
213 char *etag; 215 char *etag;
214 char *title; 216 char *title;
215 int status; 217 int status;
216 char *statusmsg; 218 char *statusmsg;
217}; 219};
218 220
219struct cgit_environment { 221struct cgit_environment {
220 char *cgit_config; 222 char *cgit_config;
221 char *http_host; 223 char *http_host;
222 char *https; 224 char *https;
223 char *no_http; 225 char *no_http;
224 char *path_info; 226 char *path_info;
225 char *query_string; 227 char *query_string;
226 char *request_method; 228 char *request_method;
227 char *script_name; 229 char *script_name;
228 char *server_name; 230 char *server_name;
229 char *server_port; 231 char *server_port;
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 70e4c78..d74d9e7 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -65,96 +65,101 @@ cache-size::
65 The maximum number of entries in the cgit cache. Default value: "0" 65 The maximum number of entries in the cgit cache. Default value: "0"
66 (i.e. caching is disabled). 66 (i.e. caching is disabled).
67 67
68cache-static-ttl:: 68cache-static-ttl::
69 Number which specifies the time-to-live, in minutes, for the cached 69 Number which specifies the time-to-live, in minutes, for the cached
70 version of repository pages accessed with a fixed SHA1. Default value: 70 version of repository pages accessed with a fixed SHA1. Default value:
71 "5". 71 "5".
72 72
73clone-prefix:: 73clone-prefix::
74 Space-separated list of common prefixes which, when combined with a 74 Space-separated list of common prefixes which, when combined with a
75 repository url, generates valid clone urls for the repository. This 75 repository url, generates valid clone urls for the repository. This
76 setting is only used if `repo.clone-url` is unspecified. Default value: 76 setting is only used if `repo.clone-url` is unspecified. Default value:
77 none. 77 none.
78 78
79commit-filter:: 79commit-filter::
80 Specifies a command which will be invoked to format commit messages. 80 Specifies a command which will be invoked to format commit messages.
81 The command will get the message on its STDIN, and the STDOUT from the 81 The command will get the message on its STDIN, and the STDOUT from the
82 command will be included verbatim as the commit message, i.e. this can 82 command will be included verbatim as the commit message, i.e. this can
83 be used to implement bugtracker integration. Default value: none. 83 be used to implement bugtracker integration. Default value: none.
84 84
85css:: 85css::
86 Url which specifies the css document to include in all cgit pages. 86 Url which specifies the css document to include in all cgit pages.
87 Default value: "/cgit.css". 87 Default value: "/cgit.css".
88 88
89embedded:: 89embedded::
90 Flag which, when set to "1", will make cgit generate a html fragment 90 Flag which, when set to "1", will make cgit generate a html fragment
91 suitable for embedding in other html pages. Default value: none. See 91 suitable for embedding in other html pages. Default value: none. See
92 also: "noheader". 92 also: "noheader".
93 93
94enable-filter-overrides:: 94enable-filter-overrides::
95 Flag which, when set to "1", allows all filter settings to be 95 Flag which, when set to "1", allows all filter settings to be
96 overridden in repository-specific cgitrc files. Default value: none. 96 overridden in repository-specific cgitrc files. Default value: none.
97 97
98enable-index-links:: 98enable-index-links::
99 Flag which, when set to "1", will make cgit generate extra links for 99 Flag which, when set to "1", will make cgit generate extra links for
100 each repo in the repository index (specifically, to the "summary", 100 each repo in the repository index (specifically, to the "summary",
101 "commit" and "tree" pages). Default value: "0". 101 "commit" and "tree" pages). Default value: "0".
102 102
103enable-log-filecount:: 103enable-log-filecount::
104 Flag which, when set to "1", will make cgit print the number of 104 Flag which, when set to "1", will make cgit print the number of
105 modified files for each commit on the repository log page. Default 105 modified files for each commit on the repository log page. Default
106 value: "0". 106 value: "0".
107 107
108enable-log-linecount:: 108enable-log-linecount::
109 Flag which, when set to "1", will make cgit print the number of added 109 Flag which, when set to "1", will make cgit print the number of added
110 and removed lines for each commit on the repository log page. Default 110 and removed lines for each commit on the repository log page. Default
111 value: "0". 111 value: "0".
112 112
113enable-remote-branches::
114 Flag which, when set to "1", will make cgit display remote branches
115 in the summary and refs views. Default value: "0". See also:
116 "repo.enable-remote-branches".
117
113enable-tree-linenumbers:: 118enable-tree-linenumbers::
114 Flag which, when set to "1", will make cgit generate linenumber links 119 Flag which, when set to "1", will make cgit generate linenumber links
115 for plaintext blobs printed in the tree view. Default value: "1". 120 for plaintext blobs printed in the tree view. Default value: "1".
116 121
117favicon:: 122favicon::
118 Url used as link to a shortcut icon for cgit. If specified, it is 123 Url used as link to a shortcut icon for cgit. If specified, it is
119 suggested to use the value "/favicon.ico" since certain browsers will 124 suggested to use the value "/favicon.ico" since certain browsers will
120 ignore other values. Default value: none. 125 ignore other values. Default value: none.
121 126
122footer:: 127footer::
123 The content of the file specified with this option will be included 128 The content of the file specified with this option will be included
124 verbatim at the bottom of all pages (i.e. it replaces the standard 129 verbatim at the bottom of all pages (i.e. it replaces the standard
125 "generated by..." message. Default value: none. 130 "generated by..." message. Default value: none.
126 131
127head-include:: 132head-include::
128 The content of the file specified with this option will be included 133 The content of the file specified with this option will be included
129 verbatim in the html HEAD section on all pages. Default value: none. 134 verbatim in the html HEAD section on all pages. Default value: none.
130 135
131header:: 136header::
132 The content of the file specified with this option will be included 137 The content of the file specified with this option will be included
133 verbatim at the top of all pages. Default value: none. 138 verbatim at the top of all pages. Default value: none.
134 139
135include:: 140include::
136 Name of a configfile to include before the rest of the current config- 141 Name of a configfile to include before the rest of the current config-
137 file is parsed. Default value: none. 142 file is parsed. Default value: none.
138 143
139index-header:: 144index-header::
140 The content of the file specified with this option will be included 145 The content of the file specified with this option will be included
141 verbatim above the repository index. This setting is deprecated, and 146 verbatim above the repository index. This setting is deprecated, and
142 will not be supported by cgit-1.0 (use root-readme instead). Default 147 will not be supported by cgit-1.0 (use root-readme instead). Default
143 value: none. 148 value: none.
144 149
145index-info:: 150index-info::
146 The content of the file specified with this option will be included 151 The content of the file specified with this option will be included
147 verbatim below the heading on the repository index page. This setting 152 verbatim below the heading on the repository index page. This setting
148 is deprecated, and will not be supported by cgit-1.0 (use root-desc 153 is deprecated, and will not be supported by cgit-1.0 (use root-desc
149 instead). Default value: none. 154 instead). Default value: none.
150 155
151local-time:: 156local-time::
152 Flag which, if set to "1", makes cgit print commit and tag times in the 157 Flag which, if set to "1", makes cgit print commit and tag times in the
153 servers timezone. Default value: "0". 158 servers timezone. Default value: "0".
154 159
155logo:: 160logo::
156 Url which specifies the source of an image which will be used as a logo 161 Url which specifies the source of an image which will be used as a logo
157 on all cgit pages. Default value: "/cgit.png". 162 on all cgit pages. Default value: "/cgit.png".
158 163
159logo-link:: 164logo-link::
160 Url loaded when clicking on the cgit logo image. If unspecified the 165 Url loaded when clicking on the cgit logo image. If unspecified the
@@ -267,96 +272,100 @@ summary-branches::
267 view. Default value: "10". 272 view. Default value: "10".
268 273
269summary-log:: 274summary-log::
270 Specifies the number of log entries to display in the repository 275 Specifies the number of log entries to display in the repository
271 "summary" view. Default value: "10". 276 "summary" view. Default value: "10".
272 277
273summary-tags:: 278summary-tags::
274 Specifies the number of tags to display in the repository "summary" 279 Specifies the number of tags to display in the repository "summary"
275 view. Default value: "10". 280 view. Default value: "10".
276 281
277virtual-root:: 282virtual-root::
278 Url which, if specified, will be used as root for all cgit links. It 283 Url which, if specified, will be used as root for all cgit links. It
279 will also cause cgit to generate 'virtual urls', i.e. urls like 284 will also cause cgit to generate 'virtual urls', i.e. urls like
280 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default 285 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
281 value: none. 286 value: none.
282 NOTE: cgit has recently learned how to use PATH_INFO to achieve the 287 NOTE: cgit has recently learned how to use PATH_INFO to achieve the
283 same kind of virtual urls, so this option will probably be deprecated. 288 same kind of virtual urls, so this option will probably be deprecated.
284 289
285REPOSITORY SETTINGS 290REPOSITORY SETTINGS
286------------------- 291-------------------
287repo.about-filter:: 292repo.about-filter::
288 Override the default about-filter. Default value: none. See also: 293 Override the default about-filter. Default value: none. See also:
289 "enable-filter-overrides". 294 "enable-filter-overrides".
290 295
291repo.clone-url:: 296repo.clone-url::
292 A list of space-separated urls which can be used to clone this repo. 297 A list of space-separated urls which can be used to clone this repo.
293 Default value: none. 298 Default value: none.
294 299
295repo.commit-filter:: 300repo.commit-filter::
296 Override the default commit-filter. Default value: none. See also: 301 Override the default commit-filter. Default value: none. See also:
297 "enable-filter-overrides". 302 "enable-filter-overrides".
298 303
299repo.defbranch:: 304repo.defbranch::
300 The name of the default branch for this repository. If no such branch 305 The name of the default branch for this repository. If no such branch
301 exists in the repository, the first branch name (when sorted) is used 306 exists in the repository, the first branch name (when sorted) is used
302 as default instead. Default value: "master". 307 as default instead. Default value: "master".
303 308
304repo.desc:: 309repo.desc::
305 The value to show as repository description. Default value: none. 310 The value to show as repository description. Default value: none.
306 311
307repo.enable-log-filecount:: 312repo.enable-log-filecount::
308 A flag which can be used to disable the global setting 313 A flag which can be used to disable the global setting
309 `enable-log-filecount'. Default value: none. 314 `enable-log-filecount'. Default value: none.
310 315
311repo.enable-log-linecount:: 316repo.enable-log-linecount::
312 A flag which can be used to disable the global setting 317 A flag which can be used to disable the global setting
313 `enable-log-linecount'. Default value: none. 318 `enable-log-linecount'. Default value: none.
314 319
320repo.enable-remote-branches::
321 Flag which, when set to "1", will make cgit display remote branches
322 in the summary and refs views. Default value: <enable-remote-branches>.
323
315repo.max-stats:: 324repo.max-stats::
316 Override the default maximum statistics period. Valid values are equal 325 Override the default maximum statistics period. Valid values are equal
317 to the values specified for the global "max-stats" setting. Default 326 to the values specified for the global "max-stats" setting. Default
318 value: none. 327 value: none.
319 328
320repo.name:: 329repo.name::
321 The value to show as repository name. Default value: <repo.url>. 330 The value to show as repository name. Default value: <repo.url>.
322 331
323repo.owner:: 332repo.owner::
324 A value used to identify the owner of the repository. Default value: 333 A value used to identify the owner of the repository. Default value:
325 none. 334 none.
326 335
327repo.path:: 336repo.path::
328 An absolute path to the repository directory. For non-bare repositories 337 An absolute path to the repository directory. For non-bare repositories
329 this is the .git-directory. Default value: none. 338 this is the .git-directory. Default value: none.
330 339
331repo.readme:: 340repo.readme::
332 A path (relative to <repo.path>) which specifies a file to include 341 A path (relative to <repo.path>) which specifies a file to include
333 verbatim as the "About" page for this repo. Default value: none. 342 verbatim as the "About" page for this repo. Default value: none.
334 343
335repo.snapshots:: 344repo.snapshots::
336 A mask of allowed snapshot-formats for this repo, restricted by the 345 A mask of allowed snapshot-formats for this repo, restricted by the
337 "snapshots" global setting. Default value: <snapshots>. 346 "snapshots" global setting. Default value: <snapshots>.
338 347
339repo.section:: 348repo.section::
340 Override the current section name for this repository. Default value: 349 Override the current section name for this repository. Default value:
341 none. 350 none.
342 351
343repo.source-filter:: 352repo.source-filter::
344 Override the default source-filter. Default value: none. See also: 353 Override the default source-filter. Default value: none. See also:
345 "enable-filter-overrides". 354 "enable-filter-overrides".
346 355
347repo.url:: 356repo.url::
348 The relative url used to access the repository. This must be the first 357 The relative url used to access the repository. This must be the first
349 setting specified for each repo. Default value: none. 358 setting specified for each repo. Default value: none.
350 359
351 360
352REPOSITORY-SPECIFIC CGITRC FILE 361REPOSITORY-SPECIFIC CGITRC FILE
353------------------------------- 362-------------------------------
354When the option "scan-path" is used to auto-discover git repositories, cgit 363When the option "scan-path" is used to auto-discover git repositories, cgit
355will try to parse the file "cgitrc" within any found repository. Such a 364will try to parse the file "cgitrc" within any found repository. Such a
356repo-specific config file may contain any of the repo-specific options 365repo-specific config file may contain any of the repo-specific options
357described above, except "repo.url" and "repo.path". Additionally, the "filter" 366described above, except "repo.url" and "repo.path". Additionally, the "filter"
358options are only acknowledged in repo-specific config files when 367options are only acknowledged in repo-specific config files when
359"enable-filter-overrides" is set to "1". 368"enable-filter-overrides" is set to "1".
360 369
361Note: the "repo." prefix is dropped from the option names in repo-specific 370Note: the "repo." prefix is dropped from the option names in repo-specific
362config files, e.g. "repo.desc" becomes "desc". 371config files, e.g. "repo.desc" becomes "desc".
diff --git a/shared.c b/shared.c
index 9362d21..5f46793 100644
--- a/shared.c
+++ b/shared.c
@@ -14,96 +14,97 @@ int cgit_cmd;
14 14
15int chk_zero(int result, char *msg) 15int chk_zero(int result, char *msg)
16{ 16{
17 if (result != 0) 17 if (result != 0)
18 die("%s: %s", msg, strerror(errno)); 18 die("%s: %s", msg, strerror(errno));
19 return result; 19 return result;
20} 20}
21 21
22int chk_positive(int result, char *msg) 22int chk_positive(int result, char *msg)
23{ 23{
24 if (result <= 0) 24 if (result <= 0)
25 die("%s: %s", msg, strerror(errno)); 25 die("%s: %s", msg, strerror(errno));
26 return result; 26 return result;
27} 27}
28 28
29int chk_non_negative(int result, char *msg) 29int chk_non_negative(int result, char *msg)
30{ 30{
31 if (result < 0) 31 if (result < 0)
32 die("%s: %s",msg, strerror(errno)); 32 die("%s: %s",msg, strerror(errno));
33 return result; 33 return result;
34} 34}
35 35
36struct cgit_repo *cgit_add_repo(const char *url) 36struct cgit_repo *cgit_add_repo(const char *url)
37{ 37{
38 struct cgit_repo *ret; 38 struct cgit_repo *ret;
39 39
40 if (++cgit_repolist.count > cgit_repolist.length) { 40 if (++cgit_repolist.count > cgit_repolist.length) {
41 if (cgit_repolist.length == 0) 41 if (cgit_repolist.length == 0)
42 cgit_repolist.length = 8; 42 cgit_repolist.length = 8;
43 else 43 else
44 cgit_repolist.length *= 2; 44 cgit_repolist.length *= 2;
45 cgit_repolist.repos = xrealloc(cgit_repolist.repos, 45 cgit_repolist.repos = xrealloc(cgit_repolist.repos,
46 cgit_repolist.length * 46 cgit_repolist.length *
47 sizeof(struct cgit_repo)); 47 sizeof(struct cgit_repo));
48 } 48 }
49 49
50 ret = &cgit_repolist.repos[cgit_repolist.count-1]; 50 ret = &cgit_repolist.repos[cgit_repolist.count-1];
51 memset(ret, 0, sizeof(struct cgit_repo)); 51 memset(ret, 0, sizeof(struct cgit_repo));
52 ret->url = trim_end(url, '/'); 52 ret->url = trim_end(url, '/');
53 ret->name = ret->url; 53 ret->name = ret->url;
54 ret->path = NULL; 54 ret->path = NULL;
55 ret->desc = "[no description]"; 55 ret->desc = "[no description]";
56 ret->owner = NULL; 56 ret->owner = NULL;
57 ret->section = ctx.cfg.section; 57 ret->section = ctx.cfg.section;
58 ret->defbranch = "master"; 58 ret->defbranch = "master";
59 ret->snapshots = ctx.cfg.snapshots; 59 ret->snapshots = ctx.cfg.snapshots;
60 ret->enable_log_filecount = ctx.cfg.enable_log_filecount; 60 ret->enable_log_filecount = ctx.cfg.enable_log_filecount;
61 ret->enable_log_linecount = ctx.cfg.enable_log_linecount; 61 ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
62 ret->enable_remote_branches = ctx.cfg.enable_remote_branches;
62 ret->max_stats = ctx.cfg.max_stats; 63 ret->max_stats = ctx.cfg.max_stats;
63 ret->module_link = ctx.cfg.module_link; 64 ret->module_link = ctx.cfg.module_link;
64 ret->readme = NULL; 65 ret->readme = NULL;
65 ret->mtime = -1; 66 ret->mtime = -1;
66 ret->about_filter = ctx.cfg.about_filter; 67 ret->about_filter = ctx.cfg.about_filter;
67 ret->commit_filter = ctx.cfg.commit_filter; 68 ret->commit_filter = ctx.cfg.commit_filter;
68 ret->source_filter = ctx.cfg.source_filter; 69 ret->source_filter = ctx.cfg.source_filter;
69 return ret; 70 return ret;
70} 71}
71 72
72struct cgit_repo *cgit_get_repoinfo(const char *url) 73struct cgit_repo *cgit_get_repoinfo(const char *url)
73{ 74{
74 int i; 75 int i;
75 struct cgit_repo *repo; 76 struct cgit_repo *repo;
76 77
77 for (i=0; i<cgit_repolist.count; i++) { 78 for (i=0; i<cgit_repolist.count; i++) {
78 repo = &cgit_repolist.repos[i]; 79 repo = &cgit_repolist.repos[i];
79 if (!strcmp(repo->url, url)) 80 if (!strcmp(repo->url, url))
80 return repo; 81 return repo;
81 } 82 }
82 return NULL; 83 return NULL;
83} 84}
84 85
85void *cgit_free_commitinfo(struct commitinfo *info) 86void *cgit_free_commitinfo(struct commitinfo *info)
86{ 87{
87 free(info->author); 88 free(info->author);
88 free(info->author_email); 89 free(info->author_email);
89 free(info->committer); 90 free(info->committer);
90 free(info->committer_email); 91 free(info->committer_email);
91 free(info->subject); 92 free(info->subject);
92 free(info->msg); 93 free(info->msg);
93 free(info->msg_encoding); 94 free(info->msg_encoding);
94 free(info); 95 free(info);
95 return NULL; 96 return NULL;
96} 97}
97 98
98char *trim_end(const char *str, char c) 99char *trim_end(const char *str, char c)
99{ 100{
100 int len; 101 int len;
101 char *s, *t; 102 char *s, *t;
102 103
103 if (str == NULL) 104 if (str == NULL)
104 return NULL; 105 return NULL;
105 t = (char *)str; 106 t = (char *)str;
106 len = strlen(t); 107 len = strlen(t);
107 while(len > 0 && t[len - 1] == c) 108 while(len > 0 && t[len - 1] == c)
108 len--; 109 len--;
109 110
diff --git a/ui-refs.c b/ui-refs.c
index 33d9bec..98738db 100644
--- a/ui-refs.c
+++ b/ui-refs.c
@@ -142,96 +142,98 @@ static int print_tag(struct refinfo *ref)
142 html("</td><td>"); 142 html("</td><td>");
143 if (info->tagger) 143 if (info->tagger)
144 html(info->tagger); 144 html(info->tagger);
145 html("</td><td colspan='2'>"); 145 html("</td><td colspan='2'>");
146 if (info->tagger_date > 0) 146 if (info->tagger_date > 0)
147 cgit_print_age(info->tagger_date, -1, NULL); 147 cgit_print_age(info->tagger_date, -1, NULL);
148 html("</td></tr>\n"); 148 html("</td></tr>\n");
149 } else { 149 } else {
150 if (!header) 150 if (!header)
151 print_tag_header(); 151 print_tag_header();
152 html("<tr><td>"); 152 html("<tr><td>");
153 cgit_tag_link(name, NULL, NULL, ctx.qry.head, name); 153 cgit_tag_link(name, NULL, NULL, ctx.qry.head, name);
154 html("</td><td>"); 154 html("</td><td>");
155 if (ctx.repo->snapshots && (ref->object->type == OBJ_COMMIT)) 155 if (ctx.repo->snapshots && (ref->object->type == OBJ_COMMIT))
156 print_tag_downloads(ctx.repo, name); 156 print_tag_downloads(ctx.repo, name);
157 else 157 else
158 cgit_object_link(ref->object); 158 cgit_object_link(ref->object);
159 html("</td><td>"); 159 html("</td><td>");
160 if (ref->object->type == OBJ_COMMIT) 160 if (ref->object->type == OBJ_COMMIT)
161 html(ref->commit->author); 161 html(ref->commit->author);
162 html("</td><td colspan='2'>"); 162 html("</td><td colspan='2'>");
163 if (ref->object->type == OBJ_COMMIT) 163 if (ref->object->type == OBJ_COMMIT)
164 cgit_print_age(ref->commit->commit->date, -1, NULL); 164 cgit_print_age(ref->commit->commit->date, -1, NULL);
165 html("</td></tr>\n"); 165 html("</td></tr>\n");
166 } 166 }
167 return 0; 167 return 0;
168} 168}
169 169
170static void print_refs_link(char *path) 170static void print_refs_link(char *path)
171{ 171{
172 html("<tr class='nohover'><td colspan='4'>"); 172 html("<tr class='nohover'><td colspan='4'>");
173 cgit_refs_link("[...]", NULL, NULL, ctx.qry.head, NULL, path); 173 cgit_refs_link("[...]", NULL, NULL, ctx.qry.head, NULL, path);
174 html("</td></tr>"); 174 html("</td></tr>");
175} 175}
176 176
177void cgit_print_branches(int maxcount) 177void cgit_print_branches(int maxcount)
178{ 178{
179 struct reflist list; 179 struct reflist list;
180 int i; 180 int i;
181 181
182 html("<tr class='nohover'><th class='left'>Branch</th>" 182 html("<tr class='nohover'><th class='left'>Branch</th>"
183 "<th class='left'>Commit message</th>" 183 "<th class='left'>Commit message</th>"
184 "<th class='left'>Author</th>" 184 "<th class='left'>Author</th>"
185 "<th class='left' colspan='2'>Age</th></tr>\n"); 185 "<th class='left' colspan='2'>Age</th></tr>\n");
186 186
187 list.refs = NULL; 187 list.refs = NULL;
188 list.alloc = list.count = 0; 188 list.alloc = list.count = 0;
189 for_each_branch_ref(cgit_refs_cb, &list); 189 for_each_branch_ref(cgit_refs_cb, &list);
190 if (ctx.repo->enable_remote_branches)
191 for_each_remote_ref(cgit_refs_cb, &list);
190 192
191 if (maxcount == 0 || maxcount > list.count) 193 if (maxcount == 0 || maxcount > list.count)
192 maxcount = list.count; 194 maxcount = list.count;
193 195
194 if (maxcount < list.count) { 196 if (maxcount < list.count) {
195 qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age); 197 qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age);
196 qsort(list.refs, maxcount, sizeof(*list.refs), cmp_ref_name); 198 qsort(list.refs, maxcount, sizeof(*list.refs), cmp_ref_name);
197 } 199 }
198 200
199 for(i=0; i<maxcount; i++) 201 for(i=0; i<maxcount; i++)
200 print_branch(list.refs[i]); 202 print_branch(list.refs[i]);
201 203
202 if (maxcount < list.count) 204 if (maxcount < list.count)
203 print_refs_link("heads"); 205 print_refs_link("heads");
204} 206}
205 207
206void cgit_print_tags(int maxcount) 208void cgit_print_tags(int maxcount)
207{ 209{
208 struct reflist list; 210 struct reflist list;
209 int i; 211 int i;
210 212
211 header = 0; 213 header = 0;
212 list.refs = NULL; 214 list.refs = NULL;
213 list.alloc = list.count = 0; 215 list.alloc = list.count = 0;
214 for_each_tag_ref(cgit_refs_cb, &list); 216 for_each_tag_ref(cgit_refs_cb, &list);
215 if (list.count == 0) 217 if (list.count == 0)
216 return; 218 return;
217 qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age); 219 qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age);
218 if (!maxcount) 220 if (!maxcount)
219 maxcount = list.count; 221 maxcount = list.count;
220 else if (maxcount > list.count) 222 else if (maxcount > list.count)
221 maxcount = list.count; 223 maxcount = list.count;
222 print_tag_header(); 224 print_tag_header();
223 for(i=0; i<maxcount; i++) 225 for(i=0; i<maxcount; i++)
224 print_tag(list.refs[i]); 226 print_tag(list.refs[i]);
225 227
226 if (maxcount < list.count) 228 if (maxcount < list.count)
227 print_refs_link("tags"); 229 print_refs_link("tags");
228} 230}
229 231
230void cgit_print_refs() 232void cgit_print_refs()
231{ 233{
232 234
233 html("<table class='list nowrap'>"); 235 html("<table class='list nowrap'>");
234 236
235 if (ctx.qry.path && !strncmp(ctx.qry.path, "heads", 5)) 237 if (ctx.qry.path && !strncmp(ctx.qry.path, "heads", 5))
236 cgit_print_branches(0); 238 cgit_print_branches(0);
237 else if (ctx.qry.path && !strncmp(ctx.qry.path, "tags", 4)) 239 else if (ctx.qry.path && !strncmp(ctx.qry.path, "tags", 4))