git 5.0.0.beta.4 → 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.
data/README.md CHANGED
@@ -19,22 +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
- - [Read Operations](#read-operations)
24
- - [Write Operations](#write-operations)
25
- - [Index and Tree Operations](#index-and-tree-operations)
26
- - [Errors Raised By This Gem](#errors-raised-by-this-gem)
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
- - [📢 Project Announcements 📢](#-project-announcements-)
34
- - [2026-07-11: v5.0.0.beta.4 Released](#2026-07-11-v500beta4-released)
35
- - [2026-06-26: v5.0.0.beta.3 Released](#2026-06-26-v500beta3-released)
36
- - [2026-06-25: v5.0.0.beta.2 Released](#2026-06-25-v500beta2-released)
37
- - [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)
38
33
  - [2026-01-07: AI Policy Introduced](#2026-01-07-ai-policy-introduced)
39
34
  - [2025-07-09: Architectural Redesign](#2025-07-09-architectural-redesign)
40
35
  - [2025-07-07: We Now Use RuboCop](#2025-07-07-we-now-use-rubocop)
@@ -60,6 +55,10 @@ Methods that can be called on a repository object are documented in
60
55
 
61
56
  ## Install
62
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
+
63
62
  Install the gem and add to the application's Gemfile by executing:
64
63
 
65
64
  ```shell
@@ -90,7 +89,7 @@ require 'git'
90
89
 
91
90
  repo = Git.clone('https://github.com/ruby-git/ruby-git.git', 'ruby-git')
92
91
  repo.status.changed.each { |f| puts "changed: #{f.path}" }
93
- repo.log(5).each { |c| puts c.message }
92
+ repo.log(5).execute.each { |c| puts c.message }
94
93
  ```
95
94
 
96
95
  Open an existing repo and commit:
@@ -116,27 +115,10 @@ repo.commit('initial commit')
116
115
 
117
116
  ## Examples
118
117
 
119
- Beyond the basics covered in Quick Start, these examples show the full range of
120
- options and variations for each operation.
121
-
122
- ### Configuration
118
+ These examples cover configuring the gem and git itself. For the full set of
119
+ repository operations, see [Full API](#full-api) below.
123
120
 
124
- Configure the `git` command line:
125
-
126
- ```ruby
127
- # Global config (in ~/.gitconfig)
128
- entries = Git.config_list(global: true) # returns Array<Git::ConfigEntryInfo>
129
- entry = Git.config_get('user.email', global: true) # returns Git::ConfigEntryInfo or nil
130
- email = entry&.value # => "user@example.com" or nil
131
- Git.config_set('user.email', 'user@example.com', global: true)
132
-
133
- # Repository config
134
- repo = Git.open('path/to/repo')
135
- entries = repo.config_list # returns Array<Git::ConfigEntryInfo>
136
- entry = repo.config_get('user.email') # returns Git::ConfigEntryInfo or nil
137
- email = entry&.value # => "anotheruser@example.com" or nil
138
- repo.config_set('user.email', 'anotheruser@example.com')
139
- ```
121
+ ### Gem Configuration
140
122
 
141
123
  Configure the git gem:
142
124
 
@@ -178,326 +160,36 @@ git = Git.init('new-repo', git_ssh: 'ssh -i /path/to/private_key')
178
160
  This is especially useful in multi-threaded applications where different repositories
179
161
  require different SSH credentials.
180
162
 
181
- ### Read Operations
182
-
183
- Here are the operations that need read permission only:
184
-
185
- ```ruby
186
- repo = Git.open(working_dir, :log => Logger.new(STDOUT))
187
-
188
- repo.index # Pathname to the index file
189
- repo.index.readable? # check if index is readable
190
- repo.index.writable? # check if index is writable
191
- repo.repo # Pathname to the .git directory
192
- repo.dir # Pathname to the working directory
193
-
194
- # ls-tree with recursion into subtrees (list files)
195
- repo.ls_tree("HEAD", recursive: true)
196
-
197
- # log - returns a Git::Log object, which is an Enumerator of Git::Commit objects
198
- # default configuration returns a max of 30 commits
199
- repo.log
200
- repo.log(200) # 200 most recent commits
201
- repo.log.since('2 weeks ago') # default count of commits since 2 weeks ago.
202
- repo.log(200).since('2 weeks ago') # commits since 2 weeks ago, limited to 200.
203
- repo.log.between('v2.5', 'v2.6')
204
- repo.log.each {|l| puts l.sha }
205
- repo.gblob('v2.5:Makefile').log.since('2 weeks ago')
206
-
207
- repo.object('HEAD^').to_s # git show / git rev-parse
208
- repo.object('HEAD^').contents
209
- repo.object('v2.5:Makefile').size
210
- repo.object('v2.5:Makefile').sha
211
-
212
- repo.gtree(treeish)
213
- repo.gblob(treeish)
214
- repo.gcommit(treeish)
215
-
216
-
217
- commit = repo.gcommit('1cc8667014381')
218
-
219
- commit.gtree
220
- commit.parent.sha
221
- commit.parents.size
222
- commit.author.name
223
- commit.author.email
224
- commit.author.date.strftime("%m-%d-%y")
225
- commit.committer.name
226
- commit.date.strftime("%m-%d-%y")
227
- commit.message
228
-
229
- tree = repo.gtree("HEAD^{tree}")
230
-
231
- tree.blobs
232
- tree.subtrees
233
- tree.children # blobs and subtrees
234
-
235
- repo.rev_parse('v2.0.0:README.md')
236
-
237
- repo.branches # returns Git::Branch objects
238
- repo.branches.local
239
- repo.current_branch
240
- repo.branches.remote
241
- repo.branches[:main].gcommit
242
- repo.branches['origin/main'].gcommit
243
-
244
- repo.grep('hello') # implies HEAD
245
- repo.blob('v2.5:Makefile').grep('hello')
246
- repo.tag('v2.5').grep('hello', 'docs/')
247
- repo.describe()
248
- repo.describe('0djf2aa')
249
- repo.describe('HEAD', {:all => true, :tags => true})
250
-
251
- repo.diff(commit1, commit2).size
252
- repo.diff(commit1, commit2).stats
253
- repo.diff(commit1, commit2).name_status
254
- repo.gtree('v2.5').diff('v2.6').insertions
255
- repo.diff('gitsearch1', 'v2.5').path('lib/')
256
- repo.diff('gitsearch1', 'v2.5').path('lib/', 'docs/', 'README.md') # multiple paths
257
- repo.diff('gitsearch1', repo.gtree('v2.5'))
258
- repo.diff('gitsearch1', 'v2.5').path('docs/').patch
259
- repo.gtree('v2.5').diff('v2.6').patch
260
-
261
- repo.gtree('v2.5').diff('v2.6').each do |file_diff|
262
- puts file_diff.path
263
- puts file_diff.patch
264
- puts file_diff.blob(:src).contents
265
- end
266
-
267
- repo.worktrees # returns Git::Worktree objects
268
- repo.worktrees.count
269
- repo.worktrees.each do |worktree|
270
- worktree.dir
271
- worktree.gcommit
272
- worktree.to_s
273
- end
274
-
275
- # Check repository integrity with fsck
276
- result = repo.fsck
277
- result.dangling.each { |obj| puts "dangling #{obj.type}: #{obj.sha}" }
278
- result.missing.each { |obj| puts "missing #{obj.type}: #{obj.sha}" }
279
-
280
- # Check if repository has any issues
281
- puts "Repository is clean" if result.empty?
282
-
283
- # fsck with options
284
- result = repo.fsck(unreachable: true, strict: true)
285
-
286
- # Suppress dangling object output
287
- result = repo.fsck(dangling: false)
288
-
289
- repo.config_get('user.name')&.value # returns 'Scott Chacon'
290
- repo.config_list # returns Array<Git::ConfigEntryInfo>
291
-
292
- # Configuration can be set when cloning using the :config option.
293
- # This option can be an single configuration String or an Array
294
- # if multiple config items need to be set.
295
- #
296
- repo = Git.clone(
297
- git_uri, destination_path,
298
- :config => [
299
- 'core.sshCommand=ssh -i /home/user/.ssh/id_rsa',
300
- 'submodule.recurse=true'
301
- ]
302
- )
303
-
304
- repo.tags # returns array of Git::Tag objects
305
-
306
- repo.show()
307
- repo.show('HEAD')
308
- repo.show('v2.8', 'README.md')
163
+ ### Git Configuration
309
164
 
310
- Git.ls_remote('https://github.com/ruby-git/ruby-git.git') # returns a hash containing the available references of the repo.
311
- Git.ls_remote('/path/to/local/repo')
312
- Git.ls_remote() # same as Git.ls_remote('.')
313
-
314
- Git.default_branch('https://github.com/ruby-git/ruby-git') #=> 'main'
315
- ```
316
-
317
- ### Write Operations
318
-
319
- And here are the operations that will need to write to your git repository.
165
+ Read and set `git` configuration values (via `git config`):
320
166
 
321
167
  ```ruby
322
- repo = Git.init # default is the current directory
323
- repo = Git.init('project')
324
- repo = Git.init(
325
- '/home/schacon/proj',
326
- { :repository => '/opt/git/proj.git', :index => '/tmp/index'}
327
- )
328
-
329
- # Clone from a git url
330
- git_url = 'https://github.com/ruby-git/ruby-git.git'
331
- repo = Git.clone(git_url)
332
-
333
- # Clone into /tmp/clone/ruby-git-clean
334
- name = 'ruby-git-clean'
335
- path = '/tmp/clone'
336
- repo = Git.clone(git_url, name, :path => path)
337
- repo.dir #=> /tmp/clone/ruby-git-clean
338
-
339
- repo.config_set('user.name', 'Scott Chacon')
340
- repo.config_set('user.email', 'email@email.com')
341
-
342
- # Clone can take a filter to tell the serve to send a partial clone
343
- repo = Git.clone(git_url, name, :path => path, :filter => 'tree:0')
344
-
345
- # Clone can control single-branch behavior (nil default keeps current git behavior)
346
- repo = Git.clone(git_url, name, :path => path, :depth => 1, :single_branch => false)
347
-
348
- # Clone can take an optional logger
349
- logger = Logger.new(STDOUT)
350
- repo = Git.clone(git_url, 'my-repo', :log => logger)
351
-
352
- repo.add # git add -- "."
353
- repo.add(:all=>true) # git add --all -- "."
354
- repo.add('file_path') # git add -- "file_path"
355
- repo.add(['file_path_1', 'file_path_2']) # git add -- "file_path_1" "file_path_2"
356
-
357
- repo.remove() # git rm -f -- "."
358
- repo.remove('file.txt') # git rm -f -- "file.txt"
359
- repo.remove(['file.txt', 'file2.txt']) # git rm -f -- "file.txt" "file2.txt"
360
- repo.remove('file.txt', :recursive => true) # git rm -f -r -- "file.txt"
361
- repo.remove('file.txt', :cached => true) # git rm -f --cached -- "file.txt"
362
-
363
- repo.commit('message')
364
- repo.commit_all('message')
365
-
366
- # Sign a commit using the gpg key configured in the user.signingkey config setting
367
- repo.config_set('user.signingkey', '0A46826A')
368
- repo.commit('message', gpg_sign: true)
369
-
370
- # Sign a commit using a specified gpg key
371
- key_id = '0A46826A'
372
- repo.commit('message', gpg_sign: key_id)
373
-
374
- # Skip signing a commit (overriding any global gpgsign setting)
375
- repo.commit('message', no_gpg_sign: true)
376
-
377
- repo = Git.clone(git_url, 'myrepo')
378
- repo.chdir do
379
- File.write('test-file', 'blahblahblah')
380
- repo.status.changed.each do |file|
381
- puts file.blob(:index).contents
382
- end
383
- end
384
-
385
- repo.reset # defaults to HEAD
386
- repo.reset_hard(Git::Commit)
387
-
388
- repo.branch('new_branch') # creates new or fetches existing
389
- repo.branch('new_branch').checkout
390
- repo.branch('new_branch').delete
391
- repo.branch('existing_branch').checkout
392
- repo.branch('main').contains?('existing_branch')
393
-
394
- # delete remote branch
395
- repo.push('origin', 'remote_branch_name', force: true, delete: true)
396
-
397
- repo.checkout('new_branch')
398
- repo.checkout('new_branch', new_branch: true, start_point: 'main')
399
- repo.checkout(repo.branch('new_branch'))
400
-
401
- repo.branch(name).merge(branch2)
402
- repo.branch(branch2).merge # merges HEAD with branch2
403
-
404
- repo.branch(name).in_branch(message) { # add files } # auto-commits
405
- repo.merge('new_branch')
406
- repo.merge('new_branch', 'merge commit message', no_ff: true)
407
- repo.merge('origin/remote_branch')
408
- repo.merge(repo.branch('main'))
409
- repo.merge([branch1, branch2])
410
-
411
- repo.merge_base('branch1', 'branch2')
412
-
413
- r = repo.remote_add(name, uri) # Git::Remote
414
- r = repo.remote_add(name, other_repo) # Git::Remote (other_repo is a Git::Repository instance)
415
-
416
- repo.remotes # array of Git::Remotes
417
- repo.remote(name).fetch
418
- repo.remote(name).remove
419
- repo.remote(name).merge
420
- repo.remote(name).merge(branch)
421
-
422
- repo.remote_set_branches('origin', '*', add: true) # append additional fetch refspecs
423
- repo.remote_set_branches('origin', 'feature', 'release/*') # replace fetch refspecs
424
-
425
- repo.fetch
426
- repo.fetch(repo.remotes.first)
427
- repo.fetch('origin', {:ref => 'some/ref/head'} )
428
- repo.fetch(all: true, force: true, depth: 2)
429
- repo.fetch('origin', {:'update-head-ok' => true})
430
-
431
- repo.pull
432
- repo.pull(Git::Repo, Git::Branch) # fetch and a merge
433
-
434
- repo.tag_add('tag_name') # returns Git::Object::Tag
435
- repo.tag_add('tag_name', 'object_reference')
436
- repo.tag_add('tag_name', 'object_reference', {:options => 'here'})
437
- repo.tag_add('tag_name', {:options => 'here'})
438
-
439
- repo.tag_delete('tag_name')
440
-
441
- repo.repack
442
-
443
- repo.push
444
- repo.push(repo.remote('name'))
445
-
446
- # delete remote branch
447
- repo.push('origin', 'remote_branch_name', force: true, delete: true)
448
-
449
- # push all branches to remote at one time
450
- 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)
451
173
 
452
- repo.worktree('/tmp/new_worktree').add
453
- repo.worktree('/tmp/new_worktree', 'branch1').add
454
- repo.worktree('/tmp/new_worktree').remove
455
- repo.worktrees.prune
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')
456
180
  ```
457
181
 
458
- ### Index and Tree Operations
459
-
460
- Some examples of more low-level index and tree operations
461
-
462
- ```ruby
463
- repo.with_temp_index do
464
-
465
- repo.read_tree(tree3) # calls self.index.read_tree
466
- repo.read_tree(tree1, :prefix => 'hi/')
467
-
468
- c = repo.commit_tree('message')
469
- # or #
470
- t = repo.write_tree
471
- c = repo.commit_tree(t, :message => 'message', :parents => [sha1, sha2])
472
-
473
- repo.branch('branch_name').update_ref(c)
474
- repo.update_ref(branch, c)
475
-
476
- repo.with_temp_working do # new blank working directory
477
- repo.checkout
478
- repo.checkout(another_index)
479
- repo.commit # commits to temp_index
480
- end
481
- end
182
+ ### Full API
482
183
 
483
- repo.set_index('/path/to/index')
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.
484
191
 
485
- repo.with_index(path) do
486
- # calls set_index, then switches back after
487
- end
488
-
489
- repo.with_working(dir) do
490
- # calls set_working, then switches back after
491
- end
492
-
493
- repo.with_temp_working(dir) do
494
- repo.checkout_index(:prefix => dir, :path_limiter => path)
495
- # do file work
496
- repo.commit # commits to index
497
- end
498
- ```
499
-
500
- ## Errors Raised By This Gem
192
+ ## Errors Raised by This Gem
501
193
 
502
194
  The git gem will only raise an `ArgumentError` or an error that is a subclass of
503
195
  `Git::Error`. It does not explicitly raise any other types of errors.
@@ -515,9 +207,7 @@ end
515
207
 
516
208
  See [`Git::Error`](https://rubydoc.info/gems/git/Git/Error) for more information.
517
209
 
518
- ## Specifying And Handling Timeouts
519
-
520
- The timeout feature was added in git gem version `2.0.0`.
210
+ ## Specifying and Handling Timeouts
521
211
 
522
212
  A timeout for git command line operations can be set either globally or for specific
523
213
  method calls that accept a `:timeout` parameter.
@@ -554,7 +244,7 @@ repo_url = 'https://github.com/ruby-git/ruby-git.git'
554
244
  Git.clone(repo_url) # Use the global timeout value
555
245
  Git.clone(repo_url, timeout: nil) # Also uses the global timeout value
556
246
  Git.clone(repo_url, timeout: 0) # Do not enforce a timeout
557
- Git.clone(repo_url, timeout: 10.5) # Timeout after 10.5 seconds raising Git::SignaledError
247
+ Git.clone(repo_url, timeout: 10.5) # Timeout after 10.5 seconds raising Git::TimeoutError
558
248
  ```
559
249
 
560
250
  If the command takes too long, a `Git::TimeoutError` will be raised:
@@ -607,11 +297,6 @@ needed for the upgrade.
607
297
  For the full list of deprecated methods and their replacements, see
608
298
  [UPGRADING.md](UPGRADING.md).
609
299
 
610
- ## Upgrading from v4.x to v5.0.0
611
-
612
- v5.0.0 is a major release with breaking changes. See
613
- [UPGRADING.md](UPGRADING.md) for a comprehensive migration guide.
614
-
615
300
  ## Project Policies
616
301
 
617
302
  These documents set expectations for behavior, contribution workflows, AI-assisted
@@ -662,107 +347,32 @@ gem as new git features are adopted or as maintaining backward compatibility bec
662
347
  impractical. Such changes will be clearly documented in the CHANGELOG and release
663
348
  notes.
664
349
 
665
- ## 📢 Project Announcements 📢
350
+ ## Project Announcements
666
351
 
667
- ### 2026-07-11: v5.0.0.beta.4 Released
352
+ ### 2026-07-28: v5.0.0 Released
668
353
 
669
- The architectural redesign is **feature complete** and we have published
670
- [`git v5.0.0.beta.4`](https://rubygems.org/gems/git/versions/5.0.0.beta.4) as our
671
- fourth pre-release.
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.
672
357
 
673
- **To try the beta**, add the pre-release version to your `Gemfile`:
674
-
675
- ```ruby
676
- gem 'git', '~> 5.0.0.beta'
677
- ```
678
-
679
- Or install it directly:
680
-
681
- ```sh
682
- gem install git --pre
683
- ```
358
+ **v5.0.0 is a major release with breaking changes.** See
359
+ [UPGRADING.md](UPGRADING.md) for the complete migration guide.
684
360
 
685
- The intent is full backward compatibility with v4.x, but given the size and scope of
686
- the redesign, some incompatibilities may exist. Please give the latest beta a try and
687
- [open an issue](https://github.com/ruby-git/ruby-git/issues) if you hit anything
688
- unexpected — your feedback helps us ship a solid v5.0.0.
689
-
690
- See [UPGRADING.md](UPGRADING.md) for a full list of deprecations and breaking changes.
691
-
692
- ### 2026-06-26: v5.0.0.beta.3 Released
693
-
694
- The architectural redesign is approximately **93% complete** and we have published
695
- [`git v5.0.0.beta.3`](https://rubygems.org/gems/git/versions/5.0.0.beta.3) as our
696
- third pre-release.
697
-
698
- **To try the beta**, add the pre-release version to your `Gemfile`:
699
-
700
- ```ruby
701
- gem 'git', '~> 5.0.0.beta'
702
- ```
703
-
704
- Or install it directly:
705
-
706
- ```sh
707
- gem install git --pre
708
- ```
709
-
710
- The intent is full backward compatibility with v4.x, but given the size and scope of
711
- the redesign, some incompatibilities may exist. Please give the latest beta a try and
712
- [open an issue](https://github.com/ruby-git/ruby-git/issues) if you hit anything
713
- unexpected — your feedback helps us ship a solid v5.0.0.
714
-
715
- See [UPGRADING.md](UPGRADING.md) for a full list of deprecations and breaking changes.
716
-
717
- ### 2026-06-25: v5.0.0.beta.2 Released
718
-
719
- The architectural redesign is approximately **90% complete** and we have published
720
- [`git v5.0.0.beta.2`](https://rubygems.org/gems/git/versions/5.0.0.beta.2) as our
721
- second pre-release.
722
-
723
- **To try the beta**, add the pre-release version to your `Gemfile`:
361
+ To install:
724
362
 
725
363
  ```ruby
726
- gem 'git', '~> 5.0.0.beta'
364
+ gem 'git', '~> 5.0'
727
365
  ```
728
366
 
729
- Or install it directly:
367
+ Or:
730
368
 
731
369
  ```sh
732
- gem install git --pre
733
- ```
734
-
735
- The intent is full backward compatibility with v4.x, but given the size and scope of
736
- the redesign, some incompatibilities may exist. Please give the latest beta a try and
737
- [open an issue](https://github.com/ruby-git/ruby-git/issues) if you hit anything
738
- unexpected — your feedback helps us ship a solid v5.0.0.
739
-
740
- See [UPGRADING.md](UPGRADING.md) for a full list of deprecations and breaking changes.
741
-
742
- ### 2026-06-04: v5.0.0.beta.1 Released
743
-
744
- The architectural redesign is approximately **65% complete** and we have published
745
- [`git v5.0.0.beta.1`](https://rubygems.org/gems/git/versions/5.0.0.beta.1) as our
746
- first pre-release.
747
-
748
- **To try the beta**, add the pre-release version to your `Gemfile`:
749
-
750
- ```ruby
751
- gem 'git', '~> 5.0.0.beta'
752
- ```
753
-
754
- Or install it directly:
755
-
756
- ```sh
757
- gem install git --pre
370
+ gem install git
758
371
  ```
759
372
 
760
- The intent is full backward compatibility with 4.x, but given the size and scope of
761
- the redesign, some incompatibilities may exist. Please give the latest beta a try and
762
- [open an issue](https://github.com/ruby-git/ruby-git/issues) if you hit anything
763
- unexpected — your feedback helps us ship a solid 5.0.0.
764
-
765
- 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.
766
376
 
767
377
  ### 2026-01-07: AI Policy Introduced
768
378
 
data/UPGRADING.md CHANGED
@@ -9,7 +9,9 @@ to update your code when upgrading from the preceding major version.
9
9
  - [Breaking changes](#breaking-changes)
10
10
  - [`Git::Base` removed](#gitbase-removed)
11
11
  - [Return type of `Git.open`, `Git.clone`, `Git.init`, `Git.bare`](#return-type-of-gitopen-gitclone-gitinit-gitbare)
12
+ - [Unsupported options raise `ArgumentError`](#unsupported-options-raise-argumenterror)
12
13
  - [`Git::Lib` removed](#gitlib-removed)
14
+ - [`Git::Log#object` is not a path limiter](#gitlogobject-is-not-a-path-limiter)
13
15
  - [`Git::CommandLineResult` deprecated](#gitcommandlineresult-deprecated)
14
16
  - [Deprecated methods](#deprecated-methods)
15
17
  - [Facade method renames](#facade-method-renames)
@@ -45,6 +47,8 @@ For information on how to suppress or configure deprecation warnings, see the
45
47
  | `Git::Base` removed | Hard break | High for code that references it by name | Replace with `Git::Repository` (returned by `Git.open` etc.) |
46
48
  | `Git::Lib` removed | Hard break | High for `.lib.*` callers | Use the equivalent method directly on the repo object (see table below) |
47
49
  | `Git.open` etc. return `Git::Repository` (not `Git::Base`) | Hard break | Low for most callers; breaks `is_a?(Git::Base)` | Update type checks and update `be_a(Git::Base)` in tests |
50
+ | Unsupported options now raise `ArgumentError` | Behavior change | Medium for code passing unknown or misspelled options | Check option names against the documented API |
51
+ | `Git::Log#object` is not a path limiter | Behavior change | Medium for code that used `object(path)` to filter logs by path | Use `Git::Log#path` for path filtering |
48
52
  | `Git::CommandLineResult` deprecated | Deprecation (removed in v6.0.0) | Low; only affects code that references the constant by name | Use `Git::CommandLine::Result` instead |
49
53
 
50
54
  ---
@@ -89,6 +93,32 @@ require 'git'
89
93
  `Git.open` (e.g., `repo.commit`, `repo.status`, `repo.add`) requires no
90
94
  changes.
91
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
+
92
122
  ---
93
123
 
94
124
  #### Return type of `Git.open`, `Git.clone`, `Git.init`, `Git.bare`
@@ -114,6 +144,27 @@ construct a repository object.
114
144
 
115
145
  ---
116
146
 
147
+ #### Unsupported options raise `ArgumentError`
148
+
149
+ v5.x validates options more strictly for factory methods and command APIs.
150
+ Unknown options that were silently ignored in v4.x may now raise
151
+ `ArgumentError`. Check option names against the documented API when upgrading,
152
+ especially for calls that pass keyword options through helper methods or shared
153
+ option hashes.
154
+
155
+ For example, `Git.clone` supports `log:`, not `logger:`. A misspelled or
156
+ unsupported option that v4.x ignored must be corrected:
157
+
158
+ ```ruby
159
+ # v4.x — silently ignored; did not configure clone logging
160
+ Git.clone(url, path, logger: logger)
161
+
162
+ # v5.x — use the documented option name
163
+ Git.clone(url, path, log: logger)
164
+ ```
165
+
166
+ ---
167
+
117
168
  #### `Git::Lib` removed
118
169
 
119
170
  The object returned by `Git.open`, `Git.clone`, `Git.init`, and `Git.bare` previously
@@ -211,6 +262,30 @@ helpers with no plausible external use. They have no replacement in v5.0.0:
211
262
 
212
263
  ---
213
264
 
265
+ #### `Git::Log#object` is not a path limiter
266
+
267
+ In previous 4.x releases, some uses of `Git::Log#object(path)` could appear to
268
+ filter log output by path when combined with `#between` or other revision range
269
+ options. This relied on ambiguous `git log` argument handling and was not the
270
+ intended API for path filtering.
271
+
272
+ In v5.x, `Git::Log#object` should be treated as a revision expression. When both
273
+ `#object` and `#between` are specified, `#between` takes precedence. Code that
274
+ used `#object` to limit commits to a path should use `#path` instead.
275
+
276
+ ```ruby
277
+ # v4.x — ambiguous; could appear to filter commits touching this path
278
+ git.log(500).object('cookbooks/mycookbook').between('1.0.0', 'HEAD').execute
279
+
280
+ # v5.x — use #path for path filtering
281
+ git.log(500).path('cookbooks/mycookbook').between('1.0.0', 'HEAD').execute
282
+
283
+ # #object remains appropriate for revision expressions
284
+ git.log.object('HEAD~10..HEAD').execute
285
+ ```
286
+
287
+ ---
288
+
214
289
  #### `Git::CommandLineResult` deprecated
215
290
 
216
291
  `Git::CommandLineResult` was an alias for `Git::CommandLine::Result` introduced