Class: Git::StatusInfo

Inherits:
Data
  • Object
show all
Defined in:
lib/git/status_info.rb

Overview

Immutable value object for the status of a repository's index and working tree

Holds one StatusFileInfo per path that git status reports, in the order git listed them, together with the repository's core.ignoreCase setting. The derived readers (changed, added, deleted, untracked, unmerged) return the matching files keyed by path and are computed on each call. The path predicates (changed?, added?, deleted?, untracked?) compare paths case-insensitively when ignore_case is true.

Examples:

Inspect repository status

status = repo.status_info
status.changed.each_key { |path| puts "Modified: #{path}" }
status.added.each_key { |path| puts "Added: #{path}" }
status.deleted.each_key { |path| puts "Deleted: #{path}" }
status.untracked.each_key { |path| puts "Untracked: #{path}" }

Check one path

status = repo.status_info
status.changed?('lib/foo.rb')       #=> true
status['lib/foo.rb'].worktree_status #=> "M"

See Also:

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(files:, ignore_case:) ⇒ StatusInfo

Creates a status value object holding a frozen copy of the given files

Examples:

Build a status from parsed files

Git::StatusInfo.new(files: files, ignore_case: false)

Parameters:

  • files (Array<Git::StatusFileInfo>)

    the reported paths in git's output order

  • ignore_case (Boolean)

    whether path predicates ignore case



54
55
56
# File 'lib/git/status_info.rb', line 54

def initialize(files:, ignore_case:)
  super(files: files.dup.freeze, ignore_case: ignore_case)
end

Instance Attribute Details

#filesArray<Git::StatusFileInfo> (readonly)

Returns every reported path in git's output order; the array is frozen.

Returns:

  • (Array<Git::StatusFileInfo>)

    every reported path in git's output order; the array is frozen



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/git/status_info.rb', line 43

StatusInfo = Data.define(:files, :ignore_case) do
  # Creates a status value object holding a frozen copy of the given files
  #
  # @example Build a status from parsed files
  #   Git::StatusInfo.new(files: files, ignore_case: false)
  #
  # @param files [Array<Git::StatusFileInfo>] the reported paths in git's
  #   output order
  #
  # @param ignore_case [Boolean] whether path predicates ignore case
  #
  def initialize(files:, ignore_case:)
    super(files: files.dup.freeze, ignore_case: ignore_case)
  end

  # Returns the files modified or type-changed in the index or working tree
  #
  # @example List modified paths
  #   repo.status_info.changed.keys #=> ["lib/foo.rb"]
  #
  # @return [Hash{String => Git::StatusFileInfo}] changed files keyed by path
  #
  def changed = files_by_path(&:changed?)

  # Returns the files added to the index that are not in HEAD
  #
  # @example List added paths
  #   repo.status_info.added.keys #=> ["lib/new.rb"]
  #
  # @return [Hash{String => Git::StatusFileInfo}] added files keyed by path
  #
  def added = files_by_path(&:added?)

  # Returns the files deleted from the index or working tree
  #
  # @example List deleted paths
  #   repo.status_info.deleted.keys #=> ["lib/old.rb"]
  #
  # @return [Hash{String => Git::StatusFileInfo}] deleted files keyed by path
  #
  def deleted = files_by_path(&:deleted?)

  # Returns the files in the working tree that git does not track
  #
  # @example List untracked paths
  #   repo.status_info.untracked.keys #=> ["notes.txt"]
  #
  # @return [Hash{String => Git::StatusFileInfo}] untracked files keyed by path
  #
  def untracked = files_by_path(&:untracked?)

  # Returns the files with merge conflicts
  #
  # @example List conflicted paths
  #   repo.status_info.unmerged.keys #=> ["lib/conflict.rb"]
  #
  # @return [Hash{String => Git::StatusFileInfo}] unmerged files keyed by path
  #
  def unmerged = files_by_path(&:unmerged?)

  # Returns `true` if `path` is modified in the index or working tree
  #
  # @example Check a path
  #   repo.status_info.changed?('lib/foo.rb') #=> true
  #
  # @param path [String] the repository-relative path to check
  #
  # @return [Boolean] `true` when the path is in {#changed}
  #
  def changed?(path) = path_in?(changed, path)

  # Returns `true` if `path` was added to the index
  #
  # @example Check a path
  #   repo.status_info.added?('lib/new.rb') #=> true
  #
  # @param path [String] the repository-relative path to check
  #
  # @return [Boolean] `true` when the path is in {#added}
  #
  def added?(path) = path_in?(added, path)

  # Returns `true` if `path` was deleted from the index or working tree
  #
  # @example Check a path
  #   repo.status_info.deleted?('lib/old.rb') #=> true
  #
  # @param path [String] the repository-relative path to check
  #
  # @return [Boolean] `true` when the path is in {#deleted}
  #
  def deleted?(path) = path_in?(deleted, path)

  # Returns `true` if `path` is not tracked by git
  #
  # @example Check a path
  #   repo.status_info.untracked?('notes.txt') #=> true
  #
  # @param path [String] the repository-relative path to check
  #
  # @return [Boolean] `true` when the path is in {#untracked}
  #
  def untracked?(path) = path_in?(untracked, path)

  # Returns the {Git::StatusFileInfo} for the given path
  #
  # The path is matched exactly, regardless of `ignore_case`.
  #
  # @example Look up a path
  #   repo.status_info['lib/foo.rb'] #=> #<data Git::StatusFileInfo path="lib/foo.rb", ...>
  #   repo.status_info['clean.rb']   #=> nil
  #
  # @param path [String] the repository-relative path
  #
  # @return [Git::StatusFileInfo, nil] the file, or `nil` when git did not report it
  #
  def [](path) = files.find { |file| file.path == path }

  private

  # Returns the files for which the block is truthy, keyed by path
  #
  # @return [Hash{String => Git::StatusFileInfo}] the selected files keyed by path
  #
  # @yield [file] each {Git::StatusFileInfo} in `files`
  #
  # @yieldparam file [Git::StatusFileInfo] one reported path
  #
  # @yieldreturn [Boolean] truthy to include the file
  #
  def files_by_path(&) = files.select(&).to_h { |file| [file.path, file] }

  # Returns `true` when `path` is a key of `collection`, honoring `ignore_case`
  #
  # @param collection [Hash{String => Git::StatusFileInfo}] files keyed by path
  #
  # @param path [String] the repository-relative path to look for
  #
  # @return [Boolean] `true` when the path is present
  #
  def path_in?(collection, path)
    return collection.key?(path) unless ignore_case

    collection.each_key.any? { |key| key.casecmp?(path) }
  end
end

#ignore_caseBoolean (readonly)

Returns true when the repository's core.ignoreCase is true, making the path predicates compare paths case-insensitively.

Returns:

  • (Boolean)

    true when the repository's core.ignoreCase is true, making the path predicates compare paths case-insensitively



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/git/status_info.rb', line 43

StatusInfo = Data.define(:files, :ignore_case) do
  # Creates a status value object holding a frozen copy of the given files
  #
  # @example Build a status from parsed files
  #   Git::StatusInfo.new(files: files, ignore_case: false)
  #
  # @param files [Array<Git::StatusFileInfo>] the reported paths in git's
  #   output order
  #
  # @param ignore_case [Boolean] whether path predicates ignore case
  #
  def initialize(files:, ignore_case:)
    super(files: files.dup.freeze, ignore_case: ignore_case)
  end

  # Returns the files modified or type-changed in the index or working tree
  #
  # @example List modified paths
  #   repo.status_info.changed.keys #=> ["lib/foo.rb"]
  #
  # @return [Hash{String => Git::StatusFileInfo}] changed files keyed by path
  #
  def changed = files_by_path(&:changed?)

  # Returns the files added to the index that are not in HEAD
  #
  # @example List added paths
  #   repo.status_info.added.keys #=> ["lib/new.rb"]
  #
  # @return [Hash{String => Git::StatusFileInfo}] added files keyed by path
  #
  def added = files_by_path(&:added?)

  # Returns the files deleted from the index or working tree
  #
  # @example List deleted paths
  #   repo.status_info.deleted.keys #=> ["lib/old.rb"]
  #
  # @return [Hash{String => Git::StatusFileInfo}] deleted files keyed by path
  #
  def deleted = files_by_path(&:deleted?)

  # Returns the files in the working tree that git does not track
  #
  # @example List untracked paths
  #   repo.status_info.untracked.keys #=> ["notes.txt"]
  #
  # @return [Hash{String => Git::StatusFileInfo}] untracked files keyed by path
  #
  def untracked = files_by_path(&:untracked?)

  # Returns the files with merge conflicts
  #
  # @example List conflicted paths
  #   repo.status_info.unmerged.keys #=> ["lib/conflict.rb"]
  #
  # @return [Hash{String => Git::StatusFileInfo}] unmerged files keyed by path
  #
  def unmerged = files_by_path(&:unmerged?)

  # Returns `true` if `path` is modified in the index or working tree
  #
  # @example Check a path
  #   repo.status_info.changed?('lib/foo.rb') #=> true
  #
  # @param path [String] the repository-relative path to check
  #
  # @return [Boolean] `true` when the path is in {#changed}
  #
  def changed?(path) = path_in?(changed, path)

  # Returns `true` if `path` was added to the index
  #
  # @example Check a path
  #   repo.status_info.added?('lib/new.rb') #=> true
  #
  # @param path [String] the repository-relative path to check
  #
  # @return [Boolean] `true` when the path is in {#added}
  #
  def added?(path) = path_in?(added, path)

  # Returns `true` if `path` was deleted from the index or working tree
  #
  # @example Check a path
  #   repo.status_info.deleted?('lib/old.rb') #=> true
  #
  # @param path [String] the repository-relative path to check
  #
  # @return [Boolean] `true` when the path is in {#deleted}
  #
  def deleted?(path) = path_in?(deleted, path)

  # Returns `true` if `path` is not tracked by git
  #
  # @example Check a path
  #   repo.status_info.untracked?('notes.txt') #=> true
  #
  # @param path [String] the repository-relative path to check
  #
  # @return [Boolean] `true` when the path is in {#untracked}
  #
  def untracked?(path) = path_in?(untracked, path)

  # Returns the {Git::StatusFileInfo} for the given path
  #
  # The path is matched exactly, regardless of `ignore_case`.
  #
  # @example Look up a path
  #   repo.status_info['lib/foo.rb'] #=> #<data Git::StatusFileInfo path="lib/foo.rb", ...>
  #   repo.status_info['clean.rb']   #=> nil
  #
  # @param path [String] the repository-relative path
  #
  # @return [Git::StatusFileInfo, nil] the file, or `nil` when git did not report it
  #
  def [](path) = files.find { |file| file.path == path }

  private

  # Returns the files for which the block is truthy, keyed by path
  #
  # @return [Hash{String => Git::StatusFileInfo}] the selected files keyed by path
  #
  # @yield [file] each {Git::StatusFileInfo} in `files`
  #
  # @yieldparam file [Git::StatusFileInfo] one reported path
  #
  # @yieldreturn [Boolean] truthy to include the file
  #
  def files_by_path(&) = files.select(&).to_h { |file| [file.path, file] }

  # Returns `true` when `path` is a key of `collection`, honoring `ignore_case`
  #
  # @param collection [Hash{String => Git::StatusFileInfo}] files keyed by path
  #
  # @param path [String] the repository-relative path to look for
  #
  # @return [Boolean] `true` when the path is present
  #
  def path_in?(collection, path)
    return collection.key?(path) unless ignore_case

    collection.each_key.any? { |key| key.casecmp?(path) }
  end
end

Instance Method Details

#[](path) ⇒ Git::StatusFileInfo?

Returns the Git::StatusFileInfo for the given path

The path is matched exactly, regardless of ignore_case.

Examples:

Look up a path

repo.status_info['lib/foo.rb'] #=> #<data Git::StatusFileInfo path="lib/foo.rb", ...>
repo.status_info['clean.rb']   #=> nil

Parameters:

  • path (String)

    the repository-relative path

Returns:



159
# File 'lib/git/status_info.rb', line 159

def [](path) = files.find { |file| file.path == path }

#addedHash{String => Git::StatusFileInfo}

Returns the files added to the index that are not in HEAD

Examples:

List added paths

repo.status_info.added.keys #=> ["lib/new.rb"]

Returns:



74
# File 'lib/git/status_info.rb', line 74

def added = files_by_path(&:added?)

#added?(path) ⇒ Boolean

Returns true if path was added to the index

Examples:

Check a path

repo.status_info.added?('lib/new.rb') #=> true

Parameters:

  • path (String)

    the repository-relative path to check

Returns:

  • (Boolean)

    true when the path is in #added



123
# File 'lib/git/status_info.rb', line 123

def added?(path) = path_in?(added, path)

#changedHash{String => Git::StatusFileInfo}

Returns the files modified or type-changed in the index or working tree

Examples:

List modified paths

repo.status_info.changed.keys #=> ["lib/foo.rb"]

Returns:



65
# File 'lib/git/status_info.rb', line 65

def changed = files_by_path(&:changed?)

#changed?(path) ⇒ Boolean

Returns true if path is modified in the index or working tree

Examples:

Check a path

repo.status_info.changed?('lib/foo.rb') #=> true

Parameters:

  • path (String)

    the repository-relative path to check

Returns:

  • (Boolean)

    true when the path is in #changed



112
# File 'lib/git/status_info.rb', line 112

def changed?(path) = path_in?(changed, path)

#deletedHash{String => Git::StatusFileInfo}

Returns the files deleted from the index or working tree

Examples:

List deleted paths

repo.status_info.deleted.keys #=> ["lib/old.rb"]

Returns:



83
# File 'lib/git/status_info.rb', line 83

def deleted = files_by_path(&:deleted?)

#deleted?(path) ⇒ Boolean

Returns true if path was deleted from the index or working tree

Examples:

Check a path

repo.status_info.deleted?('lib/old.rb') #=> true

Parameters:

  • path (String)

    the repository-relative path to check

Returns:

  • (Boolean)

    true when the path is in #deleted



134
# File 'lib/git/status_info.rb', line 134

def deleted?(path) = path_in?(deleted, path)

#unmergedHash{String => Git::StatusFileInfo}

Returns the files with merge conflicts

Examples:

List conflicted paths

repo.status_info.unmerged.keys #=> ["lib/conflict.rb"]

Returns:



101
# File 'lib/git/status_info.rb', line 101

def unmerged = files_by_path(&:unmerged?)

#untrackedHash{String => Git::StatusFileInfo}

Returns the files in the working tree that git does not track

Examples:

List untracked paths

repo.status_info.untracked.keys #=> ["notes.txt"]

Returns:



92
# File 'lib/git/status_info.rb', line 92

def untracked = files_by_path(&:untracked?)

#untracked?(path) ⇒ Boolean

Returns true if path is not tracked by git

Examples:

Check a path

repo.status_info.untracked?('notes.txt') #=> true

Parameters:

  • path (String)

    the repository-relative path to check

Returns:

  • (Boolean)

    true when the path is in #untracked



145
# File 'lib/git/status_info.rb', line 145

def untracked?(path) = path_in?(untracked, path)