Merge early parts of branch 'ls/am'
authorJunio C Hamano <junkio@cox.net>
Mon, 19 Jun 2006 05:13:11 +0000 (22:13 -0700)
committerJunio C Hamano <junkio@cox.net>
Mon, 19 Jun 2006 05:13:11 +0000 (22:13 -0700)
Documentation/git-repo-config.txt
config.c
diff.c
diff.h
gitweb/gitweb.cgi
gitweb/gitweb.css [new file with mode: 0644]
pager.c
index d5142e0dcd6799b8a94c9cd5a3f2bd17c3cb7289..803c0d5cae6db0a953380adc6ee3980a0afa72e0 100644 (file)
@@ -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
 -------
 
index 984c75f5dd06bd1d53eda206a2b57eea093dfd58..d46eb6d8293766c2c188a1f89891329e4db06d93 100644 (file)
--- 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 9e9cfc8b75b7b761980b0d32cb0c516ea3505ba3..bc32a4aa29171af6ca268a54cde881934079de83 100644 (file)
--- 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 4fc597c59421b7558e0beb0f2994e15950592aaa..de9de574e78693d27ff911d882030a59407a99d5 100644 (file)
--- 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;
index 5eabe06dafadc150acbb3cc534ad7bf758a0080e..fa90c51a4da437b8fa8bfd159ec7f0cad0596e37 100755 (executable)
@@ -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 (file)
index 0000000..bf95e38
--- /dev/null
@@ -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 9a30939016620072f319b5fef81d0c913513a641..2d186e8bde1067a62c9615e8d7f0368f98442ca4 100644 (file)
--- 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);