From: Mark Jamsek Subject: show base commit markers in tog log view To: gameoftrees@openbsd.org Date: Fri, 21 Jul 2023 22:54:58 +1000 This is a simple change along the same lines of showing reference labels next to tog log messages insofar as it improves situational awareness, which can be handy at times. For example, in the case where you want to fold or drop some commits. After 'got up -c:head:-N', I often like to confirm I've updated to the correct commit before invoking histedit. With this, you can open tog and at a glance confirm that your base commit is indeed the correct one for the desired histedit operation. Similarly, when your base commit is something other than the tip, it's nice to know where the base commit is in relation to the rest when browsing the log view. This can interop well with the new :base keyword feature. The change will be immediately obvious upon running tog with the patch inside a work tree, but to summarise: we show the "[base commit]" label in the log view headline if the currently selected commit is the work tree's base commit; and, irrespective of which commit is selected, we prepend the base commit's summary log message with the '@' symbol. There is no change to the log view when tog is invoked outside a work tree. ----------------------------------------------- commit 5a5052fc079f77f45bb7d7339c1e6b5590f37d19 (main) from: Mark Jamsek date: Fri Jul 21 11:56:12 2023 UTC show base commit markers in log view header and msg line If tog is invoked in a work tree, prefix the base commit log message summary line with an '@' symbol. And, if the currently selected commit is the work tree's base commit, show "[base commit]" in the log view's headline. M regress/tog/log.sh | 89+ 1- M tog/tog.c | 71+ 4- 2 files changed, 160 insertions(+), 5 deletions(-) diff 9c986b77d3f71b37ff5b5c6b4ec58b495ad8f312 5a5052fc079f77f45bb7d7339c1e6b5590f37d19 commit - 9c986b77d3f71b37ff5b5c6b4ec58b495ad8f312 commit + 5a5052fc079f77f45bb7d7339c1e6b5590f37d19 blob - 2a28c65c9bf9e227cba943ce259438976ac67c02 blob + fecfffb8a93f29610f56212f3fac114308276e7b --- regress/tog/log.sh +++ regress/tog/log.sh @@ -448,7 +448,7 @@ test_log_commit_keywords() commit $(pop_id 5 $ids) [1/5] $ymd $(pop_id 5 $short_ids) flan_hacker commit 4 $ymd $(pop_id 4 $short_ids) flan_hacker commit 3 - $ymd $(pop_id 3 $short_ids) flan_hacker commit 2 + $ymd $(pop_id 3 $short_ids) flan_hacker @commit 2 $ymd $(pop_id 2 $short_ids) flan_hacker commit 1 $ymd $(pop_id 1 $short_ids) flan_hacker adding the test tree @@ -499,6 +499,93 @@ test_parseargs "$@" test_done "$testroot" "$ret" } +test_log_show_base_commit() +{ + # make view wide enough to show full headline + test_init log_show_base_commit 80 3 + local repo="$testroot/repo" + local id=$(git_show_head "$repo") + + echo "alpha" >> "$repo/alpha" + git_commit "$repo" -m "base commit" + + got checkout "$repo" "$testroot/wt" > /dev/null + ret=$? + if [ $ret -ne 0 ]; then + echo "got checkout failed unexpectedly" + test_done "$testroot" "$ret" + return 1 + fi + + # move into the work tree (test is run in a subshell) + cd "$testroot/wt" + + local head_id=$(git_show_head "$repo") + local author_time=$(git_show_author_time "$repo") + local ymd=$(date -u -r "$author_time" +"%G-%m-%d") + + # check base commit header label is shown when base commit is selected + # and base commit marker prefixes base commit log message + cat <<-EOF >$TOG_TEST_SCRIPT + SCREENDUMP + EOF + + cat <<-EOF >$testroot/view.expected + commit $head_id [base commit] [1/2] master + $ymd flan_hacker @[master] base commit + $ymd flan_hacker adding the test tree + EOF + + tog log + cmp -s "$testroot/view.expected" "$testroot/view" + ret=$? + if [ $ret -ne 0 ]; then + diff -u "$testroot/view.expected" "$testroot/view" + test_done "$testroot" "$ret" + return 1 + fi + + # check neither marker nor label are drawn when not in a work tree + cat <<-EOF >$testroot/view.expected + commit $head_id [1/2] master + $ymd flan_hacker [master] base commit + $ymd flan_hacker adding the test tree + EOF + + tog log -r "$repo" + cmp -s "$testroot/view.expected" "$testroot/view" + ret=$? + if [ $ret -ne 0 ]; then + diff -u "$testroot/view.expected" "$testroot/view" + test_done "$testroot" "$ret" + return 1 + fi + + # check label is not shown when base commit is not selected + # but base commit marker still prefixes log message + cat <<-EOF >$TOG_TEST_SCRIPT + j select non-base commit + SCREENDUMP + EOF + + cat <<-EOF >$testroot/view.expected + commit $id [2/2] + $ymd flan_hacker @[master] base commit + $ymd flan_hacker adding the test tree + EOF + + tog log + cmp -s "$testroot/view.expected" "$testroot/view" + ret=$? + if [ $ret -ne 0 ]; then + diff -u "$testroot/view.expected" "$testroot/view" + test_done "$testroot" "$ret" + return 1 + fi + + test_done "$testroot" "$ret" +} + test_parseargs "$@" run_test test_log_hsplit_diff run_test test_log_vsplit_diff @@ -508,3 +595,4 @@ run_test test_log_commit_keywords run_test test_log_hsplit_tree run_test test_log_logmsg_widechar run_test test_log_commit_keywords +run_test test_log_show_base_commit blob - 55201d879df128d62d2a76ec797059af85649fa8 blob + 8c0b33eedc0db80f91741d15a52ecfa711c0d664 --- tog/tog.c +++ tog/tog.c @@ -151,6 +151,7 @@ static enum got_diff_algorithm tog_diff_algo = GOT_DIF static struct got_reflist_head tog_refs = TAILQ_HEAD_INITIALIZER(tog_refs); static struct got_reflist_object_id_map *tog_refs_idmap; +static struct got_object_id *tog_base_commit_id; static enum got_diff_algorithm tog_diff_algo = GOT_DIFF_ALGORITHM_MYERS; static const struct got_error * @@ -2411,7 +2412,7 @@ draw_commit(struct tog_view *view, struct got_commit_o int author_width, refstr_width, logmsg_width; char *newline, *line = NULL; int col, limit, scrollx, logmsg_x; - const int avail = view->ncols; + const int avail = view->ncols, marker_column = author_display_cols + 1; struct tm tm; time_t committer_time; struct tog_color *tc; @@ -2476,7 +2477,12 @@ draw_commit(struct tog_view *view, struct got_commit_o waddwstr(view->window, wauthor); col += author_width; while (col < avail && author_width < author_display_cols + 2) { - waddch(view->window, ' '); + if (tog_base_commit_id != NULL && + author_width == marker_column && + got_object_id_cmp(id, tog_base_commit_id) == 0) + waddch(view->window, '@'); + else + waddch(view->window, ' '); col++; author_width++; } @@ -2777,6 +2783,7 @@ draw_commits(struct tog_view *view) int width; int ncommits, author_cols = 4, refstr_cols; char *id_str = NULL, *header = NULL, *ncommits_str = NULL; + const char *base_label = NULL; char *refs_str = NULL; wchar_t *wline; struct tog_color *tc; @@ -2802,8 +2809,13 @@ draw_commits(struct tog_view *view) if (s->thread_args.commits_needed == 0 && !using_mock_io) halfdelay(10); /* disable fast refresh */ + if (s->selected_entry != NULL && tog_base_commit_id != NULL && + got_object_id_cmp(s->selected_entry->id, tog_base_commit_id) == 0) + base_label = "[base commit] "; + if (s->thread_args.commits_needed > 0 || s->thread_args.load_all) { - if (asprintf(&ncommits_str, " [%d/%d] %s", + if (asprintf(&ncommits_str, " %s[%d/%d] %s", + base_label != NULL ? base_label : "", entry ? entry->idx + 1 : 0, s->commits->ncommits, (view->searching && !view->search_next_done) ? "searching..." : "loading...") == -1) { @@ -2826,7 +2838,8 @@ draw_commits(struct tog_view *view) if (s->limit_view && s->commits->ncommits == 0) limit_str = "no matches found"; - if (asprintf(&ncommits_str, " [%d/%d] %s %s", + if (asprintf(&ncommits_str, " %s[%d/%d] %s %s", + base_label != NULL ? base_label : "", entry ? entry->idx + 1 : 0, s->commits->ncommits, search_str ? search_str : (refs_str ? refs_str : ""), limit_str ? limit_str : "") == -1) { @@ -4518,13 +4531,24 @@ cmd_log(int argc, char *argv[]) in_repo_path, log_branches); if (error) goto done; + if (worktree) { + tog_base_commit_id = got_object_id_dup( + got_worktree_get_base_commit_id(worktree)); + if (tog_base_commit_id == NULL) { + error = got_error_from_errno( "got_object_id_dup"); + goto done; + } + /* Release work tree lock. */ got_worktree_close(worktree); worktree = NULL; } + error = view_loop(view); + done: + free(tog_base_commit_id); free(keyword_idstr); free(in_repo_path); free(repo_path); @@ -6046,8 +6070,20 @@ cmd_diff(int argc, char *argv[]) ignore_whitespace, force_text_diff, NULL, repo); if (error) goto done; + + if (worktree) { + tog_base_commit_id = got_object_id_dup( + got_worktree_get_base_commit_id(worktree)); + if (tog_base_commit_id == NULL) { + error = got_error_from_errno( "got_object_id_dup"); + goto done; + } + } + error = view_loop(view); + done: + free(tog_base_commit_id); free(keyword_idstr1); free(keyword_idstr2); free(label1); @@ -7194,13 +7230,24 @@ cmd_blame(int argc, char *argv[]) view_close(view); goto done; } + if (worktree) { + tog_base_commit_id = got_object_id_dup( + got_worktree_get_base_commit_id(worktree)); + if (tog_base_commit_id == NULL) { + error = got_error_from_errno( "got_object_id_dup"); + goto done; + } + /* Release work tree lock. */ got_worktree_close(worktree); worktree = NULL; } + error = view_loop(view); + done: + free(tog_base_commit_id); free(repo_path); free(in_repo_path); free(link_target); @@ -8183,12 +8230,22 @@ cmd_tree(int argc, char *argv[]) } if (worktree) { + tog_base_commit_id = got_object_id_dup( + got_worktree_get_base_commit_id(worktree)); + if (tog_base_commit_id == NULL) { + error = got_error_from_errno( "got_object_id_dup"); + goto done; + } + /* Release work tree lock. */ got_worktree_close(worktree); worktree = NULL; } + error = view_loop(view); + done: + free(tog_base_commit_id); free(keyword_idstr); free(repo_path); free(cwd); @@ -9032,12 +9089,22 @@ cmd_ref(int argc, char *argv[]) goto done; if (worktree) { + tog_base_commit_id = got_object_id_dup( + got_worktree_get_base_commit_id(worktree)); + if (tog_base_commit_id == NULL) { + error = got_error_from_errno( "got_object_id_dup"); + goto done; + } + /* Release work tree lock. */ got_worktree_close(worktree); worktree = NULL; } + error = view_loop(view); + done: + free(tog_base_commit_id); free(repo_path); free(cwd); if (repo) { -- Mark Jamsek GPG: F2FF 13DE 6A06 C471 CA80 E6E2 2930 DC66 86EE CF68