summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/git-repo-config.txt12
-rw-r--r--config.c12
-rw-r--r--diff.c79
-rw-r--r--diff.h3
-rwxr-xr-xgitweb/gitweb.cgi149
-rw-r--r--gitweb/gitweb.css58
-rw-r--r--pager.c2
7 files changed, 240 insertions, 75 deletions
diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt
index d5142e0dcd..803c0d5cae 100644
--- a/Documentation/git-repo-config.txt
+++ b/Documentation/git-repo-config.txt
@@ -73,6 +73,18 @@ OPTIONS
List all variables set in .git/config.
+ENVIRONMENT
+-----------
+
+GIT_CONFIG::
+ Take the configuration from the given file instead of .git/config.
+
+GIT_CONFIG_LOCAL::
+ Currently the same as $GIT_CONFIG; when Git will support global
+ configuration files, this will cause it to take the configuration
+ from the global configuration file in addition to the given file.
+
+
EXAMPLE
-------
diff --git a/config.c b/config.c
index 984c75f5dd..d46eb6d829 100644
--- a/config.c
+++ b/config.c
@@ -317,7 +317,17 @@ int git_config_from_file(config_fn_t fn, const char *filename)
int git_config(config_fn_t fn)
{
- return git_config_from_file(fn, git_path("config"));
+ const char *filename = git_path("config");
+ /* Forward-compatibility cue: $GIT_CONFIG makes git read _only_
+ * the given config file, $GIT_CONFIG_LOCAL will make it process
+ * it in addition to the global config file, the same way it would
+ * the per-repository config file otherwise. */
+ if (getenv("GIT_CONFIG")) {
+ filename = getenv("GIT_CONFIG");
+ } else if (getenv("GIT_CONFIG_LOCAL")) {
+ filename = getenv("GIT_CONFIG_LOCAL");
+ }
+ return git_config_from_file(fn, filename);
}
/*
diff --git a/diff.c b/diff.c
index 9e9cfc8b75..bc32a4aa29 100644
--- a/diff.c
+++ b/diff.c
@@ -25,6 +25,20 @@ int git_diff_config(const char *var, const char *value)
return git_default_config(var, value);
}
+enum color_diff {
+ DIFF_PLAIN = 0,
+ DIFF_METAINFO = 1,
+ DIFF_FILE_OLD = 2,
+ DIFF_FILE_NEW = 3,
+};
+
+static const char *diff_colors[] = {
+ "\033[0;0m",
+ "\033[1;35m",
+ "\033[1;31m",
+ "\033[1;34m",
+};
+
static char *quote_one(const char *str)
{
int needlen;
@@ -177,23 +191,54 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
}
struct emit_callback {
+ struct xdiff_emit_state xm;
+ int nparents, color_diff;
const char **label_path;
};
-static int fn_out(void *priv, mmbuffer_t *mb, int nbuf)
+static inline void color_diff(int diff_use_color, enum color_diff ix)
+{
+ if (diff_use_color)
+ fputs(diff_colors[ix], stdout);
+}
+
+static void fn_out_consume(void *priv, char *line, unsigned long len)
{
int i;
struct emit_callback *ecbdata = priv;
if (ecbdata->label_path[0]) {
+ color_diff(ecbdata->color_diff, DIFF_METAINFO);
printf("--- %s\n", ecbdata->label_path[0]);
+ color_diff(ecbdata->color_diff, DIFF_METAINFO);
printf("+++ %s\n", ecbdata->label_path[1]);
ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
}
- for (i = 0; i < nbuf; i++)
- if (!fwrite(mb[i].ptr, mb[i].size, 1, stdout))
- return -1;
- return 0;
+
+ /* This is not really necessary for now because
+ * this codepath only deals with two-way diffs.
+ */
+ for (i = 0; i < len && line[i] == '@'; i++)
+ ;
+ if (2 <= i && i < len && line[i] == ' ') {
+ ecbdata->nparents = i - 1;
+ color_diff(ecbdata->color_diff, DIFF_METAINFO);
+ }
+ else if (len < ecbdata->nparents)
+ color_diff(ecbdata->color_diff, DIFF_PLAIN);
+ else {
+ int nparents = ecbdata->nparents;
+ int color = DIFF_PLAIN;
+ for (i = 0; i < nparents && len; i++) {
+ if (line[i] == '-')
+ color = DIFF_FILE_OLD;
+ else if (line[i] == '+')
+ color = DIFF_FILE_NEW;
+ }
+ color_diff(ecbdata->color_diff, color);
+ }
+ fwrite(line, len, 1, stdout);
+ color_diff(ecbdata->color_diff, DIFF_PLAIN);
}
static char *pprint_rename(const char *a, const char *b)
@@ -549,25 +594,35 @@ static void builtin_diff(const char *name_a,
b_two = quote_two("b/", name_b);
lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
+ color_diff(o->color_diff, DIFF_METAINFO);
printf("diff --git %s %s\n", a_one, b_two);
if (lbl[0][0] == '/') {
/* /dev/null */
+ color_diff(o->color_diff, DIFF_METAINFO);
printf("new file mode %06o\n", two->mode);
- if (xfrm_msg && xfrm_msg[0])
+ if (xfrm_msg && xfrm_msg[0]) {
+ color_diff(o->color_diff, DIFF_METAINFO);
puts(xfrm_msg);
+ }
}
else if (lbl[1][0] == '/') {
printf("deleted file mode %06o\n", one->mode);
- if (xfrm_msg && xfrm_msg[0])
+ if (xfrm_msg && xfrm_msg[0]) {
+ color_diff(o->color_diff, DIFF_METAINFO);
puts(xfrm_msg);
+ }
}
else {
if (one->mode != two->mode) {
+ color_diff(o->color_diff, DIFF_METAINFO);
printf("old mode %06o\n", one->mode);
+ color_diff(o->color_diff, DIFF_METAINFO);
printf("new mode %06o\n", two->mode);
}
- if (xfrm_msg && xfrm_msg[0])
+ if (xfrm_msg && xfrm_msg[0]) {
+ color_diff(o->color_diff, DIFF_METAINFO);
puts(xfrm_msg);
+ }
/*
* we do not run diff between different kind
* of objects.
@@ -575,6 +630,7 @@ static void builtin_diff(const char *name_a,
if ((one->mode ^ two->mode) & S_IFMT)
goto free_ab_and_return;
if (complete_rewrite) {
+ color_diff(o->color_diff, DIFF_PLAIN);
emit_rewrite_diff(name_a, name_b, one, two);
goto free_ab_and_return;
}
@@ -602,7 +658,9 @@ static void builtin_diff(const char *name_a,
xdemitcb_t ecb;
struct emit_callback ecbdata;
+ memset(&ecbdata, 0, sizeof(ecbdata));
ecbdata.label_path = lbl;
+ ecbdata.color_diff = o->color_diff;
xpp.flags = XDF_NEED_MINIMAL;
xecfg.ctxlen = o->context;
xecfg.flags = XDL_EMIT_FUNCNAMES;
@@ -612,8 +670,9 @@ static void builtin_diff(const char *name_a,
xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
else if (!strncmp(diffopts, "-u", 2))
xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
- ecb.outf = fn_out;
+ ecb.outf = xdiff_outf;
ecb.priv = &ecbdata;
+ ecbdata.xm.consume = fn_out_consume;
xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
}
@@ -1456,6 +1515,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
else if (40 < options->abbrev)
options->abbrev = 40;
}
+ else if (!strcmp(arg, "--color"))
+ options->color_diff = 1;
else
return 0;
return 1;
diff --git a/diff.h b/diff.h
index 4fc597c594..de9de574e7 100644
--- a/diff.h
+++ b/diff.h
@@ -32,7 +32,8 @@ struct diff_options {
full_index:1,
silent_on_remove:1,
find_copies_harder:1,
- summary:1;
+ summary:1,
+ color_diff:1;
int context;
int break_opt;
int detect_rename;
diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi
index 5eabe06daf..fa90c51a4d 100755
--- a/gitweb/gitweb.cgi
+++ b/gitweb/gitweb.cgi
@@ -38,10 +38,22 @@ my $home_link = $my_uri;
# html text to include at home page
my $home_text = "indextext.html";
+# URI of default stylesheet
+my $stylesheet = "gitweb.css";
+
# source of projects list
#my $projects_list = $projectroot;
my $projects_list = "index/index.aux";
+# default blob_plain mimetype and default charset for text/plain blob
+my $default_blob_plain_mimetype = 'text/plain';
+my $default_text_plain_charset = undef;
+
+# file to use for guessing MIME types before trying /etc/mime.types
+# (relative to the current git repository)
+my $mimetypes_file = undef;
+
+
# input validation and dispatch
my $action = $cgi->param('a');
if (defined $action) {
@@ -260,68 +272,9 @@ sub git_header_html {
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<meta name="robots" content="index, nofollow"/>
+<link rel="stylesheet" href="$stylesheet"/>
<title>$title</title>
$rss_link
-<style type="text/css">
-body {
- font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px;
- margin:10px; background-color:#ffffff; color:#000000;
-}
-a { color:#0000cc; }
-a:hover, a:visited, a:active { color:#880000; }
-div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
-div.page_header a:visited, a.header { color:#0000cc; }
-div.page_header a:hover { color:#880000; }
-div.page_nav { padding:8px; }
-div.page_nav a:visited { color:#0000cc; }
-div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
-div.page_footer { height:17px; padding:4px 8px; background-color: #d9d8d1; }
-div.page_footer_text { float:left; color:#555555; font-style:italic; }
-div.page_body { padding:8px; }
-div.title, a.title {
- display:block; padding:6px 8px;
- font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
-}
-a.title:hover { background-color: #d9d8d1; }
-div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
-div.log_body { padding:8px 8px 8px 150px; }
-span.age { position:relative; float:left; width:142px; font-style:italic; }
-div.log_link {
- padding:0px 8px;
- font-size:10px; font-family:sans-serif; font-style:normal;
- position:relative; float:left; width:136px;
-}
-div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
-a.list { text-decoration:none; color:#000000; }
-a.list:hover { text-decoration:underline; color:#880000; }
-a.text { text-decoration:none; color:#0000cc; }
-a.text:visited { text-decoration:none; color:#880000; }
-a.text:hover { text-decoration:underline; color:#880000; }
-table { padding:8px 4px; }
-th { padding:2px 5px; font-size:12px; text-align:left; }
-tr.light:hover { background-color:#edece6; }
-tr.dark { background-color:#f6f6f0; }
-tr.dark:hover { background-color:#edece6; }
-td { padding:2px 5px; font-size:12px; vertical-align:top; }
-td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
-div.pre { font-family:monospace; font-size:12px; white-space:pre; }
-div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
-div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
-div.search { margin:4px 8px; position:absolute; top:56px; right:12px }
-a.linenr { color:#999999; text-decoration:none }
-a.rss_logo {
- float:right; padding:3px 0px; width:35px; line-height:10px;
- border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
- color:#ffffff; background-color:#ff6600;
- font-weight:bold; font-family:sans-serif; font-size:10px;
- text-align:center; text-decoration:none;
-}
-a.rss_logo:hover { background-color:#ee5500; }
-span.tag {
- padding:0px 4px; font-size:10px; font-weight:normal;
- background-color:#ffffaa; border:1px solid; border-color:#ffffcc #ffee00 #ffee00 #ffffcc;
-}
-</style>
</head>
<body>
EOF
@@ -1538,15 +1491,85 @@ sub git_blob {
git_footer_html();
}
+sub mimetype_guess_file {
+ my $filename = shift;
+ my $mimemap = shift;
+ -r $mimemap or return undef;
+
+ my %mimemap;
+ open(MIME, $mimemap) or return undef;
+ while (<MIME>) {
+ my ($mime, $exts) = split(/\t+/);
+ my @exts = split(/\s+/, $exts);
+ foreach my $ext (@exts) {
+ $mimemap{$ext} = $mime;
+ }
+ }
+ close(MIME);
+
+ $filename =~ /\.(.*?)$/;
+ return $mimemap{$1};
+}
+
+sub mimetype_guess {
+ my $filename = shift;
+ my $mime;
+ $filename =~ /\./ or return undef;
+
+ if ($mimetypes_file) {
+ my $file = $mimetypes_file;
+ $file =~ m#^/# or $file = "$projectroot/$path/$file";
+ $mime = mimetype_guess_file($filename, $file);
+ }
+ $mime ||= mimetype_guess_file($filename, '/etc/mime.types');
+ return $mime;
+}
+
+sub git_blob_plain_mimetype {
+ my $fd = shift;
+ my $filename = shift;
+
+ # just in case
+ return $default_blob_plain_mimetype unless $fd;
+
+ if ($filename) {
+ my $mime = mimetype_guess($filename);
+ $mime and return $mime;
+ }
+
+ if (-T $fd) {
+ return 'text/plain' .
+ ($default_text_plain_charset ? '; charset='.$default_text_plain_charset : '');
+ } elsif (! $filename) {
+ return 'application/octet-stream';
+ } elsif ($filename =~ m/\.png$/i) {
+ return 'image/png';
+ } elsif ($filename =~ m/\.gif$/i) {
+ return 'image/gif';
+ } elsif ($filename =~ m/\.jpe?g$/i) {
+ return 'image/jpeg';
+ } else {
+ return 'application/octet-stream';
+ }
+}
+
sub git_blob_plain {
- my $save_as = "$hash.txt";
+ open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or return;
+ my $type = git_blob_plain_mimetype($fd, $file_name);
+
+ # save as filename, even when no $file_name is given
+ my $save_as = "$hash";
if (defined $file_name) {
$save_as = $file_name;
+ } elsif ($type =~ m/^text\//) {
+ $save_as .= '.txt';
}
- print $cgi->header(-type => "text/plain", -charset => 'utf-8', '-content-disposition' => "inline; filename=\"$save_as\"");
- open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or return;
+
+ print $cgi->header(-type => "$type", '-content-disposition' => "inline; filename=\"$save_as\"");
undef $/;
+ binmode STDOUT, ':raw';
print <$fd>;
+ binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
$/ = "\n";
close $fd;
}
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
new file mode 100644
index 0000000000..bf95e38496
--- /dev/null
+++ b/gitweb/gitweb.css
@@ -0,0 +1,58 @@
+body {
+ font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px;
+ margin:10px; background-color:#ffffff; color:#000000;
+}
+a { color:#0000cc; }
+a:hover, a:visited, a:active { color:#880000; }
+div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
+div.page_header a:visited, a.header { color:#0000cc; }
+div.page_header a:hover { color:#880000; }
+div.page_nav { padding:8px; }
+div.page_nav a:visited { color:#0000cc; }
+div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
+div.page_footer { height:17px; padding:4px 8px; background-color: #d9d8d1; }
+div.page_footer_text { float:left; color:#555555; font-style:italic; }
+div.page_body { padding:8px; }
+div.title, a.title {
+ display:block; padding:6px 8px;
+ font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
+}
+a.title:hover { background-color: #d9d8d1; }
+div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
+div.log_body { padding:8px 8px 8px 150px; }
+span.age { position:relative; float:left; width:142px; font-style:italic; }
+div.log_link {
+ padding:0px 8px;
+ font-size:10px; font-family:sans-serif; font-style:normal;
+ position:relative; float:left; width:136px;
+}
+div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
+a.list { text-decoration:none; color:#000000; }
+a.list:hover { text-decoration:underline; color:#880000; }
+a.text { text-decoration:none; color:#0000cc; }
+a.text:visited { text-decoration:none; color:#880000; }
+a.text:hover { text-decoration:underline; color:#880000; }
+table { padding:8px 4px; }
+th { padding:2px 5px; font-size:12px; text-align:left; }
+tr.light:hover { background-color:#edece6; }
+tr.dark { background-color:#f6f6f0; }
+tr.dark:hover { background-color:#edece6; }
+td { padding:2px 5px; font-size:12px; vertical-align:top; }
+td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
+div.pre { font-family:monospace; font-size:12px; white-space:pre; }
+div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
+div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
+div.search { margin:4px 8px; position:absolute; top:56px; right:12px }
+a.linenr { color:#999999; text-decoration:none }
+a.rss_logo {
+ float:right; padding:3px 0px; width:35px; line-height:10px;
+ border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
+ color:#ffffff; background-color:#ff6600;
+ font-weight:bold; font-family:sans-serif; font-size:10px;
+ text-align:center; text-decoration:none;
+}
+a.rss_logo:hover { background-color:#ee5500; }
+span.tag {
+ padding:0px 4px; font-size:10px; font-weight:normal;
+ background-color:#ffffaa; border:1px solid; border-color:#ffffcc #ffee00 #ffee00 #ffffcc;
+}
diff --git a/pager.c b/pager.c
index 9a30939016..2d186e8bde 100644
--- a/pager.c
+++ b/pager.c
@@ -46,7 +46,7 @@ void setup_pager(void)
close(fd[0]);
close(fd[1]);
- setenv("LESS", "-S", 0);
+ setenv("LESS", "-RS", 0);
run_pager(pager);
die("unable to execute pager '%s'", pager);
exit(255);