* [PATCH v0 1/2] git-less: a specialized pager for git-log
2012-03-22 18:42 [PATCH v0 0/2] git-less: a specialized pager for git-log Hitoshi Mitake
@ 2012-03-22 18:42 ` Hitoshi Mitake
2012-03-22 18:42 ` [PATCH v0 2/2] git-less: git side support for git-less Hitoshi Mitake
` (2 subsequent siblings)
3 siblings, 0 replies; 10+ messages in thread
From: Hitoshi Mitake @ 2012-03-22 18:42 UTC (permalink / raw)
To: gitster; +Cc: git, Hitoshi Mitake
This patch adds a specialized pager for git-log: "git-less". Basically git-less
works like ordinal less, but it uses some new keys. The important feature of
this pager is git's commit oriented log viewing.
This is the key mapping of commit oriented operations:
h ... show previous commit
l ... show next commit
g ... goto top of viewing commit
G ... goto bottom of viewing commit
H ... show root of the commit tree
L ... show HEAD of the commit tree
\ ... regex forward search only in viewing commit
! ... regex backward search only in viewing commit
As this mapping shows, commit oriented move and search are implemented.
Especially I like commit local regex searches.
This patch adds the new source file less.c, the implementation of git-less. And
modifies .gitignore and Makefile for ignoring the binary and buliding.
Signed-off-by: Hitoshi Mitake <h.mitake@gmail.com>
---
.gitignore | 1 +
Makefile | 5 +
less.c | 899 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 905 insertions(+)
create mode 100644 less.c
diff --git a/.gitignore b/.gitignore
index 87fcc5f..81dba3a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -163,6 +163,7 @@
/git-web--browse
/git-whatchanged
/git-write-tree
+/git-less
/git-core-*/?*
/gitk-git/gitk-wish
/gitweb/GITWEB-BUILD-OPTIONS
diff --git a/Makefile b/Makefile
index be1957a..48600d5 100644
--- a/Makefile
+++ b/Makefile
@@ -463,6 +463,7 @@ PROGRAM_OBJS += upload-pack.o
PROGRAM_OBJS += http-backend.o
PROGRAM_OBJS += sh-i18n--envsubst.o
PROGRAM_OBJS += credential-store.o
+PROGRAM_OBJS += less.o
# Binary suffix, set to .exe for Windows builds
X =
@@ -2239,6 +2240,10 @@ git-http-push$X: revision.o http.o http-push.o GIT-LDFLAGS $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
+gitless$X: gitless.o
+ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+ $(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)
+
$(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY)
$(QUIET_LNCP)$(RM) $@ && \
ln $< $@ 2>/dev/null || \
diff --git a/less.c b/less.c
new file mode 100644
index 0000000..9848eca
--- /dev/null
+++ b/less.c
@@ -0,0 +1,899 @@
+/*
+ * git-less - a specialized pager for git-log
+ *
+ * Copyright (C) 2012 Hitoshi Mitake <h.mitake@gmail.com>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <errno.h>
+
+#include <assert.h>
+
+#include <regex.h>
+
+extern int errno;
+
+#define die(fmt, arg...) \
+ do { \
+ fprintf(stderr, "line %d: fatal error, " fmt "\n", \
+ __LINE__, ##arg); \
+ fprintf(stderr, "errno: %s\n", strerror(errno)); \
+ exit(1); \
+ } while (0)
+
+#define ETX 0x03
+
+static void *xalloc(size_t size)
+{
+ void *ret;
+
+ ret = calloc(sizeof(char), size);
+ if (!ret)
+ die("memory allocation failed");
+
+ return ret;
+}
+
+static void *xrealloc(void *ptr, size_t size)
+{
+ void *ret;
+
+ assert(size);
+ ret = realloc(ptr, size);
+ if (!ret)
+ die("memory allocation failed");
+
+ return ret;
+}
+
+static int ret_nl_index(char *s)
+{
+ int i;
+
+ for (i = 0; s[i] != '\n'; i++);
+ return i;
+}
+
+static int stdin_fd = 0, tty_fd, debug_fd;
+static unsigned int row, col;
+static int running = 1;
+static int searching;
+
+#define LINES_INIT_SIZE 128
+
+enum {
+ STATE_DEFAULT,
+ STATE_INPUT_SEARCH_QUERY,
+ STATE_SEARCHING_QUERY
+};
+static int state = STATE_DEFAULT;
+
+#define BOTTOM_MESSAGE_INIT_SIZE 32
+static char *bottom_message;
+static int bottom_message_size = BOTTOM_MESSAGE_INIT_SIZE;
+
+#define MATCH_ARRAY_INIT_SIZE 32
+static regmatch_t *match_array;
+static int match_array_size = MATCH_ARRAY_INIT_SIZE;
+
+#define bmprintf(fmt, arg...) \
+ do { \
+ snprintf(bottom_message, bottom_message_size, \
+ fmt, ##arg); \
+ } while (0)
+
+#ifdef GIT_LESS_DEBUG
+
+#define debug_printf(fmt, arg...) \
+ do { \
+ char dbg_msg[1024]; \
+ int w, wbyte, len; \
+ \
+ bzero(dbg_msg, 1024); \
+ len = snprintf(dbg_msg, 1024, \
+ fmt, ##arg); \
+ \
+ wbyte = 0; \
+ while ((w = write(debug_fd, dbg_msg + wbyte, \
+ len - wbyte))) { \
+ if (wbyte < 0) \
+ die("failed write() on debug_fd"); \
+ \
+ wbyte += w; \
+ if (wbyte == len) \
+ break; \
+ } \
+ } while (0)
+
+#else
+
+#define debug_printf(fmt, arg...) do { } while (0)
+
+#endif /* GIT_LESS_DEBUG */
+
+static void update_row_col(void)
+{
+ struct winsize size;
+
+ bzero(&size, sizeof(struct winsize));
+ ioctl(tty_fd, TIOCGWINSZ, (void *)&size);
+
+ row = size.ws_row - 1;
+ col = size.ws_col;
+
+ if (bottom_message_size - 1 < col) {
+ bottom_message_size = col + 1;
+ bottom_message = xrealloc(bottom_message, bottom_message_size);
+ }
+
+ if (match_array_size < col) {
+ match_array_size = col;
+ match_array = xrealloc(match_array,
+ match_array_size * sizeof(regmatch_t));
+ }
+}
+
+static struct termios attr;
+
+static void init_tty(void)
+{
+ tty_fd = open("/dev/tty", O_RDONLY);
+ if (tty_fd < 0)
+ die("open()ing /dev/tty");
+
+ bzero(&attr, sizeof(struct termios));
+ tcgetattr(tty_fd, &attr);
+ attr.c_lflag &= ~ICANON;
+ attr.c_lflag &= ~ECHO;
+ tcsetattr(tty_fd, TCSANOW, &attr);
+
+ update_row_col();
+}
+
+static char *logbuf;
+static int logbuf_size, logbuf_used;
+#define LOGBUF_INIT_SIZE 1024
+
+static int last_etx;
+
+struct commit {
+ unsigned int *lines; /* array of index of logbuf */
+ int lines_size;
+
+ int nr_lines, head_line;
+
+ /*
+ * caution:
+ * prev means previous commit of the commit object,
+ * next means next commit of the commit object.
+ */
+ struct commit *prev, *next;
+};
+
+/* head: HEAD, root: root of the commit tree */
+static struct commit *head, *root;
+/* current: current displaying commit, tail: tail of the read commits */
+static struct commit *current, *tail;
+
+static regex_t *re_compiled;
+
+static void update_terminal(void)
+{
+ int i, j, print;
+ char *line;
+
+ printf("\033[2J\033[0;0J");
+
+ /* FIXME: first new line should be eliminated in git-log */
+ if (current != head && !current->head_line)
+ current->head_line = 1;
+
+ for (i = current->head_line, print = 0;
+ i < current->head_line + row
+ && i < current->nr_lines; i++, print++) {
+ line = &logbuf[current->lines[i]];
+
+ if (state == STATE_SEARCHING_QUERY) {
+ int ret, mi, nli = ret_nl_index(line);
+ int rev = 0;
+
+ line[nli] = '\0';
+ ret = regexec(re_compiled, line,
+ match_array_size, match_array, 0);
+ line[nli] = '\n';
+
+ if (ret)
+ goto normal_print;
+
+ for (mi = j = 0; j < col && line[j] != '\n'; j++) {
+ if (j == match_array[mi].rm_so) {
+ printf("\033[7m");
+ rev = 1;
+ } else if (j == match_array[mi].rm_eo) {
+ printf("\033[0m");
+ rev = 0;
+
+ mi++;
+ }
+
+ if (match_array[mi].rm_so
+ == match_array[mi].rm_eo) {
+ printf("\033[0m");
+ rev = 0;
+
+ mi++;
+ }
+
+ putchar(line[j]);
+ }
+
+ if (rev)
+ printf("\033[0m");
+ } else {
+ normal_print:
+ for (j = 0; j < col && line[j] != '\n'; j++)
+ putchar(line[j]);
+ }
+ putchar('\n');
+ }
+
+ while (i++ < current->head_line + row)
+ putchar('\n');
+
+ if (current->nr_lines <= current->head_line + row)
+ printf("\033[7m100%%\033[0m");
+ else
+ printf("\033[7m% .0f%%\033[0m",
+ (float)(current->head_line + row)
+ / current->nr_lines * 100.0);
+
+ printf("\033[7m %s\033[0m", bottom_message);
+ fflush(stdout);
+}
+
+static void signal_handler(int signum)
+{
+ switch (signum) {
+ case SIGWINCH:
+ update_row_col();
+ update_terminal();
+ break;
+
+ case SIGINT:
+ if (searching)
+ searching = 0;
+ else
+ running = 0;
+ break;
+
+ default:
+ die("unknown signal: %d", signum);
+ break;
+ }
+}
+
+static int init_sighandler(void)
+{
+ struct sigaction act;
+
+ bzero(&act, sizeof(struct sigaction));
+ act.sa_handler = signal_handler;
+ sigaction(SIGWINCH, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+
+ return 0;
+}
+
+static void init_commit(struct commit *c, int first_index)
+{
+ int i, line_head;
+
+ c->lines_size = LINES_INIT_SIZE;
+ c->lines = xalloc(c->lines_size * sizeof(int));
+
+ line_head = first_index;
+
+ for (i = first_index; logbuf[i] != ETX; i++) {
+ if (logbuf[i] != '\n')
+ continue;
+
+ c->lines[c->nr_lines++] = line_head;
+ line_head = i + 1;
+
+ if (c->lines_size == c->nr_lines) {
+ c->lines_size <<= 1;
+ c->lines = xrealloc(c->lines,
+ c->lines_size * sizeof(int));
+ }
+ }
+}
+
+static int contain_etx(int begin, int end)
+{
+ int i;
+
+ for (i = begin; i < end; i++)
+ if (logbuf[i] == (char)ETX) return i;
+
+ return -1;
+}
+
+static void read_head(void)
+{
+ int prev_logbuf_used = 0;
+
+ do {
+ int rbyte;
+
+ if (logbuf_used == logbuf_size) {
+ logbuf_size <<= 1;
+ logbuf = xrealloc(logbuf, logbuf_size);
+ }
+
+ rbyte = read(stdin_fd, &logbuf[logbuf_used],
+ logbuf_size - logbuf_used);
+
+ if (rbyte < 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ die("read() failed");
+ }
+
+ if (!rbyte)
+ exit(0); /* no input */
+
+ prev_logbuf_used = logbuf_used;
+ logbuf_used += rbyte;
+ } while ((last_etx =
+ contain_etx(prev_logbuf_used, logbuf_used)) == -1);
+
+ head = xalloc(sizeof(struct commit));
+ init_commit(head, 0);
+
+ tail = current = head;
+}
+
+static void read_commit(void)
+{
+ int prev_last_etx = last_etx;
+ int prev_logbuf_used, first_logbuf_used;
+ struct commit *new_commit;
+
+ static int read_end;
+
+ if (read_end)
+ return;
+
+ if (last_etx + 1 < logbuf_used) {
+ unsigned int tmp_etx;
+
+ tmp_etx = contain_etx(last_etx + 1, logbuf_used);
+ if (tmp_etx != -1) {
+ last_etx = tmp_etx;
+ goto skip_read;
+ }
+ }
+
+ prev_logbuf_used = 0;
+ first_logbuf_used = logbuf_used;
+
+ do {
+ int rbyte;
+
+ if (logbuf_used == logbuf_size) {
+ logbuf_size <<= 1;
+ logbuf = xrealloc(logbuf, logbuf_size);
+ }
+
+ rbyte = read(stdin_fd, &logbuf[logbuf_used],
+ logbuf_size - logbuf_used);
+
+ if (rbyte < 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ die("read() failed");
+ }
+
+ if (!rbyte) {
+ read_end = 1;
+ if (first_logbuf_used == logbuf_used)
+ return;
+ }
+
+ prev_logbuf_used = logbuf_used;
+ logbuf_used += rbyte;
+ } while ((last_etx =
+ contain_etx(prev_logbuf_used, logbuf_used)) == -1);
+
+skip_read:
+
+ new_commit = xalloc(sizeof(struct commit));
+
+ assert(!tail->prev);
+ tail->prev = new_commit;
+ new_commit->next = tail;
+
+ tail = new_commit;
+
+ init_commit(new_commit, prev_last_etx + 1);
+}
+
+static int show_prev_commit(char cmd)
+{
+ if (!current->prev) {
+ read_commit();
+
+ if (!current->prev)
+ return 0;
+ }
+
+ current = current->prev;
+ return 1;
+}
+
+static int show_next_commit(char cmd)
+{
+ if (!current->next) {
+ assert(current == head);
+ return 0;
+ }
+
+ current = current->next;
+ return 1;
+}
+
+static int forward_line(char cmd)
+{
+ if (current->head_line + row < current->nr_lines) {
+ current->head_line++;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int backward_line(char cmd)
+{
+ if (0 < current->head_line) {
+ current->head_line--;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int goto_top(char cmd)
+{
+ if (!current->head_line)
+ return 0;
+
+ current->head_line = 0;
+ return 1;
+}
+
+static int goto_bottom(char cmd)
+{
+ if (current->nr_lines < row)
+ return 0;
+
+ current->head_line = current->nr_lines - row;
+ return 1;
+}
+
+static int forward_page(char cmd)
+{
+ if (current->nr_lines < current->head_line + row)
+ return 0;
+
+ current->head_line += row;
+ return 1;
+}
+
+static int backward_page(char cmd)
+{
+ if (!current->head_line)
+ return 0;
+
+ current->head_line -= row;
+ if (current->head_line < 0)
+ current->head_line = 0;
+
+ return 1;
+}
+
+static int show_root(char cmd)
+{
+ if (root) {
+ current = root;
+ return 1;
+ }
+
+ do {
+ if (!current->prev)
+ read_commit();
+ if (!current->prev)
+ break;
+ current = current->prev;
+ } while (1);
+
+ assert(!root);
+ root = current;
+
+ return 1;
+}
+
+static int show_head(char cmd)
+{
+ if (current == head)
+ return 0;
+
+ current = head;
+ return 1;
+}
+
+#define QUERY_SIZE 128
+static char query[QUERY_SIZE + 1];
+static int query_used;
+
+static int match_line(char *line)
+{
+ if (!regexec(re_compiled, line, 0, NULL, REG_NOTEOL))
+ return 1;
+
+ return 0;
+}
+
+static int match_commit(struct commit *c, int direction, int prog)
+{
+ int i = c->head_line;
+ int nli, result;
+ char *line;
+
+ if (prog) {
+ if (direction) {
+ if (c->nr_lines <= i - 1)
+ return 0;
+ } else {
+ if (!i)
+ return 0;
+ }
+
+ i += direction ? 1 : -1;
+ }
+
+ do {
+ line = &logbuf[c->lines[i]];
+ nli = ret_nl_index(line);
+
+ line[nli] = '\0';
+ result = match_line(line);
+ line[nli] = '\n';
+
+ if (result) {
+ c->head_line = i;
+ return 1;
+ }
+
+ i += direction ? 1 : -1;
+ } while (direction ? i < c->nr_lines : 0 <= i);
+
+ return 0;
+}
+
+static int do_search(int direction, int global, int prog)
+{
+ int result;
+ struct commit *p;
+
+ assert(!searching);
+ searching = 1;
+
+ result = match_commit(current, direction, prog);
+ if (result || !global)
+ goto no_match;
+
+ if (direction) {
+ if (!current->prev)
+ read_commit();
+
+ if (current->prev)
+ p = current->prev;
+ else
+ goto no_match;
+ } else {
+ if (current->next)
+ p = current->next;
+ else
+ goto no_match;
+ }
+
+ do {
+ if (direction)
+ p->head_line = 0;
+ else
+ p->head_line = p->nr_lines - 1;
+
+ result = match_commit(p, direction, prog);
+ if (result)
+ goto matched;
+
+ if (direction && !p->prev)
+ read_commit();
+ } while (searching && (direction ? (p = p->prev) : (p = p->next)));
+
+ goto no_match;
+
+matched:
+ current = p;
+no_match:
+ searching = 0;
+
+ return result;
+}
+
+static int current_direction, current_global;
+
+#define update_query_bm() do { \
+ bmprintf("\033[7m%s %s search:\033[0m %s", \
+ current_direction ? "forward" : "backward", \
+ current_global ? "global" : "local", \
+ query); \
+ \
+ } while (0)
+
+static int _search(int key, int direction, int global)
+{
+ current_direction = direction;
+ current_global = global;
+
+ switch (state) {
+ case STATE_DEFAULT:
+ case STATE_SEARCHING_QUERY:
+ query_used = 0;
+ bzero(query, QUERY_SIZE);
+
+ update_query_bm();
+ state = STATE_INPUT_SEARCH_QUERY;
+ break;
+
+ case STATE_INPUT_SEARCH_QUERY:
+ if (query_used + 1 == QUERY_SIZE) {
+ bmprintf("search query is too long!");
+ state = STATE_DEFAULT;
+
+ goto end;
+ }
+
+ if (key == '\n')
+ state = STATE_SEARCHING_QUERY;
+ else {
+ query[query_used++] = (char)key;
+ update_query_bm();
+ }
+ end:
+ break;
+
+ default:
+ die("invalid or unknown state: %d", state);
+ break;
+ }
+
+ if (state == STATE_SEARCHING_QUERY) {
+ if (re_compiled)
+ regfree(re_compiled);
+ else
+ re_compiled = xalloc(sizeof(regex_t));
+ regcomp(re_compiled, query, REG_ICASE);
+
+ if (!do_search(direction, global, 0))
+ bmprintf("not found: %s", query);
+ else
+ update_query_bm();
+ }
+
+ return 1;
+}
+
+static int search(int direction, int global)
+{
+ return _search(-1, direction, global);
+}
+
+static int search_global_forward(char cmd)
+{
+ return search(1, 1);
+}
+
+static int search_global_backward(char cmd)
+{
+ return search(0, 1);
+}
+
+static int search_local_forward(char cmd)
+{
+ return search(1, 0);
+}
+
+static int search_local_backward(char cmd)
+{
+ return search(0, 0);
+}
+
+static int search_progress(char cmd)
+{
+ if (state != STATE_SEARCHING_QUERY)
+ return 0;
+
+ if (!do_search(cmd == 'n' ?
+ current_direction : !current_direction,
+ current_global, 1))
+ bmprintf("not found: %s", query);
+ else
+ update_query_bm();
+
+ return 1;
+}
+
+static int input_query(char key)
+{
+ if (key == (char)0x7f) {
+ /* backspace */
+ if (!query_used)
+ return 0;
+
+ query[--query_used] = '\0';
+ update_query_bm();
+
+ return 1;
+ } else if (key == (char)0x1b) {
+ /* escape */
+ regfree(re_compiled);
+ free(re_compiled);
+ re_compiled = NULL;
+
+ query_used = 0;
+ bzero(query, QUERY_SIZE);
+
+ bzero(bottom_message, bottom_message_size);
+ state = STATE_DEFAULT;
+ return 1;
+ }
+
+ return _search(key, current_direction, current_global);
+}
+
+static int nop(char cmd)
+{
+ return 0;
+}
+
+static int quit(char cmd)
+{
+ running = 0;
+ return 0;
+}
+
+struct key_cmd {
+ char key;
+ int (*op)(char);
+};
+
+static struct key_cmd valid_ops[] = {
+ { 'h', show_prev_commit },
+ { 'j', forward_line },
+ { 'k', backward_line },
+ { 'l', show_next_commit },
+ { 'q', quit },
+ { 'g', goto_top },
+ { 'G', goto_bottom },
+ { ' ', forward_page },
+ { 'J', forward_page },
+ { 'K', backward_page },
+ { 'H', show_root },
+ { 'L', show_head },
+ { '/', search_global_forward },
+ { '?', search_global_backward },
+ { '\\', search_local_forward },
+ { '!', search_local_backward },
+ { 'n', search_progress },
+ { 'p', search_progress },
+
+ { '\0', NULL },
+};
+
+static int (*ops_array[256])(char);
+
+int main(void)
+{
+ int i;
+ char cmd;
+
+#ifdef GIT_LESS_DEBUG
+
+#define DEBUG_FIFO_NAME "/tmp/git-less-debug"
+ if (mkfifo(DEBUG_FIFO_NAME, S_IRWXU))
+ die("failed to create named fifo for debugging");
+
+ debug_fd = open(DEBUG_FIFO_NAME, O_RDWR);
+ if (debug_fd < 0)
+ die("failed to open() named fifo for debugging");
+
+#endif
+
+ bottom_message = xalloc(bottom_message_size);
+ match_array = xalloc(match_array_size * sizeof(regmatch_t));
+
+ init_tty();
+ init_sighandler();
+
+ logbuf_size = LOGBUF_INIT_SIZE;
+ logbuf = xalloc(logbuf_size);
+ logbuf_used = 0;
+ read_head();
+
+ update_terminal();
+
+ for (i = 0; i < 256; i++)
+ ops_array[i] = nop;
+
+ for (i = 0; valid_ops[i].key != '\0'; i++)
+ ops_array[(int)valid_ops[i].key] = valid_ops[i].op;
+
+ while (running) {
+ int ret;
+
+ ret = read(tty_fd, &cmd, 1);
+ if (ret == -1 && errno == EINTR) {
+ if (!running)
+ break;
+
+ errno = 0;
+ continue;
+ }
+
+ if (ret != 1)
+ die("reading key input failed");
+
+ if (state == STATE_INPUT_SEARCH_QUERY)
+ ret = input_query(cmd);
+ else
+ ret = ops_array[(int)cmd](cmd);
+
+ if (ret)
+ update_terminal();
+ }
+
+ printf("\n");
+
+#ifdef GIT_LESS_DEBUG
+ unlink(DEBUG_FIFO_NAME);
+#endif
+
+ return 0;
+}
--
1.7.10.rc1.33.g64ff3.dirty
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v0 2/2] git-less: git side support for git-less
2012-03-22 18:42 [PATCH v0 0/2] git-less: a specialized pager for git-log Hitoshi Mitake
2012-03-22 18:42 ` [PATCH v0 1/2] " Hitoshi Mitake
@ 2012-03-22 18:42 ` Hitoshi Mitake
2012-03-22 19:00 ` [PATCH v0 0/2] git-less: a specialized pager for git-log Junio C Hamano
2012-03-24 4:43 ` Nguyen Thai Ngoc Duy
3 siblings, 0 replies; 10+ messages in thread
From: Hitoshi Mitake @ 2012-03-22 18:42 UTC (permalink / raw)
To: gitster; +Cc: git, Hitoshi Mitake
This patch modifies three parts of git.
1. git-log ... insert ETX(0x03) between each commit for the detection the boarder
of commits by git-less.
2. pager selection ... current git uses single pager program for every command.
I designed git-less for git-log so it is not suitable for other commands like
git-grep, etc.
3. new configuration parameter for specifying git-less as a pager of git-log
(I think these three changes are one logical unit.)
The most important commit is 2. As described above, current git supports only
one pager and this is not good for git-less because git-less only assumes
git-log. So I modified setup_pager() in pager.c to separate pager selection
mechanism and pager setup mechanism.
As described in 3, git-log specific pager can be specified with the new string
typed parameter "log.pager". git-less can be set as the pager like this config:
[log]
pager = git-less
The description of this new parameter is also added in the help of git-log.
Signed-off-by: Hitoshi Mitake <h.mitake@gmail.com>
---
Documentation/git-log.txt | 6 +++++-
builtin/log.c | 32 ++++++++++++++++++++++++++++++++
cache.h | 1 +
| 9 ++++++---
4 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 249fc87..f2e07f4 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -177,7 +177,11 @@ notes.displayRef::
or 'GIT_NOTES_REF', to read notes from when showing commit
messages with the 'log' family of commands. See
linkgit:git-notes[1].
-+
+
+log.pager::
+ Pager program for git-log. Currently this option only assumes
+ "git-less".
+
May be an unabbreviated ref name or a glob and may be specified
multiple times. A warning will be issued for refs that do not exist,
but a glob that does not match any refs is silently ignored.
diff --git a/builtin/log.c b/builtin/log.c
index 8a47012..2ef3ada 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -88,6 +88,22 @@ static void cmd_log_init_defaults(struct rev_info *rev)
rev->date_mode = parse_date_format(default_date_mode);
}
+static int use_git_less;
+
+static int setup_log_pager(const char *myname)
+{
+ if (strcmp(myname, "log")) {
+ use_git_less = 0;
+ return 0;
+ }
+
+ if (!use_git_less)
+ return 0;
+
+ __setup_pager("git-less");
+ return 1;
+}
+
static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
struct rev_info *rev, struct setup_revision_opt *opt)
{
@@ -149,6 +165,10 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
rev->show_decorations = 1;
load_ref_decorations(decoration_style);
}
+
+ if (setup_log_pager(argv[0]))
+ return;
+
setup_pager();
}
@@ -314,6 +334,9 @@ static int cmd_log_walk(struct rev_info *rev)
saved_nrl = rev->diffopt.needed_rename_limit;
if (rev->diffopt.degraded_cc_to_c)
saved_dcctc = 1;
+
+ if (use_git_less)
+ printf("\003");
}
rev->diffopt.degraded_cc_to_c = saved_dcctc;
rev->diffopt.needed_rename_limit = saved_nrl;
@@ -349,6 +372,15 @@ static int git_log_config(const char *var, const char *value, void *cb)
}
if (!prefixcmp(var, "color.decorate."))
return parse_decorate_color_config(var, 15, value);
+ if (!strcmp(var, "log.pager")) {
+ if (!strcmp(value, "git-less")) {
+ use_git_less = 1;
+ return 0;
+ } else {
+ fprintf(stderr, "unknown pager for git-log: %s\n", value);
+ return -1;
+ }
+ }
return git_diff_ui_config(var, value, cb);
}
diff --git a/cache.h b/cache.h
index e5e1aa4..e5e57ff 100644
--- a/cache.h
+++ b/cache.h
@@ -1185,6 +1185,7 @@ static inline ssize_t write_str_in_full(int fd, const char *str)
/* pager.c */
extern void setup_pager(void);
+extern void __setup_pager(const char *pager);
extern const char *pager_program;
extern int pager_in_use(void);
extern int pager_use_color;
--git a/pager.c b/pager.c
index 05584de..45fe9ea 100644
--- a/pager.c
+++ b/pager.c
@@ -69,10 +69,8 @@ const char *git_pager(int stdout_is_tty)
return pager;
}
-void setup_pager(void)
+void __setup_pager(const char *pager)
{
- const char *pager = git_pager(isatty(1));
-
if (!pager)
return;
@@ -110,6 +108,11 @@ void setup_pager(void)
atexit(wait_for_pager);
}
+void setup_pager(void)
+{
+ __setup_pager(git_pager(isatty(1)));
+}
+
int pager_in_use(void)
{
const char *env;
--
1.7.10.rc1.33.g64ff3.dirty
^ permalink raw reply related [flat|nested] 10+ messages in thread