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;
91 cd0acaa7 2018-05-20 stsp static const struct got_error *
92 ea5e7bb5 2018-08-01 stsp show_diff_view(struct tog_view *, struct got_object *, struct got_object *,
93 cd0acaa7 2018-05-20 stsp struct got_repository *);
94 cd0acaa7 2018-05-20 stsp static const struct got_error *
95 ecb28ae0 2018-07-16 stsp show_log_view(struct got_object_id *, struct got_repository *, const char *);
96 a70480e0 2018-06-23 stsp static const struct got_error *
97 a70480e0 2018-06-23 stsp show_blame_view(const char *, struct got_object_id *, struct got_repository *);
98 ffd1d5e5 2018-06-23 stsp static const struct got_error *
99 ffd1d5e5 2018-06-23 stsp show_tree_view(struct got_tree_object *, struct got_object_id *,
100 ffd1d5e5 2018-06-23 stsp struct got_repository *);
102 ea5e7bb5 2018-08-01 stsp static void
103 ea5e7bb5 2018-08-01 stsp close_view(struct tog_view *view)
105 ea5e7bb5 2018-08-01 stsp if (view->panel)
106 ea5e7bb5 2018-08-01 stsp del_panel(view->panel);
107 ea5e7bb5 2018-08-01 stsp if (view->window)
108 ea5e7bb5 2018-08-01 stsp delwin(view->window);
109 ea5e7bb5 2018-08-01 stsp free(view);
112 ea5e7bb5 2018-08-01 stsp static struct tog_view *
113 842167bf 2018-08-01 stsp open_view(int nlines, int ncols, int begin_y, int begin_x)
115 ea5e7bb5 2018-08-01 stsp struct tog_view *view = malloc(sizeof(*view));
117 ea5e7bb5 2018-08-01 stsp if (view == NULL)
118 ea5e7bb5 2018-08-01 stsp return NULL;
120 97ddc146 2018-08-01 stsp view->nlines = nlines;
121 97ddc146 2018-08-01 stsp view->ncols = ncols;
122 97ddc146 2018-08-01 stsp view->begin_y = begin_y;
123 97ddc146 2018-08-01 stsp view->begin_x = begin_x;
124 842167bf 2018-08-01 stsp view->window = newwin(nlines, ncols, begin_y, begin_x);
125 ea5e7bb5 2018-08-01 stsp if (view->window == NULL) {
126 ea5e7bb5 2018-08-01 stsp close_view(view);
127 ea5e7bb5 2018-08-01 stsp return NULL;
129 ea5e7bb5 2018-08-01 stsp view->panel = new_panel(view->window);
130 ea5e7bb5 2018-08-01 stsp if (view->panel == NULL) {
131 ea5e7bb5 2018-08-01 stsp close_view(view);
132 ea5e7bb5 2018-08-01 stsp return NULL;
135 ea5e7bb5 2018-08-01 stsp keypad(view->window, TRUE);
136 ea5e7bb5 2018-08-01 stsp return view;
139 4ed7e80c 2018-05-20 stsp __dead static void
140 9f7d7167 2018-04-29 stsp usage_log(void)
143 ecb28ae0 2018-07-16 stsp fprintf(stderr, "usage: %s log [-c commit] [-r repository-path] [path]\n",
144 9f7d7167 2018-04-29 stsp getprogname());
148 963b370f 2018-05-20 stsp /* Create newly allocated wide-character string equivalent to a byte string. */
149 80ddbec8 2018-04-29 stsp static const struct got_error *
150 963b370f 2018-05-20 stsp mbs2ws(wchar_t **ws, size_t *wlen, const char *s)
152 00dfcb92 2018-06-11 stsp char *vis = NULL;
153 963b370f 2018-05-20 stsp const struct got_error *err = NULL;
155 963b370f 2018-05-20 stsp *ws = NULL;
156 963b370f 2018-05-20 stsp *wlen = mbstowcs(NULL, s, 0);
157 00dfcb92 2018-06-11 stsp if (*wlen == (size_t)-1) {
158 00dfcb92 2018-06-11 stsp int vislen;
159 00dfcb92 2018-06-11 stsp if (errno != EILSEQ)
160 00dfcb92 2018-06-11 stsp return got_error_from_errno();
162 00dfcb92 2018-06-11 stsp /* byte string invalid in current encoding; try to "fix" it */
163 00dfcb92 2018-06-11 stsp err = got_mbsavis(&vis, &vislen, s);
165 00dfcb92 2018-06-11 stsp return err;
166 00dfcb92 2018-06-11 stsp *wlen = mbstowcs(NULL, vis, 0);
167 a7f50699 2018-06-11 stsp if (*wlen == (size_t)-1) {
168 a7f50699 2018-06-11 stsp err = got_error_from_errno(); /* give up */
173 963b370f 2018-05-20 stsp *ws = calloc(*wlen + 1, sizeof(*ws));
174 a7f50699 2018-06-11 stsp if (*ws == NULL) {
175 a7f50699 2018-06-11 stsp err = got_error_from_errno();
179 00dfcb92 2018-06-11 stsp if (mbstowcs(*ws, vis ? vis : s, *wlen) != *wlen)
180 963b370f 2018-05-20 stsp err = got_error_from_errno();
185 963b370f 2018-05-20 stsp *ws = NULL;
188 963b370f 2018-05-20 stsp return err;
191 963b370f 2018-05-20 stsp /* Format a line for display, ensuring that it won't overflow a width limit. */
192 963b370f 2018-05-20 stsp static const struct got_error *
193 ffd1d5e5 2018-06-23 stsp format_line(wchar_t **wlinep, int *widthp, const char *line, int wlimit)
195 963b370f 2018-05-20 stsp const struct got_error *err = NULL;
196 963b370f 2018-05-20 stsp int cols = 0;
197 963b370f 2018-05-20 stsp wchar_t *wline = NULL;
198 963b370f 2018-05-20 stsp size_t wlen;
201 963b370f 2018-05-20 stsp *wlinep = NULL;
202 b700b5d6 2018-07-10 stsp *widthp = 0;
204 963b370f 2018-05-20 stsp err = mbs2ws(&wline, &wlen, line);
206 963b370f 2018-05-20 stsp return err;
209 b700b5d6 2018-07-10 stsp while (i < wlen && cols < wlimit) {
210 963b370f 2018-05-20 stsp int width = wcwidth(wline[i]);
211 963b370f 2018-05-20 stsp switch (width) {
217 b700b5d6 2018-07-10 stsp if (cols + width <= wlimit) {
218 b700b5d6 2018-07-10 stsp cols += width;
223 963b370f 2018-05-20 stsp if (wline[i] == L'\t')
224 b700b5d6 2018-07-10 stsp cols += TABSIZE - ((cols + 1) % TABSIZE);
228 963b370f 2018-05-20 stsp err = got_error_from_errno();
232 963b370f 2018-05-20 stsp wline[i] = L'\0';
233 b700b5d6 2018-07-10 stsp if (widthp)
234 b700b5d6 2018-07-10 stsp *widthp = cols;
237 963b370f 2018-05-20 stsp free(wline);
239 963b370f 2018-05-20 stsp *wlinep = wline;
240 963b370f 2018-05-20 stsp return err;
243 963b370f 2018-05-20 stsp static const struct got_error *
244 2814baeb 2018-08-01 stsp draw_commit(struct tog_view *view, struct got_commit_object *commit,
245 2814baeb 2018-08-01 stsp struct got_object_id *id)
247 80ddbec8 2018-04-29 stsp const struct got_error *err = NULL;
248 b39d25c7 2018-07-10 stsp char datebuf[10]; /* YY-MM-DD + SPACE + NUL */
249 80ddbec8 2018-04-29 stsp char *logmsg0 = NULL, *logmsg = NULL;
250 6d9fbc00 2018-04-29 stsp char *author0 = NULL, *author = NULL;
251 bb737323 2018-05-20 stsp wchar_t *wlogmsg = NULL, *wauthor = NULL;
252 bb737323 2018-05-20 stsp int author_width, logmsg_width;
253 6d9fbc00 2018-04-29 stsp char *newline, *smallerthan;
254 80ddbec8 2018-04-29 stsp char *line = NULL;
255 bb737323 2018-05-20 stsp int col, limit;
256 b39d25c7 2018-07-10 stsp static const size_t date_display_cols = 9;
257 bb737323 2018-05-20 stsp static const size_t author_display_cols = 16;
258 9c2eaf34 2018-05-20 stsp const int avail = COLS;
260 b39d25c7 2018-07-10 stsp if (strftime(datebuf, sizeof(datebuf), "%g/%m/%d ", &commit->tm_committer)
261 b39d25c7 2018-07-10 stsp >= sizeof(datebuf))
262 b39d25c7 2018-07-10 stsp return got_error(GOT_ERR_NO_SPACE);
264 b39d25c7 2018-07-10 stsp if (avail < date_display_cols)
265 b39d25c7 2018-07-10 stsp limit = MIN(sizeof(datebuf) - 1, avail);
267 b39d25c7 2018-07-10 stsp limit = MIN(date_display_cols, sizeof(datebuf) - 1);
268 2814baeb 2018-08-01 stsp waddnstr(view->window, datebuf, limit);
269 b39d25c7 2018-07-10 stsp col = limit + 1;
270 b39d25c7 2018-07-10 stsp if (col > avail)
273 6d9fbc00 2018-04-29 stsp author0 = strdup(commit->author);
274 6d9fbc00 2018-04-29 stsp if (author0 == NULL) {
275 80ddbec8 2018-04-29 stsp err = got_error_from_errno();
278 6d9fbc00 2018-04-29 stsp author = author0;
279 6d9fbc00 2018-04-29 stsp smallerthan = strchr(author, '<');
280 6d9fbc00 2018-04-29 stsp if (smallerthan)
281 6d9fbc00 2018-04-29 stsp *smallerthan = '\0';
283 6d9fbc00 2018-04-29 stsp char *at = strchr(author, '@');
285 6d9fbc00 2018-04-29 stsp *at = '\0';
287 b39d25c7 2018-07-10 stsp limit = avail - col;
288 bb737323 2018-05-20 stsp err = format_line(&wauthor, &author_width, author, limit);
291 2814baeb 2018-08-01 stsp waddwstr(view->window, wauthor);
292 bb737323 2018-05-20 stsp col += author_width;
293 9c2eaf34 2018-05-20 stsp while (col <= avail && author_width < author_display_cols + 1) {
294 2814baeb 2018-08-01 stsp waddch(view->window, ' ');
296 bb737323 2018-05-20 stsp author_width++;
298 9c2eaf34 2018-05-20 stsp if (col > avail)
301 bb737323 2018-05-20 stsp logmsg0 = strdup(commit->logmsg);
302 bb737323 2018-05-20 stsp if (logmsg0 == NULL) {
303 6d9fbc00 2018-04-29 stsp err = got_error_from_errno();
306 bb737323 2018-05-20 stsp logmsg = logmsg0;
307 bb737323 2018-05-20 stsp while (*logmsg == '\n')
309 bb737323 2018-05-20 stsp newline = strchr(logmsg, '\n');
310 bb737323 2018-05-20 stsp if (newline)
311 bb737323 2018-05-20 stsp *newline = '\0';
312 bb737323 2018-05-20 stsp limit = avail - col;
313 bb737323 2018-05-20 stsp err = format_line(&wlogmsg, &logmsg_width, logmsg, limit);
316 2814baeb 2018-08-01 stsp waddwstr(view->window, wlogmsg);
317 bb737323 2018-05-20 stsp col += logmsg_width;
318 bb737323 2018-05-20 stsp while (col <= avail) {
319 2814baeb 2018-08-01 stsp waddch(view->window, ' ');
323 80ddbec8 2018-04-29 stsp free(logmsg0);
324 bb737323 2018-05-20 stsp free(wlogmsg);
325 6d9fbc00 2018-04-29 stsp free(author0);
326 bb737323 2018-05-20 stsp free(wauthor);
327 80ddbec8 2018-04-29 stsp free(line);
328 80ddbec8 2018-04-29 stsp return err;
331 80ddbec8 2018-04-29 stsp struct commit_queue_entry {
332 80ddbec8 2018-04-29 stsp TAILQ_ENTRY(commit_queue_entry) entry;
333 80ddbec8 2018-04-29 stsp struct got_object_id *id;
334 80ddbec8 2018-04-29 stsp struct got_commit_object *commit;
336 ecb28ae0 2018-07-16 stsp TAILQ_HEAD(commit_queue_head, commit_queue_entry);
337 ecb28ae0 2018-07-16 stsp struct commit_queue {
338 ecb28ae0 2018-07-16 stsp int ncommits;
339 ecb28ae0 2018-07-16 stsp struct commit_queue_head head;
342 899d86c2 2018-05-10 stsp static struct commit_queue_entry *
343 899d86c2 2018-05-10 stsp alloc_commit_queue_entry(struct got_commit_object *commit,
344 899d86c2 2018-05-10 stsp struct got_object_id *id)
346 80ddbec8 2018-04-29 stsp struct commit_queue_entry *entry;
348 80ddbec8 2018-04-29 stsp entry = calloc(1, sizeof(*entry));
349 80ddbec8 2018-04-29 stsp if (entry == NULL)
350 899d86c2 2018-05-10 stsp return NULL;
352 899d86c2 2018-05-10 stsp entry->id = id;
353 99db9666 2018-05-07 stsp entry->commit = commit;
354 899d86c2 2018-05-10 stsp return entry;
357 99db9666 2018-05-07 stsp static void
358 99db9666 2018-05-07 stsp pop_commit(struct commit_queue *commits)
360 99db9666 2018-05-07 stsp struct commit_queue_entry *entry;
362 ecb28ae0 2018-07-16 stsp entry = TAILQ_FIRST(&commits->head);
363 ecb28ae0 2018-07-16 stsp TAILQ_REMOVE(&commits->head, entry, entry);
364 99db9666 2018-05-07 stsp got_object_commit_close(entry->commit);
365 ecb28ae0 2018-07-16 stsp commits->ncommits--;
366 9ba79e04 2018-06-11 stsp /* Don't free entry->id! It is owned by the commit graph. */
367 99db9666 2018-05-07 stsp free(entry);
370 99db9666 2018-05-07 stsp static void
371 99db9666 2018-05-07 stsp free_commits(struct commit_queue *commits)
373 ecb28ae0 2018-07-16 stsp while (!TAILQ_EMPTY(&commits->head))
374 99db9666 2018-05-07 stsp pop_commit(commits);
377 c4972b91 2018-05-07 stsp static const struct got_error *
378 9ba79e04 2018-06-11 stsp queue_commits(struct got_commit_graph *graph, struct commit_queue *commits,
379 ecb28ae0 2018-07-16 stsp struct got_object_id *start_id, int minqueue, int init,
380 ecb28ae0 2018-07-16 stsp struct got_repository *repo, const char *path)
382 899d86c2 2018-05-10 stsp const struct got_error *err = NULL;
383 899d86c2 2018-05-10 stsp struct got_object_id *id;
384 9ba79e04 2018-06-11 stsp struct commit_queue_entry *entry;
385 ecb28ae0 2018-07-16 stsp int nfetched, nqueued = 0, found_obj = 0;
386 c8f60bff 2018-07-23 stsp int is_root_path = strcmp(path, "/") == 0;
388 9ba79e04 2018-06-11 stsp err = got_commit_graph_iter_start(graph, start_id);
390 c4972b91 2018-05-07 stsp return err;
392 ecb28ae0 2018-07-16 stsp entry = TAILQ_LAST(&commits->head, commit_queue_head);
393 9ba79e04 2018-06-11 stsp if (entry && got_object_id_cmp(entry->id, start_id) == 0) {
394 9ba79e04 2018-06-11 stsp int nfetched;
396 9ba79e04 2018-06-11 stsp /* Start ID's commit is already on the queue; skip over it. */
397 9ba79e04 2018-06-11 stsp err = got_commit_graph_iter_next(&id, graph);
398 9ba79e04 2018-06-11 stsp if (err && err->code != GOT_ERR_ITER_NEED_MORE)
399 9ba79e04 2018-06-11 stsp return err;
401 9ba79e04 2018-06-11 stsp err = got_commit_graph_fetch_commits(&nfetched, graph, 1, repo);
403 9ba79e04 2018-06-11 stsp return err;
406 9ba79e04 2018-06-11 stsp while (1) {
407 9ba79e04 2018-06-11 stsp struct got_commit_object *commit;
409 9ba79e04 2018-06-11 stsp err = got_commit_graph_iter_next(&id, graph);
411 ecb28ae0 2018-07-16 stsp if (err->code != GOT_ERR_ITER_NEED_MORE)
413 ecb28ae0 2018-07-16 stsp if (nqueued >= minqueue) {
414 9ba79e04 2018-06-11 stsp err = NULL;
417 ecb28ae0 2018-07-16 stsp err = got_commit_graph_fetch_commits(&nfetched,
418 ecb28ae0 2018-07-16 stsp graph, 1, repo);
420 ecb28ae0 2018-07-16 stsp return err;
423 ecb28ae0 2018-07-16 stsp if (id == NULL)
426 9ba79e04 2018-06-11 stsp err = got_object_open_as_commit(&commit, repo, id);
430 c8f60bff 2018-07-23 stsp if (!is_root_path) {
431 ecb28ae0 2018-07-16 stsp struct got_object *obj;
432 ecb28ae0 2018-07-16 stsp struct got_object_qid *pid;
433 ecb28ae0 2018-07-16 stsp int changed = 0;
435 ecb28ae0 2018-07-16 stsp err = got_object_open_by_path(&obj, repo, id, path);
437 c8f60bff 2018-07-23 stsp got_object_commit_close(commit);
438 ecb28ae0 2018-07-16 stsp if (err->code == GOT_ERR_NO_OBJ &&
439 ecb28ae0 2018-07-16 stsp (found_obj || !init)) {
440 ecb28ae0 2018-07-16 stsp /* History stops here. */
441 ecb28ae0 2018-07-16 stsp err = got_error(GOT_ERR_ITER_COMPLETED);
445 ecb28ae0 2018-07-16 stsp found_obj = 1;
447 ecb28ae0 2018-07-16 stsp pid = SIMPLEQ_FIRST(&commit->parent_ids);
448 ecb28ae0 2018-07-16 stsp if (pid != NULL) {
449 ecb28ae0 2018-07-16 stsp struct got_object *pobj;
450 ecb28ae0 2018-07-16 stsp err = got_object_open_by_path(&pobj, repo,
451 ecb28ae0 2018-07-16 stsp pid->id, path);
453 ecb28ae0 2018-07-16 stsp if (err->code != GOT_ERR_NO_OBJ) {
454 ecb28ae0 2018-07-16 stsp got_object_close(obj);
455 c8f60bff 2018-07-23 stsp got_object_commit_close(commit);
458 ecb28ae0 2018-07-16 stsp err = NULL;
459 ecb28ae0 2018-07-16 stsp changed = 1;
461 c2f16475 2018-07-23 stsp struct got_object_id *id, *pid;
462 c2f16475 2018-07-23 stsp id = got_object_get_id(obj);
463 c2f16475 2018-07-23 stsp if (id == NULL) {
464 c2f16475 2018-07-23 stsp err = got_error_from_errno();
465 c8f60bff 2018-07-23 stsp got_object_close(obj);
466 c8f60bff 2018-07-23 stsp got_object_close(pobj);
469 c2f16475 2018-07-23 stsp pid = got_object_get_id(pobj);
470 c2f16475 2018-07-23 stsp if (pid == NULL) {
471 c2f16475 2018-07-23 stsp err = got_error_from_errno();
473 c8f60bff 2018-07-23 stsp got_object_close(obj);
474 c8f60bff 2018-07-23 stsp got_object_close(pobj);
478 c2f16475 2018-07-23 stsp (got_object_id_cmp(id, pid) != 0);
479 ecb28ae0 2018-07-16 stsp got_object_close(pobj);
484 c8f60bff 2018-07-23 stsp got_object_close(obj);
485 ecb28ae0 2018-07-16 stsp if (!changed) {
486 ecb28ae0 2018-07-16 stsp got_object_commit_close(commit);
491 9ba79e04 2018-06-11 stsp entry = alloc_commit_queue_entry(commit, id);
492 9ba79e04 2018-06-11 stsp if (entry == NULL) {
493 9ba79e04 2018-06-11 stsp err = got_error_from_errno();
496 ecb28ae0 2018-07-16 stsp TAILQ_INSERT_TAIL(&commits->head, entry, entry);
498 ecb28ae0 2018-07-16 stsp commits->ncommits++;
501 9ba79e04 2018-06-11 stsp return err;
504 99db9666 2018-05-07 stsp static const struct got_error *
505 9ba79e04 2018-06-11 stsp fetch_next_commit(struct commit_queue_entry **pentry,
506 9ba79e04 2018-06-11 stsp struct commit_queue_entry *entry, struct commit_queue *commits,
507 ecb28ae0 2018-07-16 stsp struct got_commit_graph *graph, struct got_repository *repo,
508 ecb28ae0 2018-07-16 stsp const char *path)
510 899d86c2 2018-05-10 stsp const struct got_error *err = NULL;
512 9ba79e04 2018-06-11 stsp *pentry = NULL;
514 ecb28ae0 2018-07-16 stsp err = queue_commits(graph, commits, entry->id, 1, 0, repo, path);
516 9ba79e04 2018-06-11 stsp return err;
518 9ba79e04 2018-06-11 stsp /* Next entry to display should now be available. */
519 9ba79e04 2018-06-11 stsp *pentry = TAILQ_NEXT(entry, entry);
520 9ba79e04 2018-06-11 stsp if (*pentry == NULL)
521 9ba79e04 2018-06-11 stsp return got_error(GOT_ERR_NO_OBJ);
523 9ba79e04 2018-06-11 stsp return NULL;
526 899d86c2 2018-05-10 stsp static const struct got_error *
527 9ba79e04 2018-06-11 stsp get_head_commit_id(struct got_object_id **head_id, struct got_repository *repo)
529 9ba79e04 2018-06-11 stsp const struct got_error *err = NULL;
530 9ba79e04 2018-06-11 stsp struct got_reference *head_ref;
532 9ba79e04 2018-06-11 stsp *head_id = NULL;
534 9ba79e04 2018-06-11 stsp err = got_ref_open(&head_ref, repo, GOT_REF_HEAD);
536 99db9666 2018-05-07 stsp return err;
538 9ba79e04 2018-06-11 stsp err = got_ref_resolve(head_id, repo, head_ref);
539 9ba79e04 2018-06-11 stsp got_ref_close(head_ref);
541 9ba79e04 2018-06-11 stsp *head_id = NULL;
542 99db9666 2018-05-07 stsp return err;
545 9ba79e04 2018-06-11 stsp return NULL;
548 0553a4e3 2018-04-30 stsp static const struct got_error *
549 2814baeb 2018-08-01 stsp draw_commits(struct tog_view *view, struct commit_queue_entry **last,
550 ecb28ae0 2018-07-16 stsp struct commit_queue_entry **selected, struct commit_queue_entry *first,
551 a7f40148 2018-07-18 stsp struct commit_queue *commits, int selected_idx, int limit,
552 a7f40148 2018-07-18 stsp struct got_commit_graph *graph, struct got_repository *repo,
553 a7f40148 2018-07-18 stsp const char *path)
555 0553a4e3 2018-04-30 stsp const struct got_error *err = NULL;
556 0553a4e3 2018-04-30 stsp struct commit_queue_entry *entry;
557 ecb28ae0 2018-07-16 stsp int ncommits, width;
558 867c6645 2018-07-10 stsp char *id_str, *header;
559 ecb28ae0 2018-07-16 stsp wchar_t *wline;
561 e0d42f60 2018-07-22 stsp entry = first;
562 e0d42f60 2018-07-22 stsp ncommits = 0;
563 e0d42f60 2018-07-22 stsp while (entry) {
564 e0d42f60 2018-07-22 stsp if (ncommits == selected_idx) {
565 e0d42f60 2018-07-22 stsp *selected = entry;
568 e0d42f60 2018-07-22 stsp entry = TAILQ_NEXT(entry, entry);
569 e0d42f60 2018-07-22 stsp ncommits++;
572 867c6645 2018-07-10 stsp err = got_object_id_str(&id_str, (*selected)->id);
574 867c6645 2018-07-10 stsp return err;
576 ecb28ae0 2018-07-16 stsp if (path) {
577 ecb28ae0 2018-07-16 stsp if (asprintf(&header, "commit: %s [%s]", id_str, path) == -1) {
578 ecb28ae0 2018-07-16 stsp err = got_error_from_errno();
579 ecb28ae0 2018-07-16 stsp free(id_str);
580 ecb28ae0 2018-07-16 stsp return err;
582 ecb28ae0 2018-07-16 stsp } else if (asprintf(&header, "commit: %s", id_str) == -1) {
583 867c6645 2018-07-10 stsp err = got_error_from_errno();
584 867c6645 2018-07-10 stsp free(id_str);
585 867c6645 2018-07-10 stsp return err;
587 ecb28ae0 2018-07-16 stsp free(id_str);
588 ecb28ae0 2018-07-16 stsp err = format_line(&wline, &width, header, COLS);
590 ecb28ae0 2018-07-16 stsp free(header);
591 ecb28ae0 2018-07-16 stsp return err;
593 ecb28ae0 2018-07-16 stsp free(header);
595 2814baeb 2018-08-01 stsp werase(view->window);
597 2814baeb 2018-08-01 stsp waddwstr(view->window, wline);
598 ecb28ae0 2018-07-16 stsp if (width < COLS)
599 2814baeb 2018-08-01 stsp waddch(view->window, '\n');
600 ecb28ae0 2018-07-16 stsp free(wline);
601 ecb28ae0 2018-07-16 stsp if (limit <= 1)
602 ecb28ae0 2018-07-16 stsp return NULL;
604 899d86c2 2018-05-10 stsp entry = first;
605 899d86c2 2018-05-10 stsp *last = first;
606 867c6645 2018-07-10 stsp ncommits = 0;
607 899d86c2 2018-05-10 stsp while (entry) {
608 ecb28ae0 2018-07-16 stsp if (ncommits >= limit - 1)
610 e0d42f60 2018-07-22 stsp if (ncommits == selected_idx)
611 2814baeb 2018-08-01 stsp wstandout(view->window);
612 2814baeb 2018-08-01 stsp err = draw_commit(view, entry->commit, entry->id);
613 cd0acaa7 2018-05-20 stsp if (ncommits == selected_idx)
614 2814baeb 2018-08-01 stsp wstandend(view->window);
617 0553a4e3 2018-04-30 stsp ncommits++;
618 899d86c2 2018-05-10 stsp *last = entry;
619 a7f40148 2018-07-18 stsp if (entry == TAILQ_LAST(&commits->head, commit_queue_head)) {
620 a7f40148 2018-07-18 stsp err = queue_commits(graph, commits, entry->id, 1,
621 a7f40148 2018-07-18 stsp 0, repo, path);
623 a7f40148 2018-07-18 stsp if (err->code != GOT_ERR_ITER_COMPLETED)
624 a7f40148 2018-07-18 stsp return err;
625 a7f40148 2018-07-18 stsp err = NULL;
628 899d86c2 2018-05-10 stsp entry = TAILQ_NEXT(entry, entry);
631 80ddbec8 2018-04-29 stsp update_panels();
632 80ddbec8 2018-04-29 stsp doupdate();
634 80ddbec8 2018-04-29 stsp return err;
637 07b55e75 2018-05-10 stsp static void
638 16482c3b 2018-05-20 stsp scroll_up(struct commit_queue_entry **first_displayed_entry, int maxscroll,
639 07b55e75 2018-05-10 stsp struct commit_queue *commits)
641 07b55e75 2018-05-10 stsp struct commit_queue_entry *entry;
642 07b55e75 2018-05-10 stsp int nscrolled = 0;
644 ecb28ae0 2018-07-16 stsp entry = TAILQ_FIRST(&commits->head);
645 07b55e75 2018-05-10 stsp if (*first_displayed_entry == entry)
648 07b55e75 2018-05-10 stsp entry = *first_displayed_entry;
649 16482c3b 2018-05-20 stsp while (entry && nscrolled < maxscroll) {
650 ecb28ae0 2018-07-16 stsp entry = TAILQ_PREV(entry, commit_queue_head, entry);
651 07b55e75 2018-05-10 stsp if (entry) {
652 07b55e75 2018-05-10 stsp *first_displayed_entry = entry;
653 07b55e75 2018-05-10 stsp nscrolled++;
658 aa075928 2018-05-10 stsp static const struct got_error *
659 16482c3b 2018-05-20 stsp scroll_down(struct commit_queue_entry **first_displayed_entry, int maxscroll,
660 aa075928 2018-05-10 stsp struct commit_queue_entry *last_displayed_entry,
661 9ba79e04 2018-06-11 stsp struct commit_queue *commits, struct got_commit_graph *graph,
662 ecb28ae0 2018-07-16 stsp struct got_repository *repo, const char *path)
664 aa075928 2018-05-10 stsp const struct got_error *err = NULL;
665 dd0a52c1 2018-05-20 stsp struct commit_queue_entry *pentry;
666 aa075928 2018-05-10 stsp int nscrolled = 0;
669 dd0a52c1 2018-05-20 stsp pentry = TAILQ_NEXT(last_displayed_entry, entry);
670 aa075928 2018-05-10 stsp if (pentry == NULL) {
671 9ba79e04 2018-06-11 stsp err = fetch_next_commit(&pentry, last_displayed_entry,
672 ecb28ae0 2018-07-16 stsp commits, graph, repo, path);
673 9a6bf2a5 2018-05-20 stsp if (err || pentry == NULL)
676 dd0a52c1 2018-05-20 stsp last_displayed_entry = pentry;
678 dd0a52c1 2018-05-20 stsp pentry = TAILQ_NEXT(*first_displayed_entry, entry);
679 dd0a52c1 2018-05-20 stsp if (pentry == NULL)
681 aa075928 2018-05-10 stsp *first_displayed_entry = pentry;
682 16482c3b 2018-05-20 stsp } while (++nscrolled < maxscroll);
684 dd0a52c1 2018-05-20 stsp return err;
687 cd0acaa7 2018-05-20 stsp static const struct got_error *
688 cd0acaa7 2018-05-20 stsp show_commit(struct commit_queue_entry *entry, struct got_repository *repo)
690 cd0acaa7 2018-05-20 stsp const struct got_error *err;
691 cd0acaa7 2018-05-20 stsp struct got_object *obj1 = NULL, *obj2 = NULL;
692 9ba79e04 2018-06-11 stsp struct got_object_qid *parent_id;
693 ea5e7bb5 2018-08-01 stsp struct tog_view *view;
695 cd0acaa7 2018-05-20 stsp err = got_object_open(&obj2, repo, entry->id);
697 cd0acaa7 2018-05-20 stsp return err;
699 9ba79e04 2018-06-11 stsp parent_id = SIMPLEQ_FIRST(&entry->commit->parent_ids);
700 9ba79e04 2018-06-11 stsp if (parent_id) {
701 9ba79e04 2018-06-11 stsp err = got_object_open(&obj1, repo, parent_id->id);
706 842167bf 2018-08-01 stsp view = open_view(0, 0, 0, 0);
707 ea5e7bb5 2018-08-01 stsp if (view == NULL) {
708 ea5e7bb5 2018-08-01 stsp err = got_error_from_errno();
712 ea5e7bb5 2018-08-01 stsp err = show_diff_view(view, obj1, obj2, repo);
713 ea5e7bb5 2018-08-01 stsp close_view(view);
716 cd0acaa7 2018-05-20 stsp got_object_close(obj1);
718 cd0acaa7 2018-05-20 stsp got_object_close(obj2);
719 cd0acaa7 2018-05-20 stsp return err;
722 80ddbec8 2018-04-29 stsp static const struct got_error *
723 9343a5fb 2018-06-23 stsp browse_commit(struct commit_queue_entry *entry, struct got_repository *repo)
725 9343a5fb 2018-06-23 stsp const struct got_error *err = NULL;
726 9343a5fb 2018-06-23 stsp struct got_tree_object *tree;
728 9343a5fb 2018-06-23 stsp err = got_object_open_as_tree(&tree, repo, entry->commit->tree_id);
730 9343a5fb 2018-06-23 stsp return err;
732 9343a5fb 2018-06-23 stsp err = show_tree_view(tree, entry->id, repo);
733 9343a5fb 2018-06-23 stsp got_object_tree_close(tree);
734 9343a5fb 2018-06-23 stsp return err;
737 9343a5fb 2018-06-23 stsp static const struct got_error *
738 ecb28ae0 2018-07-16 stsp show_log_view(struct got_object_id *start_id, struct got_repository *repo,
739 ecb28ae0 2018-07-16 stsp const char *path)
741 80ddbec8 2018-04-29 stsp const struct got_error *err = NULL;
742 9ba79e04 2018-06-11 stsp struct got_object_id *head_id = NULL;
743 ecb28ae0 2018-07-16 stsp int ch, done = 0, selected = 0, nfetched;
744 ecb28ae0 2018-07-16 stsp struct got_commit_graph *graph = NULL;
745 0553a4e3 2018-04-30 stsp struct commit_queue commits;
746 899d86c2 2018-05-10 stsp struct commit_queue_entry *first_displayed_entry = NULL;
747 899d86c2 2018-05-10 stsp struct commit_queue_entry *last_displayed_entry = NULL;
748 cd0acaa7 2018-05-20 stsp struct commit_queue_entry *selected_entry = NULL;
749 ecb28ae0 2018-07-16 stsp char *in_repo_path = NULL;
750 2814baeb 2018-08-01 stsp struct tog_view *view = NULL;
752 ecb28ae0 2018-07-16 stsp err = got_repo_map_path(&in_repo_path, repo, path);
753 ecb28ae0 2018-07-16 stsp if (err != NULL)
756 9ba79e04 2018-06-11 stsp err = get_head_commit_id(&head_id, repo);
758 9ba79e04 2018-06-11 stsp return err;
760 034e3b69 2018-07-22 stsp /* The graph contains all commits. */
761 5489743f 2018-06-13 stsp err = got_commit_graph_open(&graph, head_id, 0, repo);
764 034e3b69 2018-07-22 stsp /* The commit queue contains a subset of commits filtered by path. */
765 034e3b69 2018-07-22 stsp TAILQ_INIT(&commits.head);
766 034e3b69 2018-07-22 stsp commits.ncommits = 0;
768 9ba79e04 2018-06-11 stsp /* Populate commit graph with a sufficient number of commits. */
769 9ba79e04 2018-06-11 stsp err = got_commit_graph_fetch_commits_up_to(&nfetched, graph, start_id,
775 9ba79e04 2018-06-11 stsp * Open the initial batch of commits, sorted in commit graph order.
776 9ba79e04 2018-06-11 stsp * We keep all commits open throughout the lifetime of the log view
777 9ba79e04 2018-06-11 stsp * in order to avoid having to re-fetch commits from disk while
778 9ba79e04 2018-06-11 stsp * updating the display.
780 034e3b69 2018-07-22 stsp err = queue_commits(graph, &commits, start_id, LINES, 1, repo,
781 ecb28ae0 2018-07-16 stsp in_repo_path);
783 55198a88 2018-07-16 stsp if (err->code != GOT_ERR_ITER_COMPLETED)
785 55198a88 2018-07-16 stsp err = NULL;
788 842167bf 2018-08-01 stsp view = open_view(0, 0, 0, 0);
789 2814baeb 2018-08-01 stsp if (view == NULL) {
790 2814baeb 2018-08-01 stsp err = got_error_from_errno();
794 2814baeb 2018-08-01 stsp show_panel(view->panel);
796 034e3b69 2018-07-22 stsp first_displayed_entry = TAILQ_FIRST(&commits.head);
797 867c6645 2018-07-10 stsp selected_entry = first_displayed_entry;
798 899d86c2 2018-05-10 stsp while (!done) {
799 2814baeb 2018-08-01 stsp err = draw_commits(view, &last_displayed_entry, &selected_entry,
800 a7f40148 2018-07-18 stsp first_displayed_entry, &commits, selected, LINES,
801 a7f40148 2018-07-18 stsp graph, repo, in_repo_path);
805 80ddbec8 2018-04-29 stsp nodelay(stdscr, FALSE);
806 2814baeb 2018-08-01 stsp ch = wgetch(view->window);
807 f7182337 2018-05-20 stsp nodelay(stdscr, TRUE);
808 80ddbec8 2018-04-29 stsp switch (ch) {
815 80ddbec8 2018-04-29 stsp case KEY_UP:
816 80ddbec8 2018-04-29 stsp if (selected > 0)
817 80ddbec8 2018-04-29 stsp selected--;
818 8ec9698d 2018-05-10 stsp if (selected > 0)
820 07b55e75 2018-05-10 stsp scroll_up(&first_displayed_entry, 1, &commits);
822 48531068 2018-05-10 stsp case KEY_PPAGE:
823 ecb28ae0 2018-07-16 stsp if (TAILQ_FIRST(&commits.head) ==
824 dfc1d240 2018-05-10 stsp first_displayed_entry) {
825 dfc1d240 2018-05-10 stsp selected = 0;
828 48531068 2018-05-10 stsp scroll_up(&first_displayed_entry, LINES,
832 80ddbec8 2018-04-29 stsp case KEY_DOWN:
833 ecb28ae0 2018-07-16 stsp if (selected < MIN(LINES - 2,
834 ecb28ae0 2018-07-16 stsp commits.ncommits - 1)) {
835 15c91275 2018-05-20 stsp selected++;
838 15c91275 2018-05-20 stsp err = scroll_down(&first_displayed_entry, 1,
839 9ba79e04 2018-06-11 stsp last_displayed_entry, &commits, graph,
840 ecb28ae0 2018-07-16 stsp repo, in_repo_path);
842 ecb28ae0 2018-07-16 stsp if (err->code != GOT_ERR_ITER_COMPLETED)
844 ecb28ae0 2018-07-16 stsp err = NULL;
847 ecb28ae0 2018-07-16 stsp case KEY_NPAGE: {
848 ecb28ae0 2018-07-16 stsp struct commit_queue_entry *first = first_displayed_entry;
849 e50ee4f1 2018-05-10 stsp err = scroll_down(&first_displayed_entry, LINES,
850 9ba79e04 2018-06-11 stsp last_displayed_entry, &commits, graph,
851 ecb28ae0 2018-07-16 stsp repo, in_repo_path);
853 ecb28ae0 2018-07-16 stsp if (err->code != GOT_ERR_ITER_COMPLETED)
855 ecb28ae0 2018-07-16 stsp /* can't scroll any further; move cursor down */
856 ecb28ae0 2018-07-16 stsp if (first == first_displayed_entry && selected <
857 ecb28ae0 2018-07-16 stsp MIN(LINES - 2, commits.ncommits - 1)) {
858 ecb28ae0 2018-07-16 stsp selected = MIN(LINES - 2,
859 ecb28ae0 2018-07-16 stsp commits.ncommits - 1);
861 ecb28ae0 2018-07-16 stsp err = NULL;
865 d6df9be4 2018-04-30 stsp case KEY_RESIZE:
866 55198a88 2018-07-16 stsp if (selected > LINES - 2)
867 867c6645 2018-07-10 stsp selected = LINES - 2;
868 55198a88 2018-07-16 stsp if (selected > commits.ncommits - 1)
869 55198a88 2018-07-16 stsp selected = commits.ncommits - 1;
871 cd0acaa7 2018-05-20 stsp case KEY_ENTER:
873 cd0acaa7 2018-05-20 stsp err = show_commit(selected_entry, repo);
876 2814baeb 2018-08-01 stsp show_panel(view->panel);
879 9343a5fb 2018-06-23 stsp err = browse_commit(selected_entry, repo);
882 2814baeb 2018-08-01 stsp show_panel(view->panel);
890 2814baeb 2018-08-01 stsp close_view(view);
891 9ba79e04 2018-06-11 stsp free(head_id);
893 9ba79e04 2018-06-11 stsp got_commit_graph_close(graph);
894 0553a4e3 2018-04-30 stsp free_commits(&commits);
895 ecb28ae0 2018-07-16 stsp free(in_repo_path);
896 80ddbec8 2018-04-29 stsp return err;
899 4ed7e80c 2018-05-20 stsp static const struct got_error *
900 9f7d7167 2018-04-29 stsp cmd_log(int argc, char *argv[])
902 80ddbec8 2018-04-29 stsp const struct got_error *error;
903 ecb28ae0 2018-07-16 stsp struct got_repository *repo = NULL;
904 899d86c2 2018-05-10 stsp struct got_object_id *start_id = NULL;
905 ecb28ae0 2018-07-16 stsp char *path = NULL, *repo_path = NULL, *cwd = NULL;
906 80ddbec8 2018-04-29 stsp char *start_commit = NULL;
909 80ddbec8 2018-04-29 stsp #ifndef PROFILE
910 80ddbec8 2018-04-29 stsp if (pledge("stdio rpath wpath cpath flock proc tty", NULL) == -1)
911 80ddbec8 2018-04-29 stsp err(1, "pledge");
914 ecb28ae0 2018-07-16 stsp while ((ch = getopt(argc, argv, "c:r:")) != -1) {
915 80ddbec8 2018-04-29 stsp switch (ch) {
917 80ddbec8 2018-04-29 stsp start_commit = optarg;
920 ecb28ae0 2018-07-16 stsp repo_path = realpath(optarg, NULL);
921 ecb28ae0 2018-07-16 stsp if (repo_path == NULL)
922 ecb28ae0 2018-07-16 stsp err(1, "-r option");
926 80ddbec8 2018-04-29 stsp /* NOTREACHED */
930 80ddbec8 2018-04-29 stsp argc -= optind;
931 80ddbec8 2018-04-29 stsp argv += optind;
933 ecb28ae0 2018-07-16 stsp if (argc == 0)
934 ecb28ae0 2018-07-16 stsp path = strdup("");
935 ecb28ae0 2018-07-16 stsp else if (argc == 1)
936 ecb28ae0 2018-07-16 stsp path = strdup(argv[0]);
938 80ddbec8 2018-04-29 stsp usage_log();
939 ecb28ae0 2018-07-16 stsp if (path == NULL)
940 ecb28ae0 2018-07-16 stsp return got_error_from_errno();
942 ecb28ae0 2018-07-16 stsp cwd = getcwd(NULL, 0);
943 ecb28ae0 2018-07-16 stsp if (cwd == NULL) {
944 ecb28ae0 2018-07-16 stsp error = got_error_from_errno();
947 ecb28ae0 2018-07-16 stsp if (repo_path == NULL) {
948 ecb28ae0 2018-07-16 stsp repo_path = strdup(cwd);
949 ecb28ae0 2018-07-16 stsp if (repo_path == NULL) {
950 ecb28ae0 2018-07-16 stsp error = got_error_from_errno();
955 80ddbec8 2018-04-29 stsp error = got_repo_open(&repo, repo_path);
956 80ddbec8 2018-04-29 stsp if (error != NULL)
959 80ddbec8 2018-04-29 stsp if (start_commit == NULL) {
960 899d86c2 2018-05-10 stsp error = get_head_commit_id(&start_id, repo);
961 80ddbec8 2018-04-29 stsp if (error != NULL)
964 80ddbec8 2018-04-29 stsp struct got_object *obj;
965 80ddbec8 2018-04-29 stsp error = got_object_open_by_id_str(&obj, repo, start_commit);
966 80ddbec8 2018-04-29 stsp if (error == NULL) {
967 899d86c2 2018-05-10 stsp start_id = got_object_get_id(obj);
968 899d86c2 2018-05-10 stsp if (start_id == NULL)
969 80ddbec8 2018-04-29 stsp error = got_error_from_errno();
973 80ddbec8 2018-04-29 stsp if (error != NULL)
976 ecb28ae0 2018-07-16 stsp error = show_log_view(start_id, repo, path);
978 ecb28ae0 2018-07-16 stsp free(repo_path);
980 ecb28ae0 2018-07-16 stsp free(path);
981 899d86c2 2018-05-10 stsp free(start_id);
983 ecb28ae0 2018-07-16 stsp got_repo_close(repo);
984 80ddbec8 2018-04-29 stsp return error;
987 4ed7e80c 2018-05-20 stsp __dead static void
988 9f7d7167 2018-04-29 stsp usage_diff(void)
991 9f7d7167 2018-04-29 stsp fprintf(stderr, "usage: %s diff [repository-path] object1 object2\n",
992 9f7d7167 2018-04-29 stsp getprogname());
996 b304db33 2018-05-20 stsp static char *
997 b304db33 2018-05-20 stsp parse_next_line(FILE *f, size_t *len)
999 b304db33 2018-05-20 stsp char *line;
1000 b304db33 2018-05-20 stsp size_t linelen;
1001 b304db33 2018-05-20 stsp size_t lineno;
1002 b304db33 2018-05-20 stsp const char delim[3] = { '\0', '\0', '\0'};
1004 b304db33 2018-05-20 stsp line = fparseln(f, &linelen, &lineno, delim, 0);
1006 b304db33 2018-05-20 stsp *len = linelen;
1007 b304db33 2018-05-20 stsp return line;
1010 4ed7e80c 2018-05-20 stsp static const struct got_error *
1011 a70480e0 2018-06-23 stsp draw_file(WINDOW *window, FILE *f, int *first_displayed_line,
1012 a70480e0 2018-06-23 stsp int *last_displayed_line, int *eof, int max_lines)
1014 61e69b96 2018-05-20 stsp const struct got_error *err;
1015 26ed57b2 2018-05-19 stsp int nlines = 0, nprinted = 0;
1016 b304db33 2018-05-20 stsp char *line;
1017 b304db33 2018-05-20 stsp size_t len;
1018 61e69b96 2018-05-20 stsp wchar_t *wline;
1019 e0b650dd 2018-05-20 stsp int width;
1021 26ed57b2 2018-05-19 stsp rewind(f);
1022 a70480e0 2018-06-23 stsp werase(window);
1025 26ed57b2 2018-05-19 stsp while (nprinted < max_lines) {
1026 b304db33 2018-05-20 stsp line = parse_next_line(f, &len);
1027 26ed57b2 2018-05-19 stsp if (line == NULL) {
1031 26ed57b2 2018-05-19 stsp if (++nlines < *first_displayed_line) {
1032 26ed57b2 2018-05-19 stsp free(line);
1036 e0b650dd 2018-05-20 stsp err = format_line(&wline, &width, line, COLS);
1037 61e69b96 2018-05-20 stsp if (err) {
1038 61e69b96 2018-05-20 stsp free(line);
1039 2550e4c3 2018-07-13 stsp free(wline);
1040 61e69b96 2018-05-20 stsp return err;
1042 a70480e0 2018-06-23 stsp waddwstr(window, wline);
1043 e0b650dd 2018-05-20 stsp if (width < COLS)
1044 a70480e0 2018-06-23 stsp waddch(window, '\n');
1045 26ed57b2 2018-05-19 stsp if (++nprinted == 1)
1046 26ed57b2 2018-05-19 stsp *first_displayed_line = nlines;
1047 26ed57b2 2018-05-19 stsp free(line);
1048 2550e4c3 2018-07-13 stsp free(wline);
1049 2550e4c3 2018-07-13 stsp wline = NULL;
1051 26ed57b2 2018-05-19 stsp *last_displayed_line = nlines;
1053 26ed57b2 2018-05-19 stsp update_panels();
1054 26ed57b2 2018-05-19 stsp doupdate();
1056 26ed57b2 2018-05-19 stsp return NULL;
1059 4ed7e80c 2018-05-20 stsp static const struct got_error *
1060 ea5e7bb5 2018-08-01 stsp show_diff_view(struct tog_view *view, struct got_object *obj1,
1061 ea5e7bb5 2018-08-01 stsp struct got_object *obj2, struct got_repository *repo)
1063 26ed57b2 2018-05-19 stsp const struct got_error *err;
1065 26ed57b2 2018-05-19 stsp int ch, done = 0, first_displayed_line = 1, last_displayed_line = LINES;
1066 b304db33 2018-05-20 stsp int eof, i;
1068 cd0acaa7 2018-05-20 stsp if (obj1 != NULL && obj2 != NULL &&
1069 cd0acaa7 2018-05-20 stsp got_object_get_type(obj1) != got_object_get_type(obj2))
1070 26ed57b2 2018-05-19 stsp return got_error(GOT_ERR_OBJ_TYPE);
1072 511a516b 2018-05-19 stsp f = got_opentemp();
1073 26ed57b2 2018-05-19 stsp if (f == NULL)
1074 26ed57b2 2018-05-19 stsp return got_error_from_errno();
1076 cd0acaa7 2018-05-20 stsp switch (got_object_get_type(obj1 ? obj1 : obj2)) {
1077 26ed57b2 2018-05-19 stsp case GOT_OBJ_TYPE_BLOB:
1078 11528a82 2018-05-19 stsp err = got_diff_objects_as_blobs(obj1, obj2, repo, f);
1080 26ed57b2 2018-05-19 stsp case GOT_OBJ_TYPE_TREE:
1081 11528a82 2018-05-19 stsp err = got_diff_objects_as_trees(obj1, obj2, repo, f);
1083 26ed57b2 2018-05-19 stsp case GOT_OBJ_TYPE_COMMIT:
1084 11528a82 2018-05-19 stsp err = got_diff_objects_as_commits(obj1, obj2, repo, f);
1087 26ed57b2 2018-05-19 stsp return got_error(GOT_ERR_OBJ_TYPE);
1090 26ed57b2 2018-05-19 stsp fflush(f);
1092 ea5e7bb5 2018-08-01 stsp show_panel(view->panel);
1094 26ed57b2 2018-05-19 stsp while (!done) {
1095 ea5e7bb5 2018-08-01 stsp err = draw_file(view->window, f, &first_displayed_line,
1096 a70480e0 2018-06-23 stsp &last_displayed_line, &eof, LINES);
1099 26ed57b2 2018-05-19 stsp nodelay(stdscr, FALSE);
1100 ea5e7bb5 2018-08-01 stsp ch = wgetch(view->window);
1101 f7182337 2018-05-20 stsp nodelay(stdscr, TRUE);
1102 26ed57b2 2018-05-19 stsp switch (ch) {
1107 26ed57b2 2018-05-19 stsp case KEY_UP:
1108 26ed57b2 2018-05-19 stsp if (first_displayed_line > 1)
1109 b304db33 2018-05-20 stsp first_displayed_line--;
1111 b304db33 2018-05-20 stsp case KEY_PPAGE:
1112 c46b9352 2018-07-12 stsp case KEY_BACKSPACE:
1114 d56caa55 2018-05-20 stsp while (i++ < LINES - 1 &&
1115 d56caa55 2018-05-20 stsp first_displayed_line > 1)
1116 26ed57b2 2018-05-19 stsp first_displayed_line--;
1119 26ed57b2 2018-05-19 stsp case KEY_DOWN:
1121 26ed57b2 2018-05-19 stsp first_displayed_line++;
1123 b304db33 2018-05-20 stsp case KEY_NPAGE:
1126 b304db33 2018-05-20 stsp while (!eof && i++ < LINES - 1) {
1127 b304db33 2018-05-20 stsp char *line = parse_next_line(f, NULL);
1128 b304db33 2018-05-20 stsp first_displayed_line++;
1129 b304db33 2018-05-20 stsp if (line == NULL)
1137 26ed57b2 2018-05-19 stsp fclose(f);
1138 26ed57b2 2018-05-19 stsp return err;
1141 4ed7e80c 2018-05-20 stsp static const struct got_error *
1142 9f7d7167 2018-04-29 stsp cmd_diff(int argc, char *argv[])
1144 26ed57b2 2018-05-19 stsp const struct got_error *error = NULL;
1145 26ed57b2 2018-05-19 stsp struct got_repository *repo = NULL;
1146 26ed57b2 2018-05-19 stsp struct got_object *obj1 = NULL, *obj2 = NULL;
1147 26ed57b2 2018-05-19 stsp char *repo_path = NULL;
1148 26ed57b2 2018-05-19 stsp char *obj_id_str1 = NULL, *obj_id_str2 = NULL;
1150 ea5e7bb5 2018-08-01 stsp struct tog_view *view;
1152 26ed57b2 2018-05-19 stsp #ifndef PROFILE
1153 26ed57b2 2018-05-19 stsp if (pledge("stdio rpath wpath cpath flock proc tty", NULL) == -1)
1154 26ed57b2 2018-05-19 stsp err(1, "pledge");
1157 26ed57b2 2018-05-19 stsp while ((ch = getopt(argc, argv, "")) != -1) {
1158 26ed57b2 2018-05-19 stsp switch (ch) {
1161 26ed57b2 2018-05-19 stsp /* NOTREACHED */
1165 26ed57b2 2018-05-19 stsp argc -= optind;
1166 26ed57b2 2018-05-19 stsp argv += optind;
1168 26ed57b2 2018-05-19 stsp if (argc == 0) {
1169 26ed57b2 2018-05-19 stsp usage_diff(); /* TODO show local worktree changes */
1170 26ed57b2 2018-05-19 stsp } else if (argc == 2) {
1171 26ed57b2 2018-05-19 stsp repo_path = getcwd(NULL, 0);
1172 26ed57b2 2018-05-19 stsp if (repo_path == NULL)
1173 26ed57b2 2018-05-19 stsp return got_error_from_errno();
1174 26ed57b2 2018-05-19 stsp obj_id_str1 = argv[0];
1175 26ed57b2 2018-05-19 stsp obj_id_str2 = argv[1];
1176 26ed57b2 2018-05-19 stsp } else if (argc == 3) {
1177 26ed57b2 2018-05-19 stsp repo_path = realpath(argv[0], NULL);
1178 26ed57b2 2018-05-19 stsp if (repo_path == NULL)
1179 26ed57b2 2018-05-19 stsp return got_error_from_errno();
1180 26ed57b2 2018-05-19 stsp obj_id_str1 = argv[1];
1181 26ed57b2 2018-05-19 stsp obj_id_str2 = argv[2];
1183 26ed57b2 2018-05-19 stsp usage_diff();
1185 26ed57b2 2018-05-19 stsp error = got_repo_open(&repo, repo_path);
1186 26ed57b2 2018-05-19 stsp free(repo_path);
1187 26ed57b2 2018-05-19 stsp if (error)
1188 26ed57b2 2018-05-19 stsp goto done;
1190 26ed57b2 2018-05-19 stsp error = got_object_open_by_id_str(&obj1, repo, obj_id_str1);
1191 26ed57b2 2018-05-19 stsp if (error)
1192 26ed57b2 2018-05-19 stsp goto done;
1194 26ed57b2 2018-05-19 stsp error = got_object_open_by_id_str(&obj2, repo, obj_id_str2);
1195 26ed57b2 2018-05-19 stsp if (error)
1196 26ed57b2 2018-05-19 stsp goto done;
1198 842167bf 2018-08-01 stsp view = open_view(0, 0, 0, 0);
1199 ea5e7bb5 2018-08-01 stsp if (view == NULL) {
1200 ea5e7bb5 2018-08-01 stsp error = got_error_from_errno();
1201 ea5e7bb5 2018-08-01 stsp goto done;
1203 ea5e7bb5 2018-08-01 stsp error = show_diff_view(view, obj1, obj2, repo);
1204 ea5e7bb5 2018-08-01 stsp close_view(view);
1206 26ed57b2 2018-05-19 stsp got_repo_close(repo);
1208 26ed57b2 2018-05-19 stsp got_object_close(obj1);
1210 26ed57b2 2018-05-19 stsp got_object_close(obj2);
1211 26ed57b2 2018-05-19 stsp return error;
1214 4ed7e80c 2018-05-20 stsp __dead static void
1215 9f7d7167 2018-04-29 stsp usage_blame(void)
1218 a70480e0 2018-06-23 stsp fprintf(stderr, "usage: %s blame [-c commit] [repository-path] path\n",
1219 9f7d7167 2018-04-29 stsp getprogname());
1223 84451b3e 2018-07-10 stsp struct tog_blame_line {
1224 84451b3e 2018-07-10 stsp int annotated;
1225 84451b3e 2018-07-10 stsp struct got_object_id *id;
1228 4ed7e80c 2018-05-20 stsp static const struct got_error *
1229 ab089a2a 2018-07-12 stsp draw_blame(WINDOW *window, struct got_object_id *id, FILE *f, const char *path,
1230 18430de3 2018-07-10 stsp struct tog_blame_line *lines, int nlines, int blame_complete,
1231 b700b5d6 2018-07-10 stsp int selected_line, int *first_displayed_line, int *last_displayed_line,
1232 18430de3 2018-07-10 stsp int *eof, int max_lines)
1234 84451b3e 2018-07-10 stsp const struct got_error *err;
1235 84451b3e 2018-07-10 stsp int lineno = 0, nprinted = 0;
1236 84451b3e 2018-07-10 stsp char *line;
1237 84451b3e 2018-07-10 stsp size_t len;
1238 84451b3e 2018-07-10 stsp wchar_t *wline;
1239 b700b5d6 2018-07-10 stsp int width, wlimit;
1240 84451b3e 2018-07-10 stsp struct tog_blame_line *blame_line;
1241 ee41ec32 2018-07-10 stsp struct got_object_id *prev_id = NULL;
1242 ab089a2a 2018-07-12 stsp char *id_str;
1244 ab089a2a 2018-07-12 stsp err = got_object_id_str(&id_str, id);
1246 ab089a2a 2018-07-12 stsp return err;
1248 84451b3e 2018-07-10 stsp rewind(f);
1249 84451b3e 2018-07-10 stsp werase(window);
1251 ab089a2a 2018-07-12 stsp if (asprintf(&line, "commit: %s", id_str) == -1) {
1252 ab089a2a 2018-07-12 stsp err = got_error_from_errno();
1253 ab089a2a 2018-07-12 stsp free(id_str);
1254 ab089a2a 2018-07-12 stsp return err;
1257 ab089a2a 2018-07-12 stsp err = format_line(&wline, &width, line, COLS);
1258 ab089a2a 2018-07-12 stsp free(line);
1259 2550e4c3 2018-07-13 stsp line = NULL;
1260 ab089a2a 2018-07-12 stsp waddwstr(window, wline);
1261 2550e4c3 2018-07-13 stsp free(wline);
1262 2550e4c3 2018-07-13 stsp wline = NULL;
1263 ab089a2a 2018-07-12 stsp if (width < COLS)
1264 ab089a2a 2018-07-12 stsp waddch(window, '\n');
1266 084063cd 2018-07-12 stsp if (asprintf(&line, "[%d/%d] %s%s",
1267 084063cd 2018-07-12 stsp *first_displayed_line - 1 + selected_line, nlines,
1268 12a0b23b 2018-07-12 stsp blame_complete ? "" : "annotating ", path) == -1) {
1269 ab089a2a 2018-07-12 stsp free(id_str);
1270 3f60a8ef 2018-07-10 stsp return got_error_from_errno();
1272 ab089a2a 2018-07-12 stsp free(id_str);
1273 3f60a8ef 2018-07-10 stsp err = format_line(&wline, &width, line, COLS);
1274 3f60a8ef 2018-07-10 stsp free(line);
1275 2550e4c3 2018-07-13 stsp line = NULL;
1277 3f60a8ef 2018-07-10 stsp return err;
1278 3f60a8ef 2018-07-10 stsp waddwstr(window, wline);
1279 2550e4c3 2018-07-13 stsp free(wline);
1280 2550e4c3 2018-07-13 stsp wline = NULL;
1281 3f60a8ef 2018-07-10 stsp if (width < COLS)
1282 3f60a8ef 2018-07-10 stsp waddch(window, '\n');
1285 ab089a2a 2018-07-12 stsp while (nprinted < max_lines - 2) {
1286 84451b3e 2018-07-10 stsp line = parse_next_line(f, &len);
1287 84451b3e 2018-07-10 stsp if (line == NULL) {
1291 84451b3e 2018-07-10 stsp if (++lineno < *first_displayed_line) {
1292 84451b3e 2018-07-10 stsp free(line);
1296 b700b5d6 2018-07-10 stsp wlimit = COLS < 9 ? 0 : COLS - 9;
1297 b700b5d6 2018-07-10 stsp err = format_line(&wline, &width, line, wlimit);
1298 84451b3e 2018-07-10 stsp if (err) {
1299 84451b3e 2018-07-10 stsp free(line);
1300 84451b3e 2018-07-10 stsp return err;
1303 b700b5d6 2018-07-10 stsp if (nprinted == selected_line - 1)
1304 b700b5d6 2018-07-10 stsp wstandout(window);
1306 84451b3e 2018-07-10 stsp blame_line = &lines[lineno - 1];
1307 ee41ec32 2018-07-10 stsp if (blame_line->annotated && prev_id &&
1308 ee41ec32 2018-07-10 stsp got_object_id_cmp(prev_id, blame_line->id) == 0)
1309 ee41ec32 2018-07-10 stsp waddstr(window, " ");
1310 ee41ec32 2018-07-10 stsp else if (blame_line->annotated) {
1311 84451b3e 2018-07-10 stsp char *id_str;
1312 84451b3e 2018-07-10 stsp err = got_object_id_str(&id_str, blame_line->id);
1313 84451b3e 2018-07-10 stsp if (err) {
1314 84451b3e 2018-07-10 stsp free(line);
1315 2550e4c3 2018-07-13 stsp free(wline);
1316 84451b3e 2018-07-10 stsp return err;
1318 84451b3e 2018-07-10 stsp wprintw(window, "%.8s ", id_str);
1319 84451b3e 2018-07-10 stsp free(id_str);
1320 ee41ec32 2018-07-10 stsp prev_id = blame_line->id;
1322 ee41ec32 2018-07-10 stsp waddstr(window, "........ ");
1323 ee41ec32 2018-07-10 stsp prev_id = NULL;
1326 84451b3e 2018-07-10 stsp waddwstr(window, wline);
1327 b700b5d6 2018-07-10 stsp while (width < wlimit) {
1328 b700b5d6 2018-07-10 stsp waddch(window, ' '); /* width == wlimit - 1 ? '\n' : ' '); */
1331 b700b5d6 2018-07-10 stsp if (nprinted == selected_line - 1)
1332 b700b5d6 2018-07-10 stsp wstandend(window);
1333 84451b3e 2018-07-10 stsp if (++nprinted == 1)
1334 84451b3e 2018-07-10 stsp *first_displayed_line = lineno;
1335 84451b3e 2018-07-10 stsp free(line);
1336 2550e4c3 2018-07-13 stsp free(wline);
1337 2550e4c3 2018-07-13 stsp wline = NULL;
1339 84451b3e 2018-07-10 stsp *last_displayed_line = lineno;
1341 84451b3e 2018-07-10 stsp update_panels();
1342 84451b3e 2018-07-10 stsp doupdate();
1344 84451b3e 2018-07-10 stsp return NULL;
1347 84451b3e 2018-07-10 stsp struct tog_blame_cb_args {
1348 84451b3e 2018-07-10 stsp pthread_mutex_t *mutex;
1349 84451b3e 2018-07-10 stsp struct tog_blame_line *lines; /* one per line */
1350 84451b3e 2018-07-10 stsp int nlines;
1352 7cc84d77 2018-08-01 stsp struct tog_view *view;
1353 ab089a2a 2018-07-12 stsp struct got_object_id *commit_id;
1355 3f60a8ef 2018-07-10 stsp const char *path;
1356 84451b3e 2018-07-10 stsp int *first_displayed_line;
1357 84451b3e 2018-07-10 stsp int *last_displayed_line;
1358 b700b5d6 2018-07-10 stsp int *selected_line;
1359 18430de3 2018-07-10 stsp int *quit;
1362 84451b3e 2018-07-10 stsp static const struct got_error *
1363 84451b3e 2018-07-10 stsp blame_cb(void *arg, int nlines, int lineno, struct got_object_id *id)
1365 84451b3e 2018-07-10 stsp const struct got_error *err = NULL;
1366 84451b3e 2018-07-10 stsp struct tog_blame_cb_args *a = arg;
1367 84451b3e 2018-07-10 stsp struct tog_blame_line *line;
1370 d68a0a7d 2018-07-10 stsp if (nlines != a->nlines ||
1371 d68a0a7d 2018-07-10 stsp (lineno != -1 && lineno < 1) || lineno > a->nlines)
1372 84451b3e 2018-07-10 stsp return got_error(GOT_ERR_RANGE);
1374 84451b3e 2018-07-10 stsp if (pthread_mutex_lock(a->mutex) != 0)
1375 84451b3e 2018-07-10 stsp return got_error_from_errno();
1377 18430de3 2018-07-10 stsp if (*a->quit) { /* user has quit the blame view */
1378 d68a0a7d 2018-07-10 stsp err = got_error(GOT_ERR_ITER_COMPLETED);
1379 d68a0a7d 2018-07-10 stsp goto done;
1382 d68a0a7d 2018-07-10 stsp if (lineno == -1)
1383 d68a0a7d 2018-07-10 stsp goto done; /* no change in this commit */
1385 84451b3e 2018-07-10 stsp line = &a->lines[lineno - 1];
1386 d68a0a7d 2018-07-10 stsp if (line->annotated)
1387 d68a0a7d 2018-07-10 stsp goto done;
1389 84451b3e 2018-07-10 stsp line->id = got_object_id_dup(id);
1390 84451b3e 2018-07-10 stsp if (line->id == NULL) {
1391 84451b3e 2018-07-10 stsp err = got_error_from_errno();
1392 84451b3e 2018-07-10 stsp goto done;
1394 84451b3e 2018-07-10 stsp line->annotated = 1;
1396 7cc84d77 2018-08-01 stsp err = draw_blame(a->view->window, a->commit_id, a->f, a->path,
1397 245d91c1 2018-07-12 stsp a->lines, a->nlines, 0, *a->selected_line, a->first_displayed_line,
1398 ab089a2a 2018-07-12 stsp a->last_displayed_line, &eof, LINES);
1400 84451b3e 2018-07-10 stsp if (pthread_mutex_unlock(a->mutex) != 0)
1401 84451b3e 2018-07-10 stsp return got_error_from_errno();
1402 84451b3e 2018-07-10 stsp return err;
1405 84451b3e 2018-07-10 stsp struct tog_blame_thread_args {
1406 84451b3e 2018-07-10 stsp const char *path;
1407 84451b3e 2018-07-10 stsp struct got_repository *repo;
1408 245d91c1 2018-07-12 stsp struct tog_blame_cb_args *cb_args;
1409 18430de3 2018-07-10 stsp int *complete;
1412 84451b3e 2018-07-10 stsp static void *
1413 84451b3e 2018-07-10 stsp blame_thread(void *arg)
1415 18430de3 2018-07-10 stsp const struct got_error *err;
1416 18430de3 2018-07-10 stsp struct tog_blame_thread_args *ta = arg;
1417 245d91c1 2018-07-12 stsp struct tog_blame_cb_args *a = ta->cb_args;
1420 ab089a2a 2018-07-12 stsp err = got_blame_incremental(ta->path, a->commit_id, ta->repo,
1421 245d91c1 2018-07-12 stsp blame_cb, ta->cb_args);
1423 18430de3 2018-07-10 stsp if (pthread_mutex_lock(a->mutex) != 0)
1424 18430de3 2018-07-10 stsp return (void *)got_error_from_errno();
1426 c9beca56 2018-07-22 stsp got_repo_close(ta->repo);
1427 c9beca56 2018-07-22 stsp ta->repo = NULL;
1428 c9beca56 2018-07-22 stsp *ta->complete = 1;
1430 7cc84d77 2018-08-01 stsp err = draw_blame(a->view->window, a->commit_id, a->f,
1431 c9beca56 2018-07-22 stsp a->path, a->lines, a->nlines, 1, *a->selected_line,
1432 c9beca56 2018-07-22 stsp a->first_displayed_line, a->last_displayed_line, &eof,
1435 18430de3 2018-07-10 stsp if (pthread_mutex_unlock(a->mutex) != 0 && err == NULL)
1436 18430de3 2018-07-10 stsp err = got_error_from_errno();
1438 18430de3 2018-07-10 stsp return (void *)err;
1441 245d91c1 2018-07-12 stsp static struct got_object_id *
1442 245d91c1 2018-07-12 stsp get_selected_commit_id(struct tog_blame_line *lines,
1443 245d91c1 2018-07-12 stsp int first_displayed_line, int selected_line)
1445 245d91c1 2018-07-12 stsp struct tog_blame_line *line;
1447 245d91c1 2018-07-12 stsp line = &lines[first_displayed_line - 1 + selected_line - 1];
1448 245d91c1 2018-07-12 stsp if (!line->annotated)
1449 245d91c1 2018-07-12 stsp return NULL;
1451 245d91c1 2018-07-12 stsp return line->id;
1454 84451b3e 2018-07-10 stsp static const struct got_error *
1455 245d91c1 2018-07-12 stsp open_selected_commit(struct got_object **pobj, struct got_object **obj,
1456 b880a918 2018-07-10 stsp struct tog_blame_line *lines, int first_displayed_line,
1457 b880a918 2018-07-10 stsp int selected_line, struct got_repository *repo)
1459 b880a918 2018-07-10 stsp const struct got_error *err = NULL;
1460 b880a918 2018-07-10 stsp struct got_commit_object *commit = NULL;
1461 245d91c1 2018-07-12 stsp struct got_object_id *selected_id;
1462 b880a918 2018-07-10 stsp struct got_object_qid *pid;
1464 b880a918 2018-07-10 stsp *pobj = NULL;
1465 b880a918 2018-07-10 stsp *obj = NULL;
1467 245d91c1 2018-07-12 stsp selected_id = get_selected_commit_id(lines,
1468 245d91c1 2018-07-12 stsp first_displayed_line, selected_line);
1469 245d91c1 2018-07-12 stsp if (selected_id == NULL)
1470 b880a918 2018-07-10 stsp return NULL;
1472 245d91c1 2018-07-12 stsp err = got_object_open(obj, repo, selected_id);
1474 b880a918 2018-07-10 stsp goto done;
1476 b880a918 2018-07-10 stsp err = got_object_commit_open(&commit, repo, *obj);
1478 b880a918 2018-07-10 stsp goto done;
1480 b880a918 2018-07-10 stsp pid = SIMPLEQ_FIRST(&commit->parent_ids);
1481 b880a918 2018-07-10 stsp if (pid) {
1482 b880a918 2018-07-10 stsp err = got_object_open(pobj, repo, pid->id);
1484 b880a918 2018-07-10 stsp goto done;
1487 b880a918 2018-07-10 stsp if (commit)
1488 b880a918 2018-07-10 stsp got_object_commit_close(commit);
1489 b880a918 2018-07-10 stsp return err;
1492 245d91c1 2018-07-12 stsp struct tog_blame {
1494 245d91c1 2018-07-12 stsp size_t filesize;
1495 245d91c1 2018-07-12 stsp struct tog_blame_line *lines;
1496 245d91c1 2018-07-12 stsp size_t nlines;
1497 245d91c1 2018-07-12 stsp pthread_t thread;
1498 245d91c1 2018-07-12 stsp struct tog_blame_thread_args thread_args;
1499 245d91c1 2018-07-12 stsp struct tog_blame_cb_args cb_args;
1500 245d91c1 2018-07-12 stsp const char *path;
1503 b880a918 2018-07-10 stsp static const struct got_error *
1504 245d91c1 2018-07-12 stsp stop_blame(struct tog_blame *blame)
1506 a70480e0 2018-06-23 stsp const struct got_error *err = NULL;
1509 245d91c1 2018-07-12 stsp if (blame->thread) {
1510 245d91c1 2018-07-12 stsp if (pthread_join(blame->thread, (void **)&err) != 0)
1511 245d91c1 2018-07-12 stsp err = got_error_from_errno();
1512 245d91c1 2018-07-12 stsp if (err && err->code == GOT_ERR_ITER_COMPLETED)
1513 245d91c1 2018-07-12 stsp err = NULL;
1514 245d91c1 2018-07-12 stsp blame->thread = NULL;
1516 245d91c1 2018-07-12 stsp if (blame->thread_args.repo) {
1517 245d91c1 2018-07-12 stsp got_repo_close(blame->thread_args.repo);
1518 245d91c1 2018-07-12 stsp blame->thread_args.repo = NULL;
1520 245d91c1 2018-07-12 stsp if (blame->f) {
1521 245d91c1 2018-07-12 stsp fclose(blame->f);
1522 245d91c1 2018-07-12 stsp blame->f = NULL;
1524 245d91c1 2018-07-12 stsp for (i = 0; i < blame->nlines; i++)
1525 245d91c1 2018-07-12 stsp free(blame->lines[i].id);
1526 245d91c1 2018-07-12 stsp free(blame->lines);
1527 245d91c1 2018-07-12 stsp blame->lines = NULL;
1528 75c32340 2018-07-23 stsp free(blame->cb_args.commit_id);
1529 75c32340 2018-07-23 stsp blame->cb_args.commit_id = NULL;
1531 245d91c1 2018-07-12 stsp return err;
1534 245d91c1 2018-07-12 stsp static const struct got_error *
1535 7cc84d77 2018-08-01 stsp run_blame(struct tog_blame *blame, pthread_mutex_t *mutex,
1536 7cc84d77 2018-08-01 stsp struct tog_view *view, int *blame_complete,
1537 245d91c1 2018-07-12 stsp int *first_displayed_line, int *last_displayed_line,
1538 245d91c1 2018-07-12 stsp int *selected_line, int *done, const char *path,
1539 245d91c1 2018-07-12 stsp struct got_object_id *commit_id,
1540 245d91c1 2018-07-12 stsp struct got_repository *repo)
1542 245d91c1 2018-07-12 stsp const struct got_error *err = NULL;
1543 84451b3e 2018-07-10 stsp struct got_blob_object *blob = NULL;
1544 245d91c1 2018-07-12 stsp struct got_repository *thread_repo = NULL;
1545 245d91c1 2018-07-12 stsp struct got_object *obj;
1547 84451b3e 2018-07-10 stsp err = got_object_open_by_path(&obj, repo, commit_id, path);
1549 84451b3e 2018-07-10 stsp goto done;
1550 84451b3e 2018-07-10 stsp if (got_object_get_type(obj) != GOT_OBJ_TYPE_BLOB) {
1551 84451b3e 2018-07-10 stsp err = got_error(GOT_ERR_OBJ_TYPE);
1552 84451b3e 2018-07-10 stsp goto done;
1555 84451b3e 2018-07-10 stsp err = got_object_blob_open(&blob, repo, obj, 8192);
1557 a70480e0 2018-06-23 stsp goto done;
1558 245d91c1 2018-07-12 stsp blame->f = got_opentemp();
1559 245d91c1 2018-07-12 stsp if (blame->f == NULL) {
1560 84451b3e 2018-07-10 stsp err = got_error_from_errno();
1561 84451b3e 2018-07-10 stsp goto done;
1563 245d91c1 2018-07-12 stsp err = got_object_blob_dump_to_file(&blame->filesize, &blame->nlines,
1564 245d91c1 2018-07-12 stsp blame->f, blob);
1566 84451b3e 2018-07-10 stsp goto done;
1568 245d91c1 2018-07-12 stsp blame->lines = calloc(blame->nlines, sizeof(*blame->lines));
1569 245d91c1 2018-07-12 stsp if (blame->lines == NULL) {
1570 84451b3e 2018-07-10 stsp err = got_error_from_errno();
1571 84451b3e 2018-07-10 stsp goto done;
1574 245d91c1 2018-07-12 stsp err = got_repo_open(&thread_repo, got_repo_get_path(repo));
1576 bd24772e 2018-07-11 stsp goto done;
1578 7cc84d77 2018-08-01 stsp blame->cb_args.view = view;
1579 245d91c1 2018-07-12 stsp blame->cb_args.lines = blame->lines;
1580 245d91c1 2018-07-12 stsp blame->cb_args.nlines = blame->nlines;
1581 245d91c1 2018-07-12 stsp blame->cb_args.mutex = mutex;
1582 245d91c1 2018-07-12 stsp blame->cb_args.commit_id = got_object_id_dup(commit_id);
1583 245d91c1 2018-07-12 stsp if (blame->cb_args.commit_id == NULL) {
1584 245d91c1 2018-07-12 stsp err = got_error_from_errno();
1585 245d91c1 2018-07-12 stsp goto done;
1587 245d91c1 2018-07-12 stsp blame->cb_args.f = blame->f;
1588 245d91c1 2018-07-12 stsp blame->cb_args.path = path;
1589 245d91c1 2018-07-12 stsp blame->cb_args.first_displayed_line = first_displayed_line;
1590 245d91c1 2018-07-12 stsp blame->cb_args.selected_line = selected_line;
1591 245d91c1 2018-07-12 stsp blame->cb_args.last_displayed_line = last_displayed_line;
1592 245d91c1 2018-07-12 stsp blame->cb_args.quit = done;
1594 245d91c1 2018-07-12 stsp blame->thread_args.path = path;
1595 245d91c1 2018-07-12 stsp blame->thread_args.repo = thread_repo;
1596 245d91c1 2018-07-12 stsp blame->thread_args.cb_args = &blame->cb_args;
1597 245d91c1 2018-07-12 stsp blame->thread_args.complete = blame_complete;
1598 245d91c1 2018-07-12 stsp *blame_complete = 0;
1600 245d91c1 2018-07-12 stsp if (pthread_create(&blame->thread, NULL, blame_thread,
1601 245d91c1 2018-07-12 stsp &blame->thread_args) != 0) {
1602 245d91c1 2018-07-12 stsp err = got_error_from_errno();
1603 245d91c1 2018-07-12 stsp goto done;
1608 245d91c1 2018-07-12 stsp got_object_blob_close(blob);
1610 245d91c1 2018-07-12 stsp got_object_close(obj);
1612 245d91c1 2018-07-12 stsp stop_blame(blame);
1613 245d91c1 2018-07-12 stsp return err;
1616 245d91c1 2018-07-12 stsp static const struct got_error *
1617 245d91c1 2018-07-12 stsp show_blame_view(const char *path, struct got_object_id *commit_id,
1618 245d91c1 2018-07-12 stsp struct got_repository *repo)
1620 245d91c1 2018-07-12 stsp const struct got_error *err = NULL, *thread_err = NULL;
1621 245d91c1 2018-07-12 stsp int ch, done = 0, first_displayed_line = 1, last_displayed_line = LINES;
1622 245d91c1 2018-07-12 stsp int selected_line = first_displayed_line;
1623 245d91c1 2018-07-12 stsp int eof, blame_complete = 0;
1624 245d91c1 2018-07-12 stsp struct got_object *obj = NULL, *pobj = NULL;
1625 245d91c1 2018-07-12 stsp pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
1626 245d91c1 2018-07-12 stsp struct tog_blame blame;
1627 dbc6a6b6 2018-07-12 stsp struct got_object_id_queue blamed_commits;
1628 dbc6a6b6 2018-07-12 stsp struct got_object_qid *blamed_commit = NULL;
1629 7cc84d77 2018-08-01 stsp struct tog_view *view = NULL, *diff_view;
1631 dbc6a6b6 2018-07-12 stsp SIMPLEQ_INIT(&blamed_commits);
1633 245d91c1 2018-07-12 stsp if (pthread_mutex_init(&mutex, NULL) != 0) {
1634 245d91c1 2018-07-12 stsp err = got_error_from_errno();
1635 245d91c1 2018-07-12 stsp goto done;
1638 dbc6a6b6 2018-07-12 stsp err = got_object_qid_alloc(&blamed_commit, commit_id);
1640 245d91c1 2018-07-12 stsp goto done;
1641 dbc6a6b6 2018-07-12 stsp SIMPLEQ_INSERT_HEAD(&blamed_commits, blamed_commit, entry);
1643 842167bf 2018-08-01 stsp view = open_view(0, 0, 0, 0);
1644 7cc84d77 2018-08-01 stsp if (view == NULL) {
1645 7cc84d77 2018-08-01 stsp err = got_error_from_errno();
1646 7cc84d77 2018-08-01 stsp goto done;
1648 7cc84d77 2018-08-01 stsp show_panel(view->panel);
1650 245d91c1 2018-07-12 stsp memset(&blame, 0, sizeof(blame));
1651 7cc84d77 2018-08-01 stsp err = run_blame(&blame, &mutex, view, &blame_complete,
1652 245d91c1 2018-07-12 stsp &first_displayed_line, &last_displayed_line,
1653 dbc6a6b6 2018-07-12 stsp &selected_line, &done, path, blamed_commit->id, repo);
1655 245d91c1 2018-07-12 stsp return err;
1657 a70480e0 2018-06-23 stsp while (!done) {
1658 84451b3e 2018-07-10 stsp if (pthread_mutex_lock(&mutex) != 0) {
1659 84451b3e 2018-07-10 stsp err = got_error_from_errno();
1660 84451b3e 2018-07-10 stsp goto done;
1662 7cc84d77 2018-08-01 stsp err = draw_blame(view->window, blamed_commit->id,
1663 245d91c1 2018-07-12 stsp blame.f, path, blame.lines, blame.nlines, blame_complete,
1664 245d91c1 2018-07-12 stsp selected_line, &first_displayed_line, &last_displayed_line,
1665 245d91c1 2018-07-12 stsp &eof, LINES);
1666 84451b3e 2018-07-10 stsp if (pthread_mutex_unlock(&mutex) != 0) {
1667 84451b3e 2018-07-10 stsp err = got_error_from_errno();
1668 84451b3e 2018-07-10 stsp goto done;
1672 a70480e0 2018-06-23 stsp nodelay(stdscr, FALSE);
1673 7cc84d77 2018-08-01 stsp ch = wgetch(view->window);
1674 a70480e0 2018-06-23 stsp nodelay(stdscr, TRUE);
1675 84451b3e 2018-07-10 stsp if (pthread_mutex_lock(&mutex) != 0) {
1676 84451b3e 2018-07-10 stsp err = got_error_from_errno();
1677 84451b3e 2018-07-10 stsp goto done;
1679 a70480e0 2018-06-23 stsp switch (ch) {
1684 a70480e0 2018-06-23 stsp case KEY_UP:
1685 b700b5d6 2018-07-10 stsp if (selected_line > 1)
1686 b700b5d6 2018-07-10 stsp selected_line--;
1687 b700b5d6 2018-07-10 stsp else if (selected_line == 1 &&
1688 b700b5d6 2018-07-10 stsp first_displayed_line > 1)
1689 a70480e0 2018-06-23 stsp first_displayed_line--;
1691 a70480e0 2018-06-23 stsp case KEY_PPAGE:
1692 38f94530 2018-07-12 stsp case KEY_BACKSPACE:
1693 b700b5d6 2018-07-10 stsp if (first_displayed_line == 1) {
1694 b700b5d6 2018-07-10 stsp selected_line = 1;
1697 084063cd 2018-07-12 stsp if (first_displayed_line > LINES - 2)
1698 084063cd 2018-07-12 stsp first_displayed_line -= (LINES - 2);
1700 b700b5d6 2018-07-10 stsp first_displayed_line = 1;
1703 a70480e0 2018-06-23 stsp case KEY_DOWN:
1704 a026b947 2018-07-12 stsp if (selected_line < LINES - 2 &&
1705 a026b947 2018-07-12 stsp first_displayed_line + selected_line <=
1706 a026b947 2018-07-12 stsp blame.nlines)
1707 b700b5d6 2018-07-10 stsp selected_line++;
1708 245d91c1 2018-07-12 stsp else if (last_displayed_line < blame.nlines)
1709 b700b5d6 2018-07-10 stsp first_displayed_line++;
1712 7a2921f9 2018-07-12 stsp case 'p': {
1713 245d91c1 2018-07-12 stsp struct got_object_id *id;
1714 245d91c1 2018-07-12 stsp id = get_selected_commit_id(blame.lines,
1715 245d91c1 2018-07-12 stsp first_displayed_line, selected_line);
1716 245d91c1 2018-07-12 stsp if (id == NULL || got_object_id_cmp(id,
1717 dbc6a6b6 2018-07-12 stsp blamed_commit->id) == 0)
1719 245d91c1 2018-07-12 stsp err = open_selected_commit(&pobj, &obj,
1720 245d91c1 2018-07-12 stsp blame.lines, first_displayed_line,
1721 245d91c1 2018-07-12 stsp selected_line, repo);
1724 245d91c1 2018-07-12 stsp if (pobj == NULL && obj == NULL)
1726 7a2921f9 2018-07-12 stsp if (ch == 'p' && pobj == NULL)
1729 245d91c1 2018-07-12 stsp if (pthread_mutex_unlock(&mutex) != 0) {
1730 245d91c1 2018-07-12 stsp err = got_error_from_errno();
1731 ad6a6c43 2018-07-10 stsp goto done;
1733 245d91c1 2018-07-12 stsp thread_err = stop_blame(&blame);
1735 245d91c1 2018-07-12 stsp if (pthread_mutex_lock(&mutex) != 0) {
1736 245d91c1 2018-07-12 stsp err = got_error_from_errno();
1737 245d91c1 2018-07-12 stsp goto done;
1739 245d91c1 2018-07-12 stsp if (thread_err)
1741 7a2921f9 2018-07-12 stsp id = got_object_get_id(ch == 'b' ? obj : pobj);
1742 1960a6f9 2018-07-12 stsp got_object_close(obj);
1743 1960a6f9 2018-07-12 stsp obj = NULL;
1744 1960a6f9 2018-07-12 stsp if (pobj) {
1745 1960a6f9 2018-07-12 stsp got_object_close(pobj);
1746 14437fb1 2018-07-13 stsp pobj = NULL;
1748 dbc6a6b6 2018-07-12 stsp if (id == NULL) {
1749 245d91c1 2018-07-12 stsp err = got_error_from_errno();
1752 dbc6a6b6 2018-07-12 stsp err = got_object_qid_alloc(&blamed_commit, id);
1755 dbc6a6b6 2018-07-12 stsp goto done;
1756 dbc6a6b6 2018-07-12 stsp SIMPLEQ_INSERT_HEAD(&blamed_commits,
1757 dbc6a6b6 2018-07-12 stsp blamed_commit, entry);
1758 7cc84d77 2018-08-01 stsp err = run_blame(&blame, &mutex, view,
1759 245d91c1 2018-07-12 stsp &blame_complete, &first_displayed_line,
1760 245d91c1 2018-07-12 stsp &last_displayed_line, &selected_line,
1761 dbc6a6b6 2018-07-12 stsp &done, path, blamed_commit->id, repo);
1766 dbc6a6b6 2018-07-12 stsp case 'B': {
1767 dbc6a6b6 2018-07-12 stsp struct got_object_qid *first;
1768 dbc6a6b6 2018-07-12 stsp first = SIMPLEQ_FIRST(&blamed_commits);
1769 dbc6a6b6 2018-07-12 stsp if (!got_object_id_cmp(first->id, commit_id))
1772 dbc6a6b6 2018-07-12 stsp if (pthread_mutex_unlock(&mutex) != 0) {
1773 dbc6a6b6 2018-07-12 stsp err = got_error_from_errno();
1774 dbc6a6b6 2018-07-12 stsp goto done;
1776 dbc6a6b6 2018-07-12 stsp thread_err = stop_blame(&blame);
1778 dbc6a6b6 2018-07-12 stsp if (pthread_mutex_lock(&mutex) != 0) {
1779 dbc6a6b6 2018-07-12 stsp err = got_error_from_errno();
1780 dbc6a6b6 2018-07-12 stsp goto done;
1782 dbc6a6b6 2018-07-12 stsp if (thread_err)
1784 dbc6a6b6 2018-07-12 stsp SIMPLEQ_REMOVE_HEAD(&blamed_commits, entry);
1785 dbc6a6b6 2018-07-12 stsp got_object_qid_free(blamed_commit);
1786 dbc6a6b6 2018-07-12 stsp blamed_commit = SIMPLEQ_FIRST(&blamed_commits);
1787 7cc84d77 2018-08-01 stsp err = run_blame(&blame, &mutex, view,
1788 dbc6a6b6 2018-07-12 stsp &blame_complete, &first_displayed_line,
1789 dbc6a6b6 2018-07-12 stsp &last_displayed_line, &selected_line,
1790 dbc6a6b6 2018-07-12 stsp &done, path, blamed_commit->id, repo);
1795 245d91c1 2018-07-12 stsp case KEY_ENTER:
1796 245d91c1 2018-07-12 stsp case '\r':
1797 245d91c1 2018-07-12 stsp err = open_selected_commit(&pobj, &obj,
1798 245d91c1 2018-07-12 stsp blame.lines, first_displayed_line,
1799 245d91c1 2018-07-12 stsp selected_line, repo);
1802 b880a918 2018-07-10 stsp if (pobj == NULL && obj == NULL)
1804 842167bf 2018-08-01 stsp diff_view = open_view(0, 0, 0, 0);
1805 ea5e7bb5 2018-08-01 stsp if (diff_view == NULL) {
1806 ea5e7bb5 2018-08-01 stsp err = got_error_from_errno();
1809 ea5e7bb5 2018-08-01 stsp err = show_diff_view(diff_view, pobj, obj, repo);
1810 ea5e7bb5 2018-08-01 stsp close_view(diff_view);
1811 b880a918 2018-07-10 stsp if (pobj) {
1812 b880a918 2018-07-10 stsp got_object_close(pobj);
1813 b880a918 2018-07-10 stsp pobj = NULL;
1815 b880a918 2018-07-10 stsp got_object_close(obj);
1816 b880a918 2018-07-10 stsp obj = NULL;
1817 7cc84d77 2018-08-01 stsp show_panel(view->panel);
1821 a70480e0 2018-06-23 stsp case KEY_NPAGE:
1823 245d91c1 2018-07-12 stsp if (last_displayed_line >= blame.nlines &&
1824 084063cd 2018-07-12 stsp selected_line < LINES - 2) {
1825 d2dfcfbf 2018-07-12 stsp selected_line = MIN(blame.nlines,
1826 d2dfcfbf 2018-07-12 stsp LINES - 2);
1829 245d91c1 2018-07-12 stsp if (last_displayed_line + LINES - 2 <=
1830 245d91c1 2018-07-12 stsp blame.nlines)
1831 084063cd 2018-07-12 stsp first_displayed_line += LINES - 2;
1833 b700b5d6 2018-07-10 stsp first_displayed_line =
1834 245d91c1 2018-07-12 stsp blame.nlines - (LINES - 3);
1839 245d91c1 2018-07-12 stsp if (pthread_mutex_unlock(&mutex) != 0)
1840 84451b3e 2018-07-10 stsp err = got_error_from_errno();
1841 245d91c1 2018-07-12 stsp if (err || thread_err)
1846 b880a918 2018-07-10 stsp got_object_close(pobj);
1847 c9beca56 2018-07-22 stsp if (blame.thread)
1848 245d91c1 2018-07-12 stsp thread_err = stop_blame(&blame);
1850 7cc84d77 2018-08-01 stsp close_view(view);
1851 dbc6a6b6 2018-07-12 stsp while (!SIMPLEQ_EMPTY(&blamed_commits)) {
1852 dbc6a6b6 2018-07-12 stsp blamed_commit = SIMPLEQ_FIRST(&blamed_commits);
1853 dbc6a6b6 2018-07-12 stsp SIMPLEQ_REMOVE_HEAD(&blamed_commits, entry);
1854 dbc6a6b6 2018-07-12 stsp got_object_qid_free(blamed_commit);
1856 245d91c1 2018-07-12 stsp return thread_err ? thread_err : err;
1859 a70480e0 2018-06-23 stsp static const struct got_error *
1860 9f7d7167 2018-04-29 stsp cmd_blame(int argc, char *argv[])
1862 a70480e0 2018-06-23 stsp const struct got_error *error;
1863 a70480e0 2018-06-23 stsp struct got_repository *repo = NULL;
1864 a70480e0 2018-06-23 stsp char *repo_path = NULL;
1865 a70480e0 2018-06-23 stsp char *path = NULL;
1866 a70480e0 2018-06-23 stsp struct got_object_id *commit_id = NULL;
1867 a70480e0 2018-06-23 stsp char *commit_id_str = NULL;
1870 a70480e0 2018-06-23 stsp #ifndef PROFILE
1871 a70480e0 2018-06-23 stsp if (pledge("stdio rpath wpath cpath flock proc tty", NULL) == -1)
1872 a70480e0 2018-06-23 stsp err(1, "pledge");
1875 a70480e0 2018-06-23 stsp while ((ch = getopt(argc, argv, "c:")) != -1) {
1876 a70480e0 2018-06-23 stsp switch (ch) {
1878 a70480e0 2018-06-23 stsp commit_id_str = optarg;
1882 a70480e0 2018-06-23 stsp /* NOTREACHED */
1886 a70480e0 2018-06-23 stsp argc -= optind;
1887 a70480e0 2018-06-23 stsp argv += optind;
1889 a70480e0 2018-06-23 stsp if (argc == 0) {
1890 a70480e0 2018-06-23 stsp usage_blame();
1891 a70480e0 2018-06-23 stsp } else if (argc == 1) {
1892 a70480e0 2018-06-23 stsp repo_path = getcwd(NULL, 0);
1893 a70480e0 2018-06-23 stsp if (repo_path == NULL)
1894 a70480e0 2018-06-23 stsp return got_error_from_errno();
1895 a70480e0 2018-06-23 stsp path = argv[0];
1896 a70480e0 2018-06-23 stsp } else if (argc == 2) {
1897 a70480e0 2018-06-23 stsp repo_path = realpath(argv[0], NULL);
1898 a70480e0 2018-06-23 stsp if (repo_path == NULL)
1899 a70480e0 2018-06-23 stsp return got_error_from_errno();
1900 a70480e0 2018-06-23 stsp path = argv[1];
1902 a70480e0 2018-06-23 stsp usage_blame();
1904 a70480e0 2018-06-23 stsp error = got_repo_open(&repo, repo_path);
1905 a70480e0 2018-06-23 stsp free(repo_path);
1906 a70480e0 2018-06-23 stsp if (error != NULL)
1907 66b4983c 2018-06-23 stsp return error;
1909 a70480e0 2018-06-23 stsp if (commit_id_str == NULL) {
1910 a70480e0 2018-06-23 stsp struct got_reference *head_ref;
1911 a70480e0 2018-06-23 stsp error = got_ref_open(&head_ref, repo, GOT_REF_HEAD);
1912 a70480e0 2018-06-23 stsp if (error != NULL)
1913 66b4983c 2018-06-23 stsp goto done;
1914 a70480e0 2018-06-23 stsp error = got_ref_resolve(&commit_id, repo, head_ref);
1915 a70480e0 2018-06-23 stsp got_ref_close(head_ref);
1917 a70480e0 2018-06-23 stsp struct got_object *obj;
1918 a70480e0 2018-06-23 stsp error = got_object_open_by_id_str(&obj, repo, commit_id_str);
1919 a70480e0 2018-06-23 stsp if (error != NULL)
1920 66b4983c 2018-06-23 stsp goto done;
1921 a70480e0 2018-06-23 stsp commit_id = got_object_get_id(obj);
1922 a19e88aa 2018-06-23 stsp if (commit_id == NULL)
1923 66b4983c 2018-06-23 stsp error = got_error_from_errno();
1924 a19e88aa 2018-06-23 stsp got_object_close(obj);
1926 a19e88aa 2018-06-23 stsp if (error != NULL)
1927 a19e88aa 2018-06-23 stsp goto done;
1929 a70480e0 2018-06-23 stsp error = show_blame_view(path, commit_id, repo);
1931 a70480e0 2018-06-23 stsp free(commit_id);
1933 a70480e0 2018-06-23 stsp got_repo_close(repo);
1934 a70480e0 2018-06-23 stsp return error;
1937 ffd1d5e5 2018-06-23 stsp static const struct got_error *
1938 ffd1d5e5 2018-06-23 stsp draw_tree_entries(struct got_tree_entry **first_displayed_entry,
1939 ffd1d5e5 2018-06-23 stsp struct got_tree_entry **last_displayed_entry,
1940 ffd1d5e5 2018-06-23 stsp struct got_tree_entry **selected_entry, int *ndisplayed,
1941 1d13200f 2018-07-12 stsp WINDOW *window, const char *label, int show_ids, const char *parent_path,
1942 ce52c690 2018-06-23 stsp const struct got_tree_entries *entries, int selected, int limit, int isroot)
1944 ffd1d5e5 2018-06-23 stsp const struct got_error *err = NULL;
1945 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *te;
1946 ffd1d5e5 2018-06-23 stsp wchar_t *wline;
1947 ffd1d5e5 2018-06-23 stsp int width, n;
1949 ffd1d5e5 2018-06-23 stsp *ndisplayed = 0;
1951 ffd1d5e5 2018-06-23 stsp werase(window);
1953 ffd1d5e5 2018-06-23 stsp if (limit == 0)
1954 ffd1d5e5 2018-06-23 stsp return NULL;
1956 ffd1d5e5 2018-06-23 stsp err = format_line(&wline, &width, label, COLS);
1958 ffd1d5e5 2018-06-23 stsp return err;
1959 ffd1d5e5 2018-06-23 stsp waddwstr(window, wline);
1960 2550e4c3 2018-07-13 stsp free(wline);
1961 2550e4c3 2018-07-13 stsp wline = NULL;
1962 ffd1d5e5 2018-06-23 stsp if (width < COLS)
1963 ffd1d5e5 2018-06-23 stsp waddch(window, '\n');
1964 ffd1d5e5 2018-06-23 stsp if (--limit <= 0)
1965 ffd1d5e5 2018-06-23 stsp return NULL;
1966 ce52c690 2018-06-23 stsp err = format_line(&wline, &width, parent_path, COLS);
1968 ce52c690 2018-06-23 stsp return err;
1969 ce52c690 2018-06-23 stsp waddwstr(window, wline);
1970 2550e4c3 2018-07-13 stsp free(wline);
1971 2550e4c3 2018-07-13 stsp wline = NULL;
1972 ce52c690 2018-06-23 stsp if (width < COLS)
1973 ce52c690 2018-06-23 stsp waddch(window, '\n');
1974 ffd1d5e5 2018-06-23 stsp if (--limit <= 0)
1975 ffd1d5e5 2018-06-23 stsp return NULL;
1976 a1eca9bb 2018-06-23 stsp waddch(window, '\n');
1977 a1eca9bb 2018-06-23 stsp if (--limit <= 0)
1978 a1eca9bb 2018-06-23 stsp return NULL;
1980 ffd1d5e5 2018-06-23 stsp te = SIMPLEQ_FIRST(&entries->head);
1981 ffd1d5e5 2018-06-23 stsp if (*first_displayed_entry == NULL) {
1982 ffd1d5e5 2018-06-23 stsp if (selected == 0) {
1983 ffd1d5e5 2018-06-23 stsp wstandout(window);
1984 ffd1d5e5 2018-06-23 stsp *selected_entry = NULL;
1986 ffd1d5e5 2018-06-23 stsp waddstr(window, " ..\n"); /* parent directory */
1987 ffd1d5e5 2018-06-23 stsp if (selected == 0)
1988 ffd1d5e5 2018-06-23 stsp wstandend(window);
1989 ffd1d5e5 2018-06-23 stsp (*ndisplayed)++;
1990 ffd1d5e5 2018-06-23 stsp if (--limit <= 0)
1991 ffd1d5e5 2018-06-23 stsp return NULL;
1995 ffd1d5e5 2018-06-23 stsp while (te != *first_displayed_entry)
1996 ffd1d5e5 2018-06-23 stsp te = SIMPLEQ_NEXT(te, entry);
1999 ffd1d5e5 2018-06-23 stsp while (te) {
2000 1d13200f 2018-07-12 stsp char *line = NULL, *id_str = NULL;
2002 1d13200f 2018-07-12 stsp if (show_ids) {
2003 1d13200f 2018-07-12 stsp err = got_object_id_str(&id_str, te->id);
2005 1d13200f 2018-07-12 stsp return got_error_from_errno();
2007 1d13200f 2018-07-12 stsp if (asprintf(&line, "%s %s%s", id_str ? id_str : "",
2008 1d13200f 2018-07-12 stsp te->name, S_ISDIR(te->mode) ? "/" : "") == -1) {
2009 1d13200f 2018-07-12 stsp free(id_str);
2010 ffd1d5e5 2018-06-23 stsp return got_error_from_errno();
2012 1d13200f 2018-07-12 stsp free(id_str);
2013 ffd1d5e5 2018-06-23 stsp err = format_line(&wline, &width, line, COLS);
2014 ffd1d5e5 2018-06-23 stsp if (err) {
2015 ffd1d5e5 2018-06-23 stsp free(line);
2018 ffd1d5e5 2018-06-23 stsp if (n == selected) {
2019 ffd1d5e5 2018-06-23 stsp wstandout(window);
2020 ffd1d5e5 2018-06-23 stsp *selected_entry = te;
2022 ffd1d5e5 2018-06-23 stsp waddwstr(window, wline);
2023 ffd1d5e5 2018-06-23 stsp if (width < COLS)
2024 ffd1d5e5 2018-06-23 stsp waddch(window, '\n');
2025 ffd1d5e5 2018-06-23 stsp if (n == selected)
2026 ffd1d5e5 2018-06-23 stsp wstandend(window);
2027 ffd1d5e5 2018-06-23 stsp free(line);
2028 2550e4c3 2018-07-13 stsp free(wline);
2029 2550e4c3 2018-07-13 stsp wline = NULL;
2031 ffd1d5e5 2018-06-23 stsp (*ndisplayed)++;
2032 ffd1d5e5 2018-06-23 stsp *last_displayed_entry = te;
2033 ffd1d5e5 2018-06-23 stsp if (--limit <= 0)
2035 ffd1d5e5 2018-06-23 stsp te = SIMPLEQ_NEXT(te, entry);
2038 ffd1d5e5 2018-06-23 stsp return err;
2041 ffd1d5e5 2018-06-23 stsp static void
2042 ffd1d5e5 2018-06-23 stsp tree_scroll_up(struct got_tree_entry **first_displayed_entry, int maxscroll,
2043 ffd1d5e5 2018-06-23 stsp const struct got_tree_entries *entries, int isroot)
2045 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *te, *prev;
2048 ffd1d5e5 2018-06-23 stsp if (*first_displayed_entry == NULL)
2051 ffd1d5e5 2018-06-23 stsp te = SIMPLEQ_FIRST(&entries->head);
2052 ffd1d5e5 2018-06-23 stsp if (*first_displayed_entry == te) {
2053 ffd1d5e5 2018-06-23 stsp if (!isroot)
2054 ffd1d5e5 2018-06-23 stsp *first_displayed_entry = NULL;
2058 ffd1d5e5 2018-06-23 stsp /* XXX this is stupid... switch to TAILQ? */
2059 ffd1d5e5 2018-06-23 stsp for (i = 0; i < maxscroll; i++) {
2060 ffd1d5e5 2018-06-23 stsp while (te != *first_displayed_entry) {
2061 ffd1d5e5 2018-06-23 stsp prev = te;
2062 ffd1d5e5 2018-06-23 stsp te = SIMPLEQ_NEXT(te, entry);
2064 ffd1d5e5 2018-06-23 stsp *first_displayed_entry = prev;
2065 ffd1d5e5 2018-06-23 stsp te = SIMPLEQ_FIRST(&entries->head);
2067 ffd1d5e5 2018-06-23 stsp if (!isroot && te == SIMPLEQ_FIRST(&entries->head) && i < maxscroll)
2068 ffd1d5e5 2018-06-23 stsp *first_displayed_entry = NULL;
2071 ffd1d5e5 2018-06-23 stsp static void
2072 ffd1d5e5 2018-06-23 stsp tree_scroll_down(struct got_tree_entry **first_displayed_entry, int maxscroll,
2073 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *last_displayed_entry,
2074 ffd1d5e5 2018-06-23 stsp const struct got_tree_entries *entries)
2076 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *next;
2077 ffd1d5e5 2018-06-23 stsp int n = 0;
2079 ffd1d5e5 2018-06-23 stsp if (SIMPLEQ_NEXT(last_displayed_entry, entry) == NULL)
2082 ffd1d5e5 2018-06-23 stsp if (*first_displayed_entry)
2083 ffd1d5e5 2018-06-23 stsp next = SIMPLEQ_NEXT(*first_displayed_entry, entry);
2085 ffd1d5e5 2018-06-23 stsp next = SIMPLEQ_FIRST(&entries->head);
2086 ffd1d5e5 2018-06-23 stsp while (next) {
2087 ffd1d5e5 2018-06-23 stsp *first_displayed_entry = next;
2088 ffd1d5e5 2018-06-23 stsp if (++n >= maxscroll)
2090 ffd1d5e5 2018-06-23 stsp next = SIMPLEQ_NEXT(next, entry);
2094 ffd1d5e5 2018-06-23 stsp struct tog_parent_tree {
2095 d9765a41 2018-06-23 stsp TAILQ_ENTRY(tog_parent_tree) entry;
2096 ffd1d5e5 2018-06-23 stsp struct got_tree_object *tree;
2097 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *first_displayed_entry;
2098 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *selected_entry;
2099 ffd1d5e5 2018-06-23 stsp int selected;
2102 d9765a41 2018-06-23 stsp TAILQ_HEAD(tog_parent_trees, tog_parent_tree);
2104 ffd1d5e5 2018-06-23 stsp static const struct got_error *
2105 ce52c690 2018-06-23 stsp tree_entry_path(char **path, struct tog_parent_trees *parents,
2106 ce52c690 2018-06-23 stsp struct got_tree_entry *te)
2108 cb2ebc8a 2018-06-23 stsp const struct got_error *err = NULL;
2109 ffd1d5e5 2018-06-23 stsp struct tog_parent_tree *pt;
2110 ffd1d5e5 2018-06-23 stsp size_t len = 2; /* for leading slash and NUL */
2112 d9765a41 2018-06-23 stsp TAILQ_FOREACH(pt, parents, entry)
2113 ffd1d5e5 2018-06-23 stsp len += strlen(pt->selected_entry->name) + 1 /* slash */;
2115 ce52c690 2018-06-23 stsp len += strlen(te->name);
2117 ce52c690 2018-06-23 stsp *path = calloc(1, len);
2118 ffd1d5e5 2018-06-23 stsp if (path == NULL)
2119 ffd1d5e5 2018-06-23 stsp return got_error_from_errno();
2121 ce52c690 2018-06-23 stsp (*path)[0] = '/';
2122 d9765a41 2018-06-23 stsp pt = TAILQ_LAST(parents, tog_parent_trees);
2123 d9765a41 2018-06-23 stsp while (pt) {
2124 ce52c690 2018-06-23 stsp if (strlcat(*path, pt->selected_entry->name, len) >= len) {
2125 cb2ebc8a 2018-06-23 stsp err = got_error(GOT_ERR_NO_SPACE);
2126 cb2ebc8a 2018-06-23 stsp goto done;
2128 ce52c690 2018-06-23 stsp if (strlcat(*path, "/", len) >= len) {
2129 cb2ebc8a 2018-06-23 stsp err = got_error(GOT_ERR_NO_SPACE);
2130 cb2ebc8a 2018-06-23 stsp goto done;
2132 d9765a41 2018-06-23 stsp pt = TAILQ_PREV(pt, tog_parent_trees, entry);
2135 ce52c690 2018-06-23 stsp if (strlcat(*path, te->name, len) >= len) {
2136 ce52c690 2018-06-23 stsp err = got_error(GOT_ERR_NO_SPACE);
2137 ce52c690 2018-06-23 stsp goto done;
2141 ce52c690 2018-06-23 stsp if (err) {
2142 ce52c690 2018-06-23 stsp free(*path);
2143 ce52c690 2018-06-23 stsp *path = NULL;
2145 ce52c690 2018-06-23 stsp return err;
2148 ce52c690 2018-06-23 stsp static const struct got_error *
2149 ce52c690 2018-06-23 stsp blame_tree_entry(struct got_tree_entry *te, struct tog_parent_trees *parents,
2150 ce52c690 2018-06-23 stsp struct got_object_id *commit_id, struct got_repository *repo)
2152 ce52c690 2018-06-23 stsp const struct got_error *err = NULL;
2153 ce52c690 2018-06-23 stsp char *path;
2155 ce52c690 2018-06-23 stsp err = tree_entry_path(&path, parents, te);
2157 ce52c690 2018-06-23 stsp return err;
2159 cb2ebc8a 2018-06-23 stsp err = show_blame_view(path, commit_id, repo);
2160 69efd4c4 2018-07-18 stsp free(path);
2161 69efd4c4 2018-07-18 stsp return err;
2164 69efd4c4 2018-07-18 stsp static const struct got_error *
2165 69efd4c4 2018-07-18 stsp log_tree_entry(struct got_tree_entry *te, struct tog_parent_trees *parents,
2166 69efd4c4 2018-07-18 stsp struct got_object_id *commit_id, struct got_repository *repo)
2168 69efd4c4 2018-07-18 stsp const struct got_error *err = NULL;
2169 69efd4c4 2018-07-18 stsp char *path;
2171 69efd4c4 2018-07-18 stsp err = tree_entry_path(&path, parents, te);
2173 69efd4c4 2018-07-18 stsp return err;
2175 69efd4c4 2018-07-18 stsp err = show_log_view(commit_id, repo, path);
2176 cb2ebc8a 2018-06-23 stsp free(path);
2177 cb2ebc8a 2018-06-23 stsp return err;
2180 ffd1d5e5 2018-06-23 stsp static const struct got_error *
2181 ffd1d5e5 2018-06-23 stsp show_tree_view(struct got_tree_object *root, struct got_object_id *commit_id,
2182 ffd1d5e5 2018-06-23 stsp struct got_repository *repo)
2184 ffd1d5e5 2018-06-23 stsp const struct got_error *err = NULL;
2185 1d13200f 2018-07-12 stsp int ch, done = 0, selected = 0, show_ids = 0;
2186 ffd1d5e5 2018-06-23 stsp struct got_tree_object *tree = root;
2187 ffd1d5e5 2018-06-23 stsp const struct got_tree_entries *entries;
2188 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *first_displayed_entry = NULL;
2189 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *last_displayed_entry = NULL;
2190 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *selected_entry = NULL;
2191 ffd1d5e5 2018-06-23 stsp char *commit_id_str = NULL, *tree_label = NULL;
2192 ffd1d5e5 2018-06-23 stsp int nentries, ndisplayed;
2193 ffd1d5e5 2018-06-23 stsp struct tog_parent_trees parents;
2194 cc3c9aac 2018-08-01 stsp struct tog_view *view = NULL;
2196 d9765a41 2018-06-23 stsp TAILQ_INIT(&parents);
2198 ffd1d5e5 2018-06-23 stsp err = got_object_id_str(&commit_id_str, commit_id);
2199 ffd1d5e5 2018-06-23 stsp if (err != NULL)
2200 ffd1d5e5 2018-06-23 stsp goto done;
2202 decd3bd1 2018-07-12 stsp if (asprintf(&tree_label, "commit: %s", commit_id_str) == -1) {
2203 ffd1d5e5 2018-06-23 stsp err = got_error_from_errno();
2204 ffd1d5e5 2018-06-23 stsp goto done;
2207 842167bf 2018-08-01 stsp view = open_view(0, 0, 0, 0);
2208 cc3c9aac 2018-08-01 stsp if (view == NULL) {
2209 cc3c9aac 2018-08-01 stsp err = got_error_from_errno();
2210 cc3c9aac 2018-08-01 stsp goto done;
2212 cc3c9aac 2018-08-01 stsp show_panel(view->panel);
2214 ffd1d5e5 2018-06-23 stsp entries = got_object_tree_get_entries(root);
2215 ffd1d5e5 2018-06-23 stsp first_displayed_entry = SIMPLEQ_FIRST(&entries->head);
2216 ffd1d5e5 2018-06-23 stsp while (!done) {
2217 ce52c690 2018-06-23 stsp char *parent_path;
2218 ffd1d5e5 2018-06-23 stsp entries = got_object_tree_get_entries(tree);
2219 ffd1d5e5 2018-06-23 stsp nentries = entries->nentries;
2220 ffd1d5e5 2018-06-23 stsp if (tree != root)
2221 ffd1d5e5 2018-06-23 stsp nentries++; /* '..' directory */
2223 ce52c690 2018-06-23 stsp err = tree_entry_path(&parent_path, &parents, NULL);
2225 ce52c690 2018-06-23 stsp goto done;
2227 ffd1d5e5 2018-06-23 stsp err = draw_tree_entries(&first_displayed_entry,
2228 ffd1d5e5 2018-06-23 stsp &last_displayed_entry, &selected_entry, &ndisplayed,
2229 cc3c9aac 2018-08-01 stsp view->window, tree_label, show_ids,
2230 1d13200f 2018-07-12 stsp parent_path, entries, selected, LINES, tree == root);
2231 ce52c690 2018-06-23 stsp free(parent_path);
2235 ffd1d5e5 2018-06-23 stsp nodelay(stdscr, FALSE);
2236 cc3c9aac 2018-08-01 stsp ch = wgetch(view->window);
2237 ffd1d5e5 2018-06-23 stsp nodelay(stdscr, TRUE);
2238 ffd1d5e5 2018-06-23 stsp switch (ch) {
2243 1d13200f 2018-07-12 stsp show_ids = !show_ids;
2246 69efd4c4 2018-07-18 stsp if (selected_entry) {
2247 69efd4c4 2018-07-18 stsp err = log_tree_entry(selected_entry,
2248 69efd4c4 2018-07-18 stsp &parents, commit_id, repo);
2250 69efd4c4 2018-07-18 stsp goto done;
2254 ffd1d5e5 2018-06-23 stsp case KEY_UP:
2255 ffd1d5e5 2018-06-23 stsp if (selected > 0)
2256 ffd1d5e5 2018-06-23 stsp selected--;
2257 ffd1d5e5 2018-06-23 stsp if (selected > 0)
2259 ffd1d5e5 2018-06-23 stsp tree_scroll_up(&first_displayed_entry, 1,
2260 ffd1d5e5 2018-06-23 stsp entries, tree == root);
2262 ffd1d5e5 2018-06-23 stsp case KEY_PPAGE:
2263 ffd1d5e5 2018-06-23 stsp if (SIMPLEQ_FIRST(&entries->head) ==
2264 ffd1d5e5 2018-06-23 stsp first_displayed_entry) {
2265 cf8f1261 2018-06-23 stsp if (tree != root)
2266 cf8f1261 2018-06-23 stsp first_displayed_entry = NULL;
2267 ffd1d5e5 2018-06-23 stsp selected = 0;
2270 ffd1d5e5 2018-06-23 stsp tree_scroll_up(&first_displayed_entry, LINES,
2271 ffd1d5e5 2018-06-23 stsp entries, tree == root);
2274 ffd1d5e5 2018-06-23 stsp case KEY_DOWN:
2275 ffd1d5e5 2018-06-23 stsp if (selected < ndisplayed - 1) {
2276 ffd1d5e5 2018-06-23 stsp selected++;
2279 ffd1d5e5 2018-06-23 stsp tree_scroll_down(&first_displayed_entry, 1,
2280 ffd1d5e5 2018-06-23 stsp last_displayed_entry, entries);
2282 ffd1d5e5 2018-06-23 stsp case KEY_NPAGE:
2283 ffd1d5e5 2018-06-23 stsp tree_scroll_down(&first_displayed_entry, LINES,
2284 ffd1d5e5 2018-06-23 stsp last_displayed_entry, entries);
2285 ffd1d5e5 2018-06-23 stsp if (SIMPLEQ_NEXT(last_displayed_entry, entry))
2287 ffd1d5e5 2018-06-23 stsp /* can't scroll any further; move cursor down */
2288 ffd1d5e5 2018-06-23 stsp if (selected < ndisplayed - 1)
2289 ffd1d5e5 2018-06-23 stsp selected = ndisplayed - 1;
2291 ffd1d5e5 2018-06-23 stsp case KEY_ENTER:
2292 ffd1d5e5 2018-06-23 stsp case '\r':
2293 ffd1d5e5 2018-06-23 stsp if (selected_entry == NULL) {
2294 ffd1d5e5 2018-06-23 stsp struct tog_parent_tree *parent;
2295 ffd1d5e5 2018-06-23 stsp case KEY_BACKSPACE:
2296 ffd1d5e5 2018-06-23 stsp /* user selected '..' */
2297 ffd1d5e5 2018-06-23 stsp if (tree == root)
2299 d9765a41 2018-06-23 stsp parent = TAILQ_FIRST(&parents);
2300 d9765a41 2018-06-23 stsp TAILQ_REMOVE(&parents, parent, entry);
2301 ffd1d5e5 2018-06-23 stsp got_object_tree_close(tree);
2302 ffd1d5e5 2018-06-23 stsp tree = parent->tree;
2303 ffd1d5e5 2018-06-23 stsp first_displayed_entry =
2304 ffd1d5e5 2018-06-23 stsp parent->first_displayed_entry;
2305 ffd1d5e5 2018-06-23 stsp selected_entry = parent->selected_entry;
2306 ffd1d5e5 2018-06-23 stsp selected = parent->selected;
2307 ffd1d5e5 2018-06-23 stsp free(parent);
2308 ffd1d5e5 2018-06-23 stsp } else if (S_ISDIR(selected_entry->mode)) {
2309 ffd1d5e5 2018-06-23 stsp struct tog_parent_tree *parent;
2310 ffd1d5e5 2018-06-23 stsp struct got_tree_object *child;
2311 ffd1d5e5 2018-06-23 stsp err = got_object_open_as_tree(
2312 ffd1d5e5 2018-06-23 stsp &child, repo, selected_entry->id);
2314 ffd1d5e5 2018-06-23 stsp goto done;
2315 ffd1d5e5 2018-06-23 stsp parent = calloc(1, sizeof(*parent));
2316 ffd1d5e5 2018-06-23 stsp if (parent == NULL) {
2317 ffd1d5e5 2018-06-23 stsp err = got_error_from_errno();
2318 ffd1d5e5 2018-06-23 stsp goto done;
2320 ffd1d5e5 2018-06-23 stsp parent->tree = tree;
2321 ffd1d5e5 2018-06-23 stsp parent->first_displayed_entry =
2322 ffd1d5e5 2018-06-23 stsp first_displayed_entry;
2323 ffd1d5e5 2018-06-23 stsp parent->selected_entry = selected_entry;
2324 ffd1d5e5 2018-06-23 stsp parent->selected = selected;
2325 d9765a41 2018-06-23 stsp TAILQ_INSERT_HEAD(&parents, parent,
2327 ffd1d5e5 2018-06-23 stsp tree = child;
2328 ffd1d5e5 2018-06-23 stsp selected = 0;
2329 ffd1d5e5 2018-06-23 stsp first_displayed_entry = NULL;
2330 ffd1d5e5 2018-06-23 stsp } else if (S_ISREG(selected_entry->mode)) {
2331 ffd1d5e5 2018-06-23 stsp err = blame_tree_entry(selected_entry,
2332 ffd1d5e5 2018-06-23 stsp &parents, commit_id, repo);
2334 ffd1d5e5 2018-06-23 stsp goto done;
2337 ffd1d5e5 2018-06-23 stsp case KEY_RESIZE:
2338 ffd1d5e5 2018-06-23 stsp if (selected > LINES)
2339 ffd1d5e5 2018-06-23 stsp selected = ndisplayed - 1;
2347 cc3c9aac 2018-08-01 stsp close_view(view);
2348 ffd1d5e5 2018-06-23 stsp free(tree_label);
2349 ffd1d5e5 2018-06-23 stsp free(commit_id_str);
2350 d9765a41 2018-06-23 stsp while (!TAILQ_EMPTY(&parents)) {
2351 ffd1d5e5 2018-06-23 stsp struct tog_parent_tree *parent;
2352 d9765a41 2018-06-23 stsp parent = TAILQ_FIRST(&parents);
2353 d9765a41 2018-06-23 stsp TAILQ_REMOVE(&parents, parent, entry);
2354 ffd1d5e5 2018-06-23 stsp free(parent);
2357 ffd1d5e5 2018-06-23 stsp if (tree != root)
2358 ffd1d5e5 2018-06-23 stsp got_object_tree_close(tree);
2359 ffd1d5e5 2018-06-23 stsp return err;
2362 ffd1d5e5 2018-06-23 stsp __dead static void
2363 ffd1d5e5 2018-06-23 stsp usage_tree(void)
2366 ffd1d5e5 2018-06-23 stsp fprintf(stderr, "usage: %s tree [-c commit] [repository-path]\n",
2367 ffd1d5e5 2018-06-23 stsp getprogname());
2371 ffd1d5e5 2018-06-23 stsp static const struct got_error *
2372 ffd1d5e5 2018-06-23 stsp cmd_tree(int argc, char *argv[])
2374 ffd1d5e5 2018-06-23 stsp const struct got_error *error;
2375 ffd1d5e5 2018-06-23 stsp struct got_repository *repo = NULL;
2376 ffd1d5e5 2018-06-23 stsp char *repo_path = NULL;
2377 ffd1d5e5 2018-06-23 stsp struct got_object_id *commit_id = NULL;
2378 ffd1d5e5 2018-06-23 stsp char *commit_id_arg = NULL;
2379 ffd1d5e5 2018-06-23 stsp struct got_commit_object *commit = NULL;
2380 ffd1d5e5 2018-06-23 stsp struct got_tree_object *tree = NULL;
2383 ffd1d5e5 2018-06-23 stsp #ifndef PROFILE
2384 ffd1d5e5 2018-06-23 stsp if (pledge("stdio rpath wpath cpath flock proc tty", NULL) == -1)
2385 ffd1d5e5 2018-06-23 stsp err(1, "pledge");
2388 ffd1d5e5 2018-06-23 stsp while ((ch = getopt(argc, argv, "c:")) != -1) {
2389 ffd1d5e5 2018-06-23 stsp switch (ch) {
2391 ffd1d5e5 2018-06-23 stsp commit_id_arg = optarg;
2395 ffd1d5e5 2018-06-23 stsp /* NOTREACHED */
2399 ffd1d5e5 2018-06-23 stsp argc -= optind;
2400 ffd1d5e5 2018-06-23 stsp argv += optind;
2402 ffd1d5e5 2018-06-23 stsp if (argc == 0) {
2403 ffd1d5e5 2018-06-23 stsp repo_path = getcwd(NULL, 0);
2404 ffd1d5e5 2018-06-23 stsp if (repo_path == NULL)
2405 ffd1d5e5 2018-06-23 stsp return got_error_from_errno();
2406 ffd1d5e5 2018-06-23 stsp } else if (argc == 1) {
2407 ffd1d5e5 2018-06-23 stsp repo_path = realpath(argv[0], NULL);
2408 ffd1d5e5 2018-06-23 stsp if (repo_path == NULL)
2409 ffd1d5e5 2018-06-23 stsp return got_error_from_errno();
2411 ffd1d5e5 2018-06-23 stsp usage_log();
2413 ffd1d5e5 2018-06-23 stsp error = got_repo_open(&repo, repo_path);
2414 ffd1d5e5 2018-06-23 stsp free(repo_path);
2415 ffd1d5e5 2018-06-23 stsp if (error != NULL)
2416 ffd1d5e5 2018-06-23 stsp return error;
2418 ffd1d5e5 2018-06-23 stsp if (commit_id_arg == NULL) {
2419 ffd1d5e5 2018-06-23 stsp error = get_head_commit_id(&commit_id, repo);
2420 ffd1d5e5 2018-06-23 stsp if (error != NULL)
2421 ffd1d5e5 2018-06-23 stsp goto done;
2423 ffd1d5e5 2018-06-23 stsp struct got_object *obj;
2424 ffd1d5e5 2018-06-23 stsp error = got_object_open_by_id_str(&obj, repo, commit_id_arg);
2425 ffd1d5e5 2018-06-23 stsp if (error == NULL) {
2426 ffd1d5e5 2018-06-23 stsp commit_id = got_object_get_id(obj);
2427 ffd1d5e5 2018-06-23 stsp if (commit_id == NULL)
2428 ffd1d5e5 2018-06-23 stsp error = got_error_from_errno();
2431 ffd1d5e5 2018-06-23 stsp if (error != NULL)
2432 ffd1d5e5 2018-06-23 stsp goto done;
2434 ffd1d5e5 2018-06-23 stsp error = got_object_open_as_commit(&commit, repo, commit_id);
2435 ffd1d5e5 2018-06-23 stsp if (error != NULL)
2436 ffd1d5e5 2018-06-23 stsp goto done;
2438 ffd1d5e5 2018-06-23 stsp error = got_object_open_as_tree(&tree, repo, commit->tree_id);
2439 ffd1d5e5 2018-06-23 stsp if (error != NULL)
2440 ffd1d5e5 2018-06-23 stsp goto done;
2442 ffd1d5e5 2018-06-23 stsp error = show_tree_view(tree, commit_id, repo);
2444 ffd1d5e5 2018-06-23 stsp free(commit_id);
2445 ffd1d5e5 2018-06-23 stsp if (commit)
2446 ffd1d5e5 2018-06-23 stsp got_object_commit_close(commit);
2448 ffd1d5e5 2018-06-23 stsp got_object_tree_close(tree);
2450 ffd1d5e5 2018-06-23 stsp got_repo_close(repo);
2451 ffd1d5e5 2018-06-23 stsp return error;
2453 5c5136c5 2018-05-20 stsp static void
2454 9f7d7167 2018-04-29 stsp init_curses(void)
2456 9f7d7167 2018-04-29 stsp initscr();
2460 9f7d7167 2018-04-29 stsp intrflush(stdscr, FALSE);
2461 9f7d7167 2018-04-29 stsp keypad(stdscr, TRUE);
2462 1f475ad8 2018-05-10 stsp curs_set(0);
2465 4ed7e80c 2018-05-20 stsp __dead static void
2466 9f7d7167 2018-04-29 stsp usage(void)
2470 c2301be8 2018-04-30 stsp fprintf(stderr, "usage: %s [-h] [command] [arg ...]\n\n"
2471 9f7d7167 2018-04-29 stsp "Available commands:\n", getprogname());
2472 9f7d7167 2018-04-29 stsp for (i = 0; i < nitems(tog_commands); i++) {
2473 9f7d7167 2018-04-29 stsp struct tog_cmd *cmd = &tog_commands[i];
2474 c2301be8 2018-04-30 stsp fprintf(stderr, " %s: %s\n", cmd->name, cmd->descr);
2479 c2301be8 2018-04-30 stsp static char **
2480 c2301be8 2018-04-30 stsp make_argv(const char *arg0, const char *arg1)
2482 c2301be8 2018-04-30 stsp char **argv;
2483 c2301be8 2018-04-30 stsp int argc = (arg1 == NULL ? 1 : 2);
2485 c2301be8 2018-04-30 stsp argv = calloc(argc, sizeof(char *));
2486 c2301be8 2018-04-30 stsp if (argv == NULL)
2487 c2301be8 2018-04-30 stsp err(1, "calloc");
2488 c2301be8 2018-04-30 stsp argv[0] = strdup(arg0);
2489 c2301be8 2018-04-30 stsp if (argv[0] == NULL)
2490 c2301be8 2018-04-30 stsp err(1, "calloc");
2491 c2301be8 2018-04-30 stsp if (arg1) {
2492 c2301be8 2018-04-30 stsp argv[1] = strdup(arg1);
2493 c2301be8 2018-04-30 stsp if (argv[1] == NULL)
2494 c2301be8 2018-04-30 stsp err(1, "calloc");
2497 c2301be8 2018-04-30 stsp return argv;
2501 9f7d7167 2018-04-29 stsp main(int argc, char *argv[])
2503 9f7d7167 2018-04-29 stsp const struct got_error *error = NULL;
2504 9f7d7167 2018-04-29 stsp struct tog_cmd *cmd = NULL;
2505 9f7d7167 2018-04-29 stsp int ch, hflag = 0;
2506 c2301be8 2018-04-30 stsp char **cmd_argv = NULL;
2508 9f7d7167 2018-04-29 stsp setlocale(LC_ALL, "");
2510 9f7d7167 2018-04-29 stsp while ((ch = getopt(argc, argv, "h")) != -1) {
2511 9f7d7167 2018-04-29 stsp switch (ch) {
2513 9f7d7167 2018-04-29 stsp hflag = 1;
2517 9f7d7167 2018-04-29 stsp /* NOTREACHED */
2521 9f7d7167 2018-04-29 stsp argc -= optind;
2522 9f7d7167 2018-04-29 stsp argv += optind;
2523 9f7d7167 2018-04-29 stsp optind = 0;
2524 c2301be8 2018-04-30 stsp optreset = 1;
2526 c2301be8 2018-04-30 stsp if (argc == 0) {
2527 f29d3e89 2018-06-23 stsp if (hflag)
2529 c2301be8 2018-04-30 stsp /* Build an argument vector which runs a default command. */
2530 9f7d7167 2018-04-29 stsp cmd = &tog_commands[0];
2531 c2301be8 2018-04-30 stsp cmd_argv = make_argv(cmd->name, NULL);
2536 c2301be8 2018-04-30 stsp /* Did the user specific a command? */
2537 9f7d7167 2018-04-29 stsp for (i = 0; i < nitems(tog_commands); i++) {
2538 c2301be8 2018-04-30 stsp if (strncmp(tog_commands[i].name, argv[0],
2539 9f7d7167 2018-04-29 stsp strlen(argv[0])) == 0) {
2540 9f7d7167 2018-04-29 stsp cmd = &tog_commands[i];
2541 9f7d7167 2018-04-29 stsp if (hflag)
2542 9f7d7167 2018-04-29 stsp tog_commands[i].cmd_usage();
2546 9f7d7167 2018-04-29 stsp if (cmd == NULL) {
2547 c2301be8 2018-04-30 stsp /* Did the user specify a repository? */
2548 c2301be8 2018-04-30 stsp char *repo_path = realpath(argv[0], NULL);
2549 c2301be8 2018-04-30 stsp if (repo_path) {
2550 c2301be8 2018-04-30 stsp struct got_repository *repo;
2551 c2301be8 2018-04-30 stsp error = got_repo_open(&repo, repo_path);
2552 c2301be8 2018-04-30 stsp if (error == NULL)
2553 c2301be8 2018-04-30 stsp got_repo_close(repo);
2555 ad7de8d9 2018-04-30 stsp error = got_error_from_errno();
2556 c2301be8 2018-04-30 stsp if (error) {
2557 f29d3e89 2018-06-23 stsp if (hflag) {
2558 f29d3e89 2018-06-23 stsp fprintf(stderr, "%s: '%s' is not a "
2559 f29d3e89 2018-06-23 stsp "known command\n", getprogname(),
2563 ad7de8d9 2018-04-30 stsp fprintf(stderr, "%s: '%s' is neither a known "
2564 ad7de8d9 2018-04-30 stsp "command nor a path to a repository\n",
2565 c2301be8 2018-04-30 stsp getprogname(), argv[0]);
2566 ad7de8d9 2018-04-30 stsp free(repo_path);
2569 c2301be8 2018-04-30 stsp cmd = &tog_commands[0];
2570 c2301be8 2018-04-30 stsp cmd_argv = make_argv(cmd->name, repo_path);
2572 c2301be8 2018-04-30 stsp free(repo_path);
2576 5c5136c5 2018-05-20 stsp init_curses();
2578 c2301be8 2018-04-30 stsp error = cmd->cmd_main(argc, cmd_argv ? cmd_argv : argv);
2579 9f7d7167 2018-04-29 stsp if (error)
2580 9f7d7167 2018-04-29 stsp goto done;
2583 c2301be8 2018-04-30 stsp free(cmd_argv);
2584 9f7d7167 2018-04-29 stsp if (error)
2585 9f7d7167 2018-04-29 stsp fprintf(stderr, "%s: %s\n", getprogname(), error->msg);