Blame


1 9f7d7167 2018-04-29 stsp /*
2 9f7d7167 2018-04-29 stsp * Copyright (c) 2018 Stefan Sperling <stsp@openbsd.org>
3 9f7d7167 2018-04-29 stsp *
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.
7 9f7d7167 2018-04-29 stsp *
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.
15 9f7d7167 2018-04-29 stsp */
16 9f7d7167 2018-04-29 stsp
17 80ddbec8 2018-04-29 stsp #include <sys/queue.h>
18 ffd1d5e5 2018-06-23 stsp #include <sys/stat.h>
19 80ddbec8 2018-04-29 stsp
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>
37 9f7d7167 2018-04-29 stsp
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"
47 9f7d7167 2018-04-29 stsp
48 881b2d3e 2018-04-30 stsp #ifndef MIN
49 881b2d3e 2018-04-30 stsp #define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
50 881b2d3e 2018-04-30 stsp #endif
51 881b2d3e 2018-04-30 stsp
52 9f7d7167 2018-04-29 stsp #ifndef nitems
53 9f7d7167 2018-04-29 stsp #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
54 9f7d7167 2018-04-29 stsp #endif
55 9f7d7167 2018-04-29 stsp
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;
61 9f7d7167 2018-04-29 stsp };
62 9f7d7167 2018-04-29 stsp
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);
68 9f7d7167 2018-04-29 stsp
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 *[]);
73 9f7d7167 2018-04-29 stsp
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" },
83 9f7d7167 2018-04-29 stsp };
84 9f7d7167 2018-04-29 stsp
85 cc3c9aac 2018-08-01 stsp struct tog_view {
86 26ed57b2 2018-05-19 stsp WINDOW *window;
87 26ed57b2 2018-05-19 stsp PANEL *panel;
88 97ddc146 2018-08-01 stsp int nlines, ncols, begin_y, begin_x;
89 cc3c9aac 2018-08-01 stsp };
90 cd0acaa7 2018-05-20 stsp
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 *);
101 26ed57b2 2018-05-19 stsp
102 ea5e7bb5 2018-08-01 stsp static void
103 ea5e7bb5 2018-08-01 stsp close_view(struct tog_view *view)
104 ea5e7bb5 2018-08-01 stsp {
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);
110 ea5e7bb5 2018-08-01 stsp }
111 ea5e7bb5 2018-08-01 stsp
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)
114 ea5e7bb5 2018-08-01 stsp {
115 ea5e7bb5 2018-08-01 stsp struct tog_view *view = malloc(sizeof(*view));
116 ea5e7bb5 2018-08-01 stsp
117 ea5e7bb5 2018-08-01 stsp if (view == NULL)
118 ea5e7bb5 2018-08-01 stsp return NULL;
119 ea5e7bb5 2018-08-01 stsp
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;
128 ea5e7bb5 2018-08-01 stsp }
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;
133 ea5e7bb5 2018-08-01 stsp }
134 ea5e7bb5 2018-08-01 stsp
135 ea5e7bb5 2018-08-01 stsp keypad(view->window, TRUE);
136 ea5e7bb5 2018-08-01 stsp return view;
137 ea5e7bb5 2018-08-01 stsp }
138 ea5e7bb5 2018-08-01 stsp
139 4ed7e80c 2018-05-20 stsp __dead static void
140 9f7d7167 2018-04-29 stsp usage_log(void)
141 9f7d7167 2018-04-29 stsp {
142 80ddbec8 2018-04-29 stsp endwin();
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());
145 9f7d7167 2018-04-29 stsp exit(1);
146 80ddbec8 2018-04-29 stsp }
147 80ddbec8 2018-04-29 stsp
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)
151 963b370f 2018-05-20 stsp {
152 00dfcb92 2018-06-11 stsp char *vis = NULL;
153 963b370f 2018-05-20 stsp const struct got_error *err = NULL;
154 963b370f 2018-05-20 stsp
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();
161 00dfcb92 2018-06-11 stsp
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);
164 00dfcb92 2018-06-11 stsp if (err)
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 */
169 a7f50699 2018-06-11 stsp goto done;
170 a7f50699 2018-06-11 stsp }
171 00dfcb92 2018-06-11 stsp }
172 963b370f 2018-05-20 stsp
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();
176 a7f50699 2018-06-11 stsp goto done;
177 a7f50699 2018-06-11 stsp }
178 963b370f 2018-05-20 stsp
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();
181 a7f50699 2018-06-11 stsp done:
182 00dfcb92 2018-06-11 stsp free(vis);
183 963b370f 2018-05-20 stsp if (err) {
184 963b370f 2018-05-20 stsp free(*ws);
185 963b370f 2018-05-20 stsp *ws = NULL;
186 963b370f 2018-05-20 stsp *wlen = 0;
187 963b370f 2018-05-20 stsp }
188 963b370f 2018-05-20 stsp return err;
189 963b370f 2018-05-20 stsp }
190 963b370f 2018-05-20 stsp
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)
194 963b370f 2018-05-20 stsp {
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;
199 963b370f 2018-05-20 stsp int i;
200 963b370f 2018-05-20 stsp
201 963b370f 2018-05-20 stsp *wlinep = NULL;
202 b700b5d6 2018-07-10 stsp *widthp = 0;
203 963b370f 2018-05-20 stsp
204 963b370f 2018-05-20 stsp err = mbs2ws(&wline, &wlen, line);
205 963b370f 2018-05-20 stsp if (err)
206 963b370f 2018-05-20 stsp return err;
207 963b370f 2018-05-20 stsp
208 963b370f 2018-05-20 stsp i = 0;
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) {
212 963b370f 2018-05-20 stsp case 0:
213 b700b5d6 2018-07-10 stsp i++;
214 963b370f 2018-05-20 stsp break;
215 963b370f 2018-05-20 stsp case 1:
216 963b370f 2018-05-20 stsp case 2:
217 b700b5d6 2018-07-10 stsp if (cols + width <= wlimit) {
218 b700b5d6 2018-07-10 stsp cols += width;
219 b700b5d6 2018-07-10 stsp i++;
220 b700b5d6 2018-07-10 stsp }
221 963b370f 2018-05-20 stsp break;
222 963b370f 2018-05-20 stsp case -1:
223 963b370f 2018-05-20 stsp if (wline[i] == L'\t')
224 b700b5d6 2018-07-10 stsp cols += TABSIZE - ((cols + 1) % TABSIZE);
225 b700b5d6 2018-07-10 stsp i++;
226 963b370f 2018-05-20 stsp break;
227 963b370f 2018-05-20 stsp default:
228 963b370f 2018-05-20 stsp err = got_error_from_errno();
229 963b370f 2018-05-20 stsp goto done;
230 963b370f 2018-05-20 stsp }
231 963b370f 2018-05-20 stsp }
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;
235 963b370f 2018-05-20 stsp done:
236 963b370f 2018-05-20 stsp if (err)
237 963b370f 2018-05-20 stsp free(wline);
238 963b370f 2018-05-20 stsp else
239 963b370f 2018-05-20 stsp *wlinep = wline;
240 963b370f 2018-05-20 stsp return err;
241 963b370f 2018-05-20 stsp }
242 963b370f 2018-05-20 stsp
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)
246 80ddbec8 2018-04-29 stsp {
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;
259 80ddbec8 2018-04-29 stsp
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);
263 b39d25c7 2018-07-10 stsp
264 b39d25c7 2018-07-10 stsp if (avail < date_display_cols)
265 b39d25c7 2018-07-10 stsp limit = MIN(sizeof(datebuf) - 1, avail);
266 b39d25c7 2018-07-10 stsp else
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)
271 b39d25c7 2018-07-10 stsp goto done;
272 b39d25c7 2018-07-10 stsp
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();
276 80ddbec8 2018-04-29 stsp goto done;
277 80ddbec8 2018-04-29 stsp }
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';
282 6d9fbc00 2018-04-29 stsp else {
283 6d9fbc00 2018-04-29 stsp char *at = strchr(author, '@');
284 6d9fbc00 2018-04-29 stsp if (at)
285 6d9fbc00 2018-04-29 stsp *at = '\0';
286 6d9fbc00 2018-04-29 stsp }
287 b39d25c7 2018-07-10 stsp limit = avail - col;
288 bb737323 2018-05-20 stsp err = format_line(&wauthor, &author_width, author, limit);
289 bb737323 2018-05-20 stsp if (err)
290 bb737323 2018-05-20 stsp goto done;
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, ' ');
295 bb737323 2018-05-20 stsp col++;
296 bb737323 2018-05-20 stsp author_width++;
297 bb737323 2018-05-20 stsp }
298 9c2eaf34 2018-05-20 stsp if (col > avail)
299 9c2eaf34 2018-05-20 stsp goto done;
300 80ddbec8 2018-04-29 stsp
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();
304 6d9fbc00 2018-04-29 stsp goto done;
305 6d9fbc00 2018-04-29 stsp }
306 bb737323 2018-05-20 stsp logmsg = logmsg0;
307 bb737323 2018-05-20 stsp while (*logmsg == '\n')
308 bb737323 2018-05-20 stsp logmsg++;
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);
314 bb737323 2018-05-20 stsp if (err)
315 bb737323 2018-05-20 stsp goto done;
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, ' ');
320 bb737323 2018-05-20 stsp col++;
321 881b2d3e 2018-04-30 stsp }
322 80ddbec8 2018-04-29 stsp done:
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;
329 80ddbec8 2018-04-29 stsp }
330 26ed57b2 2018-05-19 stsp
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;
335 80ddbec8 2018-04-29 stsp };
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;
340 ecb28ae0 2018-07-16 stsp };
341 80ddbec8 2018-04-29 stsp
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)
345 80ddbec8 2018-04-29 stsp {
346 80ddbec8 2018-04-29 stsp struct commit_queue_entry *entry;
347 80ddbec8 2018-04-29 stsp
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;
351 99db9666 2018-05-07 stsp
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;
355 99db9666 2018-05-07 stsp }
356 80ddbec8 2018-04-29 stsp
357 99db9666 2018-05-07 stsp static void
358 99db9666 2018-05-07 stsp pop_commit(struct commit_queue *commits)
359 99db9666 2018-05-07 stsp {
360 99db9666 2018-05-07 stsp struct commit_queue_entry *entry;
361 99db9666 2018-05-07 stsp
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);
368 99db9666 2018-05-07 stsp }
369 99db9666 2018-05-07 stsp
370 99db9666 2018-05-07 stsp static void
371 99db9666 2018-05-07 stsp free_commits(struct commit_queue *commits)
372 99db9666 2018-05-07 stsp {
373 ecb28ae0 2018-07-16 stsp while (!TAILQ_EMPTY(&commits->head))
374 99db9666 2018-05-07 stsp pop_commit(commits);
375 c4972b91 2018-05-07 stsp }
376 c4972b91 2018-05-07 stsp
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)
381 c4972b91 2018-05-07 stsp {
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;
387 c4972b91 2018-05-07 stsp
388 9ba79e04 2018-06-11 stsp err = got_commit_graph_iter_start(graph, start_id);
389 c4972b91 2018-05-07 stsp if (err)
390 c4972b91 2018-05-07 stsp return err;
391 c4972b91 2018-05-07 stsp
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;
395 899d86c2 2018-05-10 stsp
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;
400 9ba79e04 2018-06-11 stsp
401 9ba79e04 2018-06-11 stsp err = got_commit_graph_fetch_commits(&nfetched, graph, 1, repo);
402 9ba79e04 2018-06-11 stsp if (err)
403 9ba79e04 2018-06-11 stsp return err;
404 c4972b91 2018-05-07 stsp }
405 c4972b91 2018-05-07 stsp
406 9ba79e04 2018-06-11 stsp while (1) {
407 9ba79e04 2018-06-11 stsp struct got_commit_object *commit;
408 899d86c2 2018-05-10 stsp
409 9ba79e04 2018-06-11 stsp err = got_commit_graph_iter_next(&id, graph);
410 9ba79e04 2018-06-11 stsp if (err) {
411 ecb28ae0 2018-07-16 stsp if (err->code != GOT_ERR_ITER_NEED_MORE)
412 ecb28ae0 2018-07-16 stsp break;
413 ecb28ae0 2018-07-16 stsp if (nqueued >= minqueue) {
414 9ba79e04 2018-06-11 stsp err = NULL;
415 ecb28ae0 2018-07-16 stsp break;
416 ecb28ae0 2018-07-16 stsp }
417 ecb28ae0 2018-07-16 stsp err = got_commit_graph_fetch_commits(&nfetched,
418 ecb28ae0 2018-07-16 stsp graph, 1, repo);
419 ecb28ae0 2018-07-16 stsp if (err)
420 ecb28ae0 2018-07-16 stsp return err;
421 ecb28ae0 2018-07-16 stsp continue;
422 9ba79e04 2018-06-11 stsp }
423 ecb28ae0 2018-07-16 stsp if (id == NULL)
424 ecb28ae0 2018-07-16 stsp break;
425 899d86c2 2018-05-10 stsp
426 9ba79e04 2018-06-11 stsp err = got_object_open_as_commit(&commit, repo, id);
427 9ba79e04 2018-06-11 stsp if (err)
428 9ba79e04 2018-06-11 stsp break;
429 899d86c2 2018-05-10 stsp
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;
434 ecb28ae0 2018-07-16 stsp
435 ecb28ae0 2018-07-16 stsp err = got_object_open_by_path(&obj, repo, id, path);
436 ecb28ae0 2018-07-16 stsp if (err) {
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);
442 ecb28ae0 2018-07-16 stsp }
443 ecb28ae0 2018-07-16 stsp break;
444 ecb28ae0 2018-07-16 stsp }
445 ecb28ae0 2018-07-16 stsp found_obj = 1;
446 ecb28ae0 2018-07-16 stsp
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);
452 ecb28ae0 2018-07-16 stsp if (err) {
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);
456 ecb28ae0 2018-07-16 stsp break;
457 ecb28ae0 2018-07-16 stsp }
458 ecb28ae0 2018-07-16 stsp err = NULL;
459 ecb28ae0 2018-07-16 stsp changed = 1;
460 ecb28ae0 2018-07-16 stsp } else {
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);
467 c2f16475 2018-07-23 stsp break;
468 c2f16475 2018-07-23 stsp }
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();
472 c2f16475 2018-07-23 stsp free(id);
473 c8f60bff 2018-07-23 stsp got_object_close(obj);
474 c8f60bff 2018-07-23 stsp got_object_close(pobj);
475 c2f16475 2018-07-23 stsp break;
476 c2f16475 2018-07-23 stsp }
477 c2f16475 2018-07-23 stsp changed =
478 c2f16475 2018-07-23 stsp (got_object_id_cmp(id, pid) != 0);
479 ecb28ae0 2018-07-16 stsp got_object_close(pobj);
480 c2f16475 2018-07-23 stsp free(id);
481 c2f16475 2018-07-23 stsp free(pid);
482 ecb28ae0 2018-07-16 stsp }
483 ecb28ae0 2018-07-16 stsp }
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);
487 ecb28ae0 2018-07-16 stsp continue;
488 ecb28ae0 2018-07-16 stsp }
489 ecb28ae0 2018-07-16 stsp }
490 ecb28ae0 2018-07-16 stsp
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();
494 9ba79e04 2018-06-11 stsp break;
495 9ba79e04 2018-06-11 stsp }
496 ecb28ae0 2018-07-16 stsp TAILQ_INSERT_TAIL(&commits->head, entry, entry);
497 ecb28ae0 2018-07-16 stsp nqueued++;
498 ecb28ae0 2018-07-16 stsp commits->ncommits++;
499 899d86c2 2018-05-10 stsp }
500 899d86c2 2018-05-10 stsp
501 9ba79e04 2018-06-11 stsp return err;
502 99db9666 2018-05-07 stsp }
503 99db9666 2018-05-07 stsp
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)
509 899d86c2 2018-05-10 stsp {
510 899d86c2 2018-05-10 stsp const struct got_error *err = NULL;
511 899d86c2 2018-05-10 stsp
512 9ba79e04 2018-06-11 stsp *pentry = NULL;
513 899d86c2 2018-05-10 stsp
514 ecb28ae0 2018-07-16 stsp err = queue_commits(graph, commits, entry->id, 1, 0, repo, path);
515 ecb28ae0 2018-07-16 stsp if (err)
516 9ba79e04 2018-06-11 stsp return err;
517 899d86c2 2018-05-10 stsp
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);
522 899d86c2 2018-05-10 stsp
523 9ba79e04 2018-06-11 stsp return NULL;
524 899d86c2 2018-05-10 stsp }
525 899d86c2 2018-05-10 stsp
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)
528 99db9666 2018-05-07 stsp {
529 9ba79e04 2018-06-11 stsp const struct got_error *err = NULL;
530 9ba79e04 2018-06-11 stsp struct got_reference *head_ref;
531 99db9666 2018-05-07 stsp
532 9ba79e04 2018-06-11 stsp *head_id = NULL;
533 899d86c2 2018-05-10 stsp
534 9ba79e04 2018-06-11 stsp err = got_ref_open(&head_ref, repo, GOT_REF_HEAD);
535 99db9666 2018-05-07 stsp if (err)
536 99db9666 2018-05-07 stsp return err;
537 99db9666 2018-05-07 stsp
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);
540 9ba79e04 2018-06-11 stsp if (err) {
541 9ba79e04 2018-06-11 stsp *head_id = NULL;
542 99db9666 2018-05-07 stsp return err;
543 0553a4e3 2018-04-30 stsp }
544 80ddbec8 2018-04-29 stsp
545 9ba79e04 2018-06-11 stsp return NULL;
546 0553a4e3 2018-04-30 stsp }
547 0553a4e3 2018-04-30 stsp
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)
554 0553a4e3 2018-04-30 stsp {
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;
560 0553a4e3 2018-04-30 stsp
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;
566 e0d42f60 2018-07-22 stsp break;
567 e0d42f60 2018-07-22 stsp }
568 e0d42f60 2018-07-22 stsp entry = TAILQ_NEXT(entry, entry);
569 e0d42f60 2018-07-22 stsp ncommits++;
570 e0d42f60 2018-07-22 stsp }
571 e0d42f60 2018-07-22 stsp
572 867c6645 2018-07-10 stsp err = got_object_id_str(&id_str, (*selected)->id);
573 867c6645 2018-07-10 stsp if (err)
574 867c6645 2018-07-10 stsp return err;
575 867c6645 2018-07-10 stsp
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;
581 ecb28ae0 2018-07-16 stsp }
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;
586 867c6645 2018-07-10 stsp }
587 ecb28ae0 2018-07-16 stsp free(id_str);
588 ecb28ae0 2018-07-16 stsp err = format_line(&wline, &width, header, COLS);
589 ecb28ae0 2018-07-16 stsp if (err) {
590 ecb28ae0 2018-07-16 stsp free(header);
591 ecb28ae0 2018-07-16 stsp return err;
592 ecb28ae0 2018-07-16 stsp }
593 ecb28ae0 2018-07-16 stsp free(header);
594 867c6645 2018-07-10 stsp
595 2814baeb 2018-08-01 stsp werase(view->window);
596 867c6645 2018-07-10 stsp
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;
603 0553a4e3 2018-04-30 stsp
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)
609 899d86c2 2018-05-10 stsp break;
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);
615 0553a4e3 2018-04-30 stsp if (err)
616 0553a4e3 2018-04-30 stsp break;
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);
622 a7f40148 2018-07-18 stsp if (err) {
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;
626 a7f40148 2018-07-18 stsp }
627 a7f40148 2018-07-18 stsp }
628 899d86c2 2018-05-10 stsp entry = TAILQ_NEXT(entry, entry);
629 80ddbec8 2018-04-29 stsp }
630 80ddbec8 2018-04-29 stsp
631 80ddbec8 2018-04-29 stsp update_panels();
632 80ddbec8 2018-04-29 stsp doupdate();
633 0553a4e3 2018-04-30 stsp
634 80ddbec8 2018-04-29 stsp return err;
635 9f7d7167 2018-04-29 stsp }
636 07b55e75 2018-05-10 stsp
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)
640 07b55e75 2018-05-10 stsp {
641 07b55e75 2018-05-10 stsp struct commit_queue_entry *entry;
642 07b55e75 2018-05-10 stsp int nscrolled = 0;
643 07b55e75 2018-05-10 stsp
644 ecb28ae0 2018-07-16 stsp entry = TAILQ_FIRST(&commits->head);
645 07b55e75 2018-05-10 stsp if (*first_displayed_entry == entry)
646 07b55e75 2018-05-10 stsp return;
647 9f7d7167 2018-04-29 stsp
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++;
654 07b55e75 2018-05-10 stsp }
655 07b55e75 2018-05-10 stsp }
656 aa075928 2018-05-10 stsp }
657 aa075928 2018-05-10 stsp
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)
663 aa075928 2018-05-10 stsp {
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;
667 aa075928 2018-05-10 stsp
668 aa075928 2018-05-10 stsp do {
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)
674 aa075928 2018-05-10 stsp break;
675 aa075928 2018-05-10 stsp }
676 dd0a52c1 2018-05-20 stsp last_displayed_entry = pentry;
677 aa075928 2018-05-10 stsp
678 dd0a52c1 2018-05-20 stsp pentry = TAILQ_NEXT(*first_displayed_entry, entry);
679 dd0a52c1 2018-05-20 stsp if (pentry == NULL)
680 dd0a52c1 2018-05-20 stsp break;
681 aa075928 2018-05-10 stsp *first_displayed_entry = pentry;
682 16482c3b 2018-05-20 stsp } while (++nscrolled < maxscroll);
683 aa075928 2018-05-10 stsp
684 dd0a52c1 2018-05-20 stsp return err;
685 07b55e75 2018-05-10 stsp }
686 4a7f7875 2018-05-10 stsp
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)
689 cd0acaa7 2018-05-20 stsp {
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;
694 cd0acaa7 2018-05-20 stsp
695 cd0acaa7 2018-05-20 stsp err = got_object_open(&obj2, repo, entry->id);
696 cd0acaa7 2018-05-20 stsp if (err)
697 cd0acaa7 2018-05-20 stsp return err;
698 cd0acaa7 2018-05-20 stsp
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);
702 cd0acaa7 2018-05-20 stsp if (err)
703 cd0acaa7 2018-05-20 stsp goto done;
704 cd0acaa7 2018-05-20 stsp }
705 cd0acaa7 2018-05-20 stsp
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();
709 ea5e7bb5 2018-08-01 stsp goto done;
710 ea5e7bb5 2018-08-01 stsp }
711 ea5e7bb5 2018-08-01 stsp
712 ea5e7bb5 2018-08-01 stsp err = show_diff_view(view, obj1, obj2, repo);
713 ea5e7bb5 2018-08-01 stsp close_view(view);
714 cd0acaa7 2018-05-20 stsp done:
715 cd0acaa7 2018-05-20 stsp if (obj1)
716 cd0acaa7 2018-05-20 stsp got_object_close(obj1);
717 cd0acaa7 2018-05-20 stsp if (obj2)
718 cd0acaa7 2018-05-20 stsp got_object_close(obj2);
719 cd0acaa7 2018-05-20 stsp return err;
720 4a7f7875 2018-05-10 stsp }
721 4a7f7875 2018-05-10 stsp
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)
724 9343a5fb 2018-06-23 stsp {
725 9343a5fb 2018-06-23 stsp const struct got_error *err = NULL;
726 9343a5fb 2018-06-23 stsp struct got_tree_object *tree;
727 9343a5fb 2018-06-23 stsp
728 9343a5fb 2018-06-23 stsp err = got_object_open_as_tree(&tree, repo, entry->commit->tree_id);
729 9343a5fb 2018-06-23 stsp if (err)
730 9343a5fb 2018-06-23 stsp return err;
731 9343a5fb 2018-06-23 stsp
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;
735 9343a5fb 2018-06-23 stsp }
736 9343a5fb 2018-06-23 stsp
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)
740 80ddbec8 2018-04-29 stsp {
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;
751 80ddbec8 2018-04-29 stsp
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)
754 ecb28ae0 2018-07-16 stsp goto done;
755 ecb28ae0 2018-07-16 stsp
756 9ba79e04 2018-06-11 stsp err = get_head_commit_id(&head_id, repo);
757 9ba79e04 2018-06-11 stsp if (err)
758 9ba79e04 2018-06-11 stsp return err;
759 9ba79e04 2018-06-11 stsp
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);
762 9ba79e04 2018-06-11 stsp if (err)
763 9ba79e04 2018-06-11 stsp goto done;
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;
767 9ba79e04 2018-06-11 stsp
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,
770 9ba79e04 2018-06-11 stsp repo);
771 9ba79e04 2018-06-11 stsp if (err)
772 9ba79e04 2018-06-11 stsp goto done;
773 9ba79e04 2018-06-11 stsp
774 9ba79e04 2018-06-11 stsp /*
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.
779 9ba79e04 2018-06-11 stsp */
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);
782 55198a88 2018-07-16 stsp if (err) {
783 55198a88 2018-07-16 stsp if (err->code != GOT_ERR_ITER_COMPLETED)
784 55198a88 2018-07-16 stsp goto done;
785 55198a88 2018-07-16 stsp err = NULL;
786 2814baeb 2018-08-01 stsp }
787 2814baeb 2018-08-01 stsp
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();
791 2814baeb 2018-08-01 stsp goto done;
792 9ba79e04 2018-06-11 stsp }
793 9ba79e04 2018-06-11 stsp
794 2814baeb 2018-08-01 stsp show_panel(view->panel);
795 2814baeb 2018-08-01 stsp
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);
802 80ddbec8 2018-04-29 stsp if (err)
803 d0f709cb 2018-04-30 stsp goto done;
804 80ddbec8 2018-04-29 stsp
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) {
809 31120ada 2018-04-30 stsp case ERR:
810 31120ada 2018-04-30 stsp break;
811 80ddbec8 2018-04-29 stsp case 'q':
812 80ddbec8 2018-04-29 stsp done = 1;
813 80ddbec8 2018-04-29 stsp break;
814 80ddbec8 2018-04-29 stsp case 'k':
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)
819 d91e25cb 2018-05-10 stsp break;
820 07b55e75 2018-05-10 stsp scroll_up(&first_displayed_entry, 1, &commits);
821 80ddbec8 2018-04-29 stsp break;
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;
826 dfc1d240 2018-05-10 stsp break;
827 dfc1d240 2018-05-10 stsp }
828 48531068 2018-05-10 stsp scroll_up(&first_displayed_entry, LINES,
829 48531068 2018-05-10 stsp &commits);
830 48531068 2018-05-10 stsp break;
831 80ddbec8 2018-04-29 stsp case 'j':
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++;
836 15c91275 2018-05-20 stsp break;
837 8df4052c 2018-05-20 stsp }
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);
841 ecb28ae0 2018-07-16 stsp if (err) {
842 ecb28ae0 2018-07-16 stsp if (err->code != GOT_ERR_ITER_COMPLETED)
843 ecb28ae0 2018-07-16 stsp goto done;
844 ecb28ae0 2018-07-16 stsp err = NULL;
845 ecb28ae0 2018-07-16 stsp }
846 4a7f7875 2018-05-10 stsp break;
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);
852 ecb28ae0 2018-07-16 stsp if (err) {
853 ecb28ae0 2018-07-16 stsp if (err->code != GOT_ERR_ITER_COMPLETED)
854 ecb28ae0 2018-07-16 stsp goto done;
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);
860 ecb28ae0 2018-07-16 stsp }
861 ecb28ae0 2018-07-16 stsp err = NULL;
862 ecb28ae0 2018-07-16 stsp }
863 80ddbec8 2018-04-29 stsp break;
864 ecb28ae0 2018-07-16 stsp }
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;
870 cd0acaa7 2018-05-20 stsp break;
871 cd0acaa7 2018-05-20 stsp case KEY_ENTER:
872 cd0acaa7 2018-05-20 stsp case '\r':
873 cd0acaa7 2018-05-20 stsp err = show_commit(selected_entry, repo);
874 9343a5fb 2018-06-23 stsp if (err)
875 9343a5fb 2018-06-23 stsp goto done;
876 2814baeb 2018-08-01 stsp show_panel(view->panel);
877 9343a5fb 2018-06-23 stsp break;
878 9343a5fb 2018-06-23 stsp case 't':
879 9343a5fb 2018-06-23 stsp err = browse_commit(selected_entry, repo);
880 cd0acaa7 2018-05-20 stsp if (err)
881 0d4100bb 2018-06-23 stsp goto done;
882 2814baeb 2018-08-01 stsp show_panel(view->panel);
883 31120ada 2018-04-30 stsp break;
884 80ddbec8 2018-04-29 stsp default:
885 80ddbec8 2018-04-29 stsp break;
886 80ddbec8 2018-04-29 stsp }
887 899d86c2 2018-05-10 stsp }
888 80ddbec8 2018-04-29 stsp done:
889 2814baeb 2018-08-01 stsp if (view)
890 2814baeb 2018-08-01 stsp close_view(view);
891 9ba79e04 2018-06-11 stsp free(head_id);
892 9ba79e04 2018-06-11 stsp if (graph)
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;
897 80ddbec8 2018-04-29 stsp }
898 80ddbec8 2018-04-29 stsp
899 4ed7e80c 2018-05-20 stsp static const struct got_error *
900 9f7d7167 2018-04-29 stsp cmd_log(int argc, char *argv[])
901 9f7d7167 2018-04-29 stsp {
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;
907 80ddbec8 2018-04-29 stsp int ch;
908 80ddbec8 2018-04-29 stsp
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");
912 80ddbec8 2018-04-29 stsp #endif
913 80ddbec8 2018-04-29 stsp
914 ecb28ae0 2018-07-16 stsp while ((ch = getopt(argc, argv, "c:r:")) != -1) {
915 80ddbec8 2018-04-29 stsp switch (ch) {
916 80ddbec8 2018-04-29 stsp case 'c':
917 80ddbec8 2018-04-29 stsp start_commit = optarg;
918 80ddbec8 2018-04-29 stsp break;
919 ecb28ae0 2018-07-16 stsp case 'r':
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");
923 ecb28ae0 2018-07-16 stsp break;
924 80ddbec8 2018-04-29 stsp default:
925 80ddbec8 2018-04-29 stsp usage();
926 80ddbec8 2018-04-29 stsp /* NOTREACHED */
927 80ddbec8 2018-04-29 stsp }
928 80ddbec8 2018-04-29 stsp }
929 80ddbec8 2018-04-29 stsp
930 80ddbec8 2018-04-29 stsp argc -= optind;
931 80ddbec8 2018-04-29 stsp argv += optind;
932 80ddbec8 2018-04-29 stsp
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]);
937 ecb28ae0 2018-07-16 stsp else
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();
941 80ddbec8 2018-04-29 stsp
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();
945 ecb28ae0 2018-07-16 stsp goto done;
946 ecb28ae0 2018-07-16 stsp }
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();
951 ecb28ae0 2018-07-16 stsp goto done;
952 ecb28ae0 2018-07-16 stsp }
953 ecb28ae0 2018-07-16 stsp }
954 ecb28ae0 2018-07-16 stsp
955 80ddbec8 2018-04-29 stsp error = got_repo_open(&repo, repo_path);
956 80ddbec8 2018-04-29 stsp if (error != NULL)
957 ecb28ae0 2018-07-16 stsp goto done;
958 80ddbec8 2018-04-29 stsp
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)
962 ecb28ae0 2018-07-16 stsp goto done;
963 80ddbec8 2018-04-29 stsp } else {
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();
970 ecb28ae0 2018-07-16 stsp goto done;
971 80ddbec8 2018-04-29 stsp }
972 80ddbec8 2018-04-29 stsp }
973 80ddbec8 2018-04-29 stsp if (error != NULL)
974 ecb28ae0 2018-07-16 stsp goto done;
975 ecb28ae0 2018-07-16 stsp
976 ecb28ae0 2018-07-16 stsp error = show_log_view(start_id, repo, path);
977 ecb28ae0 2018-07-16 stsp done:
978 ecb28ae0 2018-07-16 stsp free(repo_path);
979 ecb28ae0 2018-07-16 stsp free(cwd);
980 ecb28ae0 2018-07-16 stsp free(path);
981 899d86c2 2018-05-10 stsp free(start_id);
982 ecb28ae0 2018-07-16 stsp if (repo)
983 ecb28ae0 2018-07-16 stsp got_repo_close(repo);
984 80ddbec8 2018-04-29 stsp return error;
985 9f7d7167 2018-04-29 stsp }
986 9f7d7167 2018-04-29 stsp
987 4ed7e80c 2018-05-20 stsp __dead static void
988 9f7d7167 2018-04-29 stsp usage_diff(void)
989 9f7d7167 2018-04-29 stsp {
990 80ddbec8 2018-04-29 stsp endwin();
991 9f7d7167 2018-04-29 stsp fprintf(stderr, "usage: %s diff [repository-path] object1 object2\n",
992 9f7d7167 2018-04-29 stsp getprogname());
993 9f7d7167 2018-04-29 stsp exit(1);
994 b304db33 2018-05-20 stsp }
995 b304db33 2018-05-20 stsp
996 b304db33 2018-05-20 stsp static char *
997 b304db33 2018-05-20 stsp parse_next_line(FILE *f, size_t *len)
998 b304db33 2018-05-20 stsp {
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'};
1003 b304db33 2018-05-20 stsp
1004 b304db33 2018-05-20 stsp line = fparseln(f, &linelen, &lineno, delim, 0);
1005 b304db33 2018-05-20 stsp if (len)
1006 b304db33 2018-05-20 stsp *len = linelen;
1007 b304db33 2018-05-20 stsp return line;
1008 26ed57b2 2018-05-19 stsp }
1009 26ed57b2 2018-05-19 stsp
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)
1013 26ed57b2 2018-05-19 stsp {
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;
1020 26ed57b2 2018-05-19 stsp
1021 26ed57b2 2018-05-19 stsp rewind(f);
1022 a70480e0 2018-06-23 stsp werase(window);
1023 26ed57b2 2018-05-19 stsp
1024 26ed57b2 2018-05-19 stsp *eof = 0;
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) {
1028 26ed57b2 2018-05-19 stsp *eof = 1;
1029 26ed57b2 2018-05-19 stsp break;
1030 26ed57b2 2018-05-19 stsp }
1031 26ed57b2 2018-05-19 stsp if (++nlines < *first_displayed_line) {
1032 26ed57b2 2018-05-19 stsp free(line);
1033 26ed57b2 2018-05-19 stsp continue;
1034 26ed57b2 2018-05-19 stsp }
1035 26ed57b2 2018-05-19 stsp
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;
1041 61e69b96 2018-05-20 stsp }
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;
1050 26ed57b2 2018-05-19 stsp }
1051 26ed57b2 2018-05-19 stsp *last_displayed_line = nlines;
1052 26ed57b2 2018-05-19 stsp
1053 26ed57b2 2018-05-19 stsp update_panels();
1054 26ed57b2 2018-05-19 stsp doupdate();
1055 26ed57b2 2018-05-19 stsp
1056 26ed57b2 2018-05-19 stsp return NULL;
1057 9f7d7167 2018-04-29 stsp }
1058 9f7d7167 2018-04-29 stsp
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)
1062 26ed57b2 2018-05-19 stsp {
1063 26ed57b2 2018-05-19 stsp const struct got_error *err;
1064 26ed57b2 2018-05-19 stsp FILE *f;
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;
1067 26ed57b2 2018-05-19 stsp
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);
1071 26ed57b2 2018-05-19 stsp
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();
1075 26ed57b2 2018-05-19 stsp
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);
1079 26ed57b2 2018-05-19 stsp break;
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);
1082 26ed57b2 2018-05-19 stsp break;
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);
1085 26ed57b2 2018-05-19 stsp break;
1086 26ed57b2 2018-05-19 stsp default:
1087 26ed57b2 2018-05-19 stsp return got_error(GOT_ERR_OBJ_TYPE);
1088 26ed57b2 2018-05-19 stsp }
1089 26ed57b2 2018-05-19 stsp
1090 26ed57b2 2018-05-19 stsp fflush(f);
1091 26ed57b2 2018-05-19 stsp
1092 ea5e7bb5 2018-08-01 stsp show_panel(view->panel);
1093 26ed57b2 2018-05-19 stsp
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);
1097 26ed57b2 2018-05-19 stsp if (err)
1098 26ed57b2 2018-05-19 stsp break;
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) {
1103 26ed57b2 2018-05-19 stsp case 'q':
1104 26ed57b2 2018-05-19 stsp done = 1;
1105 26ed57b2 2018-05-19 stsp break;
1106 26ed57b2 2018-05-19 stsp case 'k':
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--;
1110 b304db33 2018-05-20 stsp break;
1111 b304db33 2018-05-20 stsp case KEY_PPAGE:
1112 c46b9352 2018-07-12 stsp case KEY_BACKSPACE:
1113 b304db33 2018-05-20 stsp i = 0;
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--;
1117 26ed57b2 2018-05-19 stsp break;
1118 26ed57b2 2018-05-19 stsp case 'j':
1119 26ed57b2 2018-05-19 stsp case KEY_DOWN:
1120 26ed57b2 2018-05-19 stsp if (!eof)
1121 26ed57b2 2018-05-19 stsp first_displayed_line++;
1122 26ed57b2 2018-05-19 stsp break;
1123 b304db33 2018-05-20 stsp case KEY_NPAGE:
1124 75e48879 2018-05-20 stsp case ' ':
1125 b304db33 2018-05-20 stsp i = 0;
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)
1130 b304db33 2018-05-20 stsp break;
1131 b304db33 2018-05-20 stsp }
1132 b304db33 2018-05-20 stsp break;
1133 26ed57b2 2018-05-19 stsp default:
1134 26ed57b2 2018-05-19 stsp break;
1135 26ed57b2 2018-05-19 stsp }
1136 26ed57b2 2018-05-19 stsp }
1137 26ed57b2 2018-05-19 stsp fclose(f);
1138 26ed57b2 2018-05-19 stsp return err;
1139 26ed57b2 2018-05-19 stsp }
1140 26ed57b2 2018-05-19 stsp
1141 4ed7e80c 2018-05-20 stsp static const struct got_error *
1142 9f7d7167 2018-04-29 stsp cmd_diff(int argc, char *argv[])
1143 9f7d7167 2018-04-29 stsp {
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;
1149 26ed57b2 2018-05-19 stsp int ch;
1150 ea5e7bb5 2018-08-01 stsp struct tog_view *view;
1151 26ed57b2 2018-05-19 stsp
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");
1155 26ed57b2 2018-05-19 stsp #endif
1156 26ed57b2 2018-05-19 stsp
1157 26ed57b2 2018-05-19 stsp while ((ch = getopt(argc, argv, "")) != -1) {
1158 26ed57b2 2018-05-19 stsp switch (ch) {
1159 26ed57b2 2018-05-19 stsp default:
1160 26ed57b2 2018-05-19 stsp usage();
1161 26ed57b2 2018-05-19 stsp /* NOTREACHED */
1162 26ed57b2 2018-05-19 stsp }
1163 26ed57b2 2018-05-19 stsp }
1164 26ed57b2 2018-05-19 stsp
1165 26ed57b2 2018-05-19 stsp argc -= optind;
1166 26ed57b2 2018-05-19 stsp argv += optind;
1167 26ed57b2 2018-05-19 stsp
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];
1182 26ed57b2 2018-05-19 stsp } else
1183 26ed57b2 2018-05-19 stsp usage_diff();
1184 26ed57b2 2018-05-19 stsp
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;
1189 26ed57b2 2018-05-19 stsp
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;
1193 26ed57b2 2018-05-19 stsp
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;
1197 26ed57b2 2018-05-19 stsp
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;
1202 ea5e7bb5 2018-08-01 stsp }
1203 ea5e7bb5 2018-08-01 stsp error = show_diff_view(view, obj1, obj2, repo);
1204 ea5e7bb5 2018-08-01 stsp close_view(view);
1205 26ed57b2 2018-05-19 stsp done:
1206 26ed57b2 2018-05-19 stsp got_repo_close(repo);
1207 26ed57b2 2018-05-19 stsp if (obj1)
1208 26ed57b2 2018-05-19 stsp got_object_close(obj1);
1209 26ed57b2 2018-05-19 stsp if (obj2)
1210 26ed57b2 2018-05-19 stsp got_object_close(obj2);
1211 26ed57b2 2018-05-19 stsp return error;
1212 9f7d7167 2018-04-29 stsp }
1213 9f7d7167 2018-04-29 stsp
1214 4ed7e80c 2018-05-20 stsp __dead static void
1215 9f7d7167 2018-04-29 stsp usage_blame(void)
1216 9f7d7167 2018-04-29 stsp {
1217 80ddbec8 2018-04-29 stsp endwin();
1218 a70480e0 2018-06-23 stsp fprintf(stderr, "usage: %s blame [-c commit] [repository-path] path\n",
1219 9f7d7167 2018-04-29 stsp getprogname());
1220 9f7d7167 2018-04-29 stsp exit(1);
1221 9f7d7167 2018-04-29 stsp }
1222 84451b3e 2018-07-10 stsp
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;
1226 84451b3e 2018-07-10 stsp };
1227 9f7d7167 2018-04-29 stsp
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)
1233 84451b3e 2018-07-10 stsp {
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;
1243 ab089a2a 2018-07-12 stsp
1244 ab089a2a 2018-07-12 stsp err = got_object_id_str(&id_str, id);
1245 ab089a2a 2018-07-12 stsp if (err)
1246 ab089a2a 2018-07-12 stsp return err;
1247 84451b3e 2018-07-10 stsp
1248 84451b3e 2018-07-10 stsp rewind(f);
1249 84451b3e 2018-07-10 stsp werase(window);
1250 84451b3e 2018-07-10 stsp
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;
1255 ab089a2a 2018-07-12 stsp }
1256 ab089a2a 2018-07-12 stsp
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');
1265 ab089a2a 2018-07-12 stsp
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();
1271 ab089a2a 2018-07-12 stsp }
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;
1276 3f60a8ef 2018-07-10 stsp if (err)
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');
1283 3f60a8ef 2018-07-10 stsp
1284 84451b3e 2018-07-10 stsp *eof = 0;
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) {
1288 84451b3e 2018-07-10 stsp *eof = 1;
1289 84451b3e 2018-07-10 stsp break;
1290 84451b3e 2018-07-10 stsp }
1291 84451b3e 2018-07-10 stsp if (++lineno < *first_displayed_line) {
1292 84451b3e 2018-07-10 stsp free(line);
1293 84451b3e 2018-07-10 stsp continue;
1294 84451b3e 2018-07-10 stsp }
1295 84451b3e 2018-07-10 stsp
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;
1301 84451b3e 2018-07-10 stsp }
1302 84451b3e 2018-07-10 stsp
1303 b700b5d6 2018-07-10 stsp if (nprinted == selected_line - 1)
1304 b700b5d6 2018-07-10 stsp wstandout(window);
1305 b700b5d6 2018-07-10 stsp
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;
1317 84451b3e 2018-07-10 stsp }
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;
1321 ee41ec32 2018-07-10 stsp } else {
1322 ee41ec32 2018-07-10 stsp waddstr(window, "........ ");
1323 ee41ec32 2018-07-10 stsp prev_id = NULL;
1324 ee41ec32 2018-07-10 stsp }
1325 84451b3e 2018-07-10 stsp
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' : ' '); */
1329 b700b5d6 2018-07-10 stsp width++;
1330 b700b5d6 2018-07-10 stsp }
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;
1338 84451b3e 2018-07-10 stsp }
1339 84451b3e 2018-07-10 stsp *last_displayed_line = lineno;
1340 84451b3e 2018-07-10 stsp
1341 84451b3e 2018-07-10 stsp update_panels();
1342 84451b3e 2018-07-10 stsp doupdate();
1343 84451b3e 2018-07-10 stsp
1344 84451b3e 2018-07-10 stsp return NULL;
1345 84451b3e 2018-07-10 stsp }
1346 84451b3e 2018-07-10 stsp
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;
1351 84451b3e 2018-07-10 stsp
1352 7cc84d77 2018-08-01 stsp struct tog_view *view;
1353 ab089a2a 2018-07-12 stsp struct got_object_id *commit_id;
1354 84451b3e 2018-07-10 stsp FILE *f;
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;
1360 84451b3e 2018-07-10 stsp };
1361 84451b3e 2018-07-10 stsp
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)
1364 84451b3e 2018-07-10 stsp {
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;
1368 84451b3e 2018-07-10 stsp int eof;
1369 84451b3e 2018-07-10 stsp
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);
1373 84451b3e 2018-07-10 stsp
1374 84451b3e 2018-07-10 stsp if (pthread_mutex_lock(a->mutex) != 0)
1375 84451b3e 2018-07-10 stsp return got_error_from_errno();
1376 84451b3e 2018-07-10 stsp
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;
1380 d68a0a7d 2018-07-10 stsp }
1381 d68a0a7d 2018-07-10 stsp
1382 d68a0a7d 2018-07-10 stsp if (lineno == -1)
1383 d68a0a7d 2018-07-10 stsp goto done; /* no change in this commit */
1384 d68a0a7d 2018-07-10 stsp
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;
1388 d68a0a7d 2018-07-10 stsp
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;
1393 84451b3e 2018-07-10 stsp }
1394 84451b3e 2018-07-10 stsp line->annotated = 1;
1395 84451b3e 2018-07-10 stsp
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);
1399 84451b3e 2018-07-10 stsp done:
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;
1403 84451b3e 2018-07-10 stsp }
1404 84451b3e 2018-07-10 stsp
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;
1410 84451b3e 2018-07-10 stsp };
1411 84451b3e 2018-07-10 stsp
1412 84451b3e 2018-07-10 stsp static void *
1413 84451b3e 2018-07-10 stsp blame_thread(void *arg)
1414 84451b3e 2018-07-10 stsp {
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;
1418 18430de3 2018-07-10 stsp int eof;
1419 18430de3 2018-07-10 stsp
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);
1422 18430de3 2018-07-10 stsp
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();
1425 18430de3 2018-07-10 stsp
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;
1429 c9beca56 2018-07-22 stsp if (!err)
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,
1433 c9beca56 2018-07-22 stsp LINES);
1434 18430de3 2018-07-10 stsp
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();
1437 18430de3 2018-07-10 stsp
1438 18430de3 2018-07-10 stsp return (void *)err;
1439 84451b3e 2018-07-10 stsp }
1440 84451b3e 2018-07-10 stsp
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)
1444 245d91c1 2018-07-12 stsp {
1445 245d91c1 2018-07-12 stsp struct tog_blame_line *line;
1446 b880a918 2018-07-10 stsp
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;
1450 245d91c1 2018-07-12 stsp
1451 245d91c1 2018-07-12 stsp return line->id;
1452 245d91c1 2018-07-12 stsp }
1453 245d91c1 2018-07-12 stsp
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)
1458 b880a918 2018-07-10 stsp {
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;
1463 b880a918 2018-07-10 stsp
1464 b880a918 2018-07-10 stsp *pobj = NULL;
1465 b880a918 2018-07-10 stsp *obj = NULL;
1466 b880a918 2018-07-10 stsp
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;
1471 b880a918 2018-07-10 stsp
1472 245d91c1 2018-07-12 stsp err = got_object_open(obj, repo, selected_id);
1473 b880a918 2018-07-10 stsp if (err)
1474 b880a918 2018-07-10 stsp goto done;
1475 b880a918 2018-07-10 stsp
1476 b880a918 2018-07-10 stsp err = got_object_commit_open(&commit, repo, *obj);
1477 b880a918 2018-07-10 stsp if (err)
1478 b880a918 2018-07-10 stsp goto done;
1479 b880a918 2018-07-10 stsp
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);
1483 b880a918 2018-07-10 stsp if (err)
1484 b880a918 2018-07-10 stsp goto done;
1485 b880a918 2018-07-10 stsp }
1486 b880a918 2018-07-10 stsp 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;
1490 b880a918 2018-07-10 stsp }
1491 b880a918 2018-07-10 stsp
1492 245d91c1 2018-07-12 stsp struct tog_blame {
1493 245d91c1 2018-07-12 stsp FILE *f;
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;
1501 245d91c1 2018-07-12 stsp };
1502 245d91c1 2018-07-12 stsp
1503 b880a918 2018-07-10 stsp static const struct got_error *
1504 245d91c1 2018-07-12 stsp stop_blame(struct tog_blame *blame)
1505 a70480e0 2018-06-23 stsp {
1506 a70480e0 2018-06-23 stsp const struct got_error *err = NULL;
1507 245d91c1 2018-07-12 stsp int i;
1508 245d91c1 2018-07-12 stsp
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;
1515 245d91c1 2018-07-12 stsp }
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;
1519 245d91c1 2018-07-12 stsp }
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;
1523 245d91c1 2018-07-12 stsp }
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;
1530 245d91c1 2018-07-12 stsp
1531 245d91c1 2018-07-12 stsp return err;
1532 245d91c1 2018-07-12 stsp }
1533 245d91c1 2018-07-12 stsp
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)
1541 245d91c1 2018-07-12 stsp {
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;
1546 a70480e0 2018-06-23 stsp
1547 84451b3e 2018-07-10 stsp err = got_object_open_by_path(&obj, repo, commit_id, path);
1548 84451b3e 2018-07-10 stsp if (err)
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;
1553 84451b3e 2018-07-10 stsp }
1554 a70480e0 2018-06-23 stsp
1555 84451b3e 2018-07-10 stsp err = got_object_blob_open(&blob, repo, obj, 8192);
1556 a70480e0 2018-06-23 stsp if (err)
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;
1562 84451b3e 2018-07-10 stsp }
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);
1565 84451b3e 2018-07-10 stsp if (err)
1566 84451b3e 2018-07-10 stsp goto done;
1567 a70480e0 2018-06-23 stsp
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;
1572 84451b3e 2018-07-10 stsp }
1573 a70480e0 2018-06-23 stsp
1574 245d91c1 2018-07-12 stsp err = got_repo_open(&thread_repo, got_repo_get_path(repo));
1575 bd24772e 2018-07-11 stsp if (err)
1576 bd24772e 2018-07-11 stsp goto done;
1577 bd24772e 2018-07-11 stsp
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;
1586 245d91c1 2018-07-12 stsp }
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;
1593 245d91c1 2018-07-12 stsp
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;
1599 245d91c1 2018-07-12 stsp
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;
1604 245d91c1 2018-07-12 stsp }
1605 245d91c1 2018-07-12 stsp
1606 245d91c1 2018-07-12 stsp done:
1607 245d91c1 2018-07-12 stsp if (blob)
1608 245d91c1 2018-07-12 stsp got_object_blob_close(blob);
1609 245d91c1 2018-07-12 stsp if (obj)
1610 245d91c1 2018-07-12 stsp got_object_close(obj);
1611 245d91c1 2018-07-12 stsp if (err)
1612 245d91c1 2018-07-12 stsp stop_blame(blame);
1613 245d91c1 2018-07-12 stsp return err;
1614 245d91c1 2018-07-12 stsp }
1615 245d91c1 2018-07-12 stsp
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)
1619 245d91c1 2018-07-12 stsp {
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;
1630 dbc6a6b6 2018-07-12 stsp
1631 dbc6a6b6 2018-07-12 stsp SIMPLEQ_INIT(&blamed_commits);
1632 245d91c1 2018-07-12 stsp
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;
1636 245d91c1 2018-07-12 stsp }
1637 245d91c1 2018-07-12 stsp
1638 dbc6a6b6 2018-07-12 stsp err = got_object_qid_alloc(&blamed_commit, commit_id);
1639 dbc6a6b6 2018-07-12 stsp if (err)
1640 245d91c1 2018-07-12 stsp goto done;
1641 dbc6a6b6 2018-07-12 stsp SIMPLEQ_INSERT_HEAD(&blamed_commits, blamed_commit, entry);
1642 245d91c1 2018-07-12 stsp
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;
1647 a70480e0 2018-06-23 stsp }
1648 7cc84d77 2018-08-01 stsp show_panel(view->panel);
1649 a70480e0 2018-06-23 stsp
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);
1654 245d91c1 2018-07-12 stsp if (err)
1655 245d91c1 2018-07-12 stsp return err;
1656 84451b3e 2018-07-10 stsp
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;
1661 84451b3e 2018-07-10 stsp }
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;
1669 84451b3e 2018-07-10 stsp }
1670 a70480e0 2018-06-23 stsp if (err)
1671 a70480e0 2018-06-23 stsp break;
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;
1678 84451b3e 2018-07-10 stsp }
1679 a70480e0 2018-06-23 stsp switch (ch) {
1680 a70480e0 2018-06-23 stsp case 'q':
1681 a70480e0 2018-06-23 stsp done = 1;
1682 a70480e0 2018-06-23 stsp break;
1683 a70480e0 2018-06-23 stsp case 'k':
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--;
1690 a70480e0 2018-06-23 stsp break;
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;
1695 b700b5d6 2018-07-10 stsp break;
1696 b700b5d6 2018-07-10 stsp }
1697 084063cd 2018-07-12 stsp if (first_displayed_line > LINES - 2)
1698 084063cd 2018-07-12 stsp first_displayed_line -= (LINES - 2);
1699 b700b5d6 2018-07-10 stsp else
1700 b700b5d6 2018-07-10 stsp first_displayed_line = 1;
1701 a70480e0 2018-06-23 stsp break;
1702 a70480e0 2018-06-23 stsp case 'j':
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++;
1710 b700b5d6 2018-07-10 stsp break;
1711 7a2921f9 2018-07-12 stsp case 'b':
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)
1718 245d91c1 2018-07-12 stsp break;
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);
1722 ad6a6c43 2018-07-10 stsp if (err)
1723 245d91c1 2018-07-12 stsp break;
1724 245d91c1 2018-07-12 stsp if (pobj == NULL && obj == NULL)
1725 245d91c1 2018-07-12 stsp break;
1726 7a2921f9 2018-07-12 stsp if (ch == 'p' && pobj == NULL)
1727 7a2921f9 2018-07-12 stsp break;
1728 245d91c1 2018-07-12 stsp done = 1;
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;
1732 245d91c1 2018-07-12 stsp }
1733 245d91c1 2018-07-12 stsp thread_err = stop_blame(&blame);
1734 245d91c1 2018-07-12 stsp done = 0;
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;
1738 245d91c1 2018-07-12 stsp }
1739 245d91c1 2018-07-12 stsp if (thread_err)
1740 245d91c1 2018-07-12 stsp break;
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;
1747 1960a6f9 2018-07-12 stsp }
1748 dbc6a6b6 2018-07-12 stsp if (id == NULL) {
1749 245d91c1 2018-07-12 stsp err = got_error_from_errno();
1750 245d91c1 2018-07-12 stsp break;
1751 245d91c1 2018-07-12 stsp }
1752 dbc6a6b6 2018-07-12 stsp err = got_object_qid_alloc(&blamed_commit, id);
1753 dbc6a6b6 2018-07-12 stsp free(id);
1754 dbc6a6b6 2018-07-12 stsp if (err)
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);
1762 dbc6a6b6 2018-07-12 stsp if (err)
1763 dbc6a6b6 2018-07-12 stsp break;
1764 dbc6a6b6 2018-07-12 stsp break;
1765 dbc6a6b6 2018-07-12 stsp }
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))
1770 dbc6a6b6 2018-07-12 stsp break;
1771 dbc6a6b6 2018-07-12 stsp done = 1;
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;
1775 dbc6a6b6 2018-07-12 stsp }
1776 dbc6a6b6 2018-07-12 stsp thread_err = stop_blame(&blame);
1777 dbc6a6b6 2018-07-12 stsp done = 0;
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;
1781 dbc6a6b6 2018-07-12 stsp }
1782 dbc6a6b6 2018-07-12 stsp if (thread_err)
1783 dbc6a6b6 2018-07-12 stsp break;
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);
1791 245d91c1 2018-07-12 stsp if (err)
1792 245d91c1 2018-07-12 stsp break;
1793 245d91c1 2018-07-12 stsp break;
1794 245d91c1 2018-07-12 stsp }
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);
1800 245d91c1 2018-07-12 stsp if (err)
1801 245d91c1 2018-07-12 stsp break;
1802 b880a918 2018-07-10 stsp if (pobj == NULL && obj == NULL)
1803 b880a918 2018-07-10 stsp break;
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();
1807 ea5e7bb5 2018-08-01 stsp break;
1808 ea5e7bb5 2018-08-01 stsp }
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;
1814 fb311a66 2018-07-10 stsp }
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);
1818 ad6a6c43 2018-07-10 stsp if (err)
1819 245d91c1 2018-07-12 stsp break;
1820 a70480e0 2018-06-23 stsp break;
1821 a70480e0 2018-06-23 stsp case KEY_NPAGE:
1822 a70480e0 2018-06-23 stsp case ' ':
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);
1827 b700b5d6 2018-07-10 stsp break;
1828 a70480e0 2018-06-23 stsp }
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;
1832 b700b5d6 2018-07-10 stsp else
1833 b700b5d6 2018-07-10 stsp first_displayed_line =
1834 245d91c1 2018-07-12 stsp blame.nlines - (LINES - 3);
1835 a70480e0 2018-06-23 stsp break;
1836 a70480e0 2018-06-23 stsp default:
1837 a70480e0 2018-06-23 stsp break;
1838 84451b3e 2018-07-10 stsp }
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)
1842 245d91c1 2018-07-12 stsp break;
1843 a70480e0 2018-06-23 stsp }
1844 a70480e0 2018-06-23 stsp done:
1845 b880a918 2018-07-10 stsp if (pobj)
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);
1849 7cc84d77 2018-08-01 stsp if (view)
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);
1855 dbc6a6b6 2018-07-12 stsp }
1856 245d91c1 2018-07-12 stsp return thread_err ? thread_err : err;
1857 a70480e0 2018-06-23 stsp }
1858 a70480e0 2018-06-23 stsp
1859 a70480e0 2018-06-23 stsp static const struct got_error *
1860 9f7d7167 2018-04-29 stsp cmd_blame(int argc, char *argv[])
1861 9f7d7167 2018-04-29 stsp {
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;
1868 a70480e0 2018-06-23 stsp int ch;
1869 a70480e0 2018-06-23 stsp
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");
1873 a70480e0 2018-06-23 stsp #endif
1874 a70480e0 2018-06-23 stsp
1875 a70480e0 2018-06-23 stsp while ((ch = getopt(argc, argv, "c:")) != -1) {
1876 a70480e0 2018-06-23 stsp switch (ch) {
1877 a70480e0 2018-06-23 stsp case 'c':
1878 a70480e0 2018-06-23 stsp commit_id_str = optarg;
1879 a70480e0 2018-06-23 stsp break;
1880 a70480e0 2018-06-23 stsp default:
1881 a70480e0 2018-06-23 stsp usage();
1882 a70480e0 2018-06-23 stsp /* NOTREACHED */
1883 a70480e0 2018-06-23 stsp }
1884 a70480e0 2018-06-23 stsp }
1885 a70480e0 2018-06-23 stsp
1886 a70480e0 2018-06-23 stsp argc -= optind;
1887 a70480e0 2018-06-23 stsp argv += optind;
1888 a70480e0 2018-06-23 stsp
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];
1901 a70480e0 2018-06-23 stsp } else
1902 a70480e0 2018-06-23 stsp usage_blame();
1903 a70480e0 2018-06-23 stsp
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;
1908 a70480e0 2018-06-23 stsp
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);
1916 a70480e0 2018-06-23 stsp } else {
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);
1925 a70480e0 2018-06-23 stsp }
1926 a19e88aa 2018-06-23 stsp if (error != NULL)
1927 a19e88aa 2018-06-23 stsp goto done;
1928 a70480e0 2018-06-23 stsp
1929 a70480e0 2018-06-23 stsp error = show_blame_view(path, commit_id, repo);
1930 a70480e0 2018-06-23 stsp done:
1931 a70480e0 2018-06-23 stsp free(commit_id);
1932 a70480e0 2018-06-23 stsp if (repo)
1933 a70480e0 2018-06-23 stsp got_repo_close(repo);
1934 a70480e0 2018-06-23 stsp return error;
1935 ffd1d5e5 2018-06-23 stsp }
1936 ffd1d5e5 2018-06-23 stsp
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)
1943 ffd1d5e5 2018-06-23 stsp {
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;
1948 ffd1d5e5 2018-06-23 stsp
1949 ffd1d5e5 2018-06-23 stsp *ndisplayed = 0;
1950 ffd1d5e5 2018-06-23 stsp
1951 ffd1d5e5 2018-06-23 stsp werase(window);
1952 ffd1d5e5 2018-06-23 stsp
1953 ffd1d5e5 2018-06-23 stsp if (limit == 0)
1954 ffd1d5e5 2018-06-23 stsp return NULL;
1955 ffd1d5e5 2018-06-23 stsp
1956 ffd1d5e5 2018-06-23 stsp err = format_line(&wline, &width, label, COLS);
1957 ffd1d5e5 2018-06-23 stsp if (err)
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);
1967 ce52c690 2018-06-23 stsp if (err)
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;
1979 ffd1d5e5 2018-06-23 stsp
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;
1985 ffd1d5e5 2018-06-23 stsp }
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;
1992 ffd1d5e5 2018-06-23 stsp n = 1;
1993 ffd1d5e5 2018-06-23 stsp } else {
1994 ffd1d5e5 2018-06-23 stsp n = 0;
1995 ffd1d5e5 2018-06-23 stsp while (te != *first_displayed_entry)
1996 ffd1d5e5 2018-06-23 stsp te = SIMPLEQ_NEXT(te, entry);
1997 ffd1d5e5 2018-06-23 stsp }
1998 ffd1d5e5 2018-06-23 stsp
1999 ffd1d5e5 2018-06-23 stsp while (te) {
2000 1d13200f 2018-07-12 stsp char *line = NULL, *id_str = NULL;
2001 1d13200f 2018-07-12 stsp
2002 1d13200f 2018-07-12 stsp if (show_ids) {
2003 1d13200f 2018-07-12 stsp err = got_object_id_str(&id_str, te->id);
2004 1d13200f 2018-07-12 stsp if (err)
2005 1d13200f 2018-07-12 stsp return got_error_from_errno();
2006 1d13200f 2018-07-12 stsp }
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();
2011 1d13200f 2018-07-12 stsp }
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);
2016 ffd1d5e5 2018-06-23 stsp break;
2017 ffd1d5e5 2018-06-23 stsp }
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;
2021 ffd1d5e5 2018-06-23 stsp }
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;
2030 ffd1d5e5 2018-06-23 stsp n++;
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)
2034 ffd1d5e5 2018-06-23 stsp break;
2035 ffd1d5e5 2018-06-23 stsp te = SIMPLEQ_NEXT(te, entry);
2036 ffd1d5e5 2018-06-23 stsp }
2037 ffd1d5e5 2018-06-23 stsp
2038 ffd1d5e5 2018-06-23 stsp return err;
2039 ffd1d5e5 2018-06-23 stsp }
2040 ffd1d5e5 2018-06-23 stsp
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)
2044 ffd1d5e5 2018-06-23 stsp {
2045 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *te, *prev;
2046 ffd1d5e5 2018-06-23 stsp int i;
2047 ffd1d5e5 2018-06-23 stsp
2048 ffd1d5e5 2018-06-23 stsp if (*first_displayed_entry == NULL)
2049 ffd1d5e5 2018-06-23 stsp return;
2050 ffd1d5e5 2018-06-23 stsp
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;
2055 ffd1d5e5 2018-06-23 stsp return;
2056 ffd1d5e5 2018-06-23 stsp }
2057 ffd1d5e5 2018-06-23 stsp
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);
2063 ffd1d5e5 2018-06-23 stsp }
2064 ffd1d5e5 2018-06-23 stsp *first_displayed_entry = prev;
2065 ffd1d5e5 2018-06-23 stsp te = SIMPLEQ_FIRST(&entries->head);
2066 ffd1d5e5 2018-06-23 stsp }
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;
2069 ffd1d5e5 2018-06-23 stsp }
2070 ffd1d5e5 2018-06-23 stsp
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)
2075 ffd1d5e5 2018-06-23 stsp {
2076 ffd1d5e5 2018-06-23 stsp struct got_tree_entry *next;
2077 ffd1d5e5 2018-06-23 stsp int n = 0;
2078 ffd1d5e5 2018-06-23 stsp
2079 ffd1d5e5 2018-06-23 stsp if (SIMPLEQ_NEXT(last_displayed_entry, entry) == NULL)
2080 ffd1d5e5 2018-06-23 stsp return;
2081 ffd1d5e5 2018-06-23 stsp
2082 ffd1d5e5 2018-06-23 stsp if (*first_displayed_entry)
2083 ffd1d5e5 2018-06-23 stsp next = SIMPLEQ_NEXT(*first_displayed_entry, entry);
2084 ffd1d5e5 2018-06-23 stsp else
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)
2089 ffd1d5e5 2018-06-23 stsp break;
2090 ffd1d5e5 2018-06-23 stsp next = SIMPLEQ_NEXT(next, entry);
2091 ffd1d5e5 2018-06-23 stsp }
2092 ffd1d5e5 2018-06-23 stsp }
2093 ffd1d5e5 2018-06-23 stsp
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;
2100 ffd1d5e5 2018-06-23 stsp };
2101 ffd1d5e5 2018-06-23 stsp
2102 d9765a41 2018-06-23 stsp TAILQ_HEAD(tog_parent_trees, tog_parent_tree);
2103 ffd1d5e5 2018-06-23 stsp
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)
2107 ffd1d5e5 2018-06-23 stsp {
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 */
2111 ffd1d5e5 2018-06-23 stsp
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 */;
2114 ce52c690 2018-06-23 stsp if (te)
2115 ce52c690 2018-06-23 stsp len += strlen(te->name);
2116 ce52c690 2018-06-23 stsp
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();
2120 ffd1d5e5 2018-06-23 stsp
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;
2127 cb2ebc8a 2018-06-23 stsp }
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;
2131 cb2ebc8a 2018-06-23 stsp }
2132 d9765a41 2018-06-23 stsp pt = TAILQ_PREV(pt, tog_parent_trees, entry);
2133 ffd1d5e5 2018-06-23 stsp }
2134 ce52c690 2018-06-23 stsp if (te) {
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;
2138 ce52c690 2018-06-23 stsp }
2139 cb2ebc8a 2018-06-23 stsp }
2140 ce52c690 2018-06-23 stsp 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;
2144 ce52c690 2018-06-23 stsp }
2145 ce52c690 2018-06-23 stsp return err;
2146 ce52c690 2018-06-23 stsp }
2147 ce52c690 2018-06-23 stsp
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)
2151 ce52c690 2018-06-23 stsp {
2152 ce52c690 2018-06-23 stsp const struct got_error *err = NULL;
2153 ce52c690 2018-06-23 stsp char *path;
2154 69efd4c4 2018-07-18 stsp
2155 ce52c690 2018-06-23 stsp err = tree_entry_path(&path, parents, te);
2156 ce52c690 2018-06-23 stsp if (err)
2157 ce52c690 2018-06-23 stsp return err;
2158 ffd1d5e5 2018-06-23 stsp
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;
2162 69efd4c4 2018-07-18 stsp }
2163 69efd4c4 2018-07-18 stsp
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)
2167 69efd4c4 2018-07-18 stsp {
2168 69efd4c4 2018-07-18 stsp const struct got_error *err = NULL;
2169 69efd4c4 2018-07-18 stsp char *path;
2170 69efd4c4 2018-07-18 stsp
2171 69efd4c4 2018-07-18 stsp err = tree_entry_path(&path, parents, te);
2172 69efd4c4 2018-07-18 stsp if (err)
2173 69efd4c4 2018-07-18 stsp return err;
2174 69efd4c4 2018-07-18 stsp
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;
2178 ffd1d5e5 2018-06-23 stsp }
2179 ffd1d5e5 2018-06-23 stsp
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)
2183 ffd1d5e5 2018-06-23 stsp {
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;
2195 ffd1d5e5 2018-06-23 stsp
2196 d9765a41 2018-06-23 stsp TAILQ_INIT(&parents);
2197 ffd1d5e5 2018-06-23 stsp
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;
2201 ffd1d5e5 2018-06-23 stsp
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;
2205 ffd1d5e5 2018-06-23 stsp }
2206 ffd1d5e5 2018-06-23 stsp
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;
2211 ffd1d5e5 2018-06-23 stsp }
2212 cc3c9aac 2018-08-01 stsp show_panel(view->panel);
2213 ffd1d5e5 2018-06-23 stsp
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 */
2222 ce52c690 2018-06-23 stsp
2223 ce52c690 2018-06-23 stsp err = tree_entry_path(&parent_path, &parents, NULL);
2224 ce52c690 2018-06-23 stsp if (err)
2225 ce52c690 2018-06-23 stsp goto done;
2226 ffd1d5e5 2018-06-23 stsp
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);
2232 ffd1d5e5 2018-06-23 stsp if (err)
2233 ffd1d5e5 2018-06-23 stsp break;
2234 ffd1d5e5 2018-06-23 stsp
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) {
2239 ffd1d5e5 2018-06-23 stsp case 'q':
2240 ffd1d5e5 2018-06-23 stsp done = 1;
2241 ffd1d5e5 2018-06-23 stsp break;
2242 1d13200f 2018-07-12 stsp case 'i':
2243 1d13200f 2018-07-12 stsp show_ids = !show_ids;
2244 69efd4c4 2018-07-18 stsp break;
2245 69efd4c4 2018-07-18 stsp case 'l':
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);
2249 69efd4c4 2018-07-18 stsp if (err)
2250 69efd4c4 2018-07-18 stsp goto done;
2251 69efd4c4 2018-07-18 stsp }
2252 1d13200f 2018-07-12 stsp break;
2253 ffd1d5e5 2018-06-23 stsp case 'k':
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)
2258 ffd1d5e5 2018-06-23 stsp break;
2259 ffd1d5e5 2018-06-23 stsp tree_scroll_up(&first_displayed_entry, 1,
2260 ffd1d5e5 2018-06-23 stsp entries, tree == root);
2261 ffd1d5e5 2018-06-23 stsp break;
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;
2268 ffd1d5e5 2018-06-23 stsp break;
2269 ffd1d5e5 2018-06-23 stsp }
2270 ffd1d5e5 2018-06-23 stsp tree_scroll_up(&first_displayed_entry, LINES,
2271 ffd1d5e5 2018-06-23 stsp entries, tree == root);
2272 ffd1d5e5 2018-06-23 stsp break;
2273 ffd1d5e5 2018-06-23 stsp case 'j':
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++;
2277 ffd1d5e5 2018-06-23 stsp break;
2278 ffd1d5e5 2018-06-23 stsp }
2279 ffd1d5e5 2018-06-23 stsp tree_scroll_down(&first_displayed_entry, 1,
2280 ffd1d5e5 2018-06-23 stsp last_displayed_entry, entries);
2281 ffd1d5e5 2018-06-23 stsp break;
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))
2286 ffd1d5e5 2018-06-23 stsp break;
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;
2290 ffd1d5e5 2018-06-23 stsp break;
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)
2298 ffd1d5e5 2018-06-23 stsp break;
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);
2313 ffd1d5e5 2018-06-23 stsp if (err)
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;
2319 ffd1d5e5 2018-06-23 stsp }
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,
2326 ffd1d5e5 2018-06-23 stsp entry);
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);
2333 ffd1d5e5 2018-06-23 stsp if (err)
2334 ffd1d5e5 2018-06-23 stsp goto done;
2335 ffd1d5e5 2018-06-23 stsp }
2336 ffd1d5e5 2018-06-23 stsp break;
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;
2340 ffd1d5e5 2018-06-23 stsp break;
2341 ffd1d5e5 2018-06-23 stsp default:
2342 ffd1d5e5 2018-06-23 stsp break;
2343 ffd1d5e5 2018-06-23 stsp }
2344 ffd1d5e5 2018-06-23 stsp }
2345 ffd1d5e5 2018-06-23 stsp done:
2346 cc3c9aac 2018-08-01 stsp if (view)
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);
2355 ffd1d5e5 2018-06-23 stsp
2356 ffd1d5e5 2018-06-23 stsp }
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;
2360 9f7d7167 2018-04-29 stsp }
2361 9f7d7167 2018-04-29 stsp
2362 ffd1d5e5 2018-06-23 stsp __dead static void
2363 ffd1d5e5 2018-06-23 stsp usage_tree(void)
2364 ffd1d5e5 2018-06-23 stsp {
2365 ffd1d5e5 2018-06-23 stsp endwin();
2366 ffd1d5e5 2018-06-23 stsp fprintf(stderr, "usage: %s tree [-c commit] [repository-path]\n",
2367 ffd1d5e5 2018-06-23 stsp getprogname());
2368 ffd1d5e5 2018-06-23 stsp exit(1);
2369 ffd1d5e5 2018-06-23 stsp }
2370 ffd1d5e5 2018-06-23 stsp
2371 ffd1d5e5 2018-06-23 stsp static const struct got_error *
2372 ffd1d5e5 2018-06-23 stsp cmd_tree(int argc, char *argv[])
2373 ffd1d5e5 2018-06-23 stsp {
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;
2381 ffd1d5e5 2018-06-23 stsp int ch;
2382 ffd1d5e5 2018-06-23 stsp
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");
2386 ffd1d5e5 2018-06-23 stsp #endif
2387 ffd1d5e5 2018-06-23 stsp
2388 ffd1d5e5 2018-06-23 stsp while ((ch = getopt(argc, argv, "c:")) != -1) {
2389 ffd1d5e5 2018-06-23 stsp switch (ch) {
2390 ffd1d5e5 2018-06-23 stsp case 'c':
2391 ffd1d5e5 2018-06-23 stsp commit_id_arg = optarg;
2392 ffd1d5e5 2018-06-23 stsp break;
2393 ffd1d5e5 2018-06-23 stsp default:
2394 ffd1d5e5 2018-06-23 stsp usage();
2395 ffd1d5e5 2018-06-23 stsp /* NOTREACHED */
2396 ffd1d5e5 2018-06-23 stsp }
2397 ffd1d5e5 2018-06-23 stsp }
2398 ffd1d5e5 2018-06-23 stsp
2399 ffd1d5e5 2018-06-23 stsp argc -= optind;
2400 ffd1d5e5 2018-06-23 stsp argv += optind;
2401 ffd1d5e5 2018-06-23 stsp
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();
2410 ffd1d5e5 2018-06-23 stsp } else
2411 ffd1d5e5 2018-06-23 stsp usage_log();
2412 ffd1d5e5 2018-06-23 stsp
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;
2417 ffd1d5e5 2018-06-23 stsp
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;
2422 ffd1d5e5 2018-06-23 stsp } else {
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();
2429 ffd1d5e5 2018-06-23 stsp }
2430 ffd1d5e5 2018-06-23 stsp }
2431 ffd1d5e5 2018-06-23 stsp if (error != NULL)
2432 ffd1d5e5 2018-06-23 stsp goto done;
2433 ffd1d5e5 2018-06-23 stsp
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;
2437 ffd1d5e5 2018-06-23 stsp
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;
2441 ffd1d5e5 2018-06-23 stsp
2442 ffd1d5e5 2018-06-23 stsp error = show_tree_view(tree, commit_id, repo);
2443 ffd1d5e5 2018-06-23 stsp done:
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);
2447 ffd1d5e5 2018-06-23 stsp if (tree)
2448 ffd1d5e5 2018-06-23 stsp got_object_tree_close(tree);
2449 ffd1d5e5 2018-06-23 stsp if (repo)
2450 ffd1d5e5 2018-06-23 stsp got_repo_close(repo);
2451 ffd1d5e5 2018-06-23 stsp return error;
2452 ffd1d5e5 2018-06-23 stsp }
2453 5c5136c5 2018-05-20 stsp static void
2454 9f7d7167 2018-04-29 stsp init_curses(void)
2455 9f7d7167 2018-04-29 stsp {
2456 9f7d7167 2018-04-29 stsp initscr();
2457 9f7d7167 2018-04-29 stsp cbreak();
2458 9f7d7167 2018-04-29 stsp noecho();
2459 9f7d7167 2018-04-29 stsp nonl();
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);
2463 9f7d7167 2018-04-29 stsp }
2464 9f7d7167 2018-04-29 stsp
2465 4ed7e80c 2018-05-20 stsp __dead static void
2466 9f7d7167 2018-04-29 stsp usage(void)
2467 9f7d7167 2018-04-29 stsp {
2468 9f7d7167 2018-04-29 stsp int i;
2469 9f7d7167 2018-04-29 stsp
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);
2475 9f7d7167 2018-04-29 stsp }
2476 9f7d7167 2018-04-29 stsp exit(1);
2477 9f7d7167 2018-04-29 stsp }
2478 9f7d7167 2018-04-29 stsp
2479 c2301be8 2018-04-30 stsp static char **
2480 c2301be8 2018-04-30 stsp make_argv(const char *arg0, const char *arg1)
2481 c2301be8 2018-04-30 stsp {
2482 c2301be8 2018-04-30 stsp char **argv;
2483 c2301be8 2018-04-30 stsp int argc = (arg1 == NULL ? 1 : 2);
2484 c2301be8 2018-04-30 stsp
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");
2495 c2301be8 2018-04-30 stsp }
2496 c2301be8 2018-04-30 stsp
2497 c2301be8 2018-04-30 stsp return argv;
2498 c2301be8 2018-04-30 stsp }
2499 c2301be8 2018-04-30 stsp
2500 9f7d7167 2018-04-29 stsp int
2501 9f7d7167 2018-04-29 stsp main(int argc, char *argv[])
2502 9f7d7167 2018-04-29 stsp {
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;
2507 9f7d7167 2018-04-29 stsp
2508 9f7d7167 2018-04-29 stsp setlocale(LC_ALL, "");
2509 9f7d7167 2018-04-29 stsp
2510 9f7d7167 2018-04-29 stsp while ((ch = getopt(argc, argv, "h")) != -1) {
2511 9f7d7167 2018-04-29 stsp switch (ch) {
2512 9f7d7167 2018-04-29 stsp case 'h':
2513 9f7d7167 2018-04-29 stsp hflag = 1;
2514 9f7d7167 2018-04-29 stsp break;
2515 9f7d7167 2018-04-29 stsp default:
2516 9f7d7167 2018-04-29 stsp usage();
2517 9f7d7167 2018-04-29 stsp /* NOTREACHED */
2518 9f7d7167 2018-04-29 stsp }
2519 9f7d7167 2018-04-29 stsp }
2520 9f7d7167 2018-04-29 stsp
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;
2525 9f7d7167 2018-04-29 stsp
2526 c2301be8 2018-04-30 stsp if (argc == 0) {
2527 f29d3e89 2018-06-23 stsp if (hflag)
2528 f29d3e89 2018-06-23 stsp usage();
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);
2532 c2301be8 2018-04-30 stsp argc = 1;
2533 c2301be8 2018-04-30 stsp } else {
2534 9f7d7167 2018-04-29 stsp int i;
2535 9f7d7167 2018-04-29 stsp
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();
2543 9f7d7167 2018-04-29 stsp break;
2544 9f7d7167 2018-04-29 stsp }
2545 9f7d7167 2018-04-29 stsp }
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);
2554 c2301be8 2018-04-30 stsp } else
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(),
2560 f29d3e89 2018-06-23 stsp argv[0]);
2561 f29d3e89 2018-06-23 stsp usage();
2562 f29d3e89 2018-06-23 stsp }
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);
2567 c2301be8 2018-04-30 stsp return 1;
2568 c2301be8 2018-04-30 stsp }
2569 c2301be8 2018-04-30 stsp cmd = &tog_commands[0];
2570 c2301be8 2018-04-30 stsp cmd_argv = make_argv(cmd->name, repo_path);
2571 c2301be8 2018-04-30 stsp argc = 2;
2572 c2301be8 2018-04-30 stsp free(repo_path);
2573 9f7d7167 2018-04-29 stsp }
2574 9f7d7167 2018-04-29 stsp }
2575 9f7d7167 2018-04-29 stsp
2576 5c5136c5 2018-05-20 stsp init_curses();
2577 9f7d7167 2018-04-29 stsp
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;
2581 9f7d7167 2018-04-29 stsp done:
2582 9f7d7167 2018-04-29 stsp endwin();
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);
2586 9f7d7167 2018-04-29 stsp return 0;
2587 9f7d7167 2018-04-29 stsp }