git 5.2.0 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +49 -0
- data/CONTRIBUTING.md +17 -4
- data/README.md +49 -8
- data/UPGRADING.md +587 -3
- data/lib/git/author.rb +11 -0
- data/lib/git/author_info.rb +66 -0
- data/lib/git/branch.rb +216 -17
- data/lib/git/branch_info.rb +1 -1
- data/lib/git/branches.rb +35 -7
- data/lib/git/commands/cat_file/raw.rb +60 -6
- data/lib/git/object.rb +69 -15
- data/lib/git/parsers/stash.rb +50 -17
- data/lib/git/parsers/status.rb +251 -0
- data/lib/git/parsers/tag.rb +54 -8
- data/lib/git/parsers/worktree.rb +185 -0
- data/lib/git/remote.rb +37 -7
- data/lib/git/remote_info.rb +67 -10
- data/lib/git/repository/branching.rb +111 -1
- data/lib/git/repository/merging.rb +96 -2
- data/lib/git/repository/object_operations.rb +257 -20
- data/lib/git/repository/remote_operations.rb +57 -0
- data/lib/git/repository/shared_private.rb +67 -0
- data/lib/git/repository/stashing.rb +532 -52
- data/lib/git/repository/status_operations.rb +56 -8
- data/lib/git/repository/worktree_operations.rb +198 -18
- data/lib/git/stash.rb +31 -2
- data/lib/git/stash_info.rb +32 -34
- data/lib/git/stashes.rb +47 -11
- data/lib/git/status.rb +14 -0
- data/lib/git/status_file_info.rb +258 -0
- data/lib/git/status_info.rb +189 -0
- data/lib/git/tag_info.rb +23 -30
- data/lib/git/version.rb +1 -1
- data/lib/git/worktree.rb +39 -0
- data/lib/git/worktree_info.rb +128 -0
- data/lib/git/worktrees.rb +19 -1
- data/lib/git.rb +5 -0
- metadata +9 -3
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
require 'git/commands/ls_files'
|
|
4
4
|
require 'git/commands/rev_parse'
|
|
5
|
+
require 'git/commands/status'
|
|
5
6
|
require 'git/escaped_path'
|
|
7
|
+
require 'git/parsers/status'
|
|
6
8
|
require 'git/status'
|
|
9
|
+
require 'git/status_info'
|
|
7
10
|
|
|
8
11
|
module Git
|
|
9
12
|
class Repository
|
|
@@ -87,6 +90,47 @@ module Git
|
|
|
87
90
|
).stdout.split("\n").map { |f| Private.unescape_quoted_path(f) }
|
|
88
91
|
end
|
|
89
92
|
|
|
93
|
+
# Returns a {Git::StatusInfo} describing the index and working tree state
|
|
94
|
+
#
|
|
95
|
+
# Runs `git status` in porcelain v2 format with NUL-separated entries and
|
|
96
|
+
# every untracked file listed individually, then reads `core.ignoreCase`
|
|
97
|
+
# as a boolean so that the path predicates on the result compare paths the
|
|
98
|
+
# way git does in this repository. Every entry type git reports is
|
|
99
|
+
# represented, including renames, copies, and merge conflicts. Clean
|
|
100
|
+
# tracked paths are not reported by `git status`, so they are absent from
|
|
101
|
+
# the result; the deprecated {#status} listed them, and {#ls_files} still
|
|
102
|
+
# does.
|
|
103
|
+
#
|
|
104
|
+
# @example Check which files are modified
|
|
105
|
+
# repo.status_info.changed
|
|
106
|
+
# #=> { "lib/foo.rb" => #<data Git::StatusFileInfo path="lib/foo.rb", ...> }
|
|
107
|
+
#
|
|
108
|
+
# @example Check for untracked files
|
|
109
|
+
# repo.status_info.untracked.keys #=> ["new_file.rb"]
|
|
110
|
+
#
|
|
111
|
+
# @example Check one path
|
|
112
|
+
# repo.status_info.changed?('lib/foo.rb') #=> true
|
|
113
|
+
#
|
|
114
|
+
# @example Iterate over every entry
|
|
115
|
+
# repo.status_info.files.each do |file|
|
|
116
|
+
# puts "#{file.index_status}#{file.worktree_status} #{file.path}"
|
|
117
|
+
# end
|
|
118
|
+
#
|
|
119
|
+
# @return [Git::StatusInfo] the status of the repository
|
|
120
|
+
#
|
|
121
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
122
|
+
#
|
|
123
|
+
# @see https://git-scm.com/docs/git-status git-status
|
|
124
|
+
#
|
|
125
|
+
def status_info
|
|
126
|
+
result = Git::Commands::Status.new(@execution_context).call(
|
|
127
|
+
porcelain: 'v2', z: true, untracked_files: 'all'
|
|
128
|
+
)
|
|
129
|
+
files = Git::Parsers::Status.parse(result.stdout)
|
|
130
|
+
ignore_case = config_get('core.ignoreCase', type: 'bool')&.value == 'true'
|
|
131
|
+
Git::StatusInfo.new(files: files, ignore_case: ignore_case)
|
|
132
|
+
end
|
|
133
|
+
|
|
90
134
|
# Returns a {Git::Status} object describing the working tree and index state
|
|
91
135
|
#
|
|
92
136
|
# Constructs a {Git::Status} for this repository by collecting information from
|
|
@@ -95,22 +139,26 @@ module Git
|
|
|
95
139
|
# result identifies which files have been modified, added, deleted, or are
|
|
96
140
|
# untracked.
|
|
97
141
|
#
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
# @example Check for untracked files
|
|
102
|
-
# repo.status.untracked #=> { "new_file.rb" => <Git::Status::StatusFile ...> }
|
|
142
|
+
# Emits one deprecation warning per call. The {Git::Status} it constructs is
|
|
143
|
+
# built with warnings silenced so the caller does not see a second one.
|
|
103
144
|
#
|
|
104
|
-
# @example
|
|
105
|
-
# repo.status.
|
|
145
|
+
# @example Check which files are modified (deprecated; use status_info)
|
|
146
|
+
# repo.status.changed.keys #=> ["lib/foo.rb"]
|
|
147
|
+
# repo.status_info.changed.keys #=> ["lib/foo.rb"]
|
|
106
148
|
#
|
|
107
149
|
# @return [Git::Status] the status of the repository
|
|
108
150
|
#
|
|
109
151
|
# @raise [Git::FailedError] if any underlying git command exits with a
|
|
110
152
|
# non-zero exit status
|
|
111
153
|
#
|
|
154
|
+
# @deprecated Use {#status_info} instead
|
|
155
|
+
#
|
|
112
156
|
def status
|
|
113
|
-
Git::
|
|
157
|
+
Git::Deprecation.warn(
|
|
158
|
+
'Git::Repository#status is deprecated and will be removed in v6.0.0. ' \
|
|
159
|
+
'Use Git::Repository#status_info instead.'
|
|
160
|
+
)
|
|
161
|
+
Git::Deprecation.silence { Git::Status.new(self) }
|
|
114
162
|
end
|
|
115
163
|
|
|
116
164
|
# List all files tracked in the index
|
|
@@ -1,22 +1,55 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'git/commands/worktree'
|
|
4
|
+
require 'git/parsers/worktree'
|
|
4
5
|
require 'git/worktree'
|
|
6
|
+
require 'git/worktree_info'
|
|
5
7
|
require 'git/worktrees'
|
|
6
8
|
|
|
7
9
|
module Git
|
|
8
10
|
class Repository
|
|
9
|
-
# Facade methods for worktree operations
|
|
11
|
+
# Facade methods for worktree operations: listing, adding, removing, moving,
|
|
12
|
+
# locking, repairing, and pruning worktrees
|
|
10
13
|
#
|
|
11
14
|
# Included by {Git::Repository}.
|
|
12
15
|
#
|
|
13
16
|
# @api private
|
|
14
17
|
#
|
|
15
18
|
module WorktreeOperations
|
|
19
|
+
# Returns every worktree attached to the repository
|
|
20
|
+
#
|
|
21
|
+
# Lists the main worktree first, then each linked worktree, in the order
|
|
22
|
+
# git reports them. The main worktree of a bare repository is included
|
|
23
|
+
# with {Git::WorktreeInfo#bare?} true and no head or branch.
|
|
24
|
+
#
|
|
25
|
+
# @example List all worktrees
|
|
26
|
+
# repo.worktree_list.map(&:path)
|
|
27
|
+
# #=> ["/path/to/main", "/tmp/feature"]
|
|
28
|
+
#
|
|
29
|
+
# @example Find the worktree that has a branch checked out
|
|
30
|
+
# info = repo.worktree_list.find { |w| w.branch == 'refs/heads/feature' }
|
|
31
|
+
# info.path #=> "/tmp/feature"
|
|
32
|
+
# info.head #=> "b8c63202c3c0ebd37b7e45fd0c22e6c20d5bead1"
|
|
33
|
+
# info.locked? #=> false
|
|
34
|
+
#
|
|
35
|
+
# @return [Array<Git::WorktreeInfo>] one entry per worktree
|
|
36
|
+
#
|
|
37
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
38
|
+
#
|
|
39
|
+
# @raise [Git::UnexpectedResultError] if the worktree listing cannot be
|
|
40
|
+
# parsed
|
|
41
|
+
#
|
|
42
|
+
# @see https://git-scm.com/docs/git-worktree git-worktree documentation
|
|
43
|
+
#
|
|
44
|
+
def worktree_list
|
|
45
|
+
result = Git::Commands::Worktree::List.new(@execution_context).call(porcelain: true)
|
|
46
|
+
Git::Parsers::Worktree.parse_list(result.stdout)
|
|
47
|
+
end
|
|
48
|
+
|
|
16
49
|
# Returns all worktrees as an array of directory and SHA pairs
|
|
17
50
|
#
|
|
18
|
-
# Lists
|
|
19
|
-
#
|
|
51
|
+
# Lists the main worktree and all linked worktrees. The main worktree of
|
|
52
|
+
# a bare repository has no checked-out commit and is omitted.
|
|
20
53
|
#
|
|
21
54
|
# @example List all worktrees
|
|
22
55
|
# repo.worktrees_all
|
|
@@ -30,20 +63,22 @@ module Git
|
|
|
30
63
|
#
|
|
31
64
|
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
32
65
|
#
|
|
66
|
+
# @deprecated Use {#worktree_list} instead
|
|
67
|
+
#
|
|
68
|
+
# {#worktree_list} returns one {Git::WorktreeInfo} per worktree, with
|
|
69
|
+
# `path` and `head` in place of the pair, and includes the main worktree
|
|
70
|
+
# of a bare repository.
|
|
71
|
+
#
|
|
72
|
+
# @see #worktree_list
|
|
73
|
+
#
|
|
33
74
|
# @see https://git-scm.com/docs/git-worktree git-worktree documentation
|
|
34
75
|
#
|
|
35
76
|
def worktrees_all
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
key, value = line.split(' ', 2)
|
|
42
|
-
current_directory = value if key == 'worktree'
|
|
43
|
-
worktree_entries << [current_directory, value] if key == 'HEAD'
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
worktree_entries
|
|
77
|
+
Git::Deprecation.warn(
|
|
78
|
+
'Git::Repository#worktrees_all is deprecated and will be removed in v6.0.0. ' \
|
|
79
|
+
'Use Git::Repository#worktree_list instead.'
|
|
80
|
+
)
|
|
81
|
+
worktree_list.reject { |worktree| worktree.head.nil? }.map { |worktree| [worktree.path, worktree.head] }
|
|
47
82
|
end
|
|
48
83
|
|
|
49
84
|
# Create a new linked worktree at the given directory
|
|
@@ -75,10 +110,15 @@ module Git
|
|
|
75
110
|
|
|
76
111
|
# Remove a linked worktree
|
|
77
112
|
#
|
|
78
|
-
# @example Remove a worktree
|
|
113
|
+
# @example Remove a worktree by path
|
|
79
114
|
# repo.worktree_remove('/tmp/feature')
|
|
80
115
|
#
|
|
81
|
-
# @
|
|
116
|
+
# @example Remove a worktree from the list
|
|
117
|
+
# info = repo.worktree_list.find { |w| w.branch == 'refs/heads/feature' }
|
|
118
|
+
# repo.worktree_remove(info)
|
|
119
|
+
#
|
|
120
|
+
# @param worktree [String, Git::WorktreeInfo] the path of the worktree to
|
|
121
|
+
# remove, or its entry from {#worktree_list}
|
|
82
122
|
#
|
|
83
123
|
# @return [String] the output from the git worktree remove command
|
|
84
124
|
# (typically empty)
|
|
@@ -87,8 +127,120 @@ module Git
|
|
|
87
127
|
#
|
|
88
128
|
# @see https://git-scm.com/docs/git-worktree git-worktree documentation
|
|
89
129
|
#
|
|
90
|
-
def worktree_remove(
|
|
91
|
-
Git::Commands::Worktree::Remove.new(@execution_context).call(
|
|
130
|
+
def worktree_remove(worktree)
|
|
131
|
+
Git::Commands::Worktree::Remove.new(@execution_context).call(worktree.to_s).stdout
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Move a linked worktree to a new location
|
|
135
|
+
#
|
|
136
|
+
# @example Move a worktree
|
|
137
|
+
# repo.worktree_move('/tmp/feature', '/tmp/feature-moved')
|
|
138
|
+
#
|
|
139
|
+
# @example Move a locked worktree
|
|
140
|
+
# repo.worktree_move('/tmp/feature', '/tmp/feature-moved', force: 2)
|
|
141
|
+
#
|
|
142
|
+
# @param worktree [String, Git::WorktreeInfo] the path of the worktree to
|
|
143
|
+
# move, or its entry from {#worktree_list}
|
|
144
|
+
#
|
|
145
|
+
# @param new_path [String] the destination path
|
|
146
|
+
#
|
|
147
|
+
# @param opts [Hash] options for the move
|
|
148
|
+
#
|
|
149
|
+
# @option opts [Boolean, Integer, nil] :force (nil) override git's
|
|
150
|
+
# safeguards; git refuses to move a locked worktree unless the flag is
|
|
151
|
+
# given twice, so pass `2` for that
|
|
152
|
+
#
|
|
153
|
+
# @return [String] the output from the git worktree move command
|
|
154
|
+
# (typically empty)
|
|
155
|
+
#
|
|
156
|
+
# @raise [ArgumentError] if unsupported options are provided
|
|
157
|
+
#
|
|
158
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
159
|
+
#
|
|
160
|
+
# @see https://git-scm.com/docs/git-worktree git-worktree documentation
|
|
161
|
+
#
|
|
162
|
+
def worktree_move(worktree, new_path, opts = {})
|
|
163
|
+
Git::Commands::Worktree::Move.new(@execution_context).call(worktree.to_s, new_path, **opts).stdout
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Lock a linked worktree so that `git worktree prune` leaves it alone
|
|
167
|
+
#
|
|
168
|
+
# Lock a worktree whose directory is on removable media or a network
|
|
169
|
+
# share that is not always mounted.
|
|
170
|
+
#
|
|
171
|
+
# @example Lock a worktree
|
|
172
|
+
# repo.worktree_lock('/tmp/feature')
|
|
173
|
+
#
|
|
174
|
+
# @example Lock a worktree with a reason
|
|
175
|
+
# repo.worktree_lock('/tmp/feature', reason: 'on an external drive')
|
|
176
|
+
#
|
|
177
|
+
# @param worktree [String, Git::WorktreeInfo] the path of the worktree to
|
|
178
|
+
# lock, or its entry from {#worktree_list}
|
|
179
|
+
#
|
|
180
|
+
# @param opts [Hash] options for the lock
|
|
181
|
+
#
|
|
182
|
+
# @option opts [String, nil] :reason (nil) an explanation stored with the
|
|
183
|
+
# lock and reported as {Git::WorktreeInfo#lock_reason}
|
|
184
|
+
#
|
|
185
|
+
# @return [String] the output from the git worktree lock command
|
|
186
|
+
# (typically empty)
|
|
187
|
+
#
|
|
188
|
+
# @raise [ArgumentError] if unsupported options are provided
|
|
189
|
+
#
|
|
190
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
191
|
+
#
|
|
192
|
+
# @see https://git-scm.com/docs/git-worktree git-worktree documentation
|
|
193
|
+
#
|
|
194
|
+
def worktree_lock(worktree, opts = {})
|
|
195
|
+
Git::Commands::Worktree::Lock.new(@execution_context).call(worktree.to_s, **opts).stdout
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Unlock a linked worktree
|
|
199
|
+
#
|
|
200
|
+
# @example Unlock a worktree
|
|
201
|
+
# repo.worktree_unlock('/tmp/feature')
|
|
202
|
+
#
|
|
203
|
+
# @param worktree [String, Git::WorktreeInfo] the path of the worktree to
|
|
204
|
+
# unlock, or its entry from {#worktree_list}
|
|
205
|
+
#
|
|
206
|
+
# @return [String] the output from the git worktree unlock command
|
|
207
|
+
# (typically empty)
|
|
208
|
+
#
|
|
209
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
210
|
+
#
|
|
211
|
+
# @see https://git-scm.com/docs/git-worktree git-worktree documentation
|
|
212
|
+
#
|
|
213
|
+
def worktree_unlock(worktree)
|
|
214
|
+
Git::Commands::Worktree::Unlock.new(@execution_context).call(worktree.to_s).stdout
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Repair the links between the repository and its linked worktrees
|
|
218
|
+
#
|
|
219
|
+
# With no paths, repairs the link from each linked worktree back to the
|
|
220
|
+
# repository, which is needed after the repository directory was moved.
|
|
221
|
+
# Given the current paths of linked worktrees that were moved without
|
|
222
|
+
# {#worktree_move}, also repairs the repository's links to them.
|
|
223
|
+
#
|
|
224
|
+
# @example Repair after the repository directory was moved
|
|
225
|
+
# repo.worktree_repair
|
|
226
|
+
#
|
|
227
|
+
# @example Repair after a linked worktree was moved by hand
|
|
228
|
+
# repo.worktree_repair('/new/path/to/feature')
|
|
229
|
+
#
|
|
230
|
+
# @param paths [Array<String, Git::WorktreeInfo>] the current paths of the
|
|
231
|
+
# worktrees to repair, or their entries from {#worktree_list}
|
|
232
|
+
#
|
|
233
|
+
# @return [String] the output from the git worktree repair command, which
|
|
234
|
+
# reports each repair made
|
|
235
|
+
#
|
|
236
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
237
|
+
#
|
|
238
|
+
# @raise [Git::VersionError] if the installed git is older than 2.29.0
|
|
239
|
+
#
|
|
240
|
+
# @see https://git-scm.com/docs/git-worktree git-worktree documentation
|
|
241
|
+
#
|
|
242
|
+
def worktree_repair(*paths)
|
|
243
|
+
Git::Commands::Worktree::Repair.new(@execution_context).call(*paths.map(&:to_s)).stdout
|
|
92
244
|
end
|
|
93
245
|
|
|
94
246
|
# Prune stale worktree administrative files
|
|
@@ -128,7 +280,22 @@ module Git
|
|
|
128
280
|
#
|
|
129
281
|
# @return [Git::Worktree] a worktree domain object for the given path
|
|
130
282
|
#
|
|
283
|
+
# @deprecated Use {#worktree_add} and {#worktree_remove} instead
|
|
284
|
+
#
|
|
285
|
+
# `repo.worktree(dir, commitish).add` becomes
|
|
286
|
+
# `repo.worktree_add(dir, commitish)` and `repo.worktree(dir).remove`
|
|
287
|
+
# becomes `repo.worktree_remove(dir)`. Read a worktree's checked-out
|
|
288
|
+
# commit from {Git::WorktreeInfo#head} via {#worktree_list}.
|
|
289
|
+
#
|
|
290
|
+
# @see #worktree_add
|
|
291
|
+
#
|
|
292
|
+
# @see #worktree_remove
|
|
293
|
+
#
|
|
131
294
|
def worktree(dir, commitish = nil)
|
|
295
|
+
Git::Deprecation.warn(
|
|
296
|
+
'Git::Repository#worktree is deprecated and will be removed in v6.0.0. ' \
|
|
297
|
+
'Use Git::Repository#worktree_add and Git::Repository#worktree_remove instead.'
|
|
298
|
+
)
|
|
132
299
|
Git::Worktree.new(self, dir, commitish)
|
|
133
300
|
end
|
|
134
301
|
|
|
@@ -151,7 +318,20 @@ module Git
|
|
|
151
318
|
#
|
|
152
319
|
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
153
320
|
#
|
|
321
|
+
# @deprecated Use {#worktree_list} instead
|
|
322
|
+
#
|
|
323
|
+
# {#worktree_list} returns `Array<Git::WorktreeInfo>`. Look a worktree up
|
|
324
|
+
# by path with `worktree_list.find { |w| w.path == path }` in place of
|
|
325
|
+
# `worktrees[path]`, and call {#worktree_prune} in place of
|
|
326
|
+
# `worktrees.prune`. Calling this method emits one deprecation warning.
|
|
327
|
+
#
|
|
328
|
+
# @see #worktree_list
|
|
329
|
+
#
|
|
154
330
|
def worktrees
|
|
331
|
+
Git::Deprecation.warn(
|
|
332
|
+
'Git::Repository#worktrees is deprecated and will be removed in v6.0.0. ' \
|
|
333
|
+
'Use Git::Repository#worktree_list instead.'
|
|
334
|
+
)
|
|
155
335
|
Git::Worktrees.new(self)
|
|
156
336
|
end
|
|
157
337
|
end
|
data/lib/git/stash.rb
CHANGED
|
@@ -3,11 +3,25 @@
|
|
|
3
3
|
module Git
|
|
4
4
|
# Represents a single stash entry in a Git repository
|
|
5
5
|
#
|
|
6
|
-
#
|
|
6
|
+
# This class is deprecated and will be removed in v6.0.0. Use the
|
|
7
|
+
# {Git::Repository} stash methods and {Git::StashInfo} instead:
|
|
8
|
+
# {Git::Repository#stash_push} replaces `Git::Stash.new(repo, message)` and
|
|
9
|
+
# returns a {Git::StashInfo}, or `nil` when there was nothing to stash.
|
|
10
|
+
#
|
|
11
|
+
# @example Create a stash and inspect the result (deprecated)
|
|
7
12
|
# stash = Git::Stash.new(repo, 'WIP: feature work')
|
|
8
13
|
# stash.message #=> "WIP: feature work"
|
|
9
14
|
# stash.saved? #=> true
|
|
10
15
|
#
|
|
16
|
+
# @example The replacement
|
|
17
|
+
# info = repo.stash_push(message: 'WIP: feature work')
|
|
18
|
+
# info.message #=> "On main: WIP: feature work"
|
|
19
|
+
# info.nil? #=> false
|
|
20
|
+
#
|
|
21
|
+
# @deprecated Use {Git::Repository#stash_push} and {Git::StashInfo} instead
|
|
22
|
+
#
|
|
23
|
+
# @see Git::Repository#stash_push
|
|
24
|
+
#
|
|
11
25
|
# @api public
|
|
12
26
|
#
|
|
13
27
|
class Stash
|
|
@@ -16,6 +30,8 @@ module Git
|
|
|
16
30
|
# When `existing` is `false` (the default), immediately calls {#save} to push
|
|
17
31
|
# the current working-directory state onto the stash stack.
|
|
18
32
|
#
|
|
33
|
+
# Emits one deprecation warning per object.
|
|
34
|
+
#
|
|
19
35
|
# @example Create a new stash entry
|
|
20
36
|
# stash = Git::Stash.new(repo, 'WIP: feature work')
|
|
21
37
|
# stash.saved? #=> true
|
|
@@ -32,7 +48,15 @@ module Git
|
|
|
32
48
|
# without pushing any changes
|
|
33
49
|
#
|
|
34
50
|
# @return [void]
|
|
51
|
+
#
|
|
52
|
+
# @deprecated Use {Git::Repository#stash_push} and {Git::StashInfo} instead
|
|
53
|
+
#
|
|
35
54
|
def initialize(base, message, existing: false)
|
|
55
|
+
Git::Deprecation.warn(
|
|
56
|
+
'Git::Stash is deprecated and will be removed in v6.0.0. ' \
|
|
57
|
+
'Use the Git::Repository stash methods (stash_push, stash_infos, stash_apply) ' \
|
|
58
|
+
'and Git::StashInfo instead.'
|
|
59
|
+
)
|
|
36
60
|
@base = base
|
|
37
61
|
@message = message
|
|
38
62
|
save unless existing
|
|
@@ -48,8 +72,10 @@ module Git
|
|
|
48
72
|
# local changes to save
|
|
49
73
|
#
|
|
50
74
|
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
75
|
+
#
|
|
51
76
|
def save
|
|
52
|
-
|
|
77
|
+
# stash_save is deprecated too; silence it so one Git::Stash call emits one warning
|
|
78
|
+
@saved = Git::Deprecation.silence { stash_repository.stash_save(@message) }
|
|
53
79
|
end
|
|
54
80
|
|
|
55
81
|
# Returns whether the stash was saved successfully
|
|
@@ -60,6 +86,7 @@ module Git
|
|
|
60
86
|
#
|
|
61
87
|
# @return [Boolean, nil] `true` if changes were stashed, `false` if there were no
|
|
62
88
|
# local changes, `nil` if {#save} has not been called (e.g. `existing: true`)
|
|
89
|
+
#
|
|
63
90
|
def saved?
|
|
64
91
|
@saved
|
|
65
92
|
end
|
|
@@ -71,6 +98,7 @@ module Git
|
|
|
71
98
|
# stash.message #=> "WIP: feature work"
|
|
72
99
|
#
|
|
73
100
|
# @return [String] the stash message
|
|
101
|
+
#
|
|
74
102
|
attr_reader :message
|
|
75
103
|
|
|
76
104
|
# Returns the stash description as a string
|
|
@@ -80,6 +108,7 @@ module Git
|
|
|
80
108
|
# stash.to_s #=> "WIP: feature work"
|
|
81
109
|
#
|
|
82
110
|
# @return [String] the stash message
|
|
111
|
+
#
|
|
83
112
|
def to_s
|
|
84
113
|
message
|
|
85
114
|
end
|
data/lib/git/stash_info.rb
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'time'
|
|
4
|
+
|
|
5
|
+
require 'git/author_info'
|
|
6
|
+
|
|
3
7
|
module Git
|
|
4
8
|
# Immutable value object representing stash entry information
|
|
5
9
|
#
|
|
6
10
|
# StashInfo encapsulates the parsed data from `git stash list` output.
|
|
7
11
|
# Each entry contains comprehensive information about the stash including
|
|
8
|
-
# its index, reference name, commit SHA, branch, message, author
|
|
9
|
-
#
|
|
12
|
+
# its index, reference name, commit SHA, branch, message, and the author and
|
|
13
|
+
# committer identities.
|
|
10
14
|
#
|
|
15
|
+
# The author and committer are nested {Git::AuthorInfo} values. Their `date`
|
|
16
|
+
# is a `Time` parsed from git's ISO 8601 output, not an ISO 8601 string.
|
|
11
17
|
#
|
|
12
18
|
# @example Create a StashInfo from parsed stash list output
|
|
13
19
|
# info = Git::StashInfo.new(
|
|
@@ -17,12 +23,16 @@ module Git
|
|
|
17
23
|
# short_oid: 'abc123d',
|
|
18
24
|
# branch: 'main',
|
|
19
25
|
# message: 'WIP on main: abc123 Initial commit',
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
+
# author: Git::AuthorInfo.new(
|
|
27
|
+
# name: 'Jane Doe',
|
|
28
|
+
# email: 'jane@example.com',
|
|
29
|
+
# date: Time.iso8601('2026-01-24T10:30:00-08:00')
|
|
30
|
+
# ),
|
|
31
|
+
# committer: Git::AuthorInfo.new(
|
|
32
|
+
# name: 'Jane Doe',
|
|
33
|
+
# email: 'jane@example.com',
|
|
34
|
+
# date: Time.iso8601('2026-01-24T10:30:00-08:00')
|
|
35
|
+
# )
|
|
26
36
|
# )
|
|
27
37
|
#
|
|
28
38
|
# info.index # => 0
|
|
@@ -31,12 +41,12 @@ module Git
|
|
|
31
41
|
# info.short_oid # => 'abc123d'
|
|
32
42
|
# info.branch # => 'main'
|
|
33
43
|
# info.message # => 'WIP on main: abc123 Initial commit'
|
|
34
|
-
# info.
|
|
35
|
-
# info.
|
|
36
|
-
# info.
|
|
37
|
-
# info.
|
|
38
|
-
#
|
|
39
|
-
#
|
|
44
|
+
# info.author.name # => 'Jane Doe'
|
|
45
|
+
# info.author.email # => 'jane@example.com'
|
|
46
|
+
# info.author.date # => 2026-01-24 10:30:00 -0800
|
|
47
|
+
# info.committer.name # => 'Jane Doe'
|
|
48
|
+
#
|
|
49
|
+
# @see Git::AuthorInfo for the nested author and committer identities
|
|
40
50
|
#
|
|
41
51
|
# @api public
|
|
42
52
|
#
|
|
@@ -59,23 +69,15 @@ module Git
|
|
|
59
69
|
# @!attribute [r] message
|
|
60
70
|
# @return [String] the stash message (e.g., 'WIP on main: abc123 commit msg')
|
|
61
71
|
#
|
|
62
|
-
# @!attribute [r]
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
# @!attribute [r] author_email
|
|
66
|
-
# @return [String] the email of the stash author
|
|
67
|
-
#
|
|
68
|
-
# @!attribute [r] author_date
|
|
69
|
-
# @return [String] the author date in ISO 8601 format
|
|
72
|
+
# @!attribute [r] author
|
|
73
|
+
# The identity of the stash author; the nested `date` is a `Time`.
|
|
70
74
|
#
|
|
71
|
-
#
|
|
72
|
-
# @return [String] the name of the stash committer
|
|
75
|
+
# @return [Git::AuthorInfo] the author of the stash commit
|
|
73
76
|
#
|
|
74
|
-
# @!attribute [r]
|
|
75
|
-
#
|
|
77
|
+
# @!attribute [r] committer
|
|
78
|
+
# The identity of the stash committer; the nested `date` is a `Time`.
|
|
76
79
|
#
|
|
77
|
-
#
|
|
78
|
-
# @return [String] the committer date in ISO 8601 format
|
|
80
|
+
# @return [Git::AuthorInfo] the committer of the stash commit
|
|
79
81
|
#
|
|
80
82
|
StashInfo = Data.define(
|
|
81
83
|
:index,
|
|
@@ -84,12 +86,8 @@ module Git
|
|
|
84
86
|
:short_oid,
|
|
85
87
|
:branch,
|
|
86
88
|
:message,
|
|
87
|
-
:
|
|
88
|
-
:
|
|
89
|
-
:author_date,
|
|
90
|
-
:committer_name,
|
|
91
|
-
:committer_email,
|
|
92
|
-
:committer_date
|
|
89
|
+
:author,
|
|
90
|
+
:committer
|
|
93
91
|
) do
|
|
94
92
|
# Returns the stash reference name
|
|
95
93
|
#
|