Module: Mutineer::ChangedLines
- Defined in:
- lib/mutineer/changed_lines.rb
Overview
Maps each source file to the set of NEW-side line numbers changed since a git ref.
By parsing git diff --unified=0, this restricts mutations to only the
diff (issue #2): on a PR you care whether the changed code is tested, so
mutating just those lines is fast and actionable.
git is an external tool, not a gem dependency — shelling out is fine. The
parse carries the logic; git_diff is injectable so it stays testable
without invoking git.
Constant Summary collapse
- HUNK =
Matches unified-diff hunks and captures the new-file start/count.
/^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/
Class Method Summary collapse
-
.for(ref:, files:, project_root:, runner: method(:git_diff)) ⇒ Hash<String, Set<Integer>>
Builds a per-file map of changed new-side lines.
-
.git_diff(ref, abs_file, project_root) ⇒ String
Returns the stdout of
git -C <root> diff --unified=0 <ref> -- <file>. -
.parse(diff_text) ⇒ Set<Integer>
Parses unified diff text into the set of NEW-side line numbers.
Class Method Details
.for(ref:, files:, project_root:, runner: method(:git_diff)) ⇒ Hash<String, Set<Integer>>
Builds a per-file map of changed new-side lines.
51 52 53 54 55 56 |
# File 'lib/mutineer/changed_lines.rb', line 51 def for(ref:, files:, project_root:, runner: method(:git_diff)) files.each_with_object({}) do |file, acc| abs = File.(file, project_root) acc[abs] = parse(runner.call(ref, abs, project_root)) end end |
.git_diff(ref, abs_file, project_root) ⇒ String
Returns the stdout of git -C <root> diff --unified=0 <ref> -- <file>.
64 65 66 67 68 69 70 71 |
# File 'lib/mutineer/changed_lines.rb', line 64 def git_diff(ref, abs_file, project_root) out, _err, status = Open3.capture3( "git", "-C", project_root, "diff", "--unified=0", ref, "--", abs_file ) status.success? ? out : "" rescue StandardError "" end |
.parse(diff_text) ⇒ Set<Integer>
Parses unified diff text into the set of NEW-side line numbers.
With --unified=0 each hunk's +c,d block is exactly the changed lines:
c..c+d-1. d absent means 1 line; d == 0 is a pure deletion and
contributes nothing.
31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/mutineer/changed_lines.rb', line 31 def parse(diff_text) lines = Set.new diff_text.each_line do |row| m = HUNK.match(row) or next start = m[1].to_i count = m[2].nil? ? 1 : m[2].to_i next if count.zero? lines.merge(start...(start + count)) end lines end |