From: Tracey Emery Subject: Re: implement tog ref command To: gameoftrees@openbsd.org Date: Mon, 23 Nov 2020 09:57:15 -0700 On Mon, Nov 23, 2020 at 01:44:07PM +0100, Stefan Sperling wrote: > This adds a reference view to tog. > > A ref view can be opened with 'tog ref', and from the log view > with 'r' which makes it easier to navigate between branches. > > This patch requires both the tog diff improvements patch and > the tog ref list patch I have already sent. > > ok? That's awesome! ok to all three tracey Switching branches is super smooth! > > diff refs/heads/togreflist refs/heads/togref > blob - bba1d695475b78e770ce4a96eb79d9359c360c0e > blob + a8636d6509dcc3d01f0ad75d42151d795bca5ed3 > --- tog/tog.1 > +++ tog/tog.1 > @@ -50,6 +50,8 @@ Displays changes made in a particular commit. > Displays the line-by-line history of a file. > .It Tree view > Displays the tree corresponding to a particular commit. > +.It Ref view > +Displays references in the repository. > .El > .Pp > .Nm > @@ -144,6 +146,13 @@ Reload the log view and toggle display of merged commi > The > .Fl b > option determines whether merged commits are displayed initially. > +.It Cm r > +Open a > +.Cm ref > +view listing all references in the repository. > +This can then be used to open a new > +.Cm log > +view for arbitrary branches and tags. > .El > .Pp > The options for > @@ -373,7 +382,53 @@ If this directory is a > .Xr got 1 > work tree, use the repository path associated with this work tree. > .El > +.It Cm ref Oo Fl r Ar repository-path Oc > +Display references in the repository. > +.Pp > +The key bindings for > +.Cm tog ref > +are as follows: > +.Bl -tag -width Ds > +.It Cm Down-arrow, j > +Move the selection cursor down. > +.It Cm Up-arrow, k > +Move the selection cursor up. > +.It Cm Page-down, Ctrl+f > +Move the selection cursor down one page. > +.It Cm Page-up, Ctrl+b > +Move the selection cursor up one page. > +.It Cm Enter > +Open a log view which begins traversing history at the commit resolved via the > +currently selected reference. > +.It Cm i > +Show object IDs for all non-symbolic references displayed in the > +.Cm ref > +view. > +.It Cm / > +Prompt for a search pattern and start searching for matching references. > +The search pattern is an extended regular expression which is matched > +against absolute reference names. > +Regular expression syntax is documented in > +.Xr re_format 7 . > +.It Cm n > +Find the next reference which matches the current search pattern. > +.It Cm N > +Find the previous reference which matches the current search pattern. > .El > +.Pp > +The options for > +.Cm tog ref > +are as follows: > +.Bl -tag -width Ds > +.It Fl r Ar repository-path > +Use the repository at the specified path. > +If not specified, assume the repository is located at or above the current > +working directory. > +If this directory is a > +.Xr got 1 > +work tree, use the repository path associated with this work tree. > +.El > +.El > .Sh ENVIRONMENT > .Bl -tag -width TOG_COLORS > .It Ev TOG_COLORS > @@ -447,6 +502,27 @@ The color used to mark up date information. > If not set, the default value > .Dq yellow > is used. > +.It Ev TOG_COLOR_REFS_HEADS > +The color used to mark up references in the > +.Dq refs/heads/ > +namespace. > +If not set, the default value > +.Dq green > +is used. > +.It Ev TOG_COLOR_REFS_TAGS > +The color used to mark up references in the > +.Dq refs/tags/ > +namespace. > +If not set, the default value > +.Dq magenta > +is used. > +.It Ev TOG_COLOR_REFS_REMOTES > +The color used to mark up references in the > +.Dq refs/remotes/ > +namespace. > +If not set, the default value > +.Dq yellow > +is used. > .El > .Sh EXIT STATUS > .Ex -std tog > blob - 095716c1bd7e25ddf8b9ce47ab4a1ce69a63f0bb > blob + ff55bee61487e54861b3b26e6339c18a969e6908 > --- tog/tog.c > +++ tog/tog.c > @@ -81,24 +81,28 @@ __dead static void usage_log(void); > __dead static void usage_diff(void); > __dead static void usage_blame(void); > __dead static void usage_tree(void); > +__dead static void usage_ref(void); > > static const struct got_error* cmd_log(int, char *[]); > static const struct got_error* cmd_diff(int, char *[]); > static const struct got_error* cmd_blame(int, char *[]); > static const struct got_error* cmd_tree(int, char *[]); > +static const struct got_error* cmd_ref(int, char *[]); > > static struct tog_cmd tog_commands[] = { > { "log", cmd_log, usage_log }, > { "diff", cmd_diff, usage_diff }, > { "blame", cmd_blame, usage_blame }, > { "tree", cmd_tree, usage_tree }, > + { "ref", cmd_ref, usage_ref }, > }; > > enum tog_view_type { > TOG_VIEW_DIFF, > TOG_VIEW_LOG, > TOG_VIEW_BLAME, > - TOG_VIEW_TREE > + TOG_VIEW_TREE, > + TOG_VIEW_REF, > }; > > #define TOG_EOF_STRING "(END)" > @@ -207,6 +211,12 @@ default_color_value(const char *envvar) > return COLOR_CYAN; > if (strcmp(envvar, "TOG_COLOR_DATE") == 0) > return COLOR_YELLOW; > + if (strcmp(envvar, "TOG_COLOR_REFS_HEADS") == 0) > + return COLOR_GREEN; > + if (strcmp(envvar, "TOG_COLOR_REFS_TAGS") == 0) > + return COLOR_MAGENTA; > + if (strcmp(envvar, "TOG_COLOR_REFS_REMOTES") == 0) > + return COLOR_YELLOW; > > return -1; > } > @@ -315,6 +325,9 @@ struct tog_log_view_state { > #define TOG_COLOR_COMMIT 9 > #define TOG_COLOR_AUTHOR 10 > #define TOG_COLOR_DATE 11 > +#define TOG_COLOR_REFS_HEADS 12 > +#define TOG_COLOR_REFS_TAGS 13 > +#define TOG_COLOR_REFS_REMOTES 14 > > struct tog_blame_cb_args { > struct tog_blame_line *lines; /* one per line */ > @@ -388,6 +401,26 @@ struct tog_tree_view_state { > struct tog_colors colors; > }; > > +struct tog_reflist_entry { > + TAILQ_ENTRY(tog_reflist_entry) entry; > + struct got_reference *ref; > + int idx; > +}; > + > +TAILQ_HEAD(tog_reflist_head, tog_reflist_entry); > + > +struct tog_ref_view_state { > + struct got_reflist_head simplerefs; /* SIMPLEQ */ > + struct tog_reflist_head refs; /* TAILQ */ > + struct tog_reflist_entry *first_displayed_entry; > + struct tog_reflist_entry *last_displayed_entry; > + struct tog_reflist_entry *selected_entry; > + int nrefs, ndisplayed, selected, show_ids; > + struct got_repository *repo; > + struct tog_reflist_entry *matched_entry; > + struct tog_colors colors; > +}; > + > /* > * We implement two types of views: parent views and child views. > * > @@ -425,6 +458,7 @@ struct tog_view { > struct tog_log_view_state log; > struct tog_blame_view_state blame; > struct tog_tree_view_state tree; > + struct tog_ref_view_state ref; > } state; > > const struct got_error *(*show)(struct tog_view *); > @@ -484,6 +518,15 @@ static const struct got_error *close_tree_view(struct > static const struct got_error *search_start_tree_view(struct tog_view *); > static const struct got_error *search_next_tree_view(struct tog_view *); > > +static const struct got_error *open_ref_view(struct tog_view *, > + struct got_repository *); > +static const struct got_error *show_ref_view(struct tog_view *); > +static const struct got_error *input_ref_view(struct tog_view **, > + struct tog_view **, struct tog_view **, struct tog_view *, int); > +static const struct got_error *close_ref_view(struct tog_view *); > +static const struct got_error *search_start_ref_view(struct tog_view *); > +static const struct got_error *search_next_ref_view(struct tog_view *); > + > static volatile sig_atomic_t tog_sigwinch_received; > static volatile sig_atomic_t tog_sigpipe_received; > static volatile sig_atomic_t tog_sigcont_received; > @@ -2320,6 +2363,7 @@ input_log_view(struct tog_view **new_view, struct tog_ > struct tog_log_view_state *s = &view->state.log; > char *parent_path, *in_repo_path = NULL; > struct tog_view *diff_view = NULL, *tree_view = NULL, *lv = NULL; > + struct tog_view *ref_view = NULL; > int begin_x = 0; > struct got_object_id *start_id; > > @@ -2527,6 +2571,32 @@ input_log_view(struct tog_view **new_view, struct tog_ > *dead_view = view; > *new_view = lv; > break; > + case 'r': > + if (view_is_parent_view(view)) > + begin_x = view_split_begin_x(view->begin_x); > + ref_view = view_open(view->nlines, view->ncols, > + view->begin_y, begin_x, TOG_VIEW_REF); > + if (ref_view == NULL) > + return got_error_from_errno("view_open"); > + err = open_ref_view(ref_view, s->repo); > + if (err) { > + view_close(ref_view); > + return err; > + } > + if (view_is_parent_view(view)) { > + err = view_close_child(view); > + if (err) > + return err; > + err = view_set_child(view, ref_view); > + if (err) { > + view_close(ref_view); > + break; > + } > + *focus_view = ref_view; > + view->child_focussed = 1; > + } else > + *new_view = ref_view; > + break; > default: > break; > } > @@ -5574,7 +5644,608 @@ done: > return error; > } > > +static const struct got_error * > +ref_view_load_refs(struct tog_ref_view_state *s) > +{ > + const struct got_error *err; > + struct got_reflist_entry *sre; > + struct tog_reflist_entry *re; > + > + err = got_ref_list(&s->simplerefs, s->repo, NULL, > + got_ref_cmp_by_name, NULL); > + if (err) > + return err; > + > + s->nrefs = 0; > + SIMPLEQ_FOREACH(sre, &s->simplerefs, entry) { > + if (strncmp(got_ref_get_name(sre->ref), "refs/got/", 9) == 0) > + continue; > + > + re = malloc(sizeof(*re)); > + if (re == NULL) > + return got_error_from_errno("malloc"); > + > + re->ref = sre->ref; > + re->idx = s->nrefs++; > + TAILQ_INSERT_TAIL(&s->refs, re, entry); > + } > + > + return NULL; > +} > + > +void > +ref_view_free_refs(struct tog_ref_view_state *s) > +{ > + struct tog_reflist_entry *re; > + > + while (!TAILQ_EMPTY(&s->refs)) { > + re = TAILQ_FIRST(&s->refs); > + TAILQ_REMOVE(&s->refs, re, entry); > + free(re); > + } > + got_ref_list_free(&s->simplerefs); > +} > + > +static const struct got_error * > +open_ref_view(struct tog_view *view, struct got_repository *repo) > +{ > + const struct got_error *err = NULL; > + struct tog_ref_view_state *s = &view->state.ref; > + > + s->first_displayed_entry = 0; > + s->selected_entry = 0; > + s->repo = repo; > + > + SIMPLEQ_INIT(&s->simplerefs); > + TAILQ_INIT(&s->refs); > + SIMPLEQ_INIT(&s->colors); > + > + err = ref_view_load_refs(s); > + if (err) > + return err; > + > + if (has_colors() && getenv("TOG_COLORS") != NULL) { > + err = add_color(&s->colors, "^refs/heads/", > + TOG_COLOR_REFS_HEADS, > + get_color_value("TOG_COLOR_REFS_HEADS")); > + if (err) > + goto done; > + > + err = add_color(&s->colors, "^refs/tags/", > + TOG_COLOR_REFS_TAGS, > + get_color_value("TOG_COLOR_REFS_TAGS")); > + if (err) > + goto done; > + > + err = add_color(&s->colors, "^refs/remotes/", > + TOG_COLOR_REFS_REMOTES, > + get_color_value("TOG_COLOR_REFS_REMOTES")); > + if (err) > + goto done; > + } > + > + view->show = show_ref_view; > + view->input = input_ref_view; > + view->close = close_ref_view; > + view->search_start = search_start_ref_view; > + view->search_next = search_next_ref_view; > +done: > + if (err) > + free_colors(&s->colors); > + return err; > +} > + > +static const struct got_error * > +close_ref_view(struct tog_view *view) > +{ > + struct tog_ref_view_state *s = &view->state.ref; > + > + ref_view_free_refs(s); > + free_colors(&s->colors); > + > + return NULL; > +} > + > +static const struct got_error * > +log_ref_entry(struct tog_view **new_view, int begin_x, > + struct tog_reflist_entry *re, struct got_repository *repo) > +{ > + struct tog_view *log_view; > + const struct got_error *err = NULL; > + struct got_object_id *obj_id = NULL; > + struct got_object_id *commit_id = NULL; > + struct got_tag_object *tag = NULL; > + int obj_type; > + > + *new_view = NULL; > + > + err = got_ref_resolve(&obj_id, repo, re->ref); > + if (err) > + return err; > + > + err = got_object_get_type(&obj_type, repo, obj_id); > + if (err) > + goto done; > + > + switch (obj_type) { > + case GOT_OBJ_TYPE_COMMIT: > + commit_id = obj_id; > + break; > + case GOT_OBJ_TYPE_TAG: > + err = got_object_open_as_tag(&tag, repo, obj_id); > + if (err) > + goto done; > + commit_id = got_object_tag_get_object_id(tag); > + err = got_object_get_type(&obj_type, repo, commit_id); > + if (err || obj_type != GOT_OBJ_TYPE_COMMIT) > + goto done; > + break; > + default: > + free(obj_id); > + return NULL; > + } > + > + log_view = view_open(0, 0, 0, begin_x, TOG_VIEW_LOG); > + if (log_view == NULL) { > + err = got_error_from_errno("view_open"); > + goto done; > + } > + > + err = open_log_view(log_view, commit_id, repo, NULL, "", 0); > +done: > + if (err) > + view_close(log_view); > + else > + *new_view = log_view; > + if (tag) > + got_object_tag_close(tag); > + free(obj_id); > + return err; > +} > + > static void > +ref_scroll_up(struct tog_view *view, > + struct tog_reflist_entry **first_displayed_entry, int maxscroll, > + struct tog_reflist_head *refs) > +{ > + int i; > + > + if (*first_displayed_entry == TAILQ_FIRST(refs)) > + return; > + > + i = 0; > + while (*first_displayed_entry && i < maxscroll) { > + *first_displayed_entry = TAILQ_PREV(*first_displayed_entry, > + tog_reflist_head, entry); > + i++; > + } > +} > + > +static int > +ref_scroll_down(struct tog_reflist_entry **first_displayed_entry, int maxscroll, > + struct tog_reflist_entry *last_displayed_entry, > + struct tog_reflist_head *refs) > +{ > + struct tog_reflist_entry *next, *last; > + int n = 0; > + > + if (*first_displayed_entry) > + next = TAILQ_NEXT(*first_displayed_entry, entry); > + else > + next = TAILQ_FIRST(refs); > + > + last = last_displayed_entry; > + while (next && last && n++ < maxscroll) { > + last = TAILQ_NEXT(last, entry); > + if (last) { > + *first_displayed_entry = next; > + next = TAILQ_NEXT(next, entry); > + } > + } > + return n; > +} > + > +static const struct got_error * > +search_start_ref_view(struct tog_view *view) > +{ > + struct tog_ref_view_state *s = &view->state.ref; > + > + s->matched_entry = NULL; > + return NULL; > +} > + > +static int > +match_reflist_entry(struct tog_reflist_entry *re, regex_t *regex) > +{ > + regmatch_t regmatch; > + > + return regexec(regex, got_ref_get_name(re->ref), 1, ®match, > + 0) == 0; > +} > + > +static const struct got_error * > +search_next_ref_view(struct tog_view *view) > +{ > + struct tog_ref_view_state *s = &view->state.ref; > + struct tog_reflist_entry *re = NULL; > + > + if (!view->searching) { > + view->search_next_done = TOG_SEARCH_HAVE_MORE; > + return NULL; > + } > + > + if (s->matched_entry) { > + if (view->searching == TOG_SEARCH_FORWARD) { > + if (s->selected_entry) > + re = TAILQ_NEXT(s->selected_entry, entry); > + else > + re = TAILQ_PREV(s->selected_entry, > + tog_reflist_head, entry); > + } else { > + if (s->selected_entry == NULL) > + re = TAILQ_LAST(&s->refs, tog_reflist_head); > + else > + re = TAILQ_PREV(s->selected_entry, > + tog_reflist_head, entry); > + } > + } else { > + if (view->searching == TOG_SEARCH_FORWARD) > + re = TAILQ_FIRST(&s->refs); > + else > + re = TAILQ_LAST(&s->refs, tog_reflist_head); > + } > + > + while (1) { > + if (re == NULL) { > + if (s->matched_entry == NULL) { > + view->search_next_done = TOG_SEARCH_HAVE_MORE; > + return NULL; > + } > + if (view->searching == TOG_SEARCH_FORWARD) > + re = TAILQ_FIRST(&s->refs); > + else > + re = TAILQ_LAST(&s->refs, tog_reflist_head); > + } > + > + if (match_reflist_entry(re, &view->regex)) { > + view->search_next_done = TOG_SEARCH_HAVE_MORE; > + s->matched_entry = re; > + break; > + } > + > + if (view->searching == TOG_SEARCH_FORWARD) > + re = TAILQ_NEXT(re, entry); > + else > + re = TAILQ_PREV(re, tog_reflist_head, entry); > + } > + > + if (s->matched_entry) { > + s->first_displayed_entry = s->matched_entry; > + s->selected = 0; > + } > + > + return NULL; > +} > + > +static const struct got_error * > +show_ref_view(struct tog_view *view) > +{ > + const struct got_error *err = NULL; > + struct tog_ref_view_state *s = &view->state.ref; > + struct tog_reflist_entry *re; > + char *line = NULL; > + wchar_t *wline; > + struct tog_color *tc; > + int width, n; > + int limit = view->nlines; > + > + werase(view->window); > + > + s->ndisplayed = 0; > + > + if (limit == 0) > + return NULL; > + > + if (s->first_displayed_entry) > + re = s->first_displayed_entry; > + else > + re = TAILQ_FIRST(&s->refs); > + > + if (asprintf(&line, "references [%d/%d]", re->idx + s->selected + 1, > + s->nrefs) == -1) > + return got_error_from_errno("asprintf"); > + > + err = format_line(&wline, &width, line, view->ncols, 0); > + if (err) { > + free(line); > + return err; > + } > + if (view_needs_focus_indication(view)) > + wstandout(view->window); > + waddwstr(view->window, wline); > + if (view_needs_focus_indication(view)) > + wstandend(view->window); > + free(wline); > + wline = NULL; > + free(line); > + line = NULL; > + if (width < view->ncols - 1) > + waddch(view->window, '\n'); > + if (--limit <= 0) > + return NULL; > + > + n = 0; > + while (re && limit > 0) { > + char *line = NULL; > + > + if (got_ref_is_symbolic(re->ref)) { > + if (asprintf(&line, "%s -> %s", > + got_ref_get_name(re->ref), > + got_ref_get_symref_target(re->ref)) == -1) > + return got_error_from_errno("asprintf"); > + } else if (s->show_ids) { > + struct got_object_id *id; > + char *id_str; > + err = got_ref_resolve(&id, s->repo, re->ref); > + if (err) > + return err; > + err = got_object_id_str(&id_str, id); > + if (err) { > + free(id); > + return err; > + } > + if (asprintf(&line, "%s: %s", > + got_ref_get_name(re->ref), id_str) == -1) { > + err = got_error_from_errno("asprintf"); > + free(id); > + free(id_str); > + return err; > + } > + free(id); > + free(id_str); > + } else { > + line = strdup(got_ref_get_name(re->ref)); > + if (line == NULL) > + return got_error_from_errno("strdup"); > + } > + > + err = format_line(&wline, &width, line, view->ncols, 0); > + if (err) { > + free(line); > + return err; > + } > + if (n == s->selected) { > + if (view->focussed) > + wstandout(view->window); > + s->selected_entry = re; > + } > + tc = match_color(&s->colors, got_ref_get_name(re->ref)); > + if (tc) > + wattr_on(view->window, > + COLOR_PAIR(tc->colorpair), NULL); > + waddwstr(view->window, wline); > + if (tc) > + wattr_off(view->window, > + COLOR_PAIR(tc->colorpair), NULL); > + if (width < view->ncols - 1) > + waddch(view->window, '\n'); > + if (n == s->selected && view->focussed) > + wstandend(view->window); > + free(line); > + free(wline); > + wline = NULL; > + n++; > + s->ndisplayed++; > + s->last_displayed_entry = re; > + > + limit--; > + re = TAILQ_NEXT(re, entry); > + } > + > + view_vborder(view); > + return err; > +} > + > +static const struct got_error * > +input_ref_view(struct tog_view **new_view, struct tog_view **dead_view, > + struct tog_view **focus_view, struct tog_view *view, int ch) > +{ > + const struct got_error *err = NULL; > + struct tog_ref_view_state *s = &view->state.ref; > + struct tog_view *log_view; > + int begin_x = 0, nscrolled; > + > + switch (ch) { > + case 'i': > + s->show_ids = !s->show_ids; > + break; > + case KEY_ENTER: > + case '\r': > + if (!s->selected_entry) > + break; > + if (view_is_parent_view(view)) > + begin_x = view_split_begin_x(view->begin_x); > + err = log_ref_entry(&log_view, begin_x, s->selected_entry, > + s->repo); > + if (view_is_parent_view(view)) { > + err = view_close_child(view); > + if (err) > + return err; > + err = view_set_child(view, log_view); > + if (err) { > + view_close(log_view); > + break; > + } > + *focus_view = log_view; > + view->child_focussed = 1; > + } else > + *new_view = log_view; > + break; > + case 'k': > + case KEY_UP: > + if (s->selected > 0) { > + s->selected--; > + if (s->selected == 0) > + break; > + } > + if (s->selected > 0) > + break; > + ref_scroll_up(view, &s->first_displayed_entry, 1, &s->refs); > + break; > + case KEY_PPAGE: > + case CTRL('b'): > + ref_scroll_up(view, &s->first_displayed_entry, > + MAX(0, view->nlines - 4 - s->selected), &s->refs); > + s->selected = 0; > + break; > + case 'j': > + case KEY_DOWN: > + if (s->selected < s->ndisplayed - 1) { > + s->selected++; > + break; > + } > + if (TAILQ_NEXT(s->last_displayed_entry, entry) == NULL) > + /* can't scroll any further */ > + break; > + ref_scroll_down(&s->first_displayed_entry, 1, > + s->last_displayed_entry, &s->refs); > + break; > + case KEY_NPAGE: > + case CTRL('f'): > + if (TAILQ_NEXT(s->last_displayed_entry, entry) == NULL) { > + /* can't scroll any further; move cursor down */ > + if (s->selected < s->ndisplayed - 1) > + s->selected = s->ndisplayed - 1; > + break; > + } > + nscrolled = ref_scroll_down(&s->first_displayed_entry, > + view->nlines, s->last_displayed_entry, &s->refs); > + if (nscrolled < view->nlines) { > + int ndisplayed = 0; > + struct tog_reflist_entry *re; > + re = s->first_displayed_entry; > + do { > + ndisplayed++; > + re = TAILQ_NEXT(re, entry); > + } while (re); > + s->selected = ndisplayed - 1; > + } > + break; > + case CTRL('l'): > + ref_view_free_refs(s); > + err = ref_view_load_refs(s); > + break; > + case KEY_RESIZE: > + if (s->selected > view->nlines) > + s->selected = s->ndisplayed - 1; > + break; > + default: > + break; > + } > + > + return err; > +} > + > +__dead static void > +usage_ref(void) > +{ > + endwin(); > + fprintf(stderr, "usage: %s ref [-r repository-path]\n", > + getprogname()); > + exit(1); > +} > + > +static const struct got_error * > +cmd_ref(int argc, char *argv[]) > +{ > + const struct got_error *error; > + struct got_repository *repo = NULL; > + struct got_worktree *worktree = NULL; > + char *cwd = NULL, *repo_path = NULL; > + int ch; > + struct tog_view *view; > + > +#ifndef PROFILE > + if (pledge("stdio rpath wpath cpath flock proc tty exec sendfd unveil", > + NULL) == -1) > + err(1, "pledge"); > +#endif > + > + while ((ch = getopt(argc, argv, "r:")) != -1) { > + switch (ch) { > + case 'r': > + repo_path = realpath(optarg, NULL); > + if (repo_path == NULL) > + return got_error_from_errno2("realpath", > + optarg); > + break; > + default: > + usage_tree(); > + /* NOTREACHED */ > + } > + } > + > + argc -= optind; > + argv += optind; > + > + if (argc > 1) > + usage_tree(); > + > + cwd = getcwd(NULL, 0); > + if (cwd == NULL) > + return got_error_from_errno("getcwd"); > + > + error = got_worktree_open(&worktree, cwd); > + if (error && error->code != GOT_ERR_NOT_WORKTREE) > + goto done; > + > + if (repo_path == NULL) { > + if (worktree) > + repo_path = > + strdup(got_worktree_get_repo_path(worktree)); > + else > + repo_path = strdup(cwd); > + } > + if (repo_path == NULL) { > + error = got_error_from_errno("strdup"); > + goto done; > + } > + > + error = got_repo_open(&repo, repo_path, NULL); > + if (error != NULL) > + goto done; > + > + init_curses(); > + > + error = apply_unveil(got_repo_get_path(repo), NULL); > + if (error) > + goto done; > + > + view = view_open(0, 0, 0, 0, TOG_VIEW_REF); > + if (view == NULL) { > + error = got_error_from_errno("view_open"); > + goto done; > + } > + > + error = open_ref_view(view, repo); > + if (error) > + goto done; > + > + if (worktree) { > + /* Release work tree lock. */ > + got_worktree_close(worktree); > + worktree = NULL; > + } > + error = view_loop(view); > +done: > + free(repo_path); > + free(cwd); > + if (repo) > + got_repo_close(repo); > + return error; > +} > + > +static void > list_commands(FILE *fp) > { > int i; -- Tracey Emery