chiark / gitweb /
Merge with master
authorJonas Fonseca <fonseca@diku.dk>
Mon, 29 May 2006 19:41:01 +0000 (21:41 +0200)
committerJonas Fonseca <fonseca@antimatter.localdomain>
Mon, 29 May 2006 19:41:01 +0000 (21:41 +0200)
BUGS [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile
README
SITES [new file with mode: 0644]
TODO [new file with mode: 0644]
manual.txt [new file with mode: 0644]
tig.c
tigrc.5.txt [new file with mode: 0644]
web.conf

diff --git a/BUGS b/BUGS
new file mode 100644 (file)
index 0000000..8a42e80
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,14 @@
+BUGS
+----
+Known bugs and problems:
+
+ - In it's current state tig is pretty much UTF-8 only.
+
+ - If the screen width is very small the main view can draw
+   outside the current view causing bad wrapping. Same goes
+   for title and status windows.
+
+ - The cursor can wrap-around on the last line and cause the
+   window to scroll.
+
+ - The prompt doesn't work while loading.
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..caf210d
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,30 @@
+Installation instructions
+-------------------------
+
+Download a tarball from http://jonas.nitro.dk/tig/releases[] or clone the tig
+repository http://jonas.nitro.dk/tig/tig.git[]. Documentation files are
+available either in the tarballs or in the above repository in the branch named
+'release'.
+
+To install tig simply run:
+
+       $ make install
+
+To install documentation run:
+
+       $ make install-docs
+
+The following tools and packages are needed:
+
+`-------------------------------`----------------------------------------------
+Tool                           Description
+-------------------------------------------------------------------------------
+git-core                       Tig is just a frontend for git.
+ncurses                                Be sure to also have development files \
+                               installed. Usually they are available in a
+                               separate package ending with `-dev`.
+asciidoc (>= 7.0), xmlto       For building documentation. (Optional)
+-------------------------------------------------------------------------------
+
+NOTE: You need to install the tig(1) man page in order for the internal help to
+be available.
index fb0e31ea6cc679b7379631188190e975f5789c26..ac545e6a3d2ab1c6081c8a2dc9eb4f725ecea9f7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,8 @@ LDLIBS  = -lcurses
 CFLAGS = -Wall -O2
 DFLAGS = -g -DDEBUG -Werror
 PROGS  = tig
-DOCS   = tig.1.txt tig.1.html tig.1 README.html
+DOCS   = tig.1.txt tig.1.html tig.1 tigrc.5.html tigrc.5 \
+         manual.html manual.html-chunked README.html
 
 ifneq (,$(wildcard .git))
 VERSION = $(shell git-describe)
@@ -25,10 +26,12 @@ install-docs: docs
        for doc in $(DOCS); do \
                case "$$doc" in \
                *.1) install $$doc $(PREFIX)/man/man1 ;; \
+               *.5) install $$doc $(PREFIX)/man/man5 ;; \
                esac \
        done
 
 clean:
+       rm -rf manual.html-chunked
        rm -f $(PROGS) $(DOCS) core
 
 spell-check:
@@ -40,7 +43,7 @@ tig: tig.c
 
 tig.1.txt: tig.c
        sed -n '/\/\*\*/,/\*\*\//p' < $< | \
-       sed 's/.*\*\*\///' | \
+       sed 's/.*\*\*\/.*//' | \
        sed '/^[^*]*\*\*/d' | \
        sed 's/\*\///;s/^[^*]*\* *//' > $@
 
@@ -55,3 +58,21 @@ README.html: README
 
 %.1 : %.1.xml
        xmlto man $<
+
+%.5.html : %.5.txt
+       asciidoc -b xhtml11 -d manpage $<
+
+%.5.xml : %.5.txt
+       asciidoc -b docbook -d manpage $<
+
+%.5 : %.5.xml
+       xmlto man $<
+
+%.html : %.txt
+       asciidoc -b xhtml11 -d article $<
+
+%.xml : %.txt
+       asciidoc -b docbook -d article $<
+
+%.html-chunked : %.xml
+       xmlto html -o $@ $<
diff --git a/README b/README
index 2387c7be731df336895b1eefd27ea5ed54cf6968..59cc07deffdb1af6be9a724425d271a7a40f0b44 100644 (file)
--- a/README
+++ b/README
@@ -2,50 +2,17 @@ Tig: text-mode interface for git
 ================================
 
 Tig is a git repository browser that additionally can act as a pager for output
-of various git commands.
+from various git commands.
 
 When browsing repositories, it uses the underlying git commands to present the
 user with various views, such as summarized revision log and showing the commit
 with the log message, diffstat, and the diff.
 
-Using it as a pager, it will display input from stdin and try to colorize it.
+Using it as a pager, it will display input from stdin and colorize it.
 
-Getting it
-----------
+Resources
+---------
 
-To get tig either grab the Makefile and tig.c files from the home page:
+include::SITES[]
 
- - http://jonas.nitro.dk/tig/[]
-
-or clone the tig repository using the URL:
-
- - http://jonas.nitro.dk/tig/tig.git[]
-
-Documentation files are available either at the home page or in the above
-repository in the branch named 'release'.
-
-Installation instructions
--------------------------
-
-To install tig simply run:
-
-       $ make install
-
-To install documentation run:
-
-       $ make install-docs
-
-The following tools and packages are needed:
-
-`-------------------------------`----------------------------------------------
-Tool                           Description
--------------------------------------------------------------------------------
-git-core                       Tig is just a frontend for git.
-ncurses                                Be sure to also have development files \
-                               installed. Usually they are available in a
-                               separate package ending with `-dev`.
-asciidoc (>= 7.0), xmlto       For building documentation. (Optional)
--------------------------------------------------------------------------------
-
-NOTE: You need to install the tig(1) man page in order for the internal help to
-be available.
+include::INSTALL[]
diff --git a/SITES b/SITES
new file mode 100644 (file)
index 0000000..4ed0e9e
--- /dev/null
+++ b/SITES
@@ -0,0 +1,4 @@
+ - Homepage: http://jonas.nitro.dk/tig[]
+ - Releases: http://jonas.nitro.dk/tig/releases[]
+ - Git URL:  http://jonas.nitro.dk/tig/tig.git[]
+ - Gitweb: http://pasky.or.cz/gitweb.cgi?p=tig.git;a=summary[]
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..cb48616
--- /dev/null
+++ b/TODO
@@ -0,0 +1,9 @@
+TODO
+----
+Features that should be explored.
+ - Searching.
+ - Locale support.
+ - Make '?' show a one page keybinding cheat sheet.
diff --git a/manual.txt b/manual.txt
new file mode 100644 (file)
index 0000000..775c8c5
--- /dev/null
@@ -0,0 +1,386 @@
+The tig Manual
+==============
+Jonas Fonseca <fonseca@diku.dk>
+
+This is the manual for tig, the ncurses-based text-mode interface for git.
+Tig allows you to browse changes in a git repository and can additionally act
+as a pager for output of various git commands. When used as a pager, it will
+display input from stdin and colorize it.
+
+When browsing repositories, tig uses the underlying git commands to present
+the user with various views, such as summarized commit log and showing the
+commit with the log message, diffstat, and the diff.
+
+Calling Conventions
+-------------------
+
+Pager Mode
+~~~~~~~~~~
+
+If stdin is a pipe, any log or diff options will be ignored and the pager view
+will be opened loading data from stdin. The pager mode can be used for
+colorizing output from various git commands.
+
+Example on how to colorize the output of git-show(1):
+
+       $ git show | tig
+
+Git Command Options
+~~~~~~~~~~~~~~~~~~~
+
+All git command options specified on the command line will be passed to the
+given command and all will be shell quoted before they are passed to the
+shell.
+
+NOTE: If you specify options for the main view, you should not use the
+`--pretty` option as this option will be set automatically to the format
+expected by the main view.
+
+Example on how to open the log view and show both author and committer
+information:
+
+       $ tig log --pretty=fuller
+
+See the <<refspec, "Specifying revisions">> section below for an introduction
+to revision options supported by the git commands. For details on specific git
+command options, refer to the man page of the command in question.
+
+Environment Variables
+---------------------
+
+Several options related to the interface with git can be configured via
+environment options.
+
+Repository References
+~~~~~~~~~~~~~~~~~~~~~
+
+Commits that are referenced by tags and branch heads will be marked by the
+reference name surrounded by '[' and ']':
+
+       2006-03-26 19:42 Petr Baudis         | [cogito-0.17.1] Cogito 0.17.1
+
+If you want to filter out certain directories under `.git/refs/`, say `tmp`
+you can do it by setting the following variable:
+
+       $ TIG_LS_REMOTE="git ls-remote . | sed /\/tmp\//d" tig
+
+Or set the variable permanently in your environment.
+
+TIG_LS_REMOTE::
+       Set command for retrieving all repository references. The command
+       should output data in the same format as git-ls-remote(1).
+
+[[history-commands]]
+History Commands
+~~~~~~~~~~~~~~~~
+
+It is possible to alter which commands are used for the different views.  If
+for example you prefer commits in the main view to be sorted by date and only
+show 500 commits, use:
+
+       $ TIG_MAIN_CMD="git log --date-order -n500 --pretty=raw %s" tig
+
+Or set the variable permanently in your environment.
+
+Notice, how `%s` is used to specify the commit reference. There can be a
+maximum of 5 `%s` ref specifications.
+
+TIG_DIFF_CMD::
+       The command used for the diff view. By default, git show is used
+       as a backend.
+
+TIG_LOG_CMD::
+       The command used for the log view. If you prefer to have both
+       author and committer shown in the log view be sure to pass
+       `--pretty=fuller` to git log.
+
+TIG_MAIN_CMD::
+       The command used for the main view. Note, you must always specify
+       the option: `--pretty=raw` since the main view parser expects to
+       read that format.
+
+The Viewer
+----------
+
+The display consists of a status window on the last line of the screen and one
+or more views. The default is to only show one view at the time but it is
+possible to split both the main and log view to also show the commit diff.
+
+If you are in the log view and press 'Enter' when the current line is a commit
+line, such as:
+
+       commit 4d55caff4cc89335192f3e566004b4ceef572521
+
+You will split the view so that the log view is displayed in the top window
+and the diff view in the bottom window. You can switch between the two views
+by pressing 'Tab'. To maximize the log view again, simply press 'l'.
+
+Current Head and Commit ID
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The viewer keeps track of both what head and commit ID you are currently
+viewing. The commit ID will follow the cursor line and change everytime time
+you highlight a different commit. Whenever you reopen the diff view it will be
+reloaded, if the commit ID changed.
+
+The head ID is used when opening the main and log view to indicate from what
+revision to show history.
+
+Views
+~~~~~
+
+tig(1) presents various 'views' of a repository. Each view is based on output
+from an external command, most often 'git log', 'git diff', or 'git show'.
+
+The main view::
+       Is the default view, and it shows a one line summary of each commit
+       in the chosen list of revisions. The summary includes commit date,
+       author, and the first line of the log message. Additionally, any
+       repository references, such as tags, will be shown.
+
+The log view::
+       Presents a more rich view of the revision log showing the whole log
+       message and the diffstat.
+
+The diff view::
+       Shows either the diff of the current working tree, that is, what
+       has changed since the last commit, or the commit diff complete
+       with log message, diffstat and diff.
+
+The pager view::
+       Is used for displaying both input from stdin and output from git
+       commands entered in the internal prompt.
+
+The help view::
+       Displays the information from the tig(1) man page. For the help view
+       to work you need to have the tig(1) man page installed.
+
+Title Windows
+~~~~~~~~~~~~~
+
+Each view has a title window which shows the name of the view, current commit
+ID if available, and where the view is positioned:
+
+       [main] c622eefaa485995320bc743431bae0d497b1d875 - commit 1 of 61 (1%)
+
+By default, the title of the current view is highlighted using bold font.  For
+long loading views (taking over 3 seconds) the time since loading started will
+be appended:
+
+       [main] 77d9e40fbcea3238015aea403e06f61542df9a31 - commit 1 of 779 (0%) 5s
+
+Keys
+----
+Below the default key bindings are shown.
+
+View Switching
+~~~~~~~~~~~~~~
+m::
+       Switch to main view.
+d::
+       Switch to diff view.
+l::
+       Switch to log view.
+p::
+       Switch to pager view.
+h::
+       Show man page.
+
+View Manipulation
+~~~~~~~~~~~~~~~~~
+q::
+       Close view, if multiple views are open it will jump back to the
+       previous view in the view stack. If it is the last open view it
+       will quit. Use 'Q' to quit all views at once.
+Enter::
+       This key is "context sensitive" depending on what view you are
+       currently in. When in log view on a commit line or in the main
+       view, split the view and show the commit diff. In the diff view
+       pressing Enter will simply scroll the view one line down.
+Tab::
+       Switch to next view.
+Up::
+       This key is "context sensitive" and will move the cursor one
+       line up. However, uf you opened a diff view from the main view
+       (split- or full-screen) it will change the cursor to point to
+       the previous commit in the main view and update the diff view
+       to display it.
+Down::
+       Similar to 'Up' but will move down.
+
+Cursor Navigation
+~~~~~~~~~~~~~~~~~
+j::
+       Move cursor one line up.
+k::
+       Move cursor one line down.
+PgUp::
+b::
+-::
+       Move cursor one page up.
+PgDown::
+Space::
+       Move cursor one page down.
+Home::
+       Jump to first line.
+End::
+       Jump to last line.
+
+Scrolling
+~~~~~~~~~
+Insert::
+       Scroll view one line up.
+Delete::
+       Scroll view one line down.
+w::
+       Scroll view one page up.
+s::
+       Scroll view one page down.
+
+Misc
+~~~~
+Q::
+       Quit.
+r::
+       Redraw screen.
+z::
+       Stop all background loading. This can be useful if you use
+       tig(1) in a repository with a long history without limiting
+       the revision log.
+v::
+       Show version.
+n::
+       Toggle line numbers on/off.
+':'::
+       Open prompt. This allows you to specify what git command
+       to run. Example:
+
+       :log -p
+
+[[refspec]]
+Revision Specification
+----------------------
+
+This section describes various ways to specify what revisions to display or
+otherwise limit the view to. tig(1) does not itself parse the described
+revision options so refer to the relevant git man pages for futher
+information. Relevant man pages besides git-log(1) are git-diff(1) and
+git-rev-list(1).
+
+You can tune the interaction with git by making use of the options explained
+in this section. For example, by configuring the environment variables
+described in the  <<history-commands, "History commands">> section.
+
+Limit by Path Name
+~~~~~~~~~~~~~~~~~~
+
+If you are interested only in those revisions that made changes to a specific
+file (or even several files) list the files like this:
+
+       $ tig log Makefile README
+
+To avoid ambiguity with repository references such as tag name, be sure to
+separate file names from other git options using "\--". So if you have a file
+named 'master' it will clash with the reference named 'master', and thus you
+will have to use:
+
+       $ tig log -- master
+
+NOTE: For the main view, avoiding ambiguity will in some cases require you to
+specify two "\--" options. The first will make tig(1) stop option processing
+and the latter will be passed to git log.
+
+Limit by Date or Number
+~~~~~~~~~~~~~~~~~~~~~~~
+
+To speed up interaction with git, you can limit the amount of commits to show
+both for the log and main view. Either limit by date using e.g.
+`--since=1.month` or limit by the number of commits using `-n400`.
+
+If you are only interested in changed that happened between two dates you can
+use:
+
+       $ tig -- --after="May 5th" --before="2006-05-16 15:44"
+
+NOTE: If you want to avoid having to quote dates containing spaces you can use
+"." instead, e.g. `--after=May.5th`.
+
+Limiting by Commit Ranges
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Alternatively, commits can be limited to a specific range, such as "all
+commits between 'tag-1.0' and 'tag-2.0'". For example:
+
+       $ tig log tag-1.0..tag-2.0
+
+This way of commit limiting makes it trivial to only browse the commits which
+haven't been pushed to a remote branch. Assuming 'origin' is your upstream
+remote branch, using:
+
+       $ tig log origin..HEAD
+
+will list what will be pushed to the remote branch. Optionally, the ending
+'HEAD' can be left out since it is implied.
+
+Limiting by Reachability
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Git interprets the range specifier "tag-1.0..tag-2.0" as "all commits
+reachable from 'tag-2.0' but not from 'tag-1.0'".  Where reachability refers
+to what commits are ancestors (or part of the history) of the branch or tagged
+revision in question.
+
+If you prefer to specify which commit to preview in this way use the
+following:
+
+       $ tig log tag-2.0 ^tag-1.0
+
+You can think of '^' as a negation operator. Using this alternate syntax, it
+is possible to further prune commits by specifying multiple branch cut offs.
+
+Combining Revisions Specification
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Revisions options can to some degree be combined, which makes it possible to
+say "show at most 20 commits from within the last month that changed files
+under the Documentation/ directory."
+
+       $ tig -- --since=1.month -n20 -- Documentation/
+
+Examining All Repository References
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In some cases, it can be useful to query changes across all references in a
+repository. An example is to ask "did any line of development in this
+repository change a particular file within the last week". This can be
+accomplished using:
+
+       $ tig -- --all --since=1.week -- Makefile
+
+include::BUGS[]
+
+Copyright
+---------
+
+Copyright (c) 2006 Jonas Fonseca <fonseca@diku.dk>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+References and Related Tools
+----------------------------
+
+include::SITES[]
+
+Git porcelains:
+
+ - link:http://www.kernel.org/pub/software/scm/git/docs/[git],
+ - link:http://www.kernel.org/pub/software/scm/cogito/docs/[Cogito]
+
+Other git repository browsers:
+
+ - gitk(1)
+ - qgit(1)
+ - gitview(1)
diff --git a/tig.c b/tig.c
index bf5309254db1ac46aa89a25e4db7a545fbed16cb..e7deb2ec1d1ce423c1a1117527fbe487c91ac86a 100644 (file)
--- a/tig.c
+++ b/tig.c
@@ -1,5 +1,15 @@
 /* Copyright (c) 2006 Jonas Fonseca <fonseca@diku.dk>
- * See license info at the bottom. */
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
 /**
  * TIG(1)
  * ======
@@ -54,6 +64,7 @@
 
 static void die(const char *err, ...);
 static void report(const char *msg, ...);
+static int read_properties(FILE *pipe, const char *separators, int (*read)(char *, int, char *, int));
 static void set_nonblocking_input(bool loading);
 static size_t utf8_length(const char *string, size_t max_width, int *coloffset, int *trimmed);
 
@@ -138,6 +149,29 @@ struct ref {
 
 static struct ref **get_refs(char *id);
 
+struct int_map {
+       const char *name;
+       int namelen;
+       int value;
+};
+
+static int
+set_from_int_map(struct int_map *map, size_t map_size,
+                int *value, const char *name, int namelen)
+{
+
+       int i;
+
+       for (i = 0; i < map_size; i++)
+               if (namelen == map[i].namelen &&
+                   !strncasecmp(name, map[i].name, namelen)) {
+                       *value = map[i].value;
+                       return OK;
+               }
+
+       return ERR;
+}
+
 
 /*
  * String helpers
@@ -155,6 +189,21 @@ string_ncopy(char *dst, const char *src, int dstlen)
 #define string_copy(dst, src) \
        string_ncopy(dst, src, sizeof(dst))
 
+static char *
+chomp_string(char *name)
+{
+       int namelen;
+
+       while (isspace(*name))
+               name++;
+
+       namelen = strlen(name) - 1;
+       while (namelen > 0 && isspace(name[namelen]))
+               name[namelen--] = 0;
+
+       return name;
+}
+
 
 /* Shell quoting
  *
@@ -216,7 +265,7 @@ VERSION " (" __DATE__ ")\n"
 "  -l                          Start up in log view\n"
 "  -d                          Start up in diff view\n"
 "  -n[I], --line-number[=I]    Show line numbers with given interval\n"
-"  -t[N], --tab-size[=N]       Set number of spaces for tab expansion\n"
+"  -b[N], --tab-size[=N]       Set number of spaces for tab expansion\n"
 "  --                          Mark end of tig options\n"
 "  -v, --version               Show version and exit\n"
 "  -h, --help                  Show help message and exit\n";
@@ -227,6 +276,8 @@ static int opt_num_interval = NUMBER_INTERVAL;
 static int opt_tab_size                = TABSIZE;
 static enum request opt_request = REQ_VIEW_MAIN;
 static char opt_cmd[SIZEOF_CMD]        = "";
+static char opt_encoding[20]   = "";
+static bool opt_utf8           = TRUE;
 static FILE *opt_pipe          = NULL;
 
 /* Returns the index of log or diff command or -1 to exit. */
@@ -280,14 +331,14 @@ parse_options(int argc, char *argv[])
                }
 
                /**
-                * -t[NSPACES], --tab-size[=NSPACES]::
+                * -b[NSPACES], --tab-size[=NSPACES]::
                 *      Set the number of spaces tabs should be expanded to.
                 **/
-               if (!strncmp(opt, "-t", 2) ||
+               if (!strncmp(opt, "-b", 2) ||
                    !strncmp(opt, "--tab-size", 10)) {
                        char *num = opt;
 
-                       if (opt[1] == 't') {
+                       if (opt[1] == 'b') {
                                num = opt + 2;
 
                        } else if (opt[STRING_SIZE("--tab-size")] == '=') {
@@ -366,45 +417,12 @@ parse_options(int argc, char *argv[])
        }
 
        if (!isatty(STDIN_FILENO)) {
-               /**
-                * Pager mode
-                * ~~~~~~~~~~
-                * If stdin is a pipe, any log or diff options will be ignored and the
-                * pager view will be opened loading data from stdin. The pager mode
-                * can be used for colorizing output from various git commands.
-                *
-                * Example on how to colorize the output of git-show(1):
-                *
-                *      $ git show | tig
-                **/
                opt_request = REQ_VIEW_PAGER;
                opt_pipe = stdin;
 
        } else if (i < argc) {
                size_t buf_size;
 
-               /**
-                * Git command options
-                * ~~~~~~~~~~~~~~~~~~~
-                * All git command options specified on the command line will
-                * be passed to the given command and all will be shell quoted
-                * before they are passed to the shell.
-                *
-                * NOTE: If you specify options for the main view, you should
-                * not use the `--pretty` option as this option will be set
-                * automatically to the format expected by the main view.
-                *
-                * Example on how to open the log view and show both author and
-                * committer information:
-                *
-                *      $ tig log --pretty=fuller
-                *
-                * See the <<refspec, "Specifying revisions">> section below
-                * for an introduction to revision options supported by the git
-                * commands. For details on specific git command options, refer
-                * to the man page of the command in question.
-                **/
-
                if (opt_request == REQ_VIEW_MAIN)
                        /* XXX: This is vulnerable to the user overriding
                         * options required for the main view parser. */
@@ -425,46 +443,120 @@ parse_options(int argc, char *argv[])
 
        }
 
+       if (*opt_encoding && strcasecmp(opt_encoding, "UTF-8"))
+               opt_utf8 = FALSE;
+
        return TRUE;
 }
 
 
-/*
- * Line-oriented content detection.
- */
+/**
+ * ENVIRONMENT VARIABLES
+ * ---------------------
+ * TIG_LS_REMOTE::
+ *     Set command for retrieving all repository references. The command
+ *     should output data in the same format as git-ls-remote(1).
+ **/
+
+#define TIG_LS_REMOTE \
+       "git ls-remote . 2>/dev/null"
+
+/**
+ * TIG_DIFF_CMD::
+ *     The command used for the diff view. By default, git show is used
+ *     as a backend.
+ *
+ * TIG_LOG_CMD::
+ *     The command used for the log view. If you prefer to have both
+ *     author and committer shown in the log view be sure to pass
+ *     `--pretty=fuller` to git log.
+ *
+ * TIG_MAIN_CMD::
+ *     The command used for the main view. Note, you must always specify
+ *     the option: `--pretty=raw` since the main view parser expects to
+ *     read that format.
+ **/
+
+#define TIG_DIFF_CMD \
+       "git show --patch-with-stat --find-copies-harder -B -C %s"
+
+#define TIG_LOG_CMD    \
+       "git log --cc --stat -n100 %s"
+
+#define TIG_MAIN_CMD \
+       "git log --topo-order --stat --pretty=raw %s"
+
+/* ... silently ignore that the following are also exported. */
+
+#define TIG_HELP_CMD \
+       "man tig 2>/dev/null"
+
+#define TIG_PAGER_CMD \
+       ""
+
+
+/**
+ * FILES
+ * -----
+ * '~/.tigrc'::
+ *     User configuration file. See tigrc(5) for examples.
+ *
+ * '.git/config'::
+ *     Repository config file. Read on startup with the help of
+ *     git-repo-config(1).
+ **/
+
+static struct int_map color_map[] = {
+#define COLOR_MAP(name) { #name, STRING_SIZE(#name), COLOR_##name }
+       COLOR_MAP(DEFAULT),
+       COLOR_MAP(BLACK),
+       COLOR_MAP(BLUE),
+       COLOR_MAP(CYAN),
+       COLOR_MAP(GREEN),
+       COLOR_MAP(MAGENTA),
+       COLOR_MAP(RED),
+       COLOR_MAP(WHITE),
+       COLOR_MAP(YELLOW),
+};
+
+static struct int_map attr_map[] = {
+#define ATTR_MAP(name) { #name, STRING_SIZE(#name), A_##name }
+       ATTR_MAP(NORMAL),
+       ATTR_MAP(BLINK),
+       ATTR_MAP(BOLD),
+       ATTR_MAP(DIM),
+       ATTR_MAP(REVERSE),
+       ATTR_MAP(STANDOUT),
+       ATTR_MAP(UNDERLINE),
+};
 
 #define LINE_INFO \
-/*   Line type    String to match      Foreground      Background      Attributes
- *   ---------     ---------------      ----------      ----------      ---------- */ \
-/* Diff markup */ \
-LINE(DIFF,        "diff --git ",       COLOR_YELLOW,   COLOR_DEFAULT,  0), \
-LINE(DIFF_INDEX,   "index ",           COLOR_BLUE,     COLOR_DEFAULT,  0), \
+LINE(DIFF_HEADER,  "diff --git ",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
 LINE(DIFF_CHUNK,   "@@",               COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
 LINE(DIFF_ADD,    "+",                 COLOR_GREEN,    COLOR_DEFAULT,  0), \
 LINE(DIFF_DEL,    "-",                 COLOR_RED,      COLOR_DEFAULT,  0), \
-LINE(DIFF_OLDMODE, "old file mode ",   COLOR_YELLOW,   COLOR_DEFAULT,  0), \
-LINE(DIFF_NEWMODE, "new file mode ",   COLOR_YELLOW,   COLOR_DEFAULT,  0), \
-LINE(DIFF_COPY,           "copy ",             COLOR_YELLOW,   COLOR_DEFAULT,  0), \
-LINE(DIFF_RENAME,  "rename ",          COLOR_YELLOW,   COLOR_DEFAULT,  0), \
-LINE(DIFF_SIM,    "similarity ",       COLOR_YELLOW,   COLOR_DEFAULT,  0), \
-LINE(DIFF_DISSIM,  "dissimilarity ",   COLOR_YELLOW,   COLOR_DEFAULT,  0), \
-/* Pretty print commit header */ \
+LINE(DIFF_INDEX,       "index ",         COLOR_BLUE,   COLOR_DEFAULT,  0), \
+LINE(DIFF_OLDMODE,     "old file mode ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
+LINE(DIFF_NEWMODE,     "new file mode ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
+LINE(DIFF_COPY_FROM,   "copy from",      COLOR_YELLOW, COLOR_DEFAULT,  0), \
+LINE(DIFF_COPY_TO,     "copy to",        COLOR_YELLOW, COLOR_DEFAULT,  0), \
+LINE(DIFF_RENAME_FROM, "rename from",    COLOR_YELLOW, COLOR_DEFAULT,  0), \
+LINE(DIFF_RENAME_TO,   "rename to",      COLOR_YELLOW, COLOR_DEFAULT,  0), \
+LINE(DIFF_SIMILARITY,   "similarity ",   COLOR_YELLOW, COLOR_DEFAULT,  0), \
+LINE(DIFF_DISSIMILARITY,"dissimilarity ", COLOR_YELLOW,        COLOR_DEFAULT,  0), \
+LINE(DIFF_TREE,                "diff-tree ",     COLOR_BLUE,   COLOR_DEFAULT,  0), \
 LINE(PP_AUTHOR,           "Author: ",          COLOR_CYAN,     COLOR_DEFAULT,  0), \
 LINE(PP_COMMIT,           "Commit: ",          COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
 LINE(PP_MERGE,    "Merge: ",           COLOR_BLUE,     COLOR_DEFAULT,  0), \
 LINE(PP_DATE,     "Date:   ",          COLOR_YELLOW,   COLOR_DEFAULT,  0), \
 LINE(PP_ADATE,    "AuthorDate: ",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
 LINE(PP_CDATE,    "CommitDate: ",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
-/* Raw commit header */ \
 LINE(COMMIT,      "commit ",           COLOR_GREEN,    COLOR_DEFAULT,  0), \
 LINE(PARENT,      "parent ",           COLOR_BLUE,     COLOR_DEFAULT,  0), \
 LINE(TREE,        "tree ",             COLOR_BLUE,     COLOR_DEFAULT,  0), \
 LINE(AUTHOR,      "author ",           COLOR_CYAN,     COLOR_DEFAULT,  0), \
 LINE(COMMITTER,           "committer ",        COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
-/* Misc */ \
-LINE(DIFF_TREE,           "diff-tree ",        COLOR_BLUE,     COLOR_DEFAULT,  0), \
 LINE(SIGNOFF,     "    Signed-off-by", COLOR_YELLOW,   COLOR_DEFAULT,  0), \
-/* UI colors */ \
 LINE(DEFAULT,     "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
 LINE(CURSOR,      "",                  COLOR_WHITE,    COLOR_GREEN,    A_BOLD), \
 LINE(STATUS,      "",                  COLOR_GREEN,    COLOR_DEFAULT,  0), \
@@ -475,7 +567,12 @@ LINE(MAIN_AUTHOR,  "",                     COLOR_GREEN,    COLOR_DEFAULT,  0), \
 LINE(MAIN_COMMIT,  "",                 COLOR_DEFAULT,  COLOR_DEFAULT,  0), \
 LINE(MAIN_DELIM,   "",                 COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
 LINE(MAIN_TAG,     "",                 COLOR_MAGENTA,  COLOR_DEFAULT,  A_BOLD), \
-LINE(MAIN_REF,     "",                 COLOR_CYAN,     COLOR_DEFAULT,  A_BOLD),
+LINE(MAIN_REF,     "",                 COLOR_CYAN,     COLOR_DEFAULT,  A_BOLD), \
+
+
+/*
+ * Line-oriented content detection.
+ */
 
 enum line_type {
 #define LINE(type, line, fg, bg, attr) \
@@ -485,6 +582,8 @@ enum line_type {
 };
 
 struct line_info {
+       const char *name;       /* Option name. */
+       int namelen;            /* Size of option name. */
        const char *line;       /* The start of line to match. */
        int linelen;            /* Size of string to match. */
        int fg, bg, attr;       /* Color and text attributes for the lines. */
@@ -492,7 +591,7 @@ struct line_info {
 
 static struct line_info line_info[] = {
 #define LINE(type, line, fg, bg, attr) \
-       { (line), STRING_SIZE(line), (fg), (bg), (attr) }
+       { #type, STRING_SIZE(#type), (line), STRING_SIZE(line), (fg), (bg), (attr) }
        LINE_INFO
 #undef LINE
 };
@@ -519,6 +618,28 @@ get_line_attr(enum line_type type)
        return COLOR_PAIR(type) | line_info[type].attr;
 }
 
+static struct line_info *
+get_line_info(char *name, int namelen)
+{
+       enum line_type type;
+       int i;
+
+       /* Diff-Header -> DIFF_HEADER */
+       for (i = 0; i < namelen; i++) {
+               if (name[i] == '-')
+                       name[i] = '_';
+               else if (name[i] == '.')
+                       name[i] = '_';
+       }
+
+       for (type = 0; type < ARRAY_SIZE(line_info); type++)
+               if (namelen == line_info[type].namelen &&
+                   !strncasecmp(line_info[type].name, name, namelen))
+                       return &line_info[type];
+
+       return NULL;
+}
+
 static void
 init_colors(void)
 {
@@ -542,102 +663,133 @@ init_colors(void)
        }
 }
 
+struct line {
+       enum line_type type;
+       void *data;             /* User data */
+};
 
-/**
- * ENVIRONMENT VARIABLES
- * ---------------------
- * Several options related to the interface with git can be configured
- * via environment options.
- *
- * Repository references
- * ~~~~~~~~~~~~~~~~~~~~~
- * Commits that are referenced by tags and branch heads will be marked
- * by the reference name surrounded by '[' and ']':
- *
- *     2006-03-26 19:42 Petr Baudis         | [cogito-0.17.1] Cogito 0.17.1
- *
- * If you want to filter out certain directories under `.git/refs/`, say
- * `tmp` you can do it by setting the following variable:
- *
- *     $ TIG_LS_REMOTE="git ls-remote . | sed /\/tmp\//d" tig
- *
- * Or set the variable permanently in your environment.
- *
- * TIG_LS_REMOTE::
- *     Set command for retrieving all repository references. The command
- *     should output data in the same format as git-ls-remote(1).
- **/
 
-#define TIG_LS_REMOTE \
-       "git ls-remote . 2>/dev/null"
+/*
+ * User config file handling.
+ */
 
-/**
- * [[view-commands]]
- * View commands
- * ~~~~~~~~~~~~~
- * It is possible to alter which commands are used for the different views.
- * If for example you prefer commits in the main view to be sorted by date
- * and only show 500 commits, use:
- *
- *     $ TIG_MAIN_CMD="git log --date-order -n500 --pretty=raw %s" tig
- *
- * Or set the variable permanently in your environment.
- *
- * Notice, how `%s` is used to specify the commit reference. There can
- * be a maximum of 5 `%s` ref specifications.
- *
- * TIG_DIFF_CMD::
- *     The command used for the diff view. By default, git show is used
- *     as a backend.
- *
- * TIG_LOG_CMD::
- *     The command used for the log view. If you prefer to have both
- *     author and committer shown in the log view be sure to pass
- *     `--pretty=fuller` to git log.
- *
- * TIG_MAIN_CMD::
- *     The command used for the main view. Note, you must always specify
- *     the option: `--pretty=raw` since the main view parser expects to
- *     read that format.
- **/
+#define set_color(color, name, namelen) \
+       set_from_int_map(color_map, ARRAY_SIZE(color_map), color, name, namelen)
 
-#define TIG_DIFF_CMD \
-       "git show --patch-with-stat --find-copies-harder -B -C %s"
+#define set_attribute(attr, name, namelen) \
+       set_from_int_map(attr_map, ARRAY_SIZE(attr_map), attr, name, namelen)
 
-#define TIG_LOG_CMD    \
-       "git log --cc --stat -n100 %s"
+static int   config_lineno;
+static bool  config_errors;
+static char *config_msg;
 
-#define TIG_MAIN_CMD \
-       "git log --topo-order --stat --pretty=raw %s"
+static int
+set_option(char *opt, int optlen, char *value, int valuelen)
+{
+       /* Reads: "color" object fgcolor bgcolor [attr] */
+       if (!strcmp(opt, "color")) {
+               struct line_info *info;
+
+               value = chomp_string(value);
+               valuelen = strcspn(value, " \t");
+               info = get_line_info(value, valuelen);
+               if (!info) {
+                       config_msg = "Unknown color name";
+                       return ERR;
+               }
 
-/* ... silently ignore that the following are also exported. */
+               value = chomp_string(value + valuelen);
+               valuelen = strcspn(value, " \t");
+               if (set_color(&info->fg, value, valuelen) == ERR) {
+                       config_msg = "Unknown color";
+                       return ERR;
+               }
 
-#define TIG_HELP_CMD \
-       "man tig 2>/dev/null"
+               value = chomp_string(value + valuelen);
+               valuelen = strcspn(value, " \t");
+               if (set_color(&info->bg, value, valuelen) == ERR) {
+                       config_msg = "Unknown color";
+                       return ERR;
+               }
 
-#define TIG_PAGER_CMD \
-       ""
+               value = chomp_string(value + valuelen);
+               if (*value &&
+                   set_attribute(&info->attr, value, strlen(value)) == ERR) {
+                       config_msg = "Unknown attribute";
+                       return ERR;
+               }
 
+               return OK;
+       }
 
-/**
+       return ERR;
+}
+
+static int
+read_option(char *opt, int optlen, char *value, int valuelen)
+{
+       config_lineno++;
+       config_msg = "Internal error";
+
+       optlen = strcspn(opt, "#;");
+       if (optlen == 0) {
+               /* The whole line is a commend or empty. */
+               return OK;
+
+       } else if (opt[optlen] != 0) {
+               /* Part of the option name is a comment, so the value part
+                * should be ignored. */
+               valuelen = 0;
+               opt[optlen] = value[valuelen] = 0;
+       } else {
+               /* Else look for comment endings in the value. */
+               valuelen = strcspn(value, "#;");
+               value[valuelen] = 0;
+       }
+
+       if (set_option(opt, optlen, value, valuelen) == ERR) {
+               fprintf(stderr, "Error on line %d, near '%.*s' option: %s\n",
+                       config_lineno, optlen, opt, config_msg);
+               config_errors = TRUE;
+       }
+
+       /* Always keep going if errors are encountered. */
+       return OK;
+}
+
+static int
+load_options(void)
+{
+       char *home = getenv("HOME");
+       char buf[1024];
+       FILE *file;
+
+       config_lineno = 0;
+       config_errors = FALSE;
+
+       if (!home ||
+           snprintf(buf, sizeof(buf), "%s/.tigrc", home) >= sizeof(buf))
+               return ERR;
+
+       /* It's ok that the file doesn't exist. */
+       file = fopen(buf, "r");
+       if (!file)
+               return OK;
+
+       if (read_properties(file, " \t", read_option) == ERR ||
+           config_errors == TRUE)
+               fprintf(stderr, "Errors while loading %s.\n", buf);
+
+       return OK;
+}
+
+
+/*
  * The viewer
- * ----------
- * The display consists of a status window on the last line of the screen and
- * one or more views. The default is to only show one view at the time but it
- * is possible to split both the main and log view to also show the commit
- * diff.
- *
- * If you are in the log view and press 'Enter' when the current line is a
- * commit line, such as:
- *
- *     commit 4d55caff4cc89335192f3e566004b4ceef572521
- *
- * You will split the view so that the log view is displayed in the top window
- * and the diff view in the bottom window. You can switch between the two
- * views by pressing 'Tab'. To maximize the log view again, simply press 'l'.
- **/
+ */
 
 struct view;
+struct view_ops;
 
 /* The display array of active views and the index of the current view. */
 static struct view *display[2];
@@ -646,40 +798,19 @@ static unsigned int current_view;
 #define foreach_view(view, i) \
        for (i = 0; i < ARRAY_SIZE(display) && (view = display[i]); i++)
 
+#define displayed_views()      (display[1] != NULL ? 2 : 1)
 
-/**
- * Current head and commit ID
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~
- * The viewer keeps track of both what head and commit ID you are currently
- * viewing. The commit ID will follow the cursor line and change everytime time
- * you highlight a different commit. Whenever you reopen the diff view it
- * will be reloaded, if the commit ID changed.
- *
- * The head ID is used when opening the main and log view to indicate from
- * what revision to show history.
- **/
-
+/* Current head and commit ID */
 static char ref_commit[SIZEOF_REF]     = "HEAD";
 static char ref_head[SIZEOF_REF]       = "HEAD";
 
-
 struct view {
        const char *name;       /* View name */
        const char *cmd_fmt;    /* Default command line format */
        const char *cmd_env;    /* Command line set via environment */
        const char *id;         /* Points to either of ref_{head,commit} */
 
-       struct view_ops {
-               /* What type of content being displayed. Used in the
-                * title bar. */
-               const char *type;
-               /* Draw one line; @lineno must be < view->height. */
-               bool (*draw)(struct view *view, unsigned int lineno);
-               /* Read one line; updates view->line. */
-               bool (*read)(struct view *view, char *line);
-               /* Depending on view, change display based on current line. */
-               bool (*enter)(struct view *view);
-       } *ops;
+       struct view_ops *ops;   /* View operations */
 
        char cmd[SIZEOF_CMD];   /* Command buffer */
        char ref[SIZEOF_REF];   /* Hovered commit reference */
@@ -699,7 +830,7 @@ struct view {
 
        /* Buffering */
        unsigned long lines;    /* Total number of lines */
-       void **line;            /* Line index; each line contains user data */
+       struct line *line;      /* Line index */
        unsigned int digits;    /* Number of digits in the lines member. */
 
        /* Loading */
@@ -707,6 +838,17 @@ struct view {
        time_t start_time;
 };
 
+struct view_ops {
+       /* What type of content being displayed. Used in the title bar. */
+       const char *type;
+       /* Draw one line; @lineno must be < view->height. */
+       bool (*draw)(struct view *view, struct line *line, unsigned int lineno);
+       /* Read one line; updates view->line. */
+       bool (*read)(struct view *view, struct line *prev, char *data);
+       /* Depending on view, change display based on current line. */
+       bool (*enter)(struct view *view, struct line *line);
+};
+
 static struct view_ops pager_ops;
 static struct view_ops main_ops;
 
@@ -716,35 +858,6 @@ static struct view_ops main_ops;
 #define VIEW_(id, name, ops, ref) \
        VIEW_STR(name, TIG_##id##_CMD,  TIG_##id##_CMD, ref, ops)
 
-/**
- * Views
- * ~~~~~
- * tig(1) presents various 'views' of a repository. Each view is based on output
- * from an external command, most often 'git log', 'git diff', or 'git show'.
- *
- * The main view::
- *     Is the default view, and it shows a one line summary of each commit
- *     in the chosen list of revisions. The summary includes commit date,
- *     author, and the first line of the log message. Additionally, any
- *     repository references, such as tags, will be shown.
- *
- * The log view::
- *     Presents a more rich view of the revision log showing the whole log
- *     message and the diffstat.
- *
- * The diff view::
- *     Shows either the diff of the current working tree, that is, what
- *     has changed since the last commit, or the commit diff complete
- *     with log message, diffstat and diff.
- *
- * The pager view::
- *     Is used for displaying both input from stdin and output from git
- *     commands entered in the internal prompt.
- *
- * The help view::
- *     Displays the information from the tig(1) man page. For the help view
- *     to work you need to have the tig(1) man page installed.
- **/
 
 static struct view views[] = {
        VIEW_(MAIN,  "main",  &main_ops,  ref_head),
@@ -757,13 +870,22 @@ static struct view views[] = {
 #define VIEW(req) (&views[(req) - REQ_OFFSET - 1])
 
 
+static bool
+draw_view_line(struct view *view, unsigned int lineno)
+{
+       if (view->offset + lineno >= view->lines)
+               return FALSE;
+
+       return view->ops->draw(view, &view->line[view->offset + lineno], lineno);
+}
+
 static void
 redraw_view_from(struct view *view, int lineno)
 {
        assert(0 <= lineno && lineno < view->height);
 
        for (; lineno < view->height; lineno++) {
-               if (!view->ops->draw(view, lineno))
+               if (!draw_view_line(view, lineno))
                        break;
        }
 
@@ -779,17 +901,6 @@ redraw_view(struct view *view)
 }
 
 
-/**
- * Title windows
- * ~~~~~~~~~~~~~
- * Each view has a title window which shows the name of the view, current
- * commit ID if available, and where the view is positioned:
- *
- *     [main] c622eefaa485995320bc743431bae0d497b1d875 - commit 1 of 61 (1%)
- *
- * By default, the title of the current view is highlighted using bold font.
- **/
-
 static void
 update_view_title(struct view *view)
 {
@@ -806,14 +917,27 @@ update_view_title(struct view *view)
        else
                wprintw(view->title, "[%s]", view->name);
 
-       if (view->lines) {
+       if (view->lines || view->pipe) {
+               unsigned int lines = view->lines
+                                  ? (view->lineno + 1) * 100 / view->lines
+                                  : 0;
+
                wprintw(view->title, " - %s %d of %d (%d%%)",
                        view->ops->type,
                        view->lineno + 1,
                        view->lines,
-                       (view->lineno + 1) * 100 / view->lines);
+                       lines);
+       }
+
+       if (view->pipe) {
+               time_t secs = time(NULL) - view->start_time;
+
+               /* Three git seconds are a long time ... */
+               if (secs > 2)
+                       wprintw(view->title, " %lds", secs);
        }
 
+       wmove(view->title, 0, view->width - 1);
        wrefresh(view->title);
 }
 
@@ -847,11 +971,8 @@ resize_display(void)
        offset = 0;
 
        foreach_view (view, i) {
-               /* Keep the height of all view->win windows one larger than is
-                * required so that the cursor can wrap-around on the last line
-                * without scrolling the window. */
                if (!view->win) {
-                       view->win = newwin(view->height + 1, 0, offset, 0);
+                       view->win = newwin(view->height, 0, offset, 0);
                        if (!view->win)
                                die("Failed to create %s view", view->name);
 
@@ -862,10 +983,9 @@ resize_display(void)
                                die("Failed to create title window");
 
                } else {
-                       wresize(view->win, view->height + 1, view->width);
+                       wresize(view->win, view->height, view->width);
                        mvwin(view->win,   offset, 0);
                        mvwin(view->title, offset + view->height, 0);
-                       wrefresh(view->win);
                }
 
                offset += view->height + 1;
@@ -884,6 +1004,20 @@ redraw_display(void)
        }
 }
 
+static void
+update_display_cursor(void)
+{
+       struct view *view = display[current_view];
+
+       /* Move the cursor to the right-most column of the cursor line.
+        *
+        * XXX: This could turn out to be a bit expensive, but it ensures that
+        * the cursor does not jump around. */
+       if (view->lines) {
+               wmove(view->win, view->lineno - view->offset, view->width - 1);
+               wrefresh(view->win);
+       }
+}
 
 /*
  * Navigation
@@ -910,7 +1044,7 @@ do_scroll_view(struct view *view, int lines, bool redraw)
                wscrl(view->win, lines);
 
                for (; line < end; line++) {
-                       if (!view->ops->draw(view, line))
+                       if (!draw_view_line(view, line))
                                break;
                }
        }
@@ -918,7 +1052,7 @@ do_scroll_view(struct view *view, int lines, bool redraw)
        /* Move current line into the view. */
        if (view->lineno < view->offset) {
                view->lineno = view->offset;
-               view->ops->draw(view, 0);
+               draw_view_line(view, 0);
 
        } else if (view->lineno >= view->offset + view->height) {
                if (view->lineno == view->offset + view->height) {
@@ -928,7 +1062,7 @@ do_scroll_view(struct view *view, int lines, bool redraw)
                        wclrtoeol(view->win);
                }
                view->lineno = view->offset + view->height - 1;
-               view->ops->draw(view, view->lineno - view->offset);
+               draw_view_line(view, view->lineno - view->offset);
        }
 
        assert(view->offset <= view->lineno && view->lineno < view->lines);
@@ -1037,7 +1171,7 @@ move_view(struct view *view, enum request request, bool redraw)
 
                wmove(view->win, prev_lineno, 0);
                wclrtoeol(view->win);
-               view->ops->draw(view, prev_lineno);
+               draw_view_line(view,  prev_lineno);
        }
 
        /* Check whether the view needs to be scrolled */
@@ -1060,7 +1194,7 @@ move_view(struct view *view, enum request request, bool redraw)
        }
 
        /* Draw the current line */
-       view->ops->draw(view, view->lineno - view->offset);
+       draw_view_line(view, view->lineno - view->offset);
 
        if (!redraw)
                return;
@@ -1075,11 +1209,27 @@ move_view(struct view *view, enum request request, bool redraw)
  * Incremental updating
  */
 
+static void
+end_update(struct view *view)
+{
+       if (!view->pipe)
+               return;
+       set_nonblocking_input(FALSE);
+       if (view->pipe == stdin)
+               fclose(view->pipe);
+       else
+               pclose(view->pipe);
+       view->pipe = NULL;
+}
+
 static bool
 begin_update(struct view *view)
 {
        const char *id = view->id;
 
+       if (view->pipe)
+               end_update(view);
+
        if (opt_cmd[0]) {
                string_copy(view->cmd, opt_cmd);
                opt_cmd[0] = 0;
@@ -1116,8 +1266,8 @@ begin_update(struct view *view)
                int i;
 
                for (i = 0; i < view->lines; i++)
-                       if (view->line[i])
-                               free(view->line[i]);
+                       if (view->line[i].data)
+                               free(view->line[i].data);
 
                free(view->line);
                view->line = NULL;
@@ -1128,25 +1278,12 @@ begin_update(struct view *view)
        return TRUE;
 }
 
-static void
-end_update(struct view *view)
-{
-       if (!view->pipe)
-               return;
-       set_nonblocking_input(FALSE);
-       if (view->pipe == stdin)
-               fclose(view->pipe);
-       else
-               pclose(view->pipe);
-       view->pipe = NULL;
-}
-
 static bool
 update_view(struct view *view)
 {
        char buffer[BUFSIZ];
        char *line;
-       void **tmp;
+       struct line *tmp;
        /* The number of lines to read. If too low it will cause too much
         * redrawing (and possible flickering), if too high responsiveness
         * will suffer. */
@@ -1169,10 +1306,14 @@ update_view(struct view *view)
        while ((line = fgets(buffer, sizeof(buffer), view->pipe))) {
                int linelen = strlen(line);
 
+               struct line *prev = view->lines
+                                 ? &view->line[view->lines - 1]
+                                 : NULL;
+
                if (linelen)
                        line[linelen - 1] = 0;
 
-               if (!view->ops->read(view, line))
+               if (!view->ops->read(view, prev, line))
                        goto alloc_error;
 
                if (lines-- == 1)
@@ -1213,8 +1354,6 @@ update_view(struct view *view)
                goto end;
 
        } else if (feof(view->pipe)) {
-               time_t secs = time(NULL) - view->start_time;
-
                if (view == VIEW(REQ_VIEW_HELP)) {
                        const char *msg = TIG_HELP;
 
@@ -1229,8 +1368,7 @@ update_view(struct view *view)
                        goto end;
                }
 
-               report("Loaded %d lines in %ld second%s", view->lines, secs,
-                      secs == 1 ? "" : "s");
+               report("");
                goto end;
        }
 
@@ -1258,7 +1396,8 @@ open_view(struct view *prev, enum request request, enum open_flags flags)
        bool split = !!(flags & OPEN_SPLIT);
        bool reload = !!(flags & OPEN_RELOAD);
        struct view *view = VIEW(request);
-       int nviews = display[1] ? 2 : 1;
+       int nviews = displayed_views();
+       struct view *base_view = display[0];
 
        if (view == prev && nviews == 1 && !reload) {
                report("Already in %s view", view->name);
@@ -1282,7 +1421,11 @@ open_view(struct view *prev, enum request request, enum open_flags flags)
                display[current_view] = view;
        }
 
-       resize_display();
+       /* Resize the view when switching between split- and full-screen,
+        * or when switching between two different full-screen views. */
+       if (nviews != displayed_views() ||
+           (nviews == 1 && base_view != display[0]))
+               resize_display();
 
        if (split && prev->lineno - prev->offset >= prev->height) {
                /* Take the title line into account. */
@@ -1294,21 +1437,19 @@ open_view(struct view *prev, enum request request, enum open_flags flags)
        }
 
        if (prev && view != prev) {
-               /* "Blur" the previous view. */
-               if (!backgrounded)
+               if (split && !backgrounded) {
+                       /* "Blur" the previous view. */
                        update_view_title(prev);
+               }
 
-               /* Continue loading split views in the background. */
-               if (!split)
-                       end_update(prev);
                view->parent = prev;
        }
 
-       if (view->pipe) {
+       if (view->pipe && view->lines == 0) {
                /* Clear the old view and let the incremental updating refill
                 * the screen. */
                wclear(view->win);
-               report("Loading...");
+               report("");
        } else {
                redraw_view(view);
                if (view == VIEW(REQ_VIEW_HELP))
@@ -1364,11 +1505,12 @@ view_driver(struct view *view, enum request request)
 
                if (view == VIEW(REQ_VIEW_DIFF) &&
                    view->parent == VIEW(REQ_VIEW_MAIN)) {
-                       bool redraw = display[0] == VIEW(REQ_VIEW_MAIN);
+                       bool redraw = display[1] == view;
 
                        view = view->parent;
                        move_view(view, request, redraw);
-                       update_view_title(view);
+                       if (redraw)
+                               update_view_title(view);
                } else {
                        move_view(view, request, TRUE);
                        break;
@@ -1380,11 +1522,11 @@ view_driver(struct view *view, enum request request)
                        report("Nothing to enter");
                        break;
                }
-               return view->ops->enter(view);
+               return view->ops->enter(view, &view->line[view->lineno]);
 
        case REQ_VIEW_NEXT:
        {
-               int nviews = display[1] ? 2 : 1;
+               int nviews = displayed_views();
                int next_view = (current_view + 1) % nviews;
 
                if (next_view == current_view) {
@@ -1409,9 +1551,10 @@ view_driver(struct view *view, enum request request)
                break;
 
        case REQ_STOP_LOADING:
-               foreach_view (view, i) {
+               for (i = 0; i < ARRAY_SIZE(views); i++) {
+                       view = &views[i];
                        if (view->pipe)
-                               report("Stopped loaded the %s view", view->name),
+                               report("Stopped loading the %s view", view->name),
                        end_update(view);
                }
                break;
@@ -1432,11 +1575,15 @@ view_driver(struct view *view, enum request request)
                return TRUE;
 
        case REQ_VIEW_CLOSE:
-               if (view->parent) {
+               /* XXX: Mark closed views by letting view->parent point to the
+                * view itself. Parents to closed view should never be
+                * followed. */
+               if (view->parent &&
+                   view->parent->parent != view->parent) {
                        memset(display, 0, sizeof(display));
                        current_view = 0;
                        display[current_view] = view->parent;
-                       view->parent = NULL;
+                       view->parent = view;
                        resize_display();
                        redraw_display();
                        break;
@@ -1460,24 +1607,18 @@ view_driver(struct view *view, enum request request)
  */
 
 static bool
-pager_draw(struct view *view, unsigned int lineno)
+pager_draw(struct view *view, struct line *line, unsigned int lineno)
 {
-       enum line_type type;
-       char *line;
-       int linelen;
+       char *text = line->data;
+       enum line_type type = line->type;
+       int textlen = strlen(text);
        int attr;
 
-       if (view->offset + lineno >= view->lines)
-               return FALSE;
-
-       line = view->line[view->offset + lineno];
-       type = get_line_type(line);
-
        wmove(view->win, lineno, 0);
 
        if (view->offset + lineno == view->lineno) {
                if (type == LINE_COMMIT) {
-                       string_copy(view->ref, line + 7);
+                       string_copy(view->ref, text + 7);
                        string_copy(ref_commit, view->ref);
                }
 
@@ -1488,8 +1629,6 @@ pager_draw(struct view *view, unsigned int lineno)
        attr = get_line_attr(type);
        wattrset(view->win, attr);
 
-       linelen = strlen(line);
-
        if (opt_line_number || opt_tab_size < TABSIZE) {
                static char spaces[] = "                    ";
                int col_offset = 0, col = 0;
@@ -1509,66 +1648,65 @@ pager_draw(struct view *view, unsigned int lineno)
                        col_offset = view->digits + 2;
                }
 
-               while (line && col_offset + col < view->width) {
+               while (text && col_offset + col < view->width) {
                        int cols_max = view->width - col_offset - col;
-                       char *text = line;
+                       char *pos = text;
                        int cols;
 
-                       if (*line == '\t') {
+                       if (*text == '\t') {
+                               text++;
                                assert(sizeof(spaces) > TABSIZE);
-                               line++;
-                               text = spaces;
+                               pos = spaces;
                                cols = opt_tab_size - (col % opt_tab_size);
 
                        } else {
-                               line = strchr(line, '\t');
-                               cols = line ? line - text : strlen(text);
+                               text = strchr(text, '\t');
+                               cols = line ? text - pos : strlen(pos);
                        }
 
-                       waddnstr(view->win, text, MIN(cols, cols_max));
+                       waddnstr(view->win, pos, MIN(cols, cols_max));
                        col += cols;
                }
 
        } else {
                int col = 0, pos = 0;
 
-               for (; pos < linelen && col < view->width; pos++, col++)
-                       if (line[pos] == '\t')
+               for (; pos < textlen && col < view->width; pos++, col++)
+                       if (text[pos] == '\t')
                                col += TABSIZE - (col % TABSIZE) - 1;
 
-               waddnstr(view->win, line, pos);
+               waddnstr(view->win, text, pos);
        }
 
        return TRUE;
 }
 
 static bool
-pager_read(struct view *view, char *line)
+pager_read(struct view *view, struct line *prev, char *line)
 {
        /* Compress empty lines in the help view. */
        if (view == VIEW(REQ_VIEW_HELP) &&
-           !*line &&
-           view->lines &&
-           !*((char *) view->line[view->lines - 1]))
+           !*line && prev && !*((char *) prev->data))
                return TRUE;
 
-       view->line[view->lines] = strdup(line);
-       if (!view->line[view->lines])
+       view->line[view->lines].data = strdup(line);
+       if (!view->line[view->lines].data)
                return FALSE;
 
+       view->line[view->lines].type = get_line_type(line);
+
        view->lines++;
        return TRUE;
 }
 
 static bool
-pager_enter(struct view *view)
+pager_enter(struct view *view, struct line *line)
 {
-       char *line = view->line[view->lineno];
        int split = 0;
 
-       if ((view == VIEW(REQ_VIEW_LOG) ||
-            view == VIEW(REQ_VIEW_PAGER)) &&
-           get_line_type(line) == LINE_COMMIT) {
+       if (line->type == LINE_COMMIT &&
+          (view == VIEW(REQ_VIEW_LOG) ||
+           view == VIEW(REQ_VIEW_PAGER))) {
                open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
                split = 1;
        }
@@ -1579,8 +1717,8 @@ pager_enter(struct view *view)
        scroll_view(view, REQ_SCROLL_LINE_DOWN);
 
        /* FIXME: A minor workaround. Scrolling the view will call report("")
-        * but if we are scolling a non-current view this won't properly update
-        * the view title. */
+        * but if we are scrolling a non-current view this won't properly
+        * update the view title. */
        if (split)
                update_view_title(view);
 
@@ -1608,20 +1746,16 @@ struct commit {
 };
 
 static bool
-main_draw(struct view *view, unsigned int lineno)
+main_draw(struct view *view, struct line *line, unsigned int lineno)
 {
        char buf[DATE_COLS + 1];
-       struct commit *commit;
+       struct commit *commit = line->data;
        enum line_type type;
        int col = 0;
        size_t timelen;
        size_t authorlen;
-       int trimmed;
+       int trimmed = 1;
 
-       if (view->offset + lineno >= view->lines)
-               return FALSE;
-
-       commit = view->line[view->offset + lineno];
        if (!*commit->author)
                return FALSE;
 
@@ -1648,8 +1782,15 @@ main_draw(struct view *view, unsigned int lineno)
        if (type != LINE_CURSOR)
                wattrset(view->win, get_line_attr(LINE_MAIN_AUTHOR));
 
-       /* FIXME: Make this optional, and add i18n.commitEncoding support. */
-       authorlen = utf8_length(commit->author, AUTHOR_COLS - 2, &col, &trimmed);
+       if (opt_utf8) {
+               authorlen = utf8_length(commit->author, AUTHOR_COLS - 2, &col, &trimmed);
+       } else {
+               authorlen = strlen(commit->author);
+               if (authorlen > AUTHOR_COLS - 2) {
+                       authorlen = AUTHOR_COLS - 2;
+                       trimmed = 1;
+               }
+       }
 
        if (trimmed) {
                waddnstr(view->win, commit->author, authorlen);
@@ -1705,7 +1846,7 @@ main_draw(struct view *view, unsigned int lineno)
 
 /* Reads git log --pretty=raw output and parses it into the commit struct. */
 static bool
-main_read(struct view *view, char *line)
+main_read(struct view *view, struct line *prev, char *line)
 {
        enum line_type type = get_line_type(line);
        struct commit *commit;
@@ -1718,7 +1859,7 @@ main_read(struct view *view, char *line)
 
                line += STRING_SIZE("commit ");
 
-               view->line[view->lines++] = commit;
+               view->line[view->lines++].data = commit;
                string_copy(commit->id, line);
                commit->refs = get_refs(commit->id);
                break;
@@ -1728,12 +1869,16 @@ main_read(struct view *view, char *line)
                char *ident = line + STRING_SIZE("author ");
                char *end = strchr(ident, '<');
 
+               if (!prev)
+                       break;
+
+               commit = prev->data;
+
                if (end) {
                        for (; end > ident && isspace(end[-1]); end--) ;
                        *end = 0;
                }
 
-               commit = view->line[view->lines - 1];
                string_copy(commit->author, ident);
 
                /* Parse epoch and timezone */
@@ -1767,13 +1912,12 @@ main_read(struct view *view, char *line)
                break;
        }
        default:
-               /* We should only ever end up here if there has already been a
-                * commit line, however, be safe. */
-               if (view->lines == 0)
+               if (!prev)
                        break;
 
+               commit = prev->data;
+
                /* Fill in the commit title if it has not already been set. */
-               commit = view->line[view->lines - 1];
                if (commit->title[0])
                        break;
 
@@ -1792,7 +1936,7 @@ main_read(struct view *view, char *line)
 }
 
 static bool
-main_enter(struct view *view)
+main_enter(struct view *view, struct line *line)
 {
        enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
 
@@ -1808,11 +1952,9 @@ static struct view_ops main_ops = {
 };
 
 
-/**
- * KEYS
- * ----
- * Below the default key bindings are shown.
- **/
+/*
+ * Keys
+ */
 
 struct keymap {
        int alias;
@@ -1820,74 +1962,21 @@ struct keymap {
 };
 
 static struct keymap keymap[] = {
-       /**
-        * View switching
-        * ~~~~~~~~~~~~~~
-        * m::
-        *      Switch to main view.
-        * d::
-        *      Switch to diff view.
-        * l::
-        *      Switch to log view.
-        * p::
-        *      Switch to pager view.
-        * h::
-        *      Show man page.
-        **/
+       /* View switching */
        { 'm',          REQ_VIEW_MAIN },
        { 'd',          REQ_VIEW_DIFF },
        { 'l',          REQ_VIEW_LOG },
        { 'p',          REQ_VIEW_PAGER },
        { 'h',          REQ_VIEW_HELP },
 
-       /**
-        * View manipulation
-        * ~~~~~~~~~~~~~~~~~
-        * q::
-        *      Close view, if multiple views are open it will jump back to the
-        *      previous view in the view stack. If it is the last open view it
-        *      will quit. Use 'Q' to quit all views at once.
-        * Enter::
-        *      This key is "context sensitive" depending on what view you are
-        *      currently in. When in log view on a commit line or in the main
-        *      view, split the view and show the commit diff. In the diff view
-        *      pressing Enter will simply scroll the view one line down.
-        * Tab::
-        *      Switch to next view.
-        * Up::
-        *      This key is "context sensitive" and will move the cursor one
-        *      line up. However, uf you opened a diff view from the main view
-        *      (split- or full-screen) it will change the cursor to point to
-        *      the previous commit in the main view and update the diff view
-        *      to display it.
-        * Down::
-        *      Similar to 'Up' but will move down.
-        **/
+       /* View manipulation */
        { 'q',          REQ_VIEW_CLOSE },
        { KEY_TAB,      REQ_VIEW_NEXT },
        { KEY_RETURN,   REQ_ENTER },
        { KEY_UP,       REQ_PREVIOUS },
        { KEY_DOWN,     REQ_NEXT },
 
-       /**
-        * Cursor navigation
-        * ~~~~~~~~~~~~~~~~~
-        * j::
-        *      Move cursor one line up.
-        * k::
-        *      Move cursor one line down.
-        * PgUp::
-        * b::
-        * -::
-        *      Move cursor one page up.
-        * PgDown::
-        * Space::
-        *      Move cursor one page down.
-        * Home::
-        *      Jump to first line.
-        * End::
-        *      Jump to last line.
-        **/
+       /* Cursor navigation */
        { 'k',          REQ_MOVE_UP },
        { 'j',          REQ_MOVE_DOWN },
        { KEY_HOME,     REQ_MOVE_FIRST_LINE },
@@ -1898,44 +1987,13 @@ static struct keymap keymap[] = {
        { 'b',          REQ_MOVE_PAGE_UP },
        { '-',          REQ_MOVE_PAGE_UP },
 
-       /**
-        * Scrolling
-        * ~~~~~~~~~
-        * Insert::
-        *      Scroll view one line up.
-        * Delete::
-        *      Scroll view one line down.
-        * w::
-        *      Scroll view one page up.
-        * s::
-        *      Scroll view one page down.
-        **/
+       /* Scrolling */
        { KEY_IC,       REQ_SCROLL_LINE_UP },
        { KEY_DC,       REQ_SCROLL_LINE_DOWN },
        { 'w',          REQ_SCROLL_PAGE_UP },
        { 's',          REQ_SCROLL_PAGE_DOWN },
 
-       /**
-        * Misc
-        * ~~~~
-        * Q::
-        *      Quit.
-        * r::
-        *      Redraw screen.
-        * z::
-        *      Stop all background loading. This can be useful if you use
-        *      tig(1) in a repository with a long history without limiting
-        *      the revision log.
-        * v::
-        *      Show version.
-        * n::
-        *      Toggle line numbers on/off.
-        * ':'::
-        *      Open prompt. This allows you to specify what git command
-        *      to run. Example:
-        *
-        *      :log -p
-        **/
+       /* Misc */
        { 'Q',          REQ_QUIT },
        { 'z',          REQ_STOP_LOADING },
        { 'v',          REQ_SHOW_VERSION },
@@ -1983,7 +2041,7 @@ unicode_width(unsigned long c)
            || c == 0x2329
            || c == 0x232a
            || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
-                                               /* CJK ... Yi */
+                                               /* CJK ... Yi */
            || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
            || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
            || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
@@ -2159,15 +2217,7 @@ report(const char *msg, ...)
        }
 
        update_view_title(view);
-
-       /* Move the cursor to the right-most column of the cursor line.
-        *
-        * XXX: This could turn out to be a bit expensive, but it ensures that
-        * the cursor does not jump around. */
-       if (view->lines) {
-               wmove(view->win, view->lineno - view->offset, view->width - 1);
-               wrefresh(view->win);
-       }
+       update_display_cursor();
 }
 
 /* Controls when nodelay should be in effect when polling user input. */
@@ -2278,74 +2328,117 @@ get_refs(char *id)
        return ref_list;
 }
 
+static int
+read_ref(char *id, int idlen, char *name, int namelen)
+{
+       struct ref *ref;
+       bool tag = FALSE;
+       bool tag_commit = FALSE;
+
+       /* Commits referenced by tags has "^{}" appended. */
+       if (name[namelen - 1] == '}') {
+               while (namelen > 0 && name[namelen] != '^')
+                       namelen--;
+               if (namelen > 0)
+                       tag_commit = TRUE;
+               name[namelen] = 0;
+       }
+
+       if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
+               if (!tag_commit)
+                       return OK;
+               name += STRING_SIZE("refs/tags/");
+               tag = TRUE;
+
+       } else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
+               name += STRING_SIZE("refs/heads/");
+
+       } else if (!strcmp(name, "HEAD")) {
+               return OK;
+       }
+
+       refs = realloc(refs, sizeof(*refs) * (refs_size + 1));
+       if (!refs)
+               return ERR;
+
+       ref = &refs[refs_size++];
+       ref->name = strdup(name);
+       if (!ref->name)
+               return ERR;
+
+       ref->tag = tag;
+       string_copy(ref->id, id);
+
+       return OK;
+}
+
 static int
 load_refs(void)
 {
        const char *cmd_env = getenv("TIG_LS_REMOTE");
        const char *cmd = cmd_env && *cmd_env ? cmd_env : TIG_LS_REMOTE;
-       FILE *pipe = popen(cmd, "r");
-       char buffer[BUFSIZ];
-       char *line;
 
-       if (!pipe)
-               return ERR;
+       return read_properties(popen(cmd, "r"), "\t", read_ref);
+}
 
-       while ((line = fgets(buffer, sizeof(buffer), pipe))) {
-               char *name = strchr(line, '\t');
-               struct ref *ref;
-               int namelen;
-               bool tag = FALSE;
-               bool tag_commit = FALSE;
+static int
+read_repo_config_option(char *name, int namelen, char *value, int valuelen)
+{
+       if (!strcmp(name, "i18n.commitencoding")) {
+               string_copy(opt_encoding, value);
+       }
 
-               if (!name)
-                       continue;
+       return OK;
+}
 
-               *name++ = 0;
-               namelen = strlen(name) - 1;
+static int
+load_repo_config(void)
+{
+       return read_properties(popen("git repo-config --list", "r"),
+                              "=", read_repo_config_option);
+}
 
-               /* Commits referenced by tags has "^{}" appended. */
-               if (name[namelen - 1] == '}') {
-                       while (namelen > 0 && name[namelen] != '^')
-                               namelen--;
-                       if (namelen > 0)
-                               tag_commit = TRUE;
-               }
-               name[namelen] = 0;
+static int
+read_properties(FILE *pipe, const char *separators,
+               int (*read_property)(char *, int, char *, int))
+{
+       char buffer[BUFSIZ];
+       char *name;
+       int state = OK;
 
-               if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
-                       if (!tag_commit)
-                               continue;
-                       name += STRING_SIZE("refs/tags/");
-                       tag = TRUE;
+       if (!pipe)
+               return ERR;
 
-               } else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
-                       name += STRING_SIZE("refs/heads/");
+       while (state == OK && (name = fgets(buffer, sizeof(buffer), pipe))) {
+               char *value;
+               size_t namelen;
+               size_t valuelen;
 
-               } else if (!strcmp(name, "HEAD")) {
-                       continue;
-               }
+               name = chomp_string(name);
+               namelen = strcspn(name, separators);
 
-               refs = realloc(refs, sizeof(*refs) * (refs_size + 1));
-               if (!refs)
-                       return ERR;
+               if (name[namelen]) {
+                       name[namelen] = 0;
+                       value = chomp_string(name + namelen + 1);
+                       valuelen = strlen(value);
 
-               ref = &refs[refs_size++];
-               ref->tag = tag;
-               ref->name = strdup(name);
-               if (!ref->name)
-                       return ERR;
+               } else {
+                       value = "";
+                       valuelen = 0;
+               }
 
-               string_copy(ref->id, line);
+               state = read_property(name, namelen, value, valuelen);
        }
 
-       if (ferror(pipe))
-               return ERR;
+       if (state != ERR && ferror(pipe))
+               state = ERR;
 
        pclose(pipe);
 
-       return OK;
+       return state;
 }
 
+
 /*
  * Main
  */
@@ -2390,6 +2483,14 @@ main(int argc, char *argv[])
 
        signal(SIGINT, quit);
 
+       if (load_options() == ERR)
+               die("Failed to load user config.");
+
+       /* Load the repo config file so options can be overwritten from
+        * the command line.  */
+       if (load_repo_config() == ERR)
+               die("Failed to load repo config.");
+
        if (!parse_options(argc, argv))
                return 0;
 
@@ -2432,7 +2533,9 @@ main(int argc, char *argv[])
                                memcpy(opt_cmd, "git ", 4);
                                opt_request = REQ_VIEW_PAGER;
                        } else {
-                               request = ERR;
+                               report("Prompt interrupted by loading view, "
+                                      "press 'z' to stop loading views");
+                               request = REQ_SCREEN_UPDATE;
                        }
 
                        noecho();
@@ -2463,121 +2566,11 @@ main(int argc, char *argv[])
 }
 
 /**
- * [[refspec]]
- * Revision specification
- * ----------------------
- * This section describes various ways to specify what revisions to display
- * or otherwise limit the view to. tig(1) does not itself parse the described
- * revision options so refer to the relevant git man pages for futher
- * information. Relevant man pages besides git-log(1) are git-diff(1) and
- * git-rev-list(1).
- *
- * You can tune the interaction with git by making use of the options
- * explained in this section. For example, by configuring the environment
- * variables described in the  <<view-commands, "View commands">> section.
- *
- * Limit by path name
- * ~~~~~~~~~~~~~~~~~~
- * If you are interested only in those revisions that made changes to a
- * specific file (or even several files) list the files like this:
- *
- *     $ tig log Makefile README
- *
- * To avoid ambiguity with repository references such as tag name, be sure
- * to separate file names from other git options using "\--". So if you
- * have a file named 'master' it will clash with the reference named
- * 'master', and thus you will have to use:
- *
- *     $ tig log -- master
- *
- * NOTE: For the main view, avoiding ambiguity will in some cases require
- * you to specify two "\--" options. The first will make tig(1) stop
- * option processing and the latter will be passed to git log.
- *
- * Limit by date or number
- * ~~~~~~~~~~~~~~~~~~~~~~~
- * To speed up interaction with git, you can limit the amount of commits
- * to show both for the log and main view. Either limit by date using
- * e.g. `--since=1.month` or limit by the number of commits using `-n400`.
- *
- * If you are only interested in changed that happened between two dates
- * you can use:
- *
- *     $ tig -- --after="May 5th" --before="2006-05-16 15:44"
- *
- * NOTE: If you want to avoid having to quote dates containing spaces you
- * can use "." instead, e.g. `--after=May.5th`.
- *
- * Limiting by commit ranges
- * ~~~~~~~~~~~~~~~~~~~~~~~~~
- * Alternatively, commits can be limited to a specific range, such as
- * "all commits between 'tag-1.0' and 'tag-2.0'". For example:
- *
- *     $ tig log tag-1.0..tag-2.0
- *
- * This way of commit limiting makes it trivial to only browse the commits
- * which haven't been pushed to a remote branch. Assuming 'origin' is your
- * upstream remote branch, using:
- *
- *     $ tig log origin..HEAD
- *
- * will list what will be pushed to the remote branch. Optionally, the ending
- * 'HEAD' can be left out since it is implied.
- *
- * Limiting by reachability
- * ~~~~~~~~~~~~~~~~~~~~~~~~
- * Git interprets the range specifier "tag-1.0..tag-2.0" as
- * "all commits reachable from 'tag-2.0' but not from 'tag-1.0'".
- * Where reachability refers to what commits are ancestors (or part of the
- * history) of the branch or tagged revision in question.
- *
- * If you prefer to specify which commit to preview in this way use the
- * following:
- *
- *     $ tig log tag-2.0 ^tag-1.0
- *
- * You can think of '^' as a negation operator. Using this alternate syntax,
- * it is possible to further prune commits by specifying multiple branch
- * cut offs.
- *
- * Combining revisions specification
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * Revisions options can to some degree be combined, which makes it possible
- * to say "show at most 20 commits from within the last month that changed
- * files under the Documentation/ directory."
- *
- *     $ tig -- --since=1.month -n20 -- Documentation/
- *
- * Examining all repository references
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * In some cases, it can be useful to query changes across all references
- * in a repository. An example is to ask "did any line of development in
- * this repository change a particular file within the last week". This
- * can be accomplished using:
- *
- *     $ tig -- --all --since=1.week -- Makefile
- *
- * BUGS
- * ----
- * Known bugs and problems:
- *
- * - In it's current state tig is pretty much UTF-8 only.
- *
- * - If the screen width is very small the main view can draw
- *   outside the current view causing bad wrapping. Same goes
- *   for title and status windows.
- *
- * TODO
- * ----
- * Features that should be explored.
- *
- * - Searching.
- *
- * - Locale support.
+ * include::BUGS[]
  *
  * COPYRIGHT
  * ---------
- * Copyright (c) Jonas Fonseca <fonseca@diku.dk>, 2006
+ * Copyright (c) 2006 Jonas Fonseca <fonseca@diku.dk>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -2586,10 +2579,16 @@ main(int argc, char *argv[])
  *
  * SEE ALSO
  * --------
- * [verse]
- * link:http://www.kernel.org/pub/software/scm/git/docs/[git(7)],
- * link:http://www.kernel.org/pub/software/scm/cogito/docs/[cogito(7)]
- * gitk(1): git repository browser written using tcl/tk,
- * qgit(1): git repository browser written using c++/Qt,
- * gitview(1): git repository browser written using python/gtk.
+ * - link:http://www.kernel.org/pub/software/scm/git/docs/[git(7)],
+ * - link:http://www.kernel.org/pub/software/scm/cogito/docs/[cogito(7)]
+ *
+ * Other git repository browsers:
+ *
+ *  - gitk(1)
+ *  - qgit(1)
+ *  - gitview(1)
+ *
+ * Sites:
+ *
+ * include::SITES[]
  **/
diff --git a/tigrc.5.txt b/tigrc.5.txt
new file mode 100644 (file)
index 0000000..522dad4
--- /dev/null
@@ -0,0 +1,122 @@
+tigrc(5)
+========
+
+NAME
+----
+tigrc - tig user configuration file
+
+SYNOPSIS
+--------
+[verse]
+.............................................................................
+*color* 'fgcolor' 'bgcolor' '[attributes]'
+.............................................................................
+
+DESCRIPTION
+-----------
+You can permanently set an option by putting it in the `~/.tigrc` file.
+The file consists of a series of 'commands'.  Each
+line of the file may contain only one command.
+
+The hash mark ('#'), or semi-colon (';') is used as a 'comment' character.
+All text after the comment character to the end of the line is ignored.
+You can use comments to annotate your initialization file.
+
+Color options
+-------------
+
+Color options control highlighting and the user interface styles.
+If  your terminal supports color, these commands can be used to assign
+foreground/backgound combinations to certain areas. Optionally, an
+attribute can be given as the last parameter. The syntax is:
+
+[verse]
+..........................................................................
+       *color* 'area' 'fgcolor' 'bgcolor' '[attributes]'
+..........................................................................
+
+Valid colors include: *white*, *black*, *green*, *magenta*, *blue*, *cyan*,
+*yellow*, *red*, *default*. Use *default* to refer to the default terminal
+colors.
+
+Valid attributes include: *normal*, *blink*, *bold*, *dim*, *reverse*, *standout*,
+and *underline*. Note, not all attributes may be supported by the terminal.
+
+Valid area names are described below. Note, all names are case-insensitive,
+and you may use '-', '_', and '.' interchangeably. So "Diff-Header",
+"DIFF_HEADER", and "diff.header" are the same.
+
+Diff markup
+~~~~~~~~~~~
+
+Options concerning diff start, chunks and lines added and deleted.
+
+*diff-header*, *diff-chunk*, *diff-add*, *diff-del*
+
+Enhanced git diff markup
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Extra diff information emitted by the git diff machinery, such as mode
+changes, rename detection, and similarity.
+
+*diff-oldmode*, *diff-newmode*, *diff-copy-from*, *diff-copy-to*,
+*diff-rename-from*, *diff-rename-to*, *diff-similarity* *diff-dissimilarity*
+*diff-tree*, *diff-index*
+
+Pretty print commit headers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Commit diffs and the revision logs are usually formatted using pretty
+printed headers , unless `--pretty=raw` was given. This includes lines,
+such as merge info, commit ID, and author and comitter date.
+
+*pp-author*, *pp-commit*, *pp-merge*, *pp-date*, *pp-adate*, *pp-cdate*
+
+Raw commit header
+~~~~~~~~~~~~~~~~~
+
+Usually shown when `--pretty=raw` is given, however 'commit' is pretty
+much omnipresent.
+
+*commit*, *parent*, *tree*, *author*, *committer*
+
+Commit message
+~~~~~~~~~~~~~~
+
+For now only `Signed-off-by lines` are colorized.
+
+*signoff*
+
+UI colors
+~~~~~~~~~
+
+Colors for text not matching any of the above: *default*
+
+Status window colors: *status*
+
+Title window colors: *title-blur*, *title-focus*
+
+Cursor line colors: *cursor*
+
+Main view specific: *main-date*, *main-author*, *main-commit*, *main-delim*,
+*main-tag*, *main-ref*
+
+
+EXAMPLES
+--------
+
+Some sample options:
+
+==========================================================================
+       # Diff colors
+       color diff-header       yellow  default
+       color diff-index        blue    default
+       color diff-chunk        magenta default
+       # UI colors
+       color title-blur        white   blue
+       color title-focus       white   blue    bold
+==========================================================================
+
+SEE ALSO
+--------
+tig(1)
index 7ed4c3d0815fd24d61e97948522202976762953f..ed09fe897f3c7c9af90bcf80cae92558ea88ae38 100644 (file)
--- a/web.conf
+++ b/web.conf
@@ -1,5 +1,7 @@
 # No header or footers for README.html
 [header]
+<div style="font-family: sans-serif;">
+<h1>{doctitle}</h1>
 
 [footer]
-
+</div>