Skip to content

Commit

Permalink
Memoize all of the significant calls in Git::Status
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
nevinera authored and jcouball committed Jun 1, 2024
1 parent 2bacccc commit 749a72d
Showing 1 changed file with 36 additions and 24 deletions.
60 changes: 36 additions & 24 deletions lib/git/status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

#
Expand All @@ -34,19 +34,15 @@ 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.
# File path starts at git base directory
#
# @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
Expand All @@ -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

#
Expand All @@ -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

#
Expand All @@ -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

#
Expand All @@ -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

#
Expand All @@ -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
Expand Down Expand Up @@ -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

0 comments on commit 749a72d

Please sign in to comment.