git 1.19.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/RELEASING.md CHANGED
@@ -7,64 +7,79 @@
7
7
 
8
8
  Releasing a new version of the `git` gem requires these steps:
9
9
 
10
- - [How to release a new git.gem](#how-to-release-a-new-gitgem)
11
- - [Install Prerequisites](#install-prerequisites)
12
- - [Prepare the Release](#prepare-the-release)
13
- - [Review and Merge the Release](#review-and-merge-the-release)
14
- - [Build and Release the Gem](#build-and-release-the-gem)
15
-
16
- These instructions use an example where:
17
-
18
- - The default branch is `master`
19
- - The current release version is `1.5.0`
20
- - You want to create a new *minor* release, `1.6.0`
10
+ * [Install Prerequisites](#install-prerequisites)
11
+ * [Determine the SemVer release type](#determine-the-semver-release-type)
12
+ * [Create the release](#create-the-release)
13
+ * [Review the CHANGELOG and release PR](#review-the-changelog-and-release-pr)
14
+ * [Manually merge the release PR](#manually-merge-the-release-pr)
15
+ * [Publish the git gem to RubyGems.org](#publish-the-git-gem-to-rubygemsorg)
21
16
 
22
17
  ## Install Prerequisites
23
18
 
24
19
  The following tools need to be installed in order to create the release:
25
20
 
26
- - [git](https://git-scm.com) is used to interact with the local and remote repositories
27
- - [gh](https://cli.github.com) is used to create the release and PR in GitHub
28
- - [Docker](https://www.docker.com) is used to run the script to create the release notes
21
+ * [create_githhub_release](https://github.com/main-branch/create_github_release) is used to create the release
22
+ * [git](https://git-scm.com) is used by `create-github-release` to interact with the local and remote repositories
23
+ * [gh](https://cli.github.com) is used by `create-github-release` to create the release and PR in GitHub
29
24
 
30
- On a Mac, these tools can be installed using [brew](https://brew.sh):
25
+ On a Mac, these tools can be installed using [gem](https://guides.rubygems.org/rubygems-basics/) and [brew](https://brew.sh):
31
26
 
32
27
  ```shell
28
+ $ gem install create_github_release
29
+ ...
33
30
  $ brew install git
34
31
  ...
35
32
  $ brew install gh
36
33
  ...
37
- $ brew install --cask docker
38
- ...
39
34
  $
40
35
  ```
41
36
 
42
- ## Prepare the Release
37
+ ## Determine the SemVer release type
43
38
 
44
- Bump the version, create release notes, tag the release and create a GitHub release and PR which can be used to review the release.
39
+ Determine the SemVer version increment that should be applied for the new release:
45
40
 
46
- Steps:
41
+ * `major`: when the release includes incompatible API or functional changes.
42
+ * `minor`: when the release adds functionality in a backward-compatible manner
43
+ * `patch`: when the release includes small user-facing changes that are
44
+ backward-compatible and do not introduce new functionality.
47
45
 
48
- - Check out the code with `git clone https://github.com/ruby-git/ruby-git ruby-git-v1.6.0 && cd ruby-git-v1.6.0`
49
- - Install development dependencies using bundle `bundle install`
50
- - Based upon the nature of the changes, decide on the type of release: `major`, `minor`, or `patch` (in this example we will use `minor`)
51
- - Run the release script `bundle exec create-github-release minor`
46
+ ## Create the release
52
47
 
53
- ## Review and Merge the Release
48
+ Create the release using the `create-github-release` command. If the release type
49
+ is `major`, the command is:
54
50
 
55
- Have the release PR approved and merge the changes into the `master` branch.
51
+ ```shell
52
+ create-github-release major
53
+ ```
56
54
 
57
- **IMPORTANT** DO NOT merge to the `master` branch using the GitHub UI. Instead use the instructions below.
55
+ Follow the directions given by the `create-github-release` command to finish the
56
+ release. Where the instructions given by the command differ than the instructions
57
+ below, follow the instructions given by the command.
58
58
 
59
- Steps:
59
+ ## Review the CHANGELOG and release PR
60
60
 
61
- - Get the release PR reviewed and approved in GitHub
62
- - Merge the changes with the command `git checkout master && git merge --ff-only v1.6.0 && git push`
61
+ The `create-github-release` command will output a link to the CHANGELOG and the PR
62
+ it created for the release. Review the CHANGELOG and have someone review and approve
63
+ the release PR.
63
64
 
64
- ## Build and Release the Gem
65
+ ## Manually merge the release PR
65
66
 
66
- Build the gem and publish it to [rubygems.org](https://rubygems.org/gems/git)
67
+ It is important to manually merge the PR so a separate merge commit can be avoided.
68
+ Use the commands output by the `create-github-release` which will looks like this
69
+ if you are creating a 2.0.0 release:
67
70
 
68
- Steps:
71
+ ```shell
72
+ git checkout master
73
+ git merge --ff-only release-v2.0.0
74
+ git push
75
+ ```
76
+
77
+ This will automatically close the release PR.
78
+
79
+ ## Publish the git gem to RubyGems.org
69
80
 
70
- - Build and release the gem using rake `bundle exec rake release`
81
+ Finally, publish the git gem to RubyGems.org using the following command:
82
+
83
+ ```shell
84
+ rake release:rubygem_push
85
+ ```
data/git.gemspec CHANGED
@@ -24,22 +24,22 @@ Gem::Specification.new do |s|
24
24
  s.metadata['documentation_uri'] = "https://rubydoc.info/gems/#{s.name}/#{s.version}"
25
25
 
26
26
  s.require_paths = ['lib']
27
- s.required_ruby_version = '>= 2.3'
28
- s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to?(:required_rubygems_version=)
29
- s.requirements = ['git 1.6.0.0, or greater']
27
+ s.required_ruby_version = '>= 3.0.0'
28
+ s.requirements = ['git 2.28.0 or greater']
30
29
 
30
+ s.add_runtime_dependency 'activesupport', '>= 5.0'
31
31
  s.add_runtime_dependency 'addressable', '~> 2.8'
32
+ s.add_runtime_dependency 'process_executer', '~> 1.1'
32
33
  s.add_runtime_dependency 'rchardet', '~> 1.8'
33
34
 
34
- s.add_development_dependency 'bump', '~> 0.10'
35
- s.add_development_dependency 'create_github_release', '~> 0.2'
35
+ s.add_development_dependency 'create_github_release', '~> 1.4'
36
36
  s.add_development_dependency 'minitar', '~> 0.9'
37
37
  s.add_development_dependency 'mocha', '~> 2.1'
38
- s.add_development_dependency 'rake', '~> 13.0'
39
- s.add_development_dependency 'test-unit', '~> 3.3'
38
+ s.add_development_dependency 'rake', '~> 13.1'
39
+ s.add_development_dependency 'test-unit', '~> 3.6'
40
40
 
41
41
  unless RUBY_PLATFORM == 'java'
42
- s.add_development_dependency 'redcarpet', '~> 3.5'
42
+ s.add_development_dependency 'redcarpet', '~> 3.6'
43
43
  s.add_development_dependency 'yard', '~> 0.9', '>= 0.9.28'
44
44
  s.add_development_dependency 'yardstick', '~> 0.9'
45
45
  end
data/lib/git/base.rb CHANGED
@@ -409,14 +409,27 @@ module Git
409
409
  self.lib.conflicts(&block)
410
410
  end
411
411
 
412
- # pulls the given branch from the given remote into the current branch
413
- #
414
- # @git.pull # pulls from origin/master
415
- # @git.pull('upstream') # pulls from upstream/master
416
- # @git.pull('upstream', 'develope') # pulls from upstream/develop
417
- #
418
- def pull(remote = nil, branch = nil)
419
- self.lib.pull(remote, branch)
412
+ # Pulls the given branch from the given remote into the current branch
413
+ #
414
+ # @param remote [String] the remote repository to pull from
415
+ # @param branch [String] the branch to pull from
416
+ # @param opts [Hash] options to pass to the pull command
417
+ #
418
+ # @option opts [Boolean] :allow_unrelated_histories (false) Merges histories of two projects that started their
419
+ # lives independently
420
+ # @example pulls from origin/master
421
+ # @git.pull
422
+ # @example pulls from upstream/master
423
+ # @git.pull('upstream')
424
+ # @example pulls from upstream/develop
425
+ # @git.pull('upstream', 'develop')
426
+ #
427
+ # @return [Void]
428
+ #
429
+ # @raise [Git::FailedError] if the pull fails
430
+ # @raise [ArgumentError] if a branch is given without a remote
431
+ def pull(remote = nil, branch = nil, opts = {})
432
+ self.lib.pull(remote, branch, opts)
420
433
  end
421
434
 
422
435
  # returns an array of Git:Remote objects
@@ -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