lib/git/diff.rb
# frozen_string_literal: true require_relative 'diff_path_status' require_relative 'diff_stats' module Git # object that holds the diff between two commits class Diff include Enumerable def initialize(base, from = nil, to = nil) @base = base @from = from && from.to_s @to = to && to.to_s @path = nil @full_diff_files = nil end attr_reader :from, :to def path(path) @path = path self end def patch @base.lib.diff_full(@from, @to, { path_limiter: @path }) end alias_method :to_s, :patch def [](key) process_full @full_diff_files.assoc(key)[1] end def each(&block) process_full @full_diff_files.map { |file| file[1] }.each(&block) end # # DEPRECATED METHODS # def name_status Git::Deprecation.warn("Git::Diff#name_status is deprecated. Use Git::Base#diff_path_status instead.") path_status_provider.to_h end def size Git::Deprecation.warn("Git::Diff#size is deprecated. Use Git::Base#diff_stats(...).total[:files] instead.") stats_provider.total[:files] end def lines Git::Deprecation.warn("Git::Diff#lines is deprecated. Use Git::Base#diff_stats(...).lines instead.") stats_provider.lines end def deletions Git::Deprecation.warn("Git::Diff#deletions is deprecated. Use Git::Base#diff_stats(...).deletions instead.") stats_provider.deletions end def insertions Git::Deprecation.warn("Git::Diff#insertions is deprecated. Use Git::Base#diff_stats(...).insertions instead.") stats_provider.insertions end def stats Git::Deprecation.warn("Git::Diff#stats is deprecated. Use Git::Base#diff_stats instead.") # CORRECTED: Re-create the original hash structure for backward compatibility { files: stats_provider.files, total: stats_provider.total } end class DiffFile attr_accessor :patch, :path, :mode, :src, :dst, :type @base = nil NIL_BLOB_REGEXP = /\A0{4,40}\z/.freeze def initialize(base, hash) @base = base @patch = hash[:patch] @path = hash[:path] @mode = hash[:mode] @src = hash[:src] @dst = hash[:dst] @type = hash[:type] @binary = hash[:binary] end def binary? !!@binary end def blob(type = :dst) if type == :src && !NIL_BLOB_REGEXP.match(@src) @base.object(@src) elsif !NIL_BLOB_REGEXP.match(@dst) @base.object(@dst) end end end private def process_full return if @full_diff_files @full_diff_files = process_full_diff end # CORRECTED: Pass the @path variable to the new objects def path_status_provider @path_status_provider ||= Git::DiffPathStatus.new(@base, @from, @to, @path) end # CORRECTED: Pass the @path variable to the new objects def stats_provider @stats_provider ||= Git::DiffStats.new(@base, @from, @to, @path) end def process_full_diff defaults = { mode: '', src: '', dst: '', type: 'modified' } final = {} current_file = nil patch.split("\n").each do |line| if m = %r{\Adiff --git ("?)a/(.+?)\1 ("?)b/(.+?)\3\z}.match(line) current_file = Git::EscapedPath.new(m[2]).unescape final[current_file] = defaults.merge({ patch: line, path: current_file }) else if m = /^index ([0-9a-f]{4,40})\.\.([0-9a-f]{4,40})( ......)*/.match(line) final[current_file][:src] = m[1] final[current_file][:dst] = m[2] final[current_file][:mode] = m[3].strip if m[3] end if m = /^([[:alpha:]]*?) file mode (......)/.match(line) final[current_file][:type] = m[1] final[current_file][:mode] = m[2] end if m = /^Binary files /.match(line) final[current_file][:binary] = true end final[current_file][:patch] << "\n" + line end end final.map { |e| [e[0], DiffFile.new(@base, e[1])] } end end end