git 5.6.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 -16
  3. data/CONTRIBUTING.md +44 -31
  4. data/README.md +135 -67
  5. data/UPGRADING.md +915 -268
  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 +2 -2
  23. data/lib/git/encoding_utils.rb +25 -0
  24. data/lib/git/errors.rb +23 -5
  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 -44
  33. data/lib/git/repository/merging.rb +32 -54
  34. data/lib/git/repository/object_operations.rb +121 -338
  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 +31 -118
  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 +25 -19
  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)
@@ -17,6 +38,7 @@ to update your code when upgrading from the preceding major version.
17
38
  - [Deprecated methods](#deprecated-methods)
18
39
  - [Facade method renames](#facade-method-renames)
19
40
  - [`Git::Repository` method renames](#gitrepository-method-renames)
41
+ - [`Git::Repository` alias renames](#gitrepository-alias-renames)
20
42
  - [`Git::Repository` option renames](#gitrepository-option-renames)
21
43
  - [v4.x-style configuration methods](#v4x-style-configuration-methods)
22
44
  - [`Git` module mixin deprecations](#git-module-mixin-deprecations)
@@ -35,24 +57,628 @@ to update your code when upgrading from the preceding major version.
35
57
  - [`Git::Log` Enumerable interface deprecated](#gitlog-enumerable-interface-deprecated)
36
58
  - [`Git::Object::Commit#set_commit` deprecated](#gitobjectcommitset_commit-deprecated)
37
59
  - [`Git.export` `:remote` option deprecated](#gitexport-remote-option-deprecated)
38
- - [Context helper blocks that declare no parameter deprecated](#context-helper-blocks-that-declare-no-parameter-deprecated)
39
60
 
40
- ## Upgrading to v6.0.0
41
-
42
- v6.0.0 is not yet released. This section will be completed when it ships.
61
+ ## Upgrading to v6.x
43
62
 
44
63
  v6.0.0 removes the APIs deprecated during v5.x under the project's
45
- [deprecation policy](README.md#deprecation-policy).
46
- [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.
47
217
 
48
- 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
49
225
 
50
- 1. Upgrade to the latest v5.x release.
51
- 2. Set `GIT_DEPRECATION_BEHAVIOR=raise` (or `Git::Deprecation.behavior = :raise`) in
52
- your test suite and, if possible, staging.
53
- 3. Fix each deprecation using the entries under
54
- [Deprecated methods](#deprecated-methods) until the suite is clean.
55
- 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
+ ```
56
682
 
57
683
  ---
58
684
 
@@ -60,25 +686,24 @@ To prepare:
60
686
 
61
687
  ### Overview
62
688
 
63
- v5.0.0 delivers a new internal architecture while keeping the v4.x API working
64
- for the vast majority of users. Most v4.x code requires **no changes** to run on
65
- 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.
66
691
 
67
692
  The new architecture introduces a layered design (`Git::Commands`,
68
- `Git::Repository`, and associated parsers). Compatibility shims — deprecated
69
- forwarding methods that map old call patterns to the new API — ensure that v4.x
70
- code continues to work. These shims emit deprecation warnings that tell you
71
- 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.
72
697
 
73
- Hard breaks are limited to a small number of things that had no safe migration
74
- path. These are described in the [Breaking changes](#breaking-changes) section,
75
- followed by [Deprecated methods](#deprecated-methods) that still work in v5.x
76
- 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.
77
702
 
78
- For information on how to suppress or configure deprecation warnings, see the
79
- [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.
80
705
 
81
- **Changes at a glance:**
706
+ Changes at a glance:
82
707
 
83
708
  | Change | Type | Impact | Action required |
84
709
  |--------|------|--------|-----------------|
@@ -95,59 +720,57 @@ For information on how to suppress or configure deprecation warnings, see the
95
720
 
96
721
  #### `Git::Base` removed
97
722
 
98
- `Git::Base` — the class previously returned by `Git.open`, `Git.clone`,
99
- `Git.init`, and `Git.bare` — is removed in v5.0.0. The replacement is
100
- `Git::Repository`, which is returned by all four entry points and exposes the
101
- 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.
102
726
 
103
- **Code that must be updated:**
727
+ Code that must be updated:
104
728
 
105
729
  ```ruby
106
- # v4.x — explicit Git::Base reference (raises NameError in v5.x)
730
+ # v4.x: explicit Git::Base reference (raises NameError in v5.x)
107
731
  repo = Git::Base.new(working_directory: '/path/to/repo')
108
732
 
109
- # 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
110
734
  repo = Git.open('/path/to/repo')
111
735
  ```
112
736
 
113
737
  ```ruby
114
- # 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)
115
739
  raise unless repo.is_a?(Git::Base)
116
740
 
117
- # v5.x — check against Git::Repository
741
+ # v5.x: check against Git::Repository
118
742
  raise unless repo.is_a?(Git::Repository)
119
743
  ```
120
744
 
121
745
  ```ruby
122
- # v4.x — requiring the internal file (raises LoadError in v5.x)
746
+ # v4.x: requiring the internal file (raises LoadError in v5.x)
123
747
  require 'git/base'
124
748
 
125
- # 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
126
750
  require 'git'
127
751
  ```
128
752
 
129
- **Public API is preserved:** `Git::Repository` provides every method that
130
- `Git::Base` did. Code that simply calls methods on the object returned by
131
- `Git.open` (e.g., `repo.commit`, `repo.status`, `repo.add`) requires no
132
- 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.
133
756
 
134
- **Monkeypatching `Git::Base` is deprecated:** v5.x includes a temporary
757
+ **Monkeypatching `Git::Base` is deprecated.** v5.x includes a temporary
135
758
  compatibility shim for applications that define instance methods on `Git::Base`.
136
- Those methods are made available on `Git::Repository` instances, but each method
137
- 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.
138
761
 
139
762
  Move custom repository helpers to an application-owned extension module and
140
763
  include or prepend that module into `Git::Repository` during application setup:
141
764
 
142
765
  ```ruby
143
- # Deprecated in v5.x and will be removed in v6.0.0
766
+ # Deprecated in v5.x and removed in v6.0.0
144
767
  module Git::Base
145
768
  def worktree_clean?
146
769
  status.changed.empty?
147
770
  end
148
771
  end
149
772
 
150
- # v5.x — keep the extension in application-owned code
773
+ # v5.x: keep the extension in application-owned code
151
774
  module MyAppGitRepositoryExtensions
152
775
  def worktree_clean?
153
776
  status.changed.empty?
@@ -164,9 +787,9 @@ Git::Repository.include(MyAppGitRepositoryExtensions)
164
787
  `Git.open`, `Git.clone`, `Git.init`, and `Git.bare` now return
165
788
  `Git::Repository` instead of `Git::Base`.
166
789
 
167
- For most callers this is transparent — the returned object responds to the same
168
- methods. Code that explicitly checks `is_a?(Git::Base)` or `be_a(Git::Base)` in
169
- 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:
170
793
 
171
794
  ```ruby
172
795
  # v4.x
@@ -184,20 +807,26 @@ construct a repository object.
184
807
 
185
808
  #### Unsupported options raise `ArgumentError`
186
809
 
187
- v5.x validates options more strictly for factory methods and command APIs.
188
- Unknown options that were silently ignored in v4.x may now raise
189
- `ArgumentError`. Check option names against the documented API when upgrading,
190
- especially for calls that pass keyword options through helper methods or shared
191
- 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.
192
821
 
193
822
  For example, `Git.clone` supports `log:`, not `logger:`. A misspelled or
194
823
  unsupported option that v4.x ignored must be corrected:
195
824
 
196
825
  ```ruby
197
- # v4.x — silently ignored; did not configure clone logging
826
+ # v4.x: silently ignored; did not configure clone logging
198
827
  Git.clone(url, path, logger: logger)
199
828
 
200
- # v5.x — use the documented option name
829
+ # v5.x: use the documented option name
201
830
  Git.clone(url, path, log: logger)
202
831
  ```
203
832
 
@@ -210,31 +839,29 @@ exposed a `#lib` method that gave access to `Git::Lib`, the gem's internal
210
839
  implementation class. `Git::Lib` is removed in v5.0.0.
211
840
 
212
841
  In v5.x, calling `#lib` on a repo object returns `self` with a deprecation
213
- warning. This means `g.lib.some_method(args)` is forwarded to
214
- `g.some_method(args)` — but only if `some_method` exists on `Git::Repository`.
215
- Methods that were unique to `Git::Lib` and have no counterpart on
216
- `Git::Repository` raise `NoMethodError` immediately. The `#lib` method itself
217
- 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.
218
846
 
219
- Most public behavior previously accessible via `g.lib.*` is available directly
220
- on the repository object (`g.*`). See the tables below for every affected
221
- 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.
222
849
 
223
- ##### Methods that work via the `#lib` shim (with deprecation warning)
850
+ ##### Methods that work through the `#lib` shim (with deprecation warning)
224
851
 
225
852
  The following v4.x `g.lib.*` call shapes are forwarded to their `Git::Repository`
226
- counterpart by the `#lib → self` shim. They emit a deprecation warning; migrate
227
- 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.
228
855
 
229
- > **Note — config return type change:** `g.lib.config_get(name)` returned a
856
+ > **Config return type change.** `g.lib.config_get(name)` returned a
230
857
  > `String`; `g.lib.config_list` returned a `Hash`.
231
858
  > The v5.x replacements `config_get` and `config_list` return
232
- > `Git::ConfigEntryInfo` and `Array<Git::ConfigEntryInfo>` respectively — richer
233
- > 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`.
234
861
  >
235
862
  > If you only need the String value:
236
- > - `g.config_get(name)&.value` → replaces `g.lib.config_get(name)`
237
- > - `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`
238
865
  >
239
866
  > If your code was using the v4.x public `g.config(name)` API (not `g.lib.*`),
240
867
  > that deprecated bridge still returns a `String` in v5.x and continues to work
@@ -242,17 +869,17 @@ to the replacement shown to silence it.
242
869
 
243
870
  | v4.x call | Replacement in v5.x |
244
871
  |-----------|---------------------|
245
- | `g.lib.config_get(name)` | `g.config_get(name)` — returns `Git::ConfigEntryInfo`; use `.value` for the String |
246
- | `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>` |
247
874
  | `g.lib.config_set(name, value)` | `g.config_set(name, value)` |
248
875
  | `g.lib.git_version` | `g.git_version` |
249
- | `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 |
250
877
  | `g.lib.unmerged` | `g.unmerged` |
251
878
  | `g.lib.change_head_branch(name)` | `g.change_head_branch(name)` |
252
879
  | `g.lib.ls_remote(location, opts)` | `g.ls_remote(location, opts)` |
253
880
  | `g.lib.current_branch_state` | `g.current_branch_state` |
254
881
 
255
- > **Note — `current_branch_state` return type change:** `g.lib.current_branch_state`
882
+ > **`current_branch_state` return type change.** `g.lib.current_branch_state`
256
883
  > returned a `Git::Lib::HeadState` (a mutable `Struct`). `g.current_branch_state`
257
884
  > returns a `Git::Repository::Branching::HeadState` (an immutable `Data` object).
258
885
  > Both expose `.state` (`:active`, `:unborn`, or `:detached`) and `.name`. If your
@@ -263,8 +890,8 @@ to the replacement shown to silence it.
263
890
  ##### Methods that raise `NoMethodError` in v5.x
264
891
 
265
892
  These `Git::Lib` method names have no counterpart on `Git::Repository`, so
266
- `g.lib.method_name` raises `NoMethodError` even in v5.x (the `#lib → self`
267
- 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:
268
895
 
269
896
  | v4.x call | Replacement in v5.x |
270
897
  |-----------|---------------------|
@@ -282,8 +909,8 @@ shim cannot forward them). Update call sites directly:
282
909
 
283
910
  ##### Internal plumbing methods (no replacement)
284
911
 
285
- The following methods were technically public on `Git::Lib` but are internal
286
- 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:
287
914
 
288
915
  - `assert_args_are_not_options`
289
916
  - `assert_valid_opts`
@@ -307,15 +934,15 @@ filter log output by path when combined with `#between` or other revision range
307
934
  options. This relied on ambiguous `git log` argument handling and was not the
308
935
  intended API for path filtering.
309
936
 
310
- In v5.x, `Git::Log#object` should be treated as a revision expression. When both
311
- `#object` and `#between` are specified, `#between` takes precedence. Code that
312
- 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.
313
940
 
314
941
  ```ruby
315
- # v4.x — ambiguous; could appear to filter commits touching this path
942
+ # v4.x: ambiguous; could appear to filter commits touching this path
316
943
  git.log(500).object('cookbooks/mycookbook').between('1.0.0', 'HEAD').execute
317
944
 
318
- # v5.x — use #path for path filtering
945
+ # v5.x: use #path for path filtering
319
946
  git.log(500).path('cookbooks/mycookbook').between('1.0.0', 'HEAD').execute
320
947
 
321
948
  # #object remains appropriate for revision expressions
@@ -339,19 +966,25 @@ result.is_a?(Git::CommandLine::Result)
339
966
  ```
340
967
 
341
968
  This change is only relevant if your code references `Git::CommandLineResult`
342
- by name (typically in type checks or documentation). Code that simply uses the
343
- 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.
344
971
 
345
972
  ---
346
973
 
347
974
  ### Deprecated methods
348
975
 
349
976
  The following methods are available in v5.x with deprecation warnings and are
350
- 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.
351
984
 
352
985
  #### Facade method renames
353
986
 
354
- 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.
355
988
  The old names continue to work but emit deprecation warnings:
356
989
 
357
990
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
@@ -364,11 +997,11 @@ The old names continue to work but emit deprecation warnings:
364
997
 
365
998
  #### `Git::Repository` method renames
366
999
 
367
- Seven more `Git::Repository` methods were renamed in v5.x. The old names continue
368
- 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
369
1002
  replacement returns, except `branches_all`.
370
1003
 
371
- > **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
372
1005
  > tuples `[refname, current, worktree, symref]`, where `refname` is the short
373
1006
  > form (`main` or `remotes/origin/main`), `current` and `worktree` are booleans,
374
1007
  > and `symref` is the symbolic-ref target or `nil`. `g.branch_list` returns
@@ -386,14 +1019,36 @@ replacement returns, except `branches_all`.
386
1019
 
387
1020
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
388
1021
  |-----------------------------------------------------|-------------|
389
- | `g.empty?` | `g.no_commits?` — `true` when the repository has no commits |
390
- | `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 |
391
- | `g.reset_hard(commitish)` | `g.reset(commitish, hard: true)` — `reset_hard` ignored any `:hard` option passed to it and always reset with `--hard` |
392
- | `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 |
393
1026
  | `g.is_local_branch?(name)` | `g.local_branch?(name)` |
394
1027
  | `g.is_remote_branch?(name)` | `g.remote_branch?(name)` |
395
1028
  | `g.is_branch?(name)` | `g.branch?(name)` |
396
- | `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 |
1030
+
1031
+ #### `Git::Repository` alias renames
1032
+
1033
+ Eight `Git::Repository` methods have a second, older name carried over from v4.x.
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
1036
+ same arguments and returns exactly what its replacement returns.
1037
+
1038
+ > **`diff_name_status`:** a `:path` option emits its own deprecation warning
1039
+ > after the method-name warning. Pass `:path_limiter` instead, as described
1040
+ > under [`Git::Repository` option renames](#gitrepository-option-renames) below.
1041
+
1042
+ | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
1043
+ |-----------------------------------------------------|-------------|
1044
+ | `g.object_contents(object)` | `g.cat_file_contents(object)` |
1045
+ | `g.object_size(object)` | `g.cat_file_size(object)` |
1046
+ | `g.object_type(object)` | `g.cat_file_type(object)` |
1047
+ | `g.commit_data(object)` | `g.cat_file_commit(object)` |
1048
+ | `g.tag_data(object)` | `g.cat_file_tag(object)` |
1049
+ | `g.revparse(objectish)` | `g.rev_parse(objectish)` |
1050
+ | `g.namerev(commit_ish)` | `g.name_rev(commit_ish)` |
1051
+ | `g.diff_name_status(from, to, opts)` | `g.diff_path_status(from, to, opts)` |
397
1052
 
398
1053
  #### `Git::Repository` option renames
399
1054
 
@@ -401,7 +1056,7 @@ Five methods accept a v4.x option or positional argument under its old name.
401
1056
  The old form still works in v5.x but emits a deprecation warning and is
402
1057
  translated to the v5.x form shown below.
403
1058
 
404
- > **`clean`:** `force: 2` runs `git clean -ff`, which also removes untracked
1059
+ > **`clean`.** `force: 2` runs `git clean -ff`, which also removes untracked
405
1060
  > nested git repositories. A `false` or `nil` value for `:ff` or `:force_force`
406
1061
  > still warns and has no effect; a value other than `true`, `false`, or `nil`
407
1062
  > raises `ArgumentError`. When the deprecated key is `true` and a valid
@@ -410,11 +1065,11 @@ translated to the v5.x form shown below.
410
1065
  > unchanged and still raises `ArgumentError`; the deprecated key does not mask
411
1066
  > it.
412
1067
 
413
- > **`diff_path_status`:** `:path_limiter` accepts the same values as `:path`
1068
+ > **`diff_path_status`.** `:path_limiter` accepts the same values as `:path`
414
1069
  > (a `String`, a `Pathname`, or an `Array` of them). When both keys are given,
415
1070
  > `:path_limiter` wins and no warning is emitted.
416
1071
 
417
- > **`set_working` and `set_index`:** `must_exist:` defaults to `true`. When
1072
+ > **`set_working` and `set_index`.** `must_exist:` defaults to `true`. When
418
1073
  > both the positional argument and `must_exist:` are given, they are OR'ed so
419
1074
  > the more restrictive value wins.
420
1075
 
@@ -423,39 +1078,39 @@ translated to the v5.x form shown below.
423
1078
  | `g.clean(ff: true)` | `g.clean(force: 2)` |
424
1079
  | `g.clean(force_force: true)` | `g.clean(force: 2)` |
425
1080
  | `g.diff_path_status(ref1, ref2, path: p)` | `g.diff_path_status(ref1, ref2, path_limiter: p)` |
426
- | `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` |
427
1082
  | `g.set_working(dir, check)` | `g.set_working(dir, must_exist: check)` |
428
1083
  | `g.set_index(file, check)` | `g.set_index(file, must_exist: check)` |
429
1084
 
430
1085
  #### v4.x-style configuration methods
431
1086
 
432
1087
  The v4.x `config` and `global_config` methods accepted varying argument shapes
433
- to read, write, or list configuration. These are replaced by separate,
1088
+ to read, write, or list configuration. v5.x replaces them with separate,
434
1089
  purpose-named methods. The same applies to the module-level
435
- `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`,
436
1091
  and `Git.config_list` called with `global: true`.
437
1092
 
438
- > **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)`
439
1094
  > returned a `String`; `g.config` and `Git.global_config` returned a `Hash`. The
440
1095
  > v5.x replacements `config_get` and `config_list` return `Git::ConfigEntryInfo`
441
1096
  > (or `nil` when the key is not set) and `Array<Git::ConfigEntryInfo>`
442
1097
  > respectively. Use `.value` to get the String value:
443
- > - `g.config_get(name)&.value` → String or nil
444
- > - `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
445
1100
  >
446
1101
  > The setters `g.config(name, value)` and `Git.global_config(name, value)`
447
1102
  > returned the raw command result; `config_set` returns `nil`.
448
1103
 
449
1104
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
450
1105
  |-----------------------------------------------------|-------------|
451
- | `g.config(name)` | `g.config_get(name)` — returns `Git::ConfigEntryInfo`; use `.value` for the String |
452
- | `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>` |
453
1108
  | `g.config(name, value)` | `g.config_set(name, value)` |
454
1109
  | `g.global_config(name)` | `g.config_get(name, global: true)` |
455
1110
  | `g.global_config` | `g.config_list(global: true)` |
456
1111
  | `g.global_config(name, value)` | `g.config_set(name, value, global: true)` |
457
- | `Git.global_config(name)` | `Git.config_get(name, global: true)` — returns `Git::ConfigEntryInfo` or `nil`; use `.value` for the String |
458
- | `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>` |
459
1114
  | `Git.global_config(name, value)` | `Git.config_set(name, value, global: true)` |
460
1115
 
461
1116
  #### `Git` module mixin deprecations
@@ -489,7 +1144,7 @@ legacy type that is deprecated:
489
1144
  - `Git.binary_version` is replaced by `Git.git_version`, which keeps the
490
1145
  optional binary path argument.
491
1146
 
492
- > **Return type change:** `Git.binary_version` returned an `Array<Integer>` of
1147
+ > **Return type change.** `Git.binary_version` returned an `Array<Integer>` of
493
1148
  > `[major, minor, patch]`. `Git.git_version` returns a `Git::Version`, which
494
1149
  > supports comparison and exposes `major`, `minor`, and `patch`.
495
1150
  > `Git.git_version.to_a` reproduces the legacy array. The return value of
@@ -499,19 +1154,20 @@ legacy type that is deprecated:
499
1154
  |-----------------------------------------------------|-------------|
500
1155
  | `Git.ls_remote(nil)` | `Git.ls_remote` or `Git.ls_remote('.')` |
501
1156
  | `Git.ls_remote(nil, opts)` | `Git.ls_remote('.', opts)` |
502
- | `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 |
503
1158
  | `Git.binary_version(binary_path)` | `Git.git_version(binary_path)` |
504
1159
 
505
1160
  #### `Git::Author` deprecated
506
1161
 
507
- Starting in v5.3.0, methods that return author, committer, or tagger data —
508
- `Git::Object::Commit#author`, `Git::Object::Commit#committer`,
509
- `Git::Object::Tag#tagger`, and `Git::TagInfo#tagger` — return an immutable
510
- `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`.
511
1167
 
512
1168
  `Git::AuthorInfo` exposes the same `name`, `email`, and `date` readers, so code
513
1169
  that only reads these attributes needs no changes. Code that mutated a
514
- `Git::Author` (via `name=`, `email=`, or `date=`) must be updated:
1170
+ `Git::Author` through `name=`, `email=`, or `date=` has to change.
515
1171
  `Git::AuthorInfo` is frozen, and `#with` returns a modified copy rather than
516
1172
  updating in place (e.g. `info = info.with(name: 'New Name')`).
517
1173
 
@@ -530,7 +1186,7 @@ in the repository, so `g.branch('feature').stashes` and `g.branch('main').stashe
530
1186
  return the same entries. Call `Git::Repository#stash_infos` instead; it is the
531
1187
  query `Git::Branch#stashes` was already running.
532
1188
 
533
- > **Return type change:** `Git::Branch#stashes` returns a `Git::Stashes`
1189
+ > **Return type change.** `Git::Branch#stashes` returns a `Git::Stashes`
534
1190
  > collection of `Git::Stash` objects. `g.stash_infos` returns an array of
535
1191
  > `Git::StashInfo` values. Both are newest first, so indexes carry over unchanged.
536
1192
  > `Git::Stash#message` strips the `WIP on <branch>:` or `On <branch>:` prefix;
@@ -546,15 +1202,15 @@ its methods.
546
1202
 
547
1203
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
548
1204
  |-----------------------------------------------------|-------------|
549
- | `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 |
550
1206
  | `g.branch(name).stashes.each { \|s\| puts s.message }` | `g.stash_infos.each { \|info\| puts info.message }` |
551
- | `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) |
552
1208
  | `g.branch(name).stashes.size` | `g.stash_infos.size` |
553
1209
  | `g.branch(name).stashes[i].message` (`0` = newest, `i` coerced with `to_i`) | `g.stash_infos[i.to_i].message` |
554
1210
  | `g.branch(name).stashes.save(message)` | `g.stash_push(message: message)` |
555
1211
  | `g.branch(name).stashes.apply` | `g.stash_apply` |
556
1212
  | `g.branch(name).stashes.apply(i)` (`0` = newest) | `g.stash_apply(i)` |
557
- | `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` |
558
1214
 
559
1215
  #### Legacy stash API deprecated
560
1216
 
@@ -571,14 +1227,14 @@ The legacy methods and classes are deprecated and removed in v6.0.0:
571
1227
  `Git::Repository#stash_list`, `Git::Stash`, and `Git::Stashes`. Constructing a
572
1228
  `Git::Stash` or `Git::Stashes` emits one warning per object.
573
1229
 
574
- > **Ordering flip:** `g.stashes_all` returns entries **oldest first** with a
1230
+ > **Ordering flip.** `g.stashes_all` returns entries oldest first with a
575
1231
  > sequential index of its own (`0` is the oldest). `g.stash_infos` returns entries
576
- > **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
577
1233
  > git's own `stash@{N}` number (`0` is the newest). `g.stashes_all.first` is
578
1234
  > `g.stash_infos.last`. Code that reads an entry by position must reverse the
579
1235
  > array or the index.
580
1236
 
581
- > **Message difference:** `g.stashes_all` strips the `WIP on <branch>:` or
1237
+ > **Message difference.** `g.stashes_all` strips the `WIP on <branch>:` or
582
1238
  > `On <branch>:` prefix from each message. `Git::StashInfo#message` keeps the full
583
1239
  > message git stores, and `Git::StashInfo#branch` holds the branch name. A stash
584
1240
  > created from a detached HEAD has the branch `"(no branch)"`, the label git writes
@@ -591,32 +1247,38 @@ new `Git::StashInfo`, or `nil` when there were no local changes, so a truthiness
591
1247
  check such as `if g.stash_push(message: 'WIP')` still works.
592
1248
 
593
1249
  `g.stash_list` returned the `git stash list` text as a String. Build that text from
594
- `g.stash_infos` if you need it. In v6.0.0, `stash_list` returns
595
- `Array<Git::StashInfo>`, the same value as `stash_infos`, and `stash_infos` stays as
596
- a permanent alias. Move String callers of `stash_list` to `stash_infos` before
597
- 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).
598
1260
 
599
1261
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
600
1262
  |-----------------------------------------------------|-------------|
601
- | `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 |
602
1264
  | `g.stashes_all.each { \|index, message\| ... }` | `g.stash_infos.reverse_each.with_index { \|info, index\| ... info.message }` |
603
1265
  | `g.stashes_all[i]` (`0` = oldest) | `g.stash_infos.reverse[i]` |
604
1266
  | `g.stashes_all.last` | `g.stash_infos.first` |
605
- | `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` |
606
1268
  | `g.stash_list` (String) | `g.stash_infos.map { \|s\| "#{s.name}: #{s.message}" }.join("\n")` |
607
1269
  | `Git::Stash.new(g, message)` | `info = g.stash_push(message: message)` |
608
- | `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 |
609
1271
  | `stash.save` | `info = g.stash_push(message: message)` |
610
- | `stash.saved?` | `!info.nil?` — check the value `stash_push` returned rather than pushing again |
611
- | `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 |
612
1274
  | `Git::Stashes.new(g)` | `g.stash_infos` |
613
- | `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 |
614
1276
  | `stashes.each { \|s\| ... }` (newest first) | `g.stash_infos.each { \|info\| ... }` |
615
1277
  | `stashes[i]` (`0` = newest, `i` coerced with `to_i`) | `g.stash_infos[i.to_i]` |
616
1278
  | `stashes.size` | `g.stash_infos.size` |
617
1279
  | `stashes.save(message)` | `g.stash_push(message: message)` |
618
1280
  | `stashes.apply` / `stashes.apply(i)` | `g.stash_apply` / `g.stash_apply(i)` |
619
- | `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` |
620
1282
 
621
1283
  #### `Git::Repository#remotes` deprecated
622
1284
 
@@ -626,10 +1288,10 @@ one deprecation warning for itself plus one `Git::Remote` constructor warning fo
626
1288
  each remote it returns (see the `Git::Remote` deprecation below), so a repository
627
1289
  with N remotes produces N + 1 warnings per call.
628
1290
 
629
- > **Return type change:** `remotes` returns `Array<Git::Remote>` — mutable
1291
+ > **Return type change.** `remotes` returns `Array<Git::Remote>`, mutable
630
1292
  > objects with `name`, `url`, and `fetch_opts` accessors and `fetch`, `merge`,
631
1293
  > `branch`, and `remove` operations. `remote_list` returns
632
- > `Array<Git::RemoteInfo>` — immutable value objects read from the repository's
1294
+ > `Array<Git::RemoteInfo>`, immutable value objects read from the repository's
633
1295
  > git config, with fields such as `name`, `url`, `push_url`, `fetch`, and `push`.
634
1296
  > Because a remote may carry more than one URL or refspec, `url`, `push_url`,
635
1297
  > `fetch`, and `push` are always frozen `Array<String>`. When a remote has more
@@ -637,27 +1299,28 @@ with N remotes produces N + 1 warnings per call.
637
1299
  > the last one configured, so use `r.url.last` to reproduce that exact value.
638
1300
  > Likewise, `Git::Remote#fetch_opts` returned only the last configured fetch
639
1301
  > refspec, while `fetch` holds all of them. Operations that lived on
640
- > `Git::Remote` are called on the repository with the remote name instead.
1302
+ > `Git::Remote` are called on the repository instead; the table below shows
1303
+ > the argument each one takes.
641
1304
  >
642
- > **Order change:** `remotes` lists remotes in the order `git remote` prints
1305
+ > **Order change.** `remotes` lists remotes in the order `git remote` prints
643
1306
  > them, while `remote_list` keeps the order in which remotes first appear in the
644
1307
  > config. When the legacy order matters, iterate `g.remote_names` (the same
645
1308
  > `git remote` order) or sort `g.remote_list` explicitly.
646
1309
 
647
1310
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
648
1311
  |-----------------------------------------------------|-------------|
649
- | `g.remotes` | `g.remote_list` — returns `Array<Git::RemoteInfo>` |
1312
+ | `g.remotes` | `g.remote_list`. Returns `Array<Git::RemoteInfo>` |
650
1313
  | `g.remotes.map(&:name)` | `g.remote_list.map(&:name)` or `g.remote_names` |
651
- | `g.remotes.map(&:to_s)` | `g.remote_list.map(&:name)` — `Git::RemoteInfo#to_s` is not the name |
652
- | `g.remotes.map(&:url)` | `g.remote_list.map { \|r\| r.url.first }` — `url` is an `Array<String>` |
653
- | `g.remotes.map(&:fetch_opts)` | `g.remote_list.map { \|r\| r.fetch.last }` — `fetch` holds every refspec |
654
- | `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` |
655
1318
  | `remote.fetch` | `g.fetch(remote.name)` |
656
- | `remote.fetch(opts)` | `g.fetch(remote.name, opts)` — same options hash |
1319
+ | `remote.fetch(opts)` | `g.fetch(remote.name, opts)`. Same options hash |
657
1320
  | `remote.merge` | `g.merge("#{remote.name}/#{g.current_branch}")` |
658
1321
  | `remote.merge(branch)` | `g.merge("#{remote.name}/#{branch}")` |
659
- | `remote.branch` | `g.branch_list("#{remote.name}/#{g.current_branch}").first` — returns a `Git::BranchInfo` |
660
- | `remote.branch(name)` | `g.branch_list("#{remote.name}/#{name}").first` — 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` |
661
1324
  | `remote.remove` | `g.remote_remove(remote.name)` |
662
1325
 
663
1326
  #### `Git::Remote` deprecated
@@ -665,15 +1328,15 @@ with N remotes produces N + 1 warnings per call.
665
1328
  `Git::Remote`, `Git::Repository#remote`, and `Git::Repository#config_remote` are
666
1329
  deprecated and are removed in v6.0.0. Read a remote's configuration through
667
1330
  `Git::Repository#remote_list`, which returns one `Git::RemoteInfo` value object per
668
- remote, and call the repository-level operations (`fetch`, `merge`, `branch_list`,
669
- `remote_remove`) with the remote name. Return values are unchanged. Constructing a
670
- `Git::Remote` directly emits one deprecation warning, and so does calling
671
- `g.config_remote`. Calling `g.remote` emits two: one for `Git::Repository#remote`
672
- and one for the `Git::Remote` it constructs. Likewise `g.remotes` emits one warning
673
- for itself plus one per `Git::Remote` it returns (N + 1 for N remotes). The extra
674
- warnings from `g.remote` and `g.remotes` are expected, not a bug.
675
-
676
- > **Return type changes:** `Git::RemoteInfo#url` and `Git::RemoteInfo#fetch` are
1331
+ remote; the table below shows the replacement for each operation. Return values
1332
+ are unchanged. Constructing a `Git::Remote` directly emits one deprecation warning,
1333
+ and so does calling `g.config_remote`. Calling `g.remote` emits two: one for
1334
+ `Git::Repository#remote` and one for the `Git::Remote` it constructs. Likewise
1335
+ `g.remotes` emits one warning for itself plus one per `Git::Remote` it returns
1336
+ (N + 1 for N remotes). The extra warnings from `g.remote` and `g.remotes` are
1337
+ expected, not a bug.
1338
+
1339
+ > **Return type changes.** `Git::RemoteInfo#url` and `Git::RemoteInfo#fetch` are
677
1340
  > frozen `Array<String>` because a remote may carry more than one URL or fetch
678
1341
  > refspec. The legacy `Git::Remote#url` and `Git::Remote#fetch_opts` returned only
679
1342
  > the last configured value, so `r.url.last` and `r.fetch.last` reproduce them
@@ -686,26 +1349,27 @@ warnings from `g.remote` and `g.remotes` are expected, not a bug.
686
1349
  > Code that reads custom keys should filter `g.config_list` instead (see the
687
1350
  > table); that yields the same `Hash{String => String}` as `config_remote`.
688
1351
  > `Git::Remote#branch` returned a `Git::Branch`. Its replacement,
689
- > `g.branch_list("#{name}/#{branch}").first`, returns a `Git::BranchInfo` value
690
- > object, or `nil` when the remote-tracking branch does not exist.
1352
+ > `g.branch_list("#{name}/#{branch}").find(&:remote?)`, returns a
1353
+ > `Git::BranchInfo` value object, or `nil` when the remote-tracking branch does
1354
+ > not exist.
691
1355
 
692
1356
  In the table, `name` is the remote name (`g.remote` defaults it to `'origin'`).
693
1357
 
694
1358
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
695
1359
  |-----------------------------------------------------|-------------|
696
- | `g.remote` | `g.remote_list.find { \|r\| r.name == 'origin' }` — returns a `Git::RemoteInfo`; the deprecated call emits two warnings |
697
- | `g.remote(name)` | `g.remote_list.find { \|r\| r.name == name }` — returns a `Git::RemoteInfo`; the deprecated call emits two warnings |
698
- | `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` |
699
- | `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}` |
700
1364
  | `remote.name`, `remote.to_s` | `g.remote_list.find { \|r\| r.name == name }.name` or `g.remote_names` |
701
- | `remote.url` | `g.remote_list.find { \|r\| r.name == name }.url` — `Array<String>`; `.first` for the single-URL case |
702
- | `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 |
703
1367
  | `remote.fetch` | `g.fetch(name)` |
704
- | `remote.fetch(opts)` | `g.fetch(name, opts)` — same option keys |
1368
+ | `remote.fetch(opts)` | `g.fetch(name, opts)`. Same option keys |
705
1369
  | `remote.merge` | `g.merge("#{name}/#{g.current_branch}")` |
706
1370
  | `remote.merge(branch)` | `g.merge("#{name}/#{branch}")` |
707
- | `remote.branch` | `g.branch_list("#{name}/#{g.current_branch}").first` — returns a `Git::BranchInfo` |
708
- | `remote.branch(branch)` | `g.branch_list("#{name}/#{branch}").first` — 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` |
709
1373
  | `remote.remove` | `g.remote_remove(name)` |
710
1374
 
711
1375
  #### `Git::Commands::CatFile::Raw` `allow_unknown_type` option deprecated
@@ -735,12 +1399,14 @@ that constructs the command class directly is affected.
735
1399
  data through `Git::Repository#branch_list`, which returns one `Git::BranchInfo`
736
1400
  value object per local and remote-tracking branch, and call the repository-level
737
1401
  operations (`checkout`, `branch_new`, `branch_delete`, `merge`, `merge_into`,
738
- `in_branch`, and so on) with the branch name. Calling `g.branch` or `g.branches`,
739
- constructing a `Git::Branches`, and calling any operation on a `Git::Branch` each
740
- emit a deprecation warning; their return values are unchanged. The `full`,
741
- `name`, `remote`, `to_s`, and `to_a` readers on `Git::Branch` do not warn.
742
-
743
- > **Return shape change:** `Git::Branch` exposes `full` (`main` or
1402
+ `in_branch`, and so on) with the branch name. For a remote-tracking branch the
1403
+ argument differs by operation; the table below shows the form each one takes.
1404
+ Calling `g.branch` or `g.branches`, constructing a `Git::Branches`, and calling
1405
+ any operation on a `Git::Branch` each emit a deprecation warning; their return
1406
+ values are unchanged. The `full`, `name`, `remote`, `to_s`, and `to_a` readers
1407
+ on `Git::Branch` do not warn.
1408
+
1409
+ > **Return shape change.** `Git::Branch` exposes `full` (`main` or
744
1410
  > `remotes/origin/main`), `name`, and `remote` (a `Git::Remote`, or `nil`).
745
1411
  > `Git::BranchInfo` exposes `refname` (always the full ref: `refs/heads/main` or
746
1412
  > `refs/remotes/origin/main`), `short_name` (`main` for both), `remote_name` (a
@@ -751,7 +1417,7 @@ emit a deprecation warning; their return values are unchanged. The `full`,
751
1417
  > `'origin/main'` matches the remote-tracking branch. The `remotes/origin/main`
752
1418
  > and `refs/...` forms that `g.branches[...]` accepted match nothing.
753
1419
  >
754
- > **`checkout` no longer creates the branch:** `g.branch('x').checkout` created
1420
+ > **`checkout` no longer creates the branch.** `g.branch('x').checkout` created
755
1421
  > `x` when it did not exist, ignoring any error from that attempt, and then
756
1422
  > checked it out. `g.checkout('x')` does not create a missing local branch,
757
1423
  > with one exception that is git's own: when exactly one remote has a branch
@@ -797,33 +1463,33 @@ can resolve a local branch of that name and is only used where git expects it
797
1463
 
798
1464
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
799
1465
  |-----------------------------------------------------|-------------|
800
- | `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 |
801
- | `g.branch` | `g.branch_list(g.current_branch).first` — `nil` when HEAD is detached or unborn; use `g.current_branch_state` there |
802
- | `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>` |
803
1469
  | `g.branches[name]` | `g.branch_list(name).first`, or `g.branch_list("#{remote}/#{name}").find(&:remote?)` for a remote-tracking branch |
804
1470
  | `g.branches.local` | `g.branch_list.reject(&:remote?)` |
805
1471
  | `g.branches.remote` | `g.branch_list.select(&:remote?)` |
806
1472
  | `g.branches.size` | `g.branch_list.size` |
807
1473
  | `g.branches.each { \|b\| ... }` | `g.branch_list.each { \|info\| ... }` |
808
- | `g.branches.to_s` | `g.branch_list.map { \|i\| "#{i.current? ? '* ' : ' '}#{i.refname}\n" }.join` — full refs, not `remotes/...` |
809
- | `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` |
810
1476
  | `b.to_a` | `[info.refname]` |
811
1477
  | `b.name` | `info.short_name` |
812
- | `b.remote` | `info.remote_name` — a `String`, or `nil` for a local branch |
813
- | `b.gcommit` | `g.gcommit(name)` — pass `info.refname` for a remote-tracking branch |
814
- | `b.checkout` | `g.checkout(name)` — does not create the branch (see above); pass `info.refname` for a remote-tracking branch |
815
- | `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 |
816
1482
  | `b.delete` (local) | `g.branch_delete(name)` |
817
1483
  | `b.delete` (remote-tracking) | `g.branch_delete("#{remote}/#{name}", remotes: true)` |
818
1484
  | `b.current` | `g.current_branch == name` |
819
1485
  | `b.contains?(commit)` | `!g.branch_contains(commit, name).empty?` |
820
1486
  | `b.merge` | `g.merge(name)` |
821
- | `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 |
822
1488
  | `b.update_ref(commit)` (local) | `g.update_ref(name, commit)` |
823
1489
  | `b.update_ref(commit)` (remote-tracking) | `g.update_ref("remotes/#{remote}/#{name}", commit)` |
824
- | `b.archive(file, opts)` | `g.archive(name, file, opts)` — pass `info.refname` for a remote-tracking branch |
825
- | `b.in_branch(message) { ... }` | `g.in_branch(name, message) { ... }` — local `b` only; see the differences above |
826
- | `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) |
827
1493
 
828
1494
  #### `Git::Object::Tag` deprecated
829
1495
 
@@ -842,7 +1508,7 @@ for the `g.tag_add` it calls; `g.add_tag(name, d: true)` emits three, adding the
842
1508
  `:d`/`:delete` warning described below. The readers on a `Git::Object::Tag` do not
843
1509
  warn.
844
1510
 
845
- > **Return shape change:** `Git::Object::Tag` exposes `name`, `sha`, `objectish`,
1511
+ > **Return shape change.** `Git::Object::Tag` exposes `name`, `sha`, `objectish`,
846
1512
  > `annotated?`, `message`, and `tagger`. `Git::TagInfo` exposes `name`, `oid`,
847
1513
  > `target_oid`, `objecttype`, `annotated?`, `lightweight?`, `message`, and
848
1514
  > `tagger`. `name` and `annotated?` are unchanged. `tagger` keeps the same `name`
@@ -860,18 +1526,23 @@ warn.
860
1526
  > which kind (`tag` for an annotated tag, or the target's own type such as
861
1527
  > `commit` or `blob` for a lightweight one).
862
1528
  >
863
- > **Missing tags:** `g.tag(name)` raises `Git::UnexpectedResultError` when no tag
1529
+ > **Missing tags.** `g.tag(name)` raises `Git::UnexpectedResultError` when no tag
864
1530
  > has that name. `g.tag_list(name).first` returns `nil`.
865
1531
  >
866
- > **Deleting through `tag_add`:** `g.tag_add(name, d: true)`, which was already
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).
1536
+ >
1537
+ > **Deleting through `tag_add`.** `g.tag_add(name, d: true)`, which was already
867
1538
  > deprecated, deletes the tag and emits a second warning pointing at
868
1539
  > `g.tag_delete`. `g.tag_create` rejects `:d` and `:delete` with `ArgumentError`.
869
1540
  >
870
- > **Extra positional arguments:** `g.tag_add(name, target, extra)` ignores
1541
+ > **Extra positional arguments.** `g.tag_add(name, target, extra)` ignores
871
1542
  > `extra` and tags `target`. `g.tag_create` raises `ArgumentError` when more than
872
1543
  > one positional argument follows the name.
873
1544
  >
874
- > **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
875
1546
  > when it is constructed and runs `size`, `contents`, `grep`, `diff`, `log`, and
876
1547
  > `archive` against that ID, so moving or deleting the tag afterwards does not
877
1548
  > redirect an existing object. `Git::Object::Tag.new(g, sha, name)` uses the
@@ -890,26 +1561,26 @@ In the table, `name` is the tag name, `t` is a `Git::Object::Tag`, `info` is the
890
1561
 
891
1562
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
892
1563
  |-----------------------------------------------------|-------------|
893
- | `g.tag(name)` | `g.tag_list(name).first` — a `Git::TagInfo`, or `nil` when the tag does not exist |
894
- | `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>` |
895
1566
  | `g.tags.map(&:name)` | `g.tag_list.map(&:name)` |
896
- | `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` |
897
1568
  | `g.tag_add(name, target, opts)` | `g.tag_create(name, target, opts)` |
898
1569
  | `g.tag_add(name, d: true)` | `g.tag_delete(name)` |
899
- | `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` |
900
- | `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 |
901
1572
  | `Git::Object::Tag.new(g, name)` | `g.tag_list(name).first` |
902
- | `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 |
903
- | `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 |
904
1575
  | `t.name` | `info.name` |
905
- | `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 |
906
1577
  | `t.annotated?` | `info.annotated?` |
907
- | `t.message` | `info.message` — `nil` rather than `""` for an annotated tag with an empty message |
908
- | `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 |
909
1580
  | `t.tag?` | not needed; every `Git::TagInfo` is a tag |
910
- | `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 |
911
1582
  | `t.contents` | `g.cat_file_contents(id)` |
912
- | `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 |
913
1584
  | `t.contents_array` | `g.cat_file_contents(id).split("\n")` |
914
1585
  | `t.grep(string, path, opts)` | `g.grep(string, path, opts.merge(object: id))` |
915
1586
  | `t.diff(other)` | `g.diff(id, other)` |
@@ -919,7 +1590,7 @@ In the table, `name` is the tag name, `t` is a `Git::Object::Tag`, `info` is the
919
1590
  #### `Git::Status` deprecated
920
1591
 
921
1592
  Starting in v5.4.0, `Git::Status`, `Git::Status::StatusFile`, and
922
- `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
923
1594
  index and working tree state through `Git::Repository#status_info`, which
924
1595
  returns an immutable `Git::StatusInfo` holding one `Git::StatusFileInfo` per
925
1596
  path that `git status --porcelain=v2` reports. Calling `g.status` emits one
@@ -964,7 +1635,7 @@ reference, so `blob` is gone; fetch the object through the repository instead.
964
1635
  `stage` is gone too: an unmerged entry carries its stage 1, 2, and 3 modes and
965
1636
  SHAs in `unmerged_stages`, and every other entry is at stage 0.
966
1637
 
967
- > **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
968
1639
  > sides. `sha_index` and `mode_index` held the working-tree side of the diff:
969
1640
  > the index blob when the working tree matched the index, and an all-zero SHA
970
1641
  > when it did not. `sha_repo` and `mode_repo` held the side git compared the
@@ -982,12 +1653,12 @@ In the table, `g` is a `Git::Repository`, `status` is the `Git::Status` from
982
1653
 
983
1654
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
984
1655
  |-----------------------------------------------------|-------------|
985
- | `g.status` | `g.status_info` — returns a `Git::StatusInfo` |
1656
+ | `g.status` | `g.status_info`. Returns a `Git::StatusInfo` |
986
1657
  | `Git::Status.new(g)` | `g.status_info` |
987
- | `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 |
988
1659
  | `status.changed?(path)`, `status.added?(path)`, `status.deleted?(path)`, `status.untracked?(path)` | same names on `g.status_info` |
989
- | `status[path]` | `g.status_info[path]` — a `Git::StatusFileInfo`, or `nil`; `nil` for a clean tracked path, which `status[path]` reported (see above) |
990
- | `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) |
991
1662
  | `status.pretty` | no replacement; format `g.status_info.files` yourself |
992
1663
  | `file.path` | `info.path` |
993
1664
  | `file.type` | `info.index_status` and `info.worktree_status`, or the `info.changed?`, `info.added?`, and `info.deleted?` predicates |
@@ -997,8 +1668,8 @@ In the table, `g` is a `Git::Repository`, `status` is the `Git::Status` from
997
1668
  | `file.mode_repo` | `info.mode_head`, or `info.mode_index` in a repository with no commits |
998
1669
  | `file.sha_index` | `info.sha_index` for the staged blob; `info.worktree_status` says whether the working tree differs from it |
999
1670
  | `file.mode_index` | `info.mode_worktree` |
1000
- | `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])` |
1001
- | `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 |
1002
1673
 
1003
1674
  #### `Git::Worktree` and `Git::Worktrees` deprecated
1004
1675
 
@@ -1009,7 +1680,7 @@ and are removed in v6.0.0. Read worktree data through
1009
1680
  object per worktree, and call the repository-level operations (`worktree_add`,
1010
1681
  `worktree_remove`, `worktree_move`, `worktree_lock`, `worktree_unlock`,
1011
1682
  `worktree_repair`, and `worktree_prune`) with the worktree path or its
1012
- `Git::WorktreeInfo`. Return values are unchanged. Calling `g.worktree`,
1683
+ `Git::WorktreeInfo`. Return values are unchanged on v5.x. Calling `g.worktree`,
1013
1684
  `g.worktrees`, or `g.worktrees_all`, constructing a `Git::Worktrees`, and calling
1014
1685
  `gcommit`, `add`, or `remove` on a `Git::Worktree` each emit a deprecation
1015
1686
  warning; the `dir`, `full`, `to_s`, and `to_a` readers on `Git::Worktree` do not.
@@ -1017,7 +1688,7 @@ warning; the `dir`, `full`, `to_s`, and `to_a` readers on `Git::Worktree` do not
1017
1688
  it constructs, and `g.worktree(dir).add` emits one for `g.worktree` and one for
1018
1689
  `add`.
1019
1690
 
1020
- > **Return shape change:** `worktrees_all` returns `[directory, sha]` pairs and
1691
+ > **Return shape change.** `worktrees_all` returns `[directory, sha]` pairs and
1021
1692
  > omits the main worktree of a bare repository, which has no checked-out commit.
1022
1693
  > `worktree_list` returns `Git::WorktreeInfo` objects with `path`, `head`,
1023
1694
  > `branch` (the full refname, such as `refs/heads/main`, or `nil` when detached
@@ -1026,42 +1697,54 @@ it constructs, and `g.worktree(dir).add` emits one for `g.worktree` and one for
1026
1697
  > `branch` set to `nil`. `Git::WorktreeInfo#to_s` is the path, so an entry can be
1027
1698
  > passed to any method that takes a worktree path.
1028
1699
  >
1029
- > **`gcommit` return type:** `Git::Worktree#gcommit` returned a
1700
+ > **`gcommit` return type.** `Git::Worktree#gcommit` returned a
1030
1701
  > `Git::Object::Commit` for a worktree obtained from `g.worktree(dir)` and a raw
1031
1702
  > SHA `String` for one obtained from `g.worktrees`. `info.head` is always a
1032
1703
  > `String` (or `nil` for a bare main worktree); call `g.gcommit(info.head)` for
1033
1704
  > the commit object.
1034
1705
  >
1035
- > **`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
1036
1707
  > given at construction to the path, so entries from `g.worktrees` read
1037
1708
  > `"/path/to/wt <sha>"`. `Git::WorktreeInfo#to_s` is the path alone.
1709
+ >
1710
+ > **`worktree_add` in v6.0.0.** On v5.x, `worktree_add` returns the
1711
+ > `git worktree add` output (`"HEAD is now at …"`). In v6.0.0 it returns the new
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:
1716
+ > `g.worktree_list.find { |w| File.identical?(w.path, dir) }`. Compare with
1717
+ > `File.identical?`, not string equality: git stores worktree paths with
1718
+ > symlinks resolved, so `/tmp/...` on macOS is listed as `/private/tmp/...`,
1719
+ > and on case-insensitive filesystems the listed casing may differ from `dir`.
1038
1720
 
1039
1721
  In the table, `dir` is the worktree path, `wt` is a `Git::Worktree`, and `info`
1040
1722
  is the `Git::WorktreeInfo` that replaces it.
1041
1723
 
1042
1724
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
1043
1725
  |-----------------------------------------------------|-------------|
1044
- | `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 |
1045
- | `g.worktrees` | `g.worktree_list` — returns `Array<Git::WorktreeInfo>`; the deprecated call emits two warnings |
1046
- | `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 |
1047
1729
  | `g.worktrees.size` | `g.worktree_list.size` |
1048
1730
  | `g.worktrees.each { \|wt\| ... }` | `g.worktree_list.each { \|info\| ... }` |
1049
1731
  | `g.worktrees.to_s` | `g.worktree_list.map { \|w\| "#{w.path} #{w.head}\n" }.join` |
1050
1732
  | `g.worktrees.prune` | `g.worktree_prune` |
1051
1733
  | `g.worktree(dir).add` | `g.worktree_add(dir)` |
1052
1734
  | `g.worktree(dir, commitish).add` | `g.worktree_add(dir, commitish)` |
1053
- | `g.worktree(dir).remove` | `g.worktree_remove(dir)` — or `g.worktree_remove(info)` |
1054
- | `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 |
1055
1737
  | `wt.dir` | `info.path` |
1056
- | `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 |
1057
1739
  | `wt.to_a` | `[info.path]` |
1058
1740
 
1059
1741
  #### `Git.clone` option renames
1060
1742
 
1061
- Three `Git.clone` options were renamed in v5.x. The v4.x names still work. Each
1062
- deprecated option present on a call emits its own deprecation warning, so a call
1063
- that uses two of them warns twice. Each value is passed through to the
1064
- 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.
1065
1748
 
1066
1749
  > **Precedence and value notes:**
1067
1750
  > - `:path` and `:chdir` both run `git clone` from inside the given directory.
@@ -1074,7 +1757,7 @@ replacement option, except that `:path` is dropped when `:chdir` is also given.
1074
1757
  | Deprecated call (works in v5.x, removed in v6.0.0) | Replacement |
1075
1758
  |-----------------------------------------------------|-------------|
1076
1759
  | `Git.clone(url, dir, path: p)` | `Git.clone(url, dir, chdir: p)` |
1077
- | `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 |
1078
1761
  | `Git.clone(url, dir, remote: name)` | `Git.clone(url, dir, origin: name)` |
1079
1762
 
1080
1763
  #### `Git::Log` Enumerable interface deprecated
@@ -1091,7 +1774,7 @@ builder (`map`, `select`, `count`, `to_a`, `include?`, and so on) goes through t
1091
1774
  deprecated `each` and emits its warning. Move those calls to the result as well,
1092
1775
  not only the six named methods.
1093
1776
 
1094
- > **Snapshot results:** `execute` returns a snapshot. The builder re-runs
1777
+ > **Snapshot results.** `execute` returns a snapshot. The builder re-runs
1095
1778
  > `git log` only when a query method (`since`, `max_count`, and so on) has been
1096
1779
  > called since the last run, even with the same value as before, so calling
1097
1780
  > `execute` twice on an untouched builder returns equal results without a second
@@ -1104,7 +1787,7 @@ In the table, `g` is a `Git::Repository`.
1104
1787
  |-----------------------------------------------------|-------------|
1105
1788
  | `g.log.each { \|c\| ... }` | `g.log.execute.each { \|c\| ... }` |
1106
1789
  | `g.log.size` | `g.log.execute.size` |
1107
- | `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 |
1108
1791
  | `g.log.first`, `g.log.last` | `g.log.execute.first`, `g.log.execute.last` |
1109
1792
  | `g.log[i]`, `g.log[range]` | `g.log.execute[i]`, `g.log.execute[range]` |
1110
1793
  | any other `Enumerable` method on the log (`map`, `select`, `count`, `to_a`, `include?`, ...) | the same method on `g.log.execute` |
@@ -1136,40 +1819,4 @@ observable in the result. Delete the option from the call.
1136
1819
  |--------------------------------------------------------------------|-------------|
1137
1820
  | `Git.export(url, dir, remote: name)` | `Git.export(url, dir)` |
1138
1821
 
1139
- #### Context helper blocks that declare no parameter deprecated
1140
-
1141
- `Git::Repository#with_index`, `#with_temp_index`, `#with_working`, and
1142
- `#with_temp_working` yield `self` in v5.x, so a block could ignore the yielded
1143
- value and call methods on the receiver. Calling one of these helpers with a block
1144
- that declares no positional parameter (`do ... end`, `{ }`, `{ || }`, or a block
1145
- with only keyword or block parameters) now emits a deprecation warning. The
1146
- behavior is otherwise unchanged: the helpers still yield `self`, and a call with
1147
- no block still raises `LocalJumpError`.
1148
-
1149
- In v6.0.0 these helpers yield a separate repository bound to the other index or
1150
- working tree instead of rebinding the receiver, and they raise `ArgumentError` for
1151
- a block that declares no positional parameter. A call with no block also raises
1152
- `ArgumentError` in v6.0.0 instead of `LocalJumpError`. Declare a block parameter
1153
- and call methods on it. The parameter is required even when the block never uses
1154
- the repository, for example a `with_temp_working` block that only writes files
1155
- (`do |_scratch|`). Blocks that already declare a positional parameter (`|repo|`,
1156
- `|_|`, `|repo = nil|`, `|*args|`, or a symbol-to-proc such as `&:write_tree`) do
1157
- not warn. Both versions read the block's parameter list rather than its arity, so
1158
- the v5.x warning and the v6.0.0 error agree on every block form.
1159
-
1160
- The warning detects only the block's parameter list. Adding an unused parameter
1161
- (`|_|`) silences it but does not fix a block that still calls methods on the outer
1162
- repository; that block does not warn and acts on the wrong repository in v6.0.0,
1163
- so review those blocks by hand.
1164
-
1165
- | Deprecated call (works in v5.x, raises in v6.0.0) | Replacement |
1166
- |---------------------------------------------------|-------------|
1167
- | `g.with_index(path) { g.read_tree('HEAD') }` | `g.with_index(path) { \|r\| r.read_tree('HEAD') }` |
1168
- | `g.with_temp_index { g.read_tree('HEAD') }` | `g.with_temp_index { \|r\| r.read_tree('HEAD') }` |
1169
- | `g.with_working(dir) { g.add('.') }` | `g.with_working(dir) { \|r\| r.add('.') }` |
1170
- | `g.with_temp_working { g.add('.') }` | `g.with_temp_working { \|r\| r.add('.') }` |
1171
-
1172
- `chdir` is not part of this deprecation. It yields the directory path in both
1173
- versions.
1174
-
1175
1822
  ---