gitingest 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +25 -4
- data/bin/gitingest +19 -3
- data/lib/gitingest/generator.rb +83 -58
- data/lib/gitingest/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 114b6d8e954843f88917f556760e7fab89753126cb5fd059ed6cfe55c8f2e6c6
|
4
|
+
data.tar.gz: 39873c0d14eff9d6e615f6e2f13e7e30331f0e97db1162b613047782a582ff6c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f35ffb41ddd9990cd1c2e8c4ad08018a5bdd63c8ec222bfce021ede2c67dfa9355fbc7f1102f014c555d0df46501877b01f2767aadf674896635bd7fa5bab355
|
7
|
+
data.tar.gz: e431693ee45cf65ab3c63931c5c4a69675ddafc8038f90cda3dfb7dacec7449f48a65a41bc12f56996f8e697f80d1a896243e97aa37c96085107491099dabc05
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [0.4.0] - 2025-03-03
|
4
|
+
|
5
|
+
### Added
|
6
|
+
- Added `generate_prompt` method for in-memory content generation without file I/O
|
7
|
+
- Integrated visual progress bar with file processing rate reporting
|
8
|
+
- Added human-readable time formatting for progress estimates
|
9
|
+
- Enhanced test coverage for multithreaded operations
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
- Refactored `process_content_to_output` for better code reuse between file and string output
|
13
|
+
- Improved thread management to handle various error conditions more gracefully
|
14
|
+
- Enhanced documentation with programmatic usage examples
|
15
|
+
|
16
|
+
### Fixed
|
17
|
+
- Resolved thread pool shutdown issues in test environment
|
18
|
+
- Fixed race conditions in progress indicator updates
|
19
|
+
- Addressed timing inconsistencies in multithreaded test scenarios
|
20
|
+
|
21
|
+
---
|
22
|
+
|
3
23
|
## [0.3.1] - 2025-03-03
|
4
24
|
|
5
25
|
### Added
|
data/README.md
CHANGED
@@ -39,6 +39,15 @@ gitingest --repository user/repo --branch develop
|
|
39
39
|
# Exclude additional patterns
|
40
40
|
gitingest --repository user/repo --exclude "*.md,docs/"
|
41
41
|
|
42
|
+
# Control the number of threads
|
43
|
+
gitingest --repository user/repo -T 4
|
44
|
+
|
45
|
+
# Set thread pool shutdown timeout
|
46
|
+
gitingest --repository user/repo -W 120
|
47
|
+
|
48
|
+
# Combine threading options
|
49
|
+
gitingest --repository user/repo -T 8 -W 90
|
50
|
+
|
42
51
|
# Quiet mode
|
43
52
|
gitingest --repository user/repo --quiet
|
44
53
|
|
@@ -53,6 +62,10 @@ gitingest --repository user/repo --verbose
|
|
53
62
|
- `-o, --output FILE`: Output file for the prompt [Default: reponame_prompt.txt]
|
54
63
|
- `-e, --exclude PATTERN`: File patterns to exclude (comma separated)
|
55
64
|
- `-b, --branch BRANCH`: Repository branch [Default: main]
|
65
|
+
- `-T, --threads COUNT`: Number of concurrent threads [Default: auto-detected]
|
66
|
+
- `-W, --thread-timeout SECONDS`: Thread pool shutdown timeout [Default: 60]
|
67
|
+
- `-q, --quiet`: Reduce logging to errors only
|
68
|
+
- `-v, --verbose`: Increase logging verbosity
|
56
69
|
- `-h, --help`: Show help message
|
57
70
|
|
58
71
|
### As a Library
|
@@ -60,13 +73,21 @@ gitingest --repository user/repo --verbose
|
|
60
73
|
```ruby
|
61
74
|
require "gitingest"
|
62
75
|
|
63
|
-
# Basic usage
|
76
|
+
# Basic usage - write to a file
|
64
77
|
generator = Gitingest::Generator.new(
|
65
78
|
repository: "user/repo",
|
66
79
|
token: "YOUR_GITHUB_TOKEN" # optional
|
67
80
|
)
|
81
|
+
|
82
|
+
# Run the full workflow (fetch repository and generate file)
|
68
83
|
generator.run
|
69
84
|
|
85
|
+
# OR generate file only (if you need the output path)
|
86
|
+
output_path = generator.generate_file
|
87
|
+
|
88
|
+
# Get content as a string (for in-memory processing)
|
89
|
+
content = generator.generate_prompt
|
90
|
+
|
70
91
|
# With custom options
|
71
92
|
generator = Gitingest::Generator.new(
|
72
93
|
repository: "user/repo",
|
@@ -74,9 +95,10 @@ generator = Gitingest::Generator.new(
|
|
74
95
|
output_file: "my_prompt.txt",
|
75
96
|
branch: "develop",
|
76
97
|
exclude: ["*.md", "docs/"],
|
77
|
-
|
98
|
+
threads: 4, # control concurrency
|
99
|
+
thread_timeout: 120, # custom thread timeout
|
100
|
+
quiet: true # or verbose: true
|
78
101
|
)
|
79
|
-
generator.run
|
80
102
|
|
81
103
|
# With custom logger
|
82
104
|
custom_logger = Logger.new("gitingest.log")
|
@@ -84,7 +106,6 @@ generator = Gitingest::Generator.new(
|
|
84
106
|
repository: "user/repo",
|
85
107
|
logger: custom_logger
|
86
108
|
)
|
87
|
-
generator.run
|
88
109
|
```
|
89
110
|
|
90
111
|
## Features
|
data/bin/gitingest
CHANGED
@@ -22,7 +22,7 @@ parser = OptionParser.new do |opts|
|
|
22
22
|
options[:output_file] = file
|
23
23
|
end
|
24
24
|
|
25
|
-
opts.on("-b", "--branch BRANCH", "Repository branch") do |branch|
|
25
|
+
opts.on("-b", "--branch BRANCH", "Repository branch [Default: main]") do |branch|
|
26
26
|
options[:branch] = branch
|
27
27
|
end
|
28
28
|
|
@@ -30,11 +30,20 @@ parser = OptionParser.new do |opts|
|
|
30
30
|
options[:exclude] = pattern.split(",")
|
31
31
|
end
|
32
32
|
|
33
|
-
opts.on("-
|
33
|
+
opts.on("-T", "--threads COUNT", Integer, "Number of concurrent threads [Default: auto-detected]") do |threads|
|
34
|
+
options[:threads] = threads
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on("-W", "--thread-timeout SECONDS", Integer,
|
38
|
+
"Thread pool shutdown timeout in seconds [Default: 60]") do |timeout|
|
39
|
+
options[:thread_timeout] = timeout
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on("-q", "--quiet", "Reduce logging to errors only") do
|
34
43
|
options[:quiet] = true
|
35
44
|
end
|
36
45
|
|
37
|
-
opts.on("-v", "--verbose", "
|
46
|
+
opts.on("-v", "--verbose", "Increase logging verbosity") do
|
38
47
|
options[:verbose] = true
|
39
48
|
end
|
40
49
|
|
@@ -42,6 +51,13 @@ parser = OptionParser.new do |opts|
|
|
42
51
|
puts opts
|
43
52
|
exit
|
44
53
|
end
|
54
|
+
|
55
|
+
opts.separator ""
|
56
|
+
opts.separator "Examples:"
|
57
|
+
opts.separator " gitingest -r user/repo # Basic usage with public repository"
|
58
|
+
opts.separator " gitingest -r user/repo -t YOUR_TOKEN # With GitHub token for private repositories"
|
59
|
+
opts.separator " gitingest -r user/repo -o custom_prompt.txt # Custom output file"
|
60
|
+
opts.separator " gitingest -r user/repo -T 8 # Use 8 threads for processing"
|
45
61
|
end
|
46
62
|
|
47
63
|
begin
|
data/lib/gitingest/generator.rb
CHANGED
@@ -106,10 +106,42 @@ module Gitingest
|
|
106
106
|
compile_excluded_patterns
|
107
107
|
end
|
108
108
|
|
109
|
-
# Main execution method
|
109
|
+
# Main execution method for command line
|
110
110
|
def run
|
111
111
|
fetch_repository_contents
|
112
|
-
|
112
|
+
generate_file
|
113
|
+
end
|
114
|
+
|
115
|
+
# Generate content and save it to a file
|
116
|
+
#
|
117
|
+
# @return [String] Path to the generated file
|
118
|
+
def generate_file
|
119
|
+
fetch_repository_contents if @repo_files.empty?
|
120
|
+
|
121
|
+
@logger.info "Generating file for #{@options[:repository]}"
|
122
|
+
File.open(@options[:output_file], "w") do |file|
|
123
|
+
process_content_to_output(file)
|
124
|
+
end
|
125
|
+
|
126
|
+
@logger.info "Prompt generated and saved to #{@options[:output_file]}"
|
127
|
+
@options[:output_file]
|
128
|
+
end
|
129
|
+
|
130
|
+
# Generate content and return it as a string
|
131
|
+
# Useful for programmatic usage
|
132
|
+
#
|
133
|
+
# @return [String] The generated repository content
|
134
|
+
def generate_prompt
|
135
|
+
@logger.info "Generating in-memory prompt for #{@options[:repository]}"
|
136
|
+
|
137
|
+
fetch_repository_contents if @repo_files.empty?
|
138
|
+
|
139
|
+
content = StringIO.new
|
140
|
+
process_content_to_output(content)
|
141
|
+
|
142
|
+
result = content.string
|
143
|
+
@logger.info "Generated #{result.size} bytes of content in memory"
|
144
|
+
result
|
113
145
|
end
|
114
146
|
|
115
147
|
private
|
@@ -202,89 +234,82 @@ module Gitingest
|
|
202
234
|
path.match?(@combined_exclude_regex)
|
203
235
|
end
|
204
236
|
|
205
|
-
#
|
206
|
-
def
|
207
|
-
@logger.info "Generating prompt..."
|
237
|
+
# Common implementation for both file and string output
|
238
|
+
def process_content_to_output(output)
|
208
239
|
@logger.debug "Using thread pool with #{@options[:threads]} threads"
|
209
240
|
|
210
241
|
buffer = []
|
211
242
|
progress = ProgressIndicator.new(@repo_files.size, @logger)
|
212
243
|
|
213
|
-
#
|
244
|
+
# Thread-local buffers to reduce mutex contention
|
214
245
|
thread_buffers = {}
|
215
246
|
mutex = Mutex.new
|
216
247
|
errors = []
|
217
248
|
|
218
|
-
#
|
249
|
+
# Thread pool based on configuration
|
219
250
|
pool = Concurrent::FixedThreadPool.new(@options[:threads])
|
220
251
|
|
221
|
-
# Group files by priority
|
252
|
+
# Group files by priority
|
222
253
|
prioritized_files = prioritize_files(@repo_files)
|
223
254
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
thread_buffers[thread_id] ||= []
|
230
|
-
local_buffer = thread_buffers[thread_id]
|
231
|
-
|
232
|
-
begin
|
233
|
-
content = fetch_file_content_with_retry(repo_file.path)
|
234
|
-
result = format_file_content(repo_file.path, content)
|
235
|
-
local_buffer << result
|
236
|
-
|
237
|
-
# Optimization: Only acquire mutex when local buffer reaches threshold
|
238
|
-
if local_buffer.size >= LOCAL_BUFFER_THRESHOLD
|
239
|
-
mutex.synchronize do
|
240
|
-
buffer.concat(local_buffer)
|
241
|
-
write_buffer(file, buffer) if buffer.size >= BUFFER_SIZE
|
242
|
-
local_buffer.clear
|
243
|
-
end
|
244
|
-
end
|
255
|
+
prioritized_files.each_with_index do |repo_file, index|
|
256
|
+
pool.post do
|
257
|
+
thread_id = Thread.current.object_id
|
258
|
+
thread_buffers[thread_id] ||= []
|
259
|
+
local_buffer = thread_buffers[thread_id]
|
245
260
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
rescue StandardError => e
|
261
|
+
begin
|
262
|
+
content = fetch_file_content_with_retry(repo_file.path)
|
263
|
+
result = format_file_content(repo_file.path, content)
|
264
|
+
local_buffer << result
|
265
|
+
|
266
|
+
if local_buffer.size >= LOCAL_BUFFER_THRESHOLD
|
253
267
|
mutex.synchronize do
|
254
|
-
|
255
|
-
|
268
|
+
buffer.concat(local_buffer)
|
269
|
+
write_buffer(output, buffer) if buffer.size >= BUFFER_SIZE
|
270
|
+
local_buffer.clear
|
256
271
|
end
|
257
272
|
end
|
273
|
+
|
274
|
+
progress.update(index + 1)
|
275
|
+
rescue Octokit::Error => e
|
276
|
+
mutex.synchronize do
|
277
|
+
errors << "Error fetching #{repo_file.path}: #{e.message}"
|
278
|
+
@logger.error "Error fetching #{repo_file.path}: #{e.message}"
|
279
|
+
end
|
280
|
+
rescue StandardError => e
|
281
|
+
mutex.synchronize do
|
282
|
+
errors << "Unexpected error processing #{repo_file.path}: #{e.message}"
|
283
|
+
@logger.error "Unexpected error processing #{repo_file.path}: #{e.message}"
|
284
|
+
end
|
258
285
|
end
|
259
286
|
end
|
287
|
+
end
|
260
288
|
|
261
|
-
|
262
|
-
|
263
|
-
|
289
|
+
begin
|
290
|
+
pool.shutdown
|
291
|
+
wait_success = pool.wait_for_termination(@options[:thread_timeout])
|
264
292
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
end
|
269
|
-
rescue StandardError => e
|
270
|
-
@logger.error "Error during thread pool shutdown: #{e.message}"
|
293
|
+
unless wait_success
|
294
|
+
@logger.warn "Thread pool did not shut down within #{@options[:thread_timeout]} seconds, forcing termination"
|
295
|
+
pool.kill
|
271
296
|
end
|
297
|
+
rescue StandardError => e
|
298
|
+
@logger.error "Error during thread pool shutdown: #{e.message}"
|
299
|
+
end
|
272
300
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
end
|
278
|
-
write_buffer(file, buffer) unless buffer.empty?
|
301
|
+
# Process remaining files in thread-local buffers
|
302
|
+
mutex.synchronize do
|
303
|
+
thread_buffers.each_value do |local_buffer|
|
304
|
+
buffer.concat(local_buffer) unless local_buffer.empty?
|
279
305
|
end
|
306
|
+
write_buffer(output, buffer) unless buffer.empty?
|
280
307
|
end
|
281
308
|
|
282
|
-
|
283
|
-
@logger.warn "Completed with #{errors.size} errors"
|
284
|
-
@logger.debug "First few errors: #{errors.first(3).join(", ")}" if @logger.debug?
|
285
|
-
end
|
309
|
+
return unless errors.any?
|
286
310
|
|
287
|
-
@logger.
|
311
|
+
@logger.warn "Completed with #{errors.size} errors"
|
312
|
+
@logger.debug "First few errors: #{errors.first(3).join(", ")}" if @logger.debug?
|
288
313
|
end
|
289
314
|
|
290
315
|
# Format a file's content for the prompt
|
data/lib/gitingest/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitingest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Davide Santangelo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-03-
|
11
|
+
date: 2025-03-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|