2 9f7d7167 2018-04-29 stsp * Copyright (c) 2018 Stefan Sperling <stsp@openbsd.org>
4 9f7d7167 2018-04-29 stsp * Permission to use, copy, modify, and distribute this software for any
5 9f7d7167 2018-04-29 stsp * purpose with or without fee is hereby granted, provided that the above
6 9f7d7167 2018-04-29 stsp * copyright notice and this permission notice appear in all copies.
8 9f7d7167 2018-04-29 stsp * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 9f7d7167 2018-04-29 stsp * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 9f7d7167 2018-04-29 stsp * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 9f7d7167 2018-04-29 stsp * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 9f7d7167 2018-04-29 stsp * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 9f7d7167 2018-04-29 stsp * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 9f7d7167 2018-04-29 stsp * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 80ddbec8 2018-04-29 stsp #include <sys/queue.h>
18 ffd1d5e5 2018-06-23 stsp #include <sys/stat.h>
20 31120ada 2018-04-30 stsp #include <errno.h>
21 61e69b96 2018-05-20 stsp #define _XOPEN_SOURCE_EXTENDED
22 9f7d7167 2018-04-29 stsp #include <curses.h>
23 61e69b96 2018-05-20 stsp #undef _XOPEN_SOURCE_EXTENDED
24 9f7d7167 2018-04-29 stsp #include <panel.h>
25 9f7d7167 2018-04-29 stsp #include <locale.h>
26 9f7d7167 2018-04-29 stsp #include <stdlib.h>
27 26ed57b2 2018-05-19 stsp #include <stdio.h>
28 9f7d7167 2018-04-29 stsp #include <getopt.h>
29 9f7d7167 2018-04-29 stsp #include <string.h>
30 9f7d7167 2018-04-29 stsp #include <err.h>
31 80ddbec8 2018-04-29 stsp #include <unistd.h>
32 26ed57b2 2018-05-19 stsp #include <util.h>
33 26ed57b2 2018-05-19 stsp #include <limits.h>
34 61e69b96 2018-05-20 stsp #include <wchar.h>
35 788c352e 2018-06-16 stsp #include <time.h>
36 84451b3e 2018-07-10 stsp #include <pthread.h>
38 9f7d7167 2018-04-29 stsp #include "got_error.h"
39 80ddbec8 2018-04-29 stsp #include "got_object.h"
40 80ddbec8 2018-04-29 stsp #include "got_reference.h"
41 80ddbec8 2018-04-29 stsp #include "got_repository.h"
42 80ddbec8 2018-04-29 stsp #include "got_diff.h"
43 511a516b 2018-05-19 stsp #include "got_opentemp.h"
44 9ba79e04 2018-06-11 stsp #include "got_commit_graph.h"
45 00dfcb92 2018-06-11 stsp #include "got_utf8.h"
46 a70480e0 2018-06-23 stsp #include "got_blame.h"
49 881b2d3e 2018-04-30 stsp #define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
52 9f7d7167 2018-04-29 stsp #ifndef nitems
53 9f7d7167 2018-04-29 stsp #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
56 9f7d7167 2018-04-29 stsp struct tog_cmd {
57 c2301be8 2018-04-30 stsp const char *name;
58 9f7d7167 2018-04-29 stsp const struct got_error *(*cmd_main)(int, char *[]);
59 9f7d7167 2018-04-29 stsp void (*cmd_usage)(void);
60 c2301be8 2018-04-30 stsp const char *descr;
63 4ed7e80c 2018-05-20 stsp __dead static void usage(void);
64 4ed7e80c 2018-05-20 stsp __dead static void usage_log(void);
65 4ed7e80c 2018-05-20 stsp __dead static void usage_diff(void);
66 4ed7e80c 2018-05-20 stsp __dead static void usage_blame(void);
67 ffd1d5e5 2018-06-23 stsp __dead static void usage_tree(void);
69 4ed7e80c 2018-05-20 stsp static const struct got_error* cmd_log(int, char *[]);
70 4ed7e80c 2018-05-20 stsp static const struct got_error* cmd_diff(int, char *[]);
71 4ed7e80c 2018-05-20 stsp static const struct got_error* cmd_blame(int, char *[]);
72 ffd1d5e5 2018-06-23 stsp static const struct got_error* cmd_tree(int, char *[]);
74 4ed7e80c 2018-05-20 stsp static struct tog_cmd tog_commands[] = {
75 cbb6b58a 2018-05-20 stsp { "log", cmd_log, usage_log,
76 9f7d7167 2018-04-29 stsp "show repository history" },
77 cbb6b58a 2018-05-20 stsp { "diff", cmd_diff, usage_diff,
78 9f7d7167 2018-04-29 stsp "compare files and directories" },
79 cbb6b58a 2018-05-20 stsp { "blame", cmd_blame, usage_blame,
80 9f7d7167 2018-04-29 stsp "show line-by-line file history" },
81 ffd1d5e5 2018-06-23 stsp { "tree", cmd_tree, usage_tree,
82 ffd1d5e5 2018-06-23 stsp "browse trees in repository" },
85 cc3c9aac 2018-08-01 stsp struct tog_view {
86 26ed57b2 2018-05-19 stsp WINDOW *window;
87 26ed57b2 2018-05-19 stsp PANEL *panel;
88 97ddc146 2018-08-01 stsp int nlines, ncols, begin_y, begin_x;
89 f7d12f7e 2018-08-01 stsp int lines, cols; /* copies of LINES and COLS */
90 6d0fee91 2018-08-01 stsp struct tog_view *parent;
93 cd0acaa7 2018-05-20 stsp static const struct got_error *
94 ea5e7bb5 2018-08-01 stsp show_diff_view(struct tog_view *, struct got_object *, struct got_object *,
95 cd0acaa7 2018-05-20 stsp struct got_repository *);
96 cd0acaa7 2018-05-20 stsp static const struct got_error *
97 04cc582a 2018-08-01 stsp show_log_view(struct tog_view *, struct got_object_id *,
98 04cc582a 2018-08-01 stsp struct got_repository *, const char *);
99 a70480e0 2018-06-23 stsp static const struct got_error *
100 e1cd8fed 2018-08-01 stsp show_blame_view(struct tog_view *, const char *, struct got_object_id *,
101 e1cd8fed 2018-08-01 stsp struct got_repository *);
102 ffd1d5e5 2018-06-23 stsp static const struct got_error *
103 5221c383 2018-08-01 stsp show_tree_view(struct tog_view *, struct got_tree_object *,
104 5221c383 2018-08-01 stsp struct got_object_id *, struct got_repository *);
106 ea5e7bb5 2018-08-01 stsp static void
107 ea5e7bb5 2018-08-01 stsp close_view(struct tog_view *view)
109 ea5e7bb5 2018-08-01 stsp if (view->panel)
110 ea5e7bb5 2018-08-01 stsp del_panel(view->panel);
111 ea5e7bb5 2018-08-01 stsp if (view->window)
112 ea5e7bb5 2018-08-01 stsp delwin(view->window);
113 ea5e7bb5 2018-08-01 stsp free(view);
116 ea5e7bb5 2018-08-01 stsp static struct tog_view *
117 6d0fee91 2018-08-01 stsp open_view(int nlines, int ncols, int begin_y, int begin_x,
118 6d0fee91 2018-08-01 stsp struct tog_view *parent)
120 ea5e7bb5 2018-08-01 stsp struct tog_view *view = malloc(sizeof(*view));
122 ea5e7bb5 2018-08-01 stsp if (view == NULL)
123 ea5e7bb5 2018-08-01 stsp return NULL;
125 6d0fee91 2018-08-01 stsp view->parent = parent;
126 f7d12f7e 2018-08-01 stsp view->lines = LINES;
127 f7d12f7e 2018-08-01 stsp view->cols = COLS;
128 207b9029 2018-08-01 stsp view->nlines = nlines ? nlines : LINES - begin_y;
129 207b9029 2018-08-01 stsp view->ncols = ncols ? ncols : COLS - begin_x;
130 97ddc146 2018-08-01 stsp view->begin_y = begin_y;
131 97ddc146 2018-08-01 stsp view->begin_x = begin_x;
132 842167bf 2018-08-01 stsp view->window = newwin(nlines, ncols, begin_y, begin_x);
133 ea5e7bb5 2018-08-01 stsp if (view->window == NULL) {
134 ea5e7bb5 2018-08-01 stsp close_view(view);
135 ea5e7bb5 2018-08-01 stsp return NULL;
137 ea5e7bb5 2018-08-01 stsp view->panel = new_panel(view->window);
138 ea5e7bb5 2018-08-01 stsp if (view->panel == NULL) {
139 ea5e7bb5 2018-08-01 stsp close_view(view);
140 ea5e7bb5 2018-08-01 stsp return NULL;
143 ea5e7bb5 2018-08-01 stsp keypad(view->window, TRUE);
144 ea5e7bb5 2018-08-01 stsp return view;
148 cdf1ee82 2018-08-01 stsp show_view(struct tog_view *view)
150 cdf1ee82 2018-08-01 stsp show_panel(view->panel);
151 cdf1ee82 2018-08-01 stsp update_panels();
154 f7d12f7e 2018-08-01 stsp const struct got_error *
155 f7d12f7e 2018-08-01 stsp view_resize(struct tog_view *view)
157 f7d12f7e 2018-08-01 stsp int nlines, ncols;
159 6d0fee91 2018-08-01 stsp while (view) {
160 6d0fee91 2018-08-01 stsp if (view->lines > LINES)
161 6d0fee91 2018-08-01 stsp nlines = view->nlines - (view->lines - LINES);
163 6d0fee91 2018-08-01 stsp nlines = view->nlines + (LINES - view->lines);
165 6d0fee91 2018-08-01 stsp if (view->cols > COLS)
166 6d0fee91 2018-08-01 stsp ncols = view->ncols - (view->cols - COLS);
168 6d0fee91 2018-08-01 stsp ncols = view->ncols + (COLS - view->cols);
170 6d0fee91 2018-08-01 stsp if (wresize(view->window, nlines, ncols) == ERR)
171 6d0fee91 2018-08-01 stsp return got_error_from_errno();
172 77a567dd 2018-08-01 stsp replace_panel(view->panel, view->window);
174 6d0fee91 2018-08-01 stsp view->nlines = nlines;
175 6d0fee91 2018-08-01 stsp view->ncols = ncols;
176 6d0fee91 2018-08-01 stsp view->lines = LINES;
177 6d0fee91 2018-08-01 stsp view->cols = COLS;
179 6d0fee91 2018-08-01 stsp view = view->parent;
182 f7d12f7e 2018-08-01 stsp return NULL;
185 4ed7e80c 2018-05-20 stsp __dead static void
186 9f7d7167 2018-04-29 stsp usage_log(void)
189 c70c5802 2018-08-01 stsp fprintf(stderr,
190 c70c5802 2018-08-01 stsp "usage: %s log [-c commit] [-r repository-path] [path]\n",
191 9f7d7167 2018-04-29 stsp getprogname());
195 963b370f 2018-05-20 stsp /* Create newly allocated wide-character string equivalent to a byte string. */
196 80ddbec8 2018-04-29 stsp static const struct got_error *
197 963b370f 2018-05-20 stsp mbs2ws(wchar_t **ws, size_t *wlen, const char *s)
199 00dfcb92 2018-06-11 stsp char *vis = NULL;
200 963b370f 2018-05-20 stsp const struct got_error *err = NULL;
202 963b370f 2018-05-20 stsp *ws = NULL;
203 963b370f 2018-05-20 stsp *wlen = mbstowcs(NULL, s, 0);
204 00dfcb92 2018-06-11 stsp if (*wlen == (size_t)-1) {
205 00dfcb92 2018-06-11 stsp int vislen;
206 00dfcb92 2018-06-11 stsp if (errno != EILSEQ)
207 00dfcb92 2018-06-11 stsp return got_error_from_errno();
209 00dfcb92 2018-06-11 stsp /* byte string invalid in current encoding; try to "fix" it */
210 00dfcb92 2018-06-11 stsp err = got_mbsavis(&vis, &vislen, s);
212 00dfcb92 2018-06-11 stsp return err;
213 00dfcb92 2018-06-11 stsp *wlen = mbstowcs(NULL, vis, 0);
214 a7f50699 2018-06-11 stsp if (*wlen == (size_t)-1) {
215 a7f50699 2018-06-11 stsp err = got_error_from_errno(); /* give up */
220 963b370f 2018-05-20 stsp *ws = calloc(*wlen + 1, sizeof(*ws));
221 a7f50699 2018-06-11 stsp if (*ws == NULL) {
222 a7f50699 2018-06-11 stsp err = got_error_from_errno();
226 00dfcb92 2018-06-11 stsp if (mbstowcs(*ws, vis ? vis : s, *wlen) != *wlen)
227 963b370f 2018-05-20 stsp err = got_error_from_errno();
232 963b370f 2018-05-20 stsp *ws = NULL;
235 963b370f 2018-05-20 stsp return err;
238 963b370f 2018-05-20 stsp /* Format a line for display, ensuring that it won't overflow a width limit. */
239 963b370f 2018-05-20 stsp static const struct got_error *
240 ffd1d5e5 2018-06-23 stsp format_line(wchar_t **wlinep, int *widthp, const char *line, int wlimit)
242 963b370f 2018-05-20 stsp const struct got_error *err = NULL;
243 963b370f 2018-05-20 stsp int cols = 0;
244 963b370f 2018-05-20 stsp wchar_t *wline = NULL;
245 963b370f 2018-05-20 stsp size_t wlen;
248 963b370f 2018-05-20 stsp *wlinep = NULL;
249 b700b5d6 2018-07-10 stsp *widthp = 0;
251 963b370f 2018-05-20 stsp err = mbs2ws(&wline, &wlen, line);
253 963b370f 2018-05-20 stsp return err;
256 b700b5d6 2018-07-10 stsp while (i < wlen && cols < wlimit) {
257 963b370f 2018-05-20 stsp int width = wcwidth(wline[i]);
258 963b370f 2018-05-20 stsp switch (width) {
264 b700b5d6 2018-07-10 stsp if (cols + width <= wlimit) {
265 b700b5d6 2018-07-10 stsp cols += width;
270 963b370f 2018-05-20 stsp if (wline[i] == L'\t')
271 b700b5d6 2018-07-10 stsp cols += TABSIZE - ((cols + 1) % TABSIZE);
275 963b370f 2018-05-20 stsp err = got_error_from_errno();
279 963b370f 2018-05-20 stsp wline[i] = L'\0';
280 b700b5d6 2018-07-10 stsp if (widthp)
281 b700b5d6 2018-07-10 stsp *widthp = cols;
284 963b370f 2018-05-20 stsp free(wline);
286 963b370f 2018-05-20 stsp *wlinep = wline;
287 963b370f 2018-05-20 stsp return err;
290 963b370f 2018-05-20 stsp static const struct got_error *
291 2814baeb 2018-08-01 stsp draw_commit(struct tog_view *view, struct got_commit_object *commit,
292 2814baeb 2018-08-01 stsp struct got_object_id *id)
294 80ddbec8 2018-04-29 stsp const struct got_error *err = NULL;
295 b39d25c7 2018-07-10 stsp char datebuf[10]; /* YY-MM-DD + SPACE + NUL */
296 80ddbec8 2018-04-29 stsp char *logmsg0 = NULL, *logmsg = NULL;
297 6d9fbc00 2018-04-29 stsp char *author0 = NULL, *author = NULL;
298 bb737323 2018-05-20 stsp wchar_t *wlogmsg = NULL, *wauthor = NULL;
299 bb737323 2018-05-20 stsp int author_width, logmsg_width;
300 6d9fbc00 2018-04-29 stsp char *newline, *smallerthan;
301 80ddbec8 2018-04-29 stsp char *line = NULL;
302 bb737323 2018-05-20 stsp int col, limit;
303 b39d25c7 2018-07-10 stsp static const size_t date_display_cols = 9;
304 bb737323 2018-05-20 stsp static const size_t author_display_cols = 16;
305 f7d12f7e 2018-08-01 stsp const int avail = view->ncols;
307 c70c5802 2018-08-01 stsp if (strftime(datebuf, sizeof(datebuf), "%g/%m/%d ",
308 c70c5802 2018-08-01 stsp &commit->tm_committer) >= sizeof(datebuf))
309 b39d25c7 2018-07-10 stsp return got_error(GOT_ERR_NO_SPACE);
311 b39d25c7 2018-07-10 stsp if (avail < date_display_cols)
312 b39d25c7 2018-07-10 stsp limit = MIN(sizeof(datebuf) - 1, avail);
314 b39d25c7 2018-07-10 stsp limit = MIN(date_display_cols, sizeof(datebuf) - 1);
315 2814baeb 2018-08-01 stsp waddnstr(view->window, datebuf, limit);
316 b39d25c7 2018-07-10 stsp col = limit + 1;
317 b39d25c7 2018-07-10 stsp if (col > avail)
320 6d9fbc00 2018-04-29 stsp author0 = strdup(commit->author);
321 6d9fbc00 2018-04-29 stsp if (author0 == NULL) {
322 80ddbec8 2018-04-29 stsp err = got_error_from_errno();
325 6d9fbc00 2018-04-29 stsp author = author0;
326 6d9fbc00 2018-04-29 stsp smallerthan = strchr(author, '<');
327 6d9fbc00 2018-04-29 stsp if (smallerthan)
328 6d9fbc00 2018-04-29 stsp *smallerthan = '\0';
330 6d9fbc00 2018-04-29 stsp char *at = strchr(author, '@');
332 6d9fbc00 2018-04-29 stsp *at = '\0';
334 b39d25c7 2018-07-10 stsp limit = avail - col;
335 bb737323 2018-05-20 stsp err = format_line(&wauthor, &author_width, author, limit);
338 2814baeb 2018-08-01 stsp waddwstr(view->window, wauthor);
339 bb737323 2018-05-20 stsp col += author_width;
340 9c2eaf34 2018-05-20 stsp while (col <= avail && author_width < author_display_cols + 1) {
341 2814baeb 2018-08-01 stsp waddch(view->window, ' ');
343 bb737323 2018-05-20 stsp author_width++;
345 9c2eaf34 2018-05-20 stsp if (col > avail)
348 bb737323 2018-05-20 stsp logmsg0 = strdup(commit->logmsg);
349 bb737323 2018-05-20 stsp if (logmsg0 == NULL) {
350 6d9fbc00 2018-04-29 stsp err = got_error_from_errno();
353 bb737323 2018-05-20 stsp logmsg = logmsg0;
354 bb737323 2018-05-20 stsp while (*logmsg == '\n')
356 bb737323 2018-05-20 stsp newline = strchr(logmsg, '\n');
357 bb737323 2018-05-20 stsp if (newline)
358 bb737323 2018-05-20 stsp *newline = '\0';
359 bb737323 2018-05-20 stsp limit = avail - col;
360 bb737323 2018-05-20 stsp err = format_line(&wlogmsg, &logmsg_width, logmsg, limit);
363 2814baeb 2018-08-01 stsp waddwstr(view->window, wlogmsg);
364 bb737323 2018-05-20 stsp col += logmsg_width;
365 bb737323 2018-05-20 stsp while (col <= avail) {
366 2814baeb 2018-08-01 stsp waddch(view->window, ' ');
370 80ddbec8 2018-04-29 stsp free(logmsg0);
371 bb737323 2018-05-20 stsp free(wlogmsg);
372 6d9fbc00 2018-04-29 stsp free(author0);
373 bb737323 2018-05-20 stsp free(wauthor);
374 80ddbec8 2018-04-29 stsp free(line);
375 80ddbec8 2018-04-29 stsp return err;
378 80ddbec8 2018-04-29 stsp struct commit_queue_entry {
379 80ddbec8 2018-04-29 stsp TAILQ_ENTRY(commit_queue_entry) entry;
380 80ddbec8 2018-04-29 stsp struct got_object_id *id;
381 80ddbec8 2018-04-29 stsp struct got_commit_object *commit;
383 ecb28ae0 2018-07-16 stsp TAILQ_HEAD(commit_queue_head, commit_queue_entry);
384 ecb28ae0 2018-07-16 stsp struct commit_queue {
385 ecb28ae0 2018-07-16 stsp int ncommits;
386 ecb28ae0 2018-07-16 stsp struct commit_queue_head head;
389 899d86c2 2018-05-10 stsp static struct commit_queue_entry *
390 899d86c2 2018-05-10 stsp alloc_commit_queue_entry(struct got_commit_object *commit,
391 899d86c2 2018-05-10 stsp struct got_object_id *id)
393 80ddbec8 2018-04-29 stsp struct commit_queue_entry *entry;
395 80ddbec8 2018-04-29 stsp entry = calloc(1, sizeof(*entry));
396 80ddbec8 2018-04-29 stsp if (entry == NULL)
397 899d86c2 2018-05-10 stsp return NULL;
399 899d86c2 2018-05-10 stsp entry->id = id;
400 99db9666 2018-05-07 stsp entry->commit = commit;
401 899d86c2 2018-05-10 stsp return entry;
404 99db9666 2018-05-07 stsp static void
405 99db9666 2018-05-07 stsp pop_commit(struct commit_queue *commits)
407 99db9666 2018-05-07 stsp struct commit_queue_entry *entry;
409 ecb28ae0 2018-07-16 stsp entry = TAILQ_FIRST(&commits->head);
410 ecb28ae0 2018-07-16 stsp TAILQ_REMOVE(&commits->head, entry, entry);
411 99db9666 2018-05-07 stsp got_object_commit_close(entry->commit);
412 ecb28ae0 2018-07-16 stsp commits->ncommits--;
413 9ba79e04 2018-06-11 stsp /* Don't free entry->id! It is owned by the commit graph. */
414 99db9666 2018-05-07 stsp free(entry);
417 99db9666 2018-05-07 stsp static void
418 99db9666 2018-05-07 stsp free_commits(struct commit_queue *commits)
420 ecb28ae0 2018-07-16 stsp while (!TAILQ_EMPTY(&commits->head))
421 99db9666 2018-05-07 stsp pop_commit(commits);
424 c4972b91 2018-05-07 stsp static const struct got_error *
425 9ba79e04 2018-06-11 stsp queue_commits(struct got_commit_graph *graph, struct commit_queue *commits,
426 ecb28ae0 2018-07-16 stsp struct got_object_id *start_id, int minqueue, int init,
427 ecb28ae0 2018-07-16 stsp struct got_repository *repo, const char *path)
429 899d86c2 2018-05-10 stsp const struct got_error *err = NULL;
430 899d86c2 2018-05-10 stsp struct got_object_id *id;
431 9ba79e04 2018-06-11 stsp struct commit_queue_entry *entry;
432 ecb28ae0 2018-07-16 stsp int nfetched, nqueued = 0, found_obj = 0;
433 c8f60bff 2018-07-23 stsp int is_root_path = strcmp(path, "/") == 0;
435 9ba79e04 2018-06-11 stsp err = got_commit_graph_iter_start(graph, start_id);
437 c4972b91 2018-05-07 stsp return err;
439 ecb28ae0 2018-07-16 stsp entry = TAILQ_LAST(&commits->head, commit_queue_head);
440 9ba79e04 2018-06-11 stsp if (entry && got_object_id_cmp(entry->id, start_id) == 0) {
441 9ba79e04 2018-06-11 stsp int nfetched;
443 9ba79e04 2018-06-11 stsp /* Start ID's commit is already on the queue; skip over it. */
444 9ba79e04 2018-06-11 stsp err = got_commit_graph_iter_next(&id, graph);
445 9ba79e04 2018-06-11 stsp if (err && err->code != GOT_ERR_ITER_NEED_MORE)
446 9ba79e04 2018-06-11 stsp return err;
448 9ba79e04 2018-06-11 stsp err = got_commit_graph_fetch_commits(&nfetched, graph, 1, repo);
450 9ba79e04 2018-06-11 stsp return err;
453 9ba79e04 2018-06-11 stsp while (1) {
454 9ba79e04 2018-06-11 stsp struct got_commit_object *commit;
456 9ba79e04 2018-06-11 stsp err = got_commit_graph_iter_next(&id, graph);
458 ecb28ae0 2018-07-16 stsp if (err->code != GOT_ERR_ITER_NEED_MORE)
460 ecb28ae0 2018-07-16 stsp if (nqueued >= minqueue) {
461 9ba79e04 2018-06-11 stsp err = NULL;
464 ecb28ae0 2018-07-16 stsp err = got_commit_graph_fetch_commits(&nfetched,
465 ecb28ae0 2018-07-16 stsp graph, 1, repo);
467 ecb28ae0 2018-07-16 stsp return err;
470 ecb28ae0 2018-07-16 stsp if (id == NULL)
473 9ba79e04 2018-06-11 stsp err = got_object_open_as_commit(&commit, repo, id);
477 c8f60bff 2018-07-23 stsp if (!is_root_path) {
478 ecb28ae0 2018-07-16 stsp struct got_object *obj;
479 ecb28ae0 2018-07-16 stsp struct got_object_qid *pid;
480 ecb28ae0 2018-07-16 stsp int changed = 0;
482 ecb28ae0 2018-07-16 stsp err = got_object_open_by_path(&obj, repo, id, path);
484 c8f60bff 2018-07-23 stsp got_object_commit_close(commit);
485 ecb28ae0 2018-07-16 stsp if (err->code == GOT_ERR_NO_OBJ &&
486 ecb28ae0 2018-07-16 stsp (found_obj || !init)) {
487 ecb28ae0 2018-07-16 stsp /* History stops here. */
488 ecb28ae0 2018-07-16 stsp err = got_error(GOT_ERR_ITER_COMPLETED);
492 ecb28ae0 2018-07-16 stsp found_obj = 1;
494 ecb28ae0 2018-07-16 stsp pid = SIMPLEQ_FIRST(&commit->parent_ids);
495 ecb28ae0 2018-07-16 stsp if (pid != NULL) {
496 ecb28ae0 2018-07-16 stsp struct got_object *pobj;
497 ecb28ae0 2018-07-16 stsp err = got_object_open_by_path(&pobj, repo,
498 ecb28ae0 2018-07-16 stsp pid->id, path);
500 ecb28ae0 2018-07-16 stsp if (err->code != GOT_ERR_NO_OBJ) {
501 ecb28ae0 2018-07-16 stsp got_object_close(obj);
502 c8f60bff 2018-07-23 stsp got_object_commit_close(commit);
505 ecb28ae0 2018-07-16 stsp err = NULL;
506 ecb28ae0 2018-07-16 stsp changed = 1;
508 c2f16475 2018-07-23 stsp struct got_object_id *id, *pid;
509 c2f16475 2018-07-23 stsp id = got_object_get_id(obj);
510 c2f16475 2018-07-23 stsp if (id == NULL) {
511 c2f16475 2018-07-23 stsp err = got_error_from_errno();
512 c8f60bff 2018-07-23 stsp got_object_close(obj);
513 c8f60bff 2018-07-23 stsp got_object_close(pobj);
516 c2f16475 2018-07-23 stsp pid = got_object_get_id(pobj);
517 c2f16475 2018-07-23 stsp if (pid == NULL) {
518 c2f16475 2018-07-23 stsp err = got_error_from_errno();
520 c8f60bff 2018-07-23 stsp got_object_close(obj);
521 c8f60bff 2018-07-23 stsp got_object_close(pobj);
525 c2f16475 2018-07-23 stsp (got_object_id_cmp(id, pid) != 0);
526 ecb28ae0 2018-07-16 stsp got_object_close(pobj);
531 c8f60bff 2018-07-23 stsp got_object_close(obj);
532 ecb28ae0 2018-07-16 stsp if (!changed) {
533 ecb28ae0 2018-07-16 stsp got_object_commit_close(commit);
538 9ba79e04 2018-06-11 stsp entry = alloc_commit_queue_entry(commit, id);
539 9ba79e04 2018-06-11 stsp if (entry == NULL) {
540 9ba79e04 2018-06-11 stsp err = got_error_from_errno();
543 ecb28ae0 2018-07-16 stsp TAILQ_INSERT_TAIL(&commits->head, entry, entry);
545 ecb28ae0 2018-07-16 stsp commits->ncommits++;
548 9ba79e04 2018-06-11 stsp return err;
551 99db9666 2018-05-07 stsp static const struct got_error *
552 9ba79e04 2018-06-11 stsp fetch_next_commit(struct commit_queue_entry **pentry,
553 9ba79e04 2018-06-11 stsp struct commit_queue_entry *entry, struct commit_queue *commits,
554 ecb28ae0 2018-07-16 stsp struct got_commit_graph *graph, struct got_repository *repo,
555 ecb28ae0 2018-07-16 stsp const char *path)
557 899d86c2 2018-05-10 stsp const struct got_error *err = NULL;
559 9ba79e04 2018-06-11 stsp *pentry = NULL;
561 ecb28ae0 2018-07-16 stsp err = queue_commits(graph, commits, entry->id, 1, 0, repo, path);
563 9ba79e04 2018-06-11 stsp return err;
565 9ba79e04 2018-06-11 stsp /* Next entry to display should now be available. */
566 9ba79e04 2018-06-11 stsp *pentry = TAILQ_NEXT(entry, entry);
567 9ba79e04 2018-06-11 stsp if (*pentry == NULL)
568 9ba79e04 2018-06-11 stsp return got_error(GOT_ERR_NO_OBJ);
570 9ba79e04 2018-06-11 stsp return NULL;
573 899d86c2 2018-05-10 stsp static const struct got_error *
574 9ba79e04 2018-06-11 stsp get_head_commit_id(struct got_object_id **head_id, struct got_repository *repo)
576 9ba79e04 2018-06-11 stsp const struct got_error *err = NULL;
577 9ba79e04 2018-06-11 stsp struct got_reference *head_ref;
579 9ba79e04 2018-06-11 stsp *head_id = NULL;
581 9ba79e04 2018-06-11 stsp err = got_ref_open(&head_ref, repo, GOT_REF_HEAD);
583 99db9666 2018-05-07 stsp return err;
585 9ba79e04 2018-06-11 stsp err = got_ref_resolve(head_id, repo, head_ref);
586 9ba79e04 2018-06-11 stsp got_ref_close(head_ref);
588 9ba79e04 2018-06-11 stsp *head_id = NULL;
589 99db9666 2018-05-07 stsp return err;
592 9ba79e04 2018-06-11 stsp return NULL;
595 0553a4e3 2018-04-30 stsp static const struct got_error *
596 2814baeb 2018-08-01 stsp draw_commits(struct tog_view *view, struct commit_queue_entry **last,
597 ecb28ae0 2018-07-16 stsp struct commit_queue_entry **selected, struct commit_queue_entry *first,
598 a7f40148 2018-07-18 stsp struct commit_queue *commits, int selected_idx, int limit,
599 a7f40148 2018-07-18 stsp struct got_commit_graph *graph, struct got_repository *repo,
600 a7f40148 2018-07-18 stsp const char *path)
602 0553a4e3 2018-04-30 stsp const struct got_error *err = NULL;
603 0553a4e3 2018-04-30 stsp struct commit_queue_entry *entry;
604 ecb28ae0 2018-07-16 stsp int ncommits, width;
605 867c6645 2018-07-10 stsp char *id_str, *header;
606 ecb28ae0 2018-07-16 stsp wchar_t *wline;
608 e0d42f60 2018-07-22 stsp entry = first;
609 e0d42f60 2018-07-22 stsp ncommits = 0;
610 e0d42f60 2018-07-22 stsp while (entry) {
611 e0d42f60 2018-07-22 stsp if (ncommits == selected_idx) {
612 e0d42f60 2018-07-22 stsp *selected = entry;
615 e0d42f60 2018-07-22 stsp entry = TAILQ_NEXT(entry, entry);
616 e0d42f60 2018-07-22 stsp ncommits++;
619 867c6645 2018-07-10 stsp err = got_object_id_str(&id_str, (*selected)->id);
621 867c6645 2018-07-10 stsp return err;
623 c3ba6f36 2018-08-01 stsp if (path && strcmp(path, "/") != 0) {
624 ecb28ae0 2018-07-16 stsp if (asprintf(&header, "commit: %s [%s]", id_str, path) == -1) {
625 ecb28ae0 2018-07-16 stsp err = got_error_from_errno();
626 ecb28ae0 2018-07-16 stsp free(id_str);
627 ecb28ae0 2018-07-16 stsp return err;
629 ecb28ae0 2018-07-16 stsp } else if (asprintf(&header, "commit: %s", id_str) == -1) {
630 867c6645 2018-07-10 stsp err = got_error_from_errno();
631 867c6645 2018-07-10 stsp free(id_str);
632 867c6645 2018-07-10 stsp return err;
634 ecb28ae0 2018-07-16 stsp free(id_str);
635 f7d12f7e 2018-08-01 stsp err = format_line(&wline, &width, header, view->ncols);
637 ecb28ae0 2018-07-16 stsp free(header);
638 ecb28ae0 2018-07-16 stsp return err;
640 ecb28ae0 2018-07-16 stsp free(header);
642 2814baeb 2018-08-01 stsp werase(view->window);
644 2814baeb 2018-08-01 stsp waddwstr(view->window, wline);
645 f7d12f7e 2018-08-01 stsp if (width < view->ncols)
646 2814baeb 2018-08-01 stsp waddch(view->window, '\n');
647 ecb28ae0 2018-07-16 stsp free(wline);
648 ecb28ae0 2018-07-16 stsp if (limit <= 1)
649 ecb28ae0 2018-07-16 stsp return NULL;
651 899d86c2 2018-05-10 stsp entry = first;
652 899d86c2 2018-05-10 stsp *last = first;
653 867c6645 2018-07-10 stsp ncommits = 0;
654 899d86c2 2018-05-10 stsp while (entry) {
655 ecb28ae0 2018-07-16 stsp if (ncommits >= limit - 1)
657 e0d42f60 2018-07-22 stsp if (ncommits == selected_idx)
658 2814baeb 2018-08-01 stsp wstandout(view->window);
659 2814baeb 2018-08-01 stsp err = draw_commit(view, entry->commit, entry->id);
660 cd0acaa7 2018-05-20 stsp if (ncommits == selected_idx)
661 2814baeb 2018-08-01 stsp wstandend(view->window);
664 0553a4e3 2018-04-30 stsp ncommits++;
665 899d86c2 2018-05-10 stsp *last = entry;
666 a7f40148 2018-07-18 stsp if (entry == TAILQ_LAST(&commits->head, commit_queue_head)) {
667 a7f40148 2018-07-18 stsp err = queue_commits(graph, commits, entry->id, 1,
668 a7f40148 2018-07-18 stsp 0, repo, path);
670 a7f40148 2018-07-18 stsp if (err->code != GOT_ERR_ITER_COMPLETED)
671 a7f40148 2018-07-18 stsp return err;
672 a7f40148 2018-07-18 stsp err = NULL;
675 899d86c2 2018-05-10 stsp entry = TAILQ_NEXT(entry, entry);
678 80ddbec8 2018-04-29 stsp update_panels();
679 80ddbec8 2018-04-29 stsp doupdate();
681 80ddbec8 2018-04-29 stsp return err;
684 07b55e75 2018-05-10 stsp static void
685 16482c3b 2018-05-20 stsp scroll_up(struct commit_queue_entry **first_displayed_entry, int maxscroll,
686 07b55e75 2018-05-10 stsp struct commit_queue *commits)
688 07b55e75 2018-05-10 stsp struct commit_queue_entry *entry;
689 07b55e75 2018-05-10 stsp int nscrolled = 0;
691 ecb28ae0 2018-07-16 stsp entry = TAILQ_FIRST(&commits->head);
692 07b55e75 2018-05-10 stsp if (*first_displayed_entry == entry)
695 07b55e75 2018-05-10 stsp entry = *first_displayed_entry;
696 16482c3b 2018-05-20 stsp while (entry && nscrolled < maxscroll) {
697 ecb28ae0 2018-07-16 stsp entry = TAILQ_PREV(entry, commit_queue_head, entry);
698 07b55e75 2018-05-10 stsp if (entry) {
699 07b55e75 2018-05-10 stsp *first_displayed_entry = entry;
700 07b55e75 2018-05-10 stsp nscrolled++;
705 aa075928 2018-05-10 stsp static const struct got_error *
706 16482c3b 2018-05-20 stsp scroll_down(struct commit_queue_entry **first_displayed_entry, int maxscroll,
707 aa075928 2018-05-10 stsp struct commit_queue_entry *last_displayed_entry,
708 9ba79e04 2018-06-11 stsp struct commit_queue *commits, struct got_commit_graph *graph,
709 ecb28ae0 2018-07-16 stsp struct got_repository *repo, const char *path)
711 aa075928 2018-05-10 stsp const struct got_error *err = NULL;
712 dd0a52c1 2018-05-20 stsp struct commit_queue_entry *pentry;
713 aa075928 2018-05-10 stsp int nscrolled = 0;
716 dd0a52c1 2018-05-20 stsp pentry = TAILQ_NEXT(last_displayed_entry, entry);
717 aa075928 2018-05-10 stsp if (pentry == NULL) {
718 9ba79e04 2018-06-11 stsp err = fetch_next_commit(&pentry, last_displayed_entry,
719 ecb28ae0 2018-07-16 stsp commits, graph, repo, path);
720 9a6bf2a5 2018-05-20 stsp if (err || pentry == NULL)
723 dd0a52c1 2018-05-20 stsp last_displayed_entry = pentry;
725 dd0a52c1 2018-05-20 stsp pentry = TAILQ_NEXT(*first_displayed_entry, entry);
726 dd0a52c1 2018-05-20 stsp if (pentry == NULL)
728 aa075928 2018-05-10 stsp *first_displayed_entry = pentry;
729 16482c3b 2018-05-20 stsp } while (++nscrolled < maxscroll);
731 dd0a52c1 2018-05-20 stsp return err;
734 cd0acaa7 2018-05-20 stsp static const struct got_error *
735 6d0fee91 2018-08-01 stsp show_commit(struct tog_view *parent_view, struct commit_queue_entry *entry,
736 6d0fee91 2018-08-01 stsp struct got_repository *repo)
738 cd0acaa7 2018-05-20 stsp const struct got_error *err;
739 cd0acaa7 2018-05-20 stsp struct got_object *obj1 = NULL, *obj2 = NULL;
740 9ba79e04 2018-06-11 stsp struct got_object_qid *parent_id;
741 ea5e7bb5 2018-08-01 stsp struct tog_view *view;
743 cd0acaa7 2018-05-20 stsp err = got_object_open(&obj2, repo, entry->id);
745 cd0acaa7 2018-05-20 stsp return err;
747 9ba79e04 2018-06-11 stsp parent_id = SIMPLEQ_FIRST(&entry->commit->parent_ids);
748 9ba79e04 2018-06-11 stsp if (parent_id) {
749 9ba79e04 2018-06-11 stsp err = got_object_open(&obj1, repo, parent_id->id);
754 6d0fee91 2018-08-01 stsp view = open_view(0, 0, 0, 0, parent_view);
755 ea5e7bb5 2018-08-01 stsp if (view == NULL) {
756 ea5e7bb5 2018-08-01 stsp err = got_error_from_errno();
760 ea5e7bb5 2018-08-01 stsp err = show_diff_view(view, obj1, obj2, repo);
761 ea5e7bb5 2018-08-01 stsp close_view(view);
762 cdf1ee82 2018-08-01 stsp show_view(parent_view);
765 cd0acaa7 2018-05-20 stsp got_object_close(obj1);
767 cd0acaa7 2018-05-20 stsp got_object_close(obj2);
768 cd0acaa7 2018-05-20 stsp return err;
771 80ddbec8 2018-04-29 stsp static const struct got_error *
772 6d0fee91 2018-08-01 stsp browse_commit(struct tog_view *parent_view, struct commit_queue_entry *entry,
773 6d0fee91 2018-08-01 stsp struct got_repository *repo)
775 9343a5fb 2018-06-23 stsp const struct got_error *err = NULL;
776 9343a5fb 2018-06-23 stsp struct got_tree_object *tree;
777 5221c383 2018-08-01 stsp struct tog_view *view;
779 9343a5fb 2018-06-23 stsp err = got_object_open_as_tree(&tree, repo, entry->commit->tree_id);
781 9343a5fb 2018-06-23 stsp return err;
783 6d0fee91 2018-08-01 stsp view = open_view(0, 0, 0, 0, parent_view);
784 5221c383 2018-08-01 stsp if (view == NULL) {
785 5221c383 2018-08-01 stsp err = got_error_from_errno();
788 5221c383 2018-08-01 stsp err = show_tree_view(view, tree, entry->id, repo);
789 5221c383 2018-08-01 stsp close_view(view);
790 cdf1ee82 2018-08-01 stsp show_view(parent_view);
792 9343a5fb 2018-06-23 stsp got_object_tree_close(tree);
793 9343a5fb 2018-06-23 stsp return err;
796 9343a5fb 2018-06-23 stsp static const struct got_error *
797 04cc582a 2018-08-01 stsp show_log_view(struct tog_view *view, struct got_object_id *start_id,
798 04cc582a 2018-08-01 stsp struct got_repository *repo, const char *path)
800 80ddbec8 2018-04-29 stsp const struct got_error *err = NULL;
801 9ba79e04 2018-06-11 stsp struct got_object_id *head_id = NULL;
802 ecb28ae0 2018-07-16 stsp int ch, done = 0, selected = 0, nfetched;
803 ecb28ae0 2018-07-16 stsp struct got_commit_graph *graph = NULL;
804 0553a4e3 2018-04-30 stsp struct commit_queue commits;
805 899d86c2 2018-05-10 stsp struct commit_queue_entry *first_displayed_entry = NULL;
806 899d86c2 2018-05-10 stsp struct commit_queue_entry *last_displayed_entry = NULL;
807 cd0acaa7 2018-05-20 stsp struct commit_queue_entry *selected_entry = NULL;
808 ecb28ae0 2018-07-16 stsp char *in_repo_path = NULL;
810 ecb28ae0 2018-07-16 stsp err = got_repo_map_path(&in_repo_path, repo, path);
811 ecb28ae0 2018-07-16 stsp if (err != NULL)
814 9ba79e04 2018-06-11 stsp err = get_head_commit_id(&head_id, repo);
816 9ba79e04 2018-06-11 stsp return err;
818 034e3b69 2018-07-22 stsp /* The graph contains all commits. */
819 5489743f 2018-06-13 stsp err = got_commit_graph_open(&graph, head_id, 0, repo);
822 034e3b69 2018-07-22 stsp /* The commit queue contains a subset of commits filtered by path. */
823 034e3b69 2018-07-22 stsp TAILQ_INIT(&commits.head);
824 034e3b69 2018-07-22 stsp commits.ncommits = 0;
826 9ba79e04 2018-06-11 stsp /* Populate commit graph with a sufficient number of commits. */
827 9ba79e04 2018-06-11 stsp err = got_commit_graph_fetch_commits_up_to(&nfetched, graph, start_id,
833 9ba79e04 2018-06-11 stsp * Open the initial batch of commits, sorted in commit graph order.
834 9ba79e04 2018-06-11 stsp * We keep all commits open throughout the lifetime of the log view
835 9ba79e04 2018-06-11 stsp * in order to avoid having to re-fetch commits from disk while
836 9ba79e04 2018-06-11 stsp * updating the display.
838 f7d12f7e 2018-08-01 stsp err = queue_commits(graph, &commits, start_id, view->nlines, 1, repo,
839 ecb28ae0 2018-07-16 stsp in_repo_path);
841 55198a88 2018-07-16 stsp if (err->code != GOT_ERR_ITER_COMPLETED)
843 55198a88 2018-07-16 stsp err = NULL;
846 cdf1ee82 2018-08-01 stsp show_view(view);
848 034e3b69 2018-07-22 stsp first_displayed_entry = TAILQ_FIRST(&commits.head);
849 867c6645 2018-07-10 stsp selected_entry = first_displayed_entry;
850 899d86c2 2018-05-10 stsp while (!done) {
851 2814baeb 2018-08-01 stsp err = draw_commits(view, &last_displayed_entry, &selected_entry,
852 f7d12f7e 2018-08-01 stsp first_displayed_entry, &commits, selected, view->nlines,
853 a7f40148 2018-07-18 stsp graph, repo, in_repo_path);
857 80ddbec8 2018-04-29 stsp nodelay(stdscr, FALSE);
858 2814baeb 2018-08-01 stsp ch = wgetch(view->window);
859 f7182337 2018-05-20 stsp nodelay(stdscr, TRUE);
860 80ddbec8 2018-04-29 stsp switch (ch) {
867 80ddbec8 2018-04-29 stsp case KEY_UP:
868 80ddbec8 2018-04-29 stsp if (selected > 0)
869 80ddbec8 2018-04-29 stsp selected--;
870 8ec9698d 2018-05-10 stsp if (selected > 0)
872 07b55e75 2018-05-10 stsp scroll_up(&first_displayed_entry, 1, &commits);
874 48531068 2018-05-10 stsp case KEY_PPAGE:
875 ecb28ae0 2018-07-16 stsp if (TAILQ_FIRST(&commits.head) ==
876 dfc1d240 2018-05-10 stsp first_displayed_entry) {
877 dfc1d240 2018-05-10 stsp selected = 0;
880 f7d12f7e 2018-08-01 stsp scroll_up(&first_displayed_entry, view->nlines,
884 80ddbec8 2018-04-29 stsp case KEY_DOWN:
885 f7d12f7e 2018-08-01 stsp if (selected < MIN(view->nlines - 2,
886 ecb28ae0 2018-07-16 stsp commits.ncommits - 1)) {
887 15c91275 2018-05-20 stsp selected++;
890 15c91275 2018-05-20 stsp err = scroll_down(&first_displayed_entry, 1,
891 9ba79e04 2018-06-11 stsp last_displayed_entry, &commits, graph,
892 ecb28ae0 2018-07-16 stsp repo, in_repo_path);
894 ecb28ae0 2018-07-16 stsp if (err->code != GOT_ERR_ITER_COMPLETED)
896 ecb28ae0 2018-07-16 stsp err = NULL;
899 ecb28ae0 2018-07-16 stsp case KEY_NPAGE: {
900 c70c5802 2018-08-01 stsp struct commit_queue_entry *first;
901 c70c5802 2018-08-01 stsp first = first_displayed_entry;
902 c70c5802 2018-08-01 stsp err = scroll_down(&first_displayed_entry,
903 c70c5802 2018-08-01 stsp view->nlines, last_displayed_entry,
904 c70c5802 2018-08-01 stsp &commits, graph, repo, in_repo_path);
906 ecb28ae0 2018-07-16 stsp if (err->code != GOT_ERR_ITER_COMPLETED)
908 c70c5802 2018-08-01 stsp if (first == first_displayed_entry &&
909 c70c5802 2018-08-01 stsp selected < MIN(view->nlines - 2,
910 c70c5802 2018-08-01 stsp commits.ncommits - 1)) {
911 c70c5802 2018-08-01 stsp /* can't scroll further down */
912 f7d12f7e 2018-08-01 stsp selected = MIN(view->nlines - 2,
913 ecb28ae0 2018-07-16 stsp commits.ncommits - 1);
915 ecb28ae0 2018-07-16 stsp err = NULL;
919 d6df9be4 2018-04-30 stsp case KEY_RESIZE:
920 a41d2007 2018-08-01 stsp err = view_resize(view);
923 f7d12f7e 2018-08-01 stsp if (selected > view->nlines - 2)
924 f7d12f7e 2018-08-01 stsp selected = view->nlines - 2;
925 55198a88 2018-07-16 stsp if (selected > commits.ncommits - 1)
926 55198a88 2018-07-16 stsp selected = commits.ncommits - 1;
928 cd0acaa7 2018-05-20 stsp case KEY_ENTER:
930 6d0fee91 2018-08-01 stsp err = show_commit(view, selected_entry, repo);
933 cdf1ee82 2018-08-01 stsp show_view(view);
936 6d0fee91 2018-08-01 stsp err = browse_commit(view, selected_entry, repo);
939 cdf1ee82 2018-08-01 stsp show_view(view);
946 9ba79e04 2018-06-11 stsp free(head_id);
948 9ba79e04 2018-06-11 stsp got_commit_graph_close(graph);
949 0553a4e3 2018-04-30 stsp free_commits(&commits);
950 ecb28ae0 2018-07-16 stsp free(in_repo_path);
951 80ddbec8 2018-04-29 stsp return err;
954 4ed7e80c 2018-05-20 stsp static const struct got_error *
955 9f7d7167 2018-04-29 stsp cmd_log(int argc, char *argv[])
957 80ddbec8 2018-04-29 stsp const struct got_error *error;
958 ecb28ae0 2018-07-16 stsp struct got_repository *repo = NULL;
959 899d86c2 2018-05-10 stsp struct got_object_id *start_id = NULL;
960 ecb28ae0 2018-07-16 stsp char *path = NULL, *repo_path = NULL, *cwd = NULL;
961 80ddbec8 2018-04-29 stsp char *start_commit = NULL;
963 04cc582a 2018-08-01 stsp struct tog_view *view;
965 80ddbec8 2018-04-29 stsp #ifndef PROFILE
966 80ddbec8 2018-04-29 stsp if (pledge("stdio rpath wpath cpath flock proc tty", NULL) == -1)
967 80ddbec8 2018-04-29 stsp err(1, "pledge");
970 ecb28ae0 2018-07-16 stsp while ((ch = getopt(argc, argv, "c:r:")) != -1) {
971 80ddbec8 2018-04-29 stsp switch (ch) {
973 80ddbec8 2018-04-29 stsp start_commit = optarg;
976 ecb28ae0 2018-07-16 stsp repo_path = realpath(optarg, NULL);
977 ecb28ae0 2018-07-16 stsp if (repo_path == NULL)
978 ecb28ae0 2018-07-16 stsp err(1, "-r option");
982 80ddbec8 2018-04-29 stsp /* NOTREACHED */
986 80ddbec8 2018-04-29 stsp argc -= optind;
987 80ddbec8 2018-04-29 stsp argv += optind;
989 ecb28ae0 2018-07-16 stsp if (argc == 0)
990 ecb28ae0 2018-07-16 stsp path = strdup("");
991 ecb28ae0 2018-07-16 stsp else if (argc == 1)
992 ecb28ae0 2018-07-16 stsp path = strdup(argv[0]);
994 80ddbec8 2018-04-29 stsp usage_log();
995 ecb28ae0 2018-07-16 stsp if (path == NULL)
996 ecb28ae0 2018-07-16 stsp return got_error_from_errno();
998 ecb28ae0 2018-07-16 stsp cwd = getcwd(NULL, 0);
999 ecb28ae0 2018-07-16 stsp if (cwd == NULL) {
1000 ecb28ae0 2018-07-16 stsp error = got_error_from_errno();
1001 ecb28ae0 2018-07-16 stsp goto done;
1003 ecb28ae0 2018-07-16 stsp if (repo_path == NULL) {
1004 ecb28ae0 2018-07-16 stsp repo_path = strdup(cwd);
1005 ecb28ae0 2018-07-16 stsp if (repo_path == NULL) {
1006 ecb28ae0 2018-07-16 stsp error = got_error_from_errno();
1007 ecb28ae0 2018-07-16 stsp goto done;
1011 80ddbec8 2018-04-29 stsp error = got_repo_open(&repo, repo_path);
1012 80ddbec8 2018-04-29 stsp if (error != NULL)
1013 ecb28ae0 2018-07-16 stsp goto done;
1015 80ddbec8 2018-04-29 stsp if (start_commit == NULL) {
1016 899d86c2 2018-05-10 stsp error = get_head_commit_id(&start_id, repo);
1017 80ddbec8 2018-04-29 stsp if (error != NULL)
1018 ecb28ae0 2018-07-16 stsp goto done;
1020 80ddbec8 2018-04-29 stsp struct got_object *obj;
1021 80ddbec8 2018-04-29 stsp error = got_object_open_by_id_str(&obj, repo, start_commit);
1022 80ddbec8 2018-04-29 stsp if (error == NULL) {
1023 899d86c2 2018-05-10 stsp start_id = got_object_get_id(obj);
1024 899d86c2 2018-05-10 stsp if (start_id == NULL)
1025 80ddbec8 2018-04-29 stsp error = got_error_from_errno();
1026 ecb28ae0 2018-07-16 stsp goto done;
1029 80ddbec8 2018-04-29 stsp if (error != NULL)
1030 ecb28ae0 2018-07-16 stsp goto done;
1032 6d0fee91 2018-08-01 stsp view = open_view(0, 0, 0, 0, NULL);
1033 04cc582a 2018-08-01 stsp if (view == NULL) {
1034 04cc582a 2018-08-01 stsp error = got_error_from_errno();
1035 04cc582a 2018-08-01 stsp goto done;
1037 04cc582a 2018-08-01 stsp error = show_log_view(view, start_id, repo, path);
1038 04cc582a 2018-08-01 stsp close_view(view);
1040 ecb28ae0 2018-07-16 stsp free(repo_path);
1041 ecb28ae0 2018-07-16 stsp free(cwd);
1042 ecb28ae0 2018-07-16 stsp free(path);
1043 899d86c2 2018-05-10 stsp free(start_id);
1045 ecb28ae0 2018-07-16 stsp got_repo_close(repo);
1046 80ddbec8 2018-04-29 stsp return error;
1049 4ed7e80c 2018-05-20 stsp __dead static void
1050 9f7d7167 2018-04-29 stsp usage_diff(void)
1053 9f7d7167 2018-04-29 stsp fprintf(stderr, "usage: %s diff [repository-path] object1 object2\n",
1054 9f7d7167 2018-04-29 stsp getprogname());
1058 b304db33 2018-05-20 stsp static char *
1059 b304db33 2018-05-20 stsp parse_next_line(FILE *f, size_t *len)
1061 b304db33 2018-05-20 stsp char *line;
1062 b304db33 2018-05-20 stsp size_t linelen;
1063 b304db33 2018-05-20 stsp size_t lineno;
1064 b304db33 2018-05-20 stsp const char delim[3] = { '\0', '\0', '\0'};
1066 b304db33 2018-05-20 stsp line = fparseln(f, &linelen, &lineno, delim, 0);
1068 b304db33 2018-05-20 stsp *len = linelen;
1069 b304db33 2018-05-20 stsp return line;
1072 4ed7e80c 2018-05-20 stsp static const struct got_error *
1073 f7d12f7e 2018-08-01 stsp draw_file(struct tog_view *view, FILE *f, int *first_displayed_line,
1074 a70480e0 2018-06-23 stsp int *last_displayed_line, int *eof, int max_lines)
1076 61e69b96 2018-05-20 stsp const struct got_error *err;
1077 26ed57b2 2018-05-19 stsp int nlines = 0, nprinted = 0;
1078 b304db33 2018-05-20 stsp char *line;
1079 b304db33 2018-05-20 stsp size_t len;
1080 61e69b96 2018-05-20 stsp wchar_t *wline;
1081 e0b650dd 2018-05-20 stsp int width;
1083 26ed57b2 2018-05-19 stsp rewind(f);
1084 f7d12f7e 2018-08-01 stsp werase(view->window);
1087 26ed57b2 2018-05-19 stsp while (nprinted < max_lines) {
1088 b304db33 2018-05-20 stsp line = parse_next_line(f, &len);
1089 26ed57b2 2018-05-19 stsp if (line == NULL) {
1093 26ed57b2 2018-05-19 stsp if (++nlines < *first_displayed_line) {
1094 26ed57b2 2018-05-19 stsp free(line);
1098 f7d12f7e 2018-08-01 stsp err = format_line(&wline, &width, line, view->ncols);
1099 61e69b96 2018-05-20 stsp if (err) {
1100 61e69b96 2018-05-20 stsp free(line);
1101 2550e4c3 2018-07-13 stsp free(wline);
1102 61e69b96 2018-05-20 stsp return err;
1104 f7d12f7e 2018-08-01 stsp waddwstr(view->window, wline);
1105 f7d12f7e 2018-08-01 stsp if (width < view->ncols)
1106 f7d12f7e 2018-08-01 stsp waddch(view->window, '\n');
1107 26ed57b2 2018-05-19 stsp if (++nprinted == 1)
1108 26ed57b2 2018-05-19 stsp *first_displayed_line = nlines;
1109 26ed57b2 2018-05-19 stsp free(line);
1110 2550e4c3 2018-07-13 stsp free(wline);
1111 2550e4c3 2018-07-13 stsp wline = NULL;
1113 26ed57b2 2018-05-19 stsp *last_displayed_line = nlines;
1115 26ed57b2 2018-05-19 stsp update_panels();
1116 26ed57b2 2018-05-19 stsp doupdate();
1118 26ed57b2 2018-05-19 stsp return NULL;
1121 4ed7e80c 2018-05-20 stsp static const struct got_error *
1122 ea5e7bb5 2018-08-01 stsp show_diff_view(struct tog_view *view, struct got_object *obj1,
1123 ea5e7bb5 2018-08-01 stsp struct got_object *obj2, struct got_repository *repo)
1125 26ed57b2 2018-05-19 stsp const struct got_error *err;
1127 f7d12f7e 2018-08-01 stsp int ch, done = 0;
1128 f7d12f7e 2018-08-01 stsp int first_displayed_line = 1, last_displayed_line = view->nlines;
1129 b304db33 2018-05-20 stsp int eof, i;
1131 cd0acaa7 2018-05-20 stsp if (obj1 != NULL && obj2 != NULL &&
1132 cd0acaa7 2018-05-20 stsp got_object_get_type(obj1) != got_object_get_type(obj2))
1133 26ed57b2 2018-05-19 stsp return got_error(GOT_ERR_OBJ_TYPE);
1135 511a516b 2018-05-19 stsp f = got_opentemp();
1136 26ed57b2 2018-05-19 stsp if (f == NULL)
1137 26ed57b2 2018-05-19 stsp return got_error_from_errno();
1139 cd0acaa7 2018-05-20 stsp switch (got_object_get_type(obj1 ? obj1 : obj2)) {
1140 26ed57b2 2018-05-19 stsp case GOT_OBJ_TYPE_BLOB:
1141 11528a82 2018-05-19 stsp err = got_diff_objects_as_blobs(obj1, obj2, repo, f);
1143 26ed57b2 2018-05-19 stsp case GOT_OBJ_TYPE_TREE:
1144 11528a82 2018-05-19 stsp err = got_diff_objects_as_trees(obj1, obj2, repo, f);
1146 26ed57b2 2018-05-19 stsp case GOT_OBJ_TYPE_COMMIT:
1147 11528a82 2018-05-19 stsp err = got_diff_objects_as_commits(obj1, obj2, repo, f);
1150 26ed57b2 2018-05-19 stsp return got_error(GOT_ERR_OBJ_TYPE);
1153 26ed57b2 2018-05-19 stsp fflush(f);
1155 cdf1ee82 2018-08-01 stsp show_view(view);
1157 26ed57b2 2018-05-19 stsp while (!done) {
1158 f7d12f7e 2018-08-01 stsp err = draw_file(view, f, &first_displayed_line,
1159 f7d12f7e 2018-08-01 stsp &last_displayed_line, &eof, view->nlines);
1162 26ed57b2 2018-05-19 stsp nodelay(stdscr, FALSE);
1163 ea5e7bb5 2018-08-01 stsp ch = wgetch(view->window);
1164 f7182337 2018-05-20 stsp nodelay(stdscr, TRUE);
1165 26ed57b2 2018-05-19 stsp switch (ch) {
1170 26ed57b2 2018-05-19 stsp case KEY_UP:
1171 26ed57b2 2018-05-19 stsp if (first_displayed_line > 1)
1172 b304db33 2018-05-20 stsp first_displayed_line--;
1174 b304db33 2018-05-20 stsp case KEY_PPAGE:
1175 c46b9352 2018-07-12 stsp case KEY_BACKSPACE:
1177 f7d12f7e 2018-08-01 stsp while (i++ < view->nlines - 1 &&
1178 d56caa55 2018-05-20 stsp first_displayed_line > 1)
1179 26ed57b2 2018-05-19 stsp first_displayed_line--;
1182 26ed57b2 2018-05-19 stsp case KEY_DOWN:
1184 26ed57b2 2018-05-19 stsp first_displayed_line++;
1186 b304db33 2018-05-20 stsp case KEY_NPAGE:
1189 f7d12f7e 2018-08-01 stsp while (!eof && i++ < view->nlines - 1) {
1190 b304db33 2018-05-20 stsp char *line = parse_next_line(f, NULL);
1191 b304db33 2018-05-20 stsp first_displayed_line++;
1192 b304db33 2018-05-20 stsp if (line == NULL)
1196 82357c0a 2018-08-01 stsp case KEY_RESIZE:
1197 a41d2007 2018-08-01 stsp err = view_resize(view);
1199 a41d2007 2018-08-01 stsp goto done;
1206 26ed57b2 2018-05-19 stsp fclose(f);
1207 26ed57b2 2018-05-19 stsp return err;
1210 4ed7e80c 2018-05-20 stsp static const struct got_error *
1211 9f7d7167 2018-04-29 stsp cmd_diff(int argc, char *argv[])
1213 26ed57b2 2018-05-19 stsp const struct got_error *error = NULL;
1214 26ed57b2 2018-05-19 stsp struct got_repository *repo = NULL;
1215 26ed57b2 2018-05-19 stsp struct got_object *obj1 = NULL, *obj2 = NULL;
1216 26ed57b2 2018-05-19 stsp char *repo_path = NULL;
1217 26ed57b2 2018-05-19 stsp char *obj_id_str1 = NULL, *obj_id_str2 = NULL;
1219 ea5e7bb5 2018-08-01 stsp struct tog_view *view;
1221 26ed57b2 2018-05-19 stsp #ifndef PROFILE
1222 26ed57b2 2018-05-19 stsp if (pledge("stdio rpath wpath cpath flock proc tty", NULL) == -1)
1223 26ed57b2 2018-05-19 stsp err(1, "pledge");
1226 26ed57b2 2018-05-19 stsp while ((ch = getopt(argc, argv, "")) != -1) {
1227 26ed57b2 2018-05-19 stsp switch (ch) {
1230 26ed57b2 2018-05-19 stsp /* NOTREACHED */
1234 26ed57b2 2018-05-19 stsp argc -= optind;
1235 26ed57b2 2018-05-19 stsp argv += optind;
1237 26ed57b2 2018-05-19 stsp if (argc == 0) {
1238 26ed57b2 2018-05-19 stsp usage_diff(); /* TODO show local worktree changes */
1239 26ed57b2 2018-05-19 stsp } else if (argc == 2) {
1240 26ed57b2 2018-05-19 stsp repo_path = getcwd(NULL, 0);
1241 26ed57b2 2018-05-19 stsp if (repo_path == NULL)
1242 26ed57b2 2018-05-19 stsp return got_error_from_errno();
1243 26ed57b2 2018-05-19 stsp obj_id_str1 = argv[0];
1244 26ed57b2 2018-05-19 stsp obj_id_str2 = argv[1];
1245 26ed57b2 2018-05-19 stsp } else if (argc == 3) {
1246 26ed57b2 2018-05-19 stsp repo_path = realpath(argv[0], NULL);
1247 26ed57b2 2018-05-19 stsp if (repo_path == NULL)
1248 26ed57b2 2018-05-19 stsp return got_error_from_errno();
1249 26ed57b2 2018-05-19 stsp obj_id_str1 = argv[1];
1250 26ed57b2 2018-05-19 stsp obj_id_str2 = argv[2];
1252 26ed57b2 2018-05-19 stsp usage_diff();
1254 26ed57b2 2018-05-19 stsp error = got_repo_open(&repo, repo_path);
1255 26ed57b2 2018-05-19 stsp free(repo_path);
1256 26ed57b2 2018-05-19 stsp if (error)
1257 26ed57b2 2018-05-19 stsp goto done;
1259 26ed57b2 2018-05-19 stsp error = got_object_open_by_id_str(&obj1, repo, obj_id_str1);
1260 26ed57b2 2018-05-19 stsp if (error)
1261 26ed57b2 2018-05-19 stsp goto done;
1263 26ed57b2 2018-05-19 stsp error = got_object_open_by_id_str(&obj2, repo, obj_id_str2);
1264 26ed57b2 2018-05-19 stsp if (error)
1265 26ed57b2 2018-05-19 stsp goto done;
1267 6d0fee91 2018-08-01 stsp view = open_view(0, 0, 0, 0, NULL);
1268 ea5e7bb5 2018-08-01 stsp if (view == NULL) {
1269 ea5e7bb5 2018-08-01 stsp error = got_error_from_errno();
1270 ea5e7bb5 2018-08-01 stsp goto done;
1272 ea5e7bb5 2018-08-01 stsp error = show_diff_view(view, obj1, obj2, repo);
1273 ea5e7bb5 2018-08-01 stsp close_view(view);
1275 26ed57b2 2018-05-19 stsp got_repo_close(repo);
1277 26ed57b2 2018-05-19 stsp got_object_close(obj1);
1279 26ed57b2 2018-05-19 stsp got_object_close(obj2);
1280 26ed57b2 2018-05-19 stsp return error;
1283 4ed7e80c 2018-05-20 stsp __dead static void
1284 9f7d7167 2018-04-29 stsp usage_blame(void)
1287 a70480e0 2018-06-23 stsp fprintf(stderr, "usage: %s blame [-c commit] [repository-path] path\n",
1288 9f7d7167 2018-04-29 stsp getprogname());
1292 84451b3e 2018-07-10 stsp struct tog_blame_line {
1293 84451b3e 2018-07-10 stsp int annotated;
1294 84451b3e 2018-07-10 stsp struct got_object_id *id;
1297 4ed7e80c 2018-05-20 stsp static const struct got_error *
1298 f7d12f7e 2018-08-01 stsp draw_blame(struct tog_view *view, struct got_object_id *id, FILE *f,
1299 f7d12f7e 2018-08-01 stsp const char *path, struct tog_blame_line *lines, int nlines,
1300 f7d12f7e 2018-08-01 stsp int blame_complete, int selected_line, int *first_displayed_line,
1301 f7d12f7e 2018-08-01 stsp int *last_displayed_line, int *eof, int max_lines)
1303 84451b3e 2018-07-10 stsp const struct got_error *err;
1304 84451b3e 2018-07-10 stsp int lineno = 0, nprinted = 0;
1305 84451b3e 2018-07-10 stsp char *line;
1306 84451b3e 2018-07-10 stsp size_t len;
1307 84451b3e 2018-07-10 stsp wchar_t *wline;
1308 b700b5d6 2018-07-10 stsp int width, wlimit;
1309 84451b3e 2018-07-10 stsp struct tog_blame_line *blame_line;
1310 ee41ec32 2018-07-10 stsp struct got_object_id *prev_id = NULL;
1311 ab089a2a 2018-07-12 stsp char *id_str;
1313 ab089a2a 2018-07-12 stsp err = got_object_id_str(&id_str, id);
1315 ab089a2a 2018-07-12 stsp return err;
1317 84451b3e 2018-07-10 stsp rewind(f);
1318 f7d12f7e 2018-08-01 stsp werase(view->window);
1320 ab089a2a 2018-07-12 stsp if (asprintf(&line, "commit: %s", id_str) == -1) {
1321 ab089a2a 2018-07-12 stsp err = got_error_from_errno();
1322 ab089a2a 2018-07-12 stsp free(id_str);
1323 ab089a2a 2018-07-12 stsp return err;
1326 f7d12f7e 2018-08-01 stsp err = format_line(&wline, &width, line, view->ncols);
1327 ab089a2a 2018-07-12 stsp free(line);
1328 2550e4c3 2018-07-13 stsp line = NULL;
1329 f7d12f7e 2018-08-01 stsp waddwstr(view->window, wline);
1330 2550e4c3 2018-07-13 stsp free(wline);
1331 2550e4c3 2018-07-13 stsp wline = NULL;
1332 f7d12f7e 2018-08-01 stsp if (width < view->ncols)
1333 f7d12f7e 2018-08-01 stsp waddch(view->window, '\n');
1335 084063cd 2018-07-12 stsp if (asprintf(&line, "[%d/%d] %s%s",
1336 084063cd 2018-07-12 stsp *first_displayed_line - 1 + selected_line, nlines,
1337 12a0b23b 2018-07-12 stsp blame_complete ? "" : "annotating ", path) == -1) {
1338 ab089a2a 2018-07-12 stsp free(id_str);
1339 3f60a8ef 2018-07-10 stsp return got_error_from_errno();
1341 ab089a2a 2018-07-12 stsp free(id_str);
1342 f7d12f7e 2018-08-01 stsp err = format_line(&wline, &width, line, view->ncols);
1343 3f60a8ef 2018-07-10 stsp free(line);
1344 2550e4c3 2018-07-13 stsp line = NULL;
1346 3f60a8ef 2018-07-10 stsp return err;
1347 f7d12f7e 2018-08-01 stsp waddwstr(view->window, wline);
1348 2550e4c3 2018-07-13 stsp free(wline);
1349 2550e4c3 2018-07-13 stsp wline = NULL;
1350 f7d12f7e 2018-08-01 stsp if (width < view->ncols)
1351 f7d12f7e 2018-08-01 stsp waddch(view->window, '\n');
1354 ab089a2a 2018-07-12 stsp while (nprinted < max_lines - 2) {
1355 84451b3e 2018-07-10 stsp line = parse_next_line(f, &len);
1356 84451b3e 2018-07-10 stsp if (line == NULL) {
1360 84451b3e 2018-07-10 stsp if (++lineno < *first_displayed_line) {
1361 84451b3e 2018-07-10 stsp free(line);
1365 f7d12f7e 2018-08-01 stsp wlimit = view->ncols < 9 ? 0 : view->ncols - 9;
1366 b700b5d6 2018-07-10 stsp err = format_line(&wline, &width, line, wlimit);
1367 84451b3e 2018-07-10 stsp if (err) {
1368 84451b3e 2018-07-10 stsp free(line);
1369 84451b3e 2018-07-10 stsp return err;
1372 b700b5d6 2018-07-10 stsp if (nprinted == selected_line - 1)
1373 f7d12f7e 2018-08-01 stsp wstandout(view->window);
1375 84451b3e 2018-07-10 stsp blame_line = &lines[lineno - 1];
1376 ee41ec32 2018-07-10 stsp if (blame_line->annotated && prev_id &&
1377 ee41ec32 2018-07-10 stsp got_object_id_cmp(prev_id, blame_line->id) == 0)
1378 f7d12f7e 2018-08-01 stsp waddstr(view->window, " ");
1379 ee41ec32 2018-07-10 stsp else if (blame_line->annotated) {
1380 84451b3e 2018-07-10 stsp char *id_str;
1381 84451b3e 2018-07-10 stsp err = got_object_id_str(&id_str, blame_line->id);
1382 84451b3e 2018-07-10 stsp if (err) {
1383 84451b3e 2018-07-10 stsp free(line);
1384 2550e4c3 2018-07-13 stsp free(wline);
1385 84451b3e 2018-07-10 stsp return err;
1387 f7d12f7e 2018-08-01 stsp wprintw(view->window, "%.8s ", id_str);
1388 84451b3e 2018-07-10 stsp free(id_str);
1389 ee41ec32 2018-07-10 stsp prev_id = blame_line->id;
1391 f7d12f7e 2018-08-01 stsp waddstr(view->window, "........ ");
1392 ee41ec32 2018-07-10 stsp prev_id = NULL;
1395 f7d12f7e 2018-08-01 stsp waddwstr(view->window, wline);
1396 b700b5d6 2018-07-10 stsp while (width < wlimit) {
1397 f7d12f7e 2018-08-01 stsp waddch(view->window, ' ');
1400 b700b5d6 2018-07-10 stsp if (nprinted == selected_line - 1)
1401 f7d12f7e 2018-08-01 stsp wstandend(view->window);
1402 84451b3e 2018-07-10 stsp if (++nprinted == 1)
1403 84451b3e 2018-07-10 stsp *first_displayed_line = lineno;
1404 84451b3e 2018-07-10 stsp free(line);
1405 2550e4c3 2018-07-13 stsp free(wline);
1406 2550e4c3 2018-07-13 stsp wline = NULL;
1408 84451b3e 2018-07-10 stsp *last_displayed_line = lineno;
1410 84451b3e 2018-07-10 stsp update_panels();
1411 84451b3e 2018-07-10 stsp doupdate();
1413 84451b3e 2018-07-10 stsp return NULL;
1416 84451b3e 2018-07-10 stsp struct tog_blame_cb_args {
1417 84451b3e 2018-07-10 stsp pthread_mutex_t *mutex;
1418 84451b3e 2018-07-10 stsp struct tog_blame_line *lines; /* one per line */
1419 84451b3e 2018-07-10 stsp int nlines;
1421 7cc84d77 2018-08-01 stsp struct tog_view *view;
1422 ab089a2a 2018-07-12 stsp struct got_object_id *commit_id;
1424 3f60a8ef 2018-07-10 stsp const char *path;
1425 84451b3e 2018-07-10 stsp int *first_displayed_line;
1426 84451b3e 2018-07-10 stsp int *last_displayed_line;
1427 b700b5d6 2018-07-10 stsp int *selected_line;
1428 18430de3 2018-07-10 stsp int *quit;
1431 84451b3e 2018-07-10 stsp static const struct got_error *
1432 84451b3e 2018-07-10 stsp blame_cb(void *arg, int nlines, int lineno, struct got_object_id *id)
1434 84451b3e 2018-07-10 stsp const struct got_error *err = NULL;
1435 84451b3e 2018-07-10 stsp struct tog_blame_cb_args *a = arg;
1436 84451b3e 2018-07-10 stsp struct tog_blame_line *line;
1439 d68a0a7d 2018-07-10 stsp if (nlines != a->nlines ||
1440 d68a0a7d 2018-07-10 stsp (lineno != -1 && lineno < 1) || lineno > a->nlines)
1441 84451b3e 2018-07-10 stsp return got_error(GOT_ERR_RANGE);
1443 84451b3e 2018-07-10 stsp if (pthread_mutex_lock(a->mutex) != 0)
1444 84451b3e 2018-07-10 stsp return got_error_from_errno();
1446 18430de3 2018-07-10 stsp if (*a->quit) { /* user has quit the blame view */
1447 d68a0a7d 2018-07-10 stsp err = got_error(GOT_ERR_ITER_COMPLETED);
1448 d68a0a7d 2018-07-10 stsp goto done;
1451 d68a0a7d 2018-07-10 stsp if (lineno == -1)
1452 d68a0a7d 2018-07-10 stsp goto done; /* no change in this commit */
1454 84451b3e 2018-07-10 stsp line = &a->lines[lineno - 1];
1455 d68a0a7d 2018-07-10 stsp if (line->annotated)
1456 d68a0a7d 2018-07-10 stsp goto done;
1458 84451b3e 2018-07-10 stsp line->id = got_object_id_dup(id);
1459 84451b3e 2018-07-10 stsp if (line->id == NULL) {
1460 84451b3e 2018-07-10 stsp err = got_error_from_errno();
1461 84451b3e 2018-07-10 stsp goto done;
1463 84451b3e 2018-07-10 stsp line->annotated = 1;
1465 f7d12f7e 2018-08-01 stsp err = draw_blame(a->view, a->commit_id, a->f, a->path,
1466 245d91c1 2018-07-12 stsp a->lines, a->nlines, 0, *a->selected_line, a->first_displayed_line,
1467 f7d12f7e 2018-08-01 stsp a->last_displayed_line, &eof, a->view->nlines);
1469 84451b3e 2018-07-10 stsp if (pthread_mutex_unlock(a->mutex) != 0)
1470 84451b3e 2018-07-10 stsp return got_error_from_errno();
1471 84451b3e 2018-07-10 stsp return err;
1474 84451b3e 2018-07-10 stsp struct tog_blame_thread_args {
1475 84451b3e 2018-07-10 stsp const char *path;
1476 84451b3e 2018-07-10 stsp struct got_repository *repo;
1477 245d91c1 2018-07-12 stsp struct tog_blame_cb_args *cb_args;
1478 18430de3 2018-07-10 stsp int *complete;
1481 84451b3e 2018-07-10 stsp static void *
1482 84451b3e 2018-07-10 stsp blame_thread(void *arg)
1484 18430de3 2018-07-10 stsp const struct got_error *err;
1485 18430de3 2018-07-10 stsp struct tog_blame_thread_args *ta = arg;
1486 245d91c1 2018-07-12 stsp struct tog_blame_cb_args *a = ta->cb_args;
1489 ab089a2a 2018-07-12 stsp err = got_blame_incremental(ta->path, a->commit_id, ta->repo,
1490 245d91c1 2018-07-12 stsp blame_cb, ta->cb_args);
1492 18430de3 2018-07-10 stsp if (pthread_mutex_lock(a->mutex) != 0)
1493 18430de3 2018-07-10 stsp return (void *)got_error_from_errno();
1495 c9beca56 2018-07-22 stsp got_repo_close(ta->repo);
1496 c9beca56 2018-07-22 stsp ta->repo = NULL;
1497 c9beca56 2018-07-22 stsp *ta->complete = 1;
1499 f7d12f7e 2018-08-01 stsp err = draw_blame(a->view, a->commit_id, a->f, a->path,
1500 f7d12f7e 2018-08-01 stsp a->lines, a->nlines, 1, *a->selected_line,
1501 c9beca56 2018-07-22 stsp a->first_displayed_line, a->last_displayed_line, &eof,
1502 f7d12f7e 2018-08-01 stsp a->view->nlines);
1504 18430de3 2018-07-10 stsp if (pthread_mutex_unlock(a->mutex) != 0 && err == NULL)
1505 18430de3 2018-07-10 stsp err = got_error_from_errno();
1507 18430de3 2018-07-10 stsp return (void *)err;
1510 245d91c1 2018-07-12 stsp static struct got_object_id *
1511 245d91c1 2018-07-12 stsp get_selected_commit_id(struct tog_blame_line *lines,
1512 245d91c1 2018-07-12 stsp int first_displayed_line, int selected_line)
1514 245d91c1 2018-07-12 stsp struct tog_blame_line *line;
1516 245d91c1 2018-07-12 stsp line = &lines[first_displayed_line - 1 + selected_line - 1];
1517 245d91c1 2018-07-12 stsp if (!line->annotated)
1518 245d91c1 2018-07-12 stsp return NULL;
1520 245d91c1 2018-07-12 stsp return line->id;
1523 84451b3e 2018-07-10 stsp static const struct got_error *
1524 245d91c1 2018-07-12 stsp open_selected_commit(struct got_object **pobj, struct got_object **obj,
1525 b880a918 2018-07-10 stsp struct tog_blame_line *lines, int first_displayed_line,
1526 b880a918 2018-07-10 stsp int selected_line, struct got_repository *repo)
1528 b880a918 2018-07-10 stsp const struct got_error *err = NULL;
1529 b880a918 2018-07-10 stsp struct got_commit_object *commit = NULL;
1530 245d91c1 2018-07-12 stsp struct got_object_id *selected_id;
1531 b880a918 2018-07-10 stsp struct got_object_qid *pid;
1533 b880a918 2018-07-10 stsp *pobj = NULL;
1534 b880a918 2018-07-10 stsp *obj = NULL;
1536 245d91c1 2018-07-12 stsp selected_id = get_selected_commit_id(lines,
1537 245d91c1 2018-07-12 stsp first_displayed_line, selected_line);
1538 245d91c1 2018-07-12 stsp if (selected_id == NULL)
1539 b880a918 2018-07-10 stsp return NULL;
1541 245d91c1 2018-07-12 stsp err = got_object_open(obj, repo, selected_id);
1543 b880a918 2018-07-10 stsp goto done;
1545 b880a918 2018-07-10 stsp err = got_object_commit_open(&commit, repo, *obj);
1547 b880a918 2018-07-10 stsp goto done;
1549 b880a918 2018-07-10 stsp pid = SIMPLEQ_FIRST(&commit->parent_ids);
1550 b880a918 2018-07-10 stsp if (pid) {
1551 b880a918 2018-07-10 stsp err = got_object_open(pobj, repo, pid->id);
1553 b880a918 2018-07-10 stsp goto done;
1556 b880a918 2018-07-10 stsp if (commit)
1557 b880a918 2018-07-10 stsp got_object_commit_close(commit);
1558 b880a918 2018-07-10 stsp return err;
1561 245d91c1 2018-07-12 stsp struct tog_blame {
1563 245d91c1 2018-07-12 stsp size_t filesize;
1564 245d91c1 2018-07-12 stsp struct tog_blame_line *lines;
1565 245d91c1 2018-07-12 stsp size_t nlines;
1566 245d91c1 2018-07-12 stsp pthread_t thread;
1567 245d91c1 2018-07-12 stsp struct tog_blame_thread_args thread_args;
1568 245d91c1 2018-07-12 stsp struct tog_blame_cb_args cb_args;
1569 245d91c1 2018-07-12 stsp const char *path;
1572 b880a918 2018-07-10 stsp static const struct got_error *
1573 245d91c1 2018-07-12 stsp stop_blame(struct tog_blame *blame)
1575 a70480e0 2018-06-23 stsp const struct got_error *err = NULL;
1578 245d91c1 2018-07-12 stsp if (blame->thread) {
1579 245d91c1 2018-07-12 stsp if (pthread_join(blame->thread, (void **)&err) != 0)
1580 245d91c1 2018-07-12 stsp err = got_error_from_errno();
1581 245d91c1 2018-07-12 stsp if (err && err->code == GOT_ERR_ITER_COMPLETED)
1582 245d91c1 2018-07-12 stsp err = NULL;
1583 245d91c1 2018-07-12 stsp blame->thread = NULL;
1585 245d91c1 2018-07-12 stsp if (blame->thread_args.repo) {
1586 245d91c1 2018-07-12 stsp got_repo_close(blame->thread_args.repo);
1587 245d91c1 2018-07-12 stsp blame->thread_args.repo = NULL;
1589 245d91c1 2018-07-12 stsp if (blame->f) {
1590 245d91c1 2018-07-12 stsp fclose(blame->f);
1591 245d91c1 2018-07-12 stsp blame->f = NULL;
1593 245d91c1 2018-07-12 stsp for (i = 0; i < blame->nlines; i++)
1594 245d91c1 2018-07-12 stsp free(blame->lines[i].id);
1595 245d91c1 2018-07-12 stsp free(blame->lines);
1596 245d91c1 2018-07-12 stsp blame->lines = NULL;
1597 75c32340 2018-07-23 stsp free(blame->cb_args.commit_id);
1598 75c32340 2018-07-23 stsp blame->cb_args.commit_id = NULL;
1600 245d91c1 2018-07-12 stsp return err;
1603 245d91c1 2018-07-12 stsp static const struct got_error *
1604 7cc84d77 2018-08-01 stsp run_blame(struct tog_blame *blame, pthread_mutex_t *mutex,
1605 7cc84d77 2018-08-01 stsp struct tog_view *view, int *blame_complete,
1606 245d91c1 2018-07-12 stsp int *first_displayed_line, int *last_displayed_line,
1607 245d91c1 2018-07-12 stsp int *selected_line, int *done, const char *path,
1608 245d91c1 2018-07-12 stsp struct got_object_id *commit_id,
1609 245d91c1 2018-07-12 stsp struct got_repository *repo)
1611 245d91c1 2018-07-12 stsp const struct got_error *err = NULL;
1612 84451b3e 2018-07-10 stsp struct got_blob_object *blob = NULL;
1613 245d91c1 2018-07-12 stsp struct got_repository *thread_repo = NULL;
1614 245d91c1 2018-07-12 stsp struct got_object *obj;
1616 84451b3e 2018-07-10 stsp err = got_object_open_by_path(&obj, repo, commit_id, path);
1618 84451b3e 2018-07-10 stsp goto done;
1619 84451b3e 2018-07-10 stsp if (got_object_get_type(obj) != GOT_OBJ_TYPE_BLOB) {
1620 84451b3e 2018-07-10 stsp err = got_error(GOT_ERR_OBJ_TYPE);
1621 84451b3e 2018-07-10 stsp goto done;
1624 84451b3e 2018-07-10 stsp err = got_object_blob_open(&blob, repo, obj, 8192);
1626 a70480e0 2018-06-23 stsp goto done;
1627 245d91c1 2018-07-12 stsp blame->f = got_opentemp();
1628 245d91c1 2018-07-12 stsp if (blame->f == NULL) {
1629 84451b3e 2018-07-10 stsp err = got_error_from_errno();
1630 84451b3e 2018-07-10 stsp goto done;
1632 245d91c1 2018-07-12 stsp err = got_object_blob_dump_to_file(&blame->filesize, &blame->nlines,
1633 245d91c1 2018-07-12 stsp blame->f, blob);
1635 84451b3e 2018-07-10 stsp goto done;
1637 245d91c1 2018-07-12 stsp blame->lines = calloc(blame->nlines, sizeof(*blame->lines));
1638 245d91c1 2018-07-12 stsp if (blame->lines == NULL) {
1639 84451b3e 2018-07-10 stsp err = got_error_from_errno();
1640 84451b3e 2018-07-10 stsp goto done;
1643 245d91c1 2018-07-12 stsp err = got_repo_open(&thread_repo, got_repo_get_path(repo));
1645 bd24772e 2018-07-11 stsp goto done;
1647 7cc84d77 2018-08-01 stsp blame->cb_args.view = view;
1648 245d91c1 2018-07-12 stsp blame->cb_args.lines = blame->lines;
1649 245d91c1 2018-07-12 stsp blame->cb_args.nlines = blame->nlines;
1650 245d91c1 2018-07-12 stsp blame->cb_args.mutex = mutex;
1651 245d91c1 2018-07-12 stsp blame->cb_args.commit_id = got_object_id_dup(commit_id);
1652 245d91c1 2018-07-12 stsp if (blame->cb_args.commit_id == NULL) {
1653 245d91c1 2018-07-12 stsp err = got_error_from_errno();
1654 245d91c1 2018-07-12 stsp goto done;
1656 245d91c1 2018-07-12 stsp blame->cb_args.f = blame->f;
1657 245d91c1 2018-07-12 stsp blame->cb_args.path = path;
1658 245d91c1 2018-07-12 stsp blame->cb_args.first_displayed_line = first_displayed_line;
1659 245d91c1 2018-07-12 stsp blame->cb_args.selected_line = selected_line;
1660 245d91c1 2018-07-12 stsp blame->cb_args.last_displayed_line = last_displayed_line;
1661 245d91c1 2018-07-12 stsp blame->cb_args.quit = done;
1663 245d91c1 2018-07-12 stsp blame->thread_args.path = path;
1664 245d91c1 2018-07-12 stsp blame->thread_args.repo = thread_repo;
1665 245d91c1 2018-07-12 stsp blame->thread_args.cb_args = &blame->cb_args;
1666 245d91c1 2018-07-12 stsp blame->thread_args.complete = blame_complete;
1667 245d91c1 2018-07-12 stsp *blame_complete = 0;
1669 245d91c1 2018-07-12 stsp if (pthread_create(&blame->thread, NULL, blame_thread,
1670 245d91c1 2018-07-12 stsp &blame->thread_args) != 0) {
1671 245d91c1 2018-07-12 stsp err = got_error_from_errno();
1672 245d91c1 2018-07-12 stsp goto done;
1677 245d91c1 2018-07-12 stsp got_object_blob_close(blob);
1679 245d91c1 2018-07-12 stsp got_object_close(obj);
1681 245d91c1 2018-07-12 stsp stop_blame(blame);
1682 245d91c1 2018-07-12 stsp return err;
1685 245d91c1 2018-07-12 stsp static const struct got_error *
1686 e1cd8fed 2018-08-01 stsp show_blame_view(struct tog_view *view, const char *path,
1687 e1cd8fed 2018-08-01 stsp struct got_object_id *commit_id, struct got_repository *repo)
1689 245d91c1 2018-07-12 stsp const struct got_error *err = NULL, *thread_err = NULL;
1690 f7d12f7e 2018-08-01 stsp int ch, done = 0, first_displayed_line = 1, last_displayed_line;
1691 245d91c1 2018-07-12 stsp int selected_line = first_displayed_line;
1692 245d91c1 2018-07-12 stsp int eof, blame_complete = 0;
1693 245d91c1 2018-07-12 stsp struct got_object *obj = NULL, *pobj = NULL;
1694 245d91c1 2018-07-12 stsp pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
1695 245d91c1 2018-07-12 stsp struct tog_blame blame;
1696 dbc6a6b6 2018-07-12 stsp struct got_object_id_queue blamed_commits;
1697 dbc6a6b6 2018-07-12 stsp struct got_object_qid *blamed_commit = NULL;
1698 e1cd8fed 2018-08-01 stsp struct tog_view *diff_view;
1700 dbc6a6b6 2018-07-12 stsp SIMPLEQ_INIT(&blamed_commits);
1702 245d91c1 2018-07-12 stsp if (pthread_mutex_init(&mutex, NULL) != 0) {
1703 245d91c1 2018-07-12 stsp err = got_error_from_errno();
1704 245d91c1 2018-07-12 stsp goto done;
1707 dbc6a6b6 2018-07-12 stsp err = got_object_qid_alloc(&blamed_commit, commit_id);
1709 245d91c1 2018-07-12 stsp goto done;
1710 dbc6a6b6 2018-07-12 stsp SIMPLEQ_INSERT_HEAD(&blamed_commits, blamed_commit, entry);
1712 cdf1ee82 2018-08-01 stsp show_view(view);
1713 f7d12f7e 2018-08-01 stsp last_displayed_line = view->nlines;
1715 245d91c1 2018-07-12 stsp memset(&blame, 0, sizeof(blame));
1716 7cc84d77 2018-08-01 stsp err = run_blame(&blame, &mutex, view, &blame_complete,
1717 245d91c1 2018-07-12 stsp &first_displayed_line, &last_displayed_line,
1718 dbc6a6b6 2018-07-12 stsp &selected_line, &done, path, blamed_commit->id, repo);
1720 245d91c1 2018-07-12 stsp return err;
1722 a70480e0 2018-06-23 stsp while (!done) {
1723 84451b3e 2018-07-10 stsp if (pthread_mutex_lock(&mutex) != 0) {
1724 84451b3e 2018-07-10 stsp err = got_error_from_errno();
1725 84451b3e 2018-07-10 stsp goto done;
1727 f7d12f7e 2018-08-01 stsp err = draw_blame(view, blamed_commit->id, blame.f, path,
1728 f7d12f7e 2018-08-01 stsp blame.lines, blame.nlines, blame_complete, selected_line,
1729 f7d12f7e 2018-08-01 stsp &first_displayed_line, &last_displayed_line, &eof,
1730 f7d12f7e 2018-08-01 stsp view->nlines);
1731 84451b3e 2018-07-10 stsp if (pthread_mutex_unlock(&mutex) != 0) {
1732 84451b3e 2018-07-10 stsp err = got_error_from_errno();
1733 84451b3e 2018-07-10 stsp goto done;
1737 a70480e0 2018-06-23 stsp nodelay(stdscr, FALSE);
1738 7cc84d77 2018-08-01 stsp ch = wgetch(view->window);
1739 a70480e0 2018-06-23 stsp nodelay(stdscr, TRUE);
1740 84451b3e 2018-07-10 stsp if (pthread_mutex_lock(&mutex) != 0) {
1741 84451b3e 2018-07-10 stsp err = got_error_from_errno();
1742 84451b3e 2018-07-10 stsp goto done;
1744 a70480e0 2018-06-23 stsp switch (ch) {
1749 a70480e0 2018-06-23 stsp case KEY_UP:
1750 b700b5d6 2018-07-10 stsp if (selected_line > 1)
1751 b700b5d6 2018-07-10 stsp selected_line--;
1752 b700b5d6 2018-07-10 stsp else if (selected_line == 1 &&
1753 b700b5d6 2018-07-10 stsp first_displayed_line > 1)
1754 a70480e0 2018-06-23 stsp first_displayed_line--;
1756 a70480e0 2018-06-23 stsp case KEY_PPAGE:
1757 38f94530 2018-07-12 stsp case KEY_BACKSPACE:
1758 b700b5d6 2018-07-10 stsp if (first_displayed_line == 1) {
1759 b700b5d6 2018-07-10 stsp selected_line = 1;
1762 f7d12f7e 2018-08-01 stsp if (first_displayed_line > view->nlines - 2)
1763 f7d12f7e 2018-08-01 stsp first_displayed_line -=
1764 f7d12f7e 2018-08-01 stsp (view->nlines - 2);
1766 b700b5d6 2018-07-10 stsp first_displayed_line = 1;
1769 a70480e0 2018-06-23 stsp case KEY_DOWN:
1770 f7d12f7e 2018-08-01 stsp if (selected_line < view->nlines - 2 &&
1771 a026b947 2018-07-12 stsp first_displayed_line + selected_line <=
1772 a026b947 2018-07-12 stsp blame.nlines)
1773 b700b5d6 2018-07-10 stsp selected_line++;
1774 245d91c1 2018-07-12 stsp else if (last_displayed_line < blame.nlines)
1775 b700b5d6 2018-07-10 stsp first_displayed_line++;
1778 7a2921f9 2018-07-12 stsp case 'p': {
1779 245d91c1 2018-07-12 stsp struct got_object_id *id;
1780 245d91c1 2018-07-12 stsp id = get_selected_commit_id(blame.lines,
1781 245d91c1 2018-07-12 stsp first_displayed_line, selected_line);
1782 245d91c1 2018-07-12 stsp if (id == NULL || got_object_id_cmp(id,
1783 dbc6a6b6 2018-07-12 stsp blamed_commit->id) == 0)
1785 245d91c1 2018-07-12 stsp err = open_selected_commit(&pobj, &obj,
1786 245d91c1 2018-07-12 stsp blame.lines, first_displayed_line,
1787 245d91c1 2018-07-12 stsp selected_line, repo);
1790 245d91c1 2018-07-12 stsp if (pobj == NULL && obj == NULL)
1792 7a2921f9 2018-07-12 stsp if (ch == 'p' && pobj == NULL)
1795 245d91c1 2018-07-12 stsp if (pthread_mutex_unlock(&mutex) != 0) {
1796 245d91c1 2018-07-12 stsp err = got_error_from_errno();
1797 ad6a6c43 2018-07-10 stsp goto done;
1799 245d91c1 2018-07-12 stsp thread_err = stop_blame(&blame);
1801 245d91c1 2018-07-12 stsp if (pthread_mutex_lock(&mutex) != 0) {
1802 245d91c1 2018-07-12 stsp err = got_error_from_errno();
1803 245d91c1 2018-07-12 stsp goto done;
1805 245d91c1 2018-07-12 stsp if (thread_err)
1807 7a2921f9 2018-07-12 stsp id = got_object_get_id(ch == 'b' ? obj : pobj);
1808 1960a6f9 2018-07-12 stsp got_object_close(obj);
1809 1960a6f9 2018-07-12 stsp obj = NULL;
1810 1960a6f9 2018-07-12 stsp if (pobj) {
1811 1960a6f9 2018-07-12 stsp got_object_close(pobj);
1812 14437fb1 2018-07-13 stsp pobj = NULL;
1814 dbc6a6b6 2018-07-12 stsp if (id == NULL) {
1815 245d91c1 2018-07-12 stsp err = got_error_from_errno();
1818 dbc6a6b6 2018-07-12 stsp err = got_object_qid_alloc(&blamed_commit, id);
1821 dbc6a6b6 2018-07-12 stsp goto done;
1822 dbc6a6b6 2018-07-12 stsp SIMPLEQ_INSERT_HEAD(&blamed_commits,
1823 dbc6a6b6 2018-07-12 stsp blamed_commit, entry);
1824 7cc84d77 2018-08-01 stsp err = run_blame(&blame, &mutex, view,
1825 245d91c1 2018-07-12 stsp &blame_complete, &first_displayed_line,
1826 245d91c1 2018-07-12 stsp &last_displayed_line, &selected_line,
1827 dbc6a6b6 2018-07-12 stsp &done, path, blamed_commit->id, repo);
1832 dbc6a6b6 2018-07-12 stsp case 'B': {
1833 dbc6a6b6 2018-07-12 stsp struct got_object_qid *first;
1834 dbc6a6b6 2018-07-12 stsp first = SIMPLEQ_FIRST(&blamed_commits);
1835 dbc6a6b6 2018-07-12 stsp if (!got_object_id_cmp(first->id, commit_id))
1838 dbc6a6b6 2018-07-12 stsp if (pthread_mutex_unlock(&mutex) != 0) {
1839 dbc6a6b6 2018-07-12 stsp err = got_error_from_errno();
1840 dbc6a6b6 2018-07-12 stsp goto done;
1842 dbc6a6b6 2018-07-12 stsp thread_err = stop_blame(&blame);
1844 dbc6a6b6 2018-07-12 stsp if (pthread_mutex_lock(&mutex) != 0) {
1845 dbc6a6b6 2018-07-12 stsp err = got_error_from_errno();
1846 dbc6a6b6 2018-07-12 stsp goto done;
1848 dbc6a6b6 2018-07-12 stsp if (thread_err)
1850 dbc6a6b6 2018-07-12 stsp SIMPLEQ_REMOVE_HEAD(&blamed_commits, entry);
1851 dbc6a6b6 2018-07-12 stsp got_object_qid_free(blamed_commit);
1852 dbc6a6b6 2018-07-12 stsp blamed_commit = SIMPLEQ_FIRST(&blamed_commits);
1853 7cc84d77 2018-08-01 stsp err = run_blame(&blame, &mutex, view,
1854 dbc6a6b6 2018-07-12 stsp &blame_complete, &first_displayed_line,
1855 dbc6a6b6 2018-07-12 stsp &last_displayed_line, &selected_line,
1856 dbc6a6b6 2018-07-12 stsp &done, path, blamed_commit->id, repo);
1861 245d91c1 2018-07-12 stsp case KEY_ENTER:
1862 245d91c1 2018-07-12 stsp case '\r':
1863 245d91c1 2018-07-12 stsp err = open_selected_commit(&pobj, &obj,
1864 245d91c1 2018-07-12 stsp blame.lines, first_displayed_line,
1865 245d91c1 2018-07-12 stsp selected_line, repo);
1868 b880a918 2018-07-10 stsp if (pobj == NULL && obj == NULL)
1870 6d0fee91 2018-08-01 stsp diff_view = open_view(0, 0, 0, 0, view);
1871 ea5e7bb5 2018-08-01 stsp if (diff_view == NULL) {
1872 ea5e7bb5 2018-08-01 stsp err = got_error_from_errno();
1875 ea5e7bb5 2018-08-01 stsp err = show_diff_view(diff_view, pobj, obj, repo);
1876 ea5e7bb5 2018-08-01 stsp close_view(diff_view);
1877 cdf1ee82 2018-08-01 stsp show_view(view);
1878 b880a918 2018-07-10 stsp if (pobj) {
1879 b880a918 2018-07-10 stsp got_object_close(pobj);
1880 b880a918 2018-07-10 stsp pobj = NULL;
1882 b880a918 2018-07-10 stsp got_object_close(obj);
1883 b880a918 2018-07-10 stsp obj = NULL;
1887 a70480e0 2018-06-23 stsp case KEY_NPAGE:
1889 245d91c1 2018-07-12 stsp if (last_displayed_line >= blame.nlines &&
1890 f7d12f7e 2018-08-01 stsp selected_line < view->nlines - 2) {
1891 d2dfcfbf 2018-07-12 stsp selected_line = MIN(blame.nlines,
1892 f7d12f7e 2018-08-01 stsp view->nlines - 2);
1895 f7d12f7e 2018-08-01 stsp if (last_displayed_line + view->nlines - 2 <=
1896 245d91c1 2018-07-12 stsp blame.nlines)
1897 f7d12f7e 2018-08-01 stsp first_displayed_line +=
1898 f7d12f7e 2018-08-01 stsp view->nlines - 2;
1900 b700b5d6 2018-07-10 stsp first_displayed_line =
1901 f7d12f7e 2018-08-01 stsp blame.nlines - (view->nlines - 3);
1903 7e5c1fe1 2018-08-01 stsp case KEY_RESIZE:
1904 a41d2007 2018-08-01 stsp err = view_resize(view);
1907 7e5c1fe1 2018-08-01 stsp if (selected_line > view->nlines - 2) {
1908 7e5c1fe1 2018-08-01 stsp selected_line = MIN(blame.nlines,
1909 7e5c1fe1 2018-08-01 stsp view->nlines - 2);
1915 245d91c1 2018-07-12 stsp if (pthread_mutex_unlock(&mutex) != 0)
1916 84451b3e 2018-07-10 stsp err = got_error_from_errno();
1917 245d91c1 2018-07-12 stsp if (err || thread_err)
1922 b880a918 2018-07-10 stsp got_object_close(pobj);
1923 c9beca56 2018-07-22 stsp if (blame.thread)
1924 245d91c1 2018-07-12 stsp thread_err = stop_blame(&blame);
1925 dbc6a6b6 2018-07-12 stsp while (!SIMPLEQ_EMPTY(&blamed_commits)) {
1926 dbc6a6b6 2018-07-12 stsp blamed_commit = SIMPLEQ_FIRST(&blamed_commits);
1927 dbc6a6b6 2018-07-12 stsp SIMPLEQ_REMOVE_HEAD(&blamed_commits, entry);
1928 dbc6a6b6 2018-07-12 stsp got_object_qid_free(blamed_commit);
1930 245d91c1 2018-07-12 stsp return thread_err ? thread_err : err;
1933 a70480e0 2018-06-23 stsp static const struct got_error *
1934 9f7d7167 2018-04-29 stsp cmd_blame(int argc, char *argv[])
1936 a70480e0 2018-06-23 stsp const struct got_error *error;
1937 a70480e0 2018-06-23 stsp struct got_repository *repo = NULL;
1938 a70480e0 2018-06-23 stsp char *repo_path = NULL;
1939 a70480e0 2018-06-23 stsp char *path = NULL;
1940 a70480e0 2018-06-23 stsp struct got_object_id *commit_id = NULL;
1941 a70480e0 2018-06-23 stsp char *commit_id_str = NULL;
1943 e1cd8fed 2018-08-01 stsp struct tog_view *view;
1945 a70480e0 2018-06-23 stsp #ifndef PROFILE
1946 a70480e0 2018-06-23 stsp if (pledge("stdio rpath wpath cpath flock proc tty", NULL) == -1)
1947 a70480e0 2018-06-23 stsp err(1, "pledge");
1950 a70480e0 2018-06-23 stsp while ((ch = getopt(argc, argv, "c:")) != -1) {
1951 a70480e0 2018-06-23 stsp switch (ch) {
1953 a70480e0 2018-06-23 stsp commit_id_str = optarg;
1957 a70480e0 2018-06-23 stsp /* NOTREACHED */
1961 a70480e0 2018-06-23 stsp argc -= optind;
1962 a70480e0 2018-06-23 stsp argv += optind;
1964 a70480e0 2018-06-23 stsp if (argc == 0) {
1965 a70480e0 2018-06-23 stsp usage_blame();
1966 a70480e0 2018-06-23 stsp } else if (argc == 1) {
1967 a70480e0 2018-06-23 stsp repo_path = getcwd(NULL, 0);
1968 a70480e0 2018-06-23 stsp if (repo_path == NULL)
1969 a70480e0 2018-06-23 stsp return got_error_from_errno();
1970 a70480e0 2018-06-23 stsp path = argv[0];
1971 a70480e0 2018-06-23 stsp } else if (argc == 2) {
1972 a70480e0 2018-06-23 stsp repo_path = realpath(argv[0], NULL);
1973 a70480e0 2018-06-23 stsp if (repo_path == NULL)
1974 a70480e0 2018-06-23 stsp return got_error_from_errno();
1975 a70480e0 2018-06-23 stsp path = argv[1];
1977 a70480e0 2018-06-23 stsp usage_blame();
1979 a70480e0 2018-06-23 stsp error = got_repo_open(&repo, repo_path);
1980 a70480e0 2018-06-23 stsp free(repo_path);
1981 a70480e0 2018-06-23 stsp if (error != NULL)
1982 66b4983c 2018-06-23 stsp return error;
1984 a70480e0 2018-06-23 stsp if (commit_id_str == NULL) {
1985 a70480e0 2018-06-23 stsp struct got_reference *head_ref;
1986 a70480e0 2018-06-23 stsp error = got_ref_open(&head_ref, repo, GOT_REF_HEAD);
1987 a70480e0 2018-06-23 stsp if (error != NULL)
1988 66b4983c 2018-06-23 stsp goto done;
1989 a70480e0 2018-06-23 stsp error = got_ref_resolve(&commit_id, repo, head_ref);
1990 a70480e0 2018-06-23 stsp got_ref_close(head_ref);
1992 a70480e0 2018-06-23 stsp struct got_object *obj;
1993 a70480e0 2018-06-23 stsp error = got_object_open_by_id_str(&obj, repo, commit_id_str);
1994 a70480e0 2018-06-23 stsp if (error != NULL)
1995 66b4983c 2018-06-23 stsp goto done;
1996 a70480e0 2018-06-23 stsp commit_id = got_object_get_id(obj);
1997 a19e88aa 2018-06-23 stsp if (commit_id == NULL)
1998 66b4983c 2018-06-23 stsp error = got_error_from_errno();
1999 a19e88aa 2018-06-23 stsp got_object_close(obj);
2001 a19e88aa 2018-06-23 stsp if (error != NULL)
2002 a19e88aa 2018-06-23 stsp goto done;
2004 6d0fee91 2018-08-01 stsp view = open_view(0, 0, 0, 0, NULL);
2005 e1cd8fed 2018-08-01 stsp if (view == NULL) {
2006 e1cd8fed 2018-08-01 stsp error = got_error_from_errno();
2007 e1cd8fed 2018-08-01 stsp goto done;
2009 e1cd8fed 2018-08-01 stsp error = show_blame_view(view, path, commit_id, repo);
2010 e1cd8fed 2018-08-01 stsp close_view(view);
2012 a70480e0 2018-06-23 stsp free(commit_id);
2014 a70480e0 2018-06-23 stsp got_repo_close(repo);
2015 a70480e0 2018-06-23 stsp return error;
2018 ffd1d5e5 2018-06-23 stsp static const struct got_error *
2019 f7d12f7e 2018-08-01 stsp draw_tree_entries(struct tog_view *view,
2020 f7d12f7e 2018-08-01 stsp struct got_tree_entry **first_displayed_entry,
2021 ffd1d5e5 2018-06-23 stsp struct got_tree_entry **last_displayed_entry,
2022 ffd1d5e5 2018-06-23 stsp struct got_tree_entry **selected_entry, int *ndisplayed,
2023 f7d12f7e 2018-08-01 stsp const char *label, int show_ids, const char *parent_path,
2024 ce52c690 2018-06-23 stsp const struct got_tree_entries *entries, int selected, int limit, int isroot)
2026 ffd1d5e5 2018-06-23 stsp const struct got_error *err = NULL;
2027 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *te;
2028 ffd1d5e5 2018-06-23 stsp wchar_t *wline;
2029 ffd1d5e5 2018-06-23 stsp int width, n;
2031 ffd1d5e5 2018-06-23 stsp *ndisplayed = 0;
2033 f7d12f7e 2018-08-01 stsp werase(view->window);
2035 ffd1d5e5 2018-06-23 stsp if (limit == 0)
2036 ffd1d5e5 2018-06-23 stsp return NULL;
2038 f7d12f7e 2018-08-01 stsp err = format_line(&wline, &width, label, view->ncols);
2040 ffd1d5e5 2018-06-23 stsp return err;
2041 f7d12f7e 2018-08-01 stsp waddwstr(view->window, wline);
2042 2550e4c3 2018-07-13 stsp free(wline);
2043 2550e4c3 2018-07-13 stsp wline = NULL;
2044 f7d12f7e 2018-08-01 stsp if (width < view->ncols)
2045 f7d12f7e 2018-08-01 stsp waddch(view->window, '\n');
2046 ffd1d5e5 2018-06-23 stsp if (--limit <= 0)
2047 ffd1d5e5 2018-06-23 stsp return NULL;
2048 f7d12f7e 2018-08-01 stsp err = format_line(&wline, &width, parent_path, view->ncols);
2050 ce52c690 2018-06-23 stsp return err;
2051 f7d12f7e 2018-08-01 stsp waddwstr(view->window, wline);
2052 2550e4c3 2018-07-13 stsp free(wline);
2053 2550e4c3 2018-07-13 stsp wline = NULL;
2054 f7d12f7e 2018-08-01 stsp if (width < view->ncols)
2055 f7d12f7e 2018-08-01 stsp waddch(view->window, '\n');
2056 ffd1d5e5 2018-06-23 stsp if (--limit <= 0)
2057 ffd1d5e5 2018-06-23 stsp return NULL;
2058 f7d12f7e 2018-08-01 stsp waddch(view->window, '\n');
2059 a1eca9bb 2018-06-23 stsp if (--limit <= 0)
2060 a1eca9bb 2018-06-23 stsp return NULL;
2062 ffd1d5e5 2018-06-23 stsp te = SIMPLEQ_FIRST(&entries->head);
2063 ffd1d5e5 2018-06-23 stsp if (*first_displayed_entry == NULL) {
2064 ffd1d5e5 2018-06-23 stsp if (selected == 0) {
2065 f7d12f7e 2018-08-01 stsp wstandout(view->window);
2066 ffd1d5e5 2018-06-23 stsp *selected_entry = NULL;
2068 f7d12f7e 2018-08-01 stsp waddstr(view->window, " ..\n"); /* parent directory */
2069 ffd1d5e5 2018-06-23 stsp if (selected == 0)
2070 f7d12f7e 2018-08-01 stsp wstandend(view->window);
2071 ffd1d5e5 2018-06-23 stsp (*ndisplayed)++;
2072 ffd1d5e5 2018-06-23 stsp if (--limit <= 0)
2073 ffd1d5e5 2018-06-23 stsp return NULL;
2077 ffd1d5e5 2018-06-23 stsp while (te != *first_displayed_entry)
2078 ffd1d5e5 2018-06-23 stsp te = SIMPLEQ_NEXT(te, entry);
2081 ffd1d5e5 2018-06-23 stsp while (te) {
2082 1d13200f 2018-07-12 stsp char *line = NULL, *id_str = NULL;
2084 1d13200f 2018-07-12 stsp if (show_ids) {
2085 1d13200f 2018-07-12 stsp err = got_object_id_str(&id_str, te->id);
2087 1d13200f 2018-07-12 stsp return got_error_from_errno();
2089 1d13200f 2018-07-12 stsp if (asprintf(&line, "%s %s%s", id_str ? id_str : "",
2090 1d13200f 2018-07-12 stsp te->name, S_ISDIR(te->mode) ? "/" : "") == -1) {
2091 1d13200f 2018-07-12 stsp free(id_str);
2092 ffd1d5e5 2018-06-23 stsp return got_error_from_errno();
2094 1d13200f 2018-07-12 stsp free(id_str);
2095 f7d12f7e 2018-08-01 stsp err = format_line(&wline, &width, line, view->ncols);
2096 ffd1d5e5 2018-06-23 stsp if (err) {
2097 ffd1d5e5 2018-06-23 stsp free(line);
2100 ffd1d5e5 2018-06-23 stsp if (n == selected) {
2101 f7d12f7e 2018-08-01 stsp wstandout(view->window);
2102 ffd1d5e5 2018-06-23 stsp *selected_entry = te;
2104 f7d12f7e 2018-08-01 stsp waddwstr(view->window, wline);
2105 f7d12f7e 2018-08-01 stsp if (width < view->ncols)
2106 f7d12f7e 2018-08-01 stsp waddch(view->window, '\n');
2107 ffd1d5e5 2018-06-23 stsp if (n == selected)
2108 f7d12f7e 2018-08-01 stsp wstandend(view->window);
2109 ffd1d5e5 2018-06-23 stsp free(line);
2110 2550e4c3 2018-07-13 stsp free(wline);
2111 2550e4c3 2018-07-13 stsp wline = NULL;
2113 ffd1d5e5 2018-06-23 stsp (*ndisplayed)++;
2114 ffd1d5e5 2018-06-23 stsp *last_displayed_entry = te;
2115 ffd1d5e5 2018-06-23 stsp if (--limit <= 0)
2117 ffd1d5e5 2018-06-23 stsp te = SIMPLEQ_NEXT(te, entry);
2120 ffd1d5e5 2018-06-23 stsp return err;
2123 ffd1d5e5 2018-06-23 stsp static void
2124 ffd1d5e5 2018-06-23 stsp tree_scroll_up(struct got_tree_entry **first_displayed_entry, int maxscroll,
2125 ffd1d5e5 2018-06-23 stsp const struct got_tree_entries *entries, int isroot)
2127 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *te, *prev;
2130 ffd1d5e5 2018-06-23 stsp if (*first_displayed_entry == NULL)
2133 ffd1d5e5 2018-06-23 stsp te = SIMPLEQ_FIRST(&entries->head);
2134 ffd1d5e5 2018-06-23 stsp if (*first_displayed_entry == te) {
2135 ffd1d5e5 2018-06-23 stsp if (!isroot)
2136 ffd1d5e5 2018-06-23 stsp *first_displayed_entry = NULL;
2140 ffd1d5e5 2018-06-23 stsp /* XXX this is stupid... switch to TAILQ? */
2141 ffd1d5e5 2018-06-23 stsp for (i = 0; i < maxscroll; i++) {
2142 ffd1d5e5 2018-06-23 stsp while (te != *first_displayed_entry) {
2143 ffd1d5e5 2018-06-23 stsp prev = te;
2144 ffd1d5e5 2018-06-23 stsp te = SIMPLEQ_NEXT(te, entry);
2146 ffd1d5e5 2018-06-23 stsp *first_displayed_entry = prev;
2147 ffd1d5e5 2018-06-23 stsp te = SIMPLEQ_FIRST(&entries->head);
2149 ffd1d5e5 2018-06-23 stsp if (!isroot && te == SIMPLEQ_FIRST(&entries->head) && i < maxscroll)
2150 ffd1d5e5 2018-06-23 stsp *first_displayed_entry = NULL;
2153 ffd1d5e5 2018-06-23 stsp static void
2154 ffd1d5e5 2018-06-23 stsp tree_scroll_down(struct got_tree_entry **first_displayed_entry, int maxscroll,
2155 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *last_displayed_entry,
2156 ffd1d5e5 2018-06-23 stsp const struct got_tree_entries *entries)
2158 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *next;
2159 ffd1d5e5 2018-06-23 stsp int n = 0;
2161 ffd1d5e5 2018-06-23 stsp if (SIMPLEQ_NEXT(last_displayed_entry, entry) == NULL)
2164 ffd1d5e5 2018-06-23 stsp if (*first_displayed_entry)
2165 ffd1d5e5 2018-06-23 stsp next = SIMPLEQ_NEXT(*first_displayed_entry, entry);
2167 ffd1d5e5 2018-06-23 stsp next = SIMPLEQ_FIRST(&entries->head);
2168 ffd1d5e5 2018-06-23 stsp while (next) {
2169 ffd1d5e5 2018-06-23 stsp *first_displayed_entry = next;
2170 ffd1d5e5 2018-06-23 stsp if (++n >= maxscroll)
2172 ffd1d5e5 2018-06-23 stsp next = SIMPLEQ_NEXT(next, entry);
2176 ffd1d5e5 2018-06-23 stsp struct tog_parent_tree {
2177 d9765a41 2018-06-23 stsp TAILQ_ENTRY(tog_parent_tree) entry;
2178 ffd1d5e5 2018-06-23 stsp struct got_tree_object *tree;
2179 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *first_displayed_entry;
2180 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *selected_entry;
2181 ffd1d5e5 2018-06-23 stsp int selected;
2184 d9765a41 2018-06-23 stsp TAILQ_HEAD(tog_parent_trees, tog_parent_tree);
2186 ffd1d5e5 2018-06-23 stsp static const struct got_error *
2187 ce52c690 2018-06-23 stsp tree_entry_path(char **path, struct tog_parent_trees *parents,
2188 ce52c690 2018-06-23 stsp struct got_tree_entry *te)
2190 cb2ebc8a 2018-06-23 stsp const struct got_error *err = NULL;
2191 ffd1d5e5 2018-06-23 stsp struct tog_parent_tree *pt;
2192 ffd1d5e5 2018-06-23 stsp size_t len = 2; /* for leading slash and NUL */
2194 d9765a41 2018-06-23 stsp TAILQ_FOREACH(pt, parents, entry)
2195 ffd1d5e5 2018-06-23 stsp len += strlen(pt->selected_entry->name) + 1 /* slash */;
2197 ce52c690 2018-06-23 stsp len += strlen(te->name);
2199 ce52c690 2018-06-23 stsp *path = calloc(1, len);
2200 ffd1d5e5 2018-06-23 stsp if (path == NULL)
2201 ffd1d5e5 2018-06-23 stsp return got_error_from_errno();
2203 ce52c690 2018-06-23 stsp (*path)[0] = '/';
2204 d9765a41 2018-06-23 stsp pt = TAILQ_LAST(parents, tog_parent_trees);
2205 d9765a41 2018-06-23 stsp while (pt) {
2206 ce52c690 2018-06-23 stsp if (strlcat(*path, pt->selected_entry->name, len) >= len) {
2207 cb2ebc8a 2018-06-23 stsp err = got_error(GOT_ERR_NO_SPACE);
2208 cb2ebc8a 2018-06-23 stsp goto done;
2210 ce52c690 2018-06-23 stsp if (strlcat(*path, "/", len) >= len) {
2211 cb2ebc8a 2018-06-23 stsp err = got_error(GOT_ERR_NO_SPACE);
2212 cb2ebc8a 2018-06-23 stsp goto done;
2214 d9765a41 2018-06-23 stsp pt = TAILQ_PREV(pt, tog_parent_trees, entry);
2217 ce52c690 2018-06-23 stsp if (strlcat(*path, te->name, len) >= len) {
2218 ce52c690 2018-06-23 stsp err = got_error(GOT_ERR_NO_SPACE);
2219 ce52c690 2018-06-23 stsp goto done;
2223 ce52c690 2018-06-23 stsp if (err) {
2224 ce52c690 2018-06-23 stsp free(*path);
2225 ce52c690 2018-06-23 stsp *path = NULL;
2227 ce52c690 2018-06-23 stsp return err;
2230 ce52c690 2018-06-23 stsp static const struct got_error *
2231 cdf1ee82 2018-08-01 stsp blame_tree_entry(struct tog_view *parent_view, struct got_tree_entry *te,
2232 e1cd8fed 2018-08-01 stsp struct tog_parent_trees *parents, struct got_object_id *commit_id,
2233 e1cd8fed 2018-08-01 stsp struct got_repository *repo)
2235 ce52c690 2018-06-23 stsp const struct got_error *err = NULL;
2236 ce52c690 2018-06-23 stsp char *path;
2237 cdf1ee82 2018-08-01 stsp struct tog_view *view;
2239 ce52c690 2018-06-23 stsp err = tree_entry_path(&path, parents, te);
2241 ce52c690 2018-06-23 stsp return err;
2243 cdf1ee82 2018-08-01 stsp view = open_view(0, 0, 0, 0, parent_view);
2244 cdf1ee82 2018-08-01 stsp if (view) {
2245 cdf1ee82 2018-08-01 stsp err = show_blame_view(view, path, commit_id, repo);
2246 cdf1ee82 2018-08-01 stsp close_view(view);
2248 cdf1ee82 2018-08-01 stsp err = got_error_from_errno();
2250 cdf1ee82 2018-08-01 stsp show_view(parent_view);
2251 69efd4c4 2018-07-18 stsp free(path);
2252 69efd4c4 2018-07-18 stsp return err;
2255 69efd4c4 2018-07-18 stsp static const struct got_error *
2256 04cc582a 2018-08-01 stsp log_tree_entry(struct tog_view *view, struct got_tree_entry *te,
2257 04cc582a 2018-08-01 stsp struct tog_parent_trees *parents, struct got_object_id *commit_id,
2258 04cc582a 2018-08-01 stsp struct got_repository *repo)
2260 69efd4c4 2018-07-18 stsp const struct got_error *err = NULL;
2261 69efd4c4 2018-07-18 stsp char *path;
2263 69efd4c4 2018-07-18 stsp err = tree_entry_path(&path, parents, te);
2265 69efd4c4 2018-07-18 stsp return err;
2267 04cc582a 2018-08-01 stsp err = show_log_view(view, commit_id, repo, path);
2268 cb2ebc8a 2018-06-23 stsp free(path);
2269 cb2ebc8a 2018-06-23 stsp return err;
2272 ffd1d5e5 2018-06-23 stsp static const struct got_error *
2273 5221c383 2018-08-01 stsp show_tree_view(struct tog_view *view, struct got_tree_object *root,
2274 5221c383 2018-08-01 stsp struct got_object_id *commit_id, struct got_repository *repo)
2276 ffd1d5e5 2018-06-23 stsp const struct got_error *err = NULL;
2277 1d13200f 2018-07-12 stsp int ch, done = 0, selected = 0, show_ids = 0;
2278 ffd1d5e5 2018-06-23 stsp struct got_tree_object *tree = root;
2279 ffd1d5e5 2018-06-23 stsp const struct got_tree_entries *entries;
2280 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *first_displayed_entry = NULL;
2281 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *last_displayed_entry = NULL;
2282 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *selected_entry = NULL;
2283 ffd1d5e5 2018-06-23 stsp char *commit_id_str = NULL, *tree_label = NULL;
2284 ffd1d5e5 2018-06-23 stsp int nentries, ndisplayed;
2285 ffd1d5e5 2018-06-23 stsp struct tog_parent_trees parents;
2287 d9765a41 2018-06-23 stsp TAILQ_INIT(&parents);
2289 ffd1d5e5 2018-06-23 stsp err = got_object_id_str(&commit_id_str, commit_id);
2290 ffd1d5e5 2018-06-23 stsp if (err != NULL)
2291 ffd1d5e5 2018-06-23 stsp goto done;
2293 decd3bd1 2018-07-12 stsp if (asprintf(&tree_label, "commit: %s", commit_id_str) == -1) {
2294 ffd1d5e5 2018-06-23 stsp err = got_error_from_errno();
2295 ffd1d5e5 2018-06-23 stsp goto done;
2298 cdf1ee82 2018-08-01 stsp show_view(view);
2300 ffd1d5e5 2018-06-23 stsp entries = got_object_tree_get_entries(root);
2301 ffd1d5e5 2018-06-23 stsp first_displayed_entry = SIMPLEQ_FIRST(&entries->head);
2302 ffd1d5e5 2018-06-23 stsp while (!done) {
2303 ce52c690 2018-06-23 stsp char *parent_path;
2304 ffd1d5e5 2018-06-23 stsp entries = got_object_tree_get_entries(tree);
2305 ffd1d5e5 2018-06-23 stsp nentries = entries->nentries;
2306 ffd1d5e5 2018-06-23 stsp if (tree != root)
2307 ffd1d5e5 2018-06-23 stsp nentries++; /* '..' directory */
2309 ce52c690 2018-06-23 stsp err = tree_entry_path(&parent_path, &parents, NULL);
2311 ce52c690 2018-06-23 stsp goto done;
2313 f7d12f7e 2018-08-01 stsp err = draw_tree_entries(view, &first_displayed_entry,
2314 ffd1d5e5 2018-06-23 stsp &last_displayed_entry, &selected_entry, &ndisplayed,
2315 f7d12f7e 2018-08-01 stsp tree_label, show_ids, parent_path, entries, selected,
2316 f7d12f7e 2018-08-01 stsp view->nlines, tree == root);
2317 ce52c690 2018-06-23 stsp free(parent_path);
2321 ffd1d5e5 2018-06-23 stsp nodelay(stdscr, FALSE);
2322 cc3c9aac 2018-08-01 stsp ch = wgetch(view->window);
2323 ffd1d5e5 2018-06-23 stsp nodelay(stdscr, TRUE);
2324 ffd1d5e5 2018-06-23 stsp switch (ch) {
2329 1d13200f 2018-07-12 stsp show_ids = !show_ids;
2332 69efd4c4 2018-07-18 stsp if (selected_entry) {
2333 04cc582a 2018-08-01 stsp struct tog_view *log_view;
2334 6d0fee91 2018-08-01 stsp log_view = open_view(0, 0, 0, 0, view);
2335 04cc582a 2018-08-01 stsp if (log_view == NULL) {
2336 04cc582a 2018-08-01 stsp err = got_error_from_errno();
2337 04cc582a 2018-08-01 stsp goto done;
2339 04cc582a 2018-08-01 stsp err = log_tree_entry(log_view,
2340 04cc582a 2018-08-01 stsp selected_entry, &parents,
2341 04cc582a 2018-08-01 stsp commit_id, repo);
2342 04cc582a 2018-08-01 stsp close_view(log_view);
2343 cdf1ee82 2018-08-01 stsp show_view(view);
2345 69efd4c4 2018-07-18 stsp goto done;
2349 ffd1d5e5 2018-06-23 stsp case KEY_UP:
2350 ffd1d5e5 2018-06-23 stsp if (selected > 0)
2351 ffd1d5e5 2018-06-23 stsp selected--;
2352 ffd1d5e5 2018-06-23 stsp if (selected > 0)
2354 ffd1d5e5 2018-06-23 stsp tree_scroll_up(&first_displayed_entry, 1,
2355 ffd1d5e5 2018-06-23 stsp entries, tree == root);
2357 ffd1d5e5 2018-06-23 stsp case KEY_PPAGE:
2358 ffd1d5e5 2018-06-23 stsp if (SIMPLEQ_FIRST(&entries->head) ==
2359 ffd1d5e5 2018-06-23 stsp first_displayed_entry) {
2360 cf8f1261 2018-06-23 stsp if (tree != root)
2361 cf8f1261 2018-06-23 stsp first_displayed_entry = NULL;
2362 ffd1d5e5 2018-06-23 stsp selected = 0;
2365 f7d12f7e 2018-08-01 stsp tree_scroll_up(&first_displayed_entry,
2366 f7d12f7e 2018-08-01 stsp view->nlines, entries, tree == root);
2369 ffd1d5e5 2018-06-23 stsp case KEY_DOWN:
2370 ffd1d5e5 2018-06-23 stsp if (selected < ndisplayed - 1) {
2371 ffd1d5e5 2018-06-23 stsp selected++;
2374 ffd1d5e5 2018-06-23 stsp tree_scroll_down(&first_displayed_entry, 1,
2375 ffd1d5e5 2018-06-23 stsp last_displayed_entry, entries);
2377 ffd1d5e5 2018-06-23 stsp case KEY_NPAGE:
2378 f7d12f7e 2018-08-01 stsp tree_scroll_down(&first_displayed_entry,
2379 f7d12f7e 2018-08-01 stsp view->nlines, last_displayed_entry,
2381 ffd1d5e5 2018-06-23 stsp if (SIMPLEQ_NEXT(last_displayed_entry, entry))
2383 ffd1d5e5 2018-06-23 stsp /* can't scroll any further; move cursor down */
2384 ffd1d5e5 2018-06-23 stsp if (selected < ndisplayed - 1)
2385 ffd1d5e5 2018-06-23 stsp selected = ndisplayed - 1;
2387 ffd1d5e5 2018-06-23 stsp case KEY_ENTER:
2388 ffd1d5e5 2018-06-23 stsp case '\r':
2389 ffd1d5e5 2018-06-23 stsp if (selected_entry == NULL) {
2390 ffd1d5e5 2018-06-23 stsp struct tog_parent_tree *parent;
2391 ffd1d5e5 2018-06-23 stsp case KEY_BACKSPACE:
2392 ffd1d5e5 2018-06-23 stsp /* user selected '..' */
2393 ffd1d5e5 2018-06-23 stsp if (tree == root)
2395 d9765a41 2018-06-23 stsp parent = TAILQ_FIRST(&parents);
2396 d9765a41 2018-06-23 stsp TAILQ_REMOVE(&parents, parent, entry);
2397 ffd1d5e5 2018-06-23 stsp got_object_tree_close(tree);
2398 ffd1d5e5 2018-06-23 stsp tree = parent->tree;
2399 ffd1d5e5 2018-06-23 stsp first_displayed_entry =
2400 ffd1d5e5 2018-06-23 stsp parent->first_displayed_entry;
2401 ffd1d5e5 2018-06-23 stsp selected_entry = parent->selected_entry;
2402 ffd1d5e5 2018-06-23 stsp selected = parent->selected;
2403 ffd1d5e5 2018-06-23 stsp free(parent);
2404 ffd1d5e5 2018-06-23 stsp } else if (S_ISDIR(selected_entry->mode)) {
2405 ffd1d5e5 2018-06-23 stsp struct tog_parent_tree *parent;
2406 ffd1d5e5 2018-06-23 stsp struct got_tree_object *child;
2407 ffd1d5e5 2018-06-23 stsp err = got_object_open_as_tree(
2408 ffd1d5e5 2018-06-23 stsp &child, repo, selected_entry->id);
2410 ffd1d5e5 2018-06-23 stsp goto done;
2411 ffd1d5e5 2018-06-23 stsp parent = calloc(1, sizeof(*parent));
2412 ffd1d5e5 2018-06-23 stsp if (parent == NULL) {
2413 ffd1d5e5 2018-06-23 stsp err = got_error_from_errno();
2414 ffd1d5e5 2018-06-23 stsp goto done;
2416 ffd1d5e5 2018-06-23 stsp parent->tree = tree;
2417 ffd1d5e5 2018-06-23 stsp parent->first_displayed_entry =
2418 ffd1d5e5 2018-06-23 stsp first_displayed_entry;
2419 ffd1d5e5 2018-06-23 stsp parent->selected_entry = selected_entry;
2420 ffd1d5e5 2018-06-23 stsp parent->selected = selected;
2421 d9765a41 2018-06-23 stsp TAILQ_INSERT_HEAD(&parents, parent,
2423 ffd1d5e5 2018-06-23 stsp tree = child;
2424 ffd1d5e5 2018-06-23 stsp selected = 0;
2425 ffd1d5e5 2018-06-23 stsp first_displayed_entry = NULL;
2426 ffd1d5e5 2018-06-23 stsp } else if (S_ISREG(selected_entry->mode)) {
2427 cdf1ee82 2018-08-01 stsp err = blame_tree_entry(view,
2428 e1cd8fed 2018-08-01 stsp selected_entry, &parents,
2429 e1cd8fed 2018-08-01 stsp commit_id, repo);
2431 ffd1d5e5 2018-06-23 stsp goto done;
2434 ffd1d5e5 2018-06-23 stsp case KEY_RESIZE:
2435 a41d2007 2018-08-01 stsp err = view_resize(view);
2437 a41d2007 2018-08-01 stsp goto done;
2438 f7d12f7e 2018-08-01 stsp if (selected > view->nlines)
2439 ffd1d5e5 2018-06-23 stsp selected = ndisplayed - 1;
2446 ffd1d5e5 2018-06-23 stsp free(tree_label);
2447 ffd1d5e5 2018-06-23 stsp free(commit_id_str);
2448 d9765a41 2018-06-23 stsp while (!TAILQ_EMPTY(&parents)) {
2449 ffd1d5e5 2018-06-23 stsp struct tog_parent_tree *parent;
2450 d9765a41 2018-06-23 stsp parent = TAILQ_FIRST(&parents);
2451 d9765a41 2018-06-23 stsp TAILQ_REMOVE(&parents, parent, entry);
2452 ffd1d5e5 2018-06-23 stsp free(parent);
2455 ffd1d5e5 2018-06-23 stsp if (tree != root)
2456 ffd1d5e5 2018-06-23 stsp got_object_tree_close(tree);
2457 ffd1d5e5 2018-06-23 stsp return err;
2460 ffd1d5e5 2018-06-23 stsp __dead static void
2461 ffd1d5e5 2018-06-23 stsp usage_tree(void)
2464 ffd1d5e5 2018-06-23 stsp fprintf(stderr, "usage: %s tree [-c commit] [repository-path]\n",
2465 ffd1d5e5 2018-06-23 stsp getprogname());
2469 ffd1d5e5 2018-06-23 stsp static const struct got_error *
2470 ffd1d5e5 2018-06-23 stsp cmd_tree(int argc, char *argv[])
2472 ffd1d5e5 2018-06-23 stsp const struct got_error *error;
2473 ffd1d5e5 2018-06-23 stsp struct got_repository *repo = NULL;
2474 ffd1d5e5 2018-06-23 stsp char *repo_path = NULL;
2475 ffd1d5e5 2018-06-23 stsp struct got_object_id *commit_id = NULL;
2476 ffd1d5e5 2018-06-23 stsp char *commit_id_arg = NULL;
2477 ffd1d5e5 2018-06-23 stsp struct got_commit_object *commit = NULL;
2478 ffd1d5e5 2018-06-23 stsp struct got_tree_object *tree = NULL;
2480 5221c383 2018-08-01 stsp struct tog_view *view;
2482 ffd1d5e5 2018-06-23 stsp #ifndef PROFILE
2483 ffd1d5e5 2018-06-23 stsp if (pledge("stdio rpath wpath cpath flock proc tty", NULL) == -1)
2484 ffd1d5e5 2018-06-23 stsp err(1, "pledge");
2487 ffd1d5e5 2018-06-23 stsp while ((ch = getopt(argc, argv, "c:")) != -1) {
2488 ffd1d5e5 2018-06-23 stsp switch (ch) {
2490 ffd1d5e5 2018-06-23 stsp commit_id_arg = optarg;
2494 ffd1d5e5 2018-06-23 stsp /* NOTREACHED */
2498 ffd1d5e5 2018-06-23 stsp argc -= optind;
2499 ffd1d5e5 2018-06-23 stsp argv += optind;
2501 ffd1d5e5 2018-06-23 stsp if (argc == 0) {
2502 ffd1d5e5 2018-06-23 stsp repo_path = getcwd(NULL, 0);
2503 ffd1d5e5 2018-06-23 stsp if (repo_path == NULL)
2504 ffd1d5e5 2018-06-23 stsp return got_error_from_errno();
2505 ffd1d5e5 2018-06-23 stsp } else if (argc == 1) {
2506 ffd1d5e5 2018-06-23 stsp repo_path = realpath(argv[0], NULL);
2507 ffd1d5e5 2018-06-23 stsp if (repo_path == NULL)
2508 ffd1d5e5 2018-06-23 stsp return got_error_from_errno();
2510 ffd1d5e5 2018-06-23 stsp usage_log();
2512 ffd1d5e5 2018-06-23 stsp error = got_repo_open(&repo, repo_path);
2513 ffd1d5e5 2018-06-23 stsp free(repo_path);
2514 ffd1d5e5 2018-06-23 stsp if (error != NULL)
2515 ffd1d5e5 2018-06-23 stsp return error;
2517 ffd1d5e5 2018-06-23 stsp if (commit_id_arg == NULL) {
2518 ffd1d5e5 2018-06-23 stsp error = get_head_commit_id(&commit_id, repo);
2519 ffd1d5e5 2018-06-23 stsp if (error != NULL)
2520 ffd1d5e5 2018-06-23 stsp goto done;
2522 ffd1d5e5 2018-06-23 stsp struct got_object *obj;
2523 ffd1d5e5 2018-06-23 stsp error = got_object_open_by_id_str(&obj, repo, commit_id_arg);
2524 ffd1d5e5 2018-06-23 stsp if (error == NULL) {
2525 ffd1d5e5 2018-06-23 stsp commit_id = got_object_get_id(obj);
2526 ffd1d5e5 2018-06-23 stsp if (commit_id == NULL)
2527 ffd1d5e5 2018-06-23 stsp error = got_error_from_errno();
2530 ffd1d5e5 2018-06-23 stsp if (error != NULL)
2531 ffd1d5e5 2018-06-23 stsp goto done;
2533 ffd1d5e5 2018-06-23 stsp error = got_object_open_as_commit(&commit, repo, commit_id);
2534 ffd1d5e5 2018-06-23 stsp if (error != NULL)
2535 ffd1d5e5 2018-06-23 stsp goto done;
2537 ffd1d5e5 2018-06-23 stsp error = got_object_open_as_tree(&tree, repo, commit->tree_id);
2538 ffd1d5e5 2018-06-23 stsp if (error != NULL)
2539 ffd1d5e5 2018-06-23 stsp goto done;
2541 6d0fee91 2018-08-01 stsp view = open_view(0, 0, 0, 0, NULL);
2542 5221c383 2018-08-01 stsp if (view == NULL) {
2543 5221c383 2018-08-01 stsp error = got_error_from_errno();
2544 5221c383 2018-08-01 stsp goto done;
2546 5221c383 2018-08-01 stsp error = show_tree_view(view, tree, commit_id, repo);
2547 5221c383 2018-08-01 stsp close_view(view);
2549 ffd1d5e5 2018-06-23 stsp free(commit_id);
2550 ffd1d5e5 2018-06-23 stsp if (commit)
2551 ffd1d5e5 2018-06-23 stsp got_object_commit_close(commit);
2553 ffd1d5e5 2018-06-23 stsp got_object_tree_close(tree);
2555 ffd1d5e5 2018-06-23 stsp got_repo_close(repo);
2556 ffd1d5e5 2018-06-23 stsp return error;
2558 5c5136c5 2018-05-20 stsp static void
2559 9f7d7167 2018-04-29 stsp init_curses(void)
2561 9f7d7167 2018-04-29 stsp initscr();
2565 9f7d7167 2018-04-29 stsp intrflush(stdscr, FALSE);
2566 9f7d7167 2018-04-29 stsp keypad(stdscr, TRUE);
2567 1f475ad8 2018-05-10 stsp curs_set(0);
2570 4ed7e80c 2018-05-20 stsp __dead static void
2571 9f7d7167 2018-04-29 stsp usage(void)
2575 c2301be8 2018-04-30 stsp fprintf(stderr, "usage: %s [-h] [command] [arg ...]\n\n"
2576 9f7d7167 2018-04-29 stsp "Available commands:\n", getprogname());
2577 9f7d7167 2018-04-29 stsp for (i = 0; i < nitems(tog_commands); i++) {
2578 9f7d7167 2018-04-29 stsp struct tog_cmd *cmd = &tog_commands[i];
2579 c2301be8 2018-04-30 stsp fprintf(stderr, " %s: %s\n", cmd->name, cmd->descr);
2584 c2301be8 2018-04-30 stsp static char **
2585 c2301be8 2018-04-30 stsp make_argv(const char *arg0, const char *arg1)
2587 c2301be8 2018-04-30 stsp char **argv;
2588 c2301be8 2018-04-30 stsp int argc = (arg1 == NULL ? 1 : 2);
2590 c2301be8 2018-04-30 stsp argv = calloc(argc, sizeof(char *));
2591 c2301be8 2018-04-30 stsp if (argv == NULL)
2592 c2301be8 2018-04-30 stsp err(1, "calloc");
2593 c2301be8 2018-04-30 stsp argv[0] = strdup(arg0);
2594 c2301be8 2018-04-30 stsp if (argv[0] == NULL)
2595 c2301be8 2018-04-30 stsp err(1, "calloc");
2596 c2301be8 2018-04-30 stsp if (arg1) {
2597 c2301be8 2018-04-30 stsp argv[1] = strdup(arg1);
2598 c2301be8 2018-04-30 stsp if (argv[1] == NULL)
2599 c2301be8 2018-04-30 stsp err(1, "calloc");
2602 c2301be8 2018-04-30 stsp return argv;
2606 9f7d7167 2018-04-29 stsp main(int argc, char *argv[])
2608 9f7d7167 2018-04-29 stsp const struct got_error *error = NULL;
2609 9f7d7167 2018-04-29 stsp struct tog_cmd *cmd = NULL;
2610 9f7d7167 2018-04-29 stsp int ch, hflag = 0;
2611 c2301be8 2018-04-30 stsp char **cmd_argv = NULL;
2613 9f7d7167 2018-04-29 stsp setlocale(LC_ALL, "");
2615 9f7d7167 2018-04-29 stsp while ((ch = getopt(argc, argv, "h")) != -1) {
2616 9f7d7167 2018-04-29 stsp switch (ch) {
2618 9f7d7167 2018-04-29 stsp hflag = 1;
2622 9f7d7167 2018-04-29 stsp /* NOTREACHED */
2626 9f7d7167 2018-04-29 stsp argc -= optind;
2627 9f7d7167 2018-04-29 stsp argv += optind;
2628 9f7d7167 2018-04-29 stsp optind = 0;
2629 c2301be8 2018-04-30 stsp optreset = 1;
2631 c2301be8 2018-04-30 stsp if (argc == 0) {
2632 f29d3e89 2018-06-23 stsp if (hflag)
2634 c2301be8 2018-04-30 stsp /* Build an argument vector which runs a default command. */
2635 9f7d7167 2018-04-29 stsp cmd = &tog_commands[0];
2636 c2301be8 2018-04-30 stsp cmd_argv = make_argv(cmd->name, NULL);
2641 c2301be8 2018-04-30 stsp /* Did the user specific a command? */
2642 9f7d7167 2018-04-29 stsp for (i = 0; i < nitems(tog_commands); i++) {
2643 c2301be8 2018-04-30 stsp if (strncmp(tog_commands[i].name, argv[0],
2644 9f7d7167 2018-04-29 stsp strlen(argv[0])) == 0) {
2645 9f7d7167 2018-04-29 stsp cmd = &tog_commands[i];
2646 9f7d7167 2018-04-29 stsp if (hflag)
2647 9f7d7167 2018-04-29 stsp tog_commands[i].cmd_usage();
2651 9f7d7167 2018-04-29 stsp if (cmd == NULL) {
2652 c2301be8 2018-04-30 stsp /* Did the user specify a repository? */
2653 c2301be8 2018-04-30 stsp char *repo_path = realpath(argv[0], NULL);
2654 c2301be8 2018-04-30 stsp if (repo_path) {
2655 c2301be8 2018-04-30 stsp struct got_repository *repo;
2656 c2301be8 2018-04-30 stsp error = got_repo_open(&repo, repo_path);
2657 c2301be8 2018-04-30 stsp if (error == NULL)
2658 c2301be8 2018-04-30 stsp got_repo_close(repo);
2660 ad7de8d9 2018-04-30 stsp error = got_error_from_errno();
2661 c2301be8 2018-04-30 stsp if (error) {
2662 f29d3e89 2018-06-23 stsp if (hflag) {
2663 f29d3e89 2018-06-23 stsp fprintf(stderr, "%s: '%s' is not a "
2664 f29d3e89 2018-06-23 stsp "known command\n", getprogname(),
2668 ad7de8d9 2018-04-30 stsp fprintf(stderr, "%s: '%s' is neither a known "
2669 ad7de8d9 2018-04-30 stsp "command nor a path to a repository\n",
2670 c2301be8 2018-04-30 stsp getprogname(), argv[0]);
2671 ad7de8d9 2018-04-30 stsp free(repo_path);
2674 c2301be8 2018-04-30 stsp cmd = &tog_commands[0];
2675 c2301be8 2018-04-30 stsp cmd_argv = make_argv(cmd->name, repo_path);
2677 c2301be8 2018-04-30 stsp free(repo_path);
2681 5c5136c5 2018-05-20 stsp init_curses();
2683 c2301be8 2018-04-30 stsp error = cmd->cmd_main(argc, cmd_argv ? cmd_argv : argv);
2684 9f7d7167 2018-04-29 stsp if (error)
2685 9f7d7167 2018-04-29 stsp goto done;
2688 c2301be8 2018-04-30 stsp free(cmd_argv);
2689 9f7d7167 2018-04-29 stsp if (error)
2690 9f7d7167 2018-04-29 stsp fprintf(stderr, "%s: %s\n", getprogname(), error->msg);