git 5.7.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +132 -35
- data/CONTRIBUTING.md +44 -31
- data/README.md +138 -100
- data/UPGRADING.md +867 -262
- data/git.gemspec +6 -8
- data/lib/git/author_info.rb +1 -2
- data/lib/git/commands/base.rb +22 -2
- data/lib/git/commands/cat_file/raw.rb +0 -68
- data/lib/git/commands/maintenance/register.rb +0 -4
- data/lib/git/commands/maintenance/run.rb +0 -4
- data/lib/git/commands/maintenance/start.rb +0 -4
- data/lib/git/commands/maintenance/stop.rb +0 -4
- data/lib/git/commands/maintenance/unregister.rb +0 -4
- data/lib/git/commands/maintenance.rb +0 -2
- data/lib/git/commands/show_ref/exclude_existing.rb +1 -1
- data/lib/git/commands/show_ref/exists.rb +0 -5
- data/lib/git/commands/show_ref/list.rb +1 -1
- data/lib/git/commands/show_ref/verify.rb +1 -1
- data/lib/git/commands/show_ref.rb +1 -1
- data/lib/git/commands/worktree/repair.rb +0 -5
- data/lib/git/diff_path_status.rb +1 -1
- data/lib/git/encoding_utils.rb +25 -0
- data/lib/git/errors.rb +24 -18
- data/lib/git/factories.rb +361 -125
- data/lib/git/log.rb +3 -75
- data/lib/git/object.rb +4 -185
- data/lib/git/path_resolver.rb +104 -36
- data/lib/git/repository/branching.rb +21 -189
- data/lib/git/repository/committing.rb +0 -25
- data/lib/git/repository/context_helpers.rb +249 -155
- data/lib/git/repository/diffing.rb +2 -72
- data/lib/git/repository/merging.rb +32 -54
- data/lib/git/repository/object_operations.rb +120 -497
- data/lib/git/repository/remote_operations.rb +7 -219
- data/lib/git/repository/staging.rb +6 -184
- data/lib/git/repository/stashing.rb +126 -130
- data/lib/git/repository/status_operations.rb +1 -54
- data/lib/git/repository/worktree_operations.rb +30 -125
- data/lib/git/repository.rb +20 -299
- data/lib/git/system_call_guard.rb +95 -0
- data/lib/git/version.rb +1 -1
- data/lib/git.rb +47 -364
- metadata +24 -18
- data/lib/git/author.rb +0 -46
- data/lib/git/branch.rb +0 -693
- data/lib/git/branches.rb +0 -194
- data/lib/git/remote.rb +0 -191
- data/lib/git/stash.rb +0 -124
- data/lib/git/stashes.rb +0 -198
- data/lib/git/status.rb +0 -384
- data/lib/git/worktree.rb +0 -180
- data/lib/git/worktrees.rb +0 -152
data/lib/git/status.rb
DELETED
|
@@ -1,384 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Git
|
|
4
|
-
# The Status class gets the status of a git repository. It identifies which
|
|
5
|
-
# files have been modified, added, or deleted, including untracked files.
|
|
6
|
-
# The Status object is an Enumerable of StatusFile objects.
|
|
7
|
-
#
|
|
8
|
-
# @example Inspect repository status
|
|
9
|
-
# repo = Git.open('/path/to/repo')
|
|
10
|
-
# status = repo.status
|
|
11
|
-
# status.changed.each { |path, _file| puts "Modified: #{path}" }
|
|
12
|
-
# status.added.each { |path, _file| puts "Added: #{path}" }
|
|
13
|
-
# status.deleted.each { |path, _file| puts "Deleted: #{path}" }
|
|
14
|
-
# status.untracked.each { |path, _file| puts "Untracked: #{path}" }
|
|
15
|
-
#
|
|
16
|
-
# @deprecated Use {Git::StatusInfo}, returned by {Git::Repository#status_info},
|
|
17
|
-
# instead; this class will be removed in v6.0.0
|
|
18
|
-
#
|
|
19
|
-
# @api public
|
|
20
|
-
#
|
|
21
|
-
class Status
|
|
22
|
-
include Enumerable
|
|
23
|
-
|
|
24
|
-
# Create a new Status for the given repository
|
|
25
|
-
#
|
|
26
|
-
# @param base [Git::Repository] the git object backing this status
|
|
27
|
-
#
|
|
28
|
-
# @deprecated Use {Git::Repository#status_info} instead
|
|
29
|
-
#
|
|
30
|
-
def initialize(base)
|
|
31
|
-
if defined?(Git::Deprecation)
|
|
32
|
-
Git::Deprecation.warn(
|
|
33
|
-
'Git::Status is deprecated and will be removed in v6.0.0. ' \
|
|
34
|
-
'Use Git::Repository#status_info instead.'
|
|
35
|
-
)
|
|
36
|
-
end
|
|
37
|
-
@base = base
|
|
38
|
-
# The factory returns a hash of file paths to StatusFile objects.
|
|
39
|
-
@files = StatusFileFactory.new(base).construct_files
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Return files modified in the index and/or working tree
|
|
43
|
-
#
|
|
44
|
-
# Includes both staged modifications (index vs HEAD) and unstaged modifications
|
|
45
|
-
# (working tree vs index).
|
|
46
|
-
#
|
|
47
|
-
# @return [Hash{String => Git::Status::StatusFile}] changed files keyed by path
|
|
48
|
-
#
|
|
49
|
-
def changed = @changed ||= select_files { |f| f.type == 'M' }
|
|
50
|
-
|
|
51
|
-
# Return files added to the index that are not yet in HEAD
|
|
52
|
-
#
|
|
53
|
-
# @return [Hash{String => Git::Status::StatusFile}] added files keyed by path
|
|
54
|
-
#
|
|
55
|
-
def added = @added ||= select_files { |f| f.type == 'A' }
|
|
56
|
-
|
|
57
|
-
# Return files deleted from the index
|
|
58
|
-
#
|
|
59
|
-
# @return [Hash{String => Git::Status::StatusFile}] deleted files keyed by path
|
|
60
|
-
#
|
|
61
|
-
def deleted = @deleted ||= select_files { |f| f.type == 'D' }
|
|
62
|
-
|
|
63
|
-
# Return files present in the working tree but not tracked by git
|
|
64
|
-
#
|
|
65
|
-
# @return [Hash{String => Git::Status::StatusFile}] untracked files keyed by path
|
|
66
|
-
#
|
|
67
|
-
def untracked = @untracked ||= select_files(&:untracked)
|
|
68
|
-
|
|
69
|
-
# Return `true` if `file` has been modified in the index or working tree
|
|
70
|
-
#
|
|
71
|
-
# @param file [String] the repository-relative path to check
|
|
72
|
-
#
|
|
73
|
-
# @return [Boolean] `true` if the file has been modified
|
|
74
|
-
#
|
|
75
|
-
def changed?(file) = file_in_collection?(:changed, file)
|
|
76
|
-
|
|
77
|
-
# Return `true` if `file` has been added to the index
|
|
78
|
-
#
|
|
79
|
-
# @param file [String] the repository-relative path to check
|
|
80
|
-
#
|
|
81
|
-
# @return [Boolean] `true` if the file has been added
|
|
82
|
-
#
|
|
83
|
-
def added?(file) = file_in_collection?(:added, file)
|
|
84
|
-
|
|
85
|
-
# Return `true` if `file` has been deleted from the index
|
|
86
|
-
#
|
|
87
|
-
# @param file [String] the repository-relative path to check
|
|
88
|
-
#
|
|
89
|
-
# @return [Boolean] `true` if the file has been deleted
|
|
90
|
-
#
|
|
91
|
-
def deleted?(file) = file_in_collection?(:deleted, file)
|
|
92
|
-
|
|
93
|
-
# Return `true` if `file` is not tracked by git
|
|
94
|
-
#
|
|
95
|
-
# @param file [String] the repository-relative path to check
|
|
96
|
-
#
|
|
97
|
-
# @return [Boolean] `true` if the file is untracked
|
|
98
|
-
#
|
|
99
|
-
def untracked?(file) = file_in_collection?(:untracked, file)
|
|
100
|
-
|
|
101
|
-
# Return the {Git::Status::StatusFile} for the given path
|
|
102
|
-
#
|
|
103
|
-
# @param file [String] the repository-relative path
|
|
104
|
-
#
|
|
105
|
-
# @return [Git::Status::StatusFile, nil] the status file, or `nil` if not found
|
|
106
|
-
#
|
|
107
|
-
def [](file) = @files[file]
|
|
108
|
-
|
|
109
|
-
# Iterate over all status files
|
|
110
|
-
#
|
|
111
|
-
# @overload each
|
|
112
|
-
#
|
|
113
|
-
# @return [Enumerator<Git::Status::StatusFile>] an enumerator over all status files
|
|
114
|
-
#
|
|
115
|
-
# @overload each(&block)
|
|
116
|
-
#
|
|
117
|
-
# @return [Array<Git::Status::StatusFile>] the full list of status files
|
|
118
|
-
#
|
|
119
|
-
# @yield [file] each {Git::Status::StatusFile} in the repository
|
|
120
|
-
#
|
|
121
|
-
# @yieldparam file [Git::Status::StatusFile] a single file's status
|
|
122
|
-
#
|
|
123
|
-
# @yieldreturn [void]
|
|
124
|
-
#
|
|
125
|
-
def each(&) = @files.values.each(&)
|
|
126
|
-
|
|
127
|
-
# Return a formatted multi-line string representation of the status
|
|
128
|
-
#
|
|
129
|
-
# @return [String] one indented block per file showing its SHA, mode, type,
|
|
130
|
-
# stage, and untracked flag
|
|
131
|
-
#
|
|
132
|
-
def pretty
|
|
133
|
-
map { |file| pretty_file(file) }.join << "\n"
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
private
|
|
137
|
-
|
|
138
|
-
# Format a single file's status as an indented multi-line string
|
|
139
|
-
#
|
|
140
|
-
# @param file [Git::Status::StatusFile] the file to format
|
|
141
|
-
#
|
|
142
|
-
# @return [String] the formatted status block for this file
|
|
143
|
-
#
|
|
144
|
-
def pretty_file(file)
|
|
145
|
-
<<~FILE
|
|
146
|
-
#{file.path}
|
|
147
|
-
\tsha(r) #{file.sha_repo} #{file.mode_repo}
|
|
148
|
-
\tsha(i) #{file.sha_index} #{file.mode_index}
|
|
149
|
-
\ttype #{file.type}
|
|
150
|
-
\tstage #{file.stage}
|
|
151
|
-
\tuntrac #{file.untracked}
|
|
152
|
-
FILE
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
# Return a hash of files for which the block returns a truthy value
|
|
156
|
-
#
|
|
157
|
-
# @return [Hash{String => Git::Status::StatusFile}] matching files keyed by path
|
|
158
|
-
#
|
|
159
|
-
# @yield [file] each {Git::Status::StatusFile} in the repository
|
|
160
|
-
#
|
|
161
|
-
# @yieldparam file [Git::Status::StatusFile] a single file's status
|
|
162
|
-
#
|
|
163
|
-
# @yieldreturn [Boolean] truthy to include the file in the result
|
|
164
|
-
#
|
|
165
|
-
def select_files(&block)
|
|
166
|
-
@files.select { |_path, file| block.call(file) }
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
# Return `true` if `file_path` exists in the named status collection
|
|
170
|
-
#
|
|
171
|
-
# @param collection_name [Symbol] the collection to check (e.g. `:changed`)
|
|
172
|
-
#
|
|
173
|
-
# @param file_path [String] the repository-relative path to look up
|
|
174
|
-
#
|
|
175
|
-
# @return [Boolean] `true` if the path is present in the collection
|
|
176
|
-
#
|
|
177
|
-
def file_in_collection?(collection_name, file_path)
|
|
178
|
-
collection = public_send(collection_name)
|
|
179
|
-
if ignore_case?
|
|
180
|
-
downcased_keys(collection_name).include?(file_path.downcase)
|
|
181
|
-
else
|
|
182
|
-
collection.key?(file_path)
|
|
183
|
-
end
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
# Return a memoized set of downcased keys for the named collection
|
|
187
|
-
#
|
|
188
|
-
# @param collection_name [Symbol] the collection whose keys to downcase
|
|
189
|
-
#
|
|
190
|
-
# @return [Set<String>] the lowercased path keys
|
|
191
|
-
#
|
|
192
|
-
def downcased_keys(collection_name)
|
|
193
|
-
@_downcased_keys ||= {}
|
|
194
|
-
@_downcased_keys[collection_name] ||=
|
|
195
|
-
public_send(collection_name).keys.to_set(&:downcase)
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
# Return `true` when git is configured to ignore filename case
|
|
199
|
-
#
|
|
200
|
-
# Reads `core.ignoreCase` with {Git::Repository#config_get}. Returns `false`
|
|
201
|
-
# when the config value is absent.
|
|
202
|
-
#
|
|
203
|
-
# @return [Boolean] `true` when `core.ignoreCase` is `"true"`
|
|
204
|
-
#
|
|
205
|
-
def ignore_case?
|
|
206
|
-
return @ignore_case if defined?(@ignore_case)
|
|
207
|
-
|
|
208
|
-
@ignore_case = (@base.config_get('core.ignoreCase')&.value == 'true')
|
|
209
|
-
end
|
|
210
|
-
end
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
module Git
|
|
214
|
-
class Status
|
|
215
|
-
# Represents a single file's status in the git repository. Each instance
|
|
216
|
-
# holds information about a file's state in the index and working tree.
|
|
217
|
-
#
|
|
218
|
-
# @deprecated Use {Git::StatusFileInfo}, held by {Git::StatusInfo#files},
|
|
219
|
-
# instead; this class will be removed in v6.0.0
|
|
220
|
-
#
|
|
221
|
-
# @api public
|
|
222
|
-
#
|
|
223
|
-
class StatusFile
|
|
224
|
-
# The repository-relative file path
|
|
225
|
-
#
|
|
226
|
-
# @return [String] the path
|
|
227
|
-
attr_reader :path
|
|
228
|
-
|
|
229
|
-
# The change type for this file
|
|
230
|
-
#
|
|
231
|
-
# @return [String, nil] `"M"` for modified, `"A"` for added, `"D"` for deleted,
|
|
232
|
-
# or `nil` when not applicable
|
|
233
|
-
attr_reader :type
|
|
234
|
-
|
|
235
|
-
# The merge stage for this file
|
|
236
|
-
#
|
|
237
|
-
# @return [String, nil] `"0"` for normal entries, or a non-zero value during
|
|
238
|
-
# a merge conflict
|
|
239
|
-
attr_reader :stage
|
|
240
|
-
|
|
241
|
-
# The file mode recorded in the index
|
|
242
|
-
#
|
|
243
|
-
# @return [String, nil] the octal file mode (e.g. `"100644"`), or `nil`
|
|
244
|
-
attr_reader :mode_index
|
|
245
|
-
|
|
246
|
-
# The file mode recorded in HEAD
|
|
247
|
-
#
|
|
248
|
-
# @return [String, nil] the octal file mode (e.g. `"100644"`), or `nil`
|
|
249
|
-
attr_reader :mode_repo
|
|
250
|
-
|
|
251
|
-
# The SHA of the index version of this file
|
|
252
|
-
#
|
|
253
|
-
# @return [String, nil] the SHA-1 hex digest, or `nil` if unavailable
|
|
254
|
-
attr_reader :sha_index
|
|
255
|
-
|
|
256
|
-
# The SHA of the HEAD version of this file
|
|
257
|
-
#
|
|
258
|
-
# @return [String, nil] the SHA-1 hex digest, or `nil` if unavailable
|
|
259
|
-
attr_reader :sha_repo
|
|
260
|
-
|
|
261
|
-
# Whether this file is untracked
|
|
262
|
-
#
|
|
263
|
-
# @return [Boolean, nil] `true` when the file is not tracked by git
|
|
264
|
-
attr_reader :untracked
|
|
265
|
-
|
|
266
|
-
# Initialize a new StatusFile with the given git object and data hash
|
|
267
|
-
#
|
|
268
|
-
# @param base [Git::Repository] the git object
|
|
269
|
-
#
|
|
270
|
-
# @param hash [Hash] raw status data for this file
|
|
271
|
-
#
|
|
272
|
-
def initialize(base, hash)
|
|
273
|
-
@base = base
|
|
274
|
-
@path = hash[:path]
|
|
275
|
-
@type = hash[:type]
|
|
276
|
-
@stage = hash[:stage]
|
|
277
|
-
@mode_index = hash[:mode_index]
|
|
278
|
-
@mode_repo = hash[:mode_repo]
|
|
279
|
-
@sha_index = hash[:sha_index]
|
|
280
|
-
@sha_repo = hash[:sha_repo]
|
|
281
|
-
@untracked = hash[:untracked]
|
|
282
|
-
end
|
|
283
|
-
|
|
284
|
-
# Return a blob object for the index or repo version of this file
|
|
285
|
-
#
|
|
286
|
-
# @param type [Symbol] `:index` (default) for the index version, or
|
|
287
|
-
# `:repo` for the HEAD version
|
|
288
|
-
#
|
|
289
|
-
# @return [Git::Object::Blob, nil] the blob object, or `nil` if no SHA
|
|
290
|
-
# is available for the requested version
|
|
291
|
-
#
|
|
292
|
-
def blob(type = :index)
|
|
293
|
-
sha = type == :repo ? sha_repo : (sha_index || sha_repo)
|
|
294
|
-
@base.object(sha) if sha
|
|
295
|
-
end
|
|
296
|
-
end
|
|
297
|
-
end
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
module Git
|
|
301
|
-
class Status
|
|
302
|
-
# A factory class responsible for fetching git status data and building
|
|
303
|
-
# a hash of StatusFile objects.
|
|
304
|
-
#
|
|
305
|
-
# @api private
|
|
306
|
-
#
|
|
307
|
-
class StatusFileFactory
|
|
308
|
-
# Create a new factory backed by the given git object
|
|
309
|
-
#
|
|
310
|
-
# @param base [Git::Repository] the git object used as the status data provider
|
|
311
|
-
#
|
|
312
|
-
def initialize(base)
|
|
313
|
-
@base = base
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
# Gather all status data and build a hash of file paths to StatusFile objects
|
|
317
|
-
#
|
|
318
|
-
# @return [Hash{String => Git::Status::StatusFile}] file paths mapped to
|
|
319
|
-
# their status objects
|
|
320
|
-
#
|
|
321
|
-
def construct_files
|
|
322
|
-
files_data = fetch_all_files_data
|
|
323
|
-
files_data.transform_values do |data|
|
|
324
|
-
StatusFile.new(@base, data)
|
|
325
|
-
end
|
|
326
|
-
end
|
|
327
|
-
|
|
328
|
-
private
|
|
329
|
-
|
|
330
|
-
# Fetch and merge status information from multiple git commands
|
|
331
|
-
#
|
|
332
|
-
# @return [Hash{String => Hash}] raw per-file status data keyed by path
|
|
333
|
-
#
|
|
334
|
-
def fetch_all_files_data
|
|
335
|
-
files = @base.ls_files # Start with files tracked in the index.
|
|
336
|
-
merge_untracked_files(files)
|
|
337
|
-
merge_modified_files(files)
|
|
338
|
-
merge_head_diffs(files)
|
|
339
|
-
files
|
|
340
|
-
end
|
|
341
|
-
|
|
342
|
-
# Merge untracked working-tree files into `files`
|
|
343
|
-
#
|
|
344
|
-
# @param files [Hash] the in-progress files hash to update in place
|
|
345
|
-
#
|
|
346
|
-
# @return [void]
|
|
347
|
-
#
|
|
348
|
-
def merge_untracked_files(files)
|
|
349
|
-
@base.untracked_files.each do |file|
|
|
350
|
-
files[file] = { path: file, untracked: true }
|
|
351
|
-
end
|
|
352
|
-
end
|
|
353
|
-
|
|
354
|
-
# Merge index-versus-working-tree diff data into `files`
|
|
355
|
-
#
|
|
356
|
-
# @param files [Hash] the in-progress files hash to update in place
|
|
357
|
-
#
|
|
358
|
-
# @return [void]
|
|
359
|
-
#
|
|
360
|
-
def merge_modified_files(files)
|
|
361
|
-
# Merge changes between the index and the working directory.
|
|
362
|
-
@base.diff_files.each do |path, data|
|
|
363
|
-
(files[path] ||= {}).merge!(data)
|
|
364
|
-
end
|
|
365
|
-
end
|
|
366
|
-
|
|
367
|
-
# Merge HEAD-versus-index diff data into `files`, if commits exist
|
|
368
|
-
#
|
|
369
|
-
# @param files [Hash] the in-progress files hash to update in place
|
|
370
|
-
#
|
|
371
|
-
# @return [void]
|
|
372
|
-
#
|
|
373
|
-
def merge_head_diffs(files)
|
|
374
|
-
is_empty = @base.no_commits?
|
|
375
|
-
return if is_empty
|
|
376
|
-
|
|
377
|
-
# Merge changes between HEAD and the index.
|
|
378
|
-
@base.diff_index('HEAD').each do |path, data|
|
|
379
|
-
(files[path] ||= {}).merge!(data)
|
|
380
|
-
end
|
|
381
|
-
end
|
|
382
|
-
end
|
|
383
|
-
end
|
|
384
|
-
end
|
data/lib/git/worktree.rb
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Git
|
|
4
|
-
# A worktree in a Git repository
|
|
5
|
-
#
|
|
6
|
-
# Represents a single linked or main worktree. Constructed by
|
|
7
|
-
# {Git::Repository::WorktreeOperations#worktree} or populated by
|
|
8
|
-
# {Git::Worktrees}.
|
|
9
|
-
#
|
|
10
|
-
# @example Add and remove a linked worktree
|
|
11
|
-
# worktree = repo.worktree('/path/to/new-worktree')
|
|
12
|
-
# worktree.add
|
|
13
|
-
# worktree.remove
|
|
14
|
-
#
|
|
15
|
-
# @deprecated Use {Git::Repository::WorktreeOperations#worktree_list} and the
|
|
16
|
-
# path-based worktree operations on {Git::Repository} instead
|
|
17
|
-
#
|
|
18
|
-
# {Git::Repository::WorktreeOperations#worktree_list} returns immutable
|
|
19
|
-
# {Git::WorktreeInfo} value objects. Operations that lived on this class are
|
|
20
|
-
# called on the repository with the worktree path instead (for example
|
|
21
|
-
# {Git::Repository::WorktreeOperations#worktree_add} and
|
|
22
|
-
# {Git::Repository::WorktreeOperations#worktree_remove}). {#gcommit},
|
|
23
|
-
# {#add}, and {#remove} each emit a deprecation warning; the `dir`, `full`,
|
|
24
|
-
# `to_s`, and `to_a` readers do not.
|
|
25
|
-
#
|
|
26
|
-
# @api public
|
|
27
|
-
#
|
|
28
|
-
class Worktree
|
|
29
|
-
# Full worktree descriptor including the optional commitish
|
|
30
|
-
#
|
|
31
|
-
# @return [String] the filesystem path, space-separated with the commitish
|
|
32
|
-
# when one was given at construction time
|
|
33
|
-
#
|
|
34
|
-
attr_accessor :full
|
|
35
|
-
|
|
36
|
-
# Filesystem path of this worktree
|
|
37
|
-
#
|
|
38
|
-
# @return [String] the filesystem path of the worktree directory
|
|
39
|
-
#
|
|
40
|
-
attr_accessor :dir
|
|
41
|
-
|
|
42
|
-
# Creates a new Worktree object
|
|
43
|
-
#
|
|
44
|
-
# @param base [Git::Repository] the repository that owns this
|
|
45
|
-
# worktree
|
|
46
|
-
#
|
|
47
|
-
# @param dir [String] filesystem path of the worktree
|
|
48
|
-
#
|
|
49
|
-
# @param gcommit [String, nil] commitish associated with the worktree;
|
|
50
|
-
# when non-nil it is appended to {#full}
|
|
51
|
-
#
|
|
52
|
-
# @return [void]
|
|
53
|
-
#
|
|
54
|
-
def initialize(base, dir, gcommit = nil)
|
|
55
|
-
@full = dir
|
|
56
|
-
@full += " #{gcommit}" unless gcommit.nil?
|
|
57
|
-
@base = base
|
|
58
|
-
@dir = dir
|
|
59
|
-
@gcommit = gcommit
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Returns the commit (or commitish string) associated with this worktree
|
|
63
|
-
#
|
|
64
|
-
# When a commitish string was supplied at construction time (e.g. by
|
|
65
|
-
# {Git::Worktrees} which passes the raw SHA from `git worktree list`), that
|
|
66
|
-
# string is returned as-is. Otherwise the value is lazily resolved on first
|
|
67
|
-
# call via `worktree_repository.gcommit(@full)` and the result is memoized.
|
|
68
|
-
#
|
|
69
|
-
# @example When resolved lazily (no commitish at construction)
|
|
70
|
-
# worktree = repo.worktree('/path/to/wt')
|
|
71
|
-
# worktree.gcommit # => #<Git::Object::Commit ...>
|
|
72
|
-
#
|
|
73
|
-
# @example When the commitish was given at construction
|
|
74
|
-
# worktree = repo.worktrees['/path/to/wt']
|
|
75
|
-
# worktree.gcommit # => "4bef5ab8c9..." (raw SHA string)
|
|
76
|
-
#
|
|
77
|
-
# @return [Git::Object::Commit, String] a commit object when lazily
|
|
78
|
-
# resolved, or the raw commitish string when pre-set at construction
|
|
79
|
-
#
|
|
80
|
-
# @raise [Git::FailedError] if git must resolve the commit and exits with a
|
|
81
|
-
# non-zero exit status
|
|
82
|
-
#
|
|
83
|
-
# @deprecated Use {Git::WorktreeInfo#head} from
|
|
84
|
-
# {Git::Repository::WorktreeOperations#worktree_list} instead
|
|
85
|
-
#
|
|
86
|
-
# `head` is always the commit SHA as a `String` (or `nil` for a bare main
|
|
87
|
-
# worktree). Call `repo.gcommit(info.head)` for the commit object.
|
|
88
|
-
#
|
|
89
|
-
# @see Git::WorktreeInfo#head
|
|
90
|
-
#
|
|
91
|
-
def gcommit
|
|
92
|
-
Git::Deprecation.warn(
|
|
93
|
-
'Git::Worktree#gcommit is deprecated and will be removed in v6.0.0. ' \
|
|
94
|
-
'Use Git::WorktreeInfo#head from Git::Repository#worktree_list instead.'
|
|
95
|
-
)
|
|
96
|
-
@gcommit ||= worktree_repository.gcommit(@full)
|
|
97
|
-
@gcommit
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
# Creates this worktree on disk
|
|
101
|
-
#
|
|
102
|
-
# Runs `git worktree add` for {#dir}, optionally at the commitish passed
|
|
103
|
-
# at construction time.
|
|
104
|
-
#
|
|
105
|
-
# @example Add a worktree
|
|
106
|
-
# worktree = repo.worktree('/path/to/new-worktree')
|
|
107
|
-
# worktree.add
|
|
108
|
-
#
|
|
109
|
-
# @return [String] stdout from the git command
|
|
110
|
-
#
|
|
111
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
112
|
-
#
|
|
113
|
-
# @deprecated Use {Git::Repository::WorktreeOperations#worktree_add} instead
|
|
114
|
-
#
|
|
115
|
-
# @see Git::Repository::WorktreeOperations#worktree_add
|
|
116
|
-
#
|
|
117
|
-
def add
|
|
118
|
-
Git::Deprecation.warn(
|
|
119
|
-
'Git::Worktree#add is deprecated and will be removed in v6.0.0. ' \
|
|
120
|
-
'Use Git::Repository#worktree_add instead.'
|
|
121
|
-
)
|
|
122
|
-
worktree_repository.worktree_add(@dir, @gcommit)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# Removes this worktree from disk
|
|
126
|
-
#
|
|
127
|
-
# Runs `git worktree remove` for {#dir}.
|
|
128
|
-
#
|
|
129
|
-
# @example Remove a worktree
|
|
130
|
-
# worktree.remove
|
|
131
|
-
#
|
|
132
|
-
# @return [String] stdout from the git command (typically empty)
|
|
133
|
-
#
|
|
134
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
135
|
-
#
|
|
136
|
-
# @deprecated Use {Git::Repository::WorktreeOperations#worktree_remove} instead
|
|
137
|
-
#
|
|
138
|
-
# @see Git::Repository::WorktreeOperations#worktree_remove
|
|
139
|
-
#
|
|
140
|
-
def remove
|
|
141
|
-
Git::Deprecation.warn(
|
|
142
|
-
'Git::Worktree#remove is deprecated and will be removed in v6.0.0. ' \
|
|
143
|
-
'Use Git::Repository#worktree_remove instead.'
|
|
144
|
-
)
|
|
145
|
-
worktree_repository.worktree_remove(@dir)
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
# Returns an array containing the full worktree descriptor
|
|
149
|
-
#
|
|
150
|
-
# @example Get the descriptor array
|
|
151
|
-
# worktree.to_a # => ["/path/to/worktree"]
|
|
152
|
-
#
|
|
153
|
-
# @return [Array<String>] array containing the full worktree descriptor
|
|
154
|
-
#
|
|
155
|
-
def to_a
|
|
156
|
-
[@full]
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
# Returns the full worktree descriptor as a string
|
|
160
|
-
#
|
|
161
|
-
# @example Get the descriptor string
|
|
162
|
-
# worktree.to_s # => "/path/to/worktree"
|
|
163
|
-
#
|
|
164
|
-
# @return [String] the full worktree descriptor (path and optional commitish)
|
|
165
|
-
#
|
|
166
|
-
def to_s
|
|
167
|
-
@full
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
private
|
|
171
|
-
|
|
172
|
-
# @return [Git::Repository] the repository used for worktree operations
|
|
173
|
-
#
|
|
174
|
-
# @api private
|
|
175
|
-
#
|
|
176
|
-
def worktree_repository
|
|
177
|
-
@base
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
end
|
data/lib/git/worktrees.rb
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Git
|
|
4
|
-
# Collection of all Git worktrees in a repository
|
|
5
|
-
#
|
|
6
|
-
# Wraps every linked and main worktree and provides enumeration and
|
|
7
|
-
# path-based lookup.
|
|
8
|
-
#
|
|
9
|
-
# @example Enumerate all worktrees
|
|
10
|
-
# worktrees = repo.worktrees
|
|
11
|
-
# worktrees.each { |wt| puts wt.dir }
|
|
12
|
-
#
|
|
13
|
-
# @deprecated Use {Git::Repository::WorktreeOperations#worktree_list} instead
|
|
14
|
-
#
|
|
15
|
-
# {Git::Repository::WorktreeOperations#worktree_list} returns
|
|
16
|
-
# `Array<Git::WorktreeInfo>` (immutable value objects). Look a worktree up
|
|
17
|
-
# by path with `worktree_list.find { |w| w.path == path }` in place of
|
|
18
|
-
# {#[]}, and call {Git::Repository::WorktreeOperations#worktree_prune} in
|
|
19
|
-
# place of {#prune}. Constructing a `Git::Worktrees` emits a deprecation
|
|
20
|
-
# warning.
|
|
21
|
-
#
|
|
22
|
-
# @api public
|
|
23
|
-
#
|
|
24
|
-
class Worktrees
|
|
25
|
-
include Enumerable
|
|
26
|
-
|
|
27
|
-
# Creates a new Worktrees collection populated from the given repository
|
|
28
|
-
#
|
|
29
|
-
# @param base [Git::Repository] the repository to enumerate
|
|
30
|
-
# worktrees from
|
|
31
|
-
#
|
|
32
|
-
# @return [void]
|
|
33
|
-
#
|
|
34
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
35
|
-
#
|
|
36
|
-
# @deprecated Use {Git::Repository::WorktreeOperations#worktree_list} instead
|
|
37
|
-
#
|
|
38
|
-
# @see Git::Repository::WorktreeOperations#worktree_list
|
|
39
|
-
#
|
|
40
|
-
def initialize(base)
|
|
41
|
-
Git::Deprecation.warn(
|
|
42
|
-
'Git::Worktrees is deprecated and will be removed in v6.0.0. ' \
|
|
43
|
-
'Use Git::Repository#worktree_list instead.'
|
|
44
|
-
)
|
|
45
|
-
@worktrees = {}
|
|
46
|
-
|
|
47
|
-
@base = base
|
|
48
|
-
|
|
49
|
-
# worktrees_all is deprecated too; silence it so one Git::Worktrees.new emits one warning
|
|
50
|
-
Git::Deprecation.silence { worktree_repository.worktrees_all }.each do |w|
|
|
51
|
-
@worktrees[w[0]] = Git::Worktree.new(@base, w[0], w[1])
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Returns the number of worktrees in the collection
|
|
56
|
-
#
|
|
57
|
-
# @example Count all worktrees
|
|
58
|
-
# repo.worktrees.size # => 2
|
|
59
|
-
#
|
|
60
|
-
# @return [Integer] the total number of worktrees
|
|
61
|
-
#
|
|
62
|
-
def size
|
|
63
|
-
@worktrees.size
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Iterates over every worktree in the collection
|
|
67
|
-
#
|
|
68
|
-
# @overload each
|
|
69
|
-
#
|
|
70
|
-
# @example Get an enumerator over all worktrees
|
|
71
|
-
# enum = repo.worktrees.each
|
|
72
|
-
#
|
|
73
|
-
# @return [Enumerator<Git::Worktree>] an enumerator over all worktrees
|
|
74
|
-
#
|
|
75
|
-
# @overload each(&block)
|
|
76
|
-
#
|
|
77
|
-
# @example Print every worktree path
|
|
78
|
-
# repo.worktrees.each { |wt| puts wt.dir }
|
|
79
|
-
#
|
|
80
|
-
# @return [Array<Git::Worktree>] the full list of worktrees
|
|
81
|
-
#
|
|
82
|
-
# @yield [worktree] passes each worktree to the block
|
|
83
|
-
#
|
|
84
|
-
# @yieldparam worktree [Git::Worktree] a worktree in the repository
|
|
85
|
-
#
|
|
86
|
-
# @yieldreturn [void]
|
|
87
|
-
#
|
|
88
|
-
def each(&)
|
|
89
|
-
@worktrees.values.each(&)
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# Returns the worktree with the given path
|
|
93
|
-
#
|
|
94
|
-
# Supports lookup by the filesystem path of the worktree directory or by
|
|
95
|
-
# the full worktree descriptor (path and optional commitish).
|
|
96
|
-
#
|
|
97
|
-
# @example Look up a worktree by path
|
|
98
|
-
# repo.worktrees['/path/to/linked-worktree']
|
|
99
|
-
#
|
|
100
|
-
# @param worktree_name [#to_s] the path (or full descriptor) of the
|
|
101
|
-
# worktree to retrieve
|
|
102
|
-
#
|
|
103
|
-
# @return [Git::Worktree, nil] the matching worktree, or `nil` if not found
|
|
104
|
-
#
|
|
105
|
-
def [](worktree_name)
|
|
106
|
-
@worktrees.values.each_with_object(@worktrees) do |worktree, worktrees|
|
|
107
|
-
worktrees[worktree.full] ||= worktree
|
|
108
|
-
end[worktree_name.to_s]
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# Returns a string listing all worktrees, one per line
|
|
112
|
-
#
|
|
113
|
-
# @example Display all worktrees
|
|
114
|
-
# puts repo.worktrees.to_s
|
|
115
|
-
#
|
|
116
|
-
# @return [String] a newline-separated listing of worktree descriptors
|
|
117
|
-
#
|
|
118
|
-
def to_s
|
|
119
|
-
out = +''
|
|
120
|
-
@worktrees.each_value do |b|
|
|
121
|
-
out << b.to_s << "\n"
|
|
122
|
-
end
|
|
123
|
-
out
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
# Removes stale administrative files for worktrees that no longer exist
|
|
127
|
-
#
|
|
128
|
-
# Runs `git worktree prune` to clean up any lingering worktree metadata
|
|
129
|
-
# for linked worktrees whose directories have been deleted.
|
|
130
|
-
#
|
|
131
|
-
# @example Prune stale worktree metadata
|
|
132
|
-
# repo.worktrees.prune
|
|
133
|
-
#
|
|
134
|
-
# @return [String] stdout from the git command (typically empty)
|
|
135
|
-
#
|
|
136
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
137
|
-
#
|
|
138
|
-
def prune
|
|
139
|
-
worktree_repository.worktree_prune
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
private
|
|
143
|
-
|
|
144
|
-
# @return [Git::Repository] the repository used to enumerate worktrees
|
|
145
|
-
#
|
|
146
|
-
# @api private
|
|
147
|
-
#
|
|
148
|
-
def worktree_repository
|
|
149
|
-
@base
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
end
|