2006-12-11 16:04:19 +00:00
|
|
|
/* ui-log.c: functions for log output
|
|
|
|
*
|
2014-01-08 14:10:49 +00:00
|
|
|
* Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
|
2006-12-11 16:04:19 +00:00
|
|
|
*
|
|
|
|
* Licensed under GNU General Public License v2
|
|
|
|
* (see COPYING for full license text)
|
|
|
|
*/
|
|
|
|
|
2024-07-16 07:45:13 +00:00
|
|
|
#define USE_THE_REPOSITORY_VARIABLE
|
|
|
|
|
2006-12-11 16:04:19 +00:00
|
|
|
#include "cgit.h"
|
2013-04-06 10:37:59 +00:00
|
|
|
#include "ui-log.h"
|
2008-02-23 21:45:33 +00:00
|
|
|
#include "html.h"
|
2008-03-24 15:50:57 +00:00
|
|
|
#include "ui-shared.h"
|
2020-10-06 14:32:08 +00:00
|
|
|
#include "strvec.h"
|
2006-12-11 16:04:19 +00:00
|
|
|
|
2015-08-12 14:55:28 +00:00
|
|
|
static int files, add_lines, rem_lines, lines_counted;
|
2007-05-13 09:27:46 +00:00
|
|
|
|
2010-11-15 17:39:51 +00:00
|
|
|
/*
|
|
|
|
* The list of available column colors in the commit graph.
|
|
|
|
*/
|
|
|
|
static const char *column_colors_html[] = {
|
|
|
|
"<span class='column1'>",
|
|
|
|
"<span class='column2'>",
|
|
|
|
"<span class='column3'>",
|
|
|
|
"<span class='column4'>",
|
|
|
|
"<span class='column5'>",
|
|
|
|
"<span class='column6'>",
|
|
|
|
"</span>",
|
|
|
|
};
|
|
|
|
|
|
|
|
#define COLUMN_COLORS_HTML_MAX (ARRAY_SIZE(column_colors_html) - 1)
|
|
|
|
|
2013-03-04 07:52:33 +00:00
|
|
|
static void count_lines(char *line, int size)
|
2007-05-13 15:03:27 +00:00
|
|
|
{
|
2007-11-05 23:35:12 +00:00
|
|
|
if (size <= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (line[0] == '+')
|
|
|
|
add_lines++;
|
|
|
|
|
|
|
|
else if (line[0] == '-')
|
|
|
|
rem_lines++;
|
2007-05-13 15:03:27 +00:00
|
|
|
}
|
|
|
|
|
2013-03-04 07:52:33 +00:00
|
|
|
static void inspect_files(struct diff_filepair *pair)
|
2007-05-13 09:27:46 +00:00
|
|
|
{
|
2009-01-31 09:40:40 +00:00
|
|
|
unsigned long old_size = 0;
|
|
|
|
unsigned long new_size = 0;
|
|
|
|
int binary = 0;
|
|
|
|
|
2007-05-13 09:27:46 +00:00
|
|
|
files++;
|
2008-02-16 12:56:09 +00:00
|
|
|
if (ctx.repo->enable_log_linecount)
|
2016-09-04 10:38:18 +00:00
|
|
|
cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size,
|
2010-06-24 15:52:57 +00:00
|
|
|
&new_size, &binary, 0, ctx.qry.ignorews,
|
|
|
|
count_lines);
|
2007-05-13 09:27:46 +00:00
|
|
|
}
|
|
|
|
|
2009-01-11 11:16:18 +00:00
|
|
|
void show_commit_decorations(struct commit *commit)
|
|
|
|
{
|
2014-12-19 07:28:34 +00:00
|
|
|
const struct name_decoration *deco;
|
2009-01-11 11:16:18 +00:00
|
|
|
static char buf[1024];
|
|
|
|
|
|
|
|
buf[sizeof(buf) - 1] = 0;
|
2014-12-19 07:28:34 +00:00
|
|
|
deco = get_name_decoration(&commit->object);
|
2016-02-26 20:57:30 +00:00
|
|
|
if (!deco)
|
|
|
|
return;
|
2013-05-18 14:54:49 +00:00
|
|
|
html("<span class='decoration'>");
|
2009-01-11 11:16:18 +00:00
|
|
|
while (deco) {
|
2021-03-15 21:48:26 +00:00
|
|
|
struct object_id oid_tag, peeled;
|
2018-06-05 10:46:13 +00:00
|
|
|
int is_annotated = 0;
|
2021-03-15 21:48:26 +00:00
|
|
|
|
2018-08-28 16:16:11 +00:00
|
|
|
strlcpy(buf, prettify_refname(deco->name), sizeof(buf));
|
2016-02-26 20:58:41 +00:00
|
|
|
switch(deco->type) {
|
|
|
|
case DECORATION_NONE:
|
|
|
|
/* If the git-core doesn't recognize it,
|
|
|
|
* don't display anything. */
|
|
|
|
break;
|
|
|
|
case DECORATION_REF_LOCAL:
|
2010-06-11 12:50:47 +00:00
|
|
|
cgit_log_link(buf, NULL, "branch-deco", buf, NULL,
|
2016-02-26 20:58:41 +00:00
|
|
|
ctx.qry.vpath, 0, NULL, NULL,
|
|
|
|
ctx.qry.showmsg, 0);
|
|
|
|
break;
|
|
|
|
case DECORATION_REF_TAG:
|
2024-07-16 07:45:13 +00:00
|
|
|
if (!refs_read_ref(get_main_ref_store(the_repository), deco->name, &oid_tag) &&
|
|
|
|
!peel_iterated_oid(the_repository, &oid_tag, &peeled))
|
2021-03-15 21:48:26 +00:00
|
|
|
is_annotated = !oideq(&oid_tag, &peeled);
|
2018-06-05 10:46:13 +00:00
|
|
|
cgit_tag_link(buf, NULL, is_annotated ? "tag-annotated-deco" : "tag-deco", buf);
|
2016-02-26 20:58:41 +00:00
|
|
|
break;
|
|
|
|
case DECORATION_REF_REMOTE:
|
2012-01-03 15:30:50 +00:00
|
|
|
if (!ctx.repo->enable_remote_branches)
|
2016-02-26 20:58:41 +00:00
|
|
|
break;
|
2009-01-11 11:16:18 +00:00
|
|
|
cgit_log_link(buf, NULL, "remote-deco", NULL,
|
2016-02-26 20:58:41 +00:00
|
|
|
oid_to_hex(&commit->object.oid),
|
|
|
|
ctx.qry.vpath, 0, NULL, NULL,
|
|
|
|
ctx.qry.showmsg, 0);
|
|
|
|
break;
|
|
|
|
default:
|
2009-01-11 11:16:18 +00:00
|
|
|
cgit_commit_link(buf, NULL, "deco", ctx.qry.head,
|
2016-02-26 20:58:41 +00:00
|
|
|
oid_to_hex(&commit->object.oid),
|
|
|
|
ctx.qry.vpath);
|
|
|
|
break;
|
2009-01-11 11:16:18 +00:00
|
|
|
}
|
|
|
|
deco = deco->next;
|
|
|
|
}
|
2013-05-18 14:54:49 +00:00
|
|
|
html("</span>");
|
2009-01-11 11:16:18 +00:00
|
|
|
}
|
|
|
|
|
2015-08-12 14:55:28 +00:00
|
|
|
static void handle_rename(struct diff_filepair *pair)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* After we have seen a rename, we generate links to the previous
|
|
|
|
* name of the file so that commit & diff views get fed the path
|
|
|
|
* that is correct for the commit they are showing, avoiding the
|
|
|
|
* need to walk the entire history leading back to every commit we
|
|
|
|
* show in order detect renames.
|
|
|
|
*/
|
|
|
|
if (0 != strcmp(ctx.qry.vpath, pair->two->path)) {
|
|
|
|
free(ctx.qry.vpath);
|
|
|
|
ctx.qry.vpath = xstrdup(pair->two->path);
|
|
|
|
}
|
|
|
|
inspect_files(pair);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int show_commit(struct commit *commit, struct rev_info *revs)
|
|
|
|
{
|
|
|
|
struct commit_list *parents = commit->parents;
|
|
|
|
struct commit *parent;
|
|
|
|
int found = 0, saved_fmt;
|
2018-01-18 08:19:31 +00:00
|
|
|
struct diff_flags saved_flags = revs->diffopt.flags;
|
2015-08-12 14:55:28 +00:00
|
|
|
|
|
|
|
/* Always show if we're not in "follow" mode with a single file. */
|
|
|
|
if (!ctx.qry.follow)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In "follow" mode, we don't show merges. This is consistent with
|
|
|
|
* "git log --follow -- <file>".
|
|
|
|
*/
|
|
|
|
if (parents && parents->next)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this is the root commit, do what rev_info tells us.
|
|
|
|
*/
|
|
|
|
if (!parents)
|
|
|
|
return revs->show_root_diff;
|
|
|
|
|
|
|
|
/* When we get here we have precisely one parent. */
|
|
|
|
parent = parents->item;
|
2016-01-16 11:03:05 +00:00
|
|
|
/* If we can't parse the commit, let print_commit() report an error. */
|
2023-05-16 15:02:27 +00:00
|
|
|
if (repo_parse_commit(the_repository, parent))
|
2016-01-16 11:03:05 +00:00
|
|
|
return 1;
|
2015-08-12 14:55:28 +00:00
|
|
|
|
|
|
|
files = 0;
|
|
|
|
add_lines = 0;
|
|
|
|
rem_lines = 0;
|
|
|
|
|
2018-01-18 08:19:31 +00:00
|
|
|
revs->diffopt.flags.recursive = 1;
|
2020-03-13 02:52:35 +00:00
|
|
|
diff_tree_oid(get_commit_tree_oid(parent),
|
|
|
|
get_commit_tree_oid(commit),
|
2017-08-10 00:02:56 +00:00
|
|
|
"", &revs->diffopt);
|
2015-08-12 14:55:28 +00:00
|
|
|
diffcore_std(&revs->diffopt);
|
|
|
|
|
2022-04-04 19:00:33 +00:00
|
|
|
found = !diff_queue_is_empty(&revs->diffopt);
|
2015-08-12 14:55:28 +00:00
|
|
|
saved_fmt = revs->diffopt.output_format;
|
|
|
|
revs->diffopt.output_format = DIFF_FORMAT_CALLBACK;
|
|
|
|
revs->diffopt.format_callback = cgit_diff_tree_cb;
|
|
|
|
revs->diffopt.format_callback_data = handle_rename;
|
|
|
|
diff_flush(&revs->diffopt);
|
|
|
|
revs->diffopt.output_format = saved_fmt;
|
|
|
|
revs->diffopt.flags = saved_flags;
|
|
|
|
|
|
|
|
lines_counted = 1;
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
2013-03-04 07:52:33 +00:00
|
|
|
static void print_commit(struct commit *commit, struct rev_info *revs)
|
2006-12-11 16:04:19 +00:00
|
|
|
{
|
2006-12-15 17:17:36 +00:00
|
|
|
struct commitinfo *info;
|
2013-03-05 14:42:14 +00:00
|
|
|
int columns = revs->graph ? 4 : 3;
|
2010-11-15 17:39:50 +00:00
|
|
|
struct strbuf graphbuf = STRBUF_INIT;
|
2010-11-15 17:39:52 +00:00
|
|
|
struct strbuf msgbuf = STRBUF_INIT;
|
2010-11-15 17:39:50 +00:00
|
|
|
|
2011-03-10 16:03:22 +00:00
|
|
|
if (ctx.repo->enable_log_filecount)
|
2013-03-05 14:42:14 +00:00
|
|
|
columns++;
|
2011-03-10 16:03:22 +00:00
|
|
|
if (ctx.repo->enable_log_linecount)
|
2013-03-05 14:42:14 +00:00
|
|
|
columns++;
|
2010-11-15 17:39:50 +00:00
|
|
|
|
|
|
|
if (revs->graph) {
|
|
|
|
/* Advance graph until current commit */
|
|
|
|
while (!graph_next_line(revs->graph, &graphbuf)) {
|
|
|
|
/* Print graph segment in otherwise empty table row */
|
2010-11-15 17:39:53 +00:00
|
|
|
html("<tr class='nohover'><td class='commitgraph'>");
|
2010-11-15 17:39:50 +00:00
|
|
|
html(graphbuf.buf);
|
2013-03-05 14:42:14 +00:00
|
|
|
htmlf("</td><td colspan='%d' /></tr>\n", columns);
|
2010-11-15 17:39:50 +00:00
|
|
|
strbuf_setlen(&graphbuf, 0);
|
|
|
|
}
|
|
|
|
/* Current commit's graph segment is now ready in graphbuf */
|
|
|
|
}
|
2006-12-11 16:04:19 +00:00
|
|
|
|
2006-12-15 17:17:36 +00:00
|
|
|
info = cgit_parse_commit(commit);
|
2010-11-15 17:39:53 +00:00
|
|
|
htmlf("<tr%s>", ctx.qry.showmsg ? " class='logheader'" : "");
|
2010-11-15 17:39:50 +00:00
|
|
|
|
|
|
|
if (revs->graph) {
|
|
|
|
/* Print graph segment for current commit */
|
|
|
|
html("<td class='commitgraph'>");
|
|
|
|
html(graphbuf.buf);
|
|
|
|
html("</td>");
|
|
|
|
strbuf_setlen(&graphbuf, 0);
|
|
|
|
}
|
2010-11-15 17:39:53 +00:00
|
|
|
else {
|
|
|
|
html("<td>");
|
2016-01-19 19:33:05 +00:00
|
|
|
cgit_print_age(info->committer_date, info->committer_tz, TM_WEEK * 2);
|
2010-11-15 17:39:53 +00:00
|
|
|
html("</td>");
|
|
|
|
}
|
2010-11-15 17:39:50 +00:00
|
|
|
|
|
|
|
htmlf("<td%s>", ctx.qry.showmsg ? " class='logsubject'" : "");
|
2010-11-15 17:39:52 +00:00
|
|
|
if (ctx.qry.showmsg) {
|
|
|
|
/* line-wrap long commit subjects instead of truncating them */
|
|
|
|
size_t subject_len = strlen(info->subject);
|
|
|
|
|
|
|
|
if (subject_len > ctx.cfg.max_msg_len &&
|
|
|
|
ctx.cfg.max_msg_len >= 15) {
|
|
|
|
/* symbol for signaling line-wrap (in PAGE_ENCODING) */
|
|
|
|
const char wrap_symbol[] = { ' ', 0xE2, 0x86, 0xB5, 0 };
|
|
|
|
int i = ctx.cfg.max_msg_len - strlen(wrap_symbol);
|
|
|
|
|
|
|
|
/* Rewind i to preceding space character */
|
|
|
|
while (i > 0 && !isspace(info->subject[i]))
|
|
|
|
--i;
|
|
|
|
if (!i) /* Oops, zero spaces. Reset i */
|
|
|
|
i = ctx.cfg.max_msg_len - strlen(wrap_symbol);
|
|
|
|
|
|
|
|
/* add remainder starting at i to msgbuf */
|
|
|
|
strbuf_add(&msgbuf, info->subject + i, subject_len - i);
|
|
|
|
strbuf_trim(&msgbuf);
|
|
|
|
strbuf_add(&msgbuf, "\n\n", 2);
|
|
|
|
|
|
|
|
/* Place wrap_symbol at position i in info->subject */
|
2018-08-28 16:08:33 +00:00
|
|
|
strlcpy(info->subject + i, wrap_symbol, subject_len - i + 1);
|
2010-11-15 17:39:52 +00:00
|
|
|
}
|
|
|
|
}
|
2008-02-16 10:53:40 +00:00
|
|
|
cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
|
2016-01-05 06:38:53 +00:00
|
|
|
oid_to_hex(&commit->object.oid), ctx.qry.vpath);
|
2022-12-16 17:37:10 +00:00
|
|
|
if (!ctx.qry.showmsg && info->msg && *(info->msg))
|
|
|
|
html("<span class='msg-avail'>...</span>");
|
2009-01-11 11:16:18 +00:00
|
|
|
show_commit_decorations(commit);
|
2008-04-14 20:13:38 +00:00
|
|
|
html("</td><td>");
|
2014-01-13 15:24:40 +00:00
|
|
|
cgit_open_filter(ctx.repo->email_filter, info->author_email, "log");
|
2008-04-14 20:13:38 +00:00
|
|
|
html_txt(info->author);
|
2014-01-13 03:04:52 +00:00
|
|
|
cgit_close_filter(ctx.repo->email_filter);
|
2010-11-15 17:39:53 +00:00
|
|
|
|
|
|
|
if (revs->graph) {
|
|
|
|
html("</td><td>");
|
2016-01-19 19:33:05 +00:00
|
|
|
cgit_print_age(info->committer_date, info->committer_tz, TM_WEEK * 2);
|
2010-11-15 17:39:53 +00:00
|
|
|
}
|
|
|
|
|
2015-08-12 14:55:28 +00:00
|
|
|
if (!lines_counted && (ctx.repo->enable_log_filecount ||
|
|
|
|
ctx.repo->enable_log_linecount)) {
|
2007-05-18 11:55:52 +00:00
|
|
|
files = 0;
|
2007-11-05 23:35:12 +00:00
|
|
|
add_lines = 0;
|
|
|
|
rem_lines = 0;
|
2010-09-30 18:15:14 +00:00
|
|
|
cgit_diff_commit(commit, inspect_files, ctx.qry.vpath);
|
2007-05-18 11:55:52 +00:00
|
|
|
}
|
2011-03-10 16:03:22 +00:00
|
|
|
|
|
|
|
if (ctx.repo->enable_log_filecount)
|
|
|
|
htmlf("</td><td>%d", files);
|
|
|
|
if (ctx.repo->enable_log_linecount)
|
2016-06-29 07:37:57 +00:00
|
|
|
htmlf("</td><td><span class='deletions'>-%d</span>/"
|
|
|
|
"<span class='insertions'>+%d</span>", rem_lines, add_lines);
|
2011-03-10 16:03:22 +00:00
|
|
|
|
2006-12-11 16:04:19 +00:00
|
|
|
html("</td></tr>\n");
|
2010-11-15 17:39:49 +00:00
|
|
|
|
2016-07-06 20:42:36 +00:00
|
|
|
if ((revs->graph && !graph_is_commit_finished(revs->graph))
|
|
|
|
|| ctx.qry.showmsg) { /* Print a second table row */
|
|
|
|
html("<tr class='nohover-highlight'>");
|
2010-11-15 17:39:50 +00:00
|
|
|
|
|
|
|
if (ctx.qry.showmsg) {
|
|
|
|
/* Concatenate commit message + notes in msgbuf */
|
|
|
|
if (info->msg && *(info->msg)) {
|
|
|
|
strbuf_addstr(&msgbuf, info->msg);
|
|
|
|
strbuf_addch(&msgbuf, '\n');
|
|
|
|
}
|
2017-08-10 00:02:56 +00:00
|
|
|
format_display_notes(&commit->object.oid,
|
2013-04-08 08:00:22 +00:00
|
|
|
&msgbuf, PAGE_ENCODING, 0);
|
2010-11-15 17:39:49 +00:00
|
|
|
strbuf_addch(&msgbuf, '\n');
|
2010-11-15 17:39:50 +00:00
|
|
|
strbuf_ltrim(&msgbuf);
|
2010-11-15 17:39:49 +00:00
|
|
|
}
|
2010-07-29 14:32:31 +00:00
|
|
|
|
2010-11-15 17:39:50 +00:00
|
|
|
if (revs->graph) {
|
|
|
|
int lines = 0;
|
|
|
|
|
|
|
|
/* Calculate graph padding */
|
|
|
|
if (ctx.qry.showmsg) {
|
|
|
|
/* Count #lines in commit message + notes */
|
|
|
|
const char *p = msgbuf.buf;
|
|
|
|
lines = 1;
|
|
|
|
while ((p = strchr(p, '\n'))) {
|
|
|
|
p++;
|
|
|
|
lines++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Print graph padding */
|
|
|
|
html("<td class='commitgraph'>");
|
|
|
|
while (lines > 0 || !graph_is_commit_finished(revs->graph)) {
|
|
|
|
if (graphbuf.len)
|
|
|
|
html("\n");
|
|
|
|
strbuf_setlen(&graphbuf, 0);
|
|
|
|
graph_next_line(revs->graph, &graphbuf);
|
|
|
|
html(graphbuf.buf);
|
|
|
|
lines--;
|
|
|
|
}
|
|
|
|
html("</td>\n");
|
2008-11-29 17:39:41 +00:00
|
|
|
}
|
2010-11-15 17:39:53 +00:00
|
|
|
else
|
|
|
|
html("<td/>"); /* Empty 'Age' column */
|
2010-11-15 17:39:49 +00:00
|
|
|
|
2010-11-15 17:39:50 +00:00
|
|
|
/* Print msgbuf into remainder of table row */
|
2013-03-05 14:42:14 +00:00
|
|
|
htmlf("<td colspan='%d'%s>\n", columns - (revs->graph ? 1 : 0),
|
2010-11-15 17:39:50 +00:00
|
|
|
ctx.qry.showmsg ? " class='logmsg'" : "");
|
2010-11-15 17:39:49 +00:00
|
|
|
html_txt(msgbuf.buf);
|
2008-11-29 18:11:26 +00:00
|
|
|
html("</td></tr>\n");
|
2008-11-29 17:39:41 +00:00
|
|
|
}
|
2010-11-15 17:39:49 +00:00
|
|
|
|
2010-11-15 17:39:52 +00:00
|
|
|
strbuf_release(&msgbuf);
|
2010-11-15 17:39:50 +00:00
|
|
|
strbuf_release(&graphbuf);
|
2006-12-16 13:58:20 +00:00
|
|
|
cgit_free_commitinfo(info);
|
2006-12-11 16:04:19 +00:00
|
|
|
}
|
|
|
|
|
2013-04-06 09:28:57 +00:00
|
|
|
static const char *disambiguate_ref(const char *ref, int *must_free_result)
|
2008-12-03 16:34:23 +00:00
|
|
|
{
|
2015-07-28 08:42:01 +00:00
|
|
|
struct object_id oid;
|
2013-04-06 09:28:57 +00:00
|
|
|
struct strbuf longref = STRBUF_INIT;
|
2008-12-03 16:34:23 +00:00
|
|
|
|
2013-04-06 09:28:57 +00:00
|
|
|
strbuf_addf(&longref, "refs/heads/%s", ref);
|
2023-05-16 15:02:27 +00:00
|
|
|
if (repo_get_oid(the_repository, longref.buf, &oid) == 0) {
|
2013-04-06 09:28:57 +00:00
|
|
|
*must_free_result = 1;
|
|
|
|
return strbuf_detach(&longref, NULL);
|
|
|
|
}
|
2008-12-03 16:34:23 +00:00
|
|
|
|
2013-04-06 09:28:57 +00:00
|
|
|
*must_free_result = 0;
|
|
|
|
strbuf_release(&longref);
|
2008-12-03 16:34:23 +00:00
|
|
|
return ref;
|
|
|
|
}
|
2006-12-15 17:17:36 +00:00
|
|
|
|
2010-11-09 19:53:36 +00:00
|
|
|
static char *next_token(char **src)
|
|
|
|
{
|
|
|
|
char *result;
|
|
|
|
|
|
|
|
if (!src || !*src)
|
|
|
|
return NULL;
|
|
|
|
while (isspace(**src))
|
|
|
|
(*src)++;
|
|
|
|
if (!**src)
|
|
|
|
return NULL;
|
|
|
|
result = *src;
|
|
|
|
while (**src) {
|
|
|
|
if (isspace(**src)) {
|
|
|
|
**src = '\0';
|
|
|
|
(*src)++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
(*src)++;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2008-04-14 20:13:38 +00:00
|
|
|
void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern,
|
2019-01-02 16:25:01 +00:00
|
|
|
const char *path, int pager, int commit_graph, int commit_sort)
|
2006-12-11 16:04:19 +00:00
|
|
|
{
|
|
|
|
struct rev_info rev;
|
|
|
|
struct commit *commit;
|
2020-10-06 14:32:08 +00:00
|
|
|
struct strvec rev_argv = STRVEC_INIT;
|
2013-03-05 14:42:14 +00:00
|
|
|
int i, columns = commit_graph ? 4 : 3;
|
2013-04-06 09:28:57 +00:00
|
|
|
int must_free_tip = 0;
|
2010-11-09 19:53:36 +00:00
|
|
|
|
2013-11-22 12:24:52 +00:00
|
|
|
/* rev_argv.argv[0] will be ignored by setup_revisions */
|
2020-10-06 14:32:08 +00:00
|
|
|
strvec_push(&rev_argv, "log_rev_setup");
|
2007-05-13 09:27:46 +00:00
|
|
|
|
2007-06-17 12:58:45 +00:00
|
|
|
if (!tip)
|
2008-12-03 16:34:23 +00:00
|
|
|
tip = ctx.qry.head;
|
2013-04-06 09:28:57 +00:00
|
|
|
tip = disambiguate_ref(tip, &must_free_tip);
|
2020-10-06 14:32:08 +00:00
|
|
|
strvec_push(&rev_argv, tip);
|
2007-06-17 12:58:45 +00:00
|
|
|
|
2010-10-28 15:05:39 +00:00
|
|
|
if (grep && pattern && *pattern) {
|
2010-11-09 19:53:36 +00:00
|
|
|
pattern = xstrdup(pattern);
|
2010-06-19 12:32:37 +00:00
|
|
|
if (!strcmp(grep, "grep") || !strcmp(grep, "author") ||
|
2010-11-09 19:53:36 +00:00
|
|
|
!strcmp(grep, "committer")) {
|
2020-10-06 14:32:08 +00:00
|
|
|
strvec_pushf(&rev_argv, "--%s=%s", grep, pattern);
|
2013-11-22 12:30:58 +00:00
|
|
|
} else if (!strcmp(grep, "range")) {
|
2013-04-06 09:28:57 +00:00
|
|
|
char *arg;
|
2010-11-09 19:53:36 +00:00
|
|
|
/* Split the pattern at whitespace and add each token
|
|
|
|
* as a revision expression. Do not accept other
|
|
|
|
* rev-list options. Also, replace the previously
|
|
|
|
* pushed tip (it's no longer relevant).
|
|
|
|
*/
|
2020-10-06 14:32:08 +00:00
|
|
|
strvec_pop(&rev_argv);
|
2010-11-09 19:53:36 +00:00
|
|
|
while ((arg = next_token(&pattern))) {
|
|
|
|
if (*arg == '-') {
|
|
|
|
fprintf(stderr, "Bad range expr: %s\n",
|
|
|
|
arg);
|
|
|
|
break;
|
|
|
|
}
|
2020-10-06 14:32:08 +00:00
|
|
|
strvec_push(&rev_argv, arg);
|
2010-11-09 19:53:36 +00:00
|
|
|
}
|
|
|
|
}
|
2010-06-19 12:32:37 +00:00
|
|
|
}
|
2015-08-12 14:55:28 +00:00
|
|
|
|
|
|
|
if (!path || !ctx.cfg.enable_follow_links) {
|
|
|
|
/*
|
|
|
|
* If we don't have a path, "follow" is a no-op so make sure
|
|
|
|
* the variable is set to false to avoid needing to check
|
|
|
|
* both this and whether we have a path everywhere.
|
|
|
|
*/
|
|
|
|
ctx.qry.follow = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (commit_graph && !ctx.qry.follow) {
|
2020-10-06 14:32:08 +00:00
|
|
|
strvec_push(&rev_argv, "--graph");
|
|
|
|
strvec_push(&rev_argv, "--color");
|
2010-11-15 17:39:51 +00:00
|
|
|
graph_set_column_colors(column_colors_html,
|
|
|
|
COLUMN_COLORS_HTML_MAX);
|
2010-11-15 17:39:50 +00:00
|
|
|
}
|
2007-10-28 14:23:00 +00:00
|
|
|
|
2013-11-22 12:30:58 +00:00
|
|
|
if (commit_sort == 1)
|
2020-10-06 14:32:08 +00:00
|
|
|
strvec_push(&rev_argv, "--date-order");
|
2013-11-22 12:30:58 +00:00
|
|
|
else if (commit_sort == 2)
|
2020-10-06 14:32:08 +00:00
|
|
|
strvec_push(&rev_argv, "--topo-order");
|
2012-10-13 14:10:30 +00:00
|
|
|
|
2015-08-12 14:55:28 +00:00
|
|
|
if (path && ctx.qry.follow)
|
2020-10-06 14:32:08 +00:00
|
|
|
strvec_push(&rev_argv, "--follow");
|
|
|
|
strvec_push(&rev_argv, "--");
|
ui-log: ignore unhandled arguments
If you search for a bogus range string here:
http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/log/
Using something like "range" and "qwerty123456", it returns an "Internal
Server Error" and the following in the logs:
> [Tue Jun 10 17:45:32 2014] [error] [client 172.21.1.6] fatal:
> ambiguous argument 'qwerty123456': unknown revision or path not in the
> working tree., referer:
> http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
> [Tue Jun 10 17:45:32 2014] [error] [client 172.21.1.6] Use '--' to
> separate paths from revisions, like this:, referer:
> http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
> [Tue Jun 10 17:45:32 2014] [error] [client 172.21.1.6] 'git <command>
> [<revision>...] -- [<file>...]', referer:
> http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
> [Tue Jun 10 17:45:32 2014] [error] [client 172.21.1.6] Premature end
> of script headers: cgit, referer:
> http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
The cache will kick in, so if you search for the same string again,
it'll show an empty range, so you have to change the bogus strings each
time.
This is because we just pass the arguments straight to Git's revision
parsing machinery which die()s if it cannot parse an argument, printing
the above to stderr and exiting.
The patch below makes it a bit friendlier by just ignoring unhandled
arguments, but I can't see an easy way to report errors when we can't
parse revision arguments without losing the flexibility of supporting
all of the revision specifiers supported by Git.
Reported-by: Konstantin Ryabitsev <mricon@kernel.org>
2014-06-28 13:55:06 +00:00
|
|
|
if (path)
|
2020-10-06 14:32:08 +00:00
|
|
|
strvec_push(&rev_argv, path);
|
2010-11-09 19:53:36 +00:00
|
|
|
|
2023-05-16 15:02:27 +00:00
|
|
|
repo_init_revisions(the_repository, &rev, NULL);
|
2006-12-11 16:04:19 +00:00
|
|
|
rev.abbrev = DEFAULT_ABBREV;
|
|
|
|
rev.commit_format = CMIT_FMT_DEFAULT;
|
|
|
|
rev.verbose_header = 1;
|
|
|
|
rev.show_root_diff = 0;
|
ui-log: ignore unhandled arguments
If you search for a bogus range string here:
http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/log/
Using something like "range" and "qwerty123456", it returns an "Internal
Server Error" and the following in the logs:
> [Tue Jun 10 17:45:32 2014] [error] [client 172.21.1.6] fatal:
> ambiguous argument 'qwerty123456': unknown revision or path not in the
> working tree., referer:
> http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
> [Tue Jun 10 17:45:32 2014] [error] [client 172.21.1.6] Use '--' to
> separate paths from revisions, like this:, referer:
> http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
> [Tue Jun 10 17:45:32 2014] [error] [client 172.21.1.6] 'git <command>
> [<revision>...] -- [<file>...]', referer:
> http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
> [Tue Jun 10 17:45:32 2014] [error] [client 172.21.1.6] Premature end
> of script headers: cgit, referer:
> http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
The cache will kick in, so if you search for the same string again,
it'll show an empty range, so you have to change the bogus strings each
time.
This is because we just pass the arguments straight to Git's revision
parsing machinery which die()s if it cannot parse an argument, printing
the above to stderr and exiting.
The patch below makes it a bit friendlier by just ignoring unhandled
arguments, but I can't see an easy way to report errors when we can't
parse revision arguments without losing the flexibility of supporting
all of the revision specifiers supported by Git.
Reported-by: Konstantin Ryabitsev <mricon@kernel.org>
2014-06-28 13:55:06 +00:00
|
|
|
rev.ignore_missing = 1;
|
2015-08-12 14:55:28 +00:00
|
|
|
rev.simplify_history = 1;
|
2020-10-06 14:32:08 +00:00
|
|
|
setup_revisions(rev_argv.nr, rev_argv.v, &rev, NULL);
|
2018-01-18 08:19:31 +00:00
|
|
|
load_ref_decorations(NULL, DECORATE_FULL_REFS);
|
2009-01-11 11:16:18 +00:00
|
|
|
rev.show_decorations = 1;
|
2017-08-10 00:02:56 +00:00
|
|
|
rev.grep_filter.ignore_case = 1;
|
2015-08-12 14:55:28 +00:00
|
|
|
|
|
|
|
rev.diffopt.detect_rename = 1;
|
|
|
|
rev.diffopt.rename_limit = ctx.cfg.renamelimit;
|
|
|
|
if (ctx.qry.ignorews)
|
|
|
|
DIFF_XDL_SET(&rev.diffopt, IGNORE_WHITESPACE);
|
|
|
|
|
2008-10-05 17:19:59 +00:00
|
|
|
compile_grep_patterns(&rev.grep_filter);
|
2006-12-11 16:04:19 +00:00
|
|
|
prepare_revision_walk(&rev);
|
|
|
|
|
2015-08-14 11:47:15 +00:00
|
|
|
if (pager) {
|
|
|
|
cgit_print_layout_start();
|
2008-04-14 20:13:38 +00:00
|
|
|
html("<table class='list nowrap'>");
|
2015-08-14 11:47:15 +00:00
|
|
|
}
|
2007-05-18 11:55:52 +00:00
|
|
|
|
2010-11-15 17:39:53 +00:00
|
|
|
html("<tr class='nohover'>");
|
2010-11-15 17:39:52 +00:00
|
|
|
if (commit_graph)
|
2010-11-15 17:39:50 +00:00
|
|
|
html("<th></th>");
|
2010-11-15 17:39:53 +00:00
|
|
|
else
|
|
|
|
html("<th class='left'>Age</th>");
|
2010-11-15 17:39:50 +00:00
|
|
|
html("<th class='left'>Commit message");
|
2008-11-29 17:39:41 +00:00
|
|
|
if (pager) {
|
|
|
|
html(" (");
|
2009-01-09 22:35:10 +00:00
|
|
|
cgit_log_link(ctx.qry.showmsg ? "Collapse" : "Expand", NULL,
|
2020-10-20 21:32:45 +00:00
|
|
|
NULL, ctx.qry.head, ctx.qry.oid,
|
2010-06-11 12:50:47 +00:00
|
|
|
ctx.qry.vpath, ctx.qry.ofs, ctx.qry.grep,
|
2015-08-12 14:55:28 +00:00
|
|
|
ctx.qry.search, ctx.qry.showmsg ? 0 : 1,
|
|
|
|
ctx.qry.follow);
|
2008-11-29 17:39:41 +00:00
|
|
|
html(")");
|
|
|
|
}
|
|
|
|
html("</th><th class='left'>Author</th>");
|
2015-08-12 14:55:28 +00:00
|
|
|
if (rev.graph)
|
2010-11-15 17:39:53 +00:00
|
|
|
html("<th class='left'>Age</th>");
|
2008-02-16 12:56:09 +00:00
|
|
|
if (ctx.repo->enable_log_filecount) {
|
2008-04-14 20:13:38 +00:00
|
|
|
html("<th class='left'>Files</th>");
|
|
|
|
columns++;
|
2011-03-10 16:03:22 +00:00
|
|
|
}
|
|
|
|
if (ctx.repo->enable_log_linecount) {
|
|
|
|
html("<th class='left'>Lines</th>");
|
|
|
|
columns++;
|
2007-05-18 11:55:52 +00:00
|
|
|
}
|
2008-04-14 20:13:38 +00:00
|
|
|
html("</tr>\n");
|
2006-12-13 23:40:34 +00:00
|
|
|
|
|
|
|
if (ofs<0)
|
|
|
|
ofs = 0;
|
|
|
|
|
2015-08-12 15:41:34 +00:00
|
|
|
for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; /* nop */) {
|
2015-08-12 14:55:28 +00:00
|
|
|
if (show_commit(commit, &rev))
|
|
|
|
i++;
|
2022-02-13 15:34:50 +00:00
|
|
|
release_commit_memory(the_repository->parsed_objects, commit);
|
2006-12-13 23:40:34 +00:00
|
|
|
commit->parents = NULL;
|
|
|
|
}
|
|
|
|
|
2015-08-12 15:41:34 +00:00
|
|
|
for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; /* nop */) {
|
2015-08-12 14:55:28 +00:00
|
|
|
/*
|
|
|
|
* In "follow" mode, we must count the files and lines the
|
|
|
|
* first time we invoke diff on a given commit, and we need
|
|
|
|
* to do that to see if the commit touches the path we care
|
|
|
|
* about, so we do it in show_commit. Hence we must clear
|
|
|
|
* lines_counted here.
|
|
|
|
*
|
|
|
|
* This has the side effect of avoiding running diff twice
|
|
|
|
* when we are both following renames and showing file
|
|
|
|
* and/or line counts.
|
|
|
|
*/
|
|
|
|
lines_counted = 0;
|
|
|
|
if (show_commit(commit, &rev)) {
|
|
|
|
i++;
|
|
|
|
print_commit(commit, &rev);
|
|
|
|
}
|
2022-02-13 15:34:50 +00:00
|
|
|
release_commit_memory(the_repository->parsed_objects, commit);
|
2006-12-11 16:04:19 +00:00
|
|
|
commit->parents = NULL;
|
|
|
|
}
|
2007-05-26 01:26:14 +00:00
|
|
|
if (pager) {
|
2013-03-07 07:56:22 +00:00
|
|
|
html("</table><ul class='pager'>");
|
2007-05-26 01:26:14 +00:00
|
|
|
if (ofs > 0) {
|
2013-03-07 07:56:22 +00:00
|
|
|
html("<li>");
|
2008-02-16 10:53:40 +00:00
|
|
|
cgit_log_link("[prev]", NULL, NULL, ctx.qry.head,
|
2020-10-20 21:32:45 +00:00
|
|
|
ctx.qry.oid, ctx.qry.vpath,
|
2008-02-16 10:53:40 +00:00
|
|
|
ofs - cnt, ctx.qry.grep,
|
2015-08-12 14:55:28 +00:00
|
|
|
ctx.qry.search, ctx.qry.showmsg,
|
|
|
|
ctx.qry.follow);
|
2013-03-07 07:56:22 +00:00
|
|
|
html("</li>");
|
2007-05-26 01:26:14 +00:00
|
|
|
}
|
|
|
|
if ((commit = get_revision(&rev)) != NULL) {
|
2013-03-07 07:56:22 +00:00
|
|
|
html("<li>");
|
2008-02-16 10:53:40 +00:00
|
|
|
cgit_log_link("[next]", NULL, NULL, ctx.qry.head,
|
2020-10-20 21:32:45 +00:00
|
|
|
ctx.qry.oid, ctx.qry.vpath,
|
2008-02-16 10:53:40 +00:00
|
|
|
ofs + cnt, ctx.qry.grep,
|
2015-08-12 14:55:28 +00:00
|
|
|
ctx.qry.search, ctx.qry.showmsg,
|
|
|
|
ctx.qry.follow);
|
2013-03-07 07:56:22 +00:00
|
|
|
html("</li>");
|
2007-05-26 01:26:14 +00:00
|
|
|
}
|
2013-03-07 07:56:22 +00:00
|
|
|
html("</ul>");
|
2015-08-14 11:47:15 +00:00
|
|
|
cgit_print_layout_end();
|
2008-04-14 20:13:38 +00:00
|
|
|
} else if ((commit = get_revision(&rev)) != NULL) {
|
2013-03-05 14:42:14 +00:00
|
|
|
htmlf("<tr class='nohover'><td colspan='%d'>", columns);
|
2010-06-11 12:50:47 +00:00
|
|
|
cgit_log_link("[...]", NULL, NULL, ctx.qry.head, NULL,
|
2015-08-12 14:55:28 +00:00
|
|
|
ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg,
|
|
|
|
ctx.qry.follow);
|
2008-04-14 20:13:38 +00:00
|
|
|
html("</td></tr>\n");
|
2006-12-13 23:40:34 +00:00
|
|
|
}
|
2013-04-06 09:28:57 +00:00
|
|
|
|
|
|
|
/* If we allocated tip then it is safe to cast away const. */
|
|
|
|
if (must_free_tip)
|
|
|
|
free((char*) tip);
|
2006-12-11 16:04:19 +00:00
|
|
|
}
|