git 5.7.0 → 6.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +132 -35
  3. data/CONTRIBUTING.md +44 -31
  4. data/README.md +138 -100
  5. data/UPGRADING.md +867 -262
  6. data/git.gemspec +6 -8
  7. data/lib/git/author_info.rb +1 -2
  8. data/lib/git/commands/base.rb +22 -2
  9. data/lib/git/commands/cat_file/raw.rb +0 -68
  10. data/lib/git/commands/maintenance/register.rb +0 -4
  11. data/lib/git/commands/maintenance/run.rb +0 -4
  12. data/lib/git/commands/maintenance/start.rb +0 -4
  13. data/lib/git/commands/maintenance/stop.rb +0 -4
  14. data/lib/git/commands/maintenance/unregister.rb +0 -4
  15. data/lib/git/commands/maintenance.rb +0 -2
  16. data/lib/git/commands/show_ref/exclude_existing.rb +1 -1
  17. data/lib/git/commands/show_ref/exists.rb +0 -5
  18. data/lib/git/commands/show_ref/list.rb +1 -1
  19. data/lib/git/commands/show_ref/verify.rb +1 -1
  20. data/lib/git/commands/show_ref.rb +1 -1
  21. data/lib/git/commands/worktree/repair.rb +0 -5
  22. data/lib/git/diff_path_status.rb +1 -1
  23. data/lib/git/encoding_utils.rb +25 -0
  24. data/lib/git/errors.rb +24 -18
  25. data/lib/git/factories.rb +361 -125
  26. data/lib/git/log.rb +3 -75
  27. data/lib/git/object.rb +4 -185
  28. data/lib/git/path_resolver.rb +104 -36
  29. data/lib/git/repository/branching.rb +21 -189
  30. data/lib/git/repository/committing.rb +0 -25
  31. data/lib/git/repository/context_helpers.rb +249 -155
  32. data/lib/git/repository/diffing.rb +2 -72
  33. data/lib/git/repository/merging.rb +32 -54
  34. data/lib/git/repository/object_operations.rb +120 -497
  35. data/lib/git/repository/remote_operations.rb +7 -219
  36. data/lib/git/repository/staging.rb +6 -184
  37. data/lib/git/repository/stashing.rb +126 -130
  38. data/lib/git/repository/status_operations.rb +1 -54
  39. data/lib/git/repository/worktree_operations.rb +30 -125
  40. data/lib/git/repository.rb +20 -299
  41. data/lib/git/system_call_guard.rb +95 -0
  42. data/lib/git/version.rb +1 -1
  43. data/lib/git.rb +47 -364
  44. metadata +24 -18
  45. data/lib/git/author.rb +0 -46
  46. data/lib/git/branch.rb +0 -693
  47. data/lib/git/branches.rb +0 -194
  48. data/lib/git/remote.rb +0 -191
  49. data/lib/git/stash.rb +0 -124
  50. data/lib/git/stashes.rb +0 -198
  51. data/lib/git/status.rb +0 -384
  52. data/lib/git/worktree.rb +0 -180
  53. data/lib/git/worktrees.rb +0 -152
data/UPGRADING.md CHANGED
@@ -1,10 +1,31 @@
1
- # Upgrading the `git` Gem
1
+ # Upgrading the `git` gem
2
2
 
3
3
  This document covers breaking changes and migration steps when upgrading the
4
4
  `git` gem to a new major version. Each section describes what changed and how
5
5
  to update your code when upgrading from the preceding major version.
6
6
 
7
- - [Upgrading to v6.0.0](#upgrading-to-v600)
7
+ - [Upgrading to v6.x](#upgrading-to-v6x)
8
+ - [Before you upgrade](#before-you-upgrade)
9
+ - [Minimum Ruby version](#minimum-ruby-version)
10
+ - [Minimum git version](#minimum-git-version)
11
+ - [Minimum addressable version](#minimum-addressable-version)
12
+ - [Minimum activesupport version](#minimum-activesupport-version)
13
+ - [Changes the deprecation run does not catch or does not explain](#changes-the-deprecation-run-does-not-catch-or-does-not-explain)
14
+ - [Filesystem errors raised as `Git::Error`](#filesystem-errors-raised-as-giterror)
15
+ - [Gzip failures in `archive` raised as `Git::Error`](#gzip-failures-in-archive-raised-as-giterror)
16
+ - [Relative `:repository` and `:index` paths](#relative-repository-and-index-paths)
17
+ - [Tilde expansion in `Git.init` and `Git.clone` paths](#tilde-expansion-in-gitinit-and-gitclone-paths)
18
+ - [Context helpers yield a separate repository](#context-helpers-yield-a-separate-repository)
19
+ - [`Git.init`, `Git.open`, and `Git.bare` reject unknown options](#gitinit-gitopen-and-gitbare-reject-unknown-options)
20
+ - [`tag_create` raises when the created tag is missing](#tag_create-raises-when-the-created-tag-is-missing)
21
+ - [`stash_list` returns an Array of `Git::StashInfo`](#stash_list-returns-an-array-of-gitstashinfo)
22
+ - [`stash_store` raises when the stored entry is missing](#stash_store-raises-when-the-stored-entry-is-missing)
23
+ - [`worktree_add` returns `Git::WorktreeInfo` and raises when the worktree is missing](#worktree_add-returns-gitworktreeinfo-and-raises-when-the-worktree-is-missing)
24
+ - [`Git::Deprecation` horizon is prose](#gitdeprecation-horizon-is-prose)
25
+ - [Removed APIs](#removed-apis)
26
+ - [Deprecated in v6.0.0](#deprecated-in-v600)
27
+ - [`set_index` and `set_working` deprecated](#set_index-and-set_working-deprecated)
28
+ - [`Git::GitExecuteError` deprecated](#gitgitexecuteerror-deprecated)
8
29
  - [Upgrading to v5.x](#upgrading-to-v5x)
9
30
  - [Overview](#overview)
10
31
  - [Breaking changes](#breaking-changes)
@@ -36,24 +57,628 @@ to update your code when upgrading from the preceding major version.
36
57
  - [`Git::Log` Enumerable interface deprecated](#gitlog-enumerable-interface-deprecated)
37
58
  - [`Git::Object::Commit#set_commit` deprecated](#gitobjectcommitset_commit-deprecated)
38
59
  - [`Git.export` `:remote` option deprecated](#gitexport-remote-option-deprecated)
39
- - [Context helper blocks that declare no parameter deprecated](#context-helper-blocks-that-declare-no-parameter-deprecated)
40
60
 
41
- ## Upgrading to v6.0.0
42
-
43
- v6.0.0 is not yet released. This section will be completed when it ships.
61
+ ## Upgrading to v6.x
44
62
 
45
63
  v6.0.0 removes the APIs deprecated during v5.x under the project's
46
- [deprecation policy](README.md#deprecation-policy).
47
- [Issue 1717](https://github.com/ruby-git/ruby-git/issues/1717) tracks its scope.
64
+ [deprecation policy](README.md#deprecation-policy), raises the Ruby, git, and
65
+ dependency floors, and changes a small number of behaviors that had no deprecation
66
+ path. [Issue 1717](https://github.com/ruby-git/ruby-git/issues/1717) tracks its scope.
67
+
68
+ This section is organized by what you have to do rather than by what changed:
69
+
70
+ - [Before you upgrade](#before-you-upgrade) gives the version floors and the upgrade
71
+ procedure. Read it first.
72
+ - [Changes the deprecation run does not catch or does not
73
+ explain](#changes-the-deprecation-run-does-not-catch-or-does-not-explain) covers
74
+ every change that a clean run under `GIT_DEPRECATION_BEHAVIOR=raise` does not find,
75
+ and the two warnings whose fix is not a removed API's replacement. Read each entry.
76
+ - [Removed APIs](#removed-apis) is one table of every API removed in v6.0.0 and its
77
+ replacement. Every one of them warned in v5.x when called, constructed, or used, so
78
+ the raise behavior finds each call, and the table links to the entry under
79
+ "Upgrading to v5.x" that maps each call shape to its replacement. A bare reference
80
+ to a removed constant did not warn, so step 8 has you search for those.
81
+ - [Deprecated in v6.0.0](#deprecated-in-v600) covers the APIs that warn in v6.0.0 and
82
+ are removed in a later major release.
83
+
84
+ If you are upgrading from v4.x directly, the v5.x warnings never ran against your
85
+ code, so also read [Upgrading to v5.x](#upgrading-to-v5x). Its entries map each v4.x
86
+ call to its replacement, and the [Removed APIs](#removed-apis) table tells you which of
87
+ those replacements are now the only option.
88
+
89
+ ### Before you upgrade
90
+
91
+ 1. Upgrade to Ruby 3.3 or later if you are still on Ruby 3.2 (see
92
+ [Minimum Ruby version](#minimum-ruby-version)).
93
+ 2. Check that `git --version` reports 2.43.0 or later everywhere the gem runs (see
94
+ [Minimum git version](#minimum-git-version)).
95
+ 3. Run `bundle update addressable` and confirm the lockfile resolves addressable
96
+ 2.9.0 or later (see [Minimum addressable version](#minimum-addressable-version)).
97
+ 4. Run `bundle update activesupport` and confirm the lockfile resolves activesupport
98
+ 7.0.9 or later (see [Minimum activesupport version](#minimum-activesupport-version)).
99
+ 5. Upgrade to the latest v5.x release.
100
+ 6. Set `GIT_DEPRECATION_BEHAVIOR=raise` (or `Git::Deprecation.behavior = :raise`) in
101
+ your test suite and, if possible, staging.
102
+ 7. Fix each deprecation until the suite is clean. The [Removed APIs](#removed-apis)
103
+ table links each removed API's replacement. Three warnings are not removals:
104
+ - `stash_list` keeps its name and changes its return type (see [`stash_list`
105
+ returns an Array of `Git::StashInfo`](#stash_list-returns-an-array-of-gitstashinfo)).
106
+ - A context-helper block with no positional parameter, which v5.6.0 and later
107
+ warn about (see [Context helpers yield a separate
108
+ repository](#context-helpers-yield-a-separate-repository)).
109
+ - `Git::GitExecuteError`, which still warns in v6.0.0 (see
110
+ [`Git::GitExecuteError` deprecated](#gitgitexecuteerror-deprecated)).
111
+ 8. Search your code for each constant in the [Removed APIs](#removed-apis) table. A
112
+ bare reference such as `is_a?(Git::Base)`, `rescue Git::Status`, or
113
+ `case ... when Git::Author` never warned in v5.x, so the raise behavior does not
114
+ find it, and it raises `NameError` in v6.0.0.
115
+ 9. Work through each entry under [Changes the deprecation run does not catch or does
116
+ not explain](#changes-the-deprecation-run-does-not-catch-or-does-not-explain). The two
117
+ that affect the most code:
118
+ - Replace any `rescue Errno::*` around gem calls with `rescue Git::Error` (see
119
+ [Filesystem errors raised as `Git::Error`](#filesystem-errors-raised-as-giterror)).
120
+ - Give every `with_index`, `with_temp_index`, `with_working`, and
121
+ `with_temp_working` block a parameter and call methods on it rather than on the
122
+ outer repository; v5.6.0 and later warn for a block with no positional parameter
123
+ (see [Context helpers yield a separate
124
+ repository](#context-helpers-yield-a-separate-repository)).
125
+ 10. Upgrade to v6.0.0.
126
+
127
+ #### Minimum Ruby version
128
+
129
+ v6.0.0 requires Ruby 3.3.0 or later. v5.x supports Ruby 3.2, which reached end of
130
+ life on 2026-03-31. The
131
+ [Ruby version support policy](README.md#ruby-version-support-policy) commits the
132
+ gem to the MRI versions that are still maintained, so the floor moves when a Ruby
133
+ version leaves maintenance rather than with the gem's major version. Ruby 3.3 is
134
+ in security maintenance until about 2027-03-31, so it stays supported in v6.0.0.
135
+ A 3.4 floor can land in a later v6.x release once 3.3 reaches end of life.
136
+
137
+ JRuby and TruffleRuby support is unchanged.
138
+
139
+ If your application still runs on Ruby 3.2, upgrade Ruby before upgrading the gem,
140
+ or stay on the latest v5.x release.
141
+
142
+ #### Minimum git version
143
+
144
+ v6.0.0 requires git 2.43.0 or later. v5.x required 2.28.0.
145
+
146
+ Git 2.43.0 was released on November 20, 2023, and is the version shipped by
147
+ Ubuntu 24.04, RHEL 8.10 and 9.4, SLES 15 SP6, openSUSE Leap 15.6, and Alpine 3.19.
148
+ Ubuntu 22.04 (git 2.34) and Debian 12 (git 2.39) are the supported platforms whose
149
+ stock git falls below the new floor. On Ubuntu 22.04, install a newer git (for
150
+ example from the [git-core PPA](https://launchpad.net/~git-core/+archive/ubuntu/ppa));
151
+ on Debian 12, install it from
152
+ [bookworm-backports](https://backports.debian.org/Instructions/); or stay on v5.x.
153
+ Apple's git 2.39 from Xcode 16 also falls below the floor; Xcode 26 ships git 2.50.
154
+
155
+ Features that need a git newer than 2.43.0 stay gated per command and raise
156
+ `Git::VersionError` on older installations, as `git am --retry` (2.46.0) does today.
157
+
158
+ #### Minimum addressable version
159
+
160
+ v6.0.0 depends on `addressable ~> 2.9`. v5.x accepts `~> 2.8`.
161
+
162
+ Addressable 2.9.0 (released in April 2026) fixes the ReDoS vulnerability in
163
+ `Addressable::Template#match`
164
+ ([GHSA-h27x-rffw-24p4](https://github.com/sporkmonger/addressable/security/advisories/GHSA-h27x-rffw-24p4)).
165
+ Addressable 2.8.10 mitigates only part of it, and the advisory lists every release
166
+ below 2.9.0 as affected. The gem never calls `Addressable::Template`, so v5.x is not
167
+ affected and keeps the wider constraint. v6.0.0 raises the floor to the 2.9 series so
168
+ that dependency scanners stop flagging the gem's resolved dependency tree.
169
+
170
+ The `~> 2.8` constraint already permits 2.9.0, so most applications resolve it after
171
+ `bundle update addressable`. The upgrade fails only when another gem in the bundle
172
+ caps addressable below 2.9.0. In that case, update or replace that gem, or stay on
173
+ the latest v5.x release.
174
+
175
+ #### Minimum activesupport version
176
+
177
+ v6.0.0 depends on `activesupport >= 7.0.9`. v5.x accepts `>= 5.0`.
178
+
179
+ Two things set the floor. The `Git::GitExecuteError` deprecation warning passes its
180
+ text to ActiveSupport's `DeprecatedConstantProxy` through the `message:` keyword, which
181
+ no release before 6.0.3 accepts on Ruby 3. Separately, activesupport requires
182
+ `base64`, `bigdecimal`, `drb`, and `mutex_m` from the standard library, and Ruby 3.4
183
+ removed those from the default gems, so on Ruby 3.4 or later `require 'git'` fails
184
+ under Bundler unless a gem in the bundle declares them. Activesupport 7.0.9 is the
185
+ first 7.0.x release that declares them as dependencies; every 7.1.0 and later release
186
+ does too. The floor applies on every Ruby version so that Bundler reports the conflict
187
+ when it resolves the bundle, instead of `require 'git'` failing on Ruby 3.4.
188
+
189
+ Most applications already resolve a newer activesupport. The upgrade fails only when
190
+ another gem in the bundle caps activesupport below 7.0.9. In that case, update or
191
+ replace that gem, or stay on the latest v5.x release.
192
+
193
+ ### Changes the deprecation run does not catch or does not explain
194
+
195
+ Most changes below never emitted a warning in v5.x, because the method keeps its name
196
+ and changes what it returns or does, or keeps working after the upgrade with a
197
+ different result. Two of them warned, `stash_list` and the context-helper block form,
198
+ but neither is a removed API, so the [Removed APIs](#removed-apis) table has no row
199
+ for them and their entries live here. A clean run under
200
+ `GIT_DEPRECATION_BEHAVIOR=raise` is therefore not proof that your code is unaffected.
201
+ Search your code for each one before upgrading.
202
+
203
+ #### Filesystem errors raised as `Git::Error`
204
+
205
+ The gem's own filesystem calls used to let a bare `SystemCallError` escape, which
206
+ contradicted the documented contract that the gem raises only `ArgumentError` or a
207
+ `Git::Error` subclass. Those calls now raise `Git::Error`, with the original error
208
+ available through `cause`.
209
+
210
+ The affected methods are `Git.open` (reading a gitdir pointer file), `Git.export`,
211
+ `Git::Repository#chdir`, `#with_index`, `#with_working`, `#with_temp_index`,
212
+ `#with_temp_working`, `#set_index`, `#set_working`, `#cat_file_contents`,
213
+ `#each_conflict`, `#archive`, and `#repo_size`. `Git.init`, `Git.clone`, `Git.bare`,
214
+ and `Git.open` also raise `Git::Error` when the directory argument or a
215
+ `:repository`, `:index`, or `:chdir` option cannot be expanded to an absolute path,
216
+ which happens when the process working directory has been removed.
48
217
 
49
- To prepare:
218
+ ```ruby
219
+ # v5.x
220
+ begin
221
+ Git.open('/path/to/repo')
222
+ rescue Errno::EACCES => e
223
+ handle_permission_problem(e)
224
+ end
50
225
 
51
- 1. Upgrade to the latest v5.x release.
52
- 2. Set `GIT_DEPRECATION_BEHAVIOR=raise` (or `Git::Deprecation.behavior = :raise`) in
53
- your test suite and, if possible, staging.
54
- 3. Fix each deprecation using the entries under
55
- [Deprecated methods](#deprecated-methods) until the suite is clean.
56
- 4. Upgrade to v6.0.0.
226
+ # v6.x
227
+ begin
228
+ Git.open('/path/to/repo')
229
+ rescue Git::Error => e
230
+ raise unless e.cause.is_a?(Errno::EACCES)
231
+
232
+ handle_permission_problem(e.cause)
233
+ end
234
+ ```
235
+
236
+ Code that already rescues `Git::Error` needs no change. A `SystemCallError` raised by
237
+ your own block inside `chdir`, `with_working`, `with_temp_index`, `with_temp_working`,
238
+ or the block form of `cat_file_contents` still propagates unchanged, so the gem never
239
+ relabels an error raised by your code.
240
+
241
+ #### Gzip failures in `archive` raised as `Git::Error`
242
+
243
+ `Git::Repository#archive` with `format: 'tgz'` or `add_gzip: true` used to let a
244
+ `Zlib::Error` subclass escape when compression failed. It now raises `Git::Error`, with
245
+ the `Zlib::Error` available through `cause`. Code that already rescues `Git::Error`
246
+ needs no change.
247
+
248
+ #### Relative `:repository` and `:index` paths
249
+
250
+ A relative `:repository` option of `Git.open` or `Git.init`, and a relative `:index`
251
+ option of `Git.open`, `Git.init`, or `Git.clone`, is now expanded against the process
252
+ working directory, the same way the positional directory argument, `set_index`, and
253
+ `set_working` treat a relative path. A relative `:repository` used to be expanded
254
+ against the working directory, and a relative `:index` against the git directory.
255
+ For `Git.clone` with the `:chdir` option, a relative `:index` is expanded against
256
+ `:chdir` instead, like the clone directory itself.
257
+
258
+ Omitted options resolve as before: `:repository` defaults to `<working_dir>/.git` and
259
+ `:index` to `<git_dir>/index`. Absolute paths are unaffected. A relative `:repository`
260
+ option of `Git.clone` keeps its meaning: it is relative to `:chdir` when given and to
261
+ the process working directory otherwise, which is where git already resolved it.
262
+
263
+ ```ruby
264
+ # v5.x: the index is bound to /path/to/repo/.git/scratch.index
265
+ repo = Git.open('/path/to/repo', index: 'scratch.index')
266
+
267
+ # v6.x: the index is bound to scratch.index in the process working directory.
268
+ # Join the path yourself to keep the v5.x location.
269
+ repo = Git.open('/path/to/repo', index: '/path/to/repo/.git/scratch.index')
270
+ ```
271
+
272
+ #### Tilde expansion in `Git.init` and `Git.clone` paths
273
+
274
+ `Git.init` and `Git.clone` now expand the directory argument and the `:repository` and
275
+ `:index` options before handing the path to git, so `~` is the home directory, as it
276
+ already is for `Git.bare`, `set_index`, and `set_working`. `Git.clone` expands its
277
+ `:chdir` option the same way, and `Git.open` expands its directory argument before
278
+ checking that it exists, so `Git.open('~/scratch')` opens the repository instead of
279
+ raising `ArgumentError`. git does no tilde expansion on its arguments, so a `~`-prefixed
280
+ path used to make git create a directory literally named `~` under the process working
281
+ directory, or under `:chdir` for `Git.clone`. For `Git.init('~/scratch')` the returned
282
+ repository then pointed at `$HOME/scratch`, which git had not created, and the first
283
+ command run on it failed. For a `~`-prefixed `:repository` or `:chdir` option of
284
+ `Git.clone`, git failed before cloning anything.
285
+
286
+ A caller who passed a `~`-prefixed path and wanted a directory named `~` must anchor
287
+ the path to keep it. Expanding before git runs is also visible in three other ways:
288
+
289
+ - git receives absolute paths, so a `:log` that recorded `Cloning into 'my-repo'...` now
290
+ records the absolute path.
291
+ - A `:repository`, `:index`, or `:chdir` that cannot be expanded raises before git runs,
292
+ with nothing on disk, instead of after the clone or init completed: `Git::Error` when
293
+ the process working directory has been removed, and `ArgumentError` for a `~user`
294
+ prefix that names no user.
295
+ - `..` is normalized lexically before git sees the path. `Git.init('a/../b')` creates
296
+ only `b`, where `git init a/../b` at a shell prompt also created `a`, and a path
297
+ through a symlinked directory resolves to where the link appears rather than where it
298
+ points.
299
+
300
+ The rule these methods now share is recorded in
301
+ [ADR-0010](docs/adr/0010-a-repository-locating-path-means-what-it-would-mean-at-a-shell-prompt.md).
302
+
303
+ ```ruby
304
+ # v5.x: git creates ./~/scratch; the returned repository points at $HOME/scratch
305
+ repo = Git.init('~/scratch')
306
+
307
+ # v6.x: git creates $HOME/scratch and the returned repository is bound to it
308
+ repo = Git.init('~/scratch')
309
+
310
+ # v6.x: anchor the path to keep a directory literally named ~
311
+ repo = Git.init('./~/scratch')
312
+ ```
313
+
314
+ #### Context helpers yield a separate repository
315
+
316
+ `Git::Repository#with_index`, `#with_temp_index`, `#with_working`, and
317
+ `#with_temp_working` used to rebind the receiver to the other index or working tree
318
+ for the duration of the block and yield the receiver itself. They now build a second
319
+ repository of the receiver's class bound to that index or working tree, yield it,
320
+ and leave the receiver bound to its original index and working tree.
321
+
322
+ Code that uses the block parameter and finishes with it before the block ends needs
323
+ no change. A block that declares no positional parameter, the v5.x form, now raises
324
+ `ArgumentError` before the block runs. Starting in v5.6.0 that form warns through
325
+ `Git::Deprecation`, so clear the warning on the latest v5.x release before upgrading.
326
+ A call with no block raises `ArgumentError` instead of `LocalJumpError`. Code that
327
+ declares the parameter but ignores it and calls methods on the outer repository
328
+ inside the block runs those calls against the original index or working tree. Use the
329
+ block parameter instead:
330
+
331
+ ```ruby
332
+ # v5.x
333
+ repo.with_temp_index do
334
+ repo.read_tree('HEAD')
335
+ repo.write_tree
336
+ end
337
+
338
+ # v6.x
339
+ repo.with_temp_index do |indexed|
340
+ indexed.read_tree('HEAD')
341
+ indexed.write_tree
342
+ end
343
+ ```
344
+
345
+ The same applies to reading `index` or `dir` inside the block to find the temporary
346
+ path: read them on the block parameter. A block that needs only the changed process
347
+ directory, such as one that writes files inside `with_temp_working`, still declares
348
+ the parameter (`do |_scratch|`).
349
+
350
+ A helper nested inside another must also be called on the block parameter. A nested
351
+ call on the outer repository derives from the outer repository's original index and
352
+ working tree, so it does not see the outer block's temporary location:
353
+
354
+ ```ruby
355
+ # v5.x
356
+ repo.with_temp_working do
357
+ repo.with_temp_index do
358
+ repo.add('.')
359
+ repo.write_tree
360
+ end
361
+ end
362
+
363
+ # v6.x
364
+ repo.with_temp_working do |scratch|
365
+ scratch.with_temp_index do |indexed|
366
+ indexed.add('.')
367
+ indexed.write_tree
368
+ end
369
+ end
370
+ ```
371
+
372
+ Calling `set_index` or `set_working` on the outer repository inside the block is no
373
+ longer undone when the block ends. In v5.x the block restored the receiver's
374
+ original index and working tree on exit; in v6.x there is no restore step, so the
375
+ receiver stays bound to whatever `set_index` or `set_working` gave it. Both methods
376
+ are deprecated in v6.0.0 (see [`set_index` and `set_working`
377
+ deprecated](#set_index-and-set_working-deprecated)).
378
+
379
+ The repository yielded by `with_temp_index` or `with_temp_working` stays bound to
380
+ the temporary path after the block removes it. This is the one case where code that
381
+ already used the block parameter has to change. If the yielded repository escapes
382
+ the block, directly or inside an object that holds it and later runs commands, calls
383
+ against the removed working tree raise `Git::Error`. Against the removed index,
384
+ reads do not raise: git treats the missing index file as an empty index, so
385
+ `ls_files` returns nothing and `status_info` reports every tracked file both as
386
+ deleted and as untracked. Writes such as `add` do raise `Git::Error`. Reads from
387
+ the object database, such as `Git::Object::AbstractObject#contents`, still work. In
388
+ v5.x an escaped object held the outer repository, which had been rebound to the
389
+ original path by then.
390
+
391
+ The yielded repository is built with `self.class.new(execution_context:)`, so a
392
+ subclass of `Git::Repository` has to accept that call. A subclass whose `initialize`
393
+ takes different keywords raises `ArgumentError` from these helpers. One that takes a
394
+ positional argument instead receives the keywords as a Hash and yields a
395
+ misconfigured repository, so check the constructor rather than waiting for an
396
+ exception.
397
+
398
+ `with_working` and `with_temp_working` still change the process working directory
399
+ with `Dir.chdir` for the duration of the block, so they remain unsafe to call from
400
+ more than one thread at a time.
401
+
402
+ #### `Git.init`, `Git.open`, and `Git.bare` reject unknown options
403
+
404
+ `Git.init`, `Git.open`, and `Git.bare` now raise `ArgumentError` when the options
405
+ hash contains a key they do not accept, the same way `Git.ls_remote` already does.
406
+ v5.x ignored unknown keys, so a typo such as `respository:` or an unsupported key
407
+ silently returned a repository bound to paths the caller did not ask for, and
408
+ `Git.init` created the repository in the default location instead of the one
409
+ the mistyped key was meant to name.
410
+
411
+ For the APIs that did validate option names in v5.x, see
412
+ [Unsupported options raise `ArgumentError`](#unsupported-options-raise-argumenterror).
413
+
414
+ The accepted keys are:
415
+
416
+ - `Git.init`: `:bare`, `:initial_branch`, `:repository`, `:separate_git_dir`,
417
+ `:index`, `:log`, `:git_ssh`, and `:binary_path`
418
+ - `Git.open`: `:repository`, `:index`, `:log`, `:git_ssh`, and `:binary_path`
419
+ - `Git.bare`: `:index`, `:log`, `:git_ssh`, and `:binary_path`
420
+
421
+ The check runs before any filesystem or git call, so a bad key fails fast. To fix a
422
+ call that now raises, drop the key.
423
+
424
+ ```ruby
425
+ # v5.x: the typo is ignored and the .git directory is auto-detected
426
+ # v6.x: raises ArgumentError, "Unknown options: respository"
427
+ repo = Git.open('/path/to/worktree', respository: '/srv/repo.git')
428
+
429
+ # v6.x: the corrected call opens the repository
430
+ repo = Git.open('/path/to/worktree', repository: '/srv/repo.git')
431
+
432
+ # v5.x: the typo is ignored and the repository is created at /path/to/worktree/.git
433
+ # v6.x: raises ArgumentError, "Unknown options: separate_gitdir"
434
+ repo = Git.init('/path/to/worktree', separate_gitdir: '/srv/repo.git')
435
+
436
+ # v6.x: the corrected call creates the repository at /srv/repo.git
437
+ repo = Git.init('/path/to/worktree', separate_git_dir: '/srv/repo.git')
438
+ ```
439
+
440
+ #### `tag_create` raises when the created tag is missing
441
+
442
+ In v5.x, `g.tag_create` returned `nil` when the tag it had just created was missing from
443
+ the tag list (for example, another process deleted it before the lookup). It never
444
+ emitted a deprecation warning, since the method keeps its name. In v6.0.0 it raises
445
+ `Git::UnexpectedResultError` instead, so its return value is always a `Git::TagInfo`.
446
+ Search for `tag_create` before upgrading and remove any check for a `nil` result.
447
+
448
+ #### `stash_list` returns an Array of `Git::StashInfo`
449
+
450
+ In v5.x, `g.stash_list` returned the `git stash list` text as a `String` and emitted a
451
+ `Git::Deprecation` warning on every call. A project that silences or ignores deprecation
452
+ warnings never acts on that notice, and in v6.0.0 the call keeps working with a
453
+ different return type: `Array<Git::StashInfo>`, newest first, the same value as
454
+ `Git::Repository#stash_infos`, which stays as a permanent alias. Search for `stash_list`
455
+ before upgrading. Code that needs the text builds it from the entries:
456
+ `g.stash_list.map { |s| "#{s.name}: #{s.message}" }.join("\n")`.
457
+
458
+ #### `stash_store` raises when the stored entry is missing
459
+
460
+ In v5.x, `g.stash_store` returned the top entry of the stash list after the store, so it
461
+ returned `nil` when the list was empty and another process's entry when one had been
462
+ pushed in between. It never emitted a deprecation warning, since the method keeps its
463
+ name. In v6.0.0 it returns the entry for the commit it stored and raises
464
+ `Git::UnexpectedResultError` when that entry is missing, so its return value is always a
465
+ `Git::StashInfo`. A commit that does not resolve raises `Git::FailedError` before
466
+ anything is stored. A `nil` commit or one that begins with `-` raises `ArgumentError` as
467
+ in v5.x, with a different message; one that begins with `^` raises `ArgumentError` where
468
+ v5.x raised `Git::FailedError`. An annotated tag name now stores the tagged commit; v5.x
469
+ stored the tag object, which hid every entry from `git stash list`. Search for
470
+ `stash_store` before upgrading and remove any check for a `nil` result.
471
+
472
+ #### `worktree_add` returns `Git::WorktreeInfo` and raises when the worktree is missing
473
+
474
+ In v4.x and v5.x, `g.worktree_add` returned the `git worktree add` output
475
+ (`"HEAD is now at …"`) as a `String` and never emitted a deprecation warning, since the
476
+ method keeps its name. In v6.0.0 it returns the new worktree's `Git::WorktreeInfo`, the
477
+ same entry `g.worktree_list` reports for it, and raises `Git::UnexpectedResultError`
478
+ when that entry is missing after the add, as `tag_create` and `stash_store` do. A
479
+ `rescue Git::FailedError` around the call no longer covers every error it can raise.
480
+ Search for `worktree_add` before upgrading.
481
+ Code that used the return value as a `String` reads `info.path`, `info.head`, and
482
+ `info.branch` instead; `Git::WorktreeInfo#to_s` is the path, so string interpolation of
483
+ the result yields the worktree path.
484
+
485
+ #### `Git::Deprecation` horizon is prose
486
+
487
+ v6.0.0 constructs `Git::Deprecation` with the horizon `"in a future major release"`.
488
+ v5.x used `"6.0.0"`. The gem never puts the horizon into a warning, so the warnings
489
+ themselves do not change. The horizon reaches your code through the
490
+ `Git::Deprecation.deprecation_horizon` reader, the `deprecation_horizon` key of the
491
+ payload the `:notify` behavior publishes, and the third argument of a custom
492
+ `behavior` or `disallowed_behavior` proc declared with four parameters. Code that
493
+ parses that value as a version, for example with `Gem::Version.new`, now raises; read
494
+ the removal version from the warning text instead, which names it once it is decided.
495
+
496
+ ### Removed APIs
497
+
498
+ Every API in this table emitted a deprecation warning in v5.x that named its
499
+ replacement when it was called, constructed, or used, and each row links to the entry
500
+ under "Upgrading to v5.x" that maps every call shape, reader, and option to its
501
+ replacement, including return shape differences. A bare reference to a removed
502
+ constant (`is_a?`, `rescue`, `case ... when`, `defined?`) did not warn, except for
503
+ `Git::CommandLineResult`, so search for each constant in the table as well as
504
+ clearing the warnings. In v6.0.0 a removed constant raises `NameError` when
505
+ referenced, a removed method raises `NoMethodError` when called, and a removed option
506
+ raises `ArgumentError` like any other unsupported option (see [Unsupported options
507
+ raise `ArgumentError`](#unsupported-options-raise-argumenterror)).
508
+
509
+ | Removed in v6.0.0 | Use instead | Migration entry |
510
+ | --- | --- | --- |
511
+ | `Git::Repository#add_remote`, `#remove_remote`, `#set_remote_url`, `#add_tag`, `#delete_tag` | `remote_add`, `remote_remove`, `remote_set_url`, `tag_create`, `tag_delete` | [Facade method renames](#facade-method-renames) |
512
+ | `Git::Repository#empty?`, `#reset_hard`, `#conflicts`, `#is_local_branch?`, `#is_remote_branch?`, `#is_branch?`, `#branches_all` | `no_commits?`, `reset(commitish, hard: true)`, `each_conflict`, `local_branch?`, `remote_branch?`, `branch?`, `branch_list` | [`Git::Repository` method renames](#gitrepository-method-renames) |
513
+ | `Git::Repository#object_contents`, `#object_size`, `#object_type`, `#commit_data`, `#tag_data`, `#revparse`, `#namerev`, `#diff_name_status` | `cat_file_contents`, `cat_file_size`, `cat_file_type`, `cat_file_commit`, `cat_file_tag`, `rev_parse`, `name_rev`, `diff_path_status` | [`Git::Repository` alias renames](#gitrepository-alias-renames) |
514
+ | `clean(ff: true)` and `clean(force_force: true)`; `diff_path_status(path: ...)`; `commit(add_all: true)`; the positional `check` argument of `set_working` and `set_index` | `force: 2`; `:path_limiter`; `:all`; `must_exist:` | [`Git::Repository` option renames](#gitrepository-option-renames) |
515
+ | `Git::Repository#config` and `#global_config`; `Git.global_config`; `config` and `global_config` as instance methods of the `Git` module | `config_get`, `config_set`, and `config_list` on `Git::Repository` or `Git`, with `global: true` for the global scope | [v4.x-style configuration methods](#v4x-style-configuration-methods), [`Git` module mixin deprecations](#git-module-mixin-deprecations) |
516
+ | `Git.binary_version`; `Git.ls_remote(nil)` | `Git.git_version` (`.to_a` for the Array); `Git.ls_remote` with no argument or `'.'` | [Module-level `Git` function deprecations](#module-level-git-function-deprecations) |
517
+ | `Git.clone` options `:path`, `:recursive`, and `:remote` | `:chdir`, `:recurse_submodules`, and `:origin` | [`Git.clone` option renames](#gitclone-option-renames) |
518
+ | `Git.export` option `:remote` | Delete the option; it has no replacement | [`Git.export` `:remote` option deprecated](#gitexport-remote-option-deprecated) |
519
+ | `Git::Base` | Include an application-owned module into `Git::Repository` | [`Git::Base` removed](#gitbase-removed) |
520
+ | `Git::Repository#lib` | Call the facade method directly on the repository | [`Git::Lib` removed](#gitlib-removed) |
521
+ | `Git::CommandLineResult` | `Git::CommandLine::Result` | [`Git::CommandLineResult` deprecated](#gitcommandlineresult-deprecated) |
522
+ | `Git::Log#each`, `#size`, `#to_s`, `#first`, `#last`, `#[]`, and every other `Enumerable` method on `Git::Log` | `Git::Log#execute`, which returns a `Git::Log::Result` with the same methods | [`Git::Log` Enumerable interface deprecated](#gitlog-enumerable-interface-deprecated) |
523
+ | `Git::Object::Commit#set_commit` | `from_data` | [`Git::Object::Commit#set_commit` deprecated](#gitobjectcommitset_commit-deprecated) |
524
+ | `Git::Author` | `Git::AuthorInfo` (`parse` to build one, `with` in place of the writers) | [`Git::Author` deprecated](#gitauthor-deprecated) |
525
+ | `Git::Branch#stashes` | `Git::Repository#stash_infos` | [`Git::Branch#stashes` deprecated](#gitbranchstashes-deprecated) |
526
+ | `Git::Stash`, `Git::Stashes`, `Git::Repository#stashes_all`, `#stash_save` | `stash_list` (or its alias `stash_infos`), `stash_push`, and the name-based stash operations | [Legacy stash API deprecated](#legacy-stash-api-deprecated) |
527
+ | `Git::Repository#remotes` | `remote_list`, or `remote_names` for the names alone | [`Git::Repository#remotes` deprecated](#gitrepositoryremotes-deprecated) |
528
+ | `Git::Remote`, `Git::Repository#remote`, `#config_remote` | `remote_list`, which returns `Git::RemoteInfo` objects; filter `config_list` on the `remote.<name>.` prefix for keys it does not model | [`Git::Remote` deprecated](#gitremote-deprecated) |
529
+ | The `allow_unknown_type:` option of `Git::Commands::CatFile::Raw` | No replacement | [`Git::Commands::CatFile::Raw` `allow_unknown_type` option deprecated](#gitcommandscatfileraw-allow_unknown_type-option-deprecated) |
530
+ | `Git::Branch`, `Git::Branches`, `Git::Repository#branch`, `#branches` | `branch_list`, which returns `Git::BranchInfo` objects, and the name-based branch operations | [`Git::Branch` and `Git::Branches` deprecated](#gitbranch-and-gitbranches-deprecated) |
531
+ | `Git::Object::Tag`, `Git::Repository#tag`, `#tags`, `#tag_add`, and the `is_tag` argument of `Git::Object.new` | `tag_list` and `tag_create`, which return `Git::TagInfo` objects | [`Git::Object::Tag` deprecated](#gitobjecttag-deprecated) |
532
+ | `Git::Status`, `Git::Status::StatusFile`, `Git::Repository#status` | `status_info`, which returns a `Git::StatusInfo` | [`Git::Status` deprecated](#gitstatus-deprecated) |
533
+ | `Git::Worktree`, `Git::Worktrees`, `Git::Repository#worktree`, `#worktrees`, `#worktrees_all` | `worktree_list`, which returns `Git::WorktreeInfo` objects, and the path-based worktree operations | [`Git::Worktree` and `Git::Worktrees` deprecated](#gitworktree-and-gitworktrees-deprecated) |
534
+
535
+ Notes on specific rows:
536
+
537
+ - `add_tag`: go straight to `tag_create`. The `tag_add` that the v5.x warning names is
538
+ removed too.
539
+ - `diff_name_status` callers that passed `:path` must also switch to `:path_limiter`.
540
+ - `cat_file` is unaffected and stays as a legacy name for `cat_file_contents`, and
541
+ `stash_infos` stays as an alias of `stash_list`.
542
+ - `set_working` and `set_index`: a positional `check` argument raises `ArgumentError`
543
+ for the wrong number of arguments. Pass `must_exist:` instead; it defaults to
544
+ `true`. An explicit `must_exist: nil` no longer means "not given". v5.x treated it
545
+ as `true`, and v6.0.0 treats it as `false` like any other falsy value. Omit the
546
+ keyword to get the default. Both methods are deprecated in v6.0.0 (see [`set_index`
547
+ and `set_working` deprecated](#set_index-and-set_working-deprecated)), so consider
548
+ replacing the call instead of updating its arguments.
549
+ - `Git.ls_remote(nil)`: an explicit `nil` repository argument raises `ArgumentError`
550
+ with the message `repository must not be nil`. Omitting the argument still defaults
551
+ to `'.'`, so `Git.ls_remote` and `Git.ls_remote('.', opts)` are unchanged.
552
+ - `Git.config` (the gem-configuration accessor used as `Git.config.binary_path`) and
553
+ `Git.configure` are not part of the configuration API removal and are unchanged.
554
+ - `Git.export` `:remote` renamed the clone's remote, and `Git.export` deletes the `.git`
555
+ directory before returning, so the name was never observable.
556
+ - `allow_unknown_type`: git 2.50 removed the unknown-type feature and accepts the flag
557
+ as a no-op, and on git 2.43 through 2.49 the ability to query the type and size of an
558
+ object of unknown type goes with the option.
559
+ - `status` to `status_info`: the `changed`, `added`, `deleted`, and `untracked` readers
560
+ keep their names, but the categories are derived from both porcelain status
561
+ characters and clean tracked paths are no longer listed. Read the v5.x entry before
562
+ changing the call.
563
+ - `g.branch(name)`: the string constructor could mis-split a remote-tracking name when
564
+ the remote name itself contains a slash (`remotes/team/upstream/main`). `branch_list`
565
+ resolves configured remote names and has no such limitation.
566
+
567
+ ### Deprecated in v6.0.0
568
+
569
+ Both APIs below keep working in v6.0.0 and warn through `Git::Deprecation` every time
570
+ they are used. A later major release removes them.
571
+
572
+ #### `set_index` and `set_working` deprecated
573
+
574
+ `Git::Repository#set_index` and `#set_working` rebind the receiver to another index
575
+ file or working tree. They are the last mutable state on `Git::Repository`. Once
576
+ they are gone, a repository object is bound at construction and never changes.
577
+ Until then, the rebind retargets every object that holds the receiver, such as a
578
+ `Git::Object`, a `Git::Log`, or a second caller that was handed the same
579
+ repository. v6.0.0 keeps both methods but warns on every call through
580
+ `Git::Deprecation`, and a later major release removes them. The warnings are:
581
+
582
+ - `Git::Repository#set_index is deprecated and will be removed in a future major
583
+ release. Open a repository bound to the index instead: Git.open(dir, index: path)
584
+ or Git.bare(git_dir, index: path).`
585
+ - `Git::Repository#set_working is deprecated and will be removed in a future major
586
+ release. Open a repository bound to the working tree instead: Git.open(work_dir,
587
+ repository: git_dir).`
588
+
589
+ Open a repository bound to the index or working tree up front instead of rebinding
590
+ it afterward. `Git.open` accepts `:index` and `:repository`, and `Git.bare` accepts
591
+ `:index`:
592
+
593
+ ```ruby
594
+ # instead of
595
+ repo = Git.open('/path/to/repo')
596
+ repo.set_index('/path/to/custom.index')
597
+
598
+ # open the bound repository
599
+ repo = Git.open('/path/to/repo', index: '/path/to/custom.index')
600
+
601
+ # instead of
602
+ repo = Git.bare('/srv/repo.git')
603
+ repo.set_index('/tmp/scratch.index')
604
+
605
+ # open the bound bare repository
606
+ repo = Git.bare('/srv/repo.git', index: '/tmp/scratch.index')
607
+
608
+ # instead of
609
+ repo.set_working('/path/to/worktree')
610
+
611
+ # open the bound repository
612
+ repo = Git.open('/path/to/worktree', repository: '/path/to/repo/.git')
613
+ ```
614
+
615
+ `Git.open` and `Git.bare` build the repository from scratch and carry over nothing
616
+ the caller does not pass. A receiver opened with a custom `:index`, `:git_ssh`,
617
+ `:binary_path`, or `:log` passes the same values again; `set_working` kept the
618
+ receiver's index, so its replacement needs `:index` whenever that index was custom.
619
+ When `:repository` is given without `:index`, the index resolves to
620
+ `<repository>/index`.
621
+
622
+ A replacement built from an existing object reads the paths back from it.
623
+ `repo.repo`, `repo.dir`, and `repo.index` return the `.git` directory, working tree,
624
+ and index file as `Pathname`s. A repository opened without `:index` has `repo.index`
625
+ pointing at `<git dir>/index`, so passing it back is harmless. The repository exposes
626
+ no public reader for `:git_ssh`, `:binary_path`, or `:log`, so a caller that opened
627
+ with any of them passes the same values again from wherever they were first built:
628
+
629
+ ```ruby
630
+ # instead of
631
+ repo.set_working('/path/to/worktree')
632
+
633
+ # rebind from an existing object, keeping its .git directory and index
634
+ rebound = Git.open('/path/to/worktree',
635
+ repository: repo.repo.to_s, index: repo.index.to_s,
636
+ git_ssh: ssh_command, binary_path: git_binary, log: logger)
637
+ ```
638
+
639
+ A caller that needs a copy of an existing object for the duration of a block, with
640
+ every setting intact, uses `with_index` or `with_working` and the block parameter.
641
+ The yielded repository keeps every setting the call does not replace: the logger,
642
+ `git_ssh`, `binary_path`, and the receiver's working tree under `with_index` or its
643
+ index under `with_working`:
644
+
645
+ ```ruby
646
+ repo.with_working('/path/to/worktree') do |bound|
647
+ bound.add('.')
648
+ end
649
+ ```
650
+
651
+ `set_working(dir, must_exist: false)` has no replacement. `Git.open` and
652
+ `with_working` both raise `ArgumentError` when the working directory does not exist,
653
+ so a repository can no longer be bound to a directory that is created later. When the
654
+ missing directory's parent exists, git runs commands that do not read the working
655
+ tree, such as `rev_parse` or `current_branch`, and only commands that need the
656
+ working tree fail until it exists; when the parent is missing too, git rejects every
657
+ command with an invalid path error. Create the directory, then call `Git.open`. If
658
+ your code needs the deferred bind, open an issue describing the use case; under
659
+ ADR-0007 that becomes a feature request. `set_index(path, must_exist: false)` has no
660
+ gap: the `:index` option is not checked for existence.
661
+
662
+ #### `Git::GitExecuteError` deprecated
663
+
664
+ `Git::GitExecuteError` has been a deprecated alias for `Git::Error` since v2.0.0 and
665
+ has warned whenever it is rescued or instantiated since then. The warning now follows
666
+ the project's deprecation policy: it names the replacement and says the alias is
667
+ removed in a future major release, and this entry records it. The warning is:
668
+
669
+ - `Git::GitExecuteError is deprecated and will be removed in a future major release.
670
+ Use Git::Error instead.`
671
+
672
+ Replace every reference with `Git::Error`. The two constants name the same class, so a
673
+ `rescue Git::Error` clause catches everything a `rescue Git::GitExecuteError` clause did.
674
+
675
+ ```ruby
676
+ # instead of
677
+ rescue Git::GitExecuteError => e
678
+
679
+ # rescue the class directly
680
+ rescue Git::Error => e
681
+ ```
57
682
 
58
683
  ---
59
684
 
@@ -61,25 +686,24 @@ To prepare:
61
686
 
62
687
  ### Overview
63
688
 
64
- v5.0.0 delivers a new internal architecture while keeping the v4.x API working
65
- for the vast majority of users. Most v4.x code requires **no changes** to run on
66
- v5.x.
689
+ v5.0.0 replaces the internal architecture while keeping the v4.x API working
690
+ for most users. Most v4.x code runs on v5.x without changes.
67
691
 
68
692
  The new architecture introduces a layered design (`Git::Commands`,
69
- `Git::Repository`, and associated parsers). Compatibility shims — deprecated
70
- forwarding methods that map old call patterns to the new API — ensure that v4.x
71
- code continues to work. These shims emit deprecation warnings that tell you
72
- exactly what to change and what will be eliminated in v6.0.0.
693
+ `Git::Repository`, and associated parsers). Compatibility shims, deprecated
694
+ forwarding methods that map old call patterns to the new API, keep v4.x code
695
+ working. These shims emit deprecation warnings that tell you what to change and
696
+ what v6.0.0 removes.
73
697
 
74
- Hard breaks are limited to a small number of things that had no safe migration
75
- path. These are described in the [Breaking changes](#breaking-changes) section,
76
- followed by [Deprecated methods](#deprecated-methods) that still work in v5.x
77
- but are removed in v6.0.0.
698
+ Hard breaks are limited to the few things that had no safe migration path.
699
+ The [Breaking changes](#breaking-changes) section describes them, followed by
700
+ [Deprecated methods](#deprecated-methods) that still work in v5.x but are
701
+ removed in v6.0.0.
78
702
 
79
- For information on how to suppress or configure deprecation warnings, see the
80
- [Deprecations](README.md#deprecations) section of the README.
703
+ To suppress or configure deprecation warnings, see the [Deprecations](README.md#deprecations)
704
+ section of the README.
81
705
 
82
- **Changes at a glance:**
706
+ Changes at a glance:
83
707
 
84
708
  | Change | Type | Impact | Action required |
85
709
  |--------|------|--------|-----------------|
@@ -96,59 +720,57 @@ For information on how to suppress or configure deprecation warnings, see the
96
720
 
97
721
  #### `Git::Base` removed
98
722
 
99
- `Git::Base` — the class previously returned by `Git.open`, `Git.clone`,
100
- `Git.init`, and `Git.bare` — is removed in v5.0.0. The replacement is
101
- `Git::Repository`, which is returned by all four entry points and exposes the
102
- same public API.
723
+ `Git::Base`, the class that `Git.open`, `Git.clone`, `Git.init`, and `Git.bare`
724
+ returned, is removed in v5.0.0. All four entry points now return
725
+ `Git::Repository`, which exposes the same public API.
103
726
 
104
- **Code that must be updated:**
727
+ Code that must be updated:
105
728
 
106
729
  ```ruby
107
- # v4.x — explicit Git::Base reference (raises NameError in v5.x)
730
+ # v4.x: explicit Git::Base reference (raises NameError in v5.x)
108
731
  repo = Git::Base.new(working_directory: '/path/to/repo')
109
732
 
110
- # v5.x — use the entry-point methods; do not construct Git::Repository directly
733
+ # v5.x: use the entry-point methods; do not construct Git::Repository directly
111
734
  repo = Git.open('/path/to/repo')
112
735
  ```
113
736
 
114
737
  ```ruby
115
- # v4.x — type-checking against Git::Base (raises NameError in v5.x because Git::Base is removed)
738
+ # v4.x: type-checking against Git::Base (raises NameError in v5.x because Git::Base is removed)
116
739
  raise unless repo.is_a?(Git::Base)
117
740
 
118
- # v5.x — check against Git::Repository
741
+ # v5.x: check against Git::Repository
119
742
  raise unless repo.is_a?(Git::Repository)
120
743
  ```
121
744
 
122
745
  ```ruby
123
- # v4.x — requiring the internal file (raises LoadError in v5.x)
746
+ # v4.x: requiring the internal file (raises LoadError in v5.x)
124
747
  require 'git/base'
125
748
 
126
- # v5.x — the public entry point is git itself; no internal require needed
749
+ # v5.x: the public entry point is git itself; no internal require needed
127
750
  require 'git'
128
751
  ```
129
752
 
130
- **Public API is preserved:** `Git::Repository` provides every method that
131
- `Git::Base` did. Code that simply calls methods on the object returned by
132
- `Git.open` (e.g., `repo.commit`, `repo.status`, `repo.add`) requires no
133
- changes.
753
+ **The public API is unchanged.** `Git::Repository` provides every method that
754
+ `Git::Base` did. Code that calls methods on the object returned by `Git.open`
755
+ (e.g., `repo.commit`, `repo.status`, `repo.add`) requires no changes.
134
756
 
135
- **Monkeypatching `Git::Base` is deprecated:** v5.x includes a temporary
757
+ **Monkeypatching `Git::Base` is deprecated.** v5.x includes a temporary
136
758
  compatibility shim for applications that define instance methods on `Git::Base`.
137
- Those methods are made available on `Git::Repository` instances, but each method
138
- definition emits a deprecation warning and this shim will be removed in v6.0.0.
759
+ The shim makes those methods available on `Git::Repository` instances, but each
760
+ method definition emits a deprecation warning, and v6.0.0 removes the shim.
139
761
 
140
762
  Move custom repository helpers to an application-owned extension module and
141
763
  include or prepend that module into `Git::Repository` during application setup:
142
764
 
143
765
  ```ruby
144
- # Deprecated in v5.x and will be removed in v6.0.0
766
+ # Deprecated in v5.x and removed in v6.0.0
145
767
  module Git::Base
146
768
  def worktree_clean?
147
769
  status.changed.empty?
148
770
  end
149
771
  end
150
772
 
151
- # v5.x — keep the extension in application-owned code
773
+ # v5.x: keep the extension in application-owned code
152
774
  module MyAppGitRepositoryExtensions
153
775
  def worktree_clean?
154
776
  status.changed.empty?
@@ -165,9 +787,9 @@ Git::Repository.include(MyAppGitRepositoryExtensions)
165
787
  `Git.open`, `Git.clone`, `Git.init`, and `Git.bare` now return
166
788
  `Git::Repository` instead of `Git::Base`.
167
789
 
168
- For most callers this is transparent — the returned object responds to the same
169
- methods. Code that explicitly checks `is_a?(Git::Base)` or `be_a(Git::Base)` in
170
- tests must be updated:
790
+ For most callers this is transparent, because the returned object responds to
791
+ the same methods. Update code that checks `is_a?(Git::Base)` or `be_a(Git::Base)`
792
+ in tests:
171
793
 
172
794
  ```ruby
173
795
  # v4.x
@@ -185,20 +807,26 @@ construct a repository object.
185
807
 
186
808
  #### Unsupported options raise `ArgumentError`
187
809
 
188
- v5.x validates options more strictly for factory methods and command APIs.
189
- Unknown options that were silently ignored in v4.x may now raise
190
- `ArgumentError`. Check option names against the documented API when upgrading,
191
- especially for calls that pass keyword options through helper methods or shared
192
- option hashes.
810
+ In v5.x, only some APIs reject unknown options: `Git.clone`, `Git.ls_remote`,
811
+ and most `Git::Repository` methods that accept an options hash (for example,
812
+ `#add`) raise `ArgumentError` for option names they do not recognize. The
813
+ factory methods `Git.init`, `Git.open`, and `Git.bare` do not validate option
814
+ names in v5.x. All three reject unknown keys in v6.0.0 (see
815
+ [`Git.init`, `Git.open`, and `Git.bare` reject unknown options](#gitinit-gitopen-and-gitbare-reject-unknown-options)).
816
+
817
+ For APIs that validate options, names that v4.x silently ignored may raise
818
+ `ArgumentError` in v5.x. Check option names against the documented API when
819
+ upgrading, especially for calls that pass keyword options through helper
820
+ methods or shared option hashes.
193
821
 
194
822
  For example, `Git.clone` supports `log:`, not `logger:`. A misspelled or
195
823
  unsupported option that v4.x ignored must be corrected:
196
824
 
197
825
  ```ruby
198
- # v4.x — silently ignored; did not configure clone logging
826
+ # v4.x: silently ignored; did not configure clone logging
199
827
  Git.clone(url, path, logger: logger)
200
828
 
201
- # v5.x — use the documented option name
829
+ # v5.x: use the documented option name
202
830
  Git.clone(url, path, log: logger)
203
831
  ```
204
832
 
@@ -211,31 +839,29 @@ exposed a `#lib` method that gave access to `Git::Lib`, the gem's internal
211
839
  implementation class. `Git::Lib` is removed in v5.0.0.
212
840
 
213
841
  In v5.x, calling `#lib` on a repo object returns `self` with a deprecation
214
- warning. This means `g.lib.some_method(args)` is forwarded to
215
- `g.some_method(args)` — but only if `some_method` exists on `Git::Repository`.
216
- Methods that were unique to `Git::Lib` and have no counterpart on
217
- `Git::Repository` raise `NoMethodError` immediately. The `#lib` method itself
218
- is removed in v6.0.0.
842
+ warning, so `g.lib.some_method(args)` calls `g.some_method(args)`, but only if
843
+ `some_method` exists on `Git::Repository`. Methods that were unique to `Git::Lib`
844
+ and have no counterpart on `Git::Repository` raise `NoMethodError` immediately.
845
+ The `#lib` method itself is removed in v6.0.0.
219
846
 
220
- Most public behavior previously accessible via `g.lib.*` is available directly
221
- on the repository object (`g.*`). See the tables below for every affected
222
- method.
847
+ Most of what `g.lib.*` exposed is available directly on the repository object
848
+ (`g.*`). See the tables below for every affected method.
223
849
 
224
- ##### Methods that work via the `#lib` shim (with deprecation warning)
850
+ ##### Methods that work through the `#lib` shim (with deprecation warning)
225
851
 
226
852
  The following v4.x `g.lib.*` call shapes are forwarded to their `Git::Repository`
227
- counterpart by the `#lib → self` shim. They emit a deprecation warning; migrate
228
- to the replacement shown to silence it.
853
+ counterpart by the `#lib` shim, which returns `self`. They emit a deprecation
854
+ warning; migrate to the replacement shown to silence it.
229
855
 
230
- > **Note — config return type change:** `g.lib.config_get(name)` returned a
856
+ > **Config return type change.** `g.lib.config_get(name)` returned a
231
857
  > `String`; `g.lib.config_list` returned a `Hash`.
232
858
  > The v5.x replacements `config_get` and `config_list` return
233
- > `Git::ConfigEntryInfo` and `Array<Git::ConfigEntryInfo>` respectively — richer
234
- > objects that expose `.value` (the String), `.key`, `.scope`, and `.origin`.
859
+ > `Git::ConfigEntryInfo` and `Array<Git::ConfigEntryInfo>` respectively. Those
860
+ > objects expose `.value` (the String), `.key`, `.scope`, and `.origin`.
235
861
  >
236
862
  > If you only need the String value:
237
- > - `g.config_get(name)&.value` → replaces `g.lib.config_get(name)`
238
- > - `g.config_list.to_h { |e| [e.key, e.value] }` → replaces `g.lib.config_list`
863
+ > - `g.config_get(name)&.value` replaces `g.lib.config_get(name)`
864
+ > - `g.config_list.to_h { |e| [e.key, e.value] }` replaces `g.lib.config_list`
239
865
  >
240
866
  > If your code was using the v4.x public `g.config(name)` API (not `g.lib.*`),
241
867
  > that deprecated bridge still returns a `String` in v5.x and continues to work
@@ -243,17 +869,17 @@ to the replacement shown to silence it.
243
869
 
244
870
  | v4.x call | Replacement in v5.x |
245
871
  |-----------|---------------------|
246
- | `g.lib.config_get(name)` | `g.config_get(name)` — returns `Git::ConfigEntryInfo`; use `.value` for the String |
247
- | `g.lib.config_list` | `g.config_list` — returns `Array<Git::ConfigEntryInfo>` |
872
+ | `g.lib.config_get(name)` | `g.config_get(name)`. Returns `Git::ConfigEntryInfo`; use `.value` for the String |
873
+ | `g.lib.config_list` | `g.config_list`. Returns `Array<Git::ConfigEntryInfo>` |
248
874
  | `g.lib.config_set(name, value)` | `g.config_set(name, value)` |
249
875
  | `g.lib.git_version` | `g.git_version` |
250
- | `g.lib.stash_list` | `g.stash_infos` — returns `Array<Git::StashInfo>`, newest first |
876
+ | `g.lib.stash_list` | `g.stash_infos`. Returns `Array<Git::StashInfo>`, newest first |
251
877
  | `g.lib.unmerged` | `g.unmerged` |
252
878
  | `g.lib.change_head_branch(name)` | `g.change_head_branch(name)` |
253
879
  | `g.lib.ls_remote(location, opts)` | `g.ls_remote(location, opts)` |
254
880
  | `g.lib.current_branch_state` | `g.current_branch_state` |
255
881
 
256
- > **Note — `current_branch_state` return type change:** `g.lib.current_branch_state`
882
+ > **`current_branch_state` return type change.** `g.lib.current_branch_state`
257
883
  > returned a `Git::Lib::HeadState` (a mutable `Struct`). `g.current_branch_state`
258
884
  > returns a `Git::Repository::Branching::HeadState` (an immutable `Data` object).
259
885
  > Both expose `.state` (`:active`, `:unborn`, or `:detached`) and `.name`. If your
@@ -264,8 +890,8 @@ to the replacement shown to silence it.
264
890
  ##### Methods that raise `NoMethodError` in v5.x
265
891
 
266
892
  These `Git::Lib` method names have no counterpart on `Git::Repository`, so
267
- `g.lib.method_name` raises `NoMethodError` even in v5.x (the `#lib → self`
268
- shim cannot forward them). Update call sites directly:
893
+ `g.lib.method_name` raises `NoMethodError` even in v5.x because the `#lib` shim
894
+ has nothing to forward them to. Update call sites directly:
269
895
 
270
896
  | v4.x call | Replacement in v5.x |
271
897
  |-----------|---------------------|
@@ -283,8 +909,8 @@ shim cannot forward them). Update call sites directly:
283
909
 
284
910
  ##### Internal plumbing methods (no replacement)
285
911
 
286
- The following methods were technically public on `Git::Lib` but are internal
287
- helpers with no plausible external use. They have no replacement in v5.0.0:
912
+ The following methods were public on `Git::Lib` in name only. They are internal
913
+ helpers with no plausible external use and have no replacement in v5.0.0:
288
914
 
289
915
  - `assert_args_are_not_options`
290
916
  - `assert_valid_opts`
@@ -308,15 +934,15 @@ filter log output by path when combined with `#between` or other revision range
308
934
  options. This relied on ambiguous `git log` argument handling and was not the
309
935
  intended API for path filtering.
310
936
 
311
- In v5.x, `Git::Log#object` should be treated as a revision expression. When both
312
- `#object` and `#between` are specified, `#between` takes precedence. Code that
313
- used `#object` to limit commits to a path should use `#path` instead.
937
+ In v5.x, `Git::Log#object` is a revision expression. When both `#object` and
938
+ `#between` are specified, `#between` takes precedence. Code that used `#object`
939
+ to limit commits to a path should use `#path` instead.
314
940
 
315
941
  ```ruby
316
- # v4.x — ambiguous; could appear to filter commits touching this path
942
+ # v4.x: ambiguous; could appear to filter commits touching this path
317
943
  git.log(500).object('cookbooks/mycookbook').between('1.0.0', 'HEAD').execute
318
944
 
319
- # v5.x — use #path for path filtering
945
+ # v5.x: use #path for path filtering
320
946
  git.log(500).path('cookbooks/mycookbook').between('1.0.0', 'HEAD').execute
321
947
 
322
948
  # #object remains appropriate for revision expressions
@@ -340,19 +966,25 @@ result.is_a?(Git::CommandLine::Result)
340
966
  ```
341
967
 
342
968
  This change is only relevant if your code references `Git::CommandLineResult`
343
- by name (typically in type checks or documentation). Code that simply uses the
344
- result object returned by git commands is unaffected.
969
+ by name (typically in type checks or documentation). Code that uses the result
970
+ object returned by git commands is unaffected.
345
971
 
346
972
  ---
347
973
 
348
974
  ### Deprecated methods
349
975
 
350
976
  The following methods are available in v5.x with deprecation warnings and are
351
- removed in v6.0.0. Migrate to the replacement shown to silence the warnings.
977
+ removed in v6.0.0. Migrate to the replacement shown to silence the warnings. The
978
+ [Removed APIs](#removed-apis) table under "Upgrading to v6.x" indexes these entries.
979
+
980
+ `Git::GitExecuteError` is not listed here because v6.0.0 keeps it. It has warned since
981
+ v2.0.0 and still warns in v6.0.0; see [`Git::GitExecuteError`
982
+ deprecated](#gitgitexecuteerror-deprecated) under "Upgrading to v6.x" for the
983
+ replacement.
352
984
 
353
985
  #### Facade method renames
354
986
 
355
- Five methods were renamed to follow the project's `noun_verb` naming convention.
987
+ v5.x renamed five methods to follow the project's `noun_verb` naming convention.
356
988
  The old names continue to work but emit deprecation warnings:
357
989
 
358
990
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
@@ -365,11 +997,11 @@ The old names continue to work but emit deprecation warnings:
365
997
 
366
998
  #### `Git::Repository` method renames
367
999
 
368
- Seven more `Git::Repository` methods were renamed in v5.x. The old names continue
369
- to work but emit deprecation warnings. Each old name returns exactly what its
1000
+ v5.x renamed seven more `Git::Repository` methods. The old names continue to work
1001
+ but emit deprecation warnings. Each old name returns exactly what its
370
1002
  replacement returns, except `branches_all`.
371
1003
 
372
- > **Return shape change:** `g.branches_all` returns an `Array` of 4-element
1004
+ > **Return shape change.** `g.branches_all` returns an `Array` of 4-element
373
1005
  > tuples `[refname, current, worktree, symref]`, where `refname` is the short
374
1006
  > form (`main` or `remotes/origin/main`), `current` and `worktree` are booleans,
375
1007
  > and `symref` is the symbolic-ref target or `nil`. `g.branch_list` returns
@@ -387,25 +1019,25 @@ replacement returns, except `branches_all`.
387
1019
 
388
1020
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
389
1021
  |-----------------------------------------------------|-------------|
390
- | `g.empty?` | `g.no_commits?` — `true` when the repository has no commits |
391
- | `g.reset_hard` | `g.reset(nil, hard: true)` — `reset` takes the commitish positionally, so pass `nil` before the options; returns git's stdout, as `reset_hard` did |
392
- | `g.reset_hard(commitish)` | `g.reset(commitish, hard: true)` — `reset_hard` ignored any `:hard` option passed to it and always reset with `--hard` |
393
- | `g.conflicts { \|file, yours, theirs\| ... }` | `g.each_conflict { \|file, yours, theirs\| ... }` — same block arguments; returns the unmerged paths |
1022
+ | `g.empty?` | `g.no_commits?`. `true` when the repository has no commits |
1023
+ | `g.reset_hard` | `g.reset(nil, hard: true)`. `reset` takes the commitish positionally, so pass `nil` before the options; returns git's stdout, as `reset_hard` did |
1024
+ | `g.reset_hard(commitish)` | `g.reset(commitish, hard: true)`. `reset_hard` ignored any `:hard` option passed to it and always reset with `--hard` |
1025
+ | `g.conflicts { \|file, yours, theirs\| ... }` | `g.each_conflict { \|file, yours, theirs\| ... }`. Same block arguments; returns the unmerged paths |
394
1026
  | `g.is_local_branch?(name)` | `g.local_branch?(name)` |
395
1027
  | `g.is_remote_branch?(name)` | `g.remote_branch?(name)` |
396
1028
  | `g.is_branch?(name)` | `g.branch?(name)` |
397
- | `g.branches_all` | `g.branch_list` — returns `Array<Git::BranchInfo>`; see the return shape change above |
1029
+ | `g.branches_all` | `g.branch_list`. Returns `Array<Git::BranchInfo>`; see the return shape change above |
398
1030
 
399
1031
  #### `Git::Repository` alias renames
400
1032
 
401
1033
  Eight `Git::Repository` methods have a second, older name carried over from v4.x.
402
- Earlier v5.x releases kept the older names as aliases with no warning. Later
403
- v5.x releases emit a deprecation warning for each one. Each old name takes the
1034
+ Releases before v5.7.0 kept the older names as aliases with no warning. From
1035
+ v5.7.0 on, each old name emits a deprecation warning. Each old name takes the
404
1036
  same arguments and returns exactly what its replacement returns.
405
1037
 
406
1038
  > **`diff_name_status`:** a `:path` option emits its own deprecation warning
407
1039
  > after the method-name warning. Pass `:path_limiter` instead, as described
408
- > under "`Git::Repository` option renames" below.
1040
+ > under [`Git::Repository` option renames](#gitrepository-option-renames) below.
409
1041
 
410
1042
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
411
1043
  |-----------------------------------------------------|-------------|
@@ -424,7 +1056,7 @@ Five methods accept a v4.x option or positional argument under its old name.
424
1056
  The old form still works in v5.x but emits a deprecation warning and is
425
1057
  translated to the v5.x form shown below.
426
1058
 
427
- > **`clean`:** `force: 2` runs `git clean -ff`, which also removes untracked
1059
+ > **`clean`.** `force: 2` runs `git clean -ff`, which also removes untracked
428
1060
  > nested git repositories. A `false` or `nil` value for `:ff` or `:force_force`
429
1061
  > still warns and has no effect; a value other than `true`, `false`, or `nil`
430
1062
  > raises `ArgumentError`. When the deprecated key is `true` and a valid
@@ -433,11 +1065,11 @@ translated to the v5.x form shown below.
433
1065
  > unchanged and still raises `ArgumentError`; the deprecated key does not mask
434
1066
  > it.
435
1067
 
436
- > **`diff_path_status`:** `:path_limiter` accepts the same values as `:path`
1068
+ > **`diff_path_status`.** `:path_limiter` accepts the same values as `:path`
437
1069
  > (a `String`, a `Pathname`, or an `Array` of them). When both keys are given,
438
1070
  > `:path_limiter` wins and no warning is emitted.
439
1071
 
440
- > **`set_working` and `set_index`:** `must_exist:` defaults to `true`. When
1072
+ > **`set_working` and `set_index`.** `must_exist:` defaults to `true`. When
441
1073
  > both the positional argument and `must_exist:` are given, they are OR'ed so
442
1074
  > the more restrictive value wins.
443
1075
 
@@ -446,39 +1078,39 @@ translated to the v5.x form shown below.
446
1078
  | `g.clean(ff: true)` | `g.clean(force: 2)` |
447
1079
  | `g.clean(force_force: true)` | `g.clean(force: 2)` |
448
1080
  | `g.diff_path_status(ref1, ref2, path: p)` | `g.diff_path_status(ref1, ref2, path_limiter: p)` |
449
- | `g.commit(message, add_all: true)` | `g.commit(message, all: true)` — runs `git commit -a` |
1081
+ | `g.commit(message, add_all: true)` | `g.commit(message, all: true)`. Runs `git commit -a` |
450
1082
  | `g.set_working(dir, check)` | `g.set_working(dir, must_exist: check)` |
451
1083
  | `g.set_index(file, check)` | `g.set_index(file, must_exist: check)` |
452
1084
 
453
1085
  #### v4.x-style configuration methods
454
1086
 
455
1087
  The v4.x `config` and `global_config` methods accepted varying argument shapes
456
- to read, write, or list configuration. These are replaced by separate,
1088
+ to read, write, or list configuration. v5.x replaces them with separate,
457
1089
  purpose-named methods. The same applies to the module-level
458
- `Git.global_config`, which is replaced by `Git.config_get`, `Git.config_set`,
1090
+ `Git.global_config`, whose replacements are `Git.config_get`, `Git.config_set`,
459
1091
  and `Git.config_list` called with `global: true`.
460
1092
 
461
- > **Return type change:** The v4.x `g.config(name)` and `Git.global_config(name)`
1093
+ > **Return type change.** The v4.x `g.config(name)` and `Git.global_config(name)`
462
1094
  > returned a `String`; `g.config` and `Git.global_config` returned a `Hash`. The
463
1095
  > v5.x replacements `config_get` and `config_list` return `Git::ConfigEntryInfo`
464
1096
  > (or `nil` when the key is not set) and `Array<Git::ConfigEntryInfo>`
465
1097
  > respectively. Use `.value` to get the String value:
466
- > - `g.config_get(name)&.value` → String or nil
467
- > - `g.config_list.to_h { |e| [e.key, e.value] }` → Hash (key → value)
1098
+ > - `g.config_get(name)&.value` returns a String or nil
1099
+ > - `g.config_list.to_h { |e| [e.key, e.value] }` returns a Hash of key to value
468
1100
  >
469
1101
  > The setters `g.config(name, value)` and `Git.global_config(name, value)`
470
1102
  > returned the raw command result; `config_set` returns `nil`.
471
1103
 
472
1104
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
473
1105
  |-----------------------------------------------------|-------------|
474
- | `g.config(name)` | `g.config_get(name)` — returns `Git::ConfigEntryInfo`; use `.value` for the String |
475
- | `g.config` | `g.config_list` — returns `Array<Git::ConfigEntryInfo>` |
1106
+ | `g.config(name)` | `g.config_get(name)`. Returns `Git::ConfigEntryInfo`; use `.value` for the String |
1107
+ | `g.config` | `g.config_list`. Returns `Array<Git::ConfigEntryInfo>` |
476
1108
  | `g.config(name, value)` | `g.config_set(name, value)` |
477
1109
  | `g.global_config(name)` | `g.config_get(name, global: true)` |
478
1110
  | `g.global_config` | `g.config_list(global: true)` |
479
1111
  | `g.global_config(name, value)` | `g.config_set(name, value, global: true)` |
480
- | `Git.global_config(name)` | `Git.config_get(name, global: true)` — returns `Git::ConfigEntryInfo` or `nil`; use `.value` for the String |
481
- | `Git.global_config` | `Git.config_list(global: true)` — returns `Array<Git::ConfigEntryInfo>` |
1112
+ | `Git.global_config(name)` | `Git.config_get(name, global: true)`. Returns `Git::ConfigEntryInfo` or `nil`; use `.value` for the String |
1113
+ | `Git.global_config` | `Git.config_list(global: true)`. Returns `Array<Git::ConfigEntryInfo>` |
482
1114
  | `Git.global_config(name, value)` | `Git.config_set(name, value, global: true)` |
483
1115
 
484
1116
  #### `Git` module mixin deprecations
@@ -512,7 +1144,7 @@ legacy type that is deprecated:
512
1144
  - `Git.binary_version` is replaced by `Git.git_version`, which keeps the
513
1145
  optional binary path argument.
514
1146
 
515
- > **Return type change:** `Git.binary_version` returned an `Array<Integer>` of
1147
+ > **Return type change.** `Git.binary_version` returned an `Array<Integer>` of
516
1148
  > `[major, minor, patch]`. `Git.git_version` returns a `Git::Version`, which
517
1149
  > supports comparison and exposes `major`, `minor`, and `patch`.
518
1150
  > `Git.git_version.to_a` reproduces the legacy array. The return value of
@@ -522,19 +1154,20 @@ legacy type that is deprecated:
522
1154
  |-----------------------------------------------------|-------------|
523
1155
  | `Git.ls_remote(nil)` | `Git.ls_remote` or `Git.ls_remote('.')` |
524
1156
  | `Git.ls_remote(nil, opts)` | `Git.ls_remote('.', opts)` |
525
- | `Git.binary_version` | `Git.git_version` — returns `Git::Version`; use `.to_a` for the `[major, minor, patch]` Array |
1157
+ | `Git.binary_version` | `Git.git_version`. Returns `Git::Version`; use `.to_a` for the `[major, minor, patch]` Array |
526
1158
  | `Git.binary_version(binary_path)` | `Git.git_version(binary_path)` |
527
1159
 
528
1160
  #### `Git::Author` deprecated
529
1161
 
530
- Starting in v5.3.0, methods that return author, committer, or tagger data —
531
- `Git::Object::Commit#author`, `Git::Object::Commit#committer`,
532
- `Git::Object::Tag#tagger`, and `Git::TagInfo#tagger` — return an immutable
533
- `Git::AuthorInfo` value object instead of the mutable `Git::Author`.
1162
+ Starting in v5.3.0, the methods that return author, committer, or tagger data
1163
+ return an immutable `Git::AuthorInfo` value object instead of the mutable
1164
+ `Git::Author`. Those methods are `Git::Object::Commit#author`,
1165
+ `Git::Object::Commit#committer`, `Git::Object::Tag#tagger`, and
1166
+ `Git::TagInfo#tagger`.
534
1167
 
535
1168
  `Git::AuthorInfo` exposes the same `name`, `email`, and `date` readers, so code
536
1169
  that only reads these attributes needs no changes. Code that mutated a
537
- `Git::Author` (via `name=`, `email=`, or `date=`) must be updated:
1170
+ `Git::Author` through `name=`, `email=`, or `date=` has to change.
538
1171
  `Git::AuthorInfo` is frozen, and `#with` returns a modified copy rather than
539
1172
  updating in place (e.g. `info = info.with(name: 'New Name')`).
540
1173
 
@@ -553,7 +1186,7 @@ in the repository, so `g.branch('feature').stashes` and `g.branch('main').stashe
553
1186
  return the same entries. Call `Git::Repository#stash_infos` instead; it is the
554
1187
  query `Git::Branch#stashes` was already running.
555
1188
 
556
- > **Return type change:** `Git::Branch#stashes` returns a `Git::Stashes`
1189
+ > **Return type change.** `Git::Branch#stashes` returns a `Git::Stashes`
557
1190
  > collection of `Git::Stash` objects. `g.stash_infos` returns an array of
558
1191
  > `Git::StashInfo` values. Both are newest first, so indexes carry over unchanged.
559
1192
  > `Git::Stash#message` strips the `WIP on <branch>:` or `On <branch>:` prefix;
@@ -569,15 +1202,15 @@ its methods.
569
1202
 
570
1203
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
571
1204
  |-----------------------------------------------------|-------------|
572
- | `g.branch(name).stashes` | `g.stash_infos` — returns `Array<Git::StashInfo>`, newest first |
1205
+ | `g.branch(name).stashes` | `g.stash_infos`. Returns `Array<Git::StashInfo>`, newest first |
573
1206
  | `g.branch(name).stashes.each { \|s\| puts s.message }` | `g.stash_infos.each { \|info\| puts info.message }` |
574
- | `g.branch(name).stashes.all` (`[index, message]` pairs, oldest first) | `g.stash_infos.reverse` — see the ordering note in [Legacy stash API deprecated](#legacy-stash-api-deprecated) |
1207
+ | `g.branch(name).stashes.all` (`[index, message]` pairs, oldest first) | `g.stash_infos.reverse`. See the ordering note in [Legacy stash API deprecated](#legacy-stash-api-deprecated) |
575
1208
  | `g.branch(name).stashes.size` | `g.stash_infos.size` |
576
1209
  | `g.branch(name).stashes[i].message` (`0` = newest, `i` coerced with `to_i`) | `g.stash_infos[i.to_i].message` |
577
1210
  | `g.branch(name).stashes.save(message)` | `g.stash_push(message: message)` |
578
1211
  | `g.branch(name).stashes.apply` | `g.stash_apply` |
579
1212
  | `g.branch(name).stashes.apply(i)` (`0` = newest) | `g.stash_apply(i)` |
580
- | `g.branch(name).stashes.clear` | `g.stash_clear` — returns git's stdout (normally `""`, which is truthy) where `Git::Stashes#clear` returned `nil` |
1213
+ | `g.branch(name).stashes.clear` | `g.stash_clear`. Returns git's stdout (normally `""`, which is truthy) where `Git::Stashes#clear` returned `nil` |
581
1214
 
582
1215
  #### Legacy stash API deprecated
583
1216
 
@@ -594,14 +1227,14 @@ The legacy methods and classes are deprecated and removed in v6.0.0:
594
1227
  `Git::Repository#stash_list`, `Git::Stash`, and `Git::Stashes`. Constructing a
595
1228
  `Git::Stash` or `Git::Stashes` emits one warning per object.
596
1229
 
597
- > **Ordering flip:** `g.stashes_all` returns entries **oldest first** with a
1230
+ > **Ordering flip.** `g.stashes_all` returns entries oldest first with a
598
1231
  > sequential index of its own (`0` is the oldest). `g.stash_infos` returns entries
599
- > **newest first**, the order `git stash list` uses, and `Git::StashInfo#index` is
1232
+ > newest first, the order `git stash list` uses, and `Git::StashInfo#index` is
600
1233
  > git's own `stash@{N}` number (`0` is the newest). `g.stashes_all.first` is
601
1234
  > `g.stash_infos.last`. Code that reads an entry by position must reverse the
602
1235
  > array or the index.
603
1236
 
604
- > **Message difference:** `g.stashes_all` strips the `WIP on <branch>:` or
1237
+ > **Message difference.** `g.stashes_all` strips the `WIP on <branch>:` or
605
1238
  > `On <branch>:` prefix from each message. `Git::StashInfo#message` keeps the full
606
1239
  > message git stores, and `Git::StashInfo#branch` holds the branch name. A stash
607
1240
  > created from a detached HEAD has the branch `"(no branch)"`, the label git writes
@@ -614,32 +1247,38 @@ new `Git::StashInfo`, or `nil` when there were no local changes, so a truthiness
614
1247
  check such as `if g.stash_push(message: 'WIP')` still works.
615
1248
 
616
1249
  `g.stash_list` returned the `git stash list` text as a String. Build that text from
617
- `g.stash_infos` if you need it. In v6.0.0, `stash_list` returns
618
- `Array<Git::StashInfo>`, the same value as `stash_infos`, and `stash_infos` stays as
619
- a permanent alias. Move String callers of `stash_list` to `stash_infos` before
620
- upgrading so the return type change cannot go unnoticed.
1250
+ `g.stash_infos` if you need it. In v6.0.0 `stash_list` returns what `stash_infos`
1251
+ returns (see [`stash_list` returns an Array of
1252
+ `Git::StashInfo`](#stash_list-returns-an-array-of-gitstashinfo)), so move String
1253
+ callers of `stash_list` to `stash_infos` before upgrading so the return type change
1254
+ cannot go unnoticed.
1255
+
1256
+ > **`stash_store` in v6.0.0.** On v5.x, `g.stash_store` returns the top entry of
1257
+ > the stash list after the store, which is `nil` when the list is empty. v6.0.0
1258
+ > changes that; see [`stash_store` raises when the stored entry is
1259
+ > missing](#stash_store-raises-when-the-stored-entry-is-missing).
621
1260
 
622
1261
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
623
1262
  |-----------------------------------------------------|-------------|
624
- | `g.stashes_all` | `g.stash_infos` — returns `Array<Git::StashInfo>`, newest first |
1263
+ | `g.stashes_all` | `g.stash_infos`. Returns `Array<Git::StashInfo>`, newest first |
625
1264
  | `g.stashes_all.each { \|index, message\| ... }` | `g.stash_infos.reverse_each.with_index { \|info, index\| ... info.message }` |
626
1265
  | `g.stashes_all[i]` (`0` = oldest) | `g.stash_infos.reverse[i]` |
627
1266
  | `g.stashes_all.last` | `g.stash_infos.first` |
628
- | `g.stash_save(message)` | `g.stash_push(message: message)` — returns `Git::StashInfo` or `nil` |
1267
+ | `g.stash_save(message)` | `g.stash_push(message: message)`. Returns `Git::StashInfo` or `nil` |
629
1268
  | `g.stash_list` (String) | `g.stash_infos.map { \|s\| "#{s.name}: #{s.message}" }.join("\n")` |
630
1269
  | `Git::Stash.new(g, message)` | `info = g.stash_push(message: message)` |
631
- | `Git::Stash.new(g, message, existing: true)` | `message` — `existing: true` only wrapped the String and never looked an entry up; code that needs a real entry picks one from `g.stash_infos` by index or name |
1270
+ | `Git::Stash.new(g, message, existing: true)` | `message`. `existing: true` only wrapped the String and never looked an entry up; code that needs a real entry picks one from `g.stash_infos` by index or name |
632
1271
  | `stash.save` | `info = g.stash_push(message: message)` |
633
- | `stash.saved?` | `!info.nil?` — check the value `stash_push` returned rather than pushing again |
634
- | `stash.message` / `stash.to_s` | `info.message` — keeps the branch prefix; see the note above |
1272
+ | `stash.saved?` | `!info.nil?`. Check the value `stash_push` returned rather than pushing again |
1273
+ | `stash.message` / `stash.to_s` | `info.message`. Keeps the branch prefix; see the note above |
635
1274
  | `Git::Stashes.new(g)` | `g.stash_infos` |
636
- | `stashes.all` (`[index, message]` pairs, oldest first) | `g.stash_infos.reverse` — see the ordering note above |
1275
+ | `stashes.all` (`[index, message]` pairs, oldest first) | `g.stash_infos.reverse`. See the ordering note above |
637
1276
  | `stashes.each { \|s\| ... }` (newest first) | `g.stash_infos.each { \|info\| ... }` |
638
1277
  | `stashes[i]` (`0` = newest, `i` coerced with `to_i`) | `g.stash_infos[i.to_i]` |
639
1278
  | `stashes.size` | `g.stash_infos.size` |
640
1279
  | `stashes.save(message)` | `g.stash_push(message: message)` |
641
1280
  | `stashes.apply` / `stashes.apply(i)` | `g.stash_apply` / `g.stash_apply(i)` |
642
- | `stashes.clear` | `g.stash_clear` — returns git's stdout (normally `""`, which is truthy) where `Git::Stashes#clear` returned `nil` |
1281
+ | `stashes.clear` | `g.stash_clear`. Returns git's stdout (normally `""`, which is truthy) where `Git::Stashes#clear` returned `nil` |
643
1282
 
644
1283
  #### `Git::Repository#remotes` deprecated
645
1284
 
@@ -649,10 +1288,10 @@ one deprecation warning for itself plus one `Git::Remote` constructor warning fo
649
1288
  each remote it returns (see the `Git::Remote` deprecation below), so a repository
650
1289
  with N remotes produces N + 1 warnings per call.
651
1290
 
652
- > **Return type change:** `remotes` returns `Array<Git::Remote>` — mutable
1291
+ > **Return type change.** `remotes` returns `Array<Git::Remote>`, mutable
653
1292
  > objects with `name`, `url`, and `fetch_opts` accessors and `fetch`, `merge`,
654
1293
  > `branch`, and `remove` operations. `remote_list` returns
655
- > `Array<Git::RemoteInfo>` — immutable value objects read from the repository's
1294
+ > `Array<Git::RemoteInfo>`, immutable value objects read from the repository's
656
1295
  > git config, with fields such as `name`, `url`, `push_url`, `fetch`, and `push`.
657
1296
  > Because a remote may carry more than one URL or refspec, `url`, `push_url`,
658
1297
  > `fetch`, and `push` are always frozen `Array<String>`. When a remote has more
@@ -663,25 +1302,25 @@ with N remotes produces N + 1 warnings per call.
663
1302
  > `Git::Remote` are called on the repository instead; the table below shows
664
1303
  > the argument each one takes.
665
1304
  >
666
- > **Order change:** `remotes` lists remotes in the order `git remote` prints
1305
+ > **Order change.** `remotes` lists remotes in the order `git remote` prints
667
1306
  > them, while `remote_list` keeps the order in which remotes first appear in the
668
1307
  > config. When the legacy order matters, iterate `g.remote_names` (the same
669
1308
  > `git remote` order) or sort `g.remote_list` explicitly.
670
1309
 
671
1310
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
672
1311
  |-----------------------------------------------------|-------------|
673
- | `g.remotes` | `g.remote_list` — returns `Array<Git::RemoteInfo>` |
1312
+ | `g.remotes` | `g.remote_list`. Returns `Array<Git::RemoteInfo>` |
674
1313
  | `g.remotes.map(&:name)` | `g.remote_list.map(&:name)` or `g.remote_names` |
675
- | `g.remotes.map(&:to_s)` | `g.remote_list.map(&:name)` — `Git::RemoteInfo#to_s` is not the name |
676
- | `g.remotes.map(&:url)` | `g.remote_list.map { \|r\| r.url.first }` — `url` is an `Array<String>` |
677
- | `g.remotes.map(&:fetch_opts)` | `g.remote_list.map { \|r\| r.fetch.last }` — `fetch` holds every refspec |
678
- | `g.remotes.each(&:fetch)` | `g.remote_names.each { \|name\| g.fetch(name) }` — same order as `remotes` |
1314
+ | `g.remotes.map(&:to_s)` | `g.remote_list.map(&:name)`. `Git::RemoteInfo#to_s` is not the name |
1315
+ | `g.remotes.map(&:url)` | `g.remote_list.map { \|r\| r.url.first }`. `url` is an `Array<String>` |
1316
+ | `g.remotes.map(&:fetch_opts)` | `g.remote_list.map { \|r\| r.fetch.last }`. `fetch` holds every refspec |
1317
+ | `g.remotes.each(&:fetch)` | `g.remote_names.each { \|name\| g.fetch(name) }`. Same order as `remotes` |
679
1318
  | `remote.fetch` | `g.fetch(remote.name)` |
680
- | `remote.fetch(opts)` | `g.fetch(remote.name, opts)` — same options hash |
1319
+ | `remote.fetch(opts)` | `g.fetch(remote.name, opts)`. Same options hash |
681
1320
  | `remote.merge` | `g.merge("#{remote.name}/#{g.current_branch}")` |
682
1321
  | `remote.merge(branch)` | `g.merge("#{remote.name}/#{branch}")` |
683
- | `remote.branch` | `g.branch_list("#{remote.name}/#{g.current_branch}").find(&:remote?)` — returns a `Git::BranchInfo` |
684
- | `remote.branch(name)` | `g.branch_list("#{remote.name}/#{name}").find(&:remote?)` — returns a `Git::BranchInfo` |
1322
+ | `remote.branch` | `g.branch_list("#{remote.name}/#{g.current_branch}").find(&:remote?)`. Returns a `Git::BranchInfo` |
1323
+ | `remote.branch(name)` | `g.branch_list("#{remote.name}/#{name}").find(&:remote?)`. Returns a `Git::BranchInfo` |
685
1324
  | `remote.remove` | `g.remote_remove(remote.name)` |
686
1325
 
687
1326
  #### `Git::Remote` deprecated
@@ -697,7 +1336,7 @@ and so does calling `g.config_remote`. Calling `g.remote` emits two: one for
697
1336
  (N + 1 for N remotes). The extra warnings from `g.remote` and `g.remotes` are
698
1337
  expected, not a bug.
699
1338
 
700
- > **Return type changes:** `Git::RemoteInfo#url` and `Git::RemoteInfo#fetch` are
1339
+ > **Return type changes.** `Git::RemoteInfo#url` and `Git::RemoteInfo#fetch` are
701
1340
  > frozen `Array<String>` because a remote may carry more than one URL or fetch
702
1341
  > refspec. The legacy `Git::Remote#url` and `Git::Remote#fetch_opts` returned only
703
1342
  > the last configured value, so `r.url.last` and `r.fetch.last` reproduce them
@@ -718,19 +1357,19 @@ In the table, `name` is the remote name (`g.remote` defaults it to `'origin'`).
718
1357
 
719
1358
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
720
1359
  |-----------------------------------------------------|-------------|
721
- | `g.remote` | `g.remote_list.find { \|r\| r.name == 'origin' }` — returns a `Git::RemoteInfo`; the deprecated call emits two warnings |
722
- | `g.remote(name)` | `g.remote_list.find { \|r\| r.name == name }` — returns a `Git::RemoteInfo`; the deprecated call emits two warnings |
723
- | `g.config_remote(name)` for `url`, `fetch`, and the other modeled fields | `g.remote_list.find { \|r\| r.name == name }` — a `Git::RemoteInfo`, not a `Hash` |
724
- | `g.config_remote(name)` for every key, including custom ones | `g.config_list.select { \|e\| e.key.start_with?("remote.#{name}.") }.to_h { \|e\| [e.key.delete_prefix("remote.#{name}."), e.value] }` — the same `Hash{String => String}` |
1360
+ | `g.remote` | `g.remote_list.find { \|r\| r.name == 'origin' }`. Returns a `Git::RemoteInfo`; the deprecated call emits two warnings |
1361
+ | `g.remote(name)` | `g.remote_list.find { \|r\| r.name == name }`. Returns a `Git::RemoteInfo`; the deprecated call emits two warnings |
1362
+ | `g.config_remote(name)` for `url`, `fetch`, and the other modeled fields | `g.remote_list.find { \|r\| r.name == name }`. A `Git::RemoteInfo`, not a `Hash` |
1363
+ | `g.config_remote(name)` for every key, including custom ones | `g.config_list.select { \|e\| e.key.start_with?("remote.#{name}.") }.to_h { \|e\| [e.key.delete_prefix("remote.#{name}."), e.value] }`. The same `Hash{String => String}` |
725
1364
  | `remote.name`, `remote.to_s` | `g.remote_list.find { \|r\| r.name == name }.name` or `g.remote_names` |
726
- | `remote.url` | `g.remote_list.find { \|r\| r.name == name }.url` — `Array<String>`; `.first` for the single-URL case |
727
- | `remote.fetch_opts` | `g.remote_list.find { \|r\| r.name == name }.fetch` — `Array<String>` of refspecs |
1365
+ | `remote.url` | `g.remote_list.find { \|r\| r.name == name }.url`. `Array<String>`; `.first` for the single-URL case |
1366
+ | `remote.fetch_opts` | `g.remote_list.find { \|r\| r.name == name }.fetch`. `Array<String>` of refspecs |
728
1367
  | `remote.fetch` | `g.fetch(name)` |
729
- | `remote.fetch(opts)` | `g.fetch(name, opts)` — same option keys |
1368
+ | `remote.fetch(opts)` | `g.fetch(name, opts)`. Same option keys |
730
1369
  | `remote.merge` | `g.merge("#{name}/#{g.current_branch}")` |
731
1370
  | `remote.merge(branch)` | `g.merge("#{name}/#{branch}")` |
732
- | `remote.branch` | `g.branch_list("#{name}/#{g.current_branch}").find(&:remote?)` — returns a `Git::BranchInfo` |
733
- | `remote.branch(branch)` | `g.branch_list("#{name}/#{branch}").find(&:remote?)` — returns a `Git::BranchInfo` |
1371
+ | `remote.branch` | `g.branch_list("#{name}/#{g.current_branch}").find(&:remote?)`. Returns a `Git::BranchInfo` |
1372
+ | `remote.branch(branch)` | `g.branch_list("#{name}/#{branch}").find(&:remote?)`. Returns a `Git::BranchInfo` |
734
1373
  | `remote.remove` | `g.remote_remove(name)` |
735
1374
 
736
1375
  #### `Git::Commands::CatFile::Raw` `allow_unknown_type` option deprecated
@@ -767,7 +1406,7 @@ any operation on a `Git::Branch` each emit a deprecation warning; their return
767
1406
  values are unchanged. The `full`, `name`, `remote`, `to_s`, and `to_a` readers
768
1407
  on `Git::Branch` do not warn.
769
1408
 
770
- > **Return shape change:** `Git::Branch` exposes `full` (`main` or
1409
+ > **Return shape change.** `Git::Branch` exposes `full` (`main` or
771
1410
  > `remotes/origin/main`), `name`, and `remote` (a `Git::Remote`, or `nil`).
772
1411
  > `Git::BranchInfo` exposes `refname` (always the full ref: `refs/heads/main` or
773
1412
  > `refs/remotes/origin/main`), `short_name` (`main` for both), `remote_name` (a
@@ -778,7 +1417,7 @@ on `Git::Branch` do not warn.
778
1417
  > `'origin/main'` matches the remote-tracking branch. The `remotes/origin/main`
779
1418
  > and `refs/...` forms that `g.branches[...]` accepted match nothing.
780
1419
  >
781
- > **`checkout` no longer creates the branch:** `g.branch('x').checkout` created
1420
+ > **`checkout` no longer creates the branch.** `g.branch('x').checkout` created
782
1421
  > `x` when it did not exist, ignoring any error from that attempt, and then
783
1422
  > checked it out. `g.checkout('x')` does not create a missing local branch,
784
1423
  > with one exception that is git's own: when exactly one remote has a branch
@@ -824,33 +1463,33 @@ can resolve a local branch of that name and is only used where git expects it
824
1463
 
825
1464
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
826
1465
  |-----------------------------------------------------|-------------|
827
- | `g.branch(name)` | `g.branch_list(name).first` for a local branch, or `g.branch_list("#{remote}/#{name}").find(&:remote?)` for a remote-tracking one — a `Git::BranchInfo`, or `nil` when the branch does not exist; the `remotes/` and `refs/` forms match nothing |
828
- | `g.branch` | `g.branch_list(g.current_branch).first` — `nil` when HEAD is detached or unborn; use `g.current_branch_state` there |
829
- | `g.branches` | `g.branch_list` — returns `Array<Git::BranchInfo>` |
1466
+ | `g.branch(name)` | `g.branch_list(name).first` for a local branch, or `g.branch_list("#{remote}/#{name}").find(&:remote?)` for a remote-tracking one. A `Git::BranchInfo`, or `nil` when the branch does not exist; the `remotes/` and `refs/` forms match nothing |
1467
+ | `g.branch` | `g.branch_list(g.current_branch).first`. `nil` when HEAD is detached or unborn; use `g.current_branch_state` there |
1468
+ | `g.branches` | `g.branch_list`. Returns `Array<Git::BranchInfo>` |
830
1469
  | `g.branches[name]` | `g.branch_list(name).first`, or `g.branch_list("#{remote}/#{name}").find(&:remote?)` for a remote-tracking branch |
831
1470
  | `g.branches.local` | `g.branch_list.reject(&:remote?)` |
832
1471
  | `g.branches.remote` | `g.branch_list.select(&:remote?)` |
833
1472
  | `g.branches.size` | `g.branch_list.size` |
834
1473
  | `g.branches.each { \|b\| ... }` | `g.branch_list.each { \|info\| ... }` |
835
- | `g.branches.to_s` | `g.branch_list.map { \|i\| "#{i.current? ? '* ' : ' '}#{i.refname}\n" }.join` — full refs, not `remotes/...` |
836
- | `b.full`, `b.to_s` | `info.refname` — `refs/remotes/origin/main` rather than `remotes/origin/main` |
1474
+ | `g.branches.to_s` | `g.branch_list.map { \|i\| "#{i.current? ? '* ' : ' '}#{i.refname}\n" }.join`. Full refs, not `remotes/...` |
1475
+ | `b.full`, `b.to_s` | `info.refname`. `refs/remotes/origin/main` rather than `remotes/origin/main` |
837
1476
  | `b.to_a` | `[info.refname]` |
838
1477
  | `b.name` | `info.short_name` |
839
- | `b.remote` | `info.remote_name` — a `String`, or `nil` for a local branch |
840
- | `b.gcommit` | `g.gcommit(name)` — pass `info.refname` for a remote-tracking branch |
841
- | `b.checkout` | `g.checkout(name)` — does not create the branch (see above); pass `info.refname` for a remote-tracking branch |
842
- | `b.create` | `g.branch_new(name)` — raises when the branch already exists |
1478
+ | `b.remote` | `info.remote_name`. A `String`, or `nil` for a local branch |
1479
+ | `b.gcommit` | `g.gcommit(name)`. Pass `info.refname` for a remote-tracking branch |
1480
+ | `b.checkout` | `g.checkout(name)`. Does not create the branch (see above); pass `info.refname` for a remote-tracking branch |
1481
+ | `b.create` | `g.branch_new(name)`. Raises when the branch already exists |
843
1482
  | `b.delete` (local) | `g.branch_delete(name)` |
844
1483
  | `b.delete` (remote-tracking) | `g.branch_delete("#{remote}/#{name}", remotes: true)` |
845
1484
  | `b.current` | `g.current_branch == name` |
846
1485
  | `b.contains?(commit)` | `!g.branch_contains(commit, name).empty?` |
847
1486
  | `b.merge` | `g.merge(name)` |
848
- | `b.merge(branch, message)` | `g.merge_into(name, branch, message)` — local `b` only; see the differences above |
1487
+ | `b.merge(branch, message)` | `g.merge_into(name, branch, message)`. Local `b` only; see the differences above |
849
1488
  | `b.update_ref(commit)` (local) | `g.update_ref(name, commit)` |
850
1489
  | `b.update_ref(commit)` (remote-tracking) | `g.update_ref("remotes/#{remote}/#{name}", commit)` |
851
- | `b.archive(file, opts)` | `g.archive(name, file, opts)` — pass `info.refname` for a remote-tracking branch |
852
- | `b.in_branch(message) { ... }` | `g.in_branch(name, message) { ... }` — local `b` only; see the differences above |
853
- | `b.stashes` | `g.stash_infos` — see [`Git::Branch#stashes` deprecated](#gitbranchstashes-deprecated) |
1490
+ | `b.archive(file, opts)` | `g.archive(name, file, opts)`. Pass `info.refname` for a remote-tracking branch |
1491
+ | `b.in_branch(message) { ... }` | `g.in_branch(name, message) { ... }`. Local `b` only; see the differences above |
1492
+ | `b.stashes` | `g.stash_infos`. See [`Git::Branch#stashes` deprecated](#gitbranchstashes-deprecated) |
854
1493
 
855
1494
  #### `Git::Object::Tag` deprecated
856
1495
 
@@ -869,7 +1508,7 @@ for the `g.tag_add` it calls; `g.add_tag(name, d: true)` emits three, adding the
869
1508
  `:d`/`:delete` warning described below. The readers on a `Git::Object::Tag` do not
870
1509
  warn.
871
1510
 
872
- > **Return shape change:** `Git::Object::Tag` exposes `name`, `sha`, `objectish`,
1511
+ > **Return shape change.** `Git::Object::Tag` exposes `name`, `sha`, `objectish`,
873
1512
  > `annotated?`, `message`, and `tagger`. `Git::TagInfo` exposes `name`, `oid`,
874
1513
  > `target_oid`, `objecttype`, `annotated?`, `lightweight?`, `message`, and
875
1514
  > `tagger`. `name` and `annotated?` are unchanged. `tagger` keeps the same `name`
@@ -887,25 +1526,23 @@ warn.
887
1526
  > which kind (`tag` for an annotated tag, or the target's own type such as
888
1527
  > `commit` or `blob` for a lightweight one).
889
1528
  >
890
- > **Missing tags:** `g.tag(name)` raises `Git::UnexpectedResultError` when no tag
1529
+ > **Missing tags.** `g.tag(name)` raises `Git::UnexpectedResultError` when no tag
891
1530
  > has that name. `g.tag_list(name).first` returns `nil`.
892
1531
  >
893
- > **`tag_create` when the new tag is missing in v6.0.0:** on v5.x, `g.tag_create`
894
- > returns `nil` when the tag it has just created is missing from the tag list
895
- > (for example, another process deleted it before the lookup). In v6.0.0 it
896
- > raises `Git::UnexpectedResultError` instead, so its return value is always a
897
- > `Git::TagInfo`. A `nil` check on the result keeps working today and becomes
898
- > unreachable in v6.0.0.
1532
+ > **`tag_create` in v6.0.0.** On v5.x, `g.tag_create` returns `nil` when the tag it
1533
+ > has just created is missing from the tag list. v6.0.0 raises instead; see
1534
+ > [`tag_create` raises when the created tag is
1535
+ > missing](#tag_create-raises-when-the-created-tag-is-missing).
899
1536
  >
900
- > **Deleting through `tag_add`:** `g.tag_add(name, d: true)`, which was already
1537
+ > **Deleting through `tag_add`.** `g.tag_add(name, d: true)`, which was already
901
1538
  > deprecated, deletes the tag and emits a second warning pointing at
902
1539
  > `g.tag_delete`. `g.tag_create` rejects `:d` and `:delete` with `ArgumentError`.
903
1540
  >
904
- > **Extra positional arguments:** `g.tag_add(name, target, extra)` ignores
1541
+ > **Extra positional arguments.** `g.tag_add(name, target, extra)` ignores
905
1542
  > `extra` and tags `target`. `g.tag_create` raises `ArgumentError` when more than
906
1543
  > one positional argument follows the name.
907
1544
  >
908
- > **Object identity:** every `Git::Object::Tag` resolves its tag to an object ID
1545
+ > **Object identity.** Every `Git::Object::Tag` resolves its tag to an object ID
909
1546
  > when it is constructed and runs `size`, `contents`, `grep`, `diff`, `log`, and
910
1547
  > `archive` against that ID, so moving or deleting the tag afterwards does not
911
1548
  > redirect an existing object. `Git::Object::Tag.new(g, sha, name)` uses the
@@ -924,26 +1561,26 @@ In the table, `name` is the tag name, `t` is a `Git::Object::Tag`, `info` is the
924
1561
 
925
1562
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
926
1563
  |-----------------------------------------------------|-------------|
927
- | `g.tag(name)` | `g.tag_list(name).first` — a `Git::TagInfo`, or `nil` when the tag does not exist |
928
- | `g.tags` | `g.tag_list` — returns `Array<Git::TagInfo>` |
1564
+ | `g.tag(name)` | `g.tag_list(name).first`. A `Git::TagInfo`, or `nil` when the tag does not exist |
1565
+ | `g.tags` | `g.tag_list`. Returns `Array<Git::TagInfo>` |
929
1566
  | `g.tags.map(&:name)` | `g.tag_list.map(&:name)` |
930
- | `g.tag_add(name, opts)` | `g.tag_create(name, opts)` — returns a `Git::TagInfo` |
1567
+ | `g.tag_add(name, opts)` | `g.tag_create(name, opts)`. Returns a `Git::TagInfo` |
931
1568
  | `g.tag_add(name, target, opts)` | `g.tag_create(name, target, opts)` |
932
1569
  | `g.tag_add(name, d: true)` | `g.tag_delete(name)` |
933
- | `g.add_tag(name, opts)`, `g.add_tag(name, target, opts)` | `g.tag_create(name, ...)` — its warning names `g.tag_add`, which is deprecated too; go straight to `g.tag_create` |
934
- | `g.add_tag(name, d: true)` | `g.tag_delete(name)` — `g.tag_create` rejects `:d`; see the deletion note above |
1570
+ | `g.add_tag(name, opts)`, `g.add_tag(name, target, opts)` | `g.tag_create(name, ...)`. Its warning names `g.tag_add`, which is deprecated too; go straight to `g.tag_create` |
1571
+ | `g.add_tag(name, d: true)` | `g.tag_delete(name)`. `g.tag_create` rejects `:d`; see the deletion note above |
935
1572
  | `Git::Object::Tag.new(g, name)` | `g.tag_list(name).first` |
936
- | `Git::Object::Tag.new(g, sha, name)` | `g.tag_list(name).first` — reads the ref rather than `sha`; use `sha` as `id` for the operations below, or read the object with `g.cat_file_tag(sha)`; see the object identity note above |
937
- | `Git::Object.new(g, name, nil, true)` | `g.tag_list(name).first` — its warning names `Git::Object::Tag.new`, which is deprecated too |
1573
+ | `Git::Object::Tag.new(g, sha, name)` | `g.tag_list(name).first`. Reads the ref rather than `sha`; use `sha` as `id` for the operations below, or read the object with `g.cat_file_tag(sha)`; see the object identity note above |
1574
+ | `Git::Object.new(g, name, nil, true)` | `g.tag_list(name).first`. Its warning names `Git::Object::Tag.new`, which is deprecated too |
938
1575
  | `t.name` | `info.name` |
939
- | `t.sha`, `t.objectish`, `t.to_s` | `info.oid \|\| info.target_oid` — see the return shape change above |
1576
+ | `t.sha`, `t.objectish`, `t.to_s` | `info.oid \|\| info.target_oid`. See the return shape change above |
940
1577
  | `t.annotated?` | `info.annotated?` |
941
- | `t.message` | `info.message` — `nil` rather than `""` for an annotated tag with an empty message |
942
- | `t.tagger` | `info.tagger` — `date` keeps the recorded UTC offset; see the return shape change above |
1578
+ | `t.message` | `info.message`. `nil` rather than `""` for an annotated tag with an empty message |
1579
+ | `t.tagger` | `info.tagger`. `date` keeps the recorded UTC offset; see the return shape change above |
943
1580
  | `t.tag?` | not needed; every `Git::TagInfo` is a tag |
944
- | `t.size` | `g.cat_file_size(id)` — `id` rather than `name` keeps this and the operations below on the object `t` pinned; see the object identity note above |
1581
+ | `t.size` | `g.cat_file_size(id)`. `id` rather than `name` keeps this and the operations below on the object `t` pinned; see the object identity note above |
945
1582
  | `t.contents` | `g.cat_file_contents(id)` |
946
- | `t.contents { \|file\| ... }` | `g.cat_file_contents(id) { \|file\| ... }` — streams to a temporary file instead of buffering the object |
1583
+ | `t.contents { \|file\| ... }` | `g.cat_file_contents(id) { \|file\| ... }`. Streams to a temporary file instead of buffering the object |
947
1584
  | `t.contents_array` | `g.cat_file_contents(id).split("\n")` |
948
1585
  | `t.grep(string, path, opts)` | `g.grep(string, path, opts.merge(object: id))` |
949
1586
  | `t.diff(other)` | `g.diff(id, other)` |
@@ -953,7 +1590,7 @@ In the table, `name` is the tag name, `t` is a `Git::Object::Tag`, `info` is the
953
1590
  #### `Git::Status` deprecated
954
1591
 
955
1592
  Starting in v5.4.0, `Git::Status`, `Git::Status::StatusFile`, and
956
- `Git::Repository#status` are deprecated and will be removed in v6.0.0. Read the
1593
+ `Git::Repository#status` are deprecated and are removed in v6.0.0. Read the
957
1594
  index and working tree state through `Git::Repository#status_info`, which
958
1595
  returns an immutable `Git::StatusInfo` holding one `Git::StatusFileInfo` per
959
1596
  path that `git status --porcelain=v2` reports. Calling `g.status` emits one
@@ -998,7 +1635,7 @@ reference, so `blob` is gone; fetch the object through the repository instead.
998
1635
  `stage` is gone too: an unmerged entry carries its stage 1, 2, and 3 modes and
999
1636
  SHAs in `unmerged_stages`, and every other entry is at stage 0.
1000
1637
 
1001
- > **Field renames:** the legacy mode and SHA readers were named for the wrong
1638
+ > **Field renames.** The legacy mode and SHA readers were named for the wrong
1002
1639
  > sides. `sha_index` and `mode_index` held the working-tree side of the diff:
1003
1640
  > the index blob when the working tree matched the index, and an all-zero SHA
1004
1641
  > when it did not. `sha_repo` and `mode_repo` held the side git compared the
@@ -1016,12 +1653,12 @@ In the table, `g` is a `Git::Repository`, `status` is the `Git::Status` from
1016
1653
 
1017
1654
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
1018
1655
  |-----------------------------------------------------|-------------|
1019
- | `g.status` | `g.status_info` — returns a `Git::StatusInfo` |
1656
+ | `g.status` | `g.status_info`. Returns a `Git::StatusInfo` |
1020
1657
  | `Git::Status.new(g)` | `g.status_info` |
1021
- | `status.changed`, `status.added`, `status.deleted`, `status.untracked` | same names on `g.status_info` — now `Hash{String => Git::StatusFileInfo}` keyed by path |
1658
+ | `status.changed`, `status.added`, `status.deleted`, `status.untracked` | same names on `g.status_info`. Now `Hash{String => Git::StatusFileInfo}` keyed by path |
1022
1659
  | `status.changed?(path)`, `status.added?(path)`, `status.deleted?(path)`, `status.untracked?(path)` | same names on `g.status_info` |
1023
- | `status[path]` | `g.status_info[path]` — a `Git::StatusFileInfo`, or `nil`; `nil` for a clean tracked path, which `status[path]` reported (see above) |
1024
- | `status.each { \|file\| ... }` | `g.status_info.files.each { \|info\| ... }` — does not yield clean tracked paths (see above) |
1660
+ | `status[path]` | `g.status_info[path]`. A `Git::StatusFileInfo`, or `nil`; `nil` for a clean tracked path, which `status[path]` reported (see above) |
1661
+ | `status.each { \|file\| ... }` | `g.status_info.files.each { \|info\| ... }`. Does not yield clean tracked paths (see above) |
1025
1662
  | `status.pretty` | no replacement; format `g.status_info.files` yourself |
1026
1663
  | `file.path` | `info.path` |
1027
1664
  | `file.type` | `info.index_status` and `info.worktree_status`, or the `info.changed?`, `info.added?`, and `info.deleted?` predicates |
@@ -1031,8 +1668,8 @@ In the table, `g` is a `Git::Repository`, `status` is the `Git::Status` from
1031
1668
  | `file.mode_repo` | `info.mode_head`, or `info.mode_index` in a repository with no commits |
1032
1669
  | `file.sha_index` | `info.sha_index` for the staged blob; `info.worktree_status` says whether the working tree differs from it |
1033
1670
  | `file.mode_index` | `info.mode_worktree` |
1034
- | `file.blob` | `g.object(info.sha_index)` when `info.sha_index` is set and not all zeros — it is `nil` for untracked, ignored, and unmerged entries and all zeros when the path is not in the index; legacy `blob` returned `nil` without a lookup when no SHA was available and fell back to `sha_repo` when `sha_index` was `nil`. For an unmerged entry read a stage instead: `g.object(info.unmerged_stages[2][:sha])` |
1035
- | `file.blob(:repo)` | `g.object(info.sha_head)` when `info.sha_head` is set and not all zeros — it is `nil` for untracked, ignored, and unmerged entries and all zeros when the path is not in HEAD |
1671
+ | `file.blob` | `g.object(info.sha_index)` when `info.sha_index` is set and not all zeros. It is `nil` for untracked, ignored, and unmerged entries and all zeros when the path is not in the index; legacy `blob` returned `nil` without a lookup when no SHA was available and fell back to `sha_repo` when `sha_index` was `nil`. For an unmerged entry read a stage instead: `g.object(info.unmerged_stages[2][:sha])` |
1672
+ | `file.blob(:repo)` | `g.object(info.sha_head)` when `info.sha_head` is set and not all zeros. It is `nil` for untracked, ignored, and unmerged entries and all zeros when the path is not in HEAD |
1036
1673
 
1037
1674
  #### `Git::Worktree` and `Git::Worktrees` deprecated
1038
1675
 
@@ -1051,7 +1688,7 @@ warning; the `dir`, `full`, `to_s`, and `to_a` readers on `Git::Worktree` do not
1051
1688
  it constructs, and `g.worktree(dir).add` emits one for `g.worktree` and one for
1052
1689
  `add`.
1053
1690
 
1054
- > **Return shape change:** `worktrees_all` returns `[directory, sha]` pairs and
1691
+ > **Return shape change.** `worktrees_all` returns `[directory, sha]` pairs and
1055
1692
  > omits the main worktree of a bare repository, which has no checked-out commit.
1056
1693
  > `worktree_list` returns `Git::WorktreeInfo` objects with `path`, `head`,
1057
1694
  > `branch` (the full refname, such as `refs/heads/main`, or `nil` when detached
@@ -1060,19 +1697,22 @@ it constructs, and `g.worktree(dir).add` emits one for `g.worktree` and one for
1060
1697
  > `branch` set to `nil`. `Git::WorktreeInfo#to_s` is the path, so an entry can be
1061
1698
  > passed to any method that takes a worktree path.
1062
1699
  >
1063
- > **`gcommit` return type:** `Git::Worktree#gcommit` returned a
1700
+ > **`gcommit` return type.** `Git::Worktree#gcommit` returned a
1064
1701
  > `Git::Object::Commit` for a worktree obtained from `g.worktree(dir)` and a raw
1065
1702
  > SHA `String` for one obtained from `g.worktrees`. `info.head` is always a
1066
1703
  > `String` (or `nil` for a bare main worktree); call `g.gcommit(info.head)` for
1067
1704
  > the commit object.
1068
1705
  >
1069
- > **`full` and `to_s`:** `Git::Worktree#full` and `#to_s` append the commitish
1706
+ > **`full` and `to_s`.** `Git::Worktree#full` and `#to_s` append the commitish
1070
1707
  > given at construction to the path, so entries from `g.worktrees` read
1071
1708
  > `"/path/to/wt <sha>"`. `Git::WorktreeInfo#to_s` is the path alone.
1072
1709
  >
1073
- > **`worktree_add` return type in v6.0.0:** on v5.x, `worktree_add` returns the
1710
+ > **`worktree_add` in v6.0.0.** On v5.x, `worktree_add` returns the
1074
1711
  > `git worktree add` output (`"HEAD is now at …"`). In v6.0.0 it returns the new
1075
- > `Git::WorktreeInfo` instead. To get the entry today, look it up after the add:
1712
+ > `Git::WorktreeInfo` instead (see [`worktree_add` returns `Git::WorktreeInfo` and
1713
+ > raises when the worktree is
1714
+ > missing](#worktree_add-returns-gitworktreeinfo-and-raises-when-the-worktree-is-missing)).
1715
+ > To get the entry today, look it up after the add:
1076
1716
  > `g.worktree_list.find { |w| File.identical?(w.path, dir) }`. Compare with
1077
1717
  > `File.identical?`, not string equality: git stores worktree paths with
1078
1718
  > symlinks resolved, so `/tmp/...` on macOS is listed as `/private/tmp/...`,
@@ -1083,27 +1723,28 @@ is the `Git::WorktreeInfo` that replaces it.
1083
1723
 
1084
1724
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
1085
1725
  |-----------------------------------------------------|-------------|
1086
- | `g.worktrees_all` | `g.worktree_list.map { \|w\| [w.path, w.head] }` — includes a bare main worktree as `[path, nil]`; add `.reject(&:bare?)` before `map` to omit it as `worktrees_all` did |
1087
- | `g.worktrees` | `g.worktree_list` — returns `Array<Git::WorktreeInfo>`; the deprecated call emits two warnings |
1088
- | `g.worktrees[dir]` | `g.worktree_list.find { \|w\| w.path == dir }` — `nil` when not found; `dir` is the path as git reports it (absolute, with symlinks resolved), as before |
1726
+ | `g.worktrees_all` | `g.worktree_list.map { \|w\| [w.path, w.head] }`. Includes a bare main worktree as `[path, nil]`; add `.reject(&:bare?)` before `map` to omit it as `worktrees_all` did |
1727
+ | `g.worktrees` | `g.worktree_list`. Returns `Array<Git::WorktreeInfo>`; the deprecated call emits two warnings |
1728
+ | `g.worktrees[dir]` | `g.worktree_list.find { \|w\| w.path == dir }`. `nil` when not found; `dir` is the path as git reports it (absolute, with symlinks resolved), as before |
1089
1729
  | `g.worktrees.size` | `g.worktree_list.size` |
1090
1730
  | `g.worktrees.each { \|wt\| ... }` | `g.worktree_list.each { \|info\| ... }` |
1091
1731
  | `g.worktrees.to_s` | `g.worktree_list.map { \|w\| "#{w.path} #{w.head}\n" }.join` |
1092
1732
  | `g.worktrees.prune` | `g.worktree_prune` |
1093
1733
  | `g.worktree(dir).add` | `g.worktree_add(dir)` |
1094
1734
  | `g.worktree(dir, commitish).add` | `g.worktree_add(dir, commitish)` |
1095
- | `g.worktree(dir).remove` | `g.worktree_remove(dir)` — or `g.worktree_remove(info)` |
1096
- | `wt.gcommit` | `info.head` — always a `String`, or `nil` for a bare main worktree; `g.gcommit(info.head)` for the commit object |
1735
+ | `g.worktree(dir).remove` | `g.worktree_remove(dir)` or `g.worktree_remove(info)` |
1736
+ | `wt.gcommit` | `info.head`. Always a `String`, or `nil` for a bare main worktree; `g.gcommit(info.head)` for the commit object |
1097
1737
  | `wt.dir` | `info.path` |
1098
- | `wt.full`, `wt.to_s` | `info.path` — or `"#{info.path} #{info.head}"` for the descriptor that entries from `g.worktrees` produced |
1738
+ | `wt.full`, `wt.to_s` | `info.path` or `"#{info.path} #{info.head}"` for the descriptor that entries from `g.worktrees` produced |
1099
1739
  | `wt.to_a` | `[info.path]` |
1100
1740
 
1101
1741
  #### `Git.clone` option renames
1102
1742
 
1103
- Three `Git.clone` options were renamed in v5.x. The v4.x names still work. Each
1104
- deprecated option present on a call emits its own deprecation warning, so a call
1105
- that uses two of them warns twice. Each value is passed through to the
1106
- replacement option, except that `:path` is dropped when `:chdir` is also given.
1743
+ Three `Git.clone` options were renamed in v5.x. The v4.x names still work in
1744
+ v5.x. Each deprecated option present on a call emits its own deprecation
1745
+ warning, so a call that uses two of them warns twice. Each value is passed
1746
+ through to the replacement option, except that `:path` is dropped when `:chdir`
1747
+ is also given.
1107
1748
 
1108
1749
  > **Precedence and value notes:**
1109
1750
  > - `:path` and `:chdir` both run `git clone` from inside the given directory.
@@ -1116,7 +1757,7 @@ replacement option, except that `:path` is dropped when `:chdir` is also given.
1116
1757
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
1117
1758
  |-----------------------------------------------------|-------------|
1118
1759
  | `Git.clone(url, dir, path: p)` | `Git.clone(url, dir, chdir: p)` |
1119
- | `Git.clone(url, dir, recursive: true)` | `Git.clone(url, dir, recurse_submodules: true)` — or a pathspec `String` or `Array<String>` for a subset of submodules |
1760
+ | `Git.clone(url, dir, recursive: true)` | `Git.clone(url, dir, recurse_submodules: true)` or a pathspec `String` or `Array<String>` for a subset of submodules |
1120
1761
  | `Git.clone(url, dir, remote: name)` | `Git.clone(url, dir, origin: name)` |
1121
1762
 
1122
1763
  #### `Git::Log` Enumerable interface deprecated
@@ -1133,7 +1774,7 @@ builder (`map`, `select`, `count`, `to_a`, `include?`, and so on) goes through t
1133
1774
  deprecated `each` and emits its warning. Move those calls to the result as well,
1134
1775
  not only the six named methods.
1135
1776
 
1136
- > **Snapshot results:** `execute` returns a snapshot. The builder re-runs
1777
+ > **Snapshot results.** `execute` returns a snapshot. The builder re-runs
1137
1778
  > `git log` only when a query method (`since`, `max_count`, and so on) has been
1138
1779
  > called since the last run, even with the same value as before, so calling
1139
1780
  > `execute` twice on an untouched builder returns equal results without a second
@@ -1146,7 +1787,7 @@ In the table, `g` is a `Git::Repository`.
1146
1787
  |-----------------------------------------------------|-------------|
1147
1788
  | `g.log.each { \|c\| ... }` | `g.log.execute.each { \|c\| ... }` |
1148
1789
  | `g.log.size` | `g.log.execute.size` |
1149
- | `g.log.to_s` | `g.log.execute.to_s` — commits joined with newlines, as before |
1790
+ | `g.log.to_s` | `g.log.execute.to_s`. Commits joined with newlines, as before |
1150
1791
  | `g.log.first`, `g.log.last` | `g.log.execute.first`, `g.log.execute.last` |
1151
1792
  | `g.log[i]`, `g.log[range]` | `g.log.execute[i]`, `g.log.execute[range]` |
1152
1793
  | any other `Enumerable` method on the log (`map`, `select`, `count`, `to_a`, `include?`, ...) | the same method on `g.log.execute` |
@@ -1178,40 +1819,4 @@ observable in the result. Delete the option from the call.
1178
1819
  |--------------------------------------------------------------------|-------------|
1179
1820
  | `Git.export(url, dir, remote: name)` | `Git.export(url, dir)` |
1180
1821
 
1181
- #### Context helper blocks that declare no parameter deprecated
1182
-
1183
- `Git::Repository#with_index`, `#with_temp_index`, `#with_working`, and
1184
- `#with_temp_working` yield `self` in v5.x, so a block could ignore the yielded
1185
- value and call methods on the receiver. Calling one of these helpers with a block
1186
- that declares no positional parameter (`do ... end`, `{ }`, `{ || }`, or a block
1187
- with only keyword or block parameters) now emits a deprecation warning. The
1188
- behavior is otherwise unchanged: the helpers still yield `self`, and a call with
1189
- no block still raises `LocalJumpError`.
1190
-
1191
- In v6.0.0 these helpers yield a separate repository bound to the other index or
1192
- working tree instead of rebinding the receiver, and they raise `ArgumentError` for
1193
- a block that declares no positional parameter. A call with no block also raises
1194
- `ArgumentError` in v6.0.0 instead of `LocalJumpError`. Declare a block parameter
1195
- and call methods on it. The parameter is required even when the block never uses
1196
- the repository, for example a `with_temp_working` block that only writes files
1197
- (`do |_scratch|`). Blocks that already declare a positional parameter (`|repo|`,
1198
- `|_|`, `|repo = nil|`, `|*args|`, or a symbol-to-proc such as `&:write_tree`) do
1199
- not warn. Both versions read the block's parameter list rather than its arity, so
1200
- the v5.x warning and the v6.0.0 error agree on every block form.
1201
-
1202
- The warning detects only the block's parameter list. Adding an unused parameter
1203
- (`|_|`) silences it but does not fix a block that still calls methods on the outer
1204
- repository; that block does not warn and acts on the wrong repository in v6.0.0,
1205
- so review those blocks by hand.
1206
-
1207
- | Deprecated call (works in v5.x, raises in v6.0.0) | Replacement |
1208
- |---------------------------------------------------|-------------|
1209
- | `g.with_index(path) { g.read_tree('HEAD') }` | `g.with_index(path) { \|r\| r.read_tree('HEAD') }` |
1210
- | `g.with_temp_index { g.read_tree('HEAD') }` | `g.with_temp_index { \|r\| r.read_tree('HEAD') }` |
1211
- | `g.with_working(dir) { g.add('.') }` | `g.with_working(dir) { \|r\| r.add('.') }` |
1212
- | `g.with_temp_working { g.add('.') }` | `g.with_temp_working { \|r\| r.add('.') }` |
1213
-
1214
- `chdir` is not part of this deprecation. It yields the directory path in both
1215
- versions.
1216
-
1217
1822
  ---