2015-07-12 21:06:39 -04:00
|
|
|
module Mutant
|
|
|
|
module Repository
|
|
|
|
# Error raised on repository interaction problems
|
|
|
|
RepositoryError = Class.new(RuntimeError)
|
|
|
|
|
|
|
|
# Subject filter based on repository diff
|
|
|
|
class SubjectFilter
|
|
|
|
include Adamantium, Concord.new(:diff)
|
|
|
|
|
|
|
|
# Test if subject was touched in diff
|
|
|
|
#
|
|
|
|
# @param [Subject] subject
|
|
|
|
#
|
|
|
|
# @return [Boolean]
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
def call(subject)
|
|
|
|
diff.touches?(subject.source_path, subject.source_lines)
|
|
|
|
end
|
|
|
|
|
|
|
|
end # SubjectFilter
|
|
|
|
|
|
|
|
# Diff between two objects in repository
|
|
|
|
class Diff
|
|
|
|
include Adamantium, Concord.new(:from, :to)
|
|
|
|
|
|
|
|
HEAD = 'HEAD'.freeze
|
|
|
|
private_constant(*constants(false))
|
|
|
|
|
|
|
|
# Create diff from head to revision
|
|
|
|
#
|
|
|
|
# @return [Diff]
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
def self.from_head(to)
|
|
|
|
new(HEAD, to)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Test if diff changes file at line range
|
|
|
|
#
|
|
|
|
# @param [Pathname] path
|
|
|
|
# @param [Range<Fixnum>] line_range
|
|
|
|
#
|
|
|
|
# @return [Boolean]
|
|
|
|
#
|
|
|
|
# @raise [RepositoryError]
|
|
|
|
# when git command failed
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
def touches?(path, line_range)
|
2015-07-28 15:34:01 -04:00
|
|
|
return false unless within_working_directory?(path) && tracks?(path)
|
2015-07-21 17:04:12 -04:00
|
|
|
|
2015-07-12 21:06:39 -04:00
|
|
|
command = %W[
|
2015-07-21 16:52:45 -04:00
|
|
|
git log
|
2015-07-12 21:06:39 -04:00
|
|
|
#{from}...#{to}
|
|
|
|
-L #{line_range.begin},#{line_range.end}:#{path}
|
|
|
|
]
|
|
|
|
|
|
|
|
stdout, status = Open3.capture2(*command, binmode: true)
|
|
|
|
|
|
|
|
fail RepositoryError, "Command #{command} failed!" unless status.success?
|
|
|
|
|
|
|
|
!stdout.empty?
|
|
|
|
end
|
|
|
|
|
2015-07-21 17:04:12 -04:00
|
|
|
private
|
|
|
|
|
|
|
|
# Test if path is tracked in repository
|
|
|
|
#
|
|
|
|
# FIXME: Cache results, to avoid spending time on producing redundant results.
|
|
|
|
#
|
|
|
|
# @param [Pathname] path
|
|
|
|
#
|
|
|
|
# @return [Boolean]
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
def tracks?(path)
|
|
|
|
command = %W[git ls-files --error-unmatch -- #{path}]
|
|
|
|
Kernel.system(
|
|
|
|
*command,
|
|
|
|
out: File::NULL,
|
|
|
|
err: File::NULL
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2015-07-28 15:34:01 -04:00
|
|
|
# Test if the path is within the current working directory
|
|
|
|
#
|
|
|
|
# @param [Pathname] path
|
|
|
|
#
|
|
|
|
# @return [TrueClass, nil]
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
def within_working_directory?(path)
|
|
|
|
working_directory = Pathname.pwd
|
|
|
|
path.ascend { |parent| return true if working_directory.eql?(parent) }
|
|
|
|
end
|
|
|
|
|
2015-07-12 21:06:39 -04:00
|
|
|
end # Diff
|
|
|
|
end # Repository
|
|
|
|
end # Mutant
|