git 4.0.0 → 4.0.1
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/.gitignore +1 -0
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +51 -0
- data/.rubocop_todo.yml +12 -0
- data/CHANGELOG.md +44 -0
- data/Rakefile +13 -1
- data/git.gemspec +35 -30
- data/lib/git/args_builder.rb +103 -0
- data/lib/git/author.rb +6 -5
- data/lib/git/base.rb +254 -165
- data/lib/git/branch.rb +14 -15
- data/lib/git/branches.rb +9 -13
- data/lib/git/command_line.rb +97 -55
- data/lib/git/config.rb +4 -6
- data/lib/git/diff.rb +75 -39
- data/lib/git/diff_path_status.rb +4 -3
- data/lib/git/diff_stats.rb +1 -1
- data/lib/git/errors.rb +8 -2
- data/lib/git/escaped_path.rb +1 -1
- data/lib/git/lib.rb +865 -557
- data/lib/git/log.rb +74 -210
- data/lib/git/object.rb +85 -66
- data/lib/git/path.rb +18 -8
- data/lib/git/remote.rb +3 -4
- data/lib/git/repository.rb +0 -2
- data/lib/git/stash.rb +13 -6
- data/lib/git/stashes.rb +5 -5
- data/lib/git/status.rb +108 -247
- data/lib/git/url.rb +3 -3
- data/lib/git/version.rb +1 -1
- data/lib/git/worktree.rb +4 -5
- data/lib/git/worktrees.rb +4 -6
- data/lib/git.rb +16 -15
- metadata +35 -3
data/lib/git/branch.rb
CHANGED
@@ -3,7 +3,8 @@
|
|
3
3
|
require 'git/path'
|
4
4
|
|
5
5
|
module Git
|
6
|
-
|
6
|
+
# Represents a Git branch
|
7
|
+
class Branch
|
7
8
|
attr_accessor :full, :remote, :name
|
8
9
|
|
9
10
|
def initialize(base, name)
|
@@ -56,12 +57,12 @@ module Git
|
|
56
57
|
@base.lib.branch_delete(@name)
|
57
58
|
end
|
58
59
|
|
59
|
-
def current
|
60
|
-
|
60
|
+
def current # rubocop:disable Naming/PredicateMethod
|
61
|
+
@base.lib.branch_current == @name
|
61
62
|
end
|
62
63
|
|
63
64
|
def contains?(commit)
|
64
|
-
!@base.lib.branch_contains(commit,
|
65
|
+
!@base.lib.branch_contains(commit, name).empty?
|
65
66
|
end
|
66
67
|
|
67
68
|
def merge(branch = nil, message = nil)
|
@@ -93,16 +94,6 @@ module Git
|
|
93
94
|
@full
|
94
95
|
end
|
95
96
|
|
96
|
-
private
|
97
|
-
|
98
|
-
def check_if_create
|
99
|
-
@base.lib.branch_new(@name) rescue nil
|
100
|
-
end
|
101
|
-
|
102
|
-
def determine_current
|
103
|
-
@base.lib.branch_current == @name
|
104
|
-
end
|
105
|
-
|
106
97
|
BRANCH_NAME_REGEXP = %r{
|
107
98
|
^
|
108
99
|
# Optional 'refs/remotes/' at the beggining to specify a remote tracking branch
|
@@ -114,6 +105,8 @@ module Git
|
|
114
105
|
$
|
115
106
|
}x
|
116
107
|
|
108
|
+
private
|
109
|
+
|
117
110
|
# Given a full branch name return an Array containing the remote and branch names.
|
118
111
|
#
|
119
112
|
# Removes 'remotes' from the beggining of the name (if present).
|
@@ -139,7 +132,13 @@ module Git
|
|
139
132
|
match = name.match(BRANCH_NAME_REGEXP)
|
140
133
|
remote = match[:remote_name] ? Git::Remote.new(@base, match[:remote_name]) : nil
|
141
134
|
branch_name = match[:branch_name]
|
142
|
-
[
|
135
|
+
[remote, branch_name]
|
136
|
+
end
|
137
|
+
|
138
|
+
def check_if_create
|
139
|
+
@base.lib.branch_new(@name)
|
140
|
+
rescue StandardError
|
141
|
+
nil
|
143
142
|
end
|
144
143
|
end
|
145
144
|
end
|
data/lib/git/branches.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Git
|
4
|
-
|
5
4
|
# object that holds all the available branches
|
6
5
|
class Branches
|
7
|
-
|
8
6
|
include Enumerable
|
9
7
|
|
10
8
|
def initialize(base)
|
@@ -18,11 +16,11 @@ module Git
|
|
18
16
|
end
|
19
17
|
|
20
18
|
def local
|
21
|
-
|
19
|
+
reject(&:remote)
|
22
20
|
end
|
23
21
|
|
24
22
|
def remote
|
25
|
-
self.select
|
23
|
+
self.select(&:remote)
|
26
24
|
end
|
27
25
|
|
28
26
|
# array like methods
|
@@ -31,8 +29,8 @@ module Git
|
|
31
29
|
@branches.size
|
32
30
|
end
|
33
31
|
|
34
|
-
def each(&
|
35
|
-
@branches.values.each(&
|
32
|
+
def each(&)
|
33
|
+
@branches.values.each(&)
|
36
34
|
end
|
37
35
|
|
38
36
|
# Returns the target branch
|
@@ -49,24 +47,22 @@ module Git
|
|
49
47
|
# @param [#to_s] branch_name the target branch name.
|
50
48
|
# @return [Git::Branch] the target branch.
|
51
49
|
def [](branch_name)
|
52
|
-
@branches.values.
|
50
|
+
@branches.values.each_with_object(@branches) do |branch, branches|
|
53
51
|
branches[branch.full] ||= branch
|
54
52
|
|
55
53
|
# This is how Git (version 1.7.9.5) works.
|
56
|
-
# Lets you ignore the 'remotes' if its at the beginning of the branch full
|
57
|
-
|
58
|
-
|
59
|
-
branches
|
54
|
+
# Lets you ignore the 'remotes' if its at the beginning of the branch full
|
55
|
+
# name (even if is not a real remote branch).
|
56
|
+
branches[branch.full.sub('remotes/', '')] ||= branch if branch.full =~ %r{^remotes/.+}
|
60
57
|
end[branch_name.to_s]
|
61
58
|
end
|
62
59
|
|
63
60
|
def to_s
|
64
61
|
out = ''
|
65
|
-
@branches.
|
62
|
+
@branches.each_value do |b|
|
66
63
|
out << (b.current ? '* ' : ' ') << b.to_s << "\n"
|
67
64
|
end
|
68
65
|
out
|
69
66
|
end
|
70
67
|
end
|
71
|
-
|
72
68
|
end
|
data/lib/git/command_line.rb
CHANGED
@@ -97,6 +97,10 @@ module Git
|
|
97
97
|
|
98
98
|
# Execute a git command, wait for it to finish, and return the result
|
99
99
|
#
|
100
|
+
# Non-option the command line arguements to pass to git. If you collect
|
101
|
+
# the command line arguments in an array, make sure you splat the array
|
102
|
+
# into the parameter list.
|
103
|
+
#
|
100
104
|
# NORMALIZATION
|
101
105
|
#
|
102
106
|
# The command output is returned as a Unicde string containing the binary output
|
@@ -142,11 +146,9 @@ module Git
|
|
142
146
|
# stderr.string #=> "unknown revision or path not in the working tree.\n"
|
143
147
|
# end
|
144
148
|
#
|
145
|
-
# @param
|
146
|
-
#
|
147
|
-
# This array should be splatted into the parameter list.
|
149
|
+
# @param options_hash [Hash] the options to pass to the command
|
148
150
|
#
|
149
|
-
# @
|
151
|
+
# @option options_hash [#write, nil] :out the object to write stdout to or nil to ignore stdout
|
150
152
|
#
|
151
153
|
# If this is a 'StringIO' object, then `stdout_writer.string` will be returned.
|
152
154
|
#
|
@@ -154,20 +156,20 @@ module Git
|
|
154
156
|
# stdout to a file or some other object that responds to `#write`. The default
|
155
157
|
# behavior will return the output of the command.
|
156
158
|
#
|
157
|
-
# @
|
159
|
+
# @option options_hash [#write, nil] :err the object to write stderr to or nil to ignore stderr
|
158
160
|
#
|
159
161
|
# If this is a 'StringIO' object and `merged_output` is `true`, then
|
160
162
|
# `stderr_writer.string` will be merged into the output returned by this method.
|
161
163
|
#
|
162
|
-
# @
|
164
|
+
# @option options_hash [Boolean] :normalize whether to normalize the output of stdout and stderr
|
163
165
|
#
|
164
|
-
# @
|
166
|
+
# @option options_hash [Boolean] :chomp whether to chomp both stdout and stderr output
|
165
167
|
#
|
166
|
-
# @
|
168
|
+
# @option options_hash [Boolean] :merge whether to merge stdout and stderr in the string returned
|
167
169
|
#
|
168
|
-
# @
|
170
|
+
# @option options_hash [String, nil] :chdir the directory to run the command in
|
169
171
|
#
|
170
|
-
# @
|
172
|
+
# @option options_hash [Numeric, nil] :timeout the maximum seconds to wait for the command to complete
|
171
173
|
#
|
172
174
|
# If timeout is zero, the timeout will not be enforced.
|
173
175
|
#
|
@@ -189,21 +191,50 @@ module Git
|
|
189
191
|
#
|
190
192
|
# @raise [Git::TimeoutError] if the command times out
|
191
193
|
#
|
192
|
-
def run(
|
194
|
+
def run(*, **options_hash)
|
195
|
+
options_hash = RUN_ARGS.merge(options_hash)
|
196
|
+
extra_options = options_hash.keys - RUN_ARGS.keys
|
197
|
+
raise ArgumentError, "Unknown options: #{extra_options.join(', ')}" if extra_options.any?
|
198
|
+
|
199
|
+
result = run_with_capture(*, **options_hash)
|
200
|
+
process_result(result, options_hash[:normalize], options_hash[:chomp], options_hash[:timeout])
|
201
|
+
end
|
202
|
+
|
203
|
+
# @return [Git::CommandLineResult] the result of running the command
|
204
|
+
#
|
205
|
+
# @api private
|
206
|
+
#
|
207
|
+
def run_with_capture(*args, **options_hash)
|
193
208
|
git_cmd = build_git_cmd(args)
|
194
|
-
|
195
|
-
|
209
|
+
options = run_with_capture_options(**options_hash)
|
210
|
+
ProcessExecuter.run_with_capture(env, *git_cmd, **options)
|
211
|
+
rescue ProcessExecuter::ProcessIOError => e
|
212
|
+
raise Git::ProcessIOError.new(e.message), cause: e.exception.cause
|
213
|
+
end
|
214
|
+
|
215
|
+
def run_with_capture_options(**options_hash)
|
216
|
+
chdir = options_hash[:chdir] || :not_set
|
217
|
+
timeout_after = options_hash[:timeout]
|
218
|
+
out = options_hash[:out]
|
219
|
+
err = options_hash[:err]
|
220
|
+
merge_output = options_hash[:merge] || false
|
221
|
+
|
222
|
+
{ chdir:, timeout_after:, merge_output:, raise_errors: false }.tap do |options|
|
196
223
|
options[:out] = out unless out.nil?
|
197
224
|
options[:err] = err unless err.nil?
|
198
|
-
options[:merge_output] = merge unless merge.nil?
|
199
|
-
|
200
|
-
result = ProcessExecuter.run_with_capture(env, *git_cmd, **options)
|
201
|
-
rescue ProcessExecuter::ProcessIOError => e
|
202
|
-
raise Git::ProcessIOError.new(e.message), cause: e.exception.cause
|
203
225
|
end
|
204
|
-
process_result(result, normalize, chomp, timeout)
|
205
226
|
end
|
206
227
|
|
228
|
+
RUN_ARGS = {
|
229
|
+
normalize: false,
|
230
|
+
chomp: false,
|
231
|
+
merge: false,
|
232
|
+
out: nil,
|
233
|
+
err: nil,
|
234
|
+
chdir: nil,
|
235
|
+
timeout: nil
|
236
|
+
}.freeze
|
237
|
+
|
207
238
|
private
|
208
239
|
|
209
240
|
# Build the git command line from the available sources to send to `Process.spawn`
|
@@ -211,9 +242,9 @@ module Git
|
|
211
242
|
# @api private
|
212
243
|
#
|
213
244
|
def build_git_cmd(args)
|
214
|
-
raise ArgumentError
|
245
|
+
raise ArgumentError, 'The args array can not contain an array' if args.any? { |a| a.is_a?(Array) }
|
215
246
|
|
216
|
-
[binary_path, *global_opts, *args].map
|
247
|
+
[binary_path, *global_opts, *args].map(&:to_s)
|
217
248
|
end
|
218
249
|
|
219
250
|
# Process the result of the command and return a Git::CommandLineResult
|
@@ -221,68 +252,79 @@ module Git
|
|
221
252
|
# Post process output, log the command and result, and raise an error if the
|
222
253
|
# command failed.
|
223
254
|
#
|
224
|
-
# @param result [ProcessExecuter::Command::Result] the result it is a
|
255
|
+
# @param result [ProcessExecuter::Command::Result] the result it is a
|
256
|
+
# Process::Status and include command, stdout, and stderr
|
257
|
+
#
|
225
258
|
# @param normalize [Boolean] whether to normalize the output of each writer
|
259
|
+
#
|
226
260
|
# @param chomp [Boolean] whether to chomp the output of each writer
|
227
|
-
# @param timeout [Numeric, nil] the maximum seconds to wait for the command to complete
|
228
261
|
#
|
229
|
-
# @
|
262
|
+
# @param timeout [Numeric, nil] the maximum seconds to wait for the command to
|
263
|
+
# complete
|
264
|
+
#
|
265
|
+
# @return [Git::CommandLineResult] the result of the command to return to the
|
266
|
+
# caller
|
230
267
|
#
|
231
268
|
# @raise [Git::FailedError] if the command failed
|
269
|
+
#
|
232
270
|
# @raise [Git::SignaledError] if the command was signaled
|
271
|
+
#
|
233
272
|
# @raise [Git::TimeoutError] if the command times out
|
234
|
-
#
|
273
|
+
#
|
274
|
+
# @raise [Git::ProcessIOError] if an exception was raised while collecting
|
275
|
+
# subprocess output
|
235
276
|
#
|
236
277
|
# @api private
|
237
278
|
#
|
238
279
|
def process_result(result, normalize, chomp, timeout)
|
239
280
|
command = result.command
|
240
|
-
processed_out, processed_err =
|
281
|
+
processed_out, processed_err = post_process_output(result, normalize, chomp)
|
282
|
+
log_result(result, command, processed_out, processed_err)
|
283
|
+
command_line_result(command, result, processed_out, processed_err, timeout)
|
284
|
+
end
|
285
|
+
|
286
|
+
def log_result(result, command, processed_out, processed_err)
|
241
287
|
logger.info { "#{command} exited with status #{result}" }
|
242
288
|
logger.debug { "stdout:\n#{processed_out.inspect}\nstderr:\n#{processed_err.inspect}" }
|
289
|
+
end
|
290
|
+
|
291
|
+
def command_line_result(command, result, processed_out, processed_err, timeout)
|
243
292
|
Git::CommandLineResult.new(command, result, processed_out, processed_err).tap do |processed_result|
|
244
293
|
raise Git::TimeoutError.new(processed_result, timeout) if result.timeout?
|
245
|
-
|
246
|
-
raise Git::
|
294
|
+
|
295
|
+
raise Git::SignaledError, processed_result if result.signaled?
|
296
|
+
|
297
|
+
raise Git::FailedError, processed_result unless result.success?
|
247
298
|
end
|
248
299
|
end
|
249
300
|
|
250
|
-
# Post-process
|
251
|
-
#
|
252
|
-
# @param raw_outputs [Array] the output to post-process
|
253
|
-
# @param normalize [Boolean] whether to normalize the output of each writer
|
254
|
-
# @param chomp [Boolean] whether to chomp the output of each writer
|
301
|
+
# Post-process and return an array of raw output strings
|
255
302
|
#
|
256
|
-
#
|
303
|
+
# For each raw output string:
|
257
304
|
#
|
258
|
-
#
|
305
|
+
# * If normalize: is true, normalize the encoding by transcoding each line from
|
306
|
+
# the detected encoding to UTF-8.
|
307
|
+
# * If chomp: is true chomp the output after normalization.
|
259
308
|
#
|
260
|
-
|
261
|
-
|
262
|
-
raw_outputs.each { |raw_output| result << post_process(raw_output, normalize, chomp) }
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
# Determine the output to return in the `CommandLineResult`
|
309
|
+
# Even if no post-processing is done based on the options, the strings returned
|
310
|
+
# are a copy of the raw output strings. The raw output strings are not modified.
|
267
311
|
#
|
268
|
-
#
|
269
|
-
# then return the result of normalizing the encoding and chomping the output
|
270
|
-
# as requested.
|
312
|
+
# @param result [ProcessExecuter::ResultWithCapture] the command's output to post-process
|
271
313
|
#
|
272
|
-
#
|
273
|
-
#
|
274
|
-
# is a file instead of a StringIO.
|
314
|
+
# @param normalize [Boolean] whether to normalize the output of each writer
|
315
|
+
# @param chomp [Boolean] whether to chomp the output of each writer
|
275
316
|
#
|
276
|
-
# @
|
277
|
-
# @return [String, nil]
|
317
|
+
# @return [Array<String>]
|
278
318
|
#
|
279
319
|
# @api private
|
280
320
|
#
|
281
|
-
def
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
321
|
+
def post_process_output(result, normalize, chomp)
|
322
|
+
[result.stdout, result.stderr].map do |raw_output|
|
323
|
+
output = raw_output.dup
|
324
|
+
output = output.lines.map { |l| Git::EncodingUtils.normalize_encoding(l) }.join if normalize
|
325
|
+
output.chomp! if chomp
|
326
|
+
output
|
327
|
+
end
|
286
328
|
end
|
287
329
|
end
|
288
330
|
end
|
data/lib/git/config.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Git
|
4
|
-
|
4
|
+
# The global configuration for this gem
|
5
5
|
class Config
|
6
|
-
|
7
6
|
attr_writer :binary_path, :git_ssh, :timeout
|
8
7
|
|
9
8
|
def initialize
|
@@ -13,16 +12,15 @@ module Git
|
|
13
12
|
end
|
14
13
|
|
15
14
|
def binary_path
|
16
|
-
@binary_path || ENV
|
15
|
+
@binary_path || (ENV.fetch('GIT_PATH', nil) && File.join(ENV.fetch('GIT_PATH', nil), 'git')) || 'git'
|
17
16
|
end
|
18
17
|
|
19
18
|
def git_ssh
|
20
|
-
@git_ssh || ENV
|
19
|
+
@git_ssh || ENV.fetch('GIT_SSH', nil)
|
21
20
|
end
|
22
21
|
|
23
22
|
def timeout
|
24
|
-
@timeout || (ENV
|
23
|
+
@timeout || (ENV.fetch('GIT_TIMEOUT', nil) && ENV['GIT_TIMEOUT'].to_i)
|
25
24
|
end
|
26
25
|
end
|
27
|
-
|
28
26
|
end
|
data/lib/git/diff.rb
CHANGED
@@ -10,8 +10,8 @@ module Git
|
|
10
10
|
|
11
11
|
def initialize(base, from = nil, to = nil)
|
12
12
|
@base = base
|
13
|
-
@from = from
|
14
|
-
@to = to
|
13
|
+
@from = from&.to_s
|
14
|
+
@to = to&.to_s
|
15
15
|
|
16
16
|
@path = nil
|
17
17
|
@full_diff_files = nil
|
@@ -26,16 +26,16 @@ module Git
|
|
26
26
|
def patch
|
27
27
|
@base.lib.diff_full(@from, @to, { path_limiter: @path })
|
28
28
|
end
|
29
|
-
|
29
|
+
alias to_s patch
|
30
30
|
|
31
31
|
def [](key)
|
32
32
|
process_full
|
33
33
|
@full_diff_files.assoc(key)[1]
|
34
34
|
end
|
35
35
|
|
36
|
-
def each(&
|
36
|
+
def each(&)
|
37
37
|
process_full
|
38
|
-
@full_diff_files.map { |file| file[1] }.each(&
|
38
|
+
@full_diff_files.map { |file| file[1] }.each(&)
|
39
39
|
end
|
40
40
|
|
41
41
|
#
|
@@ -43,34 +43,32 @@ module Git
|
|
43
43
|
#
|
44
44
|
|
45
45
|
def name_status
|
46
|
-
Git::Deprecation.warn(
|
46
|
+
Git::Deprecation.warn('Git::Diff#name_status is deprecated. Use Git::Base#diff_path_status instead.')
|
47
47
|
path_status_provider.to_h
|
48
48
|
end
|
49
49
|
|
50
50
|
def size
|
51
|
-
Git::Deprecation.warn(
|
51
|
+
Git::Deprecation.warn('Git::Diff#size is deprecated. Use Git::Base#diff_stats(...).total[:files] instead.')
|
52
52
|
stats_provider.total[:files]
|
53
53
|
end
|
54
54
|
|
55
|
-
|
56
|
-
|
57
55
|
def lines
|
58
|
-
Git::Deprecation.warn(
|
56
|
+
Git::Deprecation.warn('Git::Diff#lines is deprecated. Use Git::Base#diff_stats(...).lines instead.')
|
59
57
|
stats_provider.lines
|
60
58
|
end
|
61
59
|
|
62
60
|
def deletions
|
63
|
-
Git::Deprecation.warn(
|
61
|
+
Git::Deprecation.warn('Git::Diff#deletions is deprecated. Use Git::Base#diff_stats(...).deletions instead.')
|
64
62
|
stats_provider.deletions
|
65
63
|
end
|
66
64
|
|
67
65
|
def insertions
|
68
|
-
Git::Deprecation.warn(
|
66
|
+
Git::Deprecation.warn('Git::Diff#insertions is deprecated. Use Git::Base#diff_stats(...).insertions instead.')
|
69
67
|
stats_provider.insertions
|
70
68
|
end
|
71
69
|
|
72
70
|
def stats
|
73
|
-
Git::Deprecation.warn(
|
71
|
+
Git::Deprecation.warn('Git::Diff#stats is deprecated. Use Git::Base#diff_stats instead.')
|
74
72
|
# CORRECTED: Re-create the original hash structure for backward compatibility
|
75
73
|
{
|
76
74
|
files: stats_provider.files,
|
@@ -78,10 +76,12 @@ module Git
|
|
78
76
|
}
|
79
77
|
end
|
80
78
|
|
79
|
+
# The changes for a single file within a diff
|
81
80
|
class DiffFile
|
82
81
|
attr_accessor :patch, :path, :mode, :src, :dst, :type
|
82
|
+
|
83
83
|
@base = nil
|
84
|
-
NIL_BLOB_REGEXP = /\A0{4,40}\z
|
84
|
+
NIL_BLOB_REGEXP = /\A0{4,40}\z/
|
85
85
|
|
86
86
|
def initialize(base, hash)
|
87
87
|
@base = base
|
@@ -111,46 +111,82 @@ module Git
|
|
111
111
|
|
112
112
|
def process_full
|
113
113
|
return if @full_diff_files
|
114
|
+
|
114
115
|
@full_diff_files = process_full_diff
|
115
116
|
end
|
116
117
|
|
117
|
-
# CORRECTED: Pass the @path variable to the new objects
|
118
118
|
def path_status_provider
|
119
119
|
@path_status_provider ||= Git::DiffPathStatus.new(@base, @from, @to, @path)
|
120
120
|
end
|
121
121
|
|
122
|
-
# CORRECTED: Pass the @path variable to the new objects
|
123
122
|
def stats_provider
|
124
123
|
@stats_provider ||= Git::DiffStats.new(@base, @from, @to, @path)
|
125
124
|
end
|
126
125
|
|
127
126
|
def process_full_diff
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
127
|
+
FullDiffParser.new(@base, patch).parse
|
128
|
+
end
|
129
|
+
|
130
|
+
# A private parser class to process the output of `git diff`
|
131
|
+
# @api private
|
132
|
+
class FullDiffParser
|
133
|
+
def initialize(base, patch_text)
|
134
|
+
@base = base
|
135
|
+
@patch_text = patch_text
|
136
|
+
@final_files = {}
|
137
|
+
@current_file_data = nil
|
138
|
+
@defaults = { mode: '', src: '', dst: '', type: 'modified', binary: false }
|
139
|
+
end
|
140
|
+
|
141
|
+
def parse
|
142
|
+
@patch_text.split("\n").each { |line| process_line(line) }
|
143
|
+
@final_files.map { |filename, data| [filename, DiffFile.new(@base, data)] }
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def process_line(line)
|
149
|
+
if (new_file_match = line.match(%r{\Adiff --git ("?)a/(.+?)\1 ("?)b/(.+?)\3\z}))
|
150
|
+
start_new_file(new_file_match, line)
|
137
151
|
else
|
138
|
-
|
139
|
-
final[current_file][:src] = m[1]
|
140
|
-
final[current_file][:dst] = m[2]
|
141
|
-
final[current_file][:mode] = m[3].strip if m[3]
|
142
|
-
end
|
143
|
-
if m = /^([[:alpha:]]*?) file mode (......)/.match(line)
|
144
|
-
final[current_file][:type] = m[1]
|
145
|
-
final[current_file][:mode] = m[2]
|
146
|
-
end
|
147
|
-
if m = /^Binary files /.match(line)
|
148
|
-
final[current_file][:binary] = true
|
149
|
-
end
|
150
|
-
final[current_file][:patch] << "\n" + line
|
152
|
+
append_to_current_file(line)
|
151
153
|
end
|
152
154
|
end
|
153
|
-
|
155
|
+
|
156
|
+
def start_new_file(match, line)
|
157
|
+
filename = Git::EscapedPath.new(match[2]).unescape
|
158
|
+
@current_file_data = @defaults.merge({ patch: line, path: filename })
|
159
|
+
@final_files[filename] = @current_file_data
|
160
|
+
end
|
161
|
+
|
162
|
+
def append_to_current_file(line)
|
163
|
+
return unless @current_file_data
|
164
|
+
|
165
|
+
parse_index_line(line)
|
166
|
+
parse_file_mode_line(line)
|
167
|
+
check_for_binary(line)
|
168
|
+
|
169
|
+
@current_file_data[:patch] << "\n#{line}"
|
170
|
+
end
|
171
|
+
|
172
|
+
def parse_index_line(line)
|
173
|
+
return unless (match = line.match(/^index ([0-9a-f]{4,40})\.\.([0-9a-f]{4,40})( ......)*/))
|
174
|
+
|
175
|
+
@current_file_data[:src] = match[1]
|
176
|
+
@current_file_data[:dst] = match[2]
|
177
|
+
@current_file_data[:mode] = match[3].strip if match[3]
|
178
|
+
end
|
179
|
+
|
180
|
+
def parse_file_mode_line(line)
|
181
|
+
return unless (match = line.match(/^([[:alpha:]]*?) file mode (......)/))
|
182
|
+
|
183
|
+
@current_file_data[:type] = match[1]
|
184
|
+
@current_file_data[:mode] = match[2]
|
185
|
+
end
|
186
|
+
|
187
|
+
def check_for_binary(line)
|
188
|
+
@current_file_data[:binary] = true if line.match?(/^Binary files /)
|
189
|
+
end
|
154
190
|
end
|
155
191
|
end
|
156
192
|
end
|
data/lib/git/diff_path_status.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Git
|
4
|
+
# The files and their status (e.g., added, modified, deleted) between two commits
|
4
5
|
class DiffPathStatus
|
5
6
|
include Enumerable
|
6
7
|
|
@@ -21,8 +22,8 @@ module Git
|
|
21
22
|
# Iterates over each file's status.
|
22
23
|
#
|
23
24
|
# @yield [path, status]
|
24
|
-
def each(&
|
25
|
-
fetch_path_status.each(&
|
25
|
+
def each(&)
|
26
|
+
fetch_path_status.each(&)
|
26
27
|
end
|
27
28
|
|
28
29
|
# Returns the name-status report as a Hash.
|
@@ -37,7 +38,7 @@ module Git
|
|
37
38
|
|
38
39
|
# Lazily fetches and caches the path status from the git lib.
|
39
40
|
def fetch_path_status
|
40
|
-
@
|
41
|
+
@fetch_path_status ||= @base.lib.diff_path_status(
|
41
42
|
@from, @to, { path: @path_limiter }
|
42
43
|
)
|
43
44
|
end
|
data/lib/git/diff_stats.rb
CHANGED
data/lib/git/errors.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Git
|
4
|
+
# rubocop:disable Layout/LineLength
|
5
|
+
|
4
6
|
# Base class for all custom git module errors
|
5
7
|
#
|
6
8
|
# The git gem will only raise an `ArgumentError` or an error that is a subclass of
|
@@ -60,6 +62,8 @@ module Git
|
|
60
62
|
#
|
61
63
|
class Error < StandardError; end
|
62
64
|
|
65
|
+
# rubocop:enable Layout/LineLength
|
66
|
+
|
63
67
|
# An alias for Git::Error
|
64
68
|
#
|
65
69
|
# Git::GitExecuteError error class is an alias for Git::Error for backwards
|
@@ -155,7 +159,8 @@ module Git
|
|
155
159
|
# status = ProcessExecuter.spawn(*command, timeout: timeout_duration)
|
156
160
|
# result = Git::CommandLineResult.new(command, status, 'stdout', 'err output')
|
157
161
|
# error = Git::TimeoutError.new(result, timeout_duration)
|
158
|
-
# error.error_message
|
162
|
+
# error.error_message
|
163
|
+
# #=> '["sleep", "10"], status: pid 70144 SIGKILL (signal 9), stderr: "err output", timed out after 1s'
|
159
164
|
#
|
160
165
|
# @param result [Git::CommandLineResult] the result of the git command including
|
161
166
|
# the git command, status, stdout, and stderr
|
@@ -171,7 +176,8 @@ module Git
|
|
171
176
|
# The human readable representation of this error
|
172
177
|
#
|
173
178
|
# @example
|
174
|
-
# error.error_message
|
179
|
+
# error.error_message
|
180
|
+
# #=> '["sleep", "10"], status: pid 88811 SIGKILL (signal 9), stderr: "err output", timed out after 1s'
|
175
181
|
#
|
176
182
|
# @return [String]
|
177
183
|
#
|