Skip to content

Commit 3e952c0

Browse files
committed
feat: add status report entry filters
Add filters to be able to get different categories of entries: * #ignored: entries that are ignored by git * #untracked: entries that are not tracked in the index * #unstaged: entries changed in the working directory * #staged: entries changed in the index * #fully_staged: entries changed in the index but not the working directory * #unmerged: entries that have a merge conflict that must be resolved Add #merge_conflict? to determine if thare are unmerged entries.
1 parent 301c7b7 commit 3e952c0

File tree

8 files changed

+311
-1
lines changed

8 files changed

+311
-1
lines changed

lib/ruby_git/status/entry.rb

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,78 @@ def index_status = nil
8585
# Get the worktree status
8686
#
8787
# @example
88-
# entry.worktree_status #=> :unchanged
88+
# entry.worktree_status #=> :unmodified
8989
#
9090
# @return [Symbol, nil] worktree status symbol or nil if not applicable
9191
#
9292
def worktree_status = nil
93+
94+
# Is the entry an ignored file?
95+
#
96+
# * Ignored entries are not considered untracked
97+
#
98+
# @example
99+
# entry.ignored? #=> false
100+
#
101+
# @return [Boolean]
102+
#
103+
def ignored? = false
104+
105+
# Is the entry an untracked file?
106+
#
107+
# * Ignored entries are not considered untracked
108+
#
109+
# @example
110+
# entry.ignored? #=> false
111+
#
112+
# @return [Boolean]
113+
#
114+
def untracked? = false
115+
116+
# Does the entry have unstaged changes in the worktree?
117+
#
118+
# * An entry can have both staged and unstaged changes
119+
# * All untracked entries are considered unstaged
120+
#
121+
# @example
122+
# entry.ignored? #=> false
123+
#
124+
# @return [Boolean]
125+
#
126+
def unstaged? = false
127+
128+
# Does the entry have staged changes in the index?
129+
#
130+
# * An entry can have both staged and unstaged changes
131+
#
132+
# @example
133+
# entry.ignored? #=> false
134+
#
135+
# @return [Boolean]
136+
#
137+
def staged? = false
138+
139+
# Does the entry have staged changes in the index with no unstaged changes?
140+
#
141+
# * An entry can have both staged and unstaged changes
142+
#
143+
# @example
144+
# entry.fully_staged? #=> false
145+
#
146+
# @return [Boolean]
147+
#
148+
def fully_staged? = staged? && !unstaged?
149+
150+
# Does the entry represent a merge conflict?
151+
#
152+
# * Merge conflicts are not considered untracked, staged or unstaged
153+
#
154+
# @example
155+
# entry.conflict? #=> false
156+
#
157+
# @return [Boolean]
158+
#
159+
def unmerged? = false
93160
end
94161
end
95162
end

lib/ruby_git/status/ignored_entry.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ def self.parse(line)
3131
def initialize(path:)
3232
super(path)
3333
end
34+
35+
# Is the entry an ignored file?
36+
# @example
37+
# entry.ignored? #=> false
38+
# @return [Boolean]
39+
def ignored?
40+
true
41+
end
3442
end
3543
end
3644
end

lib/ruby_git/status/ordinary_entry.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,29 @@ def initialize( # rubocop:disable Metrics/ParameterLists
179179
@head_sha = head_sha
180180
@index_sha = index_sha
181181
end
182+
183+
# Does the entry have unstaged changes in the worktree?
184+
#
185+
# * An entry can have both staged and unstaged changes
186+
# * All untracked entries are considered unstaged
187+
#
188+
# @example
189+
# entry.ignored? #=> false
190+
# @return [Boolean]
191+
def unstaged?
192+
worktree_status != :unmodified
193+
end
194+
195+
# Does the entry have staged changes in the index?
196+
#
197+
# * An entry can have both staged and unstaged changes
198+
#
199+
# @example
200+
# entry.ignored? #=> false
201+
# @return [Boolean]
202+
def staged?
203+
index_status != :unmodified
204+
end
182205
end
183206
end
184207
end

lib/ruby_git/status/renamed_entry.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,29 @@ def initialize( # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
229229
@similarity = similarity_score
230230
@original_path = original_path
231231
end
232+
233+
# Does the entry have unstaged changes in the worktree?
234+
#
235+
# * An entry can have both staged and unstaged changes
236+
# * All untracked entries are considered unstaged
237+
#
238+
# @example
239+
# entry.ignored? #=> false
240+
# @return [Boolean]
241+
def unstaged?
242+
worktree_status != :unmodified
243+
end
244+
245+
# Does the entry have staged changes in the index?
246+
#
247+
# * An entry can have both staged and unstaged changes
248+
#
249+
# @example
250+
# entry.ignored? #=> false
251+
# @return [Boolean]
252+
def staged?
253+
index_status != :unmodified
254+
end
232255
end
233256
end
234257
end

lib/ruby_git/status/report.rb

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,84 @@ def initialize(branch, stash, entries)
6060
@stash = stash
6161
@entries = entries
6262
end
63+
64+
# The entries that are ignored
65+
#
66+
# @example
67+
# report.ignored #=> [#<RubyGit::Status::IgnoredEntry ...>, ...]
68+
#
69+
# @return [Array<IgnoredEntry>]
70+
#
71+
def ignored
72+
entries.select(&:ignored?)
73+
end
74+
75+
# The entries that are untracked
76+
#
77+
# @example
78+
# report.untracked #=> [#<RubyGit::Status::UntrackedEntry ...>, ...]
79+
#
80+
# @return [Array<UntrackedEntry>]
81+
#
82+
def untracked
83+
entries.select(&:untracked?)
84+
end
85+
86+
# The entries that have unstaged changes
87+
#
88+
# @example
89+
# report.unstaged #=> [#<RubyGit::Status::OrdinaryEntry ...>, ...]
90+
#
91+
# @return [Array<UntrackedEntry, OrdinaryEntry, RenamedEntry>]
92+
#
93+
def unstaged
94+
entries.select(&:unstaged?)
95+
end
96+
97+
# The entries that have staged changes
98+
#
99+
# @example
100+
# report.staged #=> [#<RubyGit::Status::OrdinaryEntry ...>, ...]
101+
#
102+
# @return [Array<UntrackedEntry, OrdinaryEntry, RenamedEntry>]
103+
#
104+
def staged
105+
entries.select(&:staged?)
106+
end
107+
108+
# The entries that have staged changes and no unstaged changes
109+
#
110+
# @example
111+
# report.fully_staged #=> [#<RubyGit::Status::OrdinaryEntry ...>, ...]
112+
#
113+
# @return [Array<UntrackedEntry, OrdinaryEntry, RenamedEntry>]
114+
#
115+
def fully_staged
116+
entries.select(&:fully_staged?)
117+
end
118+
119+
# The entries that represent merge conflicts
120+
#
121+
# @example
122+
# report.unmerged #=> [#<RubyGit::Status::UnmergedEntry ...>, ...]
123+
# report.merge_conflicts? #=> true
124+
#
125+
# @return [Array<UnmergedEntry>]
126+
#
127+
def unmerged
128+
entries.select(&:unmerged?)
129+
end
130+
131+
# Are there any unmerged entries?
132+
#
133+
# @example
134+
# report.merge_conflicts? #=> true
135+
#
136+
# @return [Boolean]
137+
#
138+
def merge_conflict?
139+
unmerged.any?
140+
end
63141
end
64142
end
65143
end

lib/ruby_git/status/unmerged_entry.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,17 @@ def initialize( # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
232232
@their_sha = their_sha
233233
@path = path
234234
end
235+
236+
# Does the entry represent a merge conflict?
237+
#
238+
# * Merge conflicts are not considered untracked, staged or unstaged
239+
#
240+
# @example
241+
# entry.conflict? #=> false
242+
#
243+
# @return [Boolean]
244+
#
245+
def unmerged? = true
235246
end
236247
end
237248
end

lib/ruby_git/status/untracked_entry.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,22 @@ def self.parse(line)
3131
def initialize(path:)
3232
super(path)
3333
end
34+
35+
# Is the entry an untracked file?
36+
# @example
37+
# entry.ignored? #=> false
38+
# @return [Boolean]
39+
def untracked? = true
40+
41+
# Does the entry have unstaged changes in the worktree?
42+
#
43+
# * An entry can have both staged and unstaged changes
44+
# * All untracked entries are considered unstaged
45+
#
46+
# @example
47+
# entry.ignored? #=> false
48+
# @return [Boolean]
49+
def unstaged? = true
3450
end
3551
end
3652
end

spec/lib/ruby_git/status_spec.rb

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,5 +294,89 @@ def reproduce_data
294294
expect { subject }.not_to raise_error
295295
end
296296
end
297+
298+
describe 'filters' do
299+
let(:data) do
300+
'1 M. N... 000000 100644 100644 0000000000000000000000000000000000000000 ' \
301+
"49351eb5b7e355128f8f569d5b3355c3e2a51d4b file1.txt\u0000" \
302+
'1 MM N... 000000 100644 100644 0000000000000000000000000000000000000000 ' \
303+
"49351eb5b7e355128f8f569d5b3355c3e2a51d4b file2.txt\u0000" \
304+
'1 .M N... 000000 100644 100644 0000000000000000000000000000000000000000 ' \
305+
"49351eb5b7e355128f8f569d5b3355c3e2a51d4b file3.txt\u0000" \
306+
'2 RD N... 100644 100755 000000 1111111111111111111111111111111111111111 ' \
307+
"2222222222222222222222222222222222222222 R100 file4.txt\u0000" \
308+
"file4_old.txt\u0000" \
309+
'2 R. N... 100644 100755 000000 1111111111111111111111111111111111111111 ' \
310+
"2222222222222222222222222222222222222222 R100 file5.txt\u0000" \
311+
"file5_old.txt\u0000" \
312+
'u UU N... 100644 100755 000000 100755 ' \
313+
'1111111111111111111111111111111111111111 2222222222222222222222222222222222222222 ' \
314+
'3333333333333333333333333333333333333333 file6.txt' \
315+
"\u0000" \
316+
"? file7.txt\u0000" \
317+
"? file8.txt\u0000" \
318+
"! file9.txt\u0000"
319+
end
320+
321+
describe '#ignored' do
322+
subject { report.ignored }
323+
it 'should return the ignored entries' do
324+
expect(subject.map(&:path)).to eq(%w[file9.txt])
325+
end
326+
end
327+
328+
describe '#untracked' do
329+
subject { report.untracked }
330+
it 'should return the untracked entries' do
331+
expect(subject.map(&:path)).to eq(%w[file7.txt file8.txt])
332+
end
333+
end
334+
335+
describe '#unstaged' do
336+
subject { report.unstaged }
337+
it 'should return the unstaged entries' do
338+
expect(subject.map(&:path)).to eq(%w[file2.txt file3.txt file4.txt file7.txt file8.txt])
339+
end
340+
end
341+
342+
describe '#staged' do
343+
subject { report.staged }
344+
it 'should return the staged entries' do
345+
expect(subject.map(&:path)).to eq(%w[file1.txt file2.txt file4.txt file5.txt])
346+
end
347+
end
348+
349+
describe '#fully_staged' do
350+
subject { report.fully_staged }
351+
it 'should return the fully staged entries' do
352+
expect(subject.map(&:path)).to eq(%w[file1.txt file5.txt])
353+
end
354+
end
355+
356+
describe '#unmerged' do
357+
subject { report.unmerged }
358+
it 'should return the unmerged entries' do
359+
expect(subject.map(&:path)).to eq(%w[file6.txt])
360+
end
361+
end
362+
end
363+
364+
describe '#merge_conflict?' do
365+
subject { report.merge_conflict? }
366+
context 'when there is a merge conflict' do
367+
let(:data) do
368+
'u UU N... 100644 100755 000000 100755 ' \
369+
'1111111111111111111111111111111111111111 2222222222222222222222222222222222222222 ' \
370+
'3333333333333333333333333333333333333333 file6.txt'
371+
end
372+
373+
it { is_expected.to eq(true) }
374+
end
375+
376+
context 'when there is not a merge conflict' do
377+
let(:data) { '' }
378+
it { is_expected.to eq(false) }
379+
end
380+
end
297381
end
298382
end

0 commit comments

Comments
 (0)