5f0a9e16698d98c87ce22f96e3b93e10eb7f8ec7
7 * tig - text-mode interface for git
13 * tig log [git log options]
14 * tig diff [git diff options]
15 * tig < [git log or git diff output]
19 * Browse changes in a git repository.
44 static void die(const char *err
, ...);
45 static void report(const char *msg
, ...);
47 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
52 #define REQ_OFFSET (MAX_COMMAND + 1)
54 /* Requests for switching between the different views. */
55 #define REQ_DIFF (REQ_OFFSET + 0)
56 #define REQ_LOG (REQ_OFFSET + 1)
57 #define REQ_MAIN (REQ_OFFSET + 2)
59 #define REQ_QUIT (REQ_OFFSET + 11)
60 #define REQ_VERSION (REQ_OFFSET + 12)
61 #define REQ_STOP (REQ_OFFSET + 13)
62 #define REQ_UPDATE (REQ_OFFSET + 14)
63 #define REQ_REDRAW (REQ_OFFSET + 15)
79 * stop all background loading
91 #define HELP "(d)iff, (l)og, (m)ain, (q)uit, (v)ersion, (h)elp"
98 struct keymap keymap
[] = {
99 { KEY_UP
, REQ_PREV_LINE
},
100 { 'k', REQ_PREV_LINE
},
101 { KEY_DOWN
, REQ_NEXT_LINE
},
102 { 'j', REQ_NEXT_LINE
},
103 { KEY_NPAGE
, REQ_NEXT_PAGE
},
104 { KEY_PPAGE
, REQ_PREV_PAGE
},
110 /* No input from wgetch() with nodelay() enabled. */
113 { KEY_ESC
, REQ_QUIT
},
116 { 'v', REQ_VERSION
},
121 get_request(int request
)
125 for (i
= 0; i
< ARRAY_SIZE(keymap
); i
++)
126 if (keymap
[i
].alias
== request
)
127 return keymap
[i
].request
;
142 int (*render
)(struct view
*, int);
146 unsigned long offset
; /* Offset of the window top */
147 unsigned long lineno
; /* Current line number */
150 unsigned long lines
; /* Total number of lines */
151 char **line
; /* Line index */
157 static int default_renderer(struct view
*view
, int lineno
);
160 "git log --stat -n1 %s ; echo; " \
161 "git diff --find-copies-harder -B -C %s^ %s"
164 "git log --stat -n100 %s"
166 /* The status window at the bottom. Used for polling keystrokes. */
167 static WINDOW
*status_win
;
169 static struct view views
[] = {
170 { "diff", DIFF_CMD
, default_renderer
},
171 { "log", LOG_CMD
, default_renderer
},
175 static struct view
*display
[ARRAY_SIZE(views
)];
176 static unsigned int current_view
;
177 static unsigned int nloading
;
179 #define foreach_view(view, i) \
180 for (i = 0; i < sizeof(display) && (view = display[i]); i++)
183 redraw_view(struct view
*view
)
189 wmove(view
->win
, 0, 0);
191 getmaxyx(view
->win
, lines
, cols
);
193 for (lineno
= 0; lineno
< lines
; lineno
++) {
194 view
->render(view
, lineno
);
197 redrawwin(view
->win
);
201 /* FIXME: Fix percentage. */
203 report_position(struct view
*view
, int all
)
205 report(all ?
"line %d of %d (%d%%) viewing from %d"
209 view
->lines ? view
->offset
* 100 / view
->lines
: 0,
214 scroll_view(struct view
*view
, int request
)
217 enum { BACKWARD
= -1, FORWARD
= 1 } direction
= FORWARD
;
219 getmaxyx(view
->win
, y
, x
);
225 if (view
->offset
+ lines
> view
->lines
)
226 lines
= view
->lines
- view
->offset
- 1;
228 if (lines
== 0 || view
->offset
+ y
>= view
->lines
) {
229 report("already at last line");
237 if (lines
> view
->offset
)
238 lines
= view
->offset
;
241 report("already at first line");
245 direction
= BACKWARD
;
252 report("off=%d lines=%d lineno=%d move=%d", view
->offset
, view
->lines
, view
->lineno
, lines
* direction
);
254 /* The rendering expects the new offset. */
255 view
->offset
+= lines
* direction
;
257 /* Move current line into the view. */
258 if (view
->lineno
< view
->offset
)
259 view
->lineno
= view
->offset
;
260 if (view
->lineno
> view
->offset
+ y
)
261 view
->lineno
= view
->offset
+ y
;
263 assert(0 <= view
->offset
&& view
->offset
< view
->lines
);
264 //assert(0 <= view->offset + lines && view->offset + lines < view->lines);
265 assert(view
->offset
<= view
->lineno
&& view
->lineno
<= view
->lines
);
268 int from
= direction
== FORWARD ? y
- lines
: 0;
269 int to
= from
+ lines
;
271 wscrl(view
->win
, lines
* direction
);
273 for (; from
< to
; from
++) {
274 if (!view
->render(view
, from
))
279 redrawwin(view
->win
);
282 report_position(view
, lines
);
286 resize_view(struct view
*view
)
290 getmaxyx(stdscr
, lines
, cols
);
293 mvwin(view
->win
, 0, 0);
294 wresize(view
->win
, lines
- 1, cols
);
297 view
->win
= newwin(lines
- 1, 0, 0, 0);
299 report("Failed to create %s view", view
->name
);
302 scrollok(view
->win
, TRUE
);
308 begin_update(struct view
*view
)
313 if (snprintf(buf
, sizeof(buf
), view
->cmd
, "HEAD", "HEAD", "HEAD") < sizeof(buf
))
314 view
->pipe
= popen(buf
, "r");
320 nodelay(status_win
, TRUE
);
323 display
[current_view
] = view
;
333 end_update(struct view
*view
)
335 wattrset(view
->win
, A_NORMAL
);
340 nodelay(status_win
, FALSE
);
344 update_view(struct view
*view
)
355 getmaxyx(view
->win
, lines
, cols
);
357 redraw
= !view
->line
;
359 tmp
= realloc(view
->line
, sizeof(*view
->line
) * (view
->lines
+ lines
));
365 while ((line
= fgets(buffer
, sizeof(buffer
), view
->pipe
))) {
371 linelen
= strlen(line
);
373 line
[linelen
- 1] = 0;
375 view
->line
[view
->lines
] = strdup(line
);
376 if (!view
->line
[view
->lines
])
384 if (ferror(view
->pipe
)) {
385 report("Failed to read %s", view
->cmd
);
388 } else if (feof(view
->pipe
)) {
389 report_position(view
, 0);
396 report("Allocation failure");
405 switch_view(struct view
*prev
, int request
)
407 struct view
*view
= &views
[request
- REQ_OFFSET
];
408 struct view
*displayed
;
412 foreach_view (displayed
, i
) ;
415 report("Already in %s view", view
->name
);
417 report("FIXME: Maximize");
422 foreach_view (displayed
, i
) {
423 if (view
== displayed
) {
425 report("New current view");
437 for (i
= 0; i
< view
->lines
; i
++)
445 if (prev
&& prev
->pipe
)
448 if (begin_update(view
)) {
452 report("loading...");
459 /* Process a keystroke */
461 view_driver(struct view
*view
, int key
)
463 int request
= get_request(key
);
472 scroll_view(view
, request
);
478 view
= switch_view(view
, request
);
486 foreach_view (view
, i
) {
489 scroll_view(view
, 0);
495 report("version %s", VERSION
);
518 #define ATTR(line, attr) { (line), sizeof(line) - 1, (attr) }
526 static struct attr attrs
[] = {
527 ATTR("commit ", COLOR_PAIR(COLOR_GREEN
)),
528 ATTR("Author: ", COLOR_PAIR(COLOR_CYAN
)),
529 ATTR("Date: ", COLOR_PAIR(COLOR_YELLOW
)),
530 ATTR("diff --git ", COLOR_PAIR(COLOR_YELLOW
)),
531 ATTR("diff-tree ", COLOR_PAIR(COLOR_BLUE
)),
532 ATTR("index ", COLOR_PAIR(COLOR_BLUE
)),
533 ATTR("-", COLOR_PAIR(COLOR_RED
)),
534 ATTR("+", COLOR_PAIR(COLOR_GREEN
)),
535 ATTR("@", COLOR_PAIR(COLOR_MAGENTA
)),
539 default_renderer(struct view
*view
, int lineno
)
546 line
= view
->line
[view
->offset
+ lineno
];
547 if (!line
) return FALSE
;
549 linelen
= strlen(line
);
551 for (i
= 0; i
< ARRAY_SIZE(attrs
); i
++) {
552 if (linelen
< attrs
[i
].linelen
553 || strncmp(attrs
[i
].line
, line
, attrs
[i
].linelen
))
556 attr
= attrs
[i
].attr
;
560 wattrset(view
->win
, attr
);
561 mvwprintw(view
->win
, lineno
, 0, "%4d: %s", view
->offset
+ lineno
, line
);
577 /* FIXME: Shutdown gracefully. */
582 static void die(const char *err
, ...)
589 fputs("tig: ", stderr
);
590 vfprintf(stderr
, err
, args
);
598 report(const char *msg
, ...)
605 wmove(status_win
, 0, 0);
607 if (display
[current_view
])
608 wprintw(status_win
, "%4s: ", display
[current_view
]->name
);
610 vwprintw(status_win
, msg
, args
);
611 wrefresh(status_win
);
619 int bg
= COLOR_BLACK
;
623 if (use_default_colors() != ERR
)
626 init_pair(COLOR_BLACK
, COLOR_BLACK
, bg
);
627 init_pair(COLOR_GREEN
, COLOR_GREEN
, bg
);
628 init_pair(COLOR_RED
, COLOR_RED
, bg
);
629 init_pair(COLOR_CYAN
, COLOR_CYAN
, bg
);
630 init_pair(COLOR_WHITE
, COLOR_WHITE
, bg
);
631 init_pair(COLOR_MAGENTA
, COLOR_MAGENTA
, bg
);
632 init_pair(COLOR_BLUE
, COLOR_BLUE
, bg
);
633 init_pair(COLOR_YELLOW
, COLOR_YELLOW
, bg
);
637 main(int argc
, char *argv
[])
639 int request
= REQ_MAIN
;
642 signal(SIGINT
, quit
);
644 initscr(); /* initialize the curses library */
645 nonl(); /* tell curses not to do NL->CR/NL on output */
646 cbreak(); /* take input chars one at a time, no wait for \n */
647 noecho(); /* don't echo input */
648 leaveok(stdscr
, TRUE
);
654 getmaxyx(stdscr
, y
, x
);
655 status_win
= newwin(1, 0, y
- 1, 0);
657 die("Failed to create status window");
659 /* Enable keyboard mapping */
660 keypad(status_win
, TRUE
);
661 wattrset(status_win
, COLOR_PAIR(COLOR_GREEN
));
663 while (view_driver(display
[current_view
], request
)) {
667 foreach_view (view
, i
) {
673 /* Refresh, accept single keystroke of input */
674 request
= wgetch(status_win
);
675 if (request
== KEY_RESIZE
) {
678 getmaxyx(stdscr
, lines
, cols
);
679 mvwin(status_win
, lines
- 1, 0);
680 wresize(status_win
, 1, cols
- 1);
692 * Copyright (c) Jonas Fonseca, 2006
694 * This program is free software; you can redistribute it and/or modify
695 * it under the terms of the GNU General Public License as published by
696 * the Free Software Foundation; either version 2 of the License, or
697 * (at your option) any later version.
701 * link:http://www.kernel.org/pub/software/scm/git/docs/[git(7)],
702 * link:http://www.kernel.org/pub/software/scm/cogito/docs/[cogito(7)]