git 1.19.1 → 2.1.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.
@@ -0,0 +1,377 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'git/base'
4
+ require 'git/command_line_result'
5
+ require 'git/errors'
6
+ require 'stringio'
7
+
8
+ module Git
9
+ # Runs a git command and returns the result
10
+ #
11
+ # @api public
12
+ #
13
+ class CommandLine
14
+ # Create a Git::CommandLine object
15
+ #
16
+ # @example
17
+ # env = { 'GIT_DIR' => '/path/to/git/dir' }
18
+ # binary_path = '/usr/bin/git'
19
+ # global_opts = %w[--git-dir /path/to/git/dir]
20
+ # logger = Logger.new(STDOUT)
21
+ # cli = CommandLine.new(env, binary_path, global_opts, logger)
22
+ # cli.run('version') #=> #<Git::CommandLineResult:0x00007f9b0c0b0e00
23
+ #
24
+ # @param env [Hash<String, String>] environment variables to set
25
+ # @param global_opts [Array<String>] global options to pass to git
26
+ # @param logger [Logger] the logger to use
27
+ #
28
+ def initialize(env, binary_path, global_opts, logger)
29
+ @env = env
30
+ @binary_path = binary_path
31
+ @global_opts = global_opts
32
+ @logger = logger
33
+ end
34
+
35
+ # @attribute [r] env
36
+ #
37
+ # Variables to set (or unset) in the git command's environment
38
+ #
39
+ # @example
40
+ # env = { 'GIT_DIR' => '/path/to/git/dir' }
41
+ # command_line = Git::CommandLine.new(env, '/usr/bin/git', [], Logger.new(STDOUT))
42
+ # command_line.env #=> { 'GIT_DIR' => '/path/to/git/dir' }
43
+ #
44
+ # @return [Hash<String, String>]
45
+ #
46
+ # @see https://ruby-doc.org/3.2.1/Process.html#method-c-spawn Process.spawn
47
+ # for details on how to set environment variables using the `env` parameter
48
+ #
49
+ attr_reader :env
50
+
51
+ # @attribute [r] binary_path
52
+ #
53
+ # The path to the command line binary to run
54
+ #
55
+ # @example
56
+ # binary_path = '/usr/bin/git'
57
+ # command_line = Git::CommandLine.new({}, binary_path, ['version'], Logger.new(STDOUT))
58
+ # command_line.binary_path #=> '/usr/bin/git'
59
+ #
60
+ # @return [String]
61
+ #
62
+ attr_reader :binary_path
63
+
64
+ # @attribute [r] global_opts
65
+ #
66
+ # The global options to pass to git
67
+ #
68
+ # These are options that are passed to git before the command name and
69
+ # arguments. For example, in `git --git-dir /path/to/git/dir version`, the
70
+ # global options are %w[--git-dir /path/to/git/dir].
71
+ #
72
+ # @example
73
+ # env = {}
74
+ # global_opts = %w[--git-dir /path/to/git/dir]
75
+ # logger = Logger.new(nil)
76
+ # cli = CommandLine.new(env, '/usr/bin/git', global_opts, logger)
77
+ # cli.global_opts #=> %w[--git-dir /path/to/git/dir]
78
+ #
79
+ # @return [Array<String>]
80
+ #
81
+ attr_reader :global_opts
82
+
83
+ # @attribute [r] logger
84
+ #
85
+ # The logger to use for logging git commands and results
86
+ #
87
+ # @example
88
+ # env = {}
89
+ # global_opts = %w[]
90
+ # logger = Logger.new(STDOUT)
91
+ # cli = CommandLine.new(env, '/usr/bin/git', global_opts, logger)
92
+ # cli.logger == logger #=> true
93
+ #
94
+ # @return [Logger]
95
+ #
96
+ attr_reader :logger
97
+
98
+ # Execute a git command, wait for it to finish, and return the result
99
+ #
100
+ # NORMALIZATION
101
+ #
102
+ # The command output is returned as a Unicde string containing the binary output
103
+ # from the command. If the binary output is not valid UTF-8, the output will
104
+ # cause problems because the encoding will be invalid.
105
+ #
106
+ # Normalization is a process that trys to convert the binary output to a valid
107
+ # UTF-8 string. It uses the `rchardet` gem to detect the encoding of the binary
108
+ # output and then converts it to UTF-8.
109
+ #
110
+ # Normalization is not enabled by default. Pass `normalize: true` to Git::CommandLine#run
111
+ # to enable it. Normalization will only be performed on stdout and only if the `out:`` option
112
+ # is nil or is a StringIO object. If the out: option is set to a file or other IO object,
113
+ # the normalize option will be ignored.
114
+ #
115
+ # @example Run a command and return the output
116
+ # cli.run('version') #=> "git version 2.39.1\n"
117
+ #
118
+ # @example The args array should be splatted into the parameter list
119
+ # args = %w[log -n 1 --oneline]
120
+ # cli.run(*args) #=> "f5baa11 beginning of Ruby/Git project\n"
121
+ #
122
+ # @example Run a command and return the chomped output
123
+ # cli.run('version', chomp: true) #=> "git version 2.39.1"
124
+ #
125
+ # @example Run a command and without normalizing the output
126
+ # cli.run('version', normalize: false) #=> "git version 2.39.1\n"
127
+ #
128
+ # @example Capture stdout in a temporary file
129
+ # require 'tempfile'
130
+ # tempfile = Tempfile.create('git') do |file|
131
+ # cli.run('version', out: file)
132
+ # file.rewind
133
+ # file.read #=> "git version 2.39.1\n"
134
+ # end
135
+ #
136
+ # @example Capture stderr in a StringIO object
137
+ # require 'stringio'
138
+ # stderr = StringIO.new
139
+ # begin
140
+ # cli.run('log', 'nonexistent-branch', err: stderr)
141
+ # rescue Git::FailedError => e
142
+ # stderr.string #=> "unknown revision or path not in the working tree.\n"
143
+ # end
144
+ #
145
+ # @param args [Array<String>] the command line arguements to pass to git
146
+ #
147
+ # This array should be splatted into the parameter list.
148
+ #
149
+ # @param out [#write, nil] the object to write stdout to or nil to ignore stdout
150
+ #
151
+ # If this is a 'StringIO' object, then `stdout_writer.string` will be returned.
152
+ #
153
+ # In general, only specify a `stdout_writer` object when you want to redirect
154
+ # stdout to a file or some other object that responds to `#write`. The default
155
+ # behavior will return the output of the command.
156
+ #
157
+ # @param err [#write] the object to write stderr to or nil to ignore stderr
158
+ #
159
+ # If this is a 'StringIO' object and `merged_output` is `true`, then
160
+ # `stderr_writer.string` will be merged into the output returned by this method.
161
+ #
162
+ # @param normalize [Boolean] whether to normalize the output to a valid encoding
163
+ #
164
+ # @param chomp [Boolean] whether to chomp the output
165
+ #
166
+ # @param merge [Boolean] whether to merge stdout and stderr in the string returned
167
+ #
168
+ # @param chdir [String] the directory to run the command in
169
+ #
170
+ # @param timeout [Numeric, nil] the maximum seconds to wait for the command to complete
171
+ #
172
+ # If timeout is zero, the timeout will not be enforced.
173
+ #
174
+ # If the command times out, it is killed via a `SIGKILL` signal and `Git::TimeoutError` is raised.
175
+ #
176
+ # If the command does not respond to SIGKILL, it will hang this method.
177
+ #
178
+ # @return [Git::CommandLineResult] the output of the command
179
+ #
180
+ # This result of running the command.
181
+ #
182
+ # @raise [ArgumentError] if `args` is not an array of strings
183
+ #
184
+ # @raise [Git::SignaledError] if the command was terminated because of an uncaught signal
185
+ #
186
+ # @raise [Git::FailedError] if the command returned a non-zero exitstatus
187
+ #
188
+ # @raise [Git::ProcessIOError] if an exception was raised while collecting subprocess output
189
+ #
190
+ # @raise [Git::TimeoutError] if the command times out
191
+ #
192
+ def run(*args, out:, err:, normalize:, chomp:, merge:, chdir: nil, timeout: nil)
193
+ git_cmd = build_git_cmd(args)
194
+ out ||= StringIO.new
195
+ err ||= (merge ? out : StringIO.new)
196
+ status = execute(git_cmd, out, err, chdir: (chdir || :not_set), timeout: timeout)
197
+
198
+ process_result(git_cmd, status, out, err, normalize, chomp, timeout)
199
+ end
200
+
201
+ private
202
+
203
+ # Build the git command line from the available sources to send to `Process.spawn`
204
+ # @return [Array<String>]
205
+ # @api private
206
+ #
207
+ def build_git_cmd(args)
208
+ raise ArgumentError.new('The args array can not contain an array') if args.any? { |a| a.is_a?(Array) }
209
+
210
+ [binary_path, *global_opts, *args].map { |e| e.to_s }
211
+ end
212
+
213
+ # Determine the output to return in the `CommandLineResult`
214
+ #
215
+ # If the writer can return the output by calling `#string` (such as a StringIO),
216
+ # then return the result of normalizing the encoding and chomping the output
217
+ # as requested.
218
+ #
219
+ # If the writer does not support `#string`, then return nil. The output is
220
+ # assumed to be collected by the writer itself such as when the writer
221
+ # is a file instead of a StringIO.
222
+ #
223
+ # @param writer [#string] the writer to post-process
224
+ #
225
+ # @return [String, nil]
226
+ #
227
+ # @api private
228
+ #
229
+ def post_process(writer, normalize, chomp)
230
+ if writer.respond_to?(:string)
231
+ output = writer.string.dup
232
+ output = output.lines.map { |l| Git::EncodingUtils.normalize_encoding(l) }.join if normalize
233
+ output.chomp! if chomp
234
+ output
235
+ else
236
+ nil
237
+ end
238
+ end
239
+
240
+ # Post-process all writers and return an array of the results
241
+ #
242
+ # @param writers [Array<#write>] the writers to post-process
243
+ # @param normalize [Boolean] whether to normalize the output of each writer
244
+ # @param chomp [Boolean] whether to chomp the output of each writer
245
+ #
246
+ # @return [Array<String, nil>] the output of each writer that supports `#string`
247
+ #
248
+ # @api private
249
+ #
250
+ def post_process_all(writers, normalize, chomp)
251
+ Array.new.tap do |result|
252
+ writers.each { |writer| result << post_process(writer, normalize, chomp) }
253
+ end
254
+ end
255
+
256
+ # Raise an error when there was exception while collecting the subprocess output
257
+ #
258
+ # @param git_cmd [Array<String>] the git command that was executed
259
+ # @param pipe_name [Symbol] the name of the pipe that raised the exception
260
+ # @param pipe [ProcessExecuter::MonitoredPipe] the pipe that raised the exception
261
+ #
262
+ # @raise [Git::ProcessIOError]
263
+ #
264
+ # @return [void] this method always raises an error
265
+ #
266
+ # @api private
267
+ #
268
+ def raise_pipe_error(git_cmd, pipe_name, pipe)
269
+ raise Git::ProcessIOError.new("Pipe Exception for #{git_cmd}: #{pipe_name}"), cause: pipe.exception
270
+ end
271
+
272
+ # Execute the git command and collect the output
273
+ #
274
+ # @param cmd [Array<String>] the git command to execute
275
+ # @param chdir [String] the directory to run the command in
276
+ # @param timeout [Numeric, nil] the maximum seconds to wait for the command to complete
277
+ #
278
+ # If timeout is zero of nil, the command will not time out. If the command
279
+ # times out, it is killed via a SIGKILL signal and `Git::TimeoutError` is raised.
280
+ #
281
+ # If the command does not respond to SIGKILL, it will hang this method.
282
+ #
283
+ # @raise [Git::ProcessIOError] if an exception was raised while collecting subprocess output
284
+ # @raise [Git::TimeoutError] if the command times out
285
+ #
286
+ # @return [ProcessExecuter::Status] the status of the completed subprocess
287
+ #
288
+ # @api private
289
+ #
290
+ def spawn(cmd, out_writers, err_writers, chdir:, timeout:)
291
+ out_pipe = ProcessExecuter::MonitoredPipe.new(*out_writers, chunk_size: 10_000)
292
+ err_pipe = ProcessExecuter::MonitoredPipe.new(*err_writers, chunk_size: 10_000)
293
+ ProcessExecuter.spawn(env, *cmd, out: out_pipe, err: err_pipe, chdir: chdir, timeout: timeout)
294
+ ensure
295
+ out_pipe.close
296
+ err_pipe.close
297
+ raise_pipe_error(cmd, :stdout, out_pipe) if out_pipe.exception
298
+ raise_pipe_error(cmd, :stderr, err_pipe) if err_pipe.exception
299
+ end
300
+
301
+ # The writers that will be used to collect stdout and stderr
302
+ #
303
+ # Additional writers could be added here if you wanted to tee output
304
+ # or send output to the terminal.
305
+ #
306
+ # @param out [#write] the object to write stdout to
307
+ # @param err [#write] the object to write stderr to
308
+ #
309
+ # @return [Array<Array<#write>, Array<#write>>] the writers for stdout and stderr
310
+ #
311
+ # @api private
312
+ #
313
+ def writers(out, err)
314
+ out_writers = [out]
315
+ err_writers = [err]
316
+ [out_writers, err_writers]
317
+ end
318
+
319
+ # Process the result of the command and return a Git::CommandLineResult
320
+ #
321
+ # Post process output, log the command and result, and raise an error if the
322
+ # command failed.
323
+ #
324
+ # @param git_cmd [Array<String>] the git command that was executed
325
+ # @param status [Process::Status] the status of the completed subprocess
326
+ # @param out [#write] the object that stdout was written to
327
+ # @param err [#write] the object that stderr was written to
328
+ # @param normalize [Boolean] whether to normalize the output of each writer
329
+ # @param chomp [Boolean] whether to chomp the output of each writer
330
+ # @param timeout [Numeric, nil] the maximum seconds to wait for the command to complete
331
+ #
332
+ # @return [Git::CommandLineResult] the result of the command to return to the caller
333
+ #
334
+ # @raise [Git::FailedError] if the command failed
335
+ # @raise [Git::SignaledError] if the command was signaled
336
+ # @raise [Git::TimeoutError] if the command times out
337
+ # @raise [Git::ProcessIOError] if an exception was raised while collecting subprocess output
338
+ #
339
+ # @api private
340
+ #
341
+ def process_result(git_cmd, status, out, err, normalize, chomp, timeout)
342
+ out_str, err_str = post_process_all([out, err], normalize, chomp)
343
+ logger.info { "#{git_cmd} exited with status #{status}" }
344
+ logger.debug { "stdout:\n#{out_str.inspect}\nstderr:\n#{err_str.inspect}" }
345
+ Git::CommandLineResult.new(git_cmd, status, out_str, err_str).tap do |result|
346
+ raise Git::TimeoutError.new(result, timeout) if status.timeout?
347
+ raise Git::SignaledError.new(result) if status.signaled?
348
+ raise Git::FailedError.new(result) unless status.success?
349
+ end
350
+ end
351
+
352
+ # Execute the git command and write the command output to out and err
353
+ #
354
+ # @param git_cmd [Array<String>] the git command to execute
355
+ # @param out [#write] the object to write stdout to
356
+ # @param err [#write] the object to write stderr to
357
+ # @param chdir [String] the directory to run the command in
358
+ # @param timeout [Numeric, nil] the maximum seconds to wait for the command to complete
359
+ #
360
+ # If timeout is zero of nil, the command will not time out. If the command
361
+ # times out, it is killed via a SIGKILL signal and `Git::TimeoutError` is raised.
362
+ #
363
+ # If the command does not respond to SIGKILL, it will hang this method.
364
+ #
365
+ # @raise [Git::ProcessIOError] if an exception was raised while collecting subprocess output
366
+ # @raise [Git::TimeoutError] if the command times out
367
+ #
368
+ # @return [Git::CommandLineResult] the result of the command to return to the caller
369
+ #
370
+ # @api private
371
+ #
372
+ def execute(git_cmd, out, err, chdir:, timeout:)
373
+ out_writers, err_writers = writers(out, err)
374
+ spawn(git_cmd, out_writers, err_writers, chdir: chdir, timeout: timeout)
375
+ end
376
+ end
377
+ end
data/lib/git/config.rb CHANGED
@@ -2,11 +2,12 @@ module Git
2
2
 
3
3
  class Config
4
4
 
5
- attr_writer :binary_path, :git_ssh
5
+ attr_writer :binary_path, :git_ssh, :timeout
6
6
 
7
7
  def initialize
8
8
  @binary_path = nil
9
9
  @git_ssh = nil
10
+ @timeout = nil
10
11
  end
11
12
 
12
13
  def binary_path
@@ -17,6 +18,9 @@ module Git
17
18
  @git_ssh || ENV['GIT_SSH']
18
19
  end
19
20
 
21
+ def timeout
22
+ @timeout || (ENV['GIT_TIMEOUT'] && ENV['GIT_TIMEOUT'].to_i)
23
+ end
20
24
  end
21
25
 
22
26
  end
data/lib/git/errors.rb ADDED
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ # Base class for all custom git module errors
5
+ #
6
+ # The git gem will only raise an `ArgumentError` or an error that is a subclass of
7
+ # `Git::Error`. It does not explicitly raise any other types of errors.
8
+ #
9
+ # It is recommended to rescue `Git::Error` to catch any runtime error raised by
10
+ # this gem unless you need more specific error handling.
11
+ #
12
+ # Git's custom errors are arranged in the following class heirarchy:
13
+ #
14
+ # ```text
15
+ # StandardError
16
+ # └─> Git::Error
17
+ # ├─> Git::CommandLineError
18
+ # │ ├─> Git::FailedError
19
+ # │ └─> Git::SignaledError
20
+ # │ └─> Git::TimeoutError
21
+ # ├─> Git::ProcessIOError
22
+ # └─> Git::UnexpectedResultError
23
+ # ```
24
+ #
25
+ # | Error Class | Description |
26
+ # | --- | --- |
27
+ # | `Error` | This catch-all error serves as the base class for other custom errors raised by the git gem. |
28
+ # | `CommandLineError` | A subclass of this error is raised when there is a problem executing the git command line. |
29
+ # | `FailedError` | This error is raised when the git command line exits with a non-zero status code that is not expected by the git gem. |
30
+ # | `SignaledError` | This error is raised when the git command line is terminated as a result of receiving a signal. This could happen if the process is forcibly terminated or if there is a serious system error. |
31
+ # | `TimeoutError` | This is a specific type of `SignaledError` that is raised when the git command line operation times out and is killed via the SIGKILL signal. This happens if the operation takes longer than the timeout duration configured in `Git.config.timeout` or via the `:timeout` parameter given in git methods that support timeouts. |
32
+ # | `ProcessIOError` | An error was encountered reading or writing to a subprocess. |
33
+ # | `UnexpectedResultError` | The command line ran without error but did not return the expected results. |
34
+ #
35
+ # @example Rescuing a generic error
36
+ # begin
37
+ # # some git operation
38
+ # rescue Git::Error => e
39
+ # puts "An error occurred: #{e.message}"
40
+ # end
41
+ #
42
+ # @example Rescuing a timeout error
43
+ # begin
44
+ # timeout_duration = 0.001 # seconds
45
+ # repo = Git.clone('https://github.com/ruby-git/ruby-git', 'ruby-git-temp', timeout: timeout_duration)
46
+ # rescue Git::TimeoutError => e # Catch the more specific error first!
47
+ # puts "Git clone took too long and timed out #{e}"
48
+ # rescue Git::Error => e
49
+ # puts "Received the following error: #{e}"
50
+ # end
51
+ #
52
+ # @see Git::CommandLineError
53
+ # @see Git::FailedError
54
+ # @see Git::SignaledError
55
+ # @see Git::TimeoutError
56
+ # @see Git::ProcessIOError
57
+ # @see Git::UnexpectedResultError
58
+ #
59
+ # @api public
60
+ #
61
+ class Error < StandardError; end
62
+
63
+ # An alias for Git::Error
64
+ #
65
+ # Git::GitExecuteError error class is an alias for Git::Error for backwards
66
+ # compatibility. It is recommended to use Git::Error directly.
67
+ #
68
+ # @deprecated Use Git::Error instead
69
+ #
70
+ GitExecuteError = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Git::GitExecuteError', 'Git::Error', Git::Deprecation)
71
+
72
+ # Raised when a git command fails or exits because of an uncaught signal
73
+ #
74
+ # The git command executed, status, stdout, and stderr are available from this
75
+ # object.
76
+ #
77
+ # The Gem will raise a more specific error for each type of failure:
78
+ #
79
+ # * {Git::FailedError}: when the git command exits with a non-zero status
80
+ # * {Git::SignaledError}: when the git command exits because of an uncaught signal
81
+ # * {Git::TimeoutError}: when the git command times out
82
+ #
83
+ # @api public
84
+ #
85
+ class CommandLineError < Git::Error
86
+ # Create a CommandLineError object
87
+ #
88
+ # @example
89
+ # `exit 1` # set $? appropriately for this example
90
+ # result = Git::CommandLineResult.new(%w[git status], $?, 'stdout', 'stderr')
91
+ # error = Git::CommandLineError.new(result)
92
+ # error.to_s #=> '["git", "status"], status: pid 89784 exit 1, stderr: "stderr"'
93
+ #
94
+ # @param result [Git::CommandLineResult] the result of the git command including
95
+ # the git command, status, stdout, and stderr
96
+ #
97
+ def initialize(result)
98
+ @result = result
99
+ super(error_message)
100
+ end
101
+
102
+ # The human readable representation of this error
103
+ #
104
+ # @example
105
+ # error.error_message #=> '["git", "status"], status: pid 89784 exit 1, stderr: "stderr"'
106
+ #
107
+ # @return [String]
108
+ #
109
+ def error_message = <<~MESSAGE.chomp
110
+ #{result.git_cmd}, status: #{result.status}, stderr: #{result.stderr.inspect}
111
+ MESSAGE
112
+
113
+ # @attribute [r] result
114
+ #
115
+ # The result of the git command including the git command and its status and output
116
+ #
117
+ # @example
118
+ # error.result #=> #<Git::CommandLineResult:0x00000001046bd488 ...>
119
+ #
120
+ # @return [Git::CommandLineResult]
121
+ #
122
+ attr_reader :result
123
+ end
124
+
125
+ # This error is raised when a git command returns a non-zero exitstatus
126
+ #
127
+ # The git command executed, status, stdout, and stderr are available from this
128
+ # object.
129
+ #
130
+ # @api public
131
+ #
132
+ class FailedError < Git::CommandLineError; end
133
+
134
+ # This error is raised when a git command exits because of an uncaught signal
135
+ #
136
+ # @api public
137
+ #
138
+ class SignaledError < Git::CommandLineError; end
139
+
140
+ # This error is raised when a git command takes longer than the configured timeout
141
+ #
142
+ # The git command executed, status, stdout, and stderr, and the timeout duration
143
+ # are available from this object.
144
+ #
145
+ # result.status.timeout? will be `true`
146
+ #
147
+ # @api public
148
+ #
149
+ class TimeoutError < Git::SignaledError
150
+ # Create a TimeoutError object
151
+ #
152
+ # @example
153
+ # command = %w[sleep 10]
154
+ # timeout_duration = 1
155
+ # status = ProcessExecuter.spawn(*command, timeout: timeout_duration)
156
+ # result = Git::CommandLineResult.new(command, status, 'stdout', 'err output')
157
+ # error = Git::TimeoutError.new(result, timeout_duration)
158
+ # error.error_message #=> '["sleep", "10"], status: pid 70144 SIGKILL (signal 9), stderr: "err output", timed out after 1s'
159
+ #
160
+ # @param result [Git::CommandLineResult] the result of the git command including
161
+ # the git command, status, stdout, and stderr
162
+ #
163
+ # @param timeout_duration [Numeric] the amount of time the subprocess was allowed
164
+ # to run before being killed
165
+ #
166
+ def initialize(result, timeout_duration)
167
+ @timeout_duration = timeout_duration
168
+ super(result)
169
+ end
170
+
171
+ # The human readable representation of this error
172
+ #
173
+ # @example
174
+ # error.error_message #=> '["sleep", "10"], status: pid 88811 SIGKILL (signal 9), stderr: "err output", timed out after 1s'
175
+ #
176
+ # @return [String]
177
+ #
178
+ def error_message = <<~MESSAGE.chomp
179
+ #{super}, timed out after #{timeout_duration}s
180
+ MESSAGE
181
+
182
+ # The amount of time the subprocess was allowed to run before being killed
183
+ #
184
+ # @example
185
+ # `kill -9 $$` # set $? appropriately for this example
186
+ # result = Git::CommandLineResult.new(%w[git status], $?, '', "killed")
187
+ # error = Git::TimeoutError.new(result, 10)
188
+ # error.timeout_duration #=> 10
189
+ #
190
+ # @return [Numeric]
191
+ #
192
+ attr_reader :timeout_duration
193
+ end
194
+
195
+ # Raised when the output of a git command can not be read
196
+ #
197
+ # @api public
198
+ #
199
+ class ProcessIOError < Git::Error; end
200
+
201
+ # Raised when the git command result was not as expected
202
+ #
203
+ # @api public
204
+ #
205
+ class UnexpectedResultError < Git::Error; end
206
+ end
@@ -3,7 +3,7 @@
3
3
  module Git
4
4
  # Represents an escaped Git path string
5
5
  #
6
- # Git commands that output paths (e.g. ls-files, diff), will escape usual
6
+ # Git commands that output paths (e.g. ls-files, diff), will escape unusual
7
7
  # characters in the path with backslashes in the same way C escapes control
8
8
  # characters (e.g. \t for TAB, \n for LF, \\ for backslash) or bytes with values
9
9
  # larger than 0x80 (e.g. octal \302\265 for "micro" in UTF-8).