Skip to content

Commit 8da29c6

Browse files
authored
Revert "Add a timeout for git commands (#692)"
This reverts commit ab0f757.
1 parent f90985c commit 8da29c6

File tree

9 files changed

+25
-257
lines changed

9 files changed

+25
-257
lines changed

README.md

Lines changed: 0 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,6 @@
1111
[![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)
1212
[![Code Climate](https://codeclimate.com/github/ruby-git/ruby-git.png)](https://codeclimate.com/github/ruby-git/ruby-git)
1313

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-
2614
The [git gem](https://rubygems.org/gems/git) provides an API that can be used to
2715
create, read, and manipulate Git repositories by wrapping system calls to the `git`
2816
command line. The API can be used for working with Git in complex interactions
@@ -152,60 +140,6 @@ rescue Git::TimeoutError => e # Catch the more specific error first!
152140
puts "Git clone took too long and timed out #{e}"
153141
rescue Git::Error => e
154142
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
209143
end
210144
```
211145

bin/command_line_test

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,10 @@ require 'optparse'
3535
class CommandLineParser
3636
def initialize
3737
@option_parser = OptionParser.new
38-
@duration = 0
3938
define_options
4039
end
4140

42-
attr_reader :duration, :stdout, :stderr, :exitstatus, :signal
41+
attr_reader :stdout, :stderr, :exitstatus, :signal
4342

4443
# Parse the command line arguements returning the options
4544
#
@@ -85,7 +84,7 @@ class CommandLineParser
8584
option_parser.separator 'Options:'
8685
%i[
8786
define_help_option define_stdout_option define_stderr_option
88-
define_exitstatus_option define_signal_option define_duration_option
87+
define_exitstatus_option define_signal_option
8988
].each { |m| send(m) }
9089
end
9190

@@ -136,15 +135,6 @@ class CommandLineParser
136135
end
137136
end
138137

139-
# Define the duration option
140-
# @return [void]
141-
# @api private
142-
def define_duration_option
143-
option_parser.on('--duration=0', 'The number of seconds the command should take') do |duration|
144-
@duration = Integer(duration)
145-
end
146-
end
147-
148138
# Define the help option
149139
# @return [void]
150140
# @api private
@@ -186,6 +176,5 @@ options = CommandLineParser.new.parse(*ARGV)
186176

187177
STDOUT.puts options.stdout if options.stdout
188178
STDERR.puts options.stderr if options.stderr
189-
sleep options.duration unless options.duration.zero?
190179
Process.kill(options.signal, Process.pid) if options.signal
191180
exit(options.exitstatus) if options.exitstatus

git.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
2828
s.requirements = ['git 2.28.0 or greater']
2929

3030
s.add_runtime_dependency 'addressable', '~> 2.8'
31-
s.add_runtime_dependency 'process_executer', '~> 1.1'
31+
s.add_runtime_dependency 'process_executer', '~> 0.7'
3232
s.add_runtime_dependency 'rchardet', '~> 1.8'
3333

3434
s.add_development_dependency 'minitar', '~> 0.9'

lib/git.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,11 @@
77
require 'git/base'
88
require 'git/branch'
99
require 'git/branches'
10-
require 'git/command_line_error'
1110
require 'git/command_line_result'
1211
require 'git/command_line'
1312
require 'git/config'
1413
require 'git/diff'
1514
require 'git/encoding_utils'
16-
require 'git/error'
1715
require 'git/escaped_path'
1816
require 'git/failed_error'
1917
require 'git/git_execute_error'
@@ -26,6 +24,7 @@
2624
require 'git/repository'
2725
require 'git/signaled_error'
2826
require 'git/status'
27+
require 'git/signaled_error'
2928
require 'git/stash'
3029
require 'git/stashes'
3130
require 'git/timeout_error'

lib/git/command_line.rb

Lines changed: 9 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -166,30 +166,21 @@ def initialize(env, binary_path, global_opts, logger)
166166
# @param merge [Boolean] whether to merge stdout and stderr in the string returned
167167
# @param chdir [String] the directory to run the command in
168168
#
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-
#
176169
# @return [Git::CommandLineResult] the output of the command
177170
#
178171
# This result of running the command.
179172
#
180173
# @raise [ArgumentError] if `args` is not an array of strings
181174
# @raise [Git::SignaledError] if the command was terminated because of an uncaught signal
182175
# @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
185176
#
186-
def run(*args, out:, err:, normalize:, chomp:, merge:, chdir: nil, timeout: nil)
177+
def run(*args, out:, err:, normalize:, chomp:, merge:, chdir: nil)
187178
git_cmd = build_git_cmd(args)
188179
out ||= StringIO.new
189180
err ||= (merge ? out : StringIO.new)
190-
status = execute(git_cmd, out, err, chdir: (chdir || :not_set), timeout: timeout)
181+
status = execute(git_cmd, out, err, chdir: (chdir || :not_set))
191182

192-
process_result(git_cmd, status, out, err, normalize, chomp, timeout)
183+
process_result(git_cmd, status, out, err, normalize, chomp)
193184
end
194185

195186
private
@@ -267,24 +258,17 @@ def raise_pipe_error(git_cmd, pipe_name, pipe)
267258
#
268259
# @param cmd [Array<String>] the git command to execute
269260
# @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.
276261
#
277262
# @raise [Git::GitExecuteError] if an exception was raised while collecting subprocess output
278-
# @raise [Git::TimeoutError] if the command times out
279263
#
280-
# @return [ProcessExecuter::Status] the status of the completed subprocess
264+
# @return [Process::Status] the status of the completed subprocess
281265
#
282266
# @api private
283267
#
284-
def spawn(cmd, out_writers, err_writers, chdir:, timeout:)
268+
def spawn(cmd, out_writers, err_writers, chdir:)
285269
out_pipe = ProcessExecuter::MonitoredPipe.new(*out_writers, chunk_size: 10_000)
286270
err_pipe = ProcessExecuter::MonitoredPipe.new(*err_writers, chunk_size: 10_000)
287-
ProcessExecuter.spawn(env, *cmd, out: out_pipe, err: err_pipe, chdir: chdir, timeout: timeout)
271+
ProcessExecuter.spawn(env, *cmd, out: out_pipe, err: err_pipe, chdir: chdir)
288272
ensure
289273
out_pipe.close
290274
err_pipe.close
@@ -329,12 +313,11 @@ def writers(out, err)
329313
#
330314
# @api private
331315
#
332-
def process_result(git_cmd, status, out, err, normalize, chomp, timeout)
316+
def process_result(git_cmd, status, out, err, normalize, chomp)
333317
out_str, err_str = post_process_all([out, err], normalize, chomp)
334318
logger.info { "#{git_cmd} exited with status #{status}" }
335319
logger.debug { "stdout:\n#{out_str.inspect}\nstderr:\n#{err_str.inspect}" }
336320
Git::CommandLineResult.new(git_cmd, status, out_str, err_str).tap do |result|
337-
raise Git::TimeoutError.new(result, timeout) if status.timeout?
338321
raise Git::SignaledError.new(result) if status.signaled?
339322
raise Git::FailedError.new(result) unless status.success?
340323
end
@@ -346,23 +329,14 @@ def process_result(git_cmd, status, out, err, normalize, chomp, timeout)
346329
# @param out [#write] the object to write stdout to
347330
# @param err [#write] the object to write stderr to
348331
# @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
358332
#
359333
# @return [Git::CommandLineResult] the result of the command to return to the caller
360334
#
361335
# @api private
362336
#
363-
def execute(git_cmd, out, err, chdir:, timeout:)
337+
def execute(git_cmd, out, err, chdir:)
364338
out_writers, err_writers = writers(out, err)
365-
spawn(git_cmd, out_writers, err_writers, chdir: chdir, timeout: timeout)
339+
spawn(git_cmd, out_writers, err_writers, chdir: chdir)
366340
end
367341
end
368342
end

lib/git/config.rb

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@ module Git
22

33
class Config
44

5-
attr_writer :binary_path, :git_ssh, :timeout
5+
attr_writer :binary_path, :git_ssh
66

77
def initialize
88
@binary_path = nil
99
@git_ssh = nil
10-
@timeout = nil
1110
end
1211

1312
def binary_path
@@ -18,9 +17,6 @@ def git_ssh
1817
@git_ssh || ENV['GIT_SSH']
1918
end
2019

21-
def timeout
22-
@timeout || (ENV['GIT_TIMEOUT'] && ENV['GIT_TIMEOUT'].to_i)
23-
end
2420
end
2521

2622
end

lib/git/lib.rb

Lines changed: 3 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def clone(repository_url, directory, opts = {})
115115
arr_opts << repository_url
116116
arr_opts << clone_dir
117117

118-
command('clone', *arr_opts, timeout: opts[:timeout])
118+
command('clone', *arr_opts)
119119

120120
return_base_opts_from_clone(clone_dir, opts)
121121
end
@@ -1191,48 +1191,8 @@ def command_line
11911191
Git::CommandLine.new(env_overrides, Git::Base.config.binary_path, global_opts, @logger)
11921192
end
11931193

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)
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)
12361196
result.stdout
12371197
end
12381198

0 commit comments

Comments
 (0)