Module: Commiti::GitReader

Defined in:
lib/services/git/git_reader.rb

Constant Summary collapse

MAX_DIFF_BYTES =
50_000
TRUNCATION_NOTICE =
"\n# ... diff clipped by Commiti to preserve context under size limit\n"
LOCKFILE_PATTERNS =
[
  /Gemfile\.lock/,
  /package-lock\.json/,
  /yarn\.lock/,
  /pnpm-lock\.yaml/,
  /composer\.lock/,
  /mix\.lock/,
  /Cargo\.lock/,
  /Pipfile\.lock/
].freeze

Class Method Summary collapse

Class Method Details

.append_notice(clipped_diff, max_bytes:) ⇒ Object



187
188
189
190
191
192
193
194
195
196
197
# File 'lib/services/git/git_reader.rb', line 187

def self.append_notice(clipped_diff, max_bytes:)
  safe_clipped = clipped_diff.to_s
  return safe_clipped if safe_clipped.bytesize >= max_bytes && max_bytes <= TRUNCATION_NOTICE.bytesize

  return safe_clipped + TRUNCATION_NOTICE if safe_clipped.bytesize + TRUNCATION_NOTICE.bytesize <= max_bytes

  available = max_bytes - TRUNCATION_NOTICE.bytesize
  return safe_clipped.byteslice(0, max_bytes) if available <= 0

  safe_clipped.byteslice(0, available) + TRUNCATION_NOTICE
end

.branch_diff(base_branch: 'main') ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
# File 'lib/services/git/git_reader.rb', line 21

def self.branch_diff(base_branch: 'main')
  raise 'Invalid branch name.' unless base_branch.match?(%r{\A[a-zA-Z0-9_\-./]+\z})

  # Strip context lines using -U0 and filter out binary/lockfile noise
  diff, status = Open3.capture2('git', 'diff', '-U0', "#{base_branch}...HEAD")
  raise "Failed to read branch diff against '#{base_branch}'." unless status.success?
  raise "No diff found against '#{base_branch}'." if diff.strip.empty?

  filtered_diff = filter_diff_noise(diff)
  clip_diff_context(filtered_diff, max_bytes: MAX_DIFF_BYTES)
end

.clip_chunks(chunks, max_bytes:) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/services/git/git_reader.rb', line 98

def self.clip_chunks(chunks, max_bytes:)
  output = +''

  chunks.each do |chunk|
    remaining = max_bytes - output.bytesize
    break if remaining <= 0

    chunk_text = chunk[:lines].join
    if chunk_text.bytesize <= remaining
      output << chunk_text
      next
    end

    output << clip_single_chunk(chunk[:lines], max_bytes: remaining)
    break
  end

  if output.empty?
    first_chunk_text = chunks.first[:lines].join
    return first_chunk_text.byteslice(0, max_bytes)
  end

  output
end

.clip_diff_context(diff, max_bytes:) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/services/git/git_reader.rb', line 38

def self.clip_diff_context(diff, max_bytes:)
  return diff if diff.bytesize <= max_bytes

  chunks = split_by_file(diff)
  clipped = if chunks.empty?
              diff.byteslice(0, max_bytes)
            else
              clip_chunks(chunks, max_bytes: max_bytes)
            end

  append_notice(clipped, max_bytes: max_bytes)
end

.clip_single_chunk(lines, max_bytes:) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
# File 'lib/services/git/git_reader.rb', line 123

def self.clip_single_chunk(lines, max_bytes:)
  output = +''
  return output if max_bytes <= 0

  header_lines, hunks = partition_chunk_lines(lines)
  append_lines_with_limit(output, header_lines, max_bytes: max_bytes)
  return output if hunks.empty?

  append_hunks_with_limit(output, hunks, max_bytes: max_bytes)
  output
end

.recent_commits(count: 10) ⇒ Object



33
34
35
36
# File 'lib/services/git/git_reader.rb', line 33

def self.recent_commits(count: 10)
  out, = Open3.capture2('git', 'log', '--oneline', "-#{count}")
  out
end

.split_by_file(diff) ⇒ Object

Returns [{ path: String, lines: Array<String> }]



94
95
96
# File 'lib/services/git/git_reader.rb', line 94

def self.split_by_file(diff)
  Commiti::DiffParser.split_by_file_lines(diff)
end

.staged_diffObject



11
12
13
14
15
16
17
18
19
# File 'lib/services/git/git_reader.rb', line 11

def self.staged_diff
  # Strip context lines using -U0 and filter out binary/lockfile noise
  diff, status = Open3.capture2('git', 'diff', '--cached', '-U0')
  raise 'Failed to read staged diff.' unless status.success?
  raise 'No staged changes. Run `git add` first.' if diff.strip.empty?

  filtered_diff = filter_diff_noise(diff)
  clip_diff_context(filtered_diff, max_bytes: MAX_DIFF_BYTES)
end