git 2.0.0.pre1 → 2.0.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf201a54a634864ee66f8e43866215d0c133fba4d2169ac1e0da3e5aa7bb1a2f
4
- data.tar.gz: 38e2e3d0f2429fe458bca0218a49bd7d09707acd7d76da40cc8f9d7ce6fcfd74
3
+ metadata.gz: 23b5fa33a9a0cb112334c8b243abd6b388984d5f63aa2ee57f83e67d66e68f4b
4
+ data.tar.gz: 292e0d1c9aa4ce4671206abcdb1a547ab606090ecd7b8f2d182def4721347c03
5
5
  SHA512:
6
- metadata.gz: 03cb58e9fc52d517d1a95584dae5c99d15532446442bac8aa6383a81770d7cc439643cd8bea04e5ae93dda8e481936ed3deebe86166fef2c4b9cd0ca3cdc8580
7
- data.tar.gz: 2ca4de81d5a9359dc8f80d1a4ca218dafc472f5b9cb5a5b7f01c6633adbeaf8361c34e9f741147ae1948593779af9770508be35a3ef3590991e6830990a6355f
6
+ metadata.gz: 2332e16fc52d5ff5fd67cea38a1e4196bb98c06256746e81d13b0572ac78957743559eb27b8de752ba8c960e72f912cb5077fce625b6233ed6e63e1c2ef67890
7
+ data.tar.gz: bf769b8943941cec7efa01bae6152302023045d01a69c6aaddf9c04f8815eb18828a2e81b8133d0c7ab873235f08acfecce528d3e7f8659fb032baa73e44c6a1
@@ -18,7 +18,7 @@ jobs:
18
18
  fail-fast: false
19
19
  matrix:
20
20
  # Only the latest versions of JRuby and TruffleRuby are tested
21
- ruby: ["3.0", "3.1", "3.2", "3.3", "truffleruby-23.1.1", "jruby-9.4.5.0"]
21
+ ruby: ["3.0", "3.1", "3.2", "3.3", "truffleruby-24.0.0", "jruby-9.4.5.0"]
22
22
  operating-system: [ubuntu-latest]
23
23
  experimental: [No]
24
24
  include:
@@ -38,7 +38,7 @@ jobs:
38
38
 
39
39
  steps:
40
40
  - name: Checkout Code
41
- uses: actions/checkout@v3
41
+ uses: actions/checkout@v4
42
42
 
43
43
  - name: Setup Ruby
44
44
  uses: ruby/setup-ruby@v1
data/CHANGELOG.md CHANGED
@@ -5,6 +5,15 @@
5
5
 
6
6
  # Change Log
7
7
 
8
+ ## v2.0.0.pre2 (2024-02-24)
9
+
10
+ [Full Changelog](https://github.com/ruby-git/ruby-git/compare/v2.0.0.pre1..v2.0.0.pre2)
11
+
12
+ Changes since v2.0.0.pre1:
13
+
14
+ * 023017b Add a timeout for git commands (#692)
15
+ * 8286ceb Refactor the Error heriarchy (#693)
16
+
8
17
  ## v2.0.0.pre1 (2024-01-15)
9
18
 
10
19
  [Full Changelog](https://github.com/ruby-git/ruby-git/compare/v1.19.1..v2.0.0.pre1)
data/README.md CHANGED
@@ -11,6 +11,18 @@
11
11
  [![Build Status](https://github.com/ruby-git/ruby-git/workflows/CI/badge.svg?branch=master)](https://github.com/ruby-git/ruby-git/actions?query=workflow%3ACI)
12
12
  [![Code Climate](https://codeclimate.com/github/ruby-git/ruby-git.png)](https://codeclimate.com/github/ruby-git/ruby-git)
13
13
 
14
+ * [Summary](#summary)
15
+ * [v2.0.0 pre-release](#v200-pre-release)
16
+ * [Install](#install)
17
+ * [Major Objects](#major-objects)
18
+ * [Errors Raised By This Gem](#errors-raised-by-this-gem)
19
+ * [Specifying And Handling Timeouts](#specifying-and-handling-timeouts)
20
+ * [Examples](#examples)
21
+ * [Ruby version support policy](#ruby-version-support-policy)
22
+ * [License](#license)
23
+
24
+ ## Summary
25
+
14
26
  The [git gem](https://rubygems.org/gems/git) provides an API that can be used to
15
27
  create, read, and manipulate Git repositories by wrapping system calls to the `git`
16
28
  command line. The API can be used for working with Git in complex interactions
@@ -90,11 +102,119 @@ Pass the `--all` option to `git log` as follows:
90
102
 
91
103
  **Git::Worktrees** - Enumerable object that holds `Git::Worktree objects`.
92
104
 
105
+ ## Errors Raised By This Gem
106
+
107
+ This gem raises custom errors that derive from `Git::Error`. These errors are
108
+ arranged in the following class heirarchy:
109
+
110
+ Error heirarchy:
111
+
112
+ ```text
113
+ Error
114
+ └── CommandLineError
115
+ ├── FailedError
116
+ └── SignaledError
117
+ └── TimeoutError
118
+ ```
119
+
120
+ Other standard errors may also be raised like `ArgumentError`. Each method should
121
+ document the errors it may raise.
122
+
123
+ Description of each Error class:
124
+
125
+ * `Error`: This catch-all error serves as the base class for other custom errors in this
126
+ gem. Errors of this class are raised when no more approriate specific error to
127
+ raise.
128
+ * `CommandLineError`: This error is raised when there's a problem executing the git
129
+ command line. This gem will raise a more specific error depending on how the
130
+ command line failed.
131
+ * `FailedError`: This error is raised when the git command line exits with a non-zero
132
+ status code that is not expected by the git gem.
133
+ * `SignaledError`: This error is raised when the git command line is terminated as a
134
+ result of receiving a signal. This could happen if the process is forcibly
135
+ terminated or if there is a serious system error.
136
+ * `TimeoutError`: This is a specific type of `SignaledError` that is raised when the
137
+ git command line operation times out and is killed via the SIGKILL signal. This
138
+ happens if the operation takes longer than the timeout duration configured in
139
+ `Git.config.timeout` or via the `:timeout` parameter given in git methods that
140
+ support this parameter.
141
+
142
+ `Git::GitExecuteError` remains as an alias for `Git::Error`. It is considered
143
+ deprecated as of git-2.0.0.
144
+
145
+ Here is an example of catching errors when using the git gem:
146
+
147
+ ```ruby
148
+ begin
149
+ timeout_duration = 0.001 # seconds
150
+ repo = Git.clone('https://github.com/ruby-git/ruby-git', 'ruby-git-temp', timeout: timeout_duration)
151
+ rescue Git::TimeoutError => e # Catch the more specific error first!
152
+ puts "Git clone took too long and timed out #{e}"
153
+ rescue Git::Error => e
154
+ puts "Received the following error: #{e}"
155
+ ```
156
+
157
+ ## Specifying And Handling Timeouts
158
+
159
+ The timeout feature was added in git gem version `2.0.0`.
160
+
161
+ A timeout for git operations can be set either globally or for specific method calls
162
+ that accept a `:timeout` parameter.
163
+
164
+ The timeout value must be a real, non-negative `Numeric` value that specifies a
165
+ number of seconds a `git` command will be given to complete before being sent a KILL
166
+ signal. This library may hang if the `git` command does not terminate after receiving
167
+ the KILL signal.
168
+
169
+ When a command times out, a `Git::TimeoutError` is raised.
170
+
171
+ If the timeout value is `0` or `nil`, no timeout will be enforced.
172
+
173
+ If a method accepts a `:timeout` parameter and a receives a non-nil value, it will
174
+ override the global timeout value. In this context, a value of `nil` (which is
175
+ usually the default) will use the global timeout value and a value of `0` will turn
176
+ off timeout enforcement for that method call no matter what the global value is.
177
+
178
+ To set a global timeout, use the `Git.config` object:
179
+
180
+ ```ruby
181
+ Git.config.timeout = nil # a value of nil or 0 means no timeout is enforced
182
+ Git.config.timeout = 1.5 # can be any real, non-negative Numeric interpreted as number of seconds
183
+ ```
184
+
185
+ The global timeout can be overridden for a specific method if the method accepts a
186
+ `:timeout` parameter:
187
+
188
+ ```ruby
189
+ repo_url = 'https://github.com/ruby-git/ruby-git.git'
190
+ Git.clone(repo_url) # Use the global timeout value
191
+ Git.clone(repo_url, timeout: nil) # Also uses the global timeout value
192
+ Git.clone(repo_url, timeout: 0) # Do not enforce a timeout
193
+ Git.clone(repo_url, timeout: 10.5) # Timeout after 10.5 seconds raising Git::SignaledError
194
+ ```
195
+
196
+ If the command takes too long, a `Git::SignaledError` will be raised:
197
+
198
+ ```ruby
199
+ begin
200
+ Git.clone(repo_url, timeout: 10)
201
+ rescue Git::TimeoutError => e
202
+ result = e.result
203
+ result.class #=> Git::CommandLineResult
204
+ result.status #=> #<Process::Status: pid 62173 SIGKILL (signal 9)>
205
+ result.status.timeout? #=> true
206
+ result.git_cmd # The git command ran as an array of strings
207
+ result.stdout # The command's output to stdout until it was terminated
208
+ result.stderr # The command's output to stderr until it was terminated
209
+ end
210
+ ```
211
+
93
212
  ## Examples
94
213
 
95
214
  Here are a bunch of examples of how to use the Ruby/Git package.
96
215
 
97
216
  Require the 'git' gem.
217
+
98
218
  ```ruby
99
219
  require 'git'
100
220
  ```
@@ -261,11 +381,11 @@ g.add(:all=>true) # git add --all -- "."
261
381
  g.add('file_path') # git add -- "file_path"
262
382
  g.add(['file_path_1', 'file_path_2']) # git add -- "file_path_1" "file_path_2"
263
383
 
264
- g.remove() # git rm -f -- "."
265
- g.remove('file.txt') # git rm -f -- "file.txt"
266
- g.remove(['file.txt', 'file2.txt']) # git rm -f -- "file.txt" "file2.txt"
267
- g.remove('file.txt', :recursive => true) # git rm -f -r -- "file.txt"
268
- g.remove('file.txt', :cached => true) # git rm -f --cached -- "file.txt"
384
+ g.remove() # git rm -f -- "."
385
+ g.remove('file.txt') # git rm -f -- "file.txt"
386
+ g.remove(['file.txt', 'file2.txt']) # git rm -f -- "file.txt" "file2.txt"
387
+ g.remove('file.txt', :recursive => true) # git rm -f -r -- "file.txt"
388
+ g.remove('file.txt', :cached => true) # git rm -f --cached -- "file.txt"
269
389
 
270
390
  g.commit('message')
271
391
  g.commit_all('message')
data/git.gemspec CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
28
28
  s.requirements = ['git 2.28.0 or greater']
29
29
 
30
30
  s.add_runtime_dependency 'addressable', '~> 2.8'
31
- s.add_runtime_dependency 'process_executer', '~> 0.7'
31
+ s.add_runtime_dependency 'process_executer', '~> 1.1'
32
32
  s.add_runtime_dependency 'rchardet', '~> 1.8'
33
33
 
34
34
  s.add_development_dependency 'minitar', '~> 0.9'
@@ -166,6 +166,13 @@ module Git
166
166
  # @param merge [Boolean] whether to merge stdout and stderr in the string returned
167
167
  # @param chdir [String] the directory to run the command in
168
168
  #
169
+ # @param timeout [Numeric, nil] the maximum seconds to wait for the command to complete
170
+ #
171
+ # If timeout is zero or nil, the command will not time out. If the command
172
+ # times out, it is killed via a SIGKILL signal and `Git::TimeoutError` is raised.
173
+ #
174
+ # If the command does not respond to SIGKILL, it will hang this method.
175
+ #
169
176
  # @return [Git::CommandLineResult] the output of the command
170
177
  #
171
178
  # This result of running the command.
@@ -173,14 +180,16 @@ module Git
173
180
  # @raise [ArgumentError] if `args` is not an array of strings
174
181
  # @raise [Git::SignaledError] if the command was terminated because of an uncaught signal
175
182
  # @raise [Git::FailedError] if the command returned a non-zero exitstatus
183
+ # @raise [Git::GitExecuteError] if an exception was raised while collecting subprocess output
184
+ # @raise [Git::TimeoutError] if the command times out
176
185
  #
177
- def run(*args, out:, err:, normalize:, chomp:, merge:, chdir: nil)
186
+ def run(*args, out:, err:, normalize:, chomp:, merge:, chdir: nil, timeout: nil)
178
187
  git_cmd = build_git_cmd(args)
179
188
  out ||= StringIO.new
180
189
  err ||= (merge ? out : StringIO.new)
181
- status = execute(git_cmd, out, err, chdir: (chdir || :not_set))
190
+ status = execute(git_cmd, out, err, chdir: (chdir || :not_set), timeout: timeout)
182
191
 
183
- process_result(git_cmd, status, out, err, normalize, chomp)
192
+ process_result(git_cmd, status, out, err, normalize, chomp, timeout)
184
193
  end
185
194
 
186
195
  private
@@ -258,17 +267,24 @@ module Git
258
267
  #
259
268
  # @param cmd [Array<String>] the git command to execute
260
269
  # @param chdir [String] the directory to run the command in
270
+ # @param timeout [Float, Integer, nil] the maximum seconds to wait for the command to complete
271
+ #
272
+ # If timeout is zero of nil, the command will not time out. If the command
273
+ # times out, it is killed via a SIGKILL signal and `Git::TimeoutError` is raised.
274
+ #
275
+ # If the command does not respond to SIGKILL, it will hang this method.
261
276
  #
262
277
  # @raise [Git::GitExecuteError] if an exception was raised while collecting subprocess output
278
+ # @raise [Git::TimeoutError] if the command times out
263
279
  #
264
- # @return [Process::Status] the status of the completed subprocess
280
+ # @return [ProcessExecuter::Status] the status of the completed subprocess
265
281
  #
266
282
  # @api private
267
283
  #
268
- def spawn(cmd, out_writers, err_writers, chdir:)
284
+ def spawn(cmd, out_writers, err_writers, chdir:, timeout:)
269
285
  out_pipe = ProcessExecuter::MonitoredPipe.new(*out_writers, chunk_size: 10_000)
270
286
  err_pipe = ProcessExecuter::MonitoredPipe.new(*err_writers, chunk_size: 10_000)
271
- ProcessExecuter.spawn(env, *cmd, out: out_pipe, err: err_pipe, chdir: chdir)
287
+ ProcessExecuter.spawn(env, *cmd, out: out_pipe, err: err_pipe, chdir: chdir, timeout: timeout)
272
288
  ensure
273
289
  out_pipe.close
274
290
  err_pipe.close
@@ -313,11 +329,12 @@ module Git
313
329
  #
314
330
  # @api private
315
331
  #
316
- def process_result(git_cmd, status, out, err, normalize, chomp)
332
+ def process_result(git_cmd, status, out, err, normalize, chomp, timeout)
317
333
  out_str, err_str = post_process_all([out, err], normalize, chomp)
318
334
  logger.info { "#{git_cmd} exited with status #{status}" }
319
335
  logger.debug { "stdout:\n#{out_str.inspect}\nstderr:\n#{err_str.inspect}" }
320
336
  Git::CommandLineResult.new(git_cmd, status, out_str, err_str).tap do |result|
337
+ raise Git::TimeoutError.new(result, timeout) if status.timeout?
321
338
  raise Git::SignaledError.new(result) if status.signaled?
322
339
  raise Git::FailedError.new(result) unless status.success?
323
340
  end
@@ -329,14 +346,23 @@ module Git
329
346
  # @param out [#write] the object to write stdout to
330
347
  # @param err [#write] the object to write stderr to
331
348
  # @param chdir [String] the directory to run the command in
349
+ # @param timeout [Float, Integer, nil] the maximum seconds to wait for the command to complete
350
+ #
351
+ # If timeout is zero of nil, the command will not time out. If the command
352
+ # times out, it is killed via a SIGKILL signal and `Git::TimeoutError` is raised.
353
+ #
354
+ # If the command does not respond to SIGKILL, it will hang this method.
355
+ #
356
+ # @raise [Git::GitExecuteError] if an exception was raised while collecting subprocess output
357
+ # @raise [Git::TimeoutError] if the command times out
332
358
  #
333
359
  # @return [Git::CommandLineResult] the result of the command to return to the caller
334
360
  #
335
361
  # @api private
336
362
  #
337
- def execute(git_cmd, out, err, chdir:)
363
+ def execute(git_cmd, out, err, chdir:, timeout:)
338
364
  out_writers, err_writers = writers(out, err)
339
- spawn(git_cmd, out_writers, err_writers, chdir: chdir)
365
+ spawn(git_cmd, out_writers, err_writers, chdir: chdir, timeout: timeout)
340
366
  end
341
367
  end
342
368
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'error'
4
+
5
+ module Git
6
+ # Raised when a git command fails or exits because of an uncaught signal
7
+ #
8
+ # The git command executed, status, stdout, and stderr are available from this
9
+ # object.
10
+ #
11
+ # Rather than creating a CommandLineError object directly, it is recommended to use
12
+ # one of the derived classes for the appropriate type of error:
13
+ #
14
+ # * {Git::FailedError}: when the git command exits with a non-zero status
15
+ # * {Git::SignaledError}: when the git command exits because of an uncaught signal
16
+ # * {Git::TimeoutError}: when the git command times out
17
+ #
18
+ # @api public
19
+ #
20
+ class CommandLineError < Git::Error
21
+ # Create a CommandLineError object
22
+ #
23
+ # @example
24
+ # `exit 1` # set $? appropriately for this example
25
+ # result = Git::CommandLineResult.new(%w[git status], $?, 'stdout', 'stderr')
26
+ # error = Git::CommandLineError.new(result)
27
+ # error.to_s #=> '["git", "status"], status: pid 89784 exit 1, stderr: "stderr"'
28
+ #
29
+ # @param result [Git::CommandLineResult] the result of the git command including
30
+ # the git command, status, stdout, and stderr
31
+ #
32
+ def initialize(result)
33
+ @result = result
34
+ super()
35
+ end
36
+
37
+ # The human readable representation of this error
38
+ #
39
+ # @example
40
+ # error.to_s #=> '["git", "status"], status: pid 89784 exit 1, stderr: "stderr"'
41
+ #
42
+ # @return [String]
43
+ #
44
+ def to_s = <<~MESSAGE.chomp
45
+ #{result.git_cmd}, status: #{result.status}, stderr: #{result.stderr.inspect}
46
+ MESSAGE
47
+
48
+ # @attribute [r] result
49
+ #
50
+ # The result of the git command including the git command and its status and output
51
+ #
52
+ # @example
53
+ # error.result #=> #<Git::CommandLineResult:0x00000001046bd488 ...>
54
+ #
55
+ # @return [Git::CommandLineResult]
56
+ #
57
+ attr_reader :result
58
+ end
59
+ 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/error.rb ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ # Base class for all custom git module errors
5
+ #
6
+ class Error < StandardError; end
7
+ end
@@ -1,51 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'git/git_execute_error'
3
+ require_relative 'command_line_error'
4
4
 
5
5
  module Git
6
- # This error is raised when a git command fails
6
+ # This error is raised when a git command returns a non-zero exitstatus
7
7
  #
8
8
  # The git command executed, status, stdout, and stderr are available from this
9
- # object. The #message includes the git command, the status of the process, and
10
- # the stderr of the process.
9
+ # object.
11
10
  #
12
11
  # @api public
13
12
  #
14
- class FailedError < Git::GitExecuteError
15
- # Create a FailedError object
16
- #
17
- # @example
18
- # `exit 1` # set $? appropriately for this example
19
- # result = Git::CommandLineResult.new(%w[git status], $?, 'stdout', 'stderr')
20
- # error = Git::FailedError.new(result)
21
- # error.message #=>
22
- # "[\"git\", \"status\"]\nstatus: pid 89784 exit 1\nstderr: \"stderr\""
23
- #
24
- # @param result [Git::CommandLineResult] the result of the git command including
25
- # the git command, status, stdout, and stderr
26
- #
27
- def initialize(result)
28
- super("#{result.git_cmd}\nstatus: #{result.status}\nstderr: #{result.stderr.inspect}")
29
- @result = result
30
- end
31
-
32
- # @attribute [r] result
33
- #
34
- # The result of the git command including the git command and its status and output
35
- #
36
- # @example
37
- # `exit 1` # set $? appropriately for this example
38
- # result = Git::CommandLineResult.new(%w[git status], $?, 'stdout', 'stderr')
39
- # error = Git::FailedError.new(result)
40
- # error.result #=>
41
- # #<Git::CommandLineResult:0x00000001046bd488
42
- # @git_cmd=["git", "status"],
43
- # @status=#<Process::Status: pid 89784 exit 1>,
44
- # @stderr="stderr",
45
- # @stdout="stdout">
46
- #
47
- # @return [Git::CommandLineResult]
48
- #
49
- attr_reader :result
50
- end
13
+ class FailedError < Git::CommandLineError; end
51
14
  end
@@ -1,7 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'error'
4
+
3
5
  module Git
4
6
  # This error is raised when a git command fails
5
7
  #
6
- class GitExecuteError < StandardError; end
8
+ # This error class is used as an alias for Git::Error for backwards compatibility.
9
+ # It is recommended to use Git::Error directly.
10
+ #
11
+ # @deprecated Use Git::Error instead
12
+ #
13
+ GitExecuteError = Git::Error
7
14
  end
data/lib/git/lib.rb CHANGED
@@ -115,7 +115,7 @@ module Git
115
115
  arr_opts << repository_url
116
116
  arr_opts << clone_dir
117
117
 
118
- command('clone', *arr_opts)
118
+ command('clone', *arr_opts, timeout: opts[:timeout])
119
119
 
120
120
  return_base_opts_from_clone(clone_dir, opts)
121
121
  end
@@ -1191,8 +1191,48 @@ module Git
1191
1191
  Git::CommandLine.new(env_overrides, Git::Base.config.binary_path, global_opts, @logger)
1192
1192
  end
1193
1193
 
1194
- def command(*args, out: nil, err: nil, normalize: true, chomp: true, merge: false, chdir: nil)
1195
- result = command_line.run(*args, out: out, err: err, normalize: normalize, chomp: chomp, merge: merge, chdir: chdir)
1194
+ # Runs a git command and returns the output
1195
+ #
1196
+ # @param args [Array] the git command to run and its arguments
1197
+ #
1198
+ # This should exclude the 'git' command itself and global options.
1199
+ #
1200
+ # For example, to run `git log --pretty=oneline`, you would pass `['log',
1201
+ # '--pretty=oneline']`
1202
+ #
1203
+ # @param out [String, nil] the path to a file or an IO to write the command's
1204
+ # stdout to
1205
+ #
1206
+ # @param err [String, nil] the path to a file or an IO to write the command's
1207
+ # stdout to
1208
+ #
1209
+ # @param normalize [Boolean] true to normalize the output encoding
1210
+ #
1211
+ # @param chomp [Boolean] true to remove trailing newlines from the output
1212
+ #
1213
+ # @param merge [Boolean] true to merge stdout and stderr
1214
+ #
1215
+ # @param chdir [String, nil] the directory to run the command in
1216
+ #
1217
+ # @param timeout [Numeric, nil] the maximum time to wait for the command to
1218
+ # complete
1219
+ #
1220
+ # @see Git::CommandLine#run
1221
+ #
1222
+ # @return [String] the command's stdout (or merged stdout and stderr if `merge`
1223
+ # is true)
1224
+ #
1225
+ # @raise [Git::GitExecuteError] if the command fails
1226
+ #
1227
+ # The exception's `result` attribute is a {Git::CommandLineResult} which will
1228
+ # contain the result of the command including the exit status, stdout, and
1229
+ # stderr.
1230
+ #
1231
+ # @api private
1232
+ #
1233
+ def command(*args, out: nil, err: nil, normalize: true, chomp: true, merge: false, chdir: nil, timeout: nil)
1234
+ timeout = timeout || Git.config.timeout
1235
+ result = command_line.run(*args, out: out, err: err, normalize: normalize, chomp: chomp, merge: merge, chdir: chdir, timeout: timeout)
1196
1236
  result.stdout
1197
1237
  end
1198
1238
 
@@ -1,50 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'git/git_execute_error'
3
+ require_relative 'command_line_error'
4
4
 
5
5
  module Git
6
6
  # This error is raised when a git command exits because of an uncaught signal
7
7
  #
8
8
  # The git command executed, status, stdout, and stderr are available from this
9
- # object. The #message includes the git command, the status of the process, and
10
- # the stderr of the process.
9
+ # object.
11
10
  #
12
11
  # @api public
13
12
  #
14
- class SignaledError < Git::GitExecuteError
15
- # Create a SignaledError object
16
- #
17
- # @example
18
- # `kill -9 $$` # set $? appropriately for this example
19
- # result = Git::CommandLineResult.new(%w[git status], $?, '', "killed")
20
- # error = Git::SignaledError.new(result)
21
- # error.message #=>
22
- # "[\"git\", \"status\"]\nstatus: pid 88811 SIGKILL (signal 9)\nstderr: \"killed\""
23
- #
24
- # @param result [Git::CommandLineResult] the result of the git command including the git command, status, stdout, and stderr
25
- #
26
- def initialize(result)
27
- super("#{result.git_cmd}\nstatus: #{result.status}\nstderr: #{result.stderr.inspect}")
28
- @result = result
29
- end
30
-
31
- # @attribute [r] result
32
- #
33
- # The result of the git command including the git command, status, and output
34
- #
35
- # @example
36
- # `kill -9 $$` # set $? appropriately for this example
37
- # result = Git::CommandLineResult.new(%w[git status], $?, '', "killed")
38
- # error = Git::SignaledError.new(result)
39
- # error.result #=>
40
- # #<Git::CommandLineResult:0x000000010470f6e8
41
- # @git_cmd=["git", "status"],
42
- # @status=#<Process::Status: pid 88811 SIGKILL (signal 9)>,
43
- # @stderr="killed",
44
- # @stdout="">
45
- #
46
- # @return [Git::CommandLineResult]
47
- #
48
- attr_reader :result
49
- end
13
+ class SignaledError < Git::CommandLineError; end
50
14
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'signaled_error'
4
+
5
+ module Git
6
+ # This error is raised when a git command takes longer than the configured timeout
7
+ #
8
+ # The git command executed, status, stdout, and stderr, and the timeout duration
9
+ # are available from this object.
10
+ #
11
+ # result.status.timeout? will be `true`
12
+ #
13
+ # @api public
14
+ #
15
+ class TimeoutError < Git::SignaledError
16
+ # Create a TimeoutError object
17
+ #
18
+ # @example
19
+ # command = %w[sleep 10]
20
+ # timeout_duration = 1
21
+ # status = ProcessExecuter.spawn(*command, timeout: timeout_duration)
22
+ # result = Git::CommandLineResult.new(command, status, 'stdout', 'err output')
23
+ # error = Git::TimeoutError.new(result, timeout_duration)
24
+ # error.to_s #=> '["sleep", "10"], status: pid 70144 SIGKILL (signal 9), stderr: "err output", timed out after 1s'
25
+ #
26
+ # @param result [Git::CommandLineResult] the result of the git command including
27
+ # the git command, status, stdout, and stderr
28
+ #
29
+ # @param timeout_duration [Numeric] the amount of time the subprocess was allowed
30
+ # to run before being killed
31
+ #
32
+ def initialize(result, timeout_duration)
33
+ @timeout_duration = timeout_duration
34
+ super(result)
35
+ end
36
+
37
+ # The human readable representation of this error
38
+ #
39
+ # @example
40
+ # error.to_s #=> '["sleep", "10"], status: pid 88811 SIGKILL (signal 9), stderr: "err output", timed out after 1s'
41
+ #
42
+ # @return [String]
43
+ #
44
+ def to_s = <<~MESSAGE.chomp
45
+ #{super}, timed out after #{timeout_duration}s
46
+ MESSAGE
47
+
48
+ # The amount of time the subprocess was allowed to run before being killed
49
+ #
50
+ # @example
51
+ # `kill -9 $$` # set $? appropriately for this example
52
+ # result = Git::CommandLineResult.new(%w[git status], $?, '', "killed")
53
+ # error = Git::TimeoutError.new(result, 10)
54
+ # error.timeout_duration #=> 10
55
+ #
56
+ # @return [Numeric]
57
+ #
58
+ attr_reader :timeout_duration
59
+ end
60
+ end
data/lib/git/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Git
2
2
  # The current gem version
3
3
  # @return [String] the current gem version.
4
- VERSION='2.0.0.pre1'
4
+ VERSION='2.0.0.pre2'
5
5
  end
data/lib/git.rb CHANGED
@@ -7,11 +7,13 @@ require 'git/author'
7
7
  require 'git/base'
8
8
  require 'git/branch'
9
9
  require 'git/branches'
10
+ require 'git/command_line_error'
10
11
  require 'git/command_line_result'
11
12
  require 'git/command_line'
12
13
  require 'git/config'
13
14
  require 'git/diff'
14
15
  require 'git/encoding_utils'
16
+ require 'git/error'
15
17
  require 'git/escaped_path'
16
18
  require 'git/failed_error'
17
19
  require 'git/git_execute_error'
@@ -24,9 +26,9 @@ require 'git/remote'
24
26
  require 'git/repository'
25
27
  require 'git/signaled_error'
26
28
  require 'git/status'
27
- require 'git/signaled_error'
28
29
  require 'git/stash'
29
30
  require 'git/stashes'
31
+ require 'git/timeout_error'
30
32
  require 'git/url'
31
33
  require 'git/version'
32
34
  require 'git/working_directory'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.pre1
4
+ version: 2.0.0.pre2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Chacon and others
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-15 00:00:00.000000000 Z
11
+ date: 2024-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.7'
33
+ version: '1.1'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.7'
40
+ version: '1.1'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rchardet
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -190,10 +190,12 @@ files:
190
190
  - lib/git/branch.rb
191
191
  - lib/git/branches.rb
192
192
  - lib/git/command_line.rb
193
+ - lib/git/command_line_error.rb
193
194
  - lib/git/command_line_result.rb
194
195
  - lib/git/config.rb
195
196
  - lib/git/diff.rb
196
197
  - lib/git/encoding_utils.rb
198
+ - lib/git/error.rb
197
199
  - lib/git/escaped_path.rb
198
200
  - lib/git/failed_error.rb
199
201
  - lib/git/git_execute_error.rb
@@ -208,6 +210,7 @@ files:
208
210
  - lib/git/stash.rb
209
211
  - lib/git/stashes.rb
210
212
  - lib/git/status.rb
213
+ - lib/git/timeout_error.rb
211
214
  - lib/git/url.rb
212
215
  - lib/git/version.rb
213
216
  - lib/git/working_directory.rb
@@ -219,8 +222,8 @@ licenses:
219
222
  metadata:
220
223
  homepage_uri: http://github.com/ruby-git/ruby-git
221
224
  source_code_uri: http://github.com/ruby-git/ruby-git
222
- changelog_uri: https://rubydoc.info/gems/git/2.0.0.pre1/file/CHANGELOG.md
223
- documentation_uri: https://rubydoc.info/gems/git/2.0.0.pre1
225
+ changelog_uri: https://rubydoc.info/gems/git/2.0.0.pre2/file/CHANGELOG.md
226
+ documentation_uri: https://rubydoc.info/gems/git/2.0.0.pre2
224
227
  post_install_message:
225
228
  rdoc_options: []
226
229
  require_paths: