* 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),
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), \
+LINE(PP_REFS, "Refs: ", COLOR_RED, COLOR_DEFAULT, 0), \
LINE(COMMIT, "commit ", COLOR_GREEN, COLOR_DEFAULT, 0), \
LINE(PARENT, "parent ", COLOR_BLUE, COLOR_DEFAULT, 0), \
LINE(TREE, "tree ", COLOR_BLUE, COLOR_DEFAULT, 0), \
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 TRUE;
}
+static void
+add_pager_refs(struct view *view, struct line *line)
+{
+ char buf[1024];
+ char *data = line->data;
+ struct ref **refs;
+ int bufpos = 0, refpos = 0;
+ const char *sep = "Refs: ";
+
+ assert(line->type == LINE_COMMIT);
+
+ refs = get_refs(data + STRING_SIZE("commit "));
+ if (!refs)
+ return;
+
+ do {
+ struct ref *ref = refs[refpos];
+ char *fmt = ref->tag ? "%s[%s]" : "%s%s";
+
+ if (!string_format_from(buf, &bufpos, fmt, sep, ref->name))
+ return;
+ sep = ", ";
+ } while (refs[refpos++]->next);
+
+ if (!realloc_lines(view, view->line_size + 1))
+ return;
+
+ line = &view->line[view->lines];
+ line->data = strdup(buf);
+ if (!line->data)
+ return;
+
+ line->type = LINE_PP_REFS;
+ view->lines++;
+}
+
static bool
-pager_read(struct view *view, struct line *prev, char *line)
+pager_read(struct view *view, char *data)
{
- view->line[view->lines].data = strdup(line);
- if (!view->line[view->lines].data)
- return FALSE;
+ struct line *line = &view->line[view->lines];
- view->line[view->lines].type = get_line_type(line);
+ line->data = strdup(data);
+ if (!line->data)
+ return FALSE;
+ line->type = get_line_type(line->data);
view->lines++;
+
+ if (line->type == LINE_COMMIT &&
+ (view == VIEW(REQ_VIEW_DIFF) ||
+ view == VIEW(REQ_VIEW_LOG)))
+ add_pager_refs(view, line);
+
return TRUE;
}
/* 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[]
- **/