git 5.0.0.beta.5 β 5.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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +520 -0
- data/README.md +53 -475
- data/UPGRADING.md +26 -0
- data/lib/git/branch.rb +5 -1
- data/lib/git/branch_info.rb +135 -76
- data/lib/git/branches.rb +1 -1
- data/lib/git/log.rb +12 -6
- data/lib/git/object.rb +5 -2
- data/lib/git/parsers/branch.rb +62 -64
- data/lib/git/parsers/remote.rb +162 -0
- data/lib/git/remote.rb +1 -22
- data/lib/git/remote_info.rb +146 -0
- data/lib/git/repository/branching.rb +57 -22
- data/lib/git/repository/committing.rb +25 -6
- data/lib/git/repository/context_helpers.rb +1 -1
- data/lib/git/repository/diffing.rb +2 -1
- data/lib/git/repository/factories.rb +10 -3
- data/lib/git/repository/merging.rb +5 -5
- data/lib/git/repository/remote_operations.rb +64 -8
- data/lib/git/repository/staging.rb +13 -4
- data/lib/git/repository/stashing.rb +1 -1
- data/lib/git/repository/status_operations.rb +1 -1
- data/lib/git/version.rb +1 -1
- data/lib/git.rb +8 -5
- data/redesign/branch_parse_refactor_plan.md +163 -0
- data/redesign/info_object_migration_plan.md +126 -0
- data/redesign/integration_test_analysis.md +521 -0
- data/redesign/remote_refactor_plan.md +164 -0
- metadata +10 -4
data/README.md
CHANGED
|
@@ -19,23 +19,17 @@ Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-%23FE5196?log
|
|
|
19
19
|
- [Install](#install)
|
|
20
20
|
- [Quick Start](#quick-start)
|
|
21
21
|
- [Examples](#examples)
|
|
22
|
-
- [Configuration](#configuration)
|
|
23
|
-
- [
|
|
24
|
-
- [
|
|
25
|
-
|
|
26
|
-
- [
|
|
27
|
-
- [Specifying And Handling Timeouts](#specifying-and-handling-timeouts)
|
|
22
|
+
- [Gem Configuration](#gem-configuration)
|
|
23
|
+
- [Git Configuration](#git-configuration)
|
|
24
|
+
- [Full API](#full-api)
|
|
25
|
+
- [Errors Raised by This Gem](#errors-raised-by-this-gem)
|
|
26
|
+
- [Specifying and Handling Timeouts](#specifying-and-handling-timeouts)
|
|
28
27
|
- [Deprecations](#deprecations)
|
|
29
|
-
- [Upgrading from v4.x to v5.0.0](#upgrading-from-v4x-to-v500)
|
|
30
28
|
- [Project Policies](#project-policies)
|
|
31
29
|
- [Ruby Version Support Policy](#ruby-version-support-policy)
|
|
32
30
|
- [Git Version Support Policy](#git-version-support-policy)
|
|
33
|
-
- [
|
|
34
|
-
- [2026-07-
|
|
35
|
-
- [2026-07-11: v5.0.0.beta.4 Released](#2026-07-11-v500beta4-released)
|
|
36
|
-
- [2026-06-26: v5.0.0.beta.3 Released](#2026-06-26-v500beta3-released)
|
|
37
|
-
- [2026-06-25: v5.0.0.beta.2 Released](#2026-06-25-v500beta2-released)
|
|
38
|
-
- [2026-06-04: v5.0.0.beta.1 Released](#2026-06-04-v500beta1-released)
|
|
31
|
+
- [Project Announcements](#project-announcements)
|
|
32
|
+
- [2026-07-28: v5.0.0 Released](#2026-07-28-v500-released)
|
|
39
33
|
- [2026-01-07: AI Policy Introduced](#2026-01-07-ai-policy-introduced)
|
|
40
34
|
- [2025-07-09: Architectural Redesign](#2025-07-09-architectural-redesign)
|
|
41
35
|
- [2025-07-07: We Now Use RuboCop](#2025-07-07-we-now-use-rubocop)
|
|
@@ -61,6 +55,10 @@ Methods that can be called on a repository object are documented in
|
|
|
61
55
|
|
|
62
56
|
## Install
|
|
63
57
|
|
|
58
|
+
This gem is a wrapper around the `git` command line, so a `git` executable (version
|
|
59
|
+
2.28.0 or greater) must be installed and on your `PATH`. See the [Git Version Support
|
|
60
|
+
Policy](#git-version-support-policy) for details.
|
|
61
|
+
|
|
64
62
|
Install the gem and add to the application's Gemfile by executing:
|
|
65
63
|
|
|
66
64
|
```shell
|
|
@@ -91,7 +89,7 @@ require 'git'
|
|
|
91
89
|
|
|
92
90
|
repo = Git.clone('https://github.com/ruby-git/ruby-git.git', 'ruby-git')
|
|
93
91
|
repo.status.changed.each { |f| puts "changed: #{f.path}" }
|
|
94
|
-
repo.log(5).each { |c| puts c.message }
|
|
92
|
+
repo.log(5).execute.each { |c| puts c.message }
|
|
95
93
|
```
|
|
96
94
|
|
|
97
95
|
Open an existing repo and commit:
|
|
@@ -117,27 +115,10 @@ repo.commit('initial commit')
|
|
|
117
115
|
|
|
118
116
|
## Examples
|
|
119
117
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
### Configuration
|
|
124
|
-
|
|
125
|
-
Configure the `git` command line:
|
|
118
|
+
These examples cover configuring the gem and git itself. For the full set of
|
|
119
|
+
repository operations, see [Full API](#full-api) below.
|
|
126
120
|
|
|
127
|
-
|
|
128
|
-
# Global config (in ~/.gitconfig)
|
|
129
|
-
entries = Git.config_list(global: true) # returns Array<Git::ConfigEntryInfo>
|
|
130
|
-
entry = Git.config_get('user.email', global: true) # returns Git::ConfigEntryInfo or nil
|
|
131
|
-
email = entry&.value # => "user@example.com" or nil
|
|
132
|
-
Git.config_set('user.email', 'user@example.com', global: true)
|
|
133
|
-
|
|
134
|
-
# Repository config
|
|
135
|
-
repo = Git.open('path/to/repo')
|
|
136
|
-
entries = repo.config_list # returns Array<Git::ConfigEntryInfo>
|
|
137
|
-
entry = repo.config_get('user.email') # returns Git::ConfigEntryInfo or nil
|
|
138
|
-
email = entry&.value # => "anotheruser@example.com" or nil
|
|
139
|
-
repo.config_set('user.email', 'anotheruser@example.com')
|
|
140
|
-
```
|
|
121
|
+
### Gem Configuration
|
|
141
122
|
|
|
142
123
|
Configure the git gem:
|
|
143
124
|
|
|
@@ -179,326 +160,36 @@ git = Git.init('new-repo', git_ssh: 'ssh -i /path/to/private_key')
|
|
|
179
160
|
This is especially useful in multi-threaded applications where different repositories
|
|
180
161
|
require different SSH credentials.
|
|
181
162
|
|
|
182
|
-
###
|
|
183
|
-
|
|
184
|
-
Here are the operations that need read permission only:
|
|
185
|
-
|
|
186
|
-
```ruby
|
|
187
|
-
repo = Git.open(working_dir, :log => Logger.new(STDOUT))
|
|
188
|
-
|
|
189
|
-
repo.index # Pathname to the index file
|
|
190
|
-
repo.index.readable? # check if index is readable
|
|
191
|
-
repo.index.writable? # check if index is writable
|
|
192
|
-
repo.repo # Pathname to the .git directory
|
|
193
|
-
repo.dir # Pathname to the working directory
|
|
194
|
-
|
|
195
|
-
# ls-tree with recursion into subtrees (list files)
|
|
196
|
-
repo.ls_tree("HEAD", recursive: true)
|
|
197
|
-
|
|
198
|
-
# log - returns a Git::Log object, which is an Enumerator of Git::Commit objects
|
|
199
|
-
# default configuration returns a max of 30 commits
|
|
200
|
-
repo.log
|
|
201
|
-
repo.log(200) # 200 most recent commits
|
|
202
|
-
repo.log.since('2 weeks ago') # default count of commits since 2 weeks ago.
|
|
203
|
-
repo.log(200).since('2 weeks ago') # commits since 2 weeks ago, limited to 200.
|
|
204
|
-
repo.log.between('v2.5', 'v2.6')
|
|
205
|
-
repo.log.each {|l| puts l.sha }
|
|
206
|
-
repo.gblob('v2.5:Makefile').log.since('2 weeks ago')
|
|
207
|
-
|
|
208
|
-
repo.object('HEAD^').to_s # git show / git rev-parse
|
|
209
|
-
repo.object('HEAD^').contents
|
|
210
|
-
repo.object('v2.5:Makefile').size
|
|
211
|
-
repo.object('v2.5:Makefile').sha
|
|
212
|
-
|
|
213
|
-
repo.gtree(treeish)
|
|
214
|
-
repo.gblob(treeish)
|
|
215
|
-
repo.gcommit(treeish)
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
commit = repo.gcommit('1cc8667014381')
|
|
219
|
-
|
|
220
|
-
commit.gtree
|
|
221
|
-
commit.parent.sha
|
|
222
|
-
commit.parents.size
|
|
223
|
-
commit.author.name
|
|
224
|
-
commit.author.email
|
|
225
|
-
commit.author.date.strftime("%m-%d-%y")
|
|
226
|
-
commit.committer.name
|
|
227
|
-
commit.date.strftime("%m-%d-%y")
|
|
228
|
-
commit.message
|
|
229
|
-
|
|
230
|
-
tree = repo.gtree("HEAD^{tree}")
|
|
231
|
-
|
|
232
|
-
tree.blobs
|
|
233
|
-
tree.subtrees
|
|
234
|
-
tree.children # blobs and subtrees
|
|
235
|
-
|
|
236
|
-
repo.rev_parse('v2.0.0:README.md')
|
|
237
|
-
|
|
238
|
-
repo.branches # returns Git::Branch objects
|
|
239
|
-
repo.branches.local
|
|
240
|
-
repo.current_branch
|
|
241
|
-
repo.branches.remote
|
|
242
|
-
repo.branches[:main].gcommit
|
|
243
|
-
repo.branches['origin/main'].gcommit
|
|
244
|
-
|
|
245
|
-
repo.grep('hello') # implies HEAD
|
|
246
|
-
repo.blob('v2.5:Makefile').grep('hello')
|
|
247
|
-
repo.tag('v2.5').grep('hello', 'docs/')
|
|
248
|
-
repo.describe()
|
|
249
|
-
repo.describe('0djf2aa')
|
|
250
|
-
repo.describe('HEAD', {:all => true, :tags => true})
|
|
251
|
-
|
|
252
|
-
repo.diff(commit1, commit2).size
|
|
253
|
-
repo.diff(commit1, commit2).stats
|
|
254
|
-
repo.diff(commit1, commit2).name_status
|
|
255
|
-
repo.gtree('v2.5').diff('v2.6').insertions
|
|
256
|
-
repo.diff('gitsearch1', 'v2.5').path('lib/')
|
|
257
|
-
repo.diff('gitsearch1', 'v2.5').path('lib/', 'docs/', 'README.md') # multiple paths
|
|
258
|
-
repo.diff('gitsearch1', repo.gtree('v2.5'))
|
|
259
|
-
repo.diff('gitsearch1', 'v2.5').path('docs/').patch
|
|
260
|
-
repo.gtree('v2.5').diff('v2.6').patch
|
|
261
|
-
|
|
262
|
-
repo.gtree('v2.5').diff('v2.6').each do |file_diff|
|
|
263
|
-
puts file_diff.path
|
|
264
|
-
puts file_diff.patch
|
|
265
|
-
puts file_diff.blob(:src).contents
|
|
266
|
-
end
|
|
267
|
-
|
|
268
|
-
repo.worktrees # returns Git::Worktree objects
|
|
269
|
-
repo.worktrees.count
|
|
270
|
-
repo.worktrees.each do |worktree|
|
|
271
|
-
worktree.dir
|
|
272
|
-
worktree.gcommit
|
|
273
|
-
worktree.to_s
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
# Check repository integrity with fsck
|
|
277
|
-
result = repo.fsck
|
|
278
|
-
result.dangling.each { |obj| puts "dangling #{obj.type}: #{obj.sha}" }
|
|
279
|
-
result.missing.each { |obj| puts "missing #{obj.type}: #{obj.sha}" }
|
|
280
|
-
|
|
281
|
-
# Check if repository has any issues
|
|
282
|
-
puts "Repository is clean" if result.empty?
|
|
283
|
-
|
|
284
|
-
# fsck with options
|
|
285
|
-
result = repo.fsck(unreachable: true, strict: true)
|
|
286
|
-
|
|
287
|
-
# Suppress dangling object output
|
|
288
|
-
result = repo.fsck(dangling: false)
|
|
289
|
-
|
|
290
|
-
repo.config_get('user.name')&.value # returns 'Scott Chacon'
|
|
291
|
-
repo.config_list # returns Array<Git::ConfigEntryInfo>
|
|
292
|
-
|
|
293
|
-
# Configuration can be set when cloning using the :config option.
|
|
294
|
-
# This option can be an single configuration String or an Array
|
|
295
|
-
# if multiple config items need to be set.
|
|
296
|
-
#
|
|
297
|
-
repo = Git.clone(
|
|
298
|
-
git_uri, destination_path,
|
|
299
|
-
:config => [
|
|
300
|
-
'core.sshCommand=ssh -i /home/user/.ssh/id_rsa',
|
|
301
|
-
'submodule.recurse=true'
|
|
302
|
-
]
|
|
303
|
-
)
|
|
304
|
-
|
|
305
|
-
repo.tags # returns array of Git::Tag objects
|
|
306
|
-
|
|
307
|
-
repo.show()
|
|
308
|
-
repo.show('HEAD')
|
|
309
|
-
repo.show('v2.8', 'README.md')
|
|
310
|
-
|
|
311
|
-
Git.ls_remote('https://github.com/ruby-git/ruby-git.git') # returns a hash containing the available references of the repo.
|
|
312
|
-
Git.ls_remote('/path/to/local/repo')
|
|
313
|
-
Git.ls_remote() # same as Git.ls_remote('.')
|
|
314
|
-
|
|
315
|
-
Git.default_branch('https://github.com/ruby-git/ruby-git') #=> 'main'
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
### Write Operations
|
|
163
|
+
### Git Configuration
|
|
319
164
|
|
|
320
|
-
|
|
165
|
+
Read and set `git` configuration values (via `git config`):
|
|
321
166
|
|
|
322
167
|
```ruby
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
)
|
|
329
|
-
|
|
330
|
-
# Clone from a git url
|
|
331
|
-
git_url = 'https://github.com/ruby-git/ruby-git.git'
|
|
332
|
-
repo = Git.clone(git_url)
|
|
333
|
-
|
|
334
|
-
# Clone into /tmp/clone/ruby-git-clean
|
|
335
|
-
name = 'ruby-git-clean'
|
|
336
|
-
path = '/tmp/clone'
|
|
337
|
-
repo = Git.clone(git_url, name, :path => path)
|
|
338
|
-
repo.dir #=> /tmp/clone/ruby-git-clean
|
|
339
|
-
|
|
340
|
-
repo.config_set('user.name', 'Scott Chacon')
|
|
341
|
-
repo.config_set('user.email', 'email@email.com')
|
|
342
|
-
|
|
343
|
-
# Clone can take a filter to tell the serve to send a partial clone
|
|
344
|
-
repo = Git.clone(git_url, name, :path => path, :filter => 'tree:0')
|
|
345
|
-
|
|
346
|
-
# Clone can control single-branch behavior (nil default keeps current git behavior)
|
|
347
|
-
repo = Git.clone(git_url, name, :path => path, :depth => 1, :single_branch => false)
|
|
348
|
-
|
|
349
|
-
# Clone can take an optional logger
|
|
350
|
-
logger = Logger.new(STDOUT)
|
|
351
|
-
repo = Git.clone(git_url, 'my-repo', :log => logger)
|
|
352
|
-
|
|
353
|
-
repo.add # git add -- "."
|
|
354
|
-
repo.add(:all=>true) # git add --all -- "."
|
|
355
|
-
repo.add('file_path') # git add -- "file_path"
|
|
356
|
-
repo.add(['file_path_1', 'file_path_2']) # git add -- "file_path_1" "file_path_2"
|
|
357
|
-
|
|
358
|
-
repo.remove() # git rm -f -- "."
|
|
359
|
-
repo.remove('file.txt') # git rm -f -- "file.txt"
|
|
360
|
-
repo.remove(['file.txt', 'file2.txt']) # git rm -f -- "file.txt" "file2.txt"
|
|
361
|
-
repo.remove('file.txt', :recursive => true) # git rm -f -r -- "file.txt"
|
|
362
|
-
repo.remove('file.txt', :cached => true) # git rm -f --cached -- "file.txt"
|
|
363
|
-
|
|
364
|
-
repo.commit('message')
|
|
365
|
-
repo.commit_all('message')
|
|
366
|
-
|
|
367
|
-
# Sign a commit using the gpg key configured in the user.signingkey config setting
|
|
368
|
-
repo.config_set('user.signingkey', '0A46826A')
|
|
369
|
-
repo.commit('message', gpg_sign: true)
|
|
370
|
-
|
|
371
|
-
# Sign a commit using a specified gpg key
|
|
372
|
-
key_id = '0A46826A'
|
|
373
|
-
repo.commit('message', gpg_sign: key_id)
|
|
374
|
-
|
|
375
|
-
# Skip signing a commit (overriding any global gpgsign setting)
|
|
376
|
-
repo.commit('message', no_gpg_sign: true)
|
|
377
|
-
|
|
378
|
-
repo = Git.clone(git_url, 'myrepo')
|
|
379
|
-
repo.chdir do
|
|
380
|
-
File.write('test-file', 'blahblahblah')
|
|
381
|
-
repo.status.changed.each do |file|
|
|
382
|
-
puts file.blob(:index).contents
|
|
383
|
-
end
|
|
384
|
-
end
|
|
385
|
-
|
|
386
|
-
repo.reset # defaults to HEAD
|
|
387
|
-
repo.reset_hard(Git::Commit)
|
|
388
|
-
|
|
389
|
-
repo.branch('new_branch') # creates new or fetches existing
|
|
390
|
-
repo.branch('new_branch').checkout
|
|
391
|
-
repo.branch('new_branch').delete
|
|
392
|
-
repo.branch('existing_branch').checkout
|
|
393
|
-
repo.branch('main').contains?('existing_branch')
|
|
394
|
-
|
|
395
|
-
# delete remote branch
|
|
396
|
-
repo.push('origin', 'remote_branch_name', force: true, delete: true)
|
|
397
|
-
|
|
398
|
-
repo.checkout('new_branch')
|
|
399
|
-
repo.checkout('new_branch', new_branch: true, start_point: 'main')
|
|
400
|
-
repo.checkout(repo.branch('new_branch'))
|
|
401
|
-
|
|
402
|
-
repo.branch(name).merge(branch2)
|
|
403
|
-
repo.branch(branch2).merge # merges HEAD with branch2
|
|
404
|
-
|
|
405
|
-
repo.branch(name).in_branch(message) { # add files } # auto-commits
|
|
406
|
-
repo.merge('new_branch')
|
|
407
|
-
repo.merge('new_branch', 'merge commit message', no_ff: true)
|
|
408
|
-
repo.merge('origin/remote_branch')
|
|
409
|
-
repo.merge(repo.branch('main'))
|
|
410
|
-
repo.merge([branch1, branch2])
|
|
411
|
-
|
|
412
|
-
repo.merge_base('branch1', 'branch2')
|
|
413
|
-
|
|
414
|
-
r = repo.remote_add(name, uri) # Git::Remote
|
|
415
|
-
r = repo.remote_add(name, other_repo) # Git::Remote (other_repo is a Git::Repository instance)
|
|
416
|
-
|
|
417
|
-
repo.remotes # array of Git::Remotes
|
|
418
|
-
repo.remote(name).fetch
|
|
419
|
-
repo.remote(name).remove
|
|
420
|
-
repo.remote(name).merge
|
|
421
|
-
repo.remote(name).merge(branch)
|
|
422
|
-
|
|
423
|
-
repo.remote_set_branches('origin', '*', add: true) # append additional fetch refspecs
|
|
424
|
-
repo.remote_set_branches('origin', 'feature', 'release/*') # replace fetch refspecs
|
|
425
|
-
|
|
426
|
-
repo.fetch
|
|
427
|
-
repo.fetch(repo.remotes.first)
|
|
428
|
-
repo.fetch('origin', {:ref => 'some/ref/head'} )
|
|
429
|
-
repo.fetch(all: true, force: true, depth: 2)
|
|
430
|
-
repo.fetch('origin', {:'update-head-ok' => true})
|
|
431
|
-
|
|
432
|
-
repo.pull
|
|
433
|
-
repo.pull(Git::Repo, Git::Branch) # fetch and a merge
|
|
434
|
-
|
|
435
|
-
repo.tag_add('tag_name') # returns Git::Object::Tag
|
|
436
|
-
repo.tag_add('tag_name', 'object_reference')
|
|
437
|
-
repo.tag_add('tag_name', 'object_reference', {:options => 'here'})
|
|
438
|
-
repo.tag_add('tag_name', {:options => 'here'})
|
|
439
|
-
|
|
440
|
-
repo.tag_delete('tag_name')
|
|
441
|
-
|
|
442
|
-
repo.repack
|
|
443
|
-
|
|
444
|
-
repo.push
|
|
445
|
-
repo.push(repo.remote('name'))
|
|
446
|
-
|
|
447
|
-
# delete remote branch
|
|
448
|
-
repo.push('origin', 'remote_branch_name', force: true, delete: true)
|
|
449
|
-
|
|
450
|
-
# push all branches to remote at one time
|
|
451
|
-
repo.push('origin', all: true)
|
|
168
|
+
# Global config (in ~/.gitconfig)
|
|
169
|
+
entries = Git.config_list(global: true) # returns Array<Git::ConfigEntryInfo>
|
|
170
|
+
entry = Git.config_get('user.email', global: true) # returns Git::ConfigEntryInfo or nil
|
|
171
|
+
email = entry&.value # => "user@example.com" or nil
|
|
172
|
+
Git.config_set('user.email', 'user@example.com', global: true)
|
|
452
173
|
|
|
453
|
-
|
|
454
|
-
repo.
|
|
455
|
-
repo.
|
|
456
|
-
repo.
|
|
174
|
+
# Repository config
|
|
175
|
+
repo = Git.open('path/to/repo')
|
|
176
|
+
entries = repo.config_list # returns Array<Git::ConfigEntryInfo>
|
|
177
|
+
entry = repo.config_get('user.email') # returns Git::ConfigEntryInfo or nil
|
|
178
|
+
email = entry&.value # => "anotheruser@example.com" or nil
|
|
179
|
+
repo.config_set('user.email', 'anotheruser@example.com')
|
|
457
180
|
```
|
|
458
181
|
|
|
459
|
-
###
|
|
460
|
-
|
|
461
|
-
Some examples of more low-level index and tree operations
|
|
182
|
+
### Full API
|
|
462
183
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
# or #
|
|
471
|
-
t = repo.write_tree
|
|
472
|
-
c = repo.commit_tree(t, :message => 'message', :parents => [sha1, sha2])
|
|
184
|
+
Quick Start and the configuration sections above cover the most common setup. For
|
|
185
|
+
the complete set of operations β reading history, diffs, branches, remotes,
|
|
186
|
+
worktrees, staging, and low-level index and tree work β see the
|
|
187
|
+
[`Git::Repository`](https://rubydoc.info/gems/git/Git/Repository) reference. It
|
|
188
|
+
documents every method along with the object types each one returns (such as
|
|
189
|
+
`Git::Log`, `Git::Object::Commit`, `Git::Diff`, `Git::Branch`, and `Git::Worktree`),
|
|
190
|
+
so you can follow the links from a method to the full API of its result.
|
|
473
191
|
|
|
474
|
-
|
|
475
|
-
repo.update_ref(branch, c)
|
|
476
|
-
|
|
477
|
-
repo.with_temp_working do # new blank working directory
|
|
478
|
-
repo.checkout
|
|
479
|
-
repo.checkout(another_index)
|
|
480
|
-
repo.commit # commits to temp_index
|
|
481
|
-
end
|
|
482
|
-
end
|
|
483
|
-
|
|
484
|
-
repo.set_index('/path/to/index')
|
|
485
|
-
|
|
486
|
-
repo.with_index(path) do
|
|
487
|
-
# calls set_index, then switches back after
|
|
488
|
-
end
|
|
489
|
-
|
|
490
|
-
repo.with_working(dir) do
|
|
491
|
-
# calls set_working, then switches back after
|
|
492
|
-
end
|
|
493
|
-
|
|
494
|
-
repo.with_temp_working(dir) do
|
|
495
|
-
repo.checkout_index(:prefix => dir, :path_limiter => path)
|
|
496
|
-
# do file work
|
|
497
|
-
repo.commit # commits to index
|
|
498
|
-
end
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
## Errors Raised By This Gem
|
|
192
|
+
## Errors Raised by This Gem
|
|
502
193
|
|
|
503
194
|
The git gem will only raise an `ArgumentError` or an error that is a subclass of
|
|
504
195
|
`Git::Error`. It does not explicitly raise any other types of errors.
|
|
@@ -516,9 +207,7 @@ end
|
|
|
516
207
|
|
|
517
208
|
See [`Git::Error`](https://rubydoc.info/gems/git/Git/Error) for more information.
|
|
518
209
|
|
|
519
|
-
## Specifying
|
|
520
|
-
|
|
521
|
-
The timeout feature was added in git gem version `2.0.0`.
|
|
210
|
+
## Specifying and Handling Timeouts
|
|
522
211
|
|
|
523
212
|
A timeout for git command line operations can be set either globally or for specific
|
|
524
213
|
method calls that accept a `:timeout` parameter.
|
|
@@ -555,7 +244,7 @@ repo_url = 'https://github.com/ruby-git/ruby-git.git'
|
|
|
555
244
|
Git.clone(repo_url) # Use the global timeout value
|
|
556
245
|
Git.clone(repo_url, timeout: nil) # Also uses the global timeout value
|
|
557
246
|
Git.clone(repo_url, timeout: 0) # Do not enforce a timeout
|
|
558
|
-
Git.clone(repo_url, timeout: 10.5) # Timeout after 10.5 seconds raising Git::
|
|
247
|
+
Git.clone(repo_url, timeout: 10.5) # Timeout after 10.5 seconds raising Git::TimeoutError
|
|
559
248
|
```
|
|
560
249
|
|
|
561
250
|
If the command takes too long, a `Git::TimeoutError` will be raised:
|
|
@@ -608,11 +297,6 @@ needed for the upgrade.
|
|
|
608
297
|
For the full list of deprecated methods and their replacements, see
|
|
609
298
|
[UPGRADING.md](UPGRADING.md).
|
|
610
299
|
|
|
611
|
-
## Upgrading from v4.x to v5.0.0
|
|
612
|
-
|
|
613
|
-
v5.0.0 is a major release with breaking changes. See
|
|
614
|
-
[UPGRADING.md](UPGRADING.md) for a comprehensive migration guide.
|
|
615
|
-
|
|
616
300
|
## Project Policies
|
|
617
301
|
|
|
618
302
|
These documents set expectations for behavior, contribution workflows, AI-assisted
|
|
@@ -663,138 +347,32 @@ gem as new git features are adopted or as maintaining backward compatibility bec
|
|
|
663
347
|
impractical. Such changes will be clearly documented in the CHANGELOG and release
|
|
664
348
|
notes.
|
|
665
349
|
|
|
666
|
-
##
|
|
667
|
-
|
|
668
|
-
### 2026-07-13: v5.0.0.beta.5 Released
|
|
669
|
-
|
|
670
|
-
We have published
|
|
671
|
-
[`git v5.0.0.beta.5`](https://rubygems.org/gems/git/versions/5.0.0.beta.5) as our
|
|
672
|
-
fifth pre-release.
|
|
673
|
-
|
|
674
|
-
The main user-visible change is a temporary compatibility shim for users who
|
|
675
|
-
monkeypatch `Git::Base`. In v5.0.0, `Git::Base` has been replaced by
|
|
676
|
-
`Git::Repository`; the shim forwards methods defined on `Git::Base` to repository
|
|
677
|
-
instances and emits deprecation warnings so applications can migrate those patches
|
|
678
|
-
to `Git::Repository` before v6.0.0.
|
|
679
|
-
|
|
680
|
-
**To try the beta**, add the pre-release version to your `Gemfile`:
|
|
681
|
-
|
|
682
|
-
```ruby
|
|
683
|
-
gem 'git', '~> 5.0.0.beta'
|
|
684
|
-
```
|
|
685
|
-
|
|
686
|
-
Or install it directly:
|
|
687
|
-
|
|
688
|
-
```sh
|
|
689
|
-
gem install git --pre
|
|
690
|
-
```
|
|
691
|
-
|
|
692
|
-
The intent is full backward compatibility with v4.x, but given the size and scope of
|
|
693
|
-
the redesign, some incompatibilities may exist. Please give the latest beta a try and
|
|
694
|
-
[open an issue](https://github.com/ruby-git/ruby-git/issues) if you hit anything
|
|
695
|
-
unexpected β your feedback helps us ship a solid v5.0.0.
|
|
696
|
-
|
|
697
|
-
See [UPGRADING.md](UPGRADING.md) for a full list of deprecations and breaking changes.
|
|
698
|
-
|
|
699
|
-
### 2026-07-11: v5.0.0.beta.4 Released
|
|
700
|
-
|
|
701
|
-
The architectural redesign is **feature complete** and we have published
|
|
702
|
-
[`git v5.0.0.beta.4`](https://rubygems.org/gems/git/versions/5.0.0.beta.4) as our
|
|
703
|
-
fourth pre-release.
|
|
704
|
-
|
|
705
|
-
**To try the beta**, add the pre-release version to your `Gemfile`:
|
|
350
|
+
## Project Announcements
|
|
706
351
|
|
|
707
|
-
|
|
708
|
-
gem 'git', '~> 5.0.0.beta'
|
|
709
|
-
```
|
|
710
|
-
|
|
711
|
-
Or install it directly:
|
|
712
|
-
|
|
713
|
-
```sh
|
|
714
|
-
gem install git --pre
|
|
715
|
-
```
|
|
716
|
-
|
|
717
|
-
The intent is full backward compatibility with v4.x, but given the size and scope of
|
|
718
|
-
the redesign, some incompatibilities may exist. Please give the latest beta a try and
|
|
719
|
-
[open an issue](https://github.com/ruby-git/ruby-git/issues) if you hit anything
|
|
720
|
-
unexpected β your feedback helps us ship a solid v5.0.0.
|
|
721
|
-
|
|
722
|
-
See [UPGRADING.md](UPGRADING.md) for a full list of deprecations and breaking changes.
|
|
723
|
-
|
|
724
|
-
### 2026-06-26: v5.0.0.beta.3 Released
|
|
725
|
-
|
|
726
|
-
The architectural redesign is approximately **93% complete** and we have published
|
|
727
|
-
[`git v5.0.0.beta.3`](https://rubygems.org/gems/git/versions/5.0.0.beta.3) as our
|
|
728
|
-
third pre-release.
|
|
729
|
-
|
|
730
|
-
**To try the beta**, add the pre-release version to your `Gemfile`:
|
|
731
|
-
|
|
732
|
-
```ruby
|
|
733
|
-
gem 'git', '~> 5.0.0.beta'
|
|
734
|
-
```
|
|
735
|
-
|
|
736
|
-
Or install it directly:
|
|
737
|
-
|
|
738
|
-
```sh
|
|
739
|
-
gem install git --pre
|
|
740
|
-
```
|
|
741
|
-
|
|
742
|
-
The intent is full backward compatibility with v4.x, but given the size and scope of
|
|
743
|
-
the redesign, some incompatibilities may exist. Please give the latest beta a try and
|
|
744
|
-
[open an issue](https://github.com/ruby-git/ruby-git/issues) if you hit anything
|
|
745
|
-
unexpected β your feedback helps us ship a solid v5.0.0.
|
|
746
|
-
|
|
747
|
-
See [UPGRADING.md](UPGRADING.md) for a full list of deprecations and breaking changes.
|
|
352
|
+
### 2026-07-28: v5.0.0 Released
|
|
748
353
|
|
|
749
|
-
|
|
354
|
+
We have published [`git v5.0.0`](https://rubygems.org/gems/git/versions/5.0.0) β
|
|
355
|
+
the first stable release of the v5.x series, after five public beta releases
|
|
356
|
+
spanning JuneβJuly 2026.
|
|
750
357
|
|
|
751
|
-
|
|
752
|
-
[
|
|
753
|
-
second pre-release.
|
|
358
|
+
**v5.0.0 is a major release with breaking changes.** See
|
|
359
|
+
[UPGRADING.md](UPGRADING.md) for the complete migration guide.
|
|
754
360
|
|
|
755
|
-
|
|
361
|
+
To install:
|
|
756
362
|
|
|
757
363
|
```ruby
|
|
758
|
-
gem 'git', '~> 5.0
|
|
364
|
+
gem 'git', '~> 5.0'
|
|
759
365
|
```
|
|
760
366
|
|
|
761
|
-
Or
|
|
367
|
+
Or:
|
|
762
368
|
|
|
763
369
|
```sh
|
|
764
|
-
gem install git
|
|
765
|
-
```
|
|
766
|
-
|
|
767
|
-
The intent is full backward compatibility with v4.x, but given the size and scope of
|
|
768
|
-
the redesign, some incompatibilities may exist. Please give the latest beta a try and
|
|
769
|
-
[open an issue](https://github.com/ruby-git/ruby-git/issues) if you hit anything
|
|
770
|
-
unexpected β your feedback helps us ship a solid v5.0.0.
|
|
771
|
-
|
|
772
|
-
See [UPGRADING.md](UPGRADING.md) for a full list of deprecations and breaking changes.
|
|
773
|
-
|
|
774
|
-
### 2026-06-04: v5.0.0.beta.1 Released
|
|
775
|
-
|
|
776
|
-
The architectural redesign is approximately **65% complete** and we have published
|
|
777
|
-
[`git v5.0.0.beta.1`](https://rubygems.org/gems/git/versions/5.0.0.beta.1) as our
|
|
778
|
-
first pre-release.
|
|
779
|
-
|
|
780
|
-
**To try the beta**, add the pre-release version to your `Gemfile`:
|
|
781
|
-
|
|
782
|
-
```ruby
|
|
783
|
-
gem 'git', '~> 5.0.0.beta'
|
|
784
|
-
```
|
|
785
|
-
|
|
786
|
-
Or install it directly:
|
|
787
|
-
|
|
788
|
-
```sh
|
|
789
|
-
gem install git --pre
|
|
370
|
+
gem install git
|
|
790
371
|
```
|
|
791
372
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
unexpected β your feedback helps us ship a solid 5.0.0.
|
|
796
|
-
|
|
797
|
-
See [UPGRADING.md](UPGRADING.md) for a full list of deprecations and breaking changes.
|
|
373
|
+
Most v4.x code requires **no changes** β compatibility shims keep the old API
|
|
374
|
+
working while emitting deprecation warnings that tell you what to migrate before
|
|
375
|
+
v6.0.0.
|
|
798
376
|
|
|
799
377
|
### 2026-01-07: AI Policy Introduced
|
|
800
378
|
|
data/UPGRADING.md
CHANGED
|
@@ -93,6 +93,32 @@ require 'git'
|
|
|
93
93
|
`Git.open` (e.g., `repo.commit`, `repo.status`, `repo.add`) requires no
|
|
94
94
|
changes.
|
|
95
95
|
|
|
96
|
+
**Monkeypatching `Git::Base` is deprecated:** v5.x includes a temporary
|
|
97
|
+
compatibility shim for applications that define instance methods on `Git::Base`.
|
|
98
|
+
Those methods are made available on `Git::Repository` instances, but each method
|
|
99
|
+
definition emits a deprecation warning and this shim will be removed in v6.0.0.
|
|
100
|
+
|
|
101
|
+
Move custom repository helpers to an application-owned extension module and
|
|
102
|
+
include or prepend that module into `Git::Repository` during application setup:
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
# Deprecated in v5.x and will be removed in v6.0.0
|
|
106
|
+
module Git::Base
|
|
107
|
+
def worktree_clean?
|
|
108
|
+
status.changed.empty?
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# v5.x β keep the extension in application-owned code
|
|
113
|
+
module MyAppGitRepositoryExtensions
|
|
114
|
+
def worktree_clean?
|
|
115
|
+
status.changed.empty?
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
Git::Repository.include(MyAppGitRepositoryExtensions)
|
|
120
|
+
```
|
|
121
|
+
|
|
96
122
|
---
|
|
97
123
|
|
|
98
124
|
#### Return type of `Git.open`, `Git.clone`, `Git.init`, `Git.bare`
|
data/lib/git/branch.rb
CHANGED
|
@@ -386,6 +386,10 @@ module Git
|
|
|
386
386
|
# Matches full and short refnames, capturing an optional remote name and the
|
|
387
387
|
# branch name. Used internally to identify remote-tracking branches.
|
|
388
388
|
#
|
|
389
|
+
# @note This legacy string-constructor path does not resolve remote names
|
|
390
|
+
# containing `/`. Use {Git::Repository#branch_list} to build branch objects
|
|
391
|
+
# from remote-aware {Git::BranchInfo} values.
|
|
392
|
+
#
|
|
389
393
|
# @api private
|
|
390
394
|
#
|
|
391
395
|
BRANCH_NAME_REGEXP = %r{
|
|
@@ -424,9 +428,9 @@ module Git
|
|
|
424
428
|
# @return [nil]
|
|
425
429
|
#
|
|
426
430
|
def initialize_from_branch_info(branch_info)
|
|
427
|
-
@full = branch_info.refname
|
|
428
431
|
@name = branch_info.short_name
|
|
429
432
|
@remote = branch_info.remote_name ? Git::Remote.new(@base, branch_info.remote_name) : nil
|
|
433
|
+
@full = @remote ? "remotes/#{@remote.name}/#{@name}" : @name
|
|
430
434
|
end
|
|
431
435
|
|
|
432
436
|
# Initialize from a string name (legacy path for backward compatibility)
|