git 2.0.0.pre1 → 2.0.0.pre3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf201a54a634864ee66f8e43866215d0c133fba4d2169ac1e0da3e5aa7bb1a2f
4
- data.tar.gz: 38e2e3d0f2429fe458bca0218a49bd7d09707acd7d76da40cc8f9d7ce6fcfd74
3
+ metadata.gz: 0bd064fe363a807e4c9a77eaf61810f6fc41323ad1b60a57eb0379460f26e91c
4
+ data.tar.gz: 890bb2663ba5e3cc9b12f3dae403a019e3b7eb0ffb95ad2fdf5989073d53e807
5
5
  SHA512:
6
- metadata.gz: 03cb58e9fc52d517d1a95584dae5c99d15532446442bac8aa6383a81770d7cc439643cd8bea04e5ae93dda8e481936ed3deebe86166fef2c4b9cd0ca3cdc8580
7
- data.tar.gz: 2ca4de81d5a9359dc8f80d1a4ca218dafc472f5b9cb5a5b7f01c6633adbeaf8361c34e9f741147ae1948593779af9770508be35a3ef3590991e6830990a6355f
6
+ metadata.gz: bbd732fb4061c1b68500860218eb14248adc48d4e4ff77d9b2c19cd13de5cb54c9c1bf313949131c91c7e3a25b24d8d105abb485571e06b25415875a67ce4f94
7
+ data.tar.gz: 6bf02024406485e4150cc76073d859446e490a659e6a17dad1f0f04c9d0084bcb063ccbc12dc839baa1b20094e2a2dcaf800c744560c49eab29264b588d154fb
@@ -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,23 @@
5
5
 
6
6
  # Change Log
7
7
 
8
+ ## v2.0.0.pre3 (2024-03-15)
9
+
10
+ [Full Changelog](https://github.com/ruby-git/ruby-git/compare/v2.0.0.pre2..v2.0.0.pre3)
11
+
12
+ Changes since v2.0.0.pre2:
13
+
14
+ * 5d4b34e Allow allow_unrelated_histories option for Base#pull
15
+
16
+ ## v2.0.0.pre2 (2024-02-24)
17
+
18
+ [Full Changelog](https://github.com/ruby-git/ruby-git/compare/v2.0.0.pre1..v2.0.0.pre2)
19
+
20
+ Changes since v2.0.0.pre1:
21
+
22
+ * 023017b Add a timeout for git commands (#692)
23
+ * 8286ceb Refactor the Error heriarchy (#693)
24
+
8
25
  ## v2.0.0.pre1 (2024-01-15)
9
26
 
10
27
  [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'
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
@@ -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
@@ -1006,10 +1006,11 @@ module Git
1006
1006
  end
1007
1007
  end
1008
1008
 
1009
- def pull(remote = nil, branch = nil)
1009
+ def pull(remote = nil, branch = nil, opts = {})
1010
1010
  raise ArgumentError, "You must specify a remote if a branch is specified" if remote.nil? && !branch.nil?
1011
1011
 
1012
1012
  arr_opts = []
1013
+ arr_opts << '--allow-unrelated-histories' if opts[:allow_unrelated_histories]
1013
1014
  arr_opts << remote if remote
1014
1015
  arr_opts << branch if branch
1015
1016
  command('pull', *arr_opts)
@@ -1191,8 +1192,48 @@ module Git
1191
1192
  Git::CommandLine.new(env_overrides, Git::Base.config.binary_path, global_opts, @logger)
1192
1193
  end
1193
1194
 
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)
1195
+ # Runs a git command and returns the output
1196
+ #
1197
+ # @param args [Array] the git command to run and its arguments
1198
+ #
1199
+ # This should exclude the 'git' command itself and global options.
1200
+ #
1201
+ # For example, to run `git log --pretty=oneline`, you would pass `['log',
1202
+ # '--pretty=oneline']`
1203
+ #
1204
+ # @param out [String, nil] the path to a file or an IO to write the command's
1205
+ # stdout to
1206
+ #
1207
+ # @param err [String, nil] the path to a file or an IO to write the command's
1208
+ # stdout to
1209
+ #
1210
+ # @param normalize [Boolean] true to normalize the output encoding
1211
+ #
1212
+ # @param chomp [Boolean] true to remove trailing newlines from the output
1213
+ #
1214
+ # @param merge [Boolean] true to merge stdout and stderr
1215
+ #
1216
+ # @param chdir [String, nil] the directory to run the command in
1217
+ #
1218
+ # @param timeout [Numeric, nil] the maximum time to wait for the command to
1219
+ # complete
1220
+ #
1221
+ # @see Git::CommandLine#run
1222
+ #
1223
+ # @return [String] the command's stdout (or merged stdout and stderr if `merge`
1224
+ # is true)
1225
+ #
1226
+ # @raise [Git::GitExecuteError] if the command fails
1227
+ #
1228
+ # The exception's `result` attribute is a {Git::CommandLineResult} which will
1229
+ # contain the result of the command including the exit status, stdout, and
1230
+ # stderr.
1231
+ #
1232
+ # @api private
1233
+ #
1234
+ def command(*args, out: nil, err: nil, normalize: true, chomp: true, merge: false, chdir: nil, timeout: nil)
1235
+ timeout = timeout || Git.config.timeout
1236
+ result = command_line.run(*args, out: out, err: err, normalize: normalize, chomp: chomp, merge: merge, chdir: chdir, timeout: timeout)
1196
1237
  result.stdout
1197
1238
  end
1198
1239
 
@@ -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.pre3'
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.pre3
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-03-15 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.pre3/file/CHANGELOG.md
226
+ documentation_uri: https://rubydoc.info/gems/git/2.0.0.pre3
224
227
  post_install_message:
225
228
  rdoc_options: []
226
229
  require_paths: