Download raw body.
implement tog ref command
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
implement tog ref command