Commit Diff


commit - 4d8c22150d533cd0f073d3e0a8c65cabe8257e9b
commit + bcbd79e2dbba7016620ee0c8dcf3a3b207ef1c14
blob - 3f2551b5751217910c7223a184348710425b4883
blob + 35d9972ad1102971e5188ade36ee2e62b56459d0
--- tog/tog.1
+++ tog/tog.1
@@ -35,6 +35,9 @@ supports several types of views which display reposito
 An arbitrary number of views may be opened simultaneously by
 navigating the repository with
 .Nm .
+Sometimes, if one view is opened from another view, the child view
+is linked to its parent view and both views will keep their data
+display in sync.
 The supported views are:
 .Bl -tag -width Ds
 .It Cm log view
@@ -89,14 +92,16 @@ The key bindings for
 .Cm tog log
 are as follows:
 .Bl -tag -width Ds
-.It Cm Down-arrow, j, Page-down
+.It Cm Down-arrow, j, ], Page-down
 Move the selection cursor down.
-.It Cm Up-arrow, k, Page-up
+.It Cm Up-arrow, k, [, Page-up
 Move the selection cursor up.
 .It Cm Enter
 Switch to the
 .Cm diff
 view showing file changes made in the currently selected commit.
+The diff view is linked to the log view so either view will be
+updated when the other switches to a different commit.
 .It Cm t
 Switch to the
 .Cm tree
@@ -135,6 +140,12 @@ are as follows:
 Scroll down.
 .It Cm Up-arrow, k, Page-up
 Scroll up.
+.It [
+Switch to the previous commit in parent
+.Cm log view .
+.It ]
+Switch to the next commit in parent
+.Cm log view .
 .El
 .It Cm blame [ Fl c Ar commit ] [ Fl r Ar repository-path ] Ar path
 Display line-by-line history of a file at the specified path.
blob - bd75a798e21e62084e6f2b3523cfcb01f3f690c1
blob + 1dc94f50c8b5a37214059da437455e8fd0e99dc2
--- tog/tog.c
+++ tog/tog.c
@@ -90,6 +90,7 @@ enum tog_view_type {
 };
 
 struct tog_diff_view_state {
+	struct got_object_id *id;
 	FILE *f;
 	int first_displayed_line;
 	int last_displayed_line;
@@ -192,6 +193,7 @@ struct tog_tree_view_state {
 	struct got_repository *repo;
 };
 
+TAILQ_HEAD(tog_view_list_head, tog_view);
 struct tog_view {
 	TAILQ_ENTRY(tog_view) entry;
 	WINDOW *window;
@@ -199,6 +201,7 @@ struct tog_view {
 	int nlines, ncols, begin_y, begin_x;
 	int lines, cols; /* copies of LINES and COLS */
 	struct tog_view *parent;
+	struct tog_view *child;
 
 	/* type-specific state */
 	enum tog_view_type type;
@@ -212,9 +215,10 @@ struct tog_view {
 	const struct got_error *(*show)(struct tog_view *);
 	const struct got_error *(*input)(struct tog_view **,
 	    struct tog_view **, struct tog_view *, int);
+	const struct got_error *(*set_child)(struct tog_view *,
+	    struct tog_view *);
 	const struct got_error *(*close)(struct tog_view *);
 };
-TAILQ_HEAD(tog_view_list_head, tog_view);
 
 static const struct got_error *open_diff_view(struct tog_view *,
     struct got_object *, struct got_object *, struct got_repository *);
@@ -229,6 +233,8 @@ static const struct got_error * show_log_view(struct t
 static const struct got_error *input_log_view(struct tog_view **,
     struct tog_view **, struct tog_view *, int);
 static const struct got_error *close_log_view(struct tog_view *);
+static const struct got_error* set_child_log_view(struct tog_view *,
+    struct tog_view *);
 
 static const struct got_error *open_blame_view(struct tog_view *, char *,
     struct got_object_id *, struct got_repository *);
@@ -249,6 +255,10 @@ view_close(struct tog_view *view)
 {
 	const struct got_error *err = NULL;
 
+	if (view->child)
+		view->child->parent = NULL;
+	if (view->parent)
+		view->parent->child = NULL;
 	if (view->close)
 		err = view->close(view);
 	if (view->panel)
@@ -272,6 +282,8 @@ view_open(int nlines, int ncols, int begin_y, int begi
 		begin_x = parent->ncols - 80;
 
 	view->parent = parent;
+	if (parent)
+		parent->child = view;
 	view->type = type;
 	view->lines = LINES;
 	view->cols = COLS;
@@ -299,10 +311,25 @@ view_show(struct tog_view *view)
 {
 	const struct got_error *err;
 
+	if (view->parent) {
+		err = view->parent->show(view->parent);
+		if (err)
+			return err;
+		show_panel(view->parent->panel);
+	}
+
 	err = view->show(view);
 	if (err)
 		return err;
 	show_panel(view->panel);
+
+	if (view->child) {
+		err = view->child->show(view->child);
+		if (err)
+			return err;
+		show_panel(view->child->panel);
+	}
+
 	update_panels();
 	doupdate();
 
@@ -394,6 +421,21 @@ view_input(struct tog_view **new, struct tog_view **de
 }
 
 static const struct got_error *
+view_set_child(struct tog_view *view, struct tog_view *child)
+{
+	const struct got_error *err;
+
+	if (view->set_child) {
+		err = view->set_child(view, child);
+		if (err)
+			return err;
+	}
+
+	view->child = child;
+	return NULL;
+}
+
+static const struct got_error *
 view_loop(struct tog_view *view)
 {
 	const struct got_error *err = NULL;
@@ -412,25 +454,32 @@ view_loop(struct tog_view *view)
 		    view, &views);
 		if (err)
 			break;
-		if (new_view) {
-			/* TODO: de-duplicate! */
-			TAILQ_INSERT_TAIL(&views, new_view, entry);
-			view = new_view;
-		}
 		if (dead_view) {
 			TAILQ_REMOVE(&views, dead_view, entry);
-			TAILQ_FOREACH(view, &views, entry) {
-				if (view->parent == dead_view)
-					view->parent = NULL;
-			}
 			if (dead_view->parent)
 				view = dead_view->parent;
 			else
 				view = TAILQ_LAST(&views, tog_view_list_head);
+			if (dead_view->child) {
+				TAILQ_REMOVE(&views, dead_view->child, entry);
+				err = view_close(dead_view->child);
+				if (err)
+					goto done;
+			}
 			err = view_close(dead_view);
 			if (err)
 				goto done;
 		}
+		if (new_view) {
+			/* TODO: de-duplicate! */
+			TAILQ_INSERT_TAIL(&views, new_view, entry);
+			if (new_view->parent) {
+				err = view_set_child(new_view->parent, new_view);
+				if (err)
+					goto done;
+			}
+			view = new_view;
+		}
 	}
 done:
 	while (!TAILQ_EMPTY(&views)) {
@@ -1037,6 +1086,43 @@ browse_commit(struct tog_view **new_view, struct tog_v
 	else
 		*new_view = tree_view;
 	return err;
+}
+
+static const struct got_error *
+set_child_log_view(struct tog_view *view, struct tog_view *child)
+{
+	struct tog_log_view_state *s = &view->state.log;
+	struct tog_diff_view_state *ds;
+	struct commit_queue_entry *commit, *child_entry = NULL;
+	int selected_idx = 0;
+
+	if (child->type != TOG_VIEW_DIFF)
+		return NULL;
+	ds = &child->state.diff;
+
+	TAILQ_FOREACH(commit, &s->commits.head, entry) {
+		if (got_object_id_cmp(commit->id, ds->id) == 0) {
+			child_entry = commit;
+			break;
+		}
+	}
+	if (child_entry == NULL)
+		return NULL;
+
+	commit = s->first_displayed_entry;
+	while (commit) {
+		if (got_object_id_cmp(commit->id, child_entry->id) == 0) {
+			s->selected_entry = child_entry;
+			s->selected = selected_idx;
+			break;
+		}
+		if (commit == s->last_displayed_entry)
+			break;
+		selected_idx++;
+		commit = TAILQ_NEXT(commit, entry);
+	}
+
+	return show_log_view(view);
 }
 
 static const struct got_error *
@@ -1092,6 +1178,7 @@ open_log_view(struct tog_view *view, struct got_object
 	view->show = show_log_view;
 	view->input = input_log_view;
 	view->close = close_log_view;
+	view->set_child = set_child_log_view;
 done:
 	free(head_id);
 	return err;
@@ -1110,14 +1197,63 @@ close_log_view(struct tog_view *view)
 }
 
 static const struct got_error *
+update_diff_child_view(struct tog_view *parent,
+    struct commit_queue_entry *selected_entry, struct got_repository *repo)
+{
+	const struct got_error *err = NULL;
+	struct tog_diff_view_state *ds;
+	struct got_object *obj1 = NULL, *obj2 = NULL;
+	struct got_object_qid *parent_id;
+	struct tog_view *child_view = parent->child;
+
+	if (child_view == NULL)
+		return NULL;
+	if (child_view->type != TOG_VIEW_DIFF)
+		return NULL;
+	ds = &child_view->state.diff;
+	if (got_object_id_cmp(ds->id, selected_entry->id) == 0)
+		return NULL;
+
+	err = got_object_open(&obj2, repo, selected_entry->id);
+	if (err)
+		return err;
+
+	parent_id = SIMPLEQ_FIRST(&selected_entry->commit->parent_ids);
+	if (parent_id) {
+		err = got_object_open(&obj1, repo, parent_id->id);
+		if (err)
+			goto done;
+	}
+
+	err = close_diff_view(child_view);
+	if (err)
+		goto done;
+
+	err = open_diff_view(child_view, obj1, obj2, repo);
+	if (err)
+		goto done;
+done:
+	if (obj1)
+		got_object_close(obj1);
+	if (obj2)
+		got_object_close(obj2);
+	return err;
+}
+
+static const struct got_error *
 show_log_view(struct tog_view *view)
 {
+	const struct got_error *err = NULL;
 	struct tog_log_view_state *s = &view->state.log;
 
-	return draw_commits(view, &s->last_displayed_entry,
+	err = draw_commits(view, &s->last_displayed_entry,
 	    &s->selected_entry, s->first_displayed_entry,
 	    &s->commits, s->selected, view->nlines, s->graph,
 	    s->repo, s->in_repo_path);
+	if (err)
+		return err;
+
+	return update_diff_child_view(view, s->selected_entry, s->repo);
 }
 
 static const struct got_error *
@@ -1130,6 +1266,7 @@ input_log_view(struct tog_view **new_view, struct tog_
 	switch (ch) {
 		case 'k':
 		case KEY_UP:
+		case '[':
 			if (s->selected > 0)
 				s->selected--;
 			if (s->selected > 0)
@@ -1148,6 +1285,7 @@ input_log_view(struct tog_view **new_view, struct tog_
 			break;
 		case 'j':
 		case KEY_DOWN:
+		case ']':
 			if (s->selected < MIN(view->nlines - 2,
 			    s->commits.ncommits - 1)) {
 				s->selected++;
@@ -1404,6 +1542,9 @@ open_diff_view(struct tog_view *view, struct got_objec
 
 	fflush(f);
 
+	view->state.diff.id = got_object_get_id(obj2);
+	if (view->state.diff.id == NULL)
+		return got_error_from_errno();
 	view->state.diff.f = f;
 	view->state.diff.first_displayed_line = 1;
 	view->state.diff.last_displayed_line = view->nlines;
@@ -1422,7 +1563,7 @@ close_diff_view(struct tog_view *view)
 
 	if (view->state.diff.f && fclose(view->state.diff.f) == EOF)
 		err = got_error_from_errno();
-
+	free(view->state.diff.id);
 	return err;
 }
 
@@ -1436,9 +1577,10 @@ show_diff_view(struct tog_view *view)
 }
 
 static const struct got_error *
-input_diff_view(struct tog_view **new, struct tog_view **dead,
+input_diff_view(struct tog_view **new_view, struct tog_view **dead_view,
     struct tog_view *view, int ch)
 {
+	const struct got_error *err = NULL;
 	struct tog_diff_view_state *s = &view->state.diff;
 	int i;
 
@@ -1468,13 +1610,49 @@ input_diff_view(struct tog_view **new, struct tog_view
 				s->first_displayed_line++;
 				if (line == NULL)
 					break;
+			}
+			break;
+		case '[':
+		case ']': {
+			struct tog_log_view_state *ls;
+			struct commit_queue_entry *entry;
+			struct tog_view *diff_view;
+
+			if (view->parent == NULL)
+				break;
+			if (view->parent->type != TOG_VIEW_LOG)
+				break;
+			ls = &view->parent->state.log;
+
+			if (ch == '[') {
+				entry = TAILQ_PREV(ls->selected_entry,
+				    commit_queue_head, entry);
+			} else {
+				entry = TAILQ_NEXT(ls->selected_entry, entry);
+				if (entry == NULL) {
+					err = fetch_next_commit(&entry,
+					    ls->selected_entry,
+					    &ls->commits, ls->graph,
+					    ls->repo, ls->in_repo_path);
+					if (err)
+						break;
+				}
 			}
+			if (entry == NULL)
+				break;
+			err = show_commit(&diff_view, view->parent,
+			    entry, ls->repo);
+			if (err)
+				break;
+			*new_view = diff_view;
+			*dead_view = view;
 			break;
+		}
 		default:
 			break;
 	}
 
-	return NULL;
+	return err;
 }
 
 static const struct got_error *