Skip to contentSkip to conversation

contrib: Add script to show uncovered "new" lines

PATCH Superseded Derrick Stolee via GitGitGadget 5 participants 22 messages Sep 12, 2018 – Oct 12, 2018

lore
Share & tools
lore
#0 lore raw

cover contrib: Add script to show uncovered "new" lines

We have coverage targets in our Makefile for using gcov to display line coverage based on our test suite. The way I like to do it is to run:

make coverage-test
make coverage-report

This leaves the repo in a state where every X.c file that was covered has an X.c.gcov file containing the coverage counts for every line, and "#####" at every uncovered line.

There have been a few bugs in recent patches what would have been caught if the test suite covered those blocks (including a few of mine). I want to work towards a "sensible" amount of coverage on new topics. In my opinion, this means that any logic should be covered, but the 'die()' blocks in error cases do not need to be covered.

It is important to not measure the coverage of the codebase by what old code is not covered. To help, I created the 'contrib/coverage-diff.sh' script. After creating the coverage statistics at a version (say, 'topic') you can then run

contrib/coverage-diff.sh base topic

to see the lines added between 'base' and 'topic' that are not covered by the test suite. For example, I ran this against the 'jch' branch (d3c0046) versus 'next' (dd90340) and got the following output:

builtin/commit.c
859fdc0c3cf (Derrick Stolee 2018-08-29 05:49:04 -0700 1657) write_commit_graph_reachable(get_object_directory(), 0);
builtin/rev-list.c
250edfa8c87 (Harald Nordgren 2018-04-18 23:05:35 +0200 431) bisect_flags |= BISECT_FIND_ALL;
builtin/worktree.c

e5353bef550     (Eric Sunshine  2018-08-28 17:20:19 -0400       60)             error_errno(_("failed to delete '%s'"), sb.buf);
e19831c94f9     (Eric Sunshine  2018-08-28 17:20:23 -0400       251)                die(_("unable to re-add worktree '%s'"), path);
68a6b3a1bd4     (Eric Sunshine  2018-08-28 17:20:24 -0400       793)                    die(_("cannot move a locked working tree, lock reason: %s\nuse 'move -f -f' to override or unlock first"),
f4143101cbb     (Eric Sunshine  2018-08-28 17:20:25 -0400       906)                    die(_("cannot remove a locked working tree, lock reason: %s\nuse 'remove -f -f' to override or unlock first"),
read-cache.c
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1754)           const unsigned char *cp = (const unsigned char *)name;
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1757)           previous_len = previous_ce ? previous_ce->ce_namelen : 0;
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1758)           strip_len = decode_varint(&cp);
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1759)           if (previous_len < strip_len) {
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1760)                   if (previous_ce)
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1761)                           die(_("malformed name field in the index, near path '%s'"),
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1762)                               previous_ce->name);
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1764)                           die(_("malformed name field in the index in the first path"));
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1766)           copy_len = previous_len - strip_len;
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1767)           name = (const char *)cp;
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1773)                   len += copy_len;
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1794)           if (copy_len)
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1795)                   memcpy(ce->name, previous_ce->name, copy_len);
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1796)           memcpy(ce->name + copy_len, name, len + 1 - copy_len);
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1797)           *ent_size = (name - ((char *)ondisk)) + len + 1 - copy_len;

remote-curl.c
c3b9bc94b9b (Elijah Newren 2018-09-05 10:03:07 -0700 181) options.filter = xstrdup(value);

Using this 'git blame' output, we can quickly inspect whether the uncovered lines are appropriate. For instance:

1. The line in builtin/commit.c is due to writing the commit-graph file when GIT_TEST_COMMIT_GRAPH is enabled, which is not on by default in the test suite. Being uncovered is expected here.

2. The lines in builtin/worktree.c are all related to error conditions. This is acceptable.

3. The line in builtin/rev-list.c is a flag replacement in a block that is otherwise unchanged. It must not be covered by the test suite normally. This could be worth adding a test to ensure the new logic maintains old behavior.

4. The lines in read-cache.c are part of a new block for the condition "if (expand_name_field)" as part of an optimization. These lines should probably be covered before that series is merged to 'next'. I understand that Ben and Duy are continuing work in this direction [1].

I used this approach for 'next' over 'master' and got a larger list, some of which I have already submitted tests to increase coverage [2] or will be covered by topics not in 'next' [3].

Thanks, -Stolee

[1]
https://public-inbox.org/git/[email protected]/T/#u

[2] https://public-inbox.org/git/[email protected]/

[3] https://public-inbox.org/git/[email protected]/

Derrick Stolee (1):
contrib: add coverage-diff script

 contrib/coverage-diff.sh | 70 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)
 create mode 100755 contrib/coverage-diff.sh

base-commit: 1d4361b0f344188ab5eec6dcea01f61a3a3a1670
Published-As: https://github.com/gitgitgadget/git/releases/tags/pr-40%2Fderrickstolee%2Fcoverage-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-40/derrickstolee/coverage-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/40

Signature · 1 line
gitgitgadget
#1 lore raw

PATCH 1/1 contrib: add coverage-diff script

From: Derrick Stolee <[email protected]>

We have coverage targets in our Makefile for using gcov to display line coverage based on our test suite. The way I like to do it is to run:

make coverage-test
make coverage-report

This leaves the repo in a state where every X.c file that was covered has an X.c.gcov file containing the coverage counts for every line, and "#####" at every uncovered line.

There have been a few bugs in recent patches what would have been caught if the test suite covered those blocks (including a few of mine). I want to work towards a "sensible" amount of coverage on new topics. In my opinion, this means that any logic should be covered, but the 'die()' blocks in error cases do not need to be covered.

It is important to not measure the coverage of the codebase by what old code is not covered. To help, I created the 'contrib/coverage-diff.sh' script. After creating the coverage statistics at a version (say, 'topic') you can then run

contrib/coverage-diff.sh base topic

to see the lines added between 'base' and 'topic' that are not covered by the test suite. The output uses 'git blame -c' format so you can find the commits responsible and view the line numbers for quick access to the context.

---
 contrib/coverage-diff.sh | 70 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)
 create mode 100755 contrib/coverage-diff.sh
contrib/coverage-diff.sh new file +70 -0
new file mode 100755
index 0000000000..22acb13d38
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+# Usage: 'contrib/coverage-diff.sh <version1> <version2>
+# Outputs a list of new lines in version2 compared to version1 that are
+# not covered by the test suite. Assumes you ran
+# 'make coverage-test coverage-report' from root first, so .gcov files exist.
+
+V1=$1
+V2=$2
+
+diff-lines() {
+    local path=
+    local line=
+    while read; do
+	esc=$'\033'
+	if [[ $REPLY =~ ---\ (a/)?.* ]]; then
+	    continue
+	elif [[ $REPLY =~ \+\+\+\ (b/)?([^[:blank:]$esc]+).* ]]; then
+	    path=${BASH_REMATCH[2]}
+	elif [[ $REPLY =~ @@\ -[0-9]+(,[0-9]+)?\ \+([0-9]+)(,[0-9]+)?\ @@.* ]]; then
+	    line=${BASH_REMATCH[2]}
+	elif [[ $REPLY =~ ^($esc\[[0-9;]+m)*([\ +-]) ]]; then
+	    echo "$path:$line:$REPLY"
+	    if [[ ${BASH_REMATCH[2]} != - ]]; then
+		((line++))
+	    fi
+	fi
+    done
+}
+
+git diff --raw $V1 $V2 | grep \.c$ | awk 'NF>1{print $NF}' >files.txt
+
+for file in $(cat files.txt)
+do
+	hash_file=${file//\//\#}
+
+	git diff $V1 $V2 -- $file \
+		| diff-lines \
+		| grep ":+" \
+		>"diff_file.txt"
+
+	cat diff_file.txt \
+		| sed -E 's/:/ /g' \
+		| awk '{print $2}' \
+		| sort \
+		>new_lines.txt
+
+	cat "$hash_file.gcov" \
+		| grep \#\#\#\#\# \
+		| sed 's/    #####: //g' \
+		| sed 's/\:/ /g' \
+		| awk '{print $1}' \
+		| sort \
+		>uncovered_lines.txt
+
+	comm -12 uncovered_lines.txt new_lines.txt \
+		| sed -e 's/$/\)/' \
+		| sed -e 's/^/\t/' \
+		>uncovered_new_lines.txt
+
+	grep -q '[^[:space:]]' < uncovered_new_lines.txt && \
+		echo $file && \
+		git blame -c $file \
+			| grep -f uncovered_new_lines.txt
+
+	rm -f diff_file.txt new_lines.txt \
+		uncovered_lines.txt uncovered_new_lines.txt
+done
+
+rm -rf files.txt
Signature · 1 line
gitgitgadget
#6 lore raw
In reply to Derrick Stolee via GitGitGadget
Quoted 2 lines · Derrick Stolee via GitGitGadget

For example, I ran this against the 'jch' branch (d3c0046) versus 'next' (dd90340)

As another example, I ran this against the 'pu' branch (4c416a53) versus 'jch' (d3c0046) and got the following output, submitted here without commentary:

builtin/bisect--helper.c
0b1f0fd910c     (Pranit Bauva   2017-10-27 15:06:37 +0000 43)             free((void *) terms->term_good);
3d3237b0e6b     (Pranit Bauva   2017-10-27 15:06:37 +0000 162)            if (get_oid_commit(commit, &oid))
3d3237b0e6b     (Pranit Bauva   2017-10-27 15:06:37 +0000 163)                    return error(_("'%s' is not a valid commit"), commit); 3d3237b0e6b     (Pranit Bauva   2017-10-27 15:06:37 +0000 164)            strbuf_addstr(&branch, commit);
3d3237b0e6b     (Pranit Bauva   2017-10-27 15:06:37 +0000 172)                    error(_("Could not check out original HEAD '%s'. Try " 3d3237b0e6b     (Pranit Bauva   2017-10-27 15:06:37 +0000 174)                    strbuf_release(&branch);
3d3237b0e6b     (Pranit Bauva   2017-10-27 15:06:37 +0000 175)                    argv_array_clear(&argv);
3d3237b0e6b     (Pranit Bauva   2017-10-27 15:06:37 +0000 176)                    return -1;
0b1f0fd910c     (Pranit Bauva   2017-10-27 15:06:37 +0000 215)            error(_("Bad bisect_write argument: %s"), state);
0b1f0fd910c     (Pranit Bauva   2017-10-27 15:06:37 +0000 216)            goto fail;
0b1f0fd910c     (Pranit Bauva   2017-10-27 15:06:37 +0000 220)            error(_("couldn't get the oid of the rev '%s'"), rev);
0b1f0fd910c     (Pranit Bauva   2017-10-27 15:06:37 +0000 221)            goto fail;
0b1f0fd910c     (Pranit Bauva   2017-10-27 15:06:37 +0000 226)            goto fail;
0b1f0fd910c     (Pranit Bauva   2017-10-27 15:06:37 +0000 230)            error_errno(_("couldn't open the file '%s'"), git_path_bisect_log()); 0b1f0fd910c     (Pranit Bauva   2017-10-27 15:06:37 +0000 231)            goto fail;
0b1f0fd910c     (Pranit Bauva   2017-10-27 15:06:37 +0000 242)fail:
0b1f0fd910c     (Pranit Bauva   2017-10-27 15:06:37 +0000 243)    retval = -1;
a919f328ba3     (Pranit Bauva   2017-10-27 15:06:37 +0000 323)            yesno = git_prompt(_("Are you sure [Y/n]? "), PROMPT_ECHO); a919f328ba3     (Pranit Bauva   2017-10-27 15:06:37 +0000 324)            if (starts_with(yesno, "N") || starts_with(yesno, "n"))
a919f328ba3     (Pranit Bauva   2017-10-27 15:06:37 +0000 327)            goto finish;
a919f328ba3     (Pranit Bauva   2017-10-27 15:06:37 +0000 336)            error(_("You need to start by \"git bisect start\". You "
a919f328ba3     (Pranit Bauva   2017-10-27 15:06:37 +0000 341)            goto fail;
a919f328ba3     (Pranit Bauva   2017-10-27 15:06:37 +0000 345)fail:
35f7ca528ae     (Pranit Bauva   2017-10-27 15:06:37 +0000 387)            return error(_("--bisect-term requires exactly one argument")); 35f7ca528ae     (Pranit Bauva   2017-10-27 15:06:37 +0000 400)                    error(_("BUG: invalid argument %s for 'git bisect terms'.\n" 5dfeec316b8     (Pranit Bauva   2017-10-27 15:06:37 +0000 416)            return -1;
5dfeec316b8     (Pranit Bauva   2017-10-27 15:06:37 +0000 419)            goto fail;
5dfeec316b8     (Pranit Bauva   2017-10-27 15:06:37 +0000 423)            goto fail;
5dfeec316b8     (Pranit Bauva   2017-10-27 15:06:37 +0000 427)fail:
5dfeec316b8     (Pranit Bauva   2017-10-27 15:06:37 +0000 428)    retval = -1;
5dfeec316b8     (Pranit Bauva   2017-10-27 15:06:37 +0000 466)                    no_checkout = 1;
5dfeec316b8     (Pranit Bauva   2017-10-27 15:06:37 +0000 488)                     !one_of(arg, "--term-good", "--term-bad", NULL)) { 5dfeec316b8     (Pranit Bauva   2017-10-27 15:06:37 +0000 489)                    return error(_("unrecognised option: '%s'"), arg);
5dfeec316b8     (Pranit Bauva   2017-10-27 15:06:37 +0000 523)            if (get_oid("HEAD", &head_oid))
5dfeec316b8     (Pranit Bauva   2017-10-27 15:06:37 +0000 524)                    return error(_("Bad HEAD - I need a HEAD"));
5dfeec316b8     (Pranit Bauva   2017-10-27 15:06:37 +0000 539)                            error(_("checking out '%s' failed. Try 'git " 5dfeec316b8     (Pranit Bauva   2017-10-27 15:06:37 +0000 559)                            return error(_("won't bisect on cg-seek'ed tree")); 5dfeec316b8     (Pranit Bauva   2017-10-27 15:06:37 +0000 562)                    return error(_("Bad HEAD - strange symbolic ref")); 5dfeec316b8     (Pranit Bauva   2017-10-27 15:06:37 +0000 570)            return -1;
5dfeec316b8     (Pranit Bauva   2017-10-27 15:06:37 +0000 588)                    goto fail;
5dfeec316b8     (Pranit Bauva   2017-10-27 15:06:37 +0000 598)                    goto fail;
5dfeec316b8     (Pranit Bauva   2017-10-27 15:06:37 +0000 606)            goto fail;
3d3237b0e6b     (Pranit Bauva   2017-10-27 15:06:37 +0000 686)                    return error(_("--bisect-reset requires either no argument or a commit")); 0b1f0fd910c     (Pranit Bauva   2017-10-27 15:06:37 +0000 690)                    return error(_("--bisect-write requires either 4 or 5 arguments")); 20edf353b72     (Pranit Bauva   2017-10-27 15:06:37 +0000 697)                    return error(_("--check-and-set-terms requires 3 arguments")); a919f328ba3     (Pranit Bauva   2017-10-27 15:06:37 +0000 703)                    return error(_("--bisect-next-check requires 2 or 3 arguments")); builtin/blame.c
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 922)    case DATE_HUMAN:
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 924)            blame_date_width = sizeof("Thu Oct 19 16:00");
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 925)            break;
builtin/gc.c
3029970275b     (Jonathan Nieder        2018-07-16 23:57:40 -0700       461)            ret = error_errno(_("cannot stat '%s'"), gc_log_path); 3029970275b     (Jonathan Nieder        2018-07-16 23:57:40 -0700       470)            ret = error_errno(_("cannot read '%s'"), gc_log_path); fec2ed21871     (Jonathan Nieder        2018-07-16 23:54:16 -0700       495)            die(FAILED_RUN, pack_refs_cmd.argv[0]);
fec2ed21871     (Jonathan Nieder        2018-07-16 23:54:16 -0700       498)            die(FAILED_RUN, reflog.argv[0]);
3029970275b     (Jonathan Nieder        2018-07-16 23:57:40 -0700       585)                            exit(128);
fec2ed21871     (Jonathan Nieder        2018-07-16 23:54:16 -0700       637)                    die(FAILED_RUN, repack.argv[0]);
fec2ed21871     (Jonathan Nieder        2018-07-16 23:54:16 -0700       647)                            die(FAILED_RUN, prune.argv[0]); fec2ed21871     (Jonathan Nieder        2018-07-16 23:54:16 -0700       654)                    die(FAILED_RUN, prune_worktrees.argv[0]); fec2ed21871     (Jonathan Nieder        2018-07-16 23:54:16 -0700       658)            die(FAILED_RUN, rerere.argv[0]); builtin/rebase--interactive.c
93420467efe     (Alban Gruin    2018-08-28 14:10:42 +0200 24)             return error(_("no HEAD?"));
93420467efe     (Alban Gruin    2018-08-28 14:10:42 +0200 51)             return error_errno(_("could not create temporary %s"), path_state_dir()); 93420467efe     (Alban Gruin    2018-08-28 14:10:42 +0200 57)             return error_errno(_("could not mark as interactive"));
93420467efe     (Alban Gruin    2018-08-28 14:10:42 +0200 77)             return -1;
93420467efe     (Alban Gruin    2018-08-28 14:10:42 +0200 81)             return -1;
93420467efe     (Alban Gruin    2018-08-28 14:10:42 +0200 87)             free(revisions);
93420467efe     (Alban Gruin    2018-08-28 14:10:42 +0200 88)             free(shortrevisions);
93420467efe     (Alban Gruin    2018-08-28 14:10:42 +0200 90)             return -1;
93420467efe     (Alban Gruin    2018-08-28 14:10:42 +0200 98)             free(revisions);
93420467efe     (Alban Gruin    2018-08-28 14:10:42 +0200 99)             free(shortrevisions);
93420467efe     (Alban Gruin    2018-08-28 14:10:42 +0200 101)            return error_errno(_("could not open %s"), rebase_path_todo()); 93420467efe     (Alban Gruin    2018-08-28 14:10:42 +0200 106)            argv_array_push(&make_script_args, restrict_revision);
93420467efe     (Alban Gruin    2018-08-28 14:10:42 +0200 114)            error(_("could not generate todo list"));
93420467efe     (Alban Gruin    2018-08-28 14:10:42 +0200 205) usage_with_options(builtin_rebase_interactive_usage, options);
93420467efe     (Alban Gruin    2018-08-28 14:10:42 +0200 219)            warning(_("--[no-]rebase-cousins has no effect without "
adb4f8f6b72     (Alban Gruin    2018-08-28 14:10:43 +0200 225)                    die(_("a base commit must be provided with --upstream or --onto")); b3fe2e1f8cb     (Alban Gruin    2018-08-28 14:10:45 +0200 259)    case REARRANGE_SQUASH:
b3fe2e1f8cb     (Alban Gruin    2018-08-28 14:10:45 +0200 260)            ret = rearrange_squash();
b3fe2e1f8cb     (Alban Gruin    2018-08-28 14:10:45 +0200 261)            break;
b3fe2e1f8cb     (Alban Gruin    2018-08-28 14:10:45 +0200 262)    case ADD_EXEC:
b3fe2e1f8cb     (Alban Gruin    2018-08-28 14:10:45 +0200 263)            ret = sequencer_add_exec_commands(cmd);
b3fe2e1f8cb     (Alban Gruin    2018-08-28 14:10:45 +0200 264)            break;
adb4f8f6b72     (Alban Gruin    2018-08-28 14:10:43 +0200 265)    default:
adb4f8f6b72     (Alban Gruin    2018-08-28 14:10:43 +0200 266)            BUG("invalid command '%d'", command);
builtin/rebase.c
55071ea248e     (Pratik Karki   2018-08-07 01:16:09 +0545 61)     strbuf_trim(&out);
55071ea248e     (Pratik Karki   2018-08-07 01:16:09 +0545 62)     ret = !strcmp("true", out.buf);
55071ea248e     (Pratik Karki   2018-08-07 01:16:09 +0545 63)     strbuf_release(&out);
002ee2fe682     (Pratik Karki   2018-09-04 14:59:57 -0700 114)    case REBASE_AM:
002ee2fe682     (Pratik Karki   2018-09-04 14:59:57 -0700 115)            die(_("%s requires an interactive rebase"), option);
f95736288a3     (Pratik Karki   2018-08-08 20:51:16 +0545 148)            return error_errno(_("could not read '%s'"), path);
f95736288a3     (Pratik Karki   2018-08-08 20:51:16 +0545 162)            return -1;
f95736288a3     (Pratik Karki   2018-08-08 20:51:16 +0545 167)            return error(_("could not get 'onto': '%s'"), buf.buf);
f95736288a3     (Pratik Karki   2018-08-08 20:51:16 +0545 178)                    return -1;
f95736288a3     (Pratik Karki   2018-08-08 20:51:16 +0545 179)    } else if (read_one(state_dir_path("head", opts), &buf))
f95736288a3     (Pratik Karki   2018-08-08 20:51:16 +0545 180)            return -1;
f95736288a3     (Pratik Karki   2018-08-08 20:51:16 +0545 182)            return error(_("invalid orig-head: '%s'"), buf.buf);
f95736288a3     (Pratik Karki   2018-08-08 20:51:16 +0545 186)            return -1;
f95736288a3     (Pratik Karki   2018-08-08 20:51:16 +0545 188)            opts->flags &= ~REBASE_NO_QUIET;
73d51ed0a59     (Pratik Karki   2018-09-04 14:59:50 -0700 196)            opts->signoff = 1;
73d51ed0a59     (Pratik Karki   2018-09-04 14:59:50 -0700 197)            opts->flags |= REBASE_FORCE;
ead98c111b8     (Pratik Karki   2018-09-04 14:59:52 -0700 204)                    return -1;
28a02c5a790     (Pratik Karki   2018-09-04 15:00:00 -0700 219)                    return -1;
399a505296a     (Pratik Karki   2018-09-04 15:00:11 -0700 227)                    return -1;
399a505296a     (Pratik Karki   2018-09-04 15:00:11 -0700 235)                    return -1;
7debdaa4ad1     (Pratik Karki   2018-09-04 15:00:02 -0700 255)            return error(_("Could not read '%s'"), path);
7debdaa4ad1     (Pratik Karki   2018-09-04 15:00:02 -0700 271)                    res = error(_("Cannot store %s"), autostash.buf);
7debdaa4ad1     (Pratik Karki   2018-09-04 15:00:02 -0700 275)                    return res;
b2263c13613     (Johannes Schindelin    2018-08-29 07:31:17 -0700       373) argv_array_pushf(&child.args,
b2263c13613     (Johannes Schindelin    2018-08-29 07:31:17 -0700       375) oid_to_hex(&opts->restrict_revision->object.oid));
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 479)    case REBASE_INTERACTIVE:
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 480)            backend = "git-rebase--interactive";
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 481)            backend_func = "git_rebase__interactive";
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 482)            break;
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 491)    default:
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 492)            BUG("Unhandled rebase type %d", opts->type);
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 509)            struct strbuf dir = STRBUF_INIT;
7debdaa4ad1     (Pratik Karki   2018-09-04 15:00:02 -0700 511)            apply_autostash(opts);
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 512)            strbuf_addstr(&dir, opts->state_dir);
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 513)            remove_dir_recursively(&dir, 0);
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 514)            strbuf_release(&dir);
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 515)            die("Nothing to do");
d4c569f8f4c     (Pratik Karki   2018-09-04 14:27:20 -0700 542)            BUG("Not a fully qualified branch: '%s'", switch_to_branch); ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 545)            return -1;
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 549)                    rollback_lock_file(&lock);
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 550)                    return error(_("could not determine HEAD revision")); ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 567)            rollback_lock_file(&lock);
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 568)            return error(_("could not read index"));
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 572)            error(_("failed to find tree of %s"), oid_to_hex(oid));
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 573)            rollback_lock_file(&lock);
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 574)            free((void *)desc.buffer);
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 575)            return -1;
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 588)            ret = error(_("could not write index"));
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 592)            return ret;
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 608)    } else if (old_orig)
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 609)            delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
bff014dac7d     (Pratik Karki   2018-09-04 14:27:13 -0700 637)                    opts->flags &= !REBASE_DIFFSTAT;
9a48a615b47     (Pratik Karki   2018-09-04 14:27:16 -0700 671)                    return 1;
9a48a615b47     (Pratik Karki   2018-09-04 14:27:16 -0700 687)            return 0;
55071ea248e     (Pratik Karki   2018-08-07 01:16:09 +0545 894)            const char *path = mkpath("%s/git-legacy-rebase",
55071ea248e     (Pratik Karki   2018-08-07 01:16:09 +0545 897)            if (sane_execvp(path, (char **)argv) < 0)
55071ea248e     (Pratik Karki   2018-08-07 01:16:09 +0545 898)                    die_errno(_("could not exec %s"), path);
55071ea248e     (Pratik Karki   2018-08-07 01:16:09 +0545 900)                    BUG("sane_execvp() returned???");
0eabf4b95ca     (Pratik Karki   2018-08-08 20:51:22 +0545 916)            die(_("It looks like 'git am' is in progress. Cannot rebase.")); f28d40d3a99     (Pratik Karki   2018-09-04 14:27:07 -0700 953)            usage_with_options(builtin_rebase_usage,
f95736288a3     (Pratik Karki   2018-08-08 20:51:16 +0545 973)                    die(_("Cannot read HEAD"));
f95736288a3     (Pratik Karki   2018-08-08 20:51:16 +0545 977)                    die(_("could not read index"));
f95736288a3     (Pratik Karki   2018-08-08 20:51:16 +0545 991)                    exit(1);
122420c2953     (Pratik Karki   2018-08-08 20:51:17 +0545 1003)                   die(_("could not discard worktree changes"));
122420c2953     (Pratik Karki   2018-08-08 20:51:17 +0545 1005)                   exit(1);
5e5d96197ca     (Pratik Karki   2018-08-08 20:51:18 +0545 1016)                   exit(1);
5e5d96197ca     (Pratik Karki   2018-08-08 20:51:18 +0545 1019)                   die(_("could not move back to %s"),
5a61494539b     (Pratik Karki   2018-08-08 20:51:19 +0545 1029)                   die(_("could not remove '%s'"), options.state_dir); f95736288a3     (Pratik Karki   2018-08-08 20:51:16 +0545 1042)   default:
51e9ea6da76     (Pratik Karki   2018-08-08 20:51:20 +0545 1043)           BUG("action: %d", action);
c54dacb50ea     (Pratik Karki   2018-09-04 14:27:18 -0700 1048)           const char *last_slash = strrchr(options.state_dir, '/');
c54dacb50ea     (Pratik Karki   2018-09-04 14:27:18 -0700 1049)           const char *state_dir_base =
c54dacb50ea     (Pratik Karki   2018-09-04 14:27:18 -0700 1050)                   last_slash ? last_slash + 1 : options.state_dir;
c54dacb50ea     (Pratik Karki   2018-09-04 14:27:18 -0700 1051)           const char *cmd_live_rebase =
c54dacb50ea     (Pratik Karki   2018-09-04 14:27:18 -0700 1053)           strbuf_reset(&buf);
c54dacb50ea     (Pratik Karki   2018-09-04 14:27:18 -0700 1054)           strbuf_addf(&buf, "rm -fr \"%s\"", options.state_dir);
c54dacb50ea     (Pratik Karki   2018-09-04 14:27:18 -0700 1055)           die(_("It seems that there is already a %s directory, and\n" 53f9e5be94e     (Pratik Karki   2018-09-04 14:59:56 -0700 1079)           strbuf_addstr(&options.git_am_opt, " --ignore-date");
53f9e5be94e     (Pratik Karki   2018-09-04 14:59:56 -0700 1080)           options.flags |= REBASE_FORCE;
c7ee2134d42     (Pratik Karki   2018-09-04 15:00:01 -0700 1092)           strbuf_addf(&options.git_am_opt, " -C%d", opt_c);
0073df2bd31     (Pratik Karki   2018-09-04 15:00:07 -0700 1124)           else if (strcmp("no-rebase-cousins", rebase_merges))
0073df2bd31     (Pratik Karki   2018-09-04 15:00:07 -0700 1125)                   die(_("Unknown mode: %s"), rebase_merges);
399a505296a     (Pratik Karki   2018-09-04 15:00:11 -0700 1146)           case REBASE_AM:
399a505296a     (Pratik Karki   2018-09-04 15:00:11 -0700 1147)                   die(_("--strategy requires --merge or --interactive")); 399a505296a     (Pratik Karki   2018-09-04 15:00:11 -0700 1156)           default:
399a505296a     (Pratik Karki   2018-09-04 15:00:11 -0700 1157)                   BUG("unhandled rebase type (%d)", options.type);
6dc73173f6c     (Pratik Karki   2018-08-08 21:21:33 +0545 1165)           strbuf_addstr(&options.git_format_patch_opt, " --progress"); ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 1173)   case REBASE_AM:
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 1174)           options.state_dir = apply_dir();
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 1175)           break;
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 1252)                   die(_("invalid upstream '%s'"), options.upstream_name); bfa5147095f     (Pratik Karki   2018-09-04 15:00:12 -0700 1258)                           die(_("Could not create new root commit")); e65123a71d0     (Pratik Karki   2018-09-04 14:27:21 -0700 1308)                   die(_("fatal: no such branch/commit '%s'"),
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 1316)                   die(_("No such ref: %s"), "HEAD");
ac7f467fef8     (Pratik Karki   2018-08-07 01:16:11 +0545 1328)                   die(_("Could not resolve HEAD to a revision"));
e65123a71d0     (Pratik Karki   2018-09-04 14:27:21 -0700 1330)           BUG("unexpected number of arguments left to parse");
e0333e5c63f     (Pratik Karki   2018-09-04 14:27:14 -0700 1341)           die(_("could not read index"));
7debdaa4ad1     (Pratik Karki   2018-09-04 15:00:02 -0700 1368)                           die(_("Cannot autostash"));
7debdaa4ad1     (Pratik Karki   2018-09-04 15:00:02 -0700 1371)                           die(_("Unexpected stash response: '%s'"),
7debdaa4ad1     (Pratik Karki   2018-09-04 15:00:02 -0700 1377)                           die(_("Could not create directory for '%s'"), 7debdaa4ad1     (Pratik Karki   2018-09-04 15:00:02 -0700 1383)                           die(_("could not reset --hard"));
e65123a71d0     (Pratik Karki   2018-09-04 14:27:21 -0700 1427)                                   ret = !!error(_("could not parse '%s'"), e65123a71d0     (Pratik Karki   2018-09-04 14:27:21 -0700 1429)                                   goto cleanup;
e65123a71d0     (Pratik Karki   2018-09-04 14:27:21 -0700 1438)                                   ret = !!error(_("could not switch to " 1ed9c14ff25     (Pratik Karki   2018-09-04 14:27:17 -0700 1448)                            resolve_ref_unsafe("HEAD", 0, NULL, &flag)) 1ed9c14ff25     (Pratik Karki   2018-09-04 14:27:17 -0700 1449)                           puts(_("HEAD is up to date."));
9a48a615b47     (Pratik Karki   2018-09-04 14:27:16 -0700 1458)                    resolve_ref_unsafe("HEAD", 0, NULL, &flag))
9a48a615b47     (Pratik Karki   2018-09-04 14:27:16 -0700 1459)                   puts(_("HEAD is up to date, rebase forced.")); builtin/rev-list.c
0eee403f2f7     (Matthew DeVore 2018-09-04 11:05:47 -0700 227)            die("unexpected missing %s object '%s'",
0eee403f2f7     (Matthew DeVore 2018-09-04 11:05:47 -0700 228)                type_name(obj->type), oid_to_hex(&obj->oid));
builtin/stash.c
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 130)            free_stash_info(info);
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 131)            error(_("'%s' is not a stash-like commit"), revision);
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 132)            exit(128);
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 165)                    free_stash_info(info);
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 166)                    fprintf_ln(stderr, _("No stash entries found."));
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 167)                    return -1;
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 201)    default: /* Invalid or ambiguous */
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 202)            free_stash_info(info);
31f109a3618     (Joel Teichroeb 2018-08-31 00:40:37 +0300 229)            return error(_("git stash clear with parameters is unimplemented")); 93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 244)            return -1;
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 252)            return -1;
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 265)            return -1;
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 268)            return error(_("unable to write new index file"));
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 374)            remove_path(stash_index_path.buf);
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 375)            return -1;
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 402)            return -1;
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 405)            return error(_("Cannot apply a stash in the middle of a merge")); 93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 415)                            strbuf_release(&out);
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 416)                            return -1;
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 422)                            return -1;
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 427)                            return -1;
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 434)            return error(_("Could not restore untracked files from stash")); 93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 465)                    return -1;
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 470)                    strbuf_release(&out);
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 475)                    strbuf_release(&out);
93871263d18     (Joel Teichroeb 2018-08-31 00:40:36 +0300 476)                    return -1;
31f109a3618     (Joel Teichroeb 2018-08-31 00:40:37 +0300 551)            return error(_("%s: Could not drop stash entry"),
b3513da4bd9     (Joel Teichroeb 2018-08-31 00:40:39 +0300 623)            printf_ln(_("The stash entry is kept in case you need it again.")); 8ceb24b2c38     (Paul-Sebastian Ungureanu       2018-08-31 00:40:41 +0300       754)            free_stash_info(&info);
129f0b0a009     (Paul-Sebastian Ungureanu       2018-08-31 00:40:48 +0300       755) usage_with_options(git_stash_show_usage, options); 0ac06fb81f2     (Paul-Sebastian Ungureanu       2018-08-31 00:40:43 +0300       808)            fprintf_ln(stderr, _("\"git stash store\" requires one <commit> argument")); 0ac06fb81f2     (Paul-Sebastian Ungureanu       2018-08-31 00:40:43 +0300       809)            return -1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       884)            return 1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       925)            ret = -1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       926)            goto done;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       931)            ret = -1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       932)            goto done;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       937)            ret = -1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       938)            goto done;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       966)            ret = -1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       967)            goto done;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       978)            ret = -1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       979)            goto done;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       984)            ret = -1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       985)            goto done;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       992)            ret = -1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       993)            goto done;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1018)           ret = -1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1019)           goto done;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1031)           ret = -1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1032)           goto done;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1037)           ret = -1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1038)           goto done;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1049)           ret = -1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1050)           goto done;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1055)           ret = -1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1056)           goto done;
8002b9e6264     (Paul-Sebastian Ungureanu       2018-08-31 00:40:46 +0300       1089)                   fprintf_ln(stderr, _("You do not have the initial commit yet")); 8002b9e6264     (Paul-Sebastian Ungureanu       2018-08-31 00:40:46 +0300       1110)           if (!quiet)
8002b9e6264     (Paul-Sebastian Ungureanu       2018-08-31 00:40:46 +0300       1111)                   fprintf_ln(stderr, _("Cannot save the current index state")); f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1112)           ret = -1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1113)           *stash_msg = NULL;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1114)           goto done;
8002b9e6264     (Paul-Sebastian Ungureanu       2018-08-31 00:40:46 +0300       1119)                   if (!quiet)
8002b9e6264     (Paul-Sebastian Ungureanu       2018-08-31 00:40:46 +0300       1120) fprintf_ln(stderr, _("Cannot save the untracked files")); f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1121)                   ret = -1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1122)                   *stash_msg = NULL;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1123)                   goto done;
8002b9e6264     (Paul-Sebastian Ungureanu       2018-08-31 00:40:46 +0300       1131)                   if (!quiet)
8002b9e6264     (Paul-Sebastian Ungureanu       2018-08-31 00:40:46 +0300       1132) fprintf_ln(stderr, _("Cannot save the current worktree state")); f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1133)                   goto done;
8002b9e6264     (Paul-Sebastian Ungureanu       2018-08-31 00:40:46 +0300       1139)                   if (!quiet)
8002b9e6264     (Paul-Sebastian Ungureanu       2018-08-31 00:40:46 +0300       1140) fprintf_ln(stderr, _("Cannot save the current worktree state")); f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1141)                   ret = -1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1142)                   *stash_msg = NULL;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1143)                   goto done;
8002b9e6264     (Paul-Sebastian Ungureanu       2018-08-31 00:40:46 +0300       1166)           if (!quiet)
8002b9e6264     (Paul-Sebastian Ungureanu       2018-08-31 00:40:46 +0300       1167)                   fprintf_ln(stderr, _("Cannot record working tree state")); f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1168)           ret = -1;
f6f191b3f25     (Paul-Sebastian Ungureanu       2018-08-31 00:40:44 +0300       1169)           goto done;
48c061fa443     (Paul-Sebastian Ungureanu       2018-08-31 00:40:45 +0300       1251)           return -1;
8002b9e6264     (Paul-Sebastian Ungureanu       2018-08-31 00:40:46 +0300       1260)           if (!quiet)
8002b9e6264     (Paul-Sebastian Ungureanu       2018-08-31 00:40:46 +0300       1261)                   fprintf_ln(stderr, _("Cannot initialize stash")); 48c061fa443     (Paul-Sebastian Ungureanu       2018-08-31 00:40:45 +0300       1262)           return -1;
8002b9e6264     (Paul-Sebastian Ungureanu       2018-08-31 00:40:46 +0300       1272)           if (!quiet)
8002b9e6264     (Paul-Sebastian Ungureanu       2018-08-31 00:40:46 +0300       1273)                   fprintf_ln(stderr, _("Cannot save the current status")); 48c061fa443     (Paul-Sebastian Ungureanu       2018-08-31 00:40:45 +0300       1274)           ret = -1;
48c061fa443     (Paul-Sebastian Ungureanu       2018-08-31 00:40:45 +0300       1275)           goto done;
48c061fa443     (Paul-Sebastian Ungureanu       2018-08-31 00:40:45 +0300       1292)                           ret = -1;
48c061fa443     (Paul-Sebastian Ungureanu       2018-08-31 00:40:45 +0300       1311)                           ret = -1;
48c061fa443     (Paul-Sebastian Ungureanu       2018-08-31 00:40:45 +0300       1312)                           goto done;
48c061fa443     (Paul-Sebastian Ungureanu       2018-08-31 00:40:45 +0300       1321)                           ret = -1;
48c061fa443     (Paul-Sebastian Ungureanu       2018-08-31 00:40:45 +0300       1322)                           goto done;
48c061fa443     (Paul-Sebastian Ungureanu       2018-08-31 00:40:45 +0300       1330)                           ret = -1;
48c061fa443     (Paul-Sebastian Ungureanu       2018-08-31 00:40:45 +0300       1339)                           ret = -1;
48c061fa443     (Paul-Sebastian Ungureanu       2018-08-31 00:40:45 +0300       1350)                           ret = -1;
48c061fa443     (Paul-Sebastian Ungureanu       2018-08-31 00:40:45 +0300       1359)                           ret = -1;
48c061fa443     (Paul-Sebastian Ungureanu       2018-08-31 00:40:45 +0300       1360)                           goto done;
48c061fa443     (Paul-Sebastian Ungureanu       2018-08-31 00:40:45 +0300       1368)                           ret = -1;
48c061fa443     (Paul-Sebastian Ungureanu       2018-08-31 00:40:45 +0300       1394)                           ret = -1;
129f0b0a009     (Paul-Sebastian Ungureanu       2018-08-31 00:40:48 +0300       1524) usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]), 129f0b0a009     (Paul-Sebastian Ungureanu       2018-08-31 00:40:48 +0300       1554)                           continue; builtin/submodule--helper.c
9d34daefb74     (Antonio Ospite 2018-08-14 13:05:22 +0200 2174)   die("submodule--helper config takes 1 or 2 arguments: name [value]"); commit-graph.c
5cef295f283     (Derrick Stolee 2018-08-20 18:24:32 +0000 66)             return 0;
20fd6d57996     (Derrick Stolee 2018-08-20 18:24:30 +0000 78)             return 0;
date.c
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 113)            die("Timestamp too large for this system: %"PRItime, time); 74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 216)            if (tm->tm_mon == human_tm->tm_mon) {
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 217)                    if (tm->tm_mday > human_tm->tm_mday) {
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 219)                    } else if (tm->tm_mday == human_tm->tm_mday) {
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 220)                            hide.date = hide.wday = 1;
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 221)                    } else if (tm->tm_mday + 5 > human_tm->tm_mday) {
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 223)                            hide.date = 1;
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 231)            gettimeofday(&now, NULL);
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 232)            show_date_relative(time, tz, &now, buf);
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 233)            return;
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 246)            hide.seconds = 1;
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 247)            hide.tz |= !hide.date;
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 248)            hide.wday = hide.time = !hide.year;
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 262)            strbuf_rtrim(buf);
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 287)            gettimeofday(&now, NULL);
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 290)            human_tz = local_time_tzoffset(now.tv_sec, &human_tm);
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 886)static int auto_date_style(void)
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 888)    return (isatty(1) || pager_in_use()) ? DATE_HUMAN : DATE_NORMAL;
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 909)            return DATE_HUMAN;
74e8221b523     (Linus Torvalds 2018-07-07 15:02:35 -0700 911)            return auto_date_style();
diff-lib.c
fa49029577c     (Alex Vandiver  2018-01-02 19:04:56 -0800 205)                    continue;
list-objects-filter.c
dbebce653b5     (Matthew DeVore 2018-09-04 11:05:49 -0700 47)             BUG("unknown filter_situation: %d", filter_situation);
7329b7c6cdc     (Matthew DeVore 2018-09-04 11:05:50 -0700 100)    default:
7329b7c6cdc     (Matthew DeVore 2018-09-04 11:05:50 -0700 101)            BUG("unknown filter_situation: %d", filter_situation);
dbebce653b5     (Matthew DeVore 2018-09-04 11:05:49 -0700 152)            BUG("unknown filter_situation: %d", filter_situation);
dbebce653b5     (Matthew DeVore 2018-09-04 11:05:49 -0700 257)            BUG("unknown filter_situation: %d", filter_situation);
dbebce653b5     (Matthew DeVore 2018-09-04 11:05:49 -0700 438)            BUG("invalid list-objects filter choice: %d",
list-objects.c
f447a499dbb     (Matthew DeVore 2018-08-13 11:14:28 -0700 197)                    ctx->show_object(obj, base->buf, ctx->show_data); rebase-interactive.c
64a43cbd5da     (Alban Gruin    2018-08-10 18:51:31 +0200 61)             return error_errno(_("could not read '%s'."), todo_file);
64a43cbd5da     (Alban Gruin    2018-08-10 18:51:31 +0200 65)             strbuf_release(&buf);
64a43cbd5da     (Alban Gruin    2018-08-10 18:51:31 +0200 66)             return -1;
a9f5476fbca     (Alban Gruin    2018-08-10 18:51:35 +0200 74)             return error_errno(_("could not read '%s'."), todo_file);
a9f5476fbca     (Alban Gruin    2018-08-10 18:51:35 +0200 78)             strbuf_release(&buf);
a9f5476fbca     (Alban Gruin    2018-08-10 18:51:35 +0200 79)             return -1;
64a43cbd5da     (Alban Gruin    2018-08-10 18:51:31 +0200 85)             return -1;
refs.c
4a6067cda51     (Stefan Beller  2018-08-20 18:24:16 +0000 1405)           return 0;
sequencer.c
65850686cf0     (Alban Gruin    2018-08-28 14:10:40 +0200 2276)           return;
65850686cf0     (Alban Gruin    2018-08-28 14:10:40 +0200 2373)           write_file(rebase_path_quiet(), "%s\n", quiet);
2c58483a598     (Alban Gruin    2018-08-10 18:51:33 +0200 3371)                   return error(_("could not checkout %s"), commit);
4df66c40b08     (Alban Gruin    2018-08-10 18:51:34 +0200 3385)           return error(_("%s: not a valid OID"), orig_head);
b97e1873649     (Alban Gruin    2018-08-28 14:10:36 +0200 4750)           return -1;
b97e1873649     (Alban Gruin    2018-08-28 14:10:36 +0200 4753)           return -1;
b97e1873649     (Alban Gruin    2018-08-28 14:10:36 +0200 4759)           return error_errno(_("could not read '%s'."), todo_file);
b97e1873649     (Alban Gruin    2018-08-28 14:10:36 +0200 4762)           todo_list_release(&todo_list);
b97e1873649     (Alban Gruin    2018-08-28 14:10:36 +0200 4763)           return error(_("unusable todo list: '%s'"), todo_file);
b97e1873649     (Alban Gruin    2018-08-28 14:10:36 +0200 4782)           todo_list_release(&todo_list);
b97e1873649     (Alban Gruin    2018-08-28 14:10:36 +0200 4783)           return -1;
b97e1873649     (Alban Gruin    2018-08-28 14:10:36 +0200 4787)           return error(_("could not copy '%s' to '%s'."), todo_file,
b97e1873649     (Alban Gruin    2018-08-28 14:10:36 +0200 4791)           return error(_("could not transform the todo list"));
b97e1873649     (Alban Gruin    2018-08-28 14:10:36 +0200 4820)           return error(_("could not transform the todo list"));
b97e1873649     (Alban Gruin    2018-08-28 14:10:36 +0200 4823)           return error(_("could not skip unnecessary pick commands")); b97e1873649     (Alban Gruin    2018-08-28 14:10:36 +0200 4829)           return -1;
strbuf.c
f95736288a3     (Pratik Karki   2018-08-08 20:51:16 +0545 127)                    --sb->len;
submodule-config.c
83a66534e63     (Antonio Ospite 2018-08-14 13:05:19 +0200 714)            return CONFIG_INVALID_KEY;
150984f5339     (Antonio Ospite 2018-08-14 13:05:20 +0200 729)            warning(_("Could not update .gitmodules entry %s"), key); t/helper/test-dump-fsmonitor.c
6e1123ec573     (Alex Vandiver  2018-01-02 19:04:54 -0800 25)                     valid++;
t/helper/test-repository.c
b7758963424     (Derrick Stolee 2018-08-20 18:24:24 +0000 21)             die("Couldn't init repo");
b7758963424     (Derrick Stolee 2018-08-20 18:24:24 +0000 47)             die("Couldn't init repo");
wrapper.c
7e621449185     (Pranit Bauva   2017-10-27 15:06:37 +0000 701)            die_errno(_("could not stat %s"), filename);
wt-status.c
f3bd35fa0dd     (Stephen P. Smith       2018-09-05 17:53:29 -0700       671)                    s->committable = 1;
1c623c066c2     (Junio C Hamano 2018-09-07 15:32:24 -0700 1949)                   if (s->state.rebase_in_progress ||
1c623c066c2     (Junio C Hamano 2018-09-07 15:32:24 -0700 1950) s->state.rebase_interactive_in_progress)
1c623c066c2     (Junio C Hamano 2018-09-07 15:32:24 -0700 1951)                           branch_name = s->state.onto;
1c623c066c2     (Junio C Hamano 2018-09-07 15:32:24 -0700 1952)                   else if (s->state.detached_from)
1c623c066c2     (Junio C Hamano 2018-09-07 15:32:24 -0700 1953)                           branch_name = s->state.detached_from;

#7 lore raw
In reply to Derrick Stolee via GitGitGadget
Quoted 6 lines · Derrick Stolee via GitGitGadget

We have coverage targets in our Makefile for using gcov to display line coverage based on our test suite. The way I like to do it is to run:

make coverage-test
make coverage-report

Very nice, I was unaware of the coverage test make targets. I like the new report; it makes it easier to verify any new changes are actually tested.

Quoted 6 lines

4. The lines in read-cache.c are part of a new block for the condition "if (expand_name_field)" as part of an optimization. These lines should probably be covered before that series is merged to 'next'. I understand that Ben and Duy are continuing work in this direction [1].

This code is only exercised when the index format is V4 but the default is version 2/3 [1]. To enable the test suite to use version 4 and test those code paths will require the addition of a new GIT_TEST_INDEX_VERSION environment variable. I'll add that to my TODO list.

[1] https://git-scm.com/docs/git/2.1.0

#8 lore raw
In reply to Derrick Stolee via GitGitGadget
Quoted 5 lines · Derrick Stolee via GitGitGadget

There have been a few bugs in recent patches what would have been caught if the test suite covered those blocks (including a few of mine). I want to work towards a "sensible" amount of coverage on new topics. In my opinion, this means that any logic should be covered, but the 'die()' blocks in error cases do not need to be covered.

Nice.

Quoted 14 lines

It is important to not measure the coverage of the codebase by what old code is not covered. To help, I created the 'contrib/coverage-diff.sh' script. After creating the coverage statistics at a version (say, 'topic') you can then run

contrib/coverage-diff.sh base topic
...
Using this 'git blame' output, we can quickly inspect whether the uncovered lines are appropriate. For instance:
...
I used this approach for 'next' over 'master' and got a larger list, some of which I have already submitted tests to increase coverage [2] or will be covered by topics not in 'next' [3].

Quoted 1 line

Thanks, -Stolee

Thanks for working on this.

#2 lore raw

Re: [PATCH 1/1] contrib: add coverage-diff script

In reply to Derrick Stolee via GitGitGadget
Quoted 3 lines · Derrick Stolee via GitGitGadget
 contrib/coverage-diff.sh | 70 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)
 create mode 100755 contrib/coverage-diff.sh

I fully appreciate the motivation. But it is a bit sad that this begins with "#!/bin/bash" but it seems that the script is full of bash-isms. I haven't gone through the script to see if these are inevitable or gratuitous yet, but I'd assume it made it easier for you to write it to step outside the pure POSIX shell?

contrib/coverage-diff.sh line 11

Quoted 4 lines
contrib/coverage-diff.sh new file +4 -0
+V1=$1
+V2=$2
+
+diff-lines() {

Being able to use '-' in identifier is probably a bash-ism that you did not have to use.

contrib/coverage-diff.sh line 14

Quoted 3 lines
contrib/coverage-diff.sh new file +3 -0
+    local path=
+    local line=
+    while read; do

Being able to omit variable to be read into and can use the implicit variable $REPLY also is.

contrib/coverage-diff.sh line 19

Quoted 5 lines
contrib/coverage-diff.sh new file +5 -0
+	esc=$'\033'
+	if [[ $REPLY =~ ---\ (a/)?.* ]]; then
+	    continue
+	elif [[ $REPLY =~ \+\+\+\ (b/)?([^[:blank:]$esc]+).* ]]; then
+	    path=${BASH_REMATCH[2]}

OK, it probably is easier to write in bash than using expr if you want to do regexp. Where do these escape code come from in "git diff" output, by the way?

contrib/coverage-diff.sh line 31

Quoted 12 lines
contrib/coverage-diff.sh new file +12 -0
+	elif [[ $REPLY =~ @@\ -[0-9]+(,[0-9]+)?\ \+([0-9]+)(,[0-9]+)?\ @@.* ]]; then
+	    line=${BASH_REMATCH[2]}
+	elif [[ $REPLY =~ ^($esc\[[0-9;]+m)*([\ +-]) ]]; then
+	    echo "$path:$line:$REPLY"
+	    if [[ ${BASH_REMATCH[2]} != - ]]; then
+		((line++))
+	    fi
+	fi
+    done
+}
+
+git diff --raw $V1 $V2 | grep \.c$ | awk 'NF>1{print $NF}' >files.txt

Hmph, not

	git diff --name-only "$V1" "$V2" -- "*.c"

Do we (or do we not) want "--no-renames"?

contrib/coverage-diff.sh line 40

Quoted 8 lines
contrib/coverage-diff.sh new file +8 -0
+for file in $(cat files.txt)
+do
+	hash_file=${file//\//\#}
+
+	git diff $V1 $V2 -- $file \
+		| diff-lines \
+		| grep ":+" \
+		>"diff_file.txt"

Style:

	cmd1 |
	cmd2 |
	cmd3 >output

is easier to read without backslashes.

contrib/coverage-diff.sh line 54

Quoted 13 lines
contrib/coverage-diff.sh new file +13 -0
+	cat diff_file.txt \
+		| sed -E 's/:/ /g' \
+		| awk '{print $2}' \
+		| sort \
+		>new_lines.txt
+
+	cat "$hash_file.gcov" \
+		| grep \#\#\#\#\# \
+		| sed 's/    #####: //g' \
+		| sed 's/\:/ /g' \
+		| awk '{print $1}' \
+		| sort \
+		>uncovered_lines.txt

OK, so we assume that we have run coverage in $V2 checkout so that we can pick up the postimage line numbers in "diff $V1 $V2" and find corresponding record in .gcov file in the filesystem. I did not realize the significance of 'topic' being the later argument to the script in this part

After creating the coverage statistics at a version (say, 'topic') you can then run

contrib/coverage-diff.sh base topic

of your description before I see this implementation. Also the comment at the beginning

# Usage: 'contrib/coverage-diff.sh <version1> <version2>
# Outputs a list of new lines in version2 compared to version1 that are # not covered by the test suite. Assumes you ran
# 'make coverage-test coverage-report' from root first, so .gcov files exist.

would want to make it clear that we want coverage run from root for version2 before using this script.

contrib/coverage-diff.sh line 61

Quoted 6 lines
contrib/coverage-diff.sh new file +6 -0
+	comm -12 uncovered_lines.txt new_lines.txt \
+		| sed -e 's/$/\)/' \
+		| sed -e 's/^/\t/' \
+		>uncovered_new_lines.txt
+
+	grep -q '[^[:space:]]' < uncovered_new_lines.txt && \

Style: when you end a line with && (or || or | for that matter), the shell knows that you have not finished speaking, and will wait to listen to you to finish the sentence. No need for backslash there.

Quoted 9 lines
contrib/coverage-diff.sh new file +9 -0
+		echo $file && \
+		git blame -c $file \
+			| grep -f uncovered_new_lines.txt
+
+	rm -f diff_file.txt new_lines.txt \
+		uncovered_lines.txt uncovered_new_lines.txt
+done
+
+rm -rf files.txt
#3 lore raw

Re: [PATCH 1/1] contrib: add coverage-diff script

Quoted 12 lines · Junio C Hamano
Quoted 3 lines · Derrick Stolee via GitGitGadget
 contrib/coverage-diff.sh | 70 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)
 create mode 100755 contrib/coverage-diff.sh

I fully appreciate the motivation. But it is a bit sad that this begins with "#!/bin/bash" but it seems that the script is full of bash-isms. I haven't gone through the script to see if these are inevitable or gratuitous yet, but I'd assume it made it easier for you to write it to step outside the pure POSIX shell?
...

Quoted 5 lines

contrib/coverage-diff.sh line 19

Quoted 2 lines
contrib/coverage-diff.sh new file +2 -0
+	elif [[ $REPLY =~ \+\+\+\ (b/)?([^[:blank:]$esc]+).* ]]; then
+	    path=${BASH_REMATCH[2]}

OK, it probably is easier to write in bash than using expr if you want to do regexp.

Just to clarify. I am saying that it is OK to give up writing in pure POSIX and relying on bash-isms after seeing these lines.

#4 lore raw

Re: [PATCH 1/1] contrib: add coverage-diff script

Quoted 4 lines · Junio C Hamano
Quoted 2 lines · Junio C Hamano

"Derrick Stolee via GitGitGadget" <[email protected]> writes:

Quoted 8 lines
Quoted 8 lines
Quoted 3 lines
  contrib/coverage-diff.sh | 70 ++++++++++++++++++++++++++++++++++++++++
  1 file changed, 70 insertions(+)

create mode 100755 contrib/coverage-diff.sh

I fully appreciate the motivation. But it is a bit sad that this begins with "#!/bin/bash" but it seems that the script is full of bash-isms. I haven't gone through the script to see if these are inevitable or gratuitous yet, but I'd assume it made it easier for you to write it to step outside the pure POSIX shell?

I completely forgot to avoid bash, as I wrote this first as an experiment.

Quoted 7 lines
Quoted 5 lines

...

contrib/coverage-diff.sh line 19

Quoted 2 lines
contrib/coverage-diff.sh new file +2 -0
+	elif [[ $REPLY =~ \+\+\+\ (b/)?([^[:blank:]$esc]+).* ]]; then
+	    path=${BASH_REMATCH[2]}

OK, it probably is easier to write in bash than using expr if you want to do regexp.

Just to clarify. I am saying that it is OK to give up writing in pure POSIX and relying on bash-isms after seeing these lines.

I'll try rewriting it using POSIX shell and see how hard it is.

Thanks,

-Stolee

#9 lore raw

v2 cover contrib: Add script to show uncovered "new" lines

In reply to Derrick Stolee via GitGitGadget

We have coverage targets in our Makefile for using gcov to display line coverage based on our test suite. The way I like to do it is to run:

make coverage-test
make coverage-report

This leaves the repo in a state where every X.c file that was covered has an X.c.gcov file containing the coverage counts for every line, and "#####" at every uncovered line.

There have been a few bugs in recent patches what would have been caught if the test suite covered those blocks (including a few of mine). I want to work towards a "sensible" amount of coverage on new topics. In my opinion, this means that any logic should be covered, but the 'die()' blocks in error cases do not need to be covered.

It is important to not measure the coverage of the codebase by what old code is not covered. To help, I created the 'contrib/coverage-diff.sh' script. After creating the coverage statistics at a version (say, 'topic') you can then run

contrib/coverage-diff.sh base topic

to see the lines added between 'base' and 'topic' that are not covered by the test suite. For example, I ran this against the 'jch' branch (d3c0046) versus 'next' (dd90340) and got the following output:

builtin/commit.c
859fdc0c3cf (Derrick Stolee 2018-08-29 05:49:04 -0700 1657) write_commit_graph_reachable(get_object_directory(), 0);
builtin/rev-list.c
250edfa8c87 (Harald Nordgren 2018-04-18 23:05:35 +0200 431) bisect_flags |= BISECT_FIND_ALL;
builtin/worktree.c

e5353bef550     (Eric Sunshine  2018-08-28 17:20:19 -0400       60)             error_errno(_("failed to delete '%s'"), sb.buf);
e19831c94f9     (Eric Sunshine  2018-08-28 17:20:23 -0400       251)                die(_("unable to re-add worktree '%s'"), path);
68a6b3a1bd4     (Eric Sunshine  2018-08-28 17:20:24 -0400       793)                    die(_("cannot move a locked working tree, lock reason: %s\nuse 'move -f -f' to override or unlock first"),
f4143101cbb     (Eric Sunshine  2018-08-28 17:20:25 -0400       906)                    die(_("cannot remove a locked working tree, lock reason: %s\nuse 'remove -f -f' to override or unlock first"),
read-cache.c
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1754)           const unsigned char *cp = (const unsigned char *)name;
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1757)           previous_len = previous_ce ? previous_ce->ce_namelen : 0;
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1758)           strip_len = decode_varint(&cp);
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1759)           if (previous_len < strip_len) {
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1760)                   if (previous_ce)
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1761)                           die(_("malformed name field in the index, near path '%s'"),
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1762)                               previous_ce->name);
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1764)                           die(_("malformed name field in the index in the first path"));
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1766)           copy_len = previous_len - strip_len;
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1767)           name = (const char *)cp;
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1773)                   len += copy_len;
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1794)           if (copy_len)
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1795)                   memcpy(ce->name, previous_ce->name, copy_len);
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1796)           memcpy(ce->name + copy_len, name, len + 1 - copy_len);
67922abbbb3     (Nguyễn Thái Ngọc Duy   2018-09-02 15:19:33 +0200       1797)           *ent_size = (name - ((char *)ondisk)) + len + 1 - copy_len;

remote-curl.c
c3b9bc94b9b (Elijah Newren 2018-09-05 10:03:07 -0700 181) options.filter = xstrdup(value);

Using this 'git blame' output, we can quickly inspect whether the uncovered lines are appropriate. For instance:

1. The line in builtin/commit.c is due to writing the commit-graph file when GIT_TEST_COMMIT_GRAPH is enabled, which is not on by default in the test suite. Being uncovered is expected here.

2. The lines in builtin/worktree.c are all related to error conditions. This is acceptable.

3. The line in builtin/rev-list.c is a flag replacement in a block that is otherwise unchanged. It must not be covered by the test suite normally. This could be worth adding a test to ensure the new logic maintains old behavior.

4. The lines in read-cache.c are part of a new block for the condition "if (expand_name_field)" as part of an optimization. These lines should probably be covered before that series is merged to 'next'. I understand that Ben and Duy are continuing work in this direction [1].

I used this approach for 'next' over 'master' and got a larger list, some of which I have already submitted tests to increase coverage [2] or will be covered by topics not in 'next' [3].

Thanks, -Stolee

CHANGES IN V2: I converted the script from bash to sh (there may still be POSIX-compliance issues with the new script, I don't know a lot when it comes to that area). I also streamlined the machinery to add line numbers to the diff. One downside is that the script runs a little slower with all of the grep process invocations.

[1]
https://public-inbox.org/git/[email protected]/T/#u

[2] https://public-inbox.org/git/[email protected]/

[3] https://public-inbox.org/git/[email protected]/

Derrick Stolee (1):
contrib: add coverage-diff script

 contrib/coverage-diff.sh | 63 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100755 contrib/coverage-diff.sh

base-commit: 1d4361b0f344188ab5eec6dcea01f61a3a3a1670
Published-As: https://github.com/gitgitgadget/git/releases/tags/pr-40%2Fderrickstolee%2Fcoverage-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-40/derrickstolee/coverage-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/40

Range-diff vs v1:

 1:  e4124471e5 < -:  ---------- contrib: add coverage-diff script
 -:  ---------- > 1:  7714b0659e contrib: add coverage-diff script
Signature · 1 line
gitgitgadget
#10 lore raw

PATCH v2 1/1 contrib: add coverage-diff script

From: Derrick Stolee <[email protected]>

We have coverage targets in our Makefile for using gcov to display line coverage based on our test suite. The way I like to do it is to run:

make coverage-test
make coverage-report

This leaves the repo in a state where every X.c file that was covered has an X.c.gcov file containing the coverage counts for every line, and "#####" at every uncovered line.

There have been a few bugs in recent patches what would have been caught if the test suite covered those blocks (including a few of mine). I want to work towards a "sensible" amount of coverage on new topics. In my opinion, this means that any logic should be covered, but the 'die()' blocks in error cases do not need to be covered.

It is important to not measure the coverage of the codebase by what old code is not covered. To help, I created the 'contrib/coverage-diff.sh' script. After creating the coverage statistics at a version (say, 'topic') you can then run

contrib/coverage-diff.sh base topic

to see the lines added between 'base' and 'topic' that are not covered by the test suite. The output uses 'git blame -c' format so you can find the commits responsible and view the line numbers for quick access to the context.

---
 contrib/coverage-diff.sh | 63 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100755 contrib/coverage-diff.sh
contrib/coverage-diff.sh new file +63 -0
new file mode 100755
index 0000000000..0f226f038c
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+# Usage: 'contrib/coverage-diff.sh <version1> <version2>
+# Outputs a list of new lines in version2 compared to version1 that are
+# not covered by the test suite. Assumes you ran
+# 'make coverage-test coverage-report' from root first, so .gcov files exist.
+
+V1=$1
+V2=$2
+
+diff_lines () {
+	while read line
+	do
+		if echo $line | grep -q -e "^@@ -([0-9]+)(,[0-9]+)? \\+([0-9]+)(,[0-9]+)? @@.*"
+		then
+			line_num=$(echo $line \
+				| awk 'match($0, "@@ -([0-9]+)(,[0-9]+)? \\+([0-9]+)(,[0-9]+)? @@.*", m) { print m[3] }')
+		else
+			echo "$line_num:$line"
+			if ! echo $line | grep -q -e "^-"
+			then
+				line_num=$(($line_num + 1))
+			fi
+		fi
+	done
+}
+
+files=$(git diff --raw $V1 $V2 \
+	| grep \.c$ \
+	| awk 'NF>1{print $NF}')
+
+for file in $files
+do
+	git diff $V1 $V2 -- $file \
+		| diff_lines \
+		| grep ":+" \
+		| sed 's/:/ /g' \
+		| awk '{print $1}' \
+		| sort \
+		>new_lines.txt
+
+	hash_file=$(echo $file | sed "s/\//\#/")
+	cat "$hash_file.gcov" \
+		| grep \#\#\#\#\# \
+		| sed 's/    #####: //g' \
+		| sed 's/\:/ /g' \
+		| awk '{print $1}' \
+		| sort \
+		>uncovered_lines.txt
+
+	comm -12 uncovered_lines.txt new_lines.txt \
+		| sed -e 's/$/\)/' \
+		| sed -e 's/^/\t/' \
+		>uncovered_new_lines.txt
+
+	grep -q '[^[:space:]]' < uncovered_new_lines.txt && \
+		echo $file && \
+		git blame -c $file \
+			| grep -f uncovered_new_lines.txt
+
+	rm -f new_lines.txt uncovered_lines.txt uncovered_new_lines.txt
+done
+
Signature · 1 line
gitgitgadget
#5 lore raw

Re: [PATCH 1/1] contrib: add coverage-diff script

In reply to Derrick Stolee
Quoted 17 lines · Derrick Stolee
Quoted 4 lines · Junio C Hamano
Quoted 2 lines · Junio C Hamano

"Derrick Stolee via GitGitGadget" <[email protected]> writes:

Quoted 8 lines
Quoted 8 lines
Quoted 3 lines
  contrib/coverage-diff.sh | 70 ++++++++++++++++++++++++++++++++++++++++
  1 file changed, 70 insertions(+)

create mode 100755 contrib/coverage-diff.sh

I fully appreciate the motivation. But it is a bit sad that this begins with "#!/bin/bash" but it seems that the script is full of bash-isms. I haven't gone through the script to see if these are inevitable or gratuitous yet, but I'd assume it made it easier for you to write it to step outside the pure POSIX shell?

I completely forgot to avoid bash, as I wrote this first as an experiment.

Quoted 1 line
Quoted 1 line

...

Quoted 8 lines
Quoted 6 lines
Quoted 4 lines

contrib/coverage-diff.sh line 19

Quoted 2 lines
contrib/coverage-diff.sh new file +2 -0
+	elif [[ $REPLY =~ \+\+\+\ (b/)?([^[:blank:]$esc]+).* ]]; then
+	    path=${BASH_REMATCH[2]}

OK, it probably is easier to write in bash than using expr if you want to do regexp.

Just to clarify. I am saying that it is OK to give up writing in pure POSIX and relying on bash-isms after seeing these lines.

I'll try rewriting it using POSIX shell and see how hard it is.

Thanks. Don't waste too much time on it and try to bend backwards too far, though.

#11 lore raw

Re: [PATCH v2 1/1] contrib: add coverage-diff script

In reply to Derrick Stolee via GitGitGadget
Quoted 41 lines · Derrick Stolee via GitGitGadget

From: Derrick Stolee <[email protected]>

We have coverage targets in our Makefile for using gcov to display line coverage based on our test suite. The way I like to do it is to run:

make coverage-test
make coverage-report

This leaves the repo in a state where every X.c file that was covered has an X.c.gcov file containing the coverage counts for every line, and "#####" at every uncovered line.

There have been a few bugs in recent patches what would have been caught if the test suite covered those blocks (including a few of mine). I want to work towards a "sensible" amount of coverage on new topics. In my opinion, this means that any logic should be covered, but the 'die()' blocks in error cases do not need to be covered.

It is important to not measure the coverage of the codebase by what old code is not covered. To help, I created the 'contrib/coverage-diff.sh' script. After creating the coverage statistics at a version (say, 'topic') you can then run

contrib/coverage-diff.sh base topic

to see the lines added between 'base' and 'topic' that are not covered by the test suite. The output uses 'git blame -c' format so you can find the commits responsible and view the line numbers for quick access to the context.

---
 contrib/coverage-diff.sh | 63 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100755 contrib/coverage-diff.sh
contrib/coverage-diff.sh new file +0 -0
new file mode 100755
index 0000000000..0f226f038c

contrib/coverage-diff.sh line 6

Quoted 6 lines
contrib/coverage-diff.sh new file +6 -0
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+# Usage: 'contrib/coverage-diff.sh <version1> <version2>
+# Outputs a list of new lines in version2 compared to version1 that are
+# not covered by the test suite. Assumes you ran
+# 'make coverage-test coverage-report' from root first, so .gcov files exist.

I presume that it is "from root first while you have a checkout of version2, so .gcov files for version2 exist in the working tree"?

contrib/coverage-diff.sh line 14

Quoted 7 lines
contrib/coverage-diff.sh new file +7 -0
+V1=$1
+V2=$2
+
+diff_lines () {
+	while read line
+	do
+		if echo $line | grep -q -e "^@@ -([0-9]+)(,[0-9]+)? \\+([0-9]+)(,[0-9]+)? @@.*"

As you know you are reading from diff output, you do not have to be so strict in this if condition. It's not like you try to carefully reject a line that began with "@@" but did not match this pattern in the corresponding else block.

"read line" does funny things to backslashes and whitespaces in the payload ("read -r line" sometimes helps), and echoing $line without quoting will destroy its contents here and in the line below (but you do not care because all you care about is if it begins with a dash).

contrib/coverage-diff.sh line 20

Quoted 6 lines
contrib/coverage-diff.sh new file +6 -0
+		then
+			line_num=$(echo $line \
+				| awk 'match($0, "@@ -([0-9]+)(,[0-9]+)? \\+([0-9]+)(,[0-9]+)? @@.*", m) { print m[3] }')
+		else
+			echo "$line_num:$line"
+			if ! echo $line | grep -q -e "^-"

If a patch removes a line with a solitary 'n' on it, possibly followed by a SP and something else, such a line would say "-n something else", and some implementation of "echo -n something else" would do what this line does not expect. A safer way to do this, as you only care if it is a deletion line, is to do

	case "$line" in -*) ;; *) line_num=$(( $line_num + 1 ));; esac

Also you can make the echoing of "$line_num:$line" above conditional, which will allow you to lose "grep ':+'" in the pipeline that consumes output from this thing, e.g.

	case "$line" in +*) echo "$line_num:$line";; esac

iff you must write this in shell (but see below).

contrib/coverage-diff.sh line 25

Quoted 5 lines
contrib/coverage-diff.sh new file +5 -0
+			then
+				line_num=$(($line_num + 1))
+			fi
+		fi
+	done

I have a feeling that a single Perl script instead of a shell loop that runs many grep and awk as subprocesses performs better even on Windows, and it would be more readable and maintainable.

perl -e '

	my $line_num;
	while (<>) {
		# Hunk header?  Grab the beginning in postimage.
		if (/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/) {
			$line_num = $1;
			next;
		}

		# Have we seen a hunk?  Ignore "diff --git" etc.
		next unless defined $line_num;

		# Deleted line? Ignore.
		if (/^-/) {
			next;
		}

		# Show only the line number of added lines.
		if (/^\+/) {
			print "$line_num\n";
		}
		# Either common context or added line appear in
		# the postimage.  Count it.
		$line_num++;
	}

'

or something like that, given that you seem to only need line numbers in new_lines.txt anyway?

contrib/coverage-diff.sh line 30

Quoted 5 lines
contrib/coverage-diff.sh new file +5 -0
+}
+
+files=$(git diff --raw $V1 $V2 \
+	| grep \.c$ \
+	| awk 'NF>1{print $NF}')

Do we need these other commands in the pipe? How is this different from, say,

	git diff --name-only "$V1" "$V2" -- \*.c

?

contrib/coverage-diff.sh line 36

Quoted 5 lines
contrib/coverage-diff.sh new file +5 -0
+for file in $files
+do
+	git diff $V1 $V2 -- $file \
+		| diff_lines \
+		| grep ":+" \

I think you meant to match "^<linenum>:+<added contents>" you are echoing out in the above helper function, but this will find a removed line that happens to have colon followed by a plus (which is not all that uncommon in a patch to a shell script).

contrib/coverage-diff.sh line 37

Quoted 1 line
contrib/coverage-diff.sh new file +1 -0
+		| sed 's/:/ /g' \

Turning "<linenum>:+<added contents>" to "<linenum> +<added contents>" with this, so that the next "awk" can show <linenum> only? Then you do not need 'g' at the end. I see you use 'g' in many sed scripts for uncovered_lines.txt, and I suspect most of 'g's you do not mean.

contrib/coverage-diff.sh line 36

Quoted 1 line
contrib/coverage-diff.sh new file +1 -0
+		| awk '{print $1}' \

Then we get a list of $line_num here?

contrib/coverage-diff.sh line 37

Quoted 1 line
contrib/coverage-diff.sh new file +1 -0
+		| sort \

This is sorted textually, which goes against intuition because these lines are all line numbers, but it is done so that you can run "comm" on it later and "comm" requires us to feed lines sorted textually.

contrib/coverage-diff.sh line 40

Quoted 1 line
contrib/coverage-diff.sh new file +1 -0
+		>new_lines.txt

If I were writing this part, I'd probably write a small Perl script that takes output from 'git diff "$V1" "$V2" -- "$file"' and then reduces it to a list of line numbers for newly introduced lines. Then pipe it to "sort >new_lines.txt" to match what we see below.

contrib/coverage-diff.sh line 49

Quoted 8 lines
contrib/coverage-diff.sh new file +8 -0
+	hash_file=$(echo $file | sed "s/\//\#/")
+	cat "$hash_file.gcov" \
+		| grep \#\#\#\#\# \
+		| sed 's/    #####: //g' \
+		| sed 's/\:/ /g' \
+		| awk '{print $1}' \
+		| sort \
+		>uncovered_lines.txt

Do not cat a single regular file into a pipe. Write pipe at the end of the line, not the beginning of next line. Do not grep into sed. Do not feed sed into sed unless needed.

Without knowing what the above is trying to do, but just mechanically translating, I'd get something like this:

	sed -ne '/#####/{
		s/    #####: //g
		s/:.*//
                p
	}' "$hash_file.gcov" |
	sort >uncovered_lines.txt

contrib/coverage-diff.sh line 54

Quoted 4 lines
contrib/coverage-diff.sh new file +4 -0
+	comm -12 uncovered_lines.txt new_lines.txt \
+		| sed -e 's/$/\)/' \
+		| sed -e 's/^/\t/' \
+		>uncovered_new_lines.txt

;-) This creates something like

	1)
	2)
	3)

out of a list of numbers. Cute.

Quoted 8 lines
contrib/coverage-diff.sh new file +8 -0
+	grep -q '[^[:space:]]' < uncovered_new_lines.txt && \
+		echo $file && \
+		git blame -c $file \
+			| grep -f uncovered_new_lines.txt
+
+	rm -f new_lines.txt uncovered_lines.txt uncovered_new_lines.txt
+done
+
#13 lore raw

Re: [PATCH v2 1/1] contrib: add coverage-diff script

In reply to Derrick Stolee via GitGitGadget
Quoted 5 lines · Derrick Stolee via GitGitGadget

There have been a few bugs in recent patches what would have been caught if the test suite covered those blocks (including a few of mine). I want to work towards a "sensible" amount of coverage on new topics. In my opinion, this means that any logic should be covered, but the 'die()' blocks in error cases do not need to be covered.

The bit about die() blocks is perhaps a bit too general. While it's true that some die()'s signal very unlikely (or near-impossible) conditions, others are merely reporting invalid user or other input to the program. The latter category is often very much worth testing, as the number of test_must_fail() invocations in the test suite shows. 68a6b3a1bd (worktree: teach 'move' to override lock when --force given twice, 2018-08-28), which was highlighted in your cover letter, provides a good example of legitimately testing that a die() is covered. So, perhaps the above can be toned-down a bit by saying something like:

...but die() blocks covering very unlikely (or near-impossible) situations may not warrant coverage.

Quoted 2 lines

It is important to not measure the coverage of the codebase by what old code is not covered.

#12 lore raw

Re: [PATCH v2 1/1] contrib: add coverage-diff script

In reply to Junio C Hamano
Quoted 35 lines · Junio C Hamano

contrib/coverage-diff.sh line 25

Quoted 5 lines · Derrick Stolee via GitGitGadget
contrib/coverage-diff.sh new file +5 -0
+			then
+				line_num=$(($line_num + 1))
+			fi
+		fi
+	done

I have a feeling that a single Perl script instead of a shell loop that runs many grep and awk as subprocesses performs better even on Windows, and it would be more readable and maintainable.

perl -e '

	my $line_num;
	while (<>) {
		# Hunk header?  Grab the beginning in postimage.
		if (/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/) {
			$line_num = $1;
			next;
		}

		# Have we seen a hunk?  Ignore "diff --git" etc.
		next unless defined $line_num;

		# Deleted line? Ignore.
		if (/^-/) {
			next;
		}

		# Show only the line number of added lines.
		if (/^\+/) {
			print "$line_num\n";
		}
		# Either common context or added line appear in
		# the postimage.  Count it.
		$line_num++;
	}
Quoted 4 lines

'

or something like that, given that you seem to only need line numbers in new_lines.txt anyway?

Thanks for the deep dive here, especially with the perl assistance. I've never written any perl, but it seems like the right tool here. I'll have time to revisit this next week.

Thanks,

-Stolee

#14 lore raw

v3 cover contrib: Add script to show uncovered "new" lines

In reply to Derrick Stolee via GitGitGadget

We have coverage targets in our Makefile for using gcov to display line coverage based on our test suite. The way I like to do it is to run:

make coverage-test
make coverage-report

This leaves the repo in a state where every X.c file that was covered has an X.c.gcov file containing the coverage counts for every line, and "#####" at every uncovered line.

There have been a few bugs in recent patches what would have been caught if the test suite covered those blocks (including a few of mine). I want to work towards a "sensible" amount of coverage on new topics. In my opinion, this means that any logic should be covered, but the 'die()' blocks in error cases do not need to be covered.

It is important to not measure the coverage of the codebase by what old code is not covered. To help, I created the 'contrib/coverage-diff.sh' script. After creating the coverage statistics at a version (say, 'topic') you can then run

contrib/coverage-diff.sh base topic

to see the lines added between 'base' and 'topic' that are not covered by the test suite. For example, I ran this against the 'next' branch (22e244b) versus 'master' (150f307) and got the following output:

fsck.c
fb8952077df (René Scharfe 2018-09-03 14:49:26 +0000 212) die_errno("Could not read '%s'", path);
list-objects-filter-options.c

f12b8fc6d3b     (Matthew DeVore 2018-09-13 17:55:27 -0700       56)                     if (errbuf) {
f12b8fc6d3b     (Matthew DeVore 2018-09-13 17:55:27 -0700       57)                             strbuf_init(errbuf, 0);
f12b8fc6d3b     (Matthew DeVore 2018-09-13 17:55:27 -0700       58)                             strbuf_addstr(
f12b8fc6d3b     (Matthew DeVore 2018-09-13 17:55:27 -0700       62)                     return 1;
list-objects-filter.c
77d7a65d502     (Matthew DeVore 2018-09-13 17:55:26 -0700       47)             BUG("unknown filter_situation: %d", filter_situation);
f12b8fc6d3b     (Matthew DeVore 2018-09-13 17:55:27 -0700       100)    default:
f12b8fc6d3b     (Matthew DeVore 2018-09-13 17:55:27 -0700       101)            BUG("unknown filter_situation: %d", filter_situation);
77d7a65d502     (Matthew DeVore 2018-09-13 17:55:26 -0700       152)            BUG("unknown filter_situation: %d", filter_situation);
77d7a65d502     (Matthew DeVore 2018-09-13 17:55:26 -0700       257)            BUG("unknown filter_situation: %d", filter_situation);
77d7a65d502     (Matthew DeVore 2018-09-13 17:55:26 -0700       438)            BUG("invalid list-objects filter choice: %d",

list-objects.c
f447a499dbb (Matthew DeVore 2018-08-13 11:14:28 -0700 197) ctx->show_object(obj, base->buf, ctx->show_data);
ll-merge.c
d64324cb60e (Torsten Bögershausen 2018-09-12 21:32:02 +0200 379) marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
midx.c

56ee7ff1565     (Derrick Stolee 2018-09-13 11:02:13 -0700       949)            return 0;
cc6af73c029     (Derrick Stolee 2018-09-13 11:02:25 -0700       990)                    midx_report(_("failed to load pack-index for packfile %s"),
cc6af73c029     (Derrick Stolee 2018-09-13 11:02:25 -0700       991)                                e.p->pack_name);
cc6af73c029     (Derrick Stolee 2018-09-13 11:02:25 -0700       992)                    break;

remote-curl.c
c3b9bc94b9b (Elijah Newren 2018-09-05 10:03:07 -0700 181) options.filter = xstrdup(value);
submodule.c
df255b8cac7 (Brandon Williams 2018-08-08 15:33:22 -0700 1738) die(_("could not create directory '%s'"), new_gitdir.buf);

Using this 'git blame' output, we can quickly inspect whether the uncovered lines are appropriate. For instance:

1. The line in builtin/commit.c is due to writing the commit-graph file when GIT_TEST_COMMIT_GRAPH is enabled, which is not on by default in the test suite. Being uncovered is expected here.

2. The lines in builtin/worktree.c are all related to error conditions. This is acceptable.

3. The line in builtin/rev-list.c is a flag replacement in a block that is otherwise unchanged. It must not be covered by the test suite normally. This could be worth adding a test to ensure the new logic maintains old behavior.

4. The lines in read-cache.c are part of a new block for the condition "if (expand_name_field)" as part of an optimization. These lines should probably be covered before that series is merged to 'next'. I understand that Ben and Duy are continuing work in this direction [1].

I used this approach for 'next' over 'master' and got a larger list, some of which I have already submitted tests to increase coverage [2] or will be covered by topics not in 'next' [3].

Thanks, -Stolee

CHANGES IN V3: I took Junio's perl script verbatim, which speeds up the performance greatly. Some of the other sed commands needed some massaging, but also added extra cleanup. Thanks for the help!

[1]
https://public-inbox.org/git/[email protected]/T/#u

[2] https://public-inbox.org/git/[email protected]/

[3] https://public-inbox.org/git/[email protected]/

Derrick Stolee (1):
contrib: add coverage-diff script

 contrib/coverage-diff.sh | 79 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)
 create mode 100755 contrib/coverage-diff.sh

base-commit: 1d4361b0f344188ab5eec6dcea01f61a3a3a1670
Published-As: https://github.com/gitgitgadget/git/releases/tags/pr-40%2Fderrickstolee%2Fcoverage-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-40/derrickstolee/coverage-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/40

Range-diff vs v2:

 1:  7714b0659e < -:  ---------- contrib: add coverage-diff script
 -:  ---------- > 1:  21214cc321 contrib: add coverage-diff script
Signature · 1 line
gitgitgadget
#15 lore raw

PATCH v3 1/1 contrib: add coverage-diff script

From: Derrick Stolee <[email protected]>

We have coverage targets in our Makefile for using gcov to display line coverage based on our test suite. The way I like to do it is to run:

make coverage-test
make coverage-report

This leaves the repo in a state where every X.c file that was covered has an X.c.gcov file containing the coverage counts for every line, and "#####" at every uncovered line.

There have been a few bugs in recent patches what would have been caught if the test suite covered those blocks (including a few of mine). I want to work towards a "sensible" amount of coverage on new topics. In my opinion, this means that any logic should be covered, but the 'die()' blocks covering very unlikely (or near-impossible) situations may not warrant coverage.

It is important to not measure the coverage of the codebase by what old code is not covered. To help, I created the 'contrib/coverage-diff.sh' script. After creating the coverage statistics at a version (say, 'topic') you can then run

contrib/coverage-diff.sh base topic

to see the lines added between 'base' and 'topic' that are not covered by the test suite. The output uses 'git blame -c' format so you can find the commits responsible and view the line numbers for quick access to the context.

---
 contrib/coverage-diff.sh | 79 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)
 create mode 100755 contrib/coverage-diff.sh
contrib/coverage-diff.sh new file +79 -0
new file mode 100755
index 0000000000..48b9a3ae96
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+# Usage: Run 'contrib/coverage-diff.sh <version1> <version2>' from source-root
+# after running
+#
+#     make coverage-test
+#     make coverage-report
+#
+# while checked out at <version2>. This script combines the *.gcov files
+# generated by the 'make' commands above with 'git diff <version1> <version2>'
+# to report new lines that are not covered by the test suite.
+
+V1=$1
+V2=$2
+
+diff_lines () {
+	perl -e '
+		my $line_num;
+		while (<>) {
+			# Hunk header?  Grab the beginning in postimage.
+			if (/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/) {
+				$line_num = $1;
+				next;
+			}
+
+			# Have we seen a hunk?  Ignore "diff --git" etc.
+			next unless defined $line_num;
+
+			# Deleted line? Ignore.
+			if (/^-/) {
+				next;
+			}
+
+			# Show only the line number of added lines.
+			if (/^\+/) {
+				print "$line_num\n";
+			}
+			# Either common context or added line appear in
+			# the postimage.  Count it.
+			$line_num++;
+		}
+	'
+}
+
+files=$(git diff --name-only $V1 $V2 -- *.c)
+
+for file in $files
+do
+	git diff $V1 $V2 -- $file \
+		| diff_lines \
+		| sort >new_lines.txt
+
+	if ! test -s new_lines.txt
+	then
+		continue
+	fi
+
+	hash_file=$(echo $file | sed "s/\//\#/")
+	sed -ne '/#####:/{
+			s/    #####://
+			s/:.*//
+			s/ //g
+			p
+		}' "$hash_file.gcov" \
+		| sort >uncovered_lines.txt
+
+	comm -12 uncovered_lines.txt new_lines.txt \
+		| sed -e 's/$/\)/' \
+		| sed -e 's/^/\t/' \
+		>uncovered_new_lines.txt
+
+	grep -q '[^[:space:]]' < uncovered_new_lines.txt && \
+		echo $file && \
+		git blame -c $file \
+			| grep -f uncovered_new_lines.txt
+
+	rm -f new_lines.txt uncovered_lines.txt uncovered_new_lines.txt
+done
+
Signature · 1 line
gitgitgadget
#17 lore raw
In reply to Derrick Stolee via GitGitGadget
Quoted 22 lines · Derrick Stolee via GitGitGadget

For example, I ran this against the 'next' branch (22e244b) versus 'master' (150f307) and got the following output:

fsck.c
fb8952077df (René Scharfe 2018-09-03 14:49:26 +0000 212) die_errno("Could not read '%s'", path);
list-objects-filter-options.c

f12b8fc6d3b     (Matthew DeVore 2018-09-13 17:55:27 -0700       56)                     if (errbuf) {
f12b8fc6d3b     (Matthew DeVore 2018-09-13 17:55:27 -0700       57)                             strbuf_init(errbuf, 0);
f12b8fc6d3b     (Matthew DeVore 2018-09-13 17:55:27 -0700       58)                             strbuf_addstr(
f12b8fc6d3b     (Matthew DeVore 2018-09-13 17:55:27 -0700       62)                     return 1;
list-objects-filter.c
77d7a65d502     (Matthew DeVore 2018-09-13 17:55:26 -0700       47)             BUG("unknown filter_situation: %d", filter_situation);
f12b8fc6d3b     (Matthew DeVore 2018-09-13 17:55:27 -0700       100)    default:
f12b8fc6d3b     (Matthew DeVore 2018-09-13 17:55:27 -0700       101)            BUG("unknown filter_situation: %d", filter_situation);
77d7a65d502     (Matthew DeVore 2018-09-13 17:55:26 -0700       152)            BUG("unknown filter_situation: %d", filter_situation);
77d7a65d502     (Matthew DeVore 2018-09-13 17:55:26 -0700       257)            BUG("unknown filter_situation: %d", filter_situation);
77d7a65d502     (Matthew DeVore 2018-09-13 17:55:26 -0700       438)            BUG("invalid list-objects filter choice: %d",

list-objects.c
f447a499dbb (Matthew DeVore 2018-08-13 11:14:28 -0700 197) ctx->show_object(obj, base->buf, ctx->show_data);
ll-merge.c
d64324cb60e (Torsten Bögershausen 2018-09-12 21:32:02 +0200 379) marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
midx.c

Quoted 8 lines
56ee7ff1565     (Derrick Stolee 2018-09-13 11:02:13 -0700       949)            return 0;
cc6af73c029     (Derrick Stolee 2018-09-13 11:02:25 -0700       990)                    midx_report(_("failed to load pack-index for packfile %s"),
cc6af73c029     (Derrick Stolee 2018-09-13 11:02:25 -0700       991)                                e.p->pack_name);
cc6af73c029     (Derrick Stolee 2018-09-13 11:02:25 -0700       992)                    break;

remote-curl.c
c3b9bc94b9b (Elijah Newren 2018-09-05 10:03:07 -0700 181) options.filter = xstrdup(value);
submodule.c
df255b8cac7 (Brandon Williams 2018-08-08 15:33:22 -0700 1738) die(_("could not create directory '%s'"), new_gitdir.buf);

I updated this output, but then forgot that I had a "commentary" of the old diff below it. Please ignore that portion of the cover letter.

-Stolee

#16 lore raw

Re: [PATCH v3 1/1] contrib: add coverage-diff script

In reply to Derrick Stolee via GitGitGadget
Quoted 1 line · Derrick Stolee via GitGitGadget

+files=$(git diff --name-only $V1 $V2 -- *.c)

You'd want to quote that *.c from the shell, i.e. either one of these

	files=$(git diff --name-only $V1 $V2 -- \*.c)
	files=$(git diff --name-only $V1 $V2 -- '*.c')

otherwise you'd lose things like "builtin/am.c", I'd think.

contrib/coverage-diff.sh line 48

Quoted 3 lines
contrib/coverage-diff.sh new file +3 -0
+
+for file in $files
+do

I know this is only for running in _our_ source tree, and we do not have a source with $IFS in it, so I'd declare that this is OK. It would be good to document that assumption in red capital letters at the beginning of this loop, though ;-)

# Note: this script is only for our codebase and we rely on # the fact that the pathnames of our source files do not # have any funny characters---letting the shell split $files # list at $IFS boundary is very much intentional, and not # quoting "$file" in the code below also is. Don't imitate # this in scripts that are meant to handle random end-user # repositories!
for file in $files
do
...

contrib/coverage-diff.sh line 51

Quoted 3 lines
contrib/coverage-diff.sh new file +3 -0
+	git diff $V1 $V2 -- $file \
+		| diff_lines \
+		| sort >new_lines.txt

I do not see a strong reason why we would want to limit $V1 and $V2 to branch names and raw commit object names, and quoting them in dq pair is a cheap fix to allow things like

	$ contrib/coverage-diff.sh master 'pu^{/^### match next}'

so let's do so.

Could you cut lines _after_ typing a pipe and omit backslashes, i.e.

	git diff "$V1" "$V2" -- $file |
	diff_lines |
	sort >new_lines.txt

It seems to be personal taste whether to indent the second and subsequent lines; I do not care if you indent or if you align too much either way (but I have moderate perference to align).

But I do not want to see people type unnecessary backslashes. This is not limited to just this pipeline but elsewhere in the script.

Quoted 7 lines
contrib/coverage-diff.sh new file +7 -0
+	if ! test -s new_lines.txt
+	then
+		continue
+	fi
+
+	hash_file=$(echo $file | sed "s/\//\#/")
+	sed -ne '/#####:/{

contrib/coverage-diff.sh line 69

Quoted 10 lines
contrib/coverage-diff.sh new file +10 -0
+			s/    #####://
+			s/:.*//
+			s/ //g
+			p
+		}' "$hash_file.gcov" \
+		| sort >uncovered_lines.txt
+
+	comm -12 uncovered_lines.txt new_lines.txt \
+		| sed -e 's/$/\)/' \
+		| sed -e 's/^/\t/' \

Do you need two sed invocations for this, or would

	sed -e 's/$/\)/' -e '/^/	/'

work as well? By the way """The meaning of an unescaped <backslash> immediately followed by any character other than '&', <backslash>, a digit, <newline>, or the delimiter character used for this command, is unspecified."""[*1*] so '\t' on the replacement side is a no-no in the portability department.

	Reference. *1*
	http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html

contrib/coverage-diff.sh line 72

Quoted 3 lines
contrib/coverage-diff.sh new file +3 -0
+		>uncovered_new_lines.txt
+
+	grep -q '[^[:space:]]' < uncovered_new_lines.txt && \

Lose the backslash at the end. The shell knows that you haven't finished your sentence when it sees a line that ends with &&, || or a pipe |, so there is no need to tell it redundantly that you have more things to say with the backslash.

contrib/coverage-diff.sh line 78

Quoted 6 lines
contrib/coverage-diff.sh new file +6 -0
+		echo $file && \
+		git blame -c $file \
+			| grep -f uncovered_new_lines.txt
+
+	rm -f new_lines.txt uncovered_lines.txt uncovered_new_lines.txt
+done

Near the begininng (like just before the "for file in $files" loop), you can probably have a trap to make sure these are removed upon exit, e.g.

trap 'rm -f new_lines.txt uncovered_lines.txt uncovered_new_lines.txt' 0

#18 lore raw

v4 cover contrib: Add script to show uncovered "new" lines

In reply to Derrick Stolee via GitGitGadget

We have coverage targets in our Makefile for using gcov to display line coverage based on our test suite. The way I like to do it is to run:

make coverage-test
make coverage-report

This leaves the repo in a state where every X.c file that was covered has an X.c.gcov file containing the coverage counts for every line, and "#####" at every uncovered line.

There have been a few bugs in recent patches what would have been caught if the test suite covered those blocks (including a few of mine). I want to work towards a "sensible" amount of coverage on new topics. In my opinion, this means that any logic should be covered, but the 'die()' blocks in error cases do not need to be covered.

It is important to not measure the coverage of the codebase by what old code is not covered. To help, I created the 'contrib/coverage-diff.sh' script. After creating the coverage statistics at a version (say, 'topic') you can then run

contrib/coverage-diff.sh base topic

to see the lines added between 'base' and 'topic' that are not covered by the test suite. For example, I ran this against the 'next' branch (e82ca0) versus 'master' (f84b9b) and got the following output:

builtin/commit.c
76f2f5c1e3 builtin/commit.c 1657) write_commit_graph_reachable(get_object_directory(), 0, 0);

builtin/fsck.c
66ec0390e7 builtin/fsck.c 862) midx_argv[2] = "--object-dir"; 66ec0390e7 builtin/fsck.c 863) midx_argv[3] = alt->path; 66ec0390e7 builtin/fsck.c 864) if (run_command(&midx_verify)) 66ec0390e7 builtin/fsck.c 865) errors_found |= ERROR_COMMIT_GRAPH;

fsck.c
fb8952077d 214) die_errno("Could not read '%s'", path);

midx.c

56ee7ff156  949) return 0;
cc6af73c02  990) midx_report(_("failed to load pack-index for packfile %s"),
cc6af73c02  991)     e.p->pack_name);
cc6af73c02  992) break;

Commits introducing uncovered code:
Derrick Stolee 56ee7ff15: multi-pack-index: add 'verify' verb
Derrick Stolee 66ec0390e: fsck: verify multi-pack-index
Derrick Stolee cc6af73c0: multi-pack-index: verify object offsets
Junio C Hamano 76f2f5c1e: Merge branch 'ab/commit-graph-progress' into next
René Scharfe fb8952077: fsck: use strbuf_getline() to read skiplist file

Thanks, -Stolee

CHANGES IN V3: I took Junio's perl script verbatim, which speeds up the performance greatly. Some of the other sed commands needed some massaging, but also added extra cleanup. Thanks for the help!

CHANGES IN V4: I reduced the blame output using -s which decreases the width. I include a summary of the commit authors at the end to help people see the lines they wrote. This version is also copied into a build definition in the public Git project on Azure Pipelines [1]. I'll use this build definition to generate the coverage report after each "What's Cooking" email.

[1] https://git.visualstudio.com/git/_build?definitionId=5

Derrick Stolee (1):
contrib: add coverage-diff script

 contrib/coverage-diff.sh | 108 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 108 insertions(+)
 create mode 100755 contrib/coverage-diff.sh

base-commit: 1d4361b0f344188ab5eec6dcea01f61a3a3a1670
Published-As: https://github.com/gitgitgadget/git/releases/tags/pr-40%2Fderrickstolee%2Fcoverage-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-40/derrickstolee/coverage-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/40

Range-diff vs v3:

1: 21214cc321 ! 1: 6daf310a43 contrib: add coverage-diff script

     @@ -26,10 +26,10 @@
              contrib/coverage-diff.sh base topic
      
          to see the lines added between 'base' and 'topic' that are not covered by the
     -    test suite. The output uses 'git blame -c' format so you can find the commits
     -    responsible and view the line numbers for quick access to the context.
     +    test suite. The output uses 'git blame -s' format so you can find the commits
     +    responsible and view the line numbers for quick access to the context, but
     +    trims leading tabs in the file contents to reduce output width.
      
     -    Helped-by: Junio C Hamano <[email protected]>
          Signed-off-by: Derrick Stolee <[email protected]>
      
      diff --git a/contrib/coverage-diff.sh b/contrib/coverage-diff.sh
     @@ -81,13 +81,16 @@
      +	'
      +}
      +
     -+files=$(git diff --name-only $V1 $V2 -- *.c)
     ++files=$(git diff --name-only "$V1" "$V2" -- \*.c)
     ++
     ++# create empty file
     ++>coverage-data.txt
      +
      +for file in $files
      +do
     -+	git diff $V1 $V2 -- $file \
     -+		| diff_lines \
     -+		| sort >new_lines.txt
     ++	git diff "$V1" "$V2" -- "$file" |
     ++	diff_lines |
     ++	sort >new_lines.txt
      +
      +	if ! test -s new_lines.txt
      +	then
     @@ -95,24 +98,50 @@
      +	fi
      +
      +	hash_file=$(echo $file | sed "s/\//\#/")
     ++
     ++	if ! test -s "$hash_file.gcov"
     ++	then
     ++		continue
     ++	fi
     ++
      +	sed -ne '/#####:/{
      +			s/    #####://
      +			s/:.*//
      +			s/ //g
      +			p
     -+		}' "$hash_file.gcov" \
     -+		| sort >uncovered_lines.txt
     ++		}' "$hash_file.gcov" |
     ++	sort >uncovered_lines.txt
      +
     -+	comm -12 uncovered_lines.txt new_lines.txt \
     -+		| sed -e 's/$/\)/' \
     -+		| sed -e 's/^/\t/' \
     -+		>uncovered_new_lines.txt
     ++	comm -12 uncovered_lines.txt new_lines.txt |
     ++	sed -e 's/$/\)/' |
     ++	sed -e 's/^/ /' >uncovered_new_lines.txt
      +
     -+	grep -q '[^[:space:]]' < uncovered_new_lines.txt && \
     -+		echo $file && \
     -+		git blame -c $file \
     -+			| grep -f uncovered_new_lines.txt
     ++	grep -q '[^[:space:]]' <uncovered_new_lines.txt &&
     ++	echo $file >>coverage-data.txt &&
     ++	git blame -s "$V2" -- "$file" |
     ++	sed 's/\t//g' |
     ++	grep -f uncovered_new_lines.txt >>coverage-data.txt &&
     ++	echo >>coverage-data.txt
      +
      +	rm -f new_lines.txt uncovered_lines.txt uncovered_new_lines.txt
      +done
      +
     ++cat coverage-data.txt
     ++
     ++echo "Commits introducing uncovered code:"
     ++
     ++commit_list=$(cat coverage-data.txt |
     ++	grep -E '^[0-9a-f]{7,} ' |
     ++	awk '{print $1;}' |
     ++	sort |
     ++	uniq)
     ++
     ++(
     ++	for commit in $commit_list
     ++	do
     ++		git log --no-decorate --pretty=format:'%an      %h: %s' -1 $commit
     ++		echo
     ++	done
     ++) | sort
     ++
     ++rm coverage-data.txt
Signature · 1 line
gitgitgadget
#19 lore raw

PATCH v4 1/1 contrib: add coverage-diff script

From: Derrick Stolee <[email protected]>

We have coverage targets in our Makefile for using gcov to display line coverage based on our test suite. The way I like to do it is to run:

make coverage-test
make coverage-report

This leaves the repo in a state where every X.c file that was covered has an X.c.gcov file containing the coverage counts for every line, and "#####" at every uncovered line.

There have been a few bugs in recent patches what would have been caught if the test suite covered those blocks (including a few of mine). I want to work towards a "sensible" amount of coverage on new topics. In my opinion, this means that any logic should be covered, but the 'die()' blocks covering very unlikely (or near-impossible) situations may not warrant coverage.

It is important to not measure the coverage of the codebase by what old code is not covered. To help, I created the 'contrib/coverage-diff.sh' script. After creating the coverage statistics at a version (say, 'topic') you can then run

contrib/coverage-diff.sh base topic

to see the lines added between 'base' and 'topic' that are not covered by the test suite. The output uses 'git blame -s' format so you can find the commits responsible and view the line numbers for quick access to the context, but trims leading tabs in the file contents to reduce output width.

---
 contrib/coverage-diff.sh | 108 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 108 insertions(+)
 create mode 100755 contrib/coverage-diff.sh
contrib/coverage-diff.sh new file +108 -0
new file mode 100755
index 0000000000..4ec419f900
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+# Usage: Run 'contrib/coverage-diff.sh <version1> <version2>' from source-root
+# after running
+#
+#     make coverage-test
+#     make coverage-report
+#
+# while checked out at <version2>. This script combines the *.gcov files
+# generated by the 'make' commands above with 'git diff <version1> <version2>'
+# to report new lines that are not covered by the test suite.
+
+V1=$1
+V2=$2
+
+diff_lines () {
+	perl -e '
+		my $line_num;
+		while (<>) {
+			# Hunk header?  Grab the beginning in postimage.
+			if (/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/) {
+				$line_num = $1;
+				next;
+			}
+
+			# Have we seen a hunk?  Ignore "diff --git" etc.
+			next unless defined $line_num;
+
+			# Deleted line? Ignore.
+			if (/^-/) {
+				next;
+			}
+
+			# Show only the line number of added lines.
+			if (/^\+/) {
+				print "$line_num\n";
+			}
+			# Either common context or added line appear in
+			# the postimage.  Count it.
+			$line_num++;
+		}
+	'
+}
+
+files=$(git diff --name-only "$V1" "$V2" -- \*.c)
+
+# create empty file
+>coverage-data.txt
+
+for file in $files
+do
+	git diff "$V1" "$V2" -- "$file" |
+	diff_lines |
+	sort >new_lines.txt
+
+	if ! test -s new_lines.txt
+	then
+		continue
+	fi
+
+	hash_file=$(echo $file | sed "s/\//\#/")
+
+	if ! test -s "$hash_file.gcov"
+	then
+		continue
+	fi
+
+	sed -ne '/#####:/{
+			s/    #####://
+			s/:.*//
+			s/ //g
+			p
+		}' "$hash_file.gcov" |
+	sort >uncovered_lines.txt
+
+	comm -12 uncovered_lines.txt new_lines.txt |
+	sed -e 's/$/\)/' |
+	sed -e 's/^/ /' >uncovered_new_lines.txt
+
+	grep -q '[^[:space:]]' <uncovered_new_lines.txt &&
+	echo $file >>coverage-data.txt &&
+	git blame -s "$V2" -- "$file" |
+	sed 's/\t//g' |
+	grep -f uncovered_new_lines.txt >>coverage-data.txt &&
+	echo >>coverage-data.txt
+
+	rm -f new_lines.txt uncovered_lines.txt uncovered_new_lines.txt
+done
+
+cat coverage-data.txt
+
+echo "Commits introducing uncovered code:"
+
+commit_list=$(cat coverage-data.txt |
+	grep -E '^[0-9a-f]{7,} ' |
+	awk '{print $1;}' |
+	sort |
+	uniq)
+
+(
+	for commit in $commit_list
+	do
+		git log --no-decorate --pretty=format:'%an      %h: %s' -1 $commit
+		echo
+	done
+) | sort
+
+rm coverage-data.txt
Signature · 1 line
gitgitgadget
#20 lore raw
In reply to Derrick Stolee via GitGitGadget
Quoted 8 lines · Derrick Stolee via GitGitGadget

CHANGES IN V4: I reduced the blame output using -s which decreases the width. I include a summary of the commit authors at the end to help people see the lines they wrote. This version is also copied into a build definition in the public Git project on Azure Pipelines [1]. I'll use this build definition to generate the coverage report after each "What's Cooking" email.

[1] https://git.visualstudio.com/git/_build?definitionId=5

Thanks. Let's move this forward by merging it down to 'next'.

#21 lore raw
Quoted 11 lines · Junio C Hamano
Quoted 8 lines · Derrick Stolee via GitGitGadget

CHANGES IN V4: I reduced the blame output using -s which decreases the width. I include a summary of the commit authors at the end to help people see the lines they wrote. This version is also copied into a build definition in the public Git project on Azure Pipelines [1]. I'll use this build definition to generate the coverage report after each "What's Cooking" email.

[1] https://git.visualstudio.com/git/_build?definitionId=5

Thanks. Let's move this forward by merging it down to 'next'.

Sounds good. When it moves into 'master' I can update my build to call the script from source.

Thanks,
-Stolee