From dfa381f02805f7f0fc32f5b88941e1c92d376503 Mon Sep 17 00:00:00 2001 From: Eric Mueller Date: Fri, 31 May 2024 23:31:21 -0400 Subject: [PATCH] Memoize all of the significant calls in Git::Status When the status has many entries, there were substantial inefficiencies in this class - calling predicates like `changed?(filename)` would iterate the status, constructing a transient `changed` subhash, then test that subhash to see if the file in question was in it (for example). After this, it will _keep_ those sub-hashes for reuse on the Status instance, as well as downcased versions if they happen to get requested (by case-insensitive calls). --- lib/git/status.rb | 60 ++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/lib/git/status.rb b/lib/git/status.rb index f937cba1..39ceace7 100644 --- a/lib/git/status.rb +++ b/lib/git/status.rb @@ -22,7 +22,7 @@ def initialize(base) # # @return [Enumerable] def changed - @files.select { |_k, f| f.type == 'M' } + @_changed ||= @files.select { |_k, f| f.type == 'M' } end # @@ -34,11 +34,7 @@ def changed # changed?('lib/git.rb') # @return [Boolean] def changed?(file) - if ignore_case? - changed.keys.map(&:downcase).include?(file.downcase) - else - changed.member?(file) - end + case_aware_include?(:changed, :lc_changed, file) end # Returns an Enumerable containing files that have been added. @@ -46,7 +42,7 @@ def changed?(file) # # @return [Enumerable] def added - @files.select { |_k, f| f.type == 'A' } + @_added ||= @files.select { |_k, f| f.type == 'A' } end # Determines whether the given file has been added to the repository @@ -58,11 +54,7 @@ def added # added?('lib/git.rb') # @return [Boolean] def added?(file) - if ignore_case? - added.keys.map(&:downcase).include?(file.downcase) - else - added.member?(file) - end + case_aware_include?(:added, :lc_added, file) end # @@ -71,7 +63,7 @@ def added?(file) # # @return [Enumerable] def deleted - @files.select { |_k, f| f.type == 'D' } + @_deleted ||= @files.select { |_k, f| f.type == 'D' } end # @@ -83,11 +75,7 @@ def deleted # deleted?('lib/git.rb') # @return [Boolean] def deleted?(file) - if ignore_case? - deleted.keys.map(&:downcase).include?(file.downcase) - else - deleted.member?(file) - end + case_aware_include?(:deleted, :lc_deleted, file) end # @@ -96,7 +84,7 @@ def deleted?(file) # # @return [Enumerable] def untracked - @files.select { |_k, f| f.untracked } + @_untracked ||= @files.select { |_k, f| f.untracked } end # @@ -108,11 +96,7 @@ def untracked # untracked?('lib/git.rb') # @return [Boolean] def untracked?(file) - if ignore_case? - untracked.keys.map(&:downcase).include?(file.downcase) - else - untracked.member?(file) - end + case_aware_include?(:untracked, :lc_untracked, file) end def pretty @@ -290,5 +274,33 @@ def ignore_case? rescue Git::FailedError @_ignore_case = false end + + def downcase_keys(hash) + hash.map { |k, v| [k.downcase, v] }.to_h + end + + def lc_changed + @_lc_changed ||= changed.transform_keys(&:downcase) + end + + def lc_added + @_lc_added ||= added.transform_keys(&:downcase) + end + + def lc_deleted + @_lc_deleted ||= deleted.transform_keys(&:downcase) + end + + def lc_untracked + @_lc_untracked ||= untracked.transform_keys(&:downcase) + end + + def case_aware_include?(cased_hash, downcased_hash, file) + if ignore_case? + send(downcased_hash).include?(file.downcase) + else + send(cased_hash).include?(file) + end + end end end