* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-/**
- * TIG(1)
- * ======
- *
- * NAME
- * ----
- * tig - text-mode interface for git
- *
- * SYNOPSIS
- * --------
- * [verse]
- * tig [options]
- * tig [options] [--] [git log options]
- * tig [options] log [git log options]
- * tig [options] diff [git diff options]
- * tig [options] show [git show options]
- * tig [options] < [git command output]
- *
- * DESCRIPTION
- * -----------
- * Browse changes in a git repository. Additionally, tig(1) can also act
- * as a pager for output of various git commands.
- *
- * When browsing repositories, tig(1) 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.
- *
- * Using tig(1) as a pager, it will display input from stdin and try
- * to colorize it.
- **/
#ifndef VERSION
#define VERSION "tig-0.3"
#define SCALE_SPLIT_VIEW(height) ((height) * 2 / 3)
+#define TIG_LS_REMOTE \
+ "git ls-remote . 2>/dev/null"
+
+#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"
+
+/* XXX: Needs to be defined to the empty string. */
+#define TIG_HELP_CMD ""
+#define TIG_PAGER_CMD ""
+
/* Some ascii-shorthands fitted into the ncurses namespace. */
#define KEY_TAB '\t'
#define KEY_RETURN '\r'
return name;
}
+static bool
+string_nformat(char *buf, size_t bufsize, int *bufpos, const char *fmt, ...)
+{
+ va_list args;
+ int pos = bufpos ? *bufpos : 0;
+
+ va_start(args, fmt);
+ pos += vsnprintf(buf + pos, bufsize - pos, fmt, args);
+ va_end(args);
+
+ if (bufpos)
+ *bufpos = pos;
+
+ return pos >= bufsize ? FALSE : TRUE;
+}
+
+#define string_format(buf, fmt, args...) \
+ string_nformat(buf, sizeof(buf), NULL, fmt, args)
+
+#define string_format_from(buf, from, fmt, args...) \
+ string_nformat(buf, sizeof(buf), from, fmt, args)
/* Shell quoting
*
#undef REQ_
};
-/**
- * OPTIONS
- * -------
- **/
+/*
+ * Options
+ */
static const char usage[] =
VERSION " (" __DATE__ ")\n"
for (i = 1; i < argc; i++) {
char *opt = argv[i];
- /**
- * -l::
- * Start up in log view using the internal log command.
- **/
if (!strcmp(opt, "-l")) {
opt_request = REQ_VIEW_LOG;
continue;
}
- /**
- * -d::
- * Start up in diff view using the internal diff command.
- **/
if (!strcmp(opt, "-d")) {
opt_request = REQ_VIEW_DIFF;
continue;
}
- /**
- * -n[INTERVAL], --line-number[=INTERVAL]::
- * Prefix line numbers in log and diff view.
- * Optionally, with interval different than each line.
- **/
if (!strncmp(opt, "-n", 2) ||
!strncmp(opt, "--line-number", 13)) {
char *num = opt;
continue;
}
- /**
- * -b[NSPACES], --tab-size[=NSPACES]::
- * Set the number of spaces tabs should be expanded to.
- **/
if (!strncmp(opt, "-b", 2) ||
!strncmp(opt, "--tab-size", 10)) {
char *num = opt;
continue;
}
- /**
- * -v, --version::
- * Show version and exit.
- **/
if (!strcmp(opt, "-v") ||
!strcmp(opt, "--version")) {
printf("tig version %s\n", VERSION);
return FALSE;
}
- /**
- * -h, --help::
- * Show help message and exit.
- **/
if (!strcmp(opt, "-h") ||
!strcmp(opt, "--help")) {
printf(usage);
return FALSE;
}
- /**
- * \--::
- * End of tig(1) options. Useful when specifying command
- * options for the main view. Example:
- *
- * $ tig -- --since=1.month
- **/
if (!strcmp(opt, "--")) {
i++;
break;
}
- /**
- * log [git log options]::
- * Open log view using the given git log options.
- *
- * diff [git diff options]::
- * Open diff view using the given git diff options.
- *
- * show [git show options]::
- * Open diff view using the given git show options.
- **/
if (!strcmp(opt, "log") ||
!strcmp(opt, "diff") ||
!strcmp(opt, "show")) {
break;
}
- /**
- * [git log options]::
- * tig(1) will stop the option parsing when the first
- * command line parameter not starting with "-" is
- * encountered. All options including this one will be
- * passed to git log when loading the main view.
- * This makes it possible to say:
- *
- * $ tig tag-1.0..HEAD
- **/
if (opt[0] && opt[0] != '-')
break;
}
-/**
- * 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 \
- ""
-
-#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),
config_lineno = 0;
config_errors = FALSE;
- if (!home ||
- snprintf(buf, sizeof(buf), "%s/.tigrc", home) >= sizeof(buf))
+ if (!home || !string_format(buf, "%s/.tigrc", home))
return ERR;
/* It's ok that the file doesn't exist. */
/* 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);
+ bool (*read)(struct view *view, char *data);
/* Depending on view, change display based on current line. */
bool (*enter)(struct view *view, struct line *line);
};
} else {
const char *format = view->cmd_env ? view->cmd_env : view->cmd_fmt;
- if (snprintf(view->cmd, sizeof(view->cmd), format,
- id, id, id, id, id) >= sizeof(view->cmd))
+ if (!string_format(view->cmd, format, id, id, id, id, id))
return FALSE;
}
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, prev, line))
+ if (!view->ops->read(view, line))
goto alloc_error;
if (lines-- == 1)
return;
do {
- char *begin = "", *end = "";
+ struct ref *ref = refs[refpos];
+ char *fmt = ref->tag ? "%s[%s]" : "%s%s";
- if (refs[refpos]->tag) {
- begin = "[";
- end = "]";
- }
-
- bufpos += snprintf(buf + bufpos, sizeof(buf) - bufpos,
- "%s%s%s%s", sep, begin, refs[refpos]->name,
- end);
- if (bufpos >= sizeof(buf))
- break;
+ if (!string_format_from(buf, &bufpos, fmt, sep, ref->name))
+ return;
sep = ", ";
} while (refs[refpos++]->next);
- if (!bufpos ||
- bufpos >= sizeof(buf) ||
- !realloc_lines(view, view->line_size + 1))
+ if (!realloc_lines(view, view->line_size + 1))
return;
line = &view->line[view->lines];
}
static bool
-pager_read(struct view *view, struct line *prev, char *data)
+pager_read(struct view *view, char *data)
{
struct line *line = &view->line[view->lines];
/* Reads git log --pretty=raw output and parses it into the commit struct. */
static bool
-main_read(struct view *view, struct line *prev, char *line)
+main_read(struct view *view, char *line)
{
enum line_type type = get_line_type(line);
- struct commit *commit;
+ struct commit *commit = view->lines
+ ? view->line[view->lines - 1].data : NULL;
switch (type) {
case LINE_COMMIT:
char *ident = line + STRING_SIZE("author ");
char *end = strchr(ident, '<');
- if (!prev)
+ if (!commit)
break;
- commit = prev->data;
-
if (end) {
for (; end > ident && isspace(end[-1]); end--) ;
*end = 0;
break;
}
default:
- if (!prev)
+ if (!commit)
break;
- commit = prev->data;
-
/* Fill in the commit title if it has not already been set. */
if (commit->title[0])
break;
if (!seq)
seq = "'?'";
- pos += snprintf(buf + pos, sizeof(buf) - pos, "%s%s", sep, seq);
- if (pos >= sizeof(buf))
+ if (!string_format_from(buf, &pos, "%s%s", sep, seq))
return "Too many keybindings!";
sep = ", ";
}
return;
}
- pager_read(view, NULL, "Quick reference for tig keybindings:");
+ pager_read(view, "Quick reference for tig keybindings:");
for (i = 0; i < ARRAY_SIZE(req_info); i++) {
char *key;
if (!req_info[i].request) {
- pager_read(view, NULL, "");
- pager_read(view, NULL, req_info[i].help);
+ pager_read(view, "");
+ pager_read(view, req_info[i].help);
continue;
}
key = get_key(req_info[i].request);
- if (snprintf(buf, sizeof(buf), "%-25s %s", key, req_info[i].help)
- >= sizeof(buf))
+ if (!string_format(buf, "%-25s %s", key, req_info[i].help))
continue;
- pager_read(view, NULL, buf);
+ pager_read(view, buf);
}
}
return 0;
}
-
-/**
- * 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.
- *
- * SEE ALSO
- * --------
- * - 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[]
- **/