From 0726d18078bab9e054ed417997b5f4db48d6ffdd Mon Sep 17 00:00:00 2001 From: Bryce Kwon Date: Sat, 17 Oct 2026 12:53:14 -1000 Subject: Show the root commit in a followed log A followed log diffs each commit against its parent to learn whether the file changed there, and a root commit was skipped rather than diffed against the empty tree, so a file created in the first commit never showed that commit. --- source/ui-log.c | 25 ++++++++++++------------- tests/t0103-log.sh | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/source/ui-log.c b/source/ui-log.c index f96689e..8adc5ac 100644 --- a/source/ui-log.c +++ b/source/ui-log.c @@ -88,15 +88,16 @@ static void follow_path(struct diff_filepair *pair) /* * Whether a commit belongs in the listing. When a single path is being - * followed the commit is diffed against its one parent here, and the counts - * that fall out of that diff are kept so the row does not repeat the work. + * followed the commit is diffed against its one parent here, or against the + * empty tree when it is the root, and the counts that fall out of that diff + * are kept so the row does not repeat the work. */ static int should_show(struct commit *commit, struct rev_info *revs) { // Static, because the walk keeps the address in its diff options. static filepair_fn follow = follow_path; struct commit_list *parents = commit->parents; - struct commit *parent; + const struct object_id *parent_tree = NULL; int found = 0, saved_fmt; struct diff_flags saved_flags = revs->diffopt.flags; @@ -108,22 +109,20 @@ static int should_show(struct commit *commit, struct rev_info *revs) if (parents && parents->next) return 0; - if (!parents) - return revs->show_root_diff; - - parent = parents->item; - - // A parent that does not parse is left for print_commit to report on, - // so the commit is shown. - if (repo_parse_commit(the_repository, parent)) - return 1; + if (parents) { + // A parent that does not parse is left for print_commit to + // report on, so the commit is shown. + if (repo_parse_commit(the_repository, parents->item)) + return 1; + parent_tree = get_commit_tree_oid(parents->item); + } file_count = 0; added_lines = 0; removed_lines = 0; revs->diffopt.flags.recursive = 1; - diff_tree_oid(get_commit_tree_oid(parent), get_commit_tree_oid(commit), "", &revs->diffopt); + diff_tree_oid(parent_tree, get_commit_tree_oid(commit), "", &revs->diffopt); diffcore_std(&revs->diffopt); found = !diff_queue_is_empty(&revs->diffopt); diff --git a/tests/t0103-log.sh b/tests/t0103-log.sh index 16fabda..08f9ddd 100755 --- a/tests/t0103-log.sh +++ b/tests/t0103-log.sh @@ -45,4 +45,42 @@ test_expect_success 'no links with space in path' '! grep "href=./with space/" t test_expect_success 'no links with space in arg' '! grep "q=commit 1" tmp' test_expect_success 'commit 2 is not visible' '! grep "commit 2" tmp' +# Following a file diffs each commit for the name the file had there, so the +# root commit that created it has to be diffed too. +test_expect_success 'set up a renamed file to follow' ' + test_create_repo repos/fol && + ( + cd repos/fol && + echo 1 >a.txt && + git add a.txt && + git commit -m "create a" && + for i in 2 3 4 + do + echo $i >>a.txt && + git commit -am "edit a $i" || return 1 + done && + git mv a.txt b.txt && + git commit -m "rename to b" && + for i in 6 7 8 + do + echo $i >>b.txt && + git commit -am "edit b $i" || return 1 + done + ) && + { + echo "virtual-root=/" && + echo "cache-size=0" && + echo "enable-follow-links=1" && + echo "max-commit-count=3" && + echo "repo.url=fol" && + echo "repo.path=$PWD/repos/fol/.git" + } >folrc +' + +test_expect_success 'a followed log reaches the root commit' ' + CGIT_CONFIG="$PWD/folrc" QUERY_STRING="url=fol/log/b.txt&follow=1&ofs=6" cgit >tmp && + grep ">edit a 2<" tmp && + grep ">create a<" tmp +' + test_done -- cgit