git 5.5.0 → 5.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a27bcb58894e06dd36f368c545d7800e95b2106122d222f98e43e4776117d38
4
- data.tar.gz: a59267db8db5a09e843914400be509c574670d824882b8c7a0c80f3e38945238
3
+ metadata.gz: a8ec4da3066f7c301611783c89f0a127f6c8ad4e7cf2467db9fe9071b7e0f943
4
+ data.tar.gz: 10fc3f33082886e7ca47e1ea97bd50075ffb15afbb91e1b309d9a12168a590ac
5
5
  SHA512:
6
- metadata.gz: c60016fdfb41208d18ab63e36fb24a26c5a4cbc73978d44bab657ffb4ee8395d9c774311a752ae91e474b36d3b11d9eafb0c5ce1f54d8202068057d50b6c1093
7
- data.tar.gz: 24656d7407eeea1ae1f92a8748eee1e65cdba4bafb9409ca0a96a819cfe7e3e2be4d9838fa3369464ccf908d5a7313de884a2e11c750f23664d88fcaa157f3dd
6
+ metadata.gz: 91e896c14c04e2135468d70031721cfbb29ba873284160f208315025bacc331a0fefaa04c837467d33b284253a04267ee8f4c003ea564770d619f9365ecb1b73
7
+ data.tar.gz: 03c6e84729ce4a4e6809cf53b92a6bcfb48572f1b8724e83cc451e829d1357b93da286e8fd6c669ddce3aa946cb0890237008992b9230e8cf41991e291fb8f76
data/CHANGELOG.md CHANGED
@@ -5,6 +5,30 @@
5
5
 
6
6
  # Change Log
7
7
 
8
+ ## [5.6.1](https://github.com/ruby-git/ruby-git/compare/v5.6.0...v5.6.1) (2026-09-16)
9
+
10
+
11
+ ### Other Changes
12
+
13
+ * **errors:** Document that filesystem and gzip errors can escape unconverted ([0b171e4](https://github.com/ruby-git/ruby-git/commit/0b171e42713a884354a84138696102f8e3cd83e5)), closes [#1809](https://github.com/ruby-git/ruby-git/issues/1809)
14
+ * **remote:** Correct the argument forms in the Git::Remote and Git::Branch entries ([cea622a](https://github.com/ruby-git/ruby-git/commit/cea622a14cce54a90c2203953a457b13b298aa22)), closes [#1852](https://github.com/ruby-git/ruby-git/issues/1852)
15
+ * **tag:** Note that tag_create raises UnexpectedResultError in v6.0.0 ([050aa44](https://github.com/ruby-git/ruby-git/commit/050aa443136156d7ecab47ad6fb44f436d9c2b7f)), closes [#1856](https://github.com/ruby-git/ruby-git/issues/1856)
16
+ * **worktree:** Look the new worktree up with File.identical? in the v5.x note ([0c941ec](https://github.com/ruby-git/ruby-git/commit/0c941ec34204ef5c3aeb6cf1de9e61f33212a0d9))
17
+ * **worktree:** Look the new worktree up with File.identical? in the YARD note ([8eb10ec](https://github.com/ruby-git/ruby-git/commit/8eb10eccb50ff14911e09033cfeec33666ce5c6a)), closes [#1845](https://github.com/ruby-git/ruby-git/issues/1845)
18
+ * **worktree:** Note that worktree_add returns Git::WorktreeInfo in v6.0.0 ([24af07e](https://github.com/ruby-git/ruby-git/commit/24af07e24199303727d823804063732fcaa98c98)), closes [#1845](https://github.com/ruby-git/ruby-git/issues/1845)
19
+
20
+ ## [5.6.0](https://github.com/ruby-git/ruby-git/compare/v5.5.0...v5.6.0) (2026-09-12)
21
+
22
+
23
+ ### Features
24
+
25
+ * **repository:** Deprecate with_* context helper blocks that declare no parameter ([231b65c](https://github.com/ruby-git/ruby-git/commit/231b65c435e9b3d9cdbf8b113b3aba9313da5110)), closes [#1835](https://github.com/ruby-git/ruby-git/issues/1835)
26
+
27
+
28
+ ### Other Changes
29
+
30
+ * **export:** Describe what a failed .git removal leaves behind ([bd08096](https://github.com/ruby-git/ruby-git/commit/bd080964e44a11594202726e3823a8cafad62bd5)), closes [#1823](https://github.com/ruby-git/ruby-git/issues/1823)
31
+
8
32
  ## [5.5.0](https://github.com/ruby-git/ruby-git/compare/v5.4.1...v5.5.0) (2026-09-10)
9
33
 
10
34
 
data/README.md CHANGED
@@ -201,11 +201,41 @@ the full API of its result.
201
201
 
202
202
  ## Errors raised by this gem
203
203
 
204
- The git gem raises only `ArgumentError` or errors that subclass `Git::Error`. It
205
- does not explicitly raise any other types of errors.
206
-
207
- Rescue `Git::Error` to catch any runtime error raised by this gem, unless you need
208
- more specific error handling.
204
+ Errors the git gem raises deliberately are either `ArgumentError` or a subclass of
205
+ `Git::Error`, with one exception: with the `raise` behavior described under
206
+ [Deprecations](#deprecations), a deprecated call raises
207
+ `ActiveSupport::DeprecationException`, which is intentionally not a `Git::Error` so a
208
+ broad rescue cannot hide a deprecation your code opted to treat as fatal. The errors
209
+ described below come from Ruby's standard library and are not converted.
210
+
211
+ The gem's own filesystem operations, such as reading a `.git` pointer file,
212
+ creating a temporary file, or changing directory, do not convert operating system
213
+ errors. These can be raised as a bare `SystemCallError` (for example `Errno::ENOENT`
214
+ or `Errno::EACCES`) rather than `Git::Error`. Gzip compression in `archive` can
215
+ likewise raise a `Zlib::Error`. Rescue `SystemCallError` and `Zlib::Error` alongside
216
+ `Git::Error` if your code needs to handle them.
217
+
218
+ The affected methods are `Git.open` when the `.git` file is a gitdir pointer or the
219
+ path is relative, `Git.init`, `Git.bare`, and `Git.clone` when the path is relative,
220
+ `Git.export`, and these `Git::Repository` methods: `#chdir`, `#with_index`,
221
+ `#with_working`, `#with_temp_index`, `#with_temp_working`, `#set_index`,
222
+ `#set_working`, `#cat_file_contents` in block form, `#each_conflict`, `#conflicts`,
223
+ `#archive` (including the gzip step), `#repo_size`, and `#tag_sha` when it reads a
224
+ loose ref.
225
+
226
+ Any method that captures git output can also raise
227
+ `Encoding::ConverterNotFoundError` when the output contains a line whose detected
228
+ encoding, such as UTF-7, has no Ruby converter.
229
+
230
+ Starting with v6.0.0, the filesystem and gzip errors are raised as `Git::Error`
231
+ with the original error available through `cause`, except that `#tag_sha` falls
232
+ back to `git show-ref` instead of raising. Lines with no Ruby converter are
233
+ scrubbed instead of raising.
234
+
235
+ Rescue `Git::Error` to catch the runtime errors this gem raises itself, and add
236
+ `SystemCallError`, `Zlib::Error`, and `Encoding::ConverterNotFoundError` where the
237
+ paragraphs above say they can escape. Rescue a more specific subclass when you need
238
+ finer-grained handling.
209
239
 
210
240
  ```ruby
211
241
  begin
data/UPGRADING.md CHANGED
@@ -35,6 +35,7 @@ to update your code when upgrading from the preceding major version.
35
35
  - [`Git::Log` Enumerable interface deprecated](#gitlog-enumerable-interface-deprecated)
36
36
  - [`Git::Object::Commit#set_commit` deprecated](#gitobjectcommitset_commit-deprecated)
37
37
  - [`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)
38
39
 
39
40
  ## Upgrading to v6.0.0
40
41
 
@@ -636,7 +637,8 @@ with N remotes produces N + 1 warnings per call.
636
637
  > the last one configured, so use `r.url.last` to reproduce that exact value.
637
638
  > Likewise, `Git::Remote#fetch_opts` returned only the last configured fetch
638
639
  > refspec, while `fetch` holds all of them. Operations that lived on
639
- > `Git::Remote` are called on the repository with the remote name instead.
640
+ > `Git::Remote` are called on the repository instead; the table below shows
641
+ > the argument each one takes.
640
642
  >
641
643
  > **Order change:** `remotes` lists remotes in the order `git remote` prints
642
644
  > them, while `remote_list` keeps the order in which remotes first appear in the
@@ -655,8 +657,8 @@ with N remotes produces N + 1 warnings per call.
655
657
  | `remote.fetch(opts)` | `g.fetch(remote.name, opts)` — same options hash |
656
658
  | `remote.merge` | `g.merge("#{remote.name}/#{g.current_branch}")` |
657
659
  | `remote.merge(branch)` | `g.merge("#{remote.name}/#{branch}")` |
658
- | `remote.branch` | `g.branch_list("#{remote.name}/#{g.current_branch}").first` — returns a `Git::BranchInfo` |
659
- | `remote.branch(name)` | `g.branch_list("#{remote.name}/#{name}").first` — returns a `Git::BranchInfo` |
660
+ | `remote.branch` | `g.branch_list("#{remote.name}/#{g.current_branch}").find(&:remote?)` — returns a `Git::BranchInfo` |
661
+ | `remote.branch(name)` | `g.branch_list("#{remote.name}/#{name}").find(&:remote?)` — returns a `Git::BranchInfo` |
660
662
  | `remote.remove` | `g.remote_remove(remote.name)` |
661
663
 
662
664
  #### `Git::Remote` deprecated
@@ -664,13 +666,13 @@ with N remotes produces N + 1 warnings per call.
664
666
  `Git::Remote`, `Git::Repository#remote`, and `Git::Repository#config_remote` are
665
667
  deprecated and are removed in v6.0.0. Read a remote's configuration through
666
668
  `Git::Repository#remote_list`, which returns one `Git::RemoteInfo` value object per
667
- remote, and call the repository-level operations (`fetch`, `merge`, `branch_list`,
668
- `remote_remove`) with the remote name. Return values are unchanged. Constructing a
669
- `Git::Remote` directly emits one deprecation warning, and so does calling
670
- `g.config_remote`. Calling `g.remote` emits two: one for `Git::Repository#remote`
671
- and one for the `Git::Remote` it constructs. Likewise `g.remotes` emits one warning
672
- for itself plus one per `Git::Remote` it returns (N + 1 for N remotes). The extra
673
- warnings from `g.remote` and `g.remotes` are expected, not a bug.
669
+ remote; the table below shows the replacement for each operation. Return values
670
+ are unchanged. Constructing a `Git::Remote` directly emits one deprecation warning,
671
+ and so does calling `g.config_remote`. Calling `g.remote` emits two: one for
672
+ `Git::Repository#remote` and one for the `Git::Remote` it constructs. Likewise
673
+ `g.remotes` emits one warning for itself plus one per `Git::Remote` it returns
674
+ (N + 1 for N remotes). The extra warnings from `g.remote` and `g.remotes` are
675
+ expected, not a bug.
674
676
 
675
677
  > **Return type changes:** `Git::RemoteInfo#url` and `Git::RemoteInfo#fetch` are
676
678
  > frozen `Array<String>` because a remote may carry more than one URL or fetch
@@ -685,8 +687,9 @@ warnings from `g.remote` and `g.remotes` are expected, not a bug.
685
687
  > Code that reads custom keys should filter `g.config_list` instead (see the
686
688
  > table); that yields the same `Hash{String => String}` as `config_remote`.
687
689
  > `Git::Remote#branch` returned a `Git::Branch`. Its replacement,
688
- > `g.branch_list("#{name}/#{branch}").first`, returns a `Git::BranchInfo` value
689
- > object, or `nil` when the remote-tracking branch does not exist.
690
+ > `g.branch_list("#{name}/#{branch}").find(&:remote?)`, returns a
691
+ > `Git::BranchInfo` value object, or `nil` when the remote-tracking branch does
692
+ > not exist.
690
693
 
691
694
  In the table, `name` is the remote name (`g.remote` defaults it to `'origin'`).
692
695
 
@@ -703,8 +706,8 @@ In the table, `name` is the remote name (`g.remote` defaults it to `'origin'`).
703
706
  | `remote.fetch(opts)` | `g.fetch(name, opts)` — same option keys |
704
707
  | `remote.merge` | `g.merge("#{name}/#{g.current_branch}")` |
705
708
  | `remote.merge(branch)` | `g.merge("#{name}/#{branch}")` |
706
- | `remote.branch` | `g.branch_list("#{name}/#{g.current_branch}").first` — returns a `Git::BranchInfo` |
707
- | `remote.branch(branch)` | `g.branch_list("#{name}/#{branch}").first` — returns a `Git::BranchInfo` |
709
+ | `remote.branch` | `g.branch_list("#{name}/#{g.current_branch}").find(&:remote?)` — returns a `Git::BranchInfo` |
710
+ | `remote.branch(branch)` | `g.branch_list("#{name}/#{branch}").find(&:remote?)` — returns a `Git::BranchInfo` |
708
711
  | `remote.remove` | `g.remote_remove(name)` |
709
712
 
710
713
  #### `Git::Commands::CatFile::Raw` `allow_unknown_type` option deprecated
@@ -734,10 +737,12 @@ that constructs the command class directly is affected.
734
737
  data through `Git::Repository#branch_list`, which returns one `Git::BranchInfo`
735
738
  value object per local and remote-tracking branch, and call the repository-level
736
739
  operations (`checkout`, `branch_new`, `branch_delete`, `merge`, `merge_into`,
737
- `in_branch`, and so on) with the branch name. Calling `g.branch` or `g.branches`,
738
- constructing a `Git::Branches`, and calling any operation on a `Git::Branch` each
739
- emit a deprecation warning; their return values are unchanged. The `full`,
740
- `name`, `remote`, `to_s`, and `to_a` readers on `Git::Branch` do not warn.
740
+ `in_branch`, and so on) with the branch name. For a remote-tracking branch the
741
+ argument differs by operation; the table below shows the form each one takes.
742
+ Calling `g.branch` or `g.branches`, constructing a `Git::Branches`, and calling
743
+ any operation on a `Git::Branch` each emit a deprecation warning; their return
744
+ values are unchanged. The `full`, `name`, `remote`, `to_s`, and `to_a` readers
745
+ on `Git::Branch` do not warn.
741
746
 
742
747
  > **Return shape change:** `Git::Branch` exposes `full` (`main` or
743
748
  > `remotes/origin/main`), `name`, and `remote` (a `Git::Remote`, or `nil`).
@@ -862,6 +867,13 @@ warn.
862
867
  > **Missing tags:** `g.tag(name)` raises `Git::UnexpectedResultError` when no tag
863
868
  > has that name. `g.tag_list(name).first` returns `nil`.
864
869
  >
870
+ > **`tag_create` when the new tag is missing in v6.0.0:** on v5.x, `g.tag_create`
871
+ > returns `nil` when the tag it has just created is missing from the tag list
872
+ > (for example, another process deleted it before the lookup). In v6.0.0 it
873
+ > raises `Git::UnexpectedResultError` instead, so its return value is always a
874
+ > `Git::TagInfo`. A `nil` check on the result keeps working today and becomes
875
+ > unreachable in v6.0.0.
876
+ >
865
877
  > **Deleting through `tag_add`:** `g.tag_add(name, d: true)`, which was already
866
878
  > deprecated, deletes the tag and emits a second warning pointing at
867
879
  > `g.tag_delete`. `g.tag_create` rejects `:d` and `:delete` with `ArgumentError`.
@@ -1008,7 +1020,7 @@ and are removed in v6.0.0. Read worktree data through
1008
1020
  object per worktree, and call the repository-level operations (`worktree_add`,
1009
1021
  `worktree_remove`, `worktree_move`, `worktree_lock`, `worktree_unlock`,
1010
1022
  `worktree_repair`, and `worktree_prune`) with the worktree path or its
1011
- `Git::WorktreeInfo`. Return values are unchanged. Calling `g.worktree`,
1023
+ `Git::WorktreeInfo`. Return values are unchanged on v5.x. Calling `g.worktree`,
1012
1024
  `g.worktrees`, or `g.worktrees_all`, constructing a `Git::Worktrees`, and calling
1013
1025
  `gcommit`, `add`, or `remove` on a `Git::Worktree` each emit a deprecation
1014
1026
  warning; the `dir`, `full`, `to_s`, and `to_a` readers on `Git::Worktree` do not.
@@ -1034,6 +1046,14 @@ it constructs, and `g.worktree(dir).add` emits one for `g.worktree` and one for
1034
1046
  > **`full` and `to_s`:** `Git::Worktree#full` and `#to_s` append the commitish
1035
1047
  > given at construction to the path, so entries from `g.worktrees` read
1036
1048
  > `"/path/to/wt <sha>"`. `Git::WorktreeInfo#to_s` is the path alone.
1049
+ >
1050
+ > **`worktree_add` return type in v6.0.0:** on v5.x, `worktree_add` returns the
1051
+ > `git worktree add` output (`"HEAD is now at …"`). In v6.0.0 it returns the new
1052
+ > `Git::WorktreeInfo` instead. To get the entry today, look it up after the add:
1053
+ > `g.worktree_list.find { |w| File.identical?(w.path, dir) }`. Compare with
1054
+ > `File.identical?`, not string equality: git stores worktree paths with
1055
+ > symlinks resolved, so `/tmp/...` on macOS is listed as `/private/tmp/...`,
1056
+ > and on case-insensitive filesystems the listed casing may differ from `dir`.
1037
1057
 
1038
1058
  In the table, `dir` is the worktree path, `wt` is a `Git::Worktree`, and `info`
1039
1059
  is the `Git::WorktreeInfo` that replaces it.
@@ -1135,4 +1155,40 @@ observable in the result. Delete the option from the call.
1135
1155
  |--------------------------------------------------------------------|-------------|
1136
1156
  | `Git.export(url, dir, remote: name)` | `Git.export(url, dir)` |
1137
1157
 
1158
+ #### Context helper blocks that declare no parameter deprecated
1159
+
1160
+ `Git::Repository#with_index`, `#with_temp_index`, `#with_working`, and
1161
+ `#with_temp_working` yield `self` in v5.x, so a block could ignore the yielded
1162
+ value and call methods on the receiver. Calling one of these helpers with a block
1163
+ that declares no positional parameter (`do ... end`, `{ }`, `{ || }`, or a block
1164
+ with only keyword or block parameters) now emits a deprecation warning. The
1165
+ behavior is otherwise unchanged: the helpers still yield `self`, and a call with
1166
+ no block still raises `LocalJumpError`.
1167
+
1168
+ In v6.0.0 these helpers yield a separate repository bound to the other index or
1169
+ working tree instead of rebinding the receiver, and they raise `ArgumentError` for
1170
+ a block that declares no positional parameter. A call with no block also raises
1171
+ `ArgumentError` in v6.0.0 instead of `LocalJumpError`. Declare a block parameter
1172
+ and call methods on it. The parameter is required even when the block never uses
1173
+ the repository, for example a `with_temp_working` block that only writes files
1174
+ (`do |_scratch|`). Blocks that already declare a positional parameter (`|repo|`,
1175
+ `|_|`, `|repo = nil|`, `|*args|`, or a symbol-to-proc such as `&:write_tree`) do
1176
+ not warn. Both versions read the block's parameter list rather than its arity, so
1177
+ the v5.x warning and the v6.0.0 error agree on every block form.
1178
+
1179
+ The warning detects only the block's parameter list. Adding an unused parameter
1180
+ (`|_|`) silences it but does not fix a block that still calls methods on the outer
1181
+ repository; that block does not warn and acts on the wrong repository in v6.0.0,
1182
+ so review those blocks by hand.
1183
+
1184
+ | Deprecated call (works in v5.x, raises in v6.0.0) | Replacement |
1185
+ |---------------------------------------------------|-------------|
1186
+ | `g.with_index(path) { g.read_tree('HEAD') }` | `g.with_index(path) { \|r\| r.read_tree('HEAD') }` |
1187
+ | `g.with_temp_index { g.read_tree('HEAD') }` | `g.with_temp_index { \|r\| r.read_tree('HEAD') }` |
1188
+ | `g.with_working(dir) { g.add('.') }` | `g.with_working(dir) { \|r\| r.add('.') }` |
1189
+ | `g.with_temp_working { g.add('.') }` | `g.with_temp_working { \|r\| r.add('.') }` |
1190
+
1191
+ `chdir` is not part of this deprecation. It yields the directory path in both
1192
+ versions.
1193
+
1138
1194
  ---
data/lib/git/errors.rb CHANGED
@@ -5,8 +5,20 @@ module Git
5
5
 
6
6
  # Base class for all custom git module errors
7
7
  #
8
- # The git gem will only raise an `ArgumentError` or an error that is a subclass of
9
- # `Git::Error`. It does not explicitly raise any other types of errors.
8
+ # Errors the git gem raises deliberately are either `ArgumentError` or a subclass
9
+ # of `Git::Error`. The exceptions described below come from Ruby's standard
10
+ # library and are not converted.
11
+ #
12
+ # The gem's own filesystem operations, such as reading a `.git` pointer file,
13
+ # creating a temporary file, or changing directory, do not convert operating
14
+ # system errors. These can be raised as a bare `SystemCallError` (for example
15
+ # `Errno::ENOENT` or `Errno::EACCES`) rather than `Git::Error`. Gzip compression
16
+ # in `archive` can likewise raise a `Zlib::Error`. Rescue `SystemCallError` and
17
+ # `Zlib::Error` alongside `Git::Error` if your code needs to handle them. Any
18
+ # method that captures git output can also raise
19
+ # `Encoding::ConverterNotFoundError` when a line's detected encoding has no Ruby
20
+ # converter. See the "Errors raised by this gem" section of the README for the
21
+ # affected methods.
10
22
  #
11
23
  # It is recommended to rescue `Git::Error` to catch any runtime error raised by
12
24
  # this gem unless you need more specific error handling.
@@ -18,6 +18,10 @@ module Git
18
18
  # @api private
19
19
  #
20
20
  module ContextHelpers
21
+ # Parameter types from `Proc#parameters` that receive a positional argument
22
+ CONTEXT_HELPERS_POSITIONAL_PARAMETERS = %i[req opt rest].freeze
23
+ private_constant :CONTEXT_HELPERS_POSITIONAL_PARAMETERS
24
+
21
25
  # Changes the current working directory to the repository working directory
22
26
  # for the duration of the block
23
27
  #
@@ -51,9 +55,13 @@ module Git
51
55
  # yields `self`, then unconditionally restores the original execution
52
56
  # context — even if the block raises an exception.
53
57
  #
58
+ # Deprecated form: a block that declares no positional parameter emits a
59
+ # deprecation warning, because v6.0.0 yields a separate repository
60
+ # instead of `self`.
61
+ #
54
62
  # @example Read a tree into a custom index
55
- # repo.with_index('/tmp/custom.index') do
56
- # repo.read_tree('HEAD')
63
+ # repo.with_index('/tmp/custom.index') do |r|
64
+ # r.read_tree('HEAD')
57
65
  # end
58
66
  #
59
67
  # @param new_index [String, Pathname] path to the replacement index file
@@ -66,7 +74,8 @@ module Git
66
74
  #
67
75
  # @yieldreturn [Object] returned as the method's return value
68
76
  #
69
- def with_index(new_index) # :yields: self
77
+ def with_index(new_index, &block) # :yields: self
78
+ context_helpers_warn_if_block_declares_no_parameter(:with_index, block)
70
79
  old_context = @execution_context
71
80
  set_index(new_index, must_exist: false)
72
81
  yield self
@@ -84,10 +93,14 @@ module Git
84
93
  # are removed unconditionally after the block exits, even if the block
85
94
  # raises an exception.
86
95
  #
96
+ # Deprecated form: a block that declares no positional parameter emits a
97
+ # deprecation warning, because v6.0.0 yields a separate repository
98
+ # instead of `self`.
99
+ #
87
100
  # @example Stage changes using a temporary index
88
- # repo.with_temp_index do
89
- # repo.read_tree('HEAD')
90
- # repo.write_tree
101
+ # repo.with_temp_index do |r|
102
+ # r.read_tree('HEAD')
103
+ # r.write_tree
91
104
  # end
92
105
  #
93
106
  # @return [Object] the value returned by the block
@@ -98,13 +111,17 @@ module Git
98
111
  #
99
112
  # @yieldreturn [Object] returned as the method's return value
100
113
  #
101
- def with_temp_index(&) # :yields: self
114
+ def with_temp_index(&block) # :yields: self
115
+ context_helpers_warn_if_block_declares_no_parameter(:with_temp_index, block)
102
116
  # Use a unique temp directory so the index file path is collision-free
103
117
  # and does not exist until git writes it. An existing empty file would
104
118
  # be treated as a corrupt index by git.
105
119
  temp_dir = Dir.mktmpdir('git-temp-index-')
106
120
  begin
107
- with_index(File.join(temp_dir, 'index'), &)
121
+ # The inner block declares a parameter so with_index does not warn a
122
+ # second time under its own name; forwarding &block would.
123
+ # rubocop:disable-next Style/ExplicitBlockArgument
124
+ with_index(File.join(temp_dir, 'index')) { |repo| yield repo }
108
125
  ensure
109
126
  FileUtils.remove_entry(temp_dir, true)
110
127
  end
@@ -118,10 +135,14 @@ module Git
118
135
  # `self`, then unconditionally restores the original execution context —
119
136
  # even if the block raises an exception.
120
137
  #
138
+ # Deprecated form: a block that declares no positional parameter emits a
139
+ # deprecation warning, because v6.0.0 yields a separate repository
140
+ # instead of `self`.
141
+ #
121
142
  # @example Commit changes from a different worktree path
122
- # repo.with_working('/path/to/worktree') do
123
- # repo.add('.')
124
- # repo.commit('chore: automated update')
143
+ # repo.with_working('/path/to/worktree') do |r|
144
+ # r.add('.')
145
+ # r.commit('chore: automated update')
125
146
  # end
126
147
  #
127
148
  # @param work_dir [String, Pathname] path to the replacement working
@@ -138,7 +159,8 @@ module Git
138
159
  #
139
160
  # @yieldreturn [Object] returned as the method's return value
140
161
  #
141
- def with_working(work_dir) # :yields: self
162
+ def with_working(work_dir, &block) # :yields: self
163
+ context_helpers_warn_if_block_declares_no_parameter(:with_working, block)
142
164
  old_context = @execution_context
143
165
  set_working(work_dir)
144
166
  Dir.chdir(dir.to_s) { yield self }
@@ -153,9 +175,14 @@ module Git
153
175
  # The temporary directory is removed unconditionally after the block
154
176
  # exits, even if the block raises an exception.
155
177
  #
178
+ # Deprecated form: a block that declares no positional parameter emits a
179
+ # deprecation warning, because v6.0.0 yields a separate repository
180
+ # instead of `self`.
181
+ #
156
182
  # @example Write files in an isolated temporary working directory
157
- # repo.with_temp_working do
183
+ # repo.with_temp_working do |r|
158
184
  # File.write('scratch.txt', 'temporary content')
185
+ # r.add('scratch.txt')
159
186
  # end
160
187
  #
161
188
  # @return [Object] the value returned by the block
@@ -168,7 +195,11 @@ module Git
168
195
  # @yieldreturn [Object] returned as the method's return value
169
196
  #
170
197
  def with_temp_working(&block) # :yields: self
171
- Dir.mktmpdir('temp-workdir') { |temp_dir| with_working(temp_dir, &block) }
198
+ context_helpers_warn_if_block_declares_no_parameter(:with_temp_working, block)
199
+ # The inner block declares a parameter so with_working does not warn a
200
+ # second time under its own name; forwarding &block would.
201
+ # rubocop:disable-next Style/ExplicitBlockArgument
202
+ Dir.mktmpdir('temp-workdir') { |temp_dir| with_working(temp_dir) { |repo| yield repo } }
172
203
  end
173
204
 
174
205
  # Sets the git index to `index_file` and rebuilds the execution context
@@ -231,6 +262,42 @@ module Git
231
262
 
232
263
  private
233
264
 
265
+ # Emits a deprecation warning when `block` declares no positional parameter
266
+ #
267
+ # In v6.0.0 the `with_*` helpers yield a separate repository instead of
268
+ # rebinding the receiver, so a block that calls methods on the receiver
269
+ # (the v5.x form) acts on the wrong repository there. v6.0.0 raises
270
+ # `ArgumentError` for that form using this same rule: the block must
271
+ # declare a required, optional, or splat positional parameter. The rule
272
+ # reads `Proc#parameters` rather than arity because an optional parameter
273
+ # gives a proc an arity of zero although it receives the repository,
274
+ # while keyword and block parameters cannot receive it.
275
+ #
276
+ # A missing block is not checked here; the helper's `yield` raises
277
+ # `LocalJumpError` as before.
278
+ #
279
+ # The warning reports the helper's caller, not this helper's caller, so
280
+ # the location points at the block that needs changing.
281
+ #
282
+ # @param helper [Symbol] the public helper name used in the warning
283
+ #
284
+ # @param block [Proc, nil] the block passed to the helper
285
+ #
286
+ # @return [void]
287
+ #
288
+ def context_helpers_warn_if_block_declares_no_parameter(helper, block)
289
+ return if block.nil?
290
+ return if block.parameters.any? { |type, _name| CONTEXT_HELPERS_POSITIONAL_PARAMETERS.include?(type) }
291
+
292
+ Git::Deprecation.warn(
293
+ "Calling Git::Repository##{helper} with a block that declares no positional " \
294
+ 'parameter is deprecated and will be removed in v6.0.0, where the helper ' \
295
+ 'yields a separate repository instead of self. Declare a block parameter and ' \
296
+ "call methods on it instead: #{helper} { |repo| repo.some_method }",
297
+ caller_locations(2)
298
+ )
299
+ end
300
+
234
301
  # Resolves deprecated `check` argument semantics with `must_exist:`
235
302
  #
236
303
  # @param check [Boolean, nil] deprecated positional existence-check value
@@ -928,7 +928,9 @@ module Git
928
928
  # @option options [Boolean, nil] :create_reflog (nil) create a reflog for
929
929
  # the tag
930
930
  #
931
- # @return [Git::TagInfo] the newly created tag
931
+ # @return [Git::TagInfo, nil] the newly created tag, or `nil` when the tag
932
+ # is missing from the tag list after the create (for example, another
933
+ # process deleted it before the lookup)
932
934
  #
933
935
  # @overload tag_create(name, target, options = {})
934
936
  #
@@ -945,7 +947,9 @@ module Git
945
947
  # @param options [Hash] options for creating the tag (same keys as the
946
948
  # first overload)
947
949
  #
948
- # @return [Git::TagInfo] the newly created tag
950
+ # @return [Git::TagInfo, nil] the newly created tag, or `nil` when the tag
951
+ # is missing from the tag list after the create (for example, another
952
+ # process deleted it before the lookup)
949
953
  #
950
954
  # @raise [ArgumentError] if unsupported options are provided, including the
951
955
  # `:d` and `:delete` keys that {#tag_add} accepts; use {#tag_delete} to
@@ -960,6 +964,12 @@ module Git
960
964
  #
961
965
  # @raise [Git::FailedError] if git exits with a non-zero exit status
962
966
  #
967
+ # @note In v6.0.0 this method raises {Git::UnexpectedResultError} instead of
968
+ # returning `nil` when the tag it has just created is missing from the tag
969
+ # list, so its return value is always a {Git::TagInfo}. Code that checks
970
+ # the result for `nil` today can be left as is; the check becomes
971
+ # unreachable in v6.0.0.
972
+ #
963
973
  # @see https://git-scm.com/docs/git-tag git-tag
964
974
  #
965
975
  def tag_create(name, *args)
@@ -99,6 +99,14 @@ module Git
99
99
  #
100
100
  # @raise [Git::FailedError] if git exits with a non-zero exit status
101
101
  #
102
+ # @note In v6.0.0 this method returns the new {Git::WorktreeInfo} instead of
103
+ # the command output. To get the entry today, look it up after the add:
104
+ # `worktree_list.find { |w| File.identical?(w.path, dir) }`.
105
+ # Compare with `File.identical?`, not string equality: git stores
106
+ # worktree paths with symlinks resolved, so `/tmp/...` on macOS is
107
+ # listed as `/private/tmp/...`, and on case-insensitive filesystems the
108
+ # listed casing may differ from `dir`.
109
+ #
102
110
  # @see https://git-scm.com/docs/git-worktree git-worktree documentation
103
111
  #
104
112
  def worktree_add(dir, commitish = nil)
data/lib/git/version.rb CHANGED
@@ -4,7 +4,7 @@ module Git
4
4
  # The current gem version
5
5
  #
6
6
  # @return [String] the current gem version
7
- VERSION = '5.5.0'
7
+ VERSION = '5.6.1'
8
8
 
9
9
  # Represents a git version with major, minor, and patch components
10
10
  #
data/lib/git.rb CHANGED
@@ -327,6 +327,11 @@ module Git
327
327
  # Exports the current HEAD (or the specific branch given in <tt>options[:branch]</tt>)
328
328
  # into the given `directory`. It then removes all traces of git from the directory.
329
329
  #
330
+ # Removing `.git` is not atomic. If it fails, the exported files are complete and
331
+ # usable, but the directory keeps whatever part of `.git` could not be deleted.
332
+ # Nothing is cleaned up, because the exported files are the deliverable and the
333
+ # leftover has to be removed by hand once the cause of the failure is fixed.
334
+ #
330
335
  # Takes the same options as {Git.clone} except that `:depth` defaults to 1 and
331
336
  # `:remote` is ignored with a deprecation warning.
332
337
  #
@@ -348,6 +353,10 @@ module Git
348
353
  #
349
354
  # @return [void]
350
355
  #
356
+ # @raise [SystemCallError] if the `.git` directory cannot be removed. The exported
357
+ # files are left in place, and the directory keeps whatever part of `.git` could
358
+ # not be deleted.
359
+ #
351
360
  def self.export(repository_url, directory = nil, options = {})
352
361
  if options.key?(:remote)
353
362
  Git::Deprecation.warn(
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.5.0
4
+ version: 5.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Chacon and others
@@ -548,8 +548,8 @@ licenses:
548
548
  metadata:
549
549
  homepage_uri: http://github.com/ruby-git/ruby-git
550
550
  source_code_uri: http://github.com/ruby-git/ruby-git
551
- changelog_uri: https://rubydoc.info/gems/git/5.5.0/file/CHANGELOG.md
552
- documentation_uri: https://rubydoc.info/gems/git/5.5.0
551
+ changelog_uri: https://rubydoc.info/gems/git/5.6.1/file/CHANGELOG.md
552
+ documentation_uri: https://rubydoc.info/gems/git/5.6.1
553
553
  rubygems_mfa_required: 'true'
554
554
  rdoc_options: []
555
555
  require_paths:
@@ -566,7 +566,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
566
566
  version: '0'
567
567
  requirements:
568
568
  - git 2.28.0 or greater
569
- rubygems_version: 4.0.16
569
+ rubygems_version: 4.0.20
570
570
  specification_version: 4
571
571
  summary: An API to create, read, and manipulate Git repositories
572
572
  test_files: []