git 5.7.1 → 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 -42
- data/CONTRIBUTING.md +44 -31
- data/README.md +138 -100
- data/UPGRADING.md +864 -259
- 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
|
@@ -17,6 +17,7 @@ require 'git/parsers/grep'
|
|
|
17
17
|
require 'git/parsers/ls_tree'
|
|
18
18
|
require 'git/parsers/tag'
|
|
19
19
|
require 'git/repository/shared_private'
|
|
20
|
+
require 'git/system_call_guard'
|
|
20
21
|
require 'git/escaped_path'
|
|
21
22
|
require 'tempfile'
|
|
22
23
|
require 'zlib'
|
|
@@ -72,20 +73,25 @@ module Git
|
|
|
72
73
|
#
|
|
73
74
|
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
74
75
|
#
|
|
76
|
+
# @raise [Git::Error] if a block is given and the object content cannot be
|
|
77
|
+
# written to a temporary file
|
|
78
|
+
#
|
|
75
79
|
# @see https://git-scm.com/docs/git-cat-file git-cat-file documentation
|
|
76
80
|
#
|
|
77
|
-
def cat_file_contents(object)
|
|
81
|
+
def cat_file_contents(object, &block)
|
|
78
82
|
raise ArgumentError, "Invalid object: '#{object}'" if object&.start_with?('-')
|
|
79
83
|
|
|
80
84
|
return Git::Commands::CatFile::Raw.new(@execution_context).call(object, p: true).stdout unless block_given?
|
|
81
85
|
|
|
82
86
|
# Stream git output directly to a tempfile to avoid buffering large
|
|
83
87
|
# object content in memory when a block is given.
|
|
84
|
-
|
|
85
|
-
file
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
Git::SystemCallGuard.call('Failed to write the object content to a temporary file') do |guard|
|
|
89
|
+
Tempfile.create do |file|
|
|
90
|
+
file.binmode
|
|
91
|
+
Git::Commands::CatFile::Raw.new(@execution_context).call(object, p: true, out: file)
|
|
92
|
+
file.rewind
|
|
93
|
+
guard.unguarded { block.call(file) }
|
|
94
|
+
end
|
|
89
95
|
end
|
|
90
96
|
end
|
|
91
97
|
|
|
@@ -95,52 +101,6 @@ module Git
|
|
|
95
101
|
# @see #cat_file_contents
|
|
96
102
|
alias cat_file cat_file_contents
|
|
97
103
|
|
|
98
|
-
# Returns the raw contents of a git object (deprecated name for {#cat_file_contents})
|
|
99
|
-
#
|
|
100
|
-
# @overload object_contents(object)
|
|
101
|
-
# Returns the object's raw content as a string
|
|
102
|
-
#
|
|
103
|
-
# @example Read a blob's contents
|
|
104
|
-
# repo.object_contents('HEAD:README.md') #=> "# My Project\n"
|
|
105
|
-
#
|
|
106
|
-
# @param object [String] the object name (SHA, ref, `HEAD`, treeish path, etc.)
|
|
107
|
-
#
|
|
108
|
-
# @return [String] the raw content of the object
|
|
109
|
-
#
|
|
110
|
-
# @overload object_contents(object, &block)
|
|
111
|
-
# Streams the object's raw content to a temporary file and yields it
|
|
112
|
-
#
|
|
113
|
-
# @example Read a large blob without buffering it in memory
|
|
114
|
-
# repo.object_contents('HEAD:large_file.bin') { |f| process(f) }
|
|
115
|
-
#
|
|
116
|
-
# @param object [String] the object name (SHA, ref, `HEAD`, treeish path, etc.)
|
|
117
|
-
#
|
|
118
|
-
# @yield [file] the temporary file containing the streamed content,
|
|
119
|
-
# positioned at the start
|
|
120
|
-
#
|
|
121
|
-
# @yieldparam file [File] readable `IO` object positioned at the beginning
|
|
122
|
-
# of the content
|
|
123
|
-
#
|
|
124
|
-
# @yieldreturn [Object] the value to return from this method
|
|
125
|
-
#
|
|
126
|
-
# @return [Object] the value returned by the block
|
|
127
|
-
#
|
|
128
|
-
# @raise [ArgumentError] if `object` starts with a hyphen
|
|
129
|
-
#
|
|
130
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
131
|
-
#
|
|
132
|
-
# @deprecated Use {#cat_file_contents} instead. Will be removed in v6.0.0.
|
|
133
|
-
#
|
|
134
|
-
# @see #cat_file_contents
|
|
135
|
-
#
|
|
136
|
-
def object_contents(object, &)
|
|
137
|
-
Git::Deprecation.warn(
|
|
138
|
-
'Git::Repository#object_contents is deprecated and will be removed in v6.0.0. ' \
|
|
139
|
-
'Use Git::Repository#cat_file_contents instead.'
|
|
140
|
-
)
|
|
141
|
-
cat_file_contents(object, &)
|
|
142
|
-
end
|
|
143
|
-
|
|
144
104
|
# Returns the size of a git object in bytes
|
|
145
105
|
#
|
|
146
106
|
# @example Get the size of a commit object
|
|
@@ -165,31 +125,6 @@ module Git
|
|
|
165
125
|
Git::Commands::CatFile::Raw.new(@execution_context).call(object, s: true).stdout.chomp.to_i
|
|
166
126
|
end
|
|
167
127
|
|
|
168
|
-
# Returns the size of a git object in bytes (deprecated name for {#cat_file_size})
|
|
169
|
-
#
|
|
170
|
-
# @example Get the size of a commit object
|
|
171
|
-
# repo.object_size('HEAD') #=> 265
|
|
172
|
-
#
|
|
173
|
-
# @param object [String] the object name (SHA, ref, `HEAD`, treeish path, etc.)
|
|
174
|
-
#
|
|
175
|
-
# @return [Integer] the size of the object in bytes
|
|
176
|
-
#
|
|
177
|
-
# @raise [ArgumentError] if `object` starts with a hyphen
|
|
178
|
-
#
|
|
179
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
180
|
-
#
|
|
181
|
-
# @deprecated Use {#cat_file_size} instead. Will be removed in v6.0.0.
|
|
182
|
-
#
|
|
183
|
-
# @see #cat_file_size
|
|
184
|
-
#
|
|
185
|
-
def object_size(object)
|
|
186
|
-
Git::Deprecation.warn(
|
|
187
|
-
'Git::Repository#object_size is deprecated and will be removed in v6.0.0. ' \
|
|
188
|
-
'Use Git::Repository#cat_file_size instead.'
|
|
189
|
-
)
|
|
190
|
-
cat_file_size(object)
|
|
191
|
-
end
|
|
192
|
-
|
|
193
128
|
# Returns the type of a git object
|
|
194
129
|
#
|
|
195
130
|
# @example Get the type of a commit reference
|
|
@@ -215,31 +150,6 @@ module Git
|
|
|
215
150
|
Git::Commands::CatFile::Raw.new(@execution_context).call(object, t: true).stdout.chomp
|
|
216
151
|
end
|
|
217
152
|
|
|
218
|
-
# Returns the type of a git object (deprecated name for {#cat_file_type})
|
|
219
|
-
#
|
|
220
|
-
# @example Get the type of HEAD
|
|
221
|
-
# repo.object_type('HEAD') #=> "commit"
|
|
222
|
-
#
|
|
223
|
-
# @param object [String] the object name (SHA, ref, `HEAD`, treeish path, etc.)
|
|
224
|
-
#
|
|
225
|
-
# @return [String] the object type: `"blob"`, `"tree"`, `"commit"`, or `"tag"`
|
|
226
|
-
#
|
|
227
|
-
# @raise [ArgumentError] if `object` starts with a hyphen
|
|
228
|
-
#
|
|
229
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
230
|
-
#
|
|
231
|
-
# @deprecated Use {#cat_file_type} instead. Will be removed in v6.0.0.
|
|
232
|
-
#
|
|
233
|
-
# @see #cat_file_type
|
|
234
|
-
#
|
|
235
|
-
def object_type(object)
|
|
236
|
-
Git::Deprecation.warn(
|
|
237
|
-
'Git::Repository#object_type is deprecated and will be removed in v6.0.0. ' \
|
|
238
|
-
'Use Git::Repository#cat_file_type instead.'
|
|
239
|
-
)
|
|
240
|
-
cat_file_type(object)
|
|
241
|
-
end
|
|
242
|
-
|
|
243
153
|
# Returns parsed commit data for the given git object
|
|
244
154
|
#
|
|
245
155
|
# @example Get commit data for HEAD
|
|
@@ -276,29 +186,6 @@ module Git
|
|
|
276
186
|
Git::Parsers::CatFile.parse_commit(result.stdout.split("\n"), object)
|
|
277
187
|
end
|
|
278
188
|
|
|
279
|
-
# Returns parsed commit data for a git object (deprecated name for {#cat_file_commit})
|
|
280
|
-
#
|
|
281
|
-
# @example Get commit data for HEAD
|
|
282
|
-
# repo.commit_data('HEAD')
|
|
283
|
-
#
|
|
284
|
-
# @param object [String] the commit name (SHA, ref, `HEAD`, etc.)
|
|
285
|
-
#
|
|
286
|
-
# @return [Hash] the parsed commit data; see {#cat_file_commit} for the keys
|
|
287
|
-
#
|
|
288
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
289
|
-
#
|
|
290
|
-
# @deprecated Use {#cat_file_commit} instead. Will be removed in v6.0.0.
|
|
291
|
-
#
|
|
292
|
-
# @see #cat_file_commit
|
|
293
|
-
#
|
|
294
|
-
def commit_data(object)
|
|
295
|
-
Git::Deprecation.warn(
|
|
296
|
-
'Git::Repository#commit_data is deprecated and will be removed in v6.0.0. ' \
|
|
297
|
-
'Use Git::Repository#cat_file_commit instead.'
|
|
298
|
-
)
|
|
299
|
-
cat_file_commit(object)
|
|
300
|
-
end
|
|
301
|
-
|
|
302
189
|
# Returns parsed tag data for the given annotated tag object
|
|
303
190
|
#
|
|
304
191
|
# Does not work with lightweight tags. To list all annotated tags in a
|
|
@@ -348,31 +235,6 @@ module Git
|
|
|
348
235
|
Git::Parsers::CatFile.parse_tag(tdata, object)
|
|
349
236
|
end
|
|
350
237
|
|
|
351
|
-
# Returns parsed tag data for an annotated tag (deprecated name for {#cat_file_tag})
|
|
352
|
-
#
|
|
353
|
-
# @example Get tag data for an annotated tag
|
|
354
|
-
# repo.tag_data('v1.0.0')
|
|
355
|
-
#
|
|
356
|
-
# @param object [String] the tag name or SHA of the annotated tag object
|
|
357
|
-
#
|
|
358
|
-
# @return [Hash] the parsed tag data; see {#cat_file_tag} for the keys
|
|
359
|
-
#
|
|
360
|
-
# @raise [ArgumentError] if `object` starts with a hyphen
|
|
361
|
-
#
|
|
362
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
363
|
-
#
|
|
364
|
-
# @deprecated Use {#cat_file_tag} instead. Will be removed in v6.0.0.
|
|
365
|
-
#
|
|
366
|
-
# @see #cat_file_tag
|
|
367
|
-
#
|
|
368
|
-
def tag_data(object)
|
|
369
|
-
Git::Deprecation.warn(
|
|
370
|
-
'Git::Repository#tag_data is deprecated and will be removed in v6.0.0. ' \
|
|
371
|
-
'Use Git::Repository#cat_file_tag instead.'
|
|
372
|
-
)
|
|
373
|
-
cat_file_tag(object)
|
|
374
|
-
end
|
|
375
|
-
|
|
376
238
|
# Resolve a revision specifier to its full object ID
|
|
377
239
|
#
|
|
378
240
|
# Passes the given revision specifier to `git rev-parse` and returns the
|
|
@@ -402,34 +264,14 @@ module Git
|
|
|
402
264
|
Git::Commands::RevParse.new(@execution_context).call(objectish, '--', revs_only: true).stdout
|
|
403
265
|
end
|
|
404
266
|
|
|
405
|
-
# Resolves a revision specifier to its full object ID (deprecated name for {#rev_parse})
|
|
406
|
-
#
|
|
407
|
-
# @example Resolve HEAD to its SHA
|
|
408
|
-
# repo.revparse('HEAD') #=> "9b9b31e704c0b85ffdd8d2af2ded85170a5af87d"
|
|
409
|
-
#
|
|
410
|
-
# @param objectish [String] the revision specifier to resolve (branch name,
|
|
411
|
-
# tag, abbreviated SHA, refspec, etc.)
|
|
412
|
-
#
|
|
413
|
-
# @return [String] the full object ID of the resolved object
|
|
414
|
-
#
|
|
415
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
416
|
-
#
|
|
417
|
-
# @deprecated Use {#rev_parse} instead. Will be removed in v6.0.0.
|
|
418
|
-
#
|
|
419
|
-
# @see #rev_parse
|
|
420
|
-
#
|
|
421
|
-
def revparse(objectish)
|
|
422
|
-
Git::Deprecation.warn(
|
|
423
|
-
'Git::Repository#revparse is deprecated and will be removed in v6.0.0. ' \
|
|
424
|
-
'Use Git::Repository#rev_parse instead.'
|
|
425
|
-
)
|
|
426
|
-
rev_parse(objectish)
|
|
427
|
-
end
|
|
428
|
-
|
|
429
267
|
# Returns the SHA of a named tag
|
|
430
268
|
#
|
|
431
269
|
# Returns an empty string when the tag does not exist.
|
|
432
270
|
#
|
|
271
|
+
# Reads the loose ref file under `refs/tags` directly when it exists and
|
|
272
|
+
# falls back to `git show-ref` otherwise, including when the file cannot
|
|
273
|
+
# be read.
|
|
274
|
+
#
|
|
433
275
|
# @example Get the SHA of an existing tag
|
|
434
276
|
# repo.tag_sha('v1.0')
|
|
435
277
|
# #=> "abc1234567890abcdef1234567890abcdef123456"
|
|
@@ -447,9 +289,7 @@ module Git
|
|
|
447
289
|
def tag_sha(tag_name)
|
|
448
290
|
tags_dir = File.expand_path(File.join(@execution_context.git_dir, 'refs', 'tags'))
|
|
449
291
|
head = File.expand_path(File.join(tags_dir, tag_name))
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
Private.show_ref_tag_sha(@execution_context, tag_name)
|
|
292
|
+
Private.loose_tag_sha(tags_dir, head) || Private.show_ref_tag_sha(@execution_context, tag_name)
|
|
453
293
|
end
|
|
454
294
|
|
|
455
295
|
# Returns all recursive entries for a given tree object
|
|
@@ -527,31 +367,6 @@ module Git
|
|
|
527
367
|
Git::Commands::NameRev.new(@execution_context).call(commit_ish).stdout.split[1]
|
|
528
368
|
end
|
|
529
369
|
|
|
530
|
-
# Finds a symbolic name for a commit (deprecated name for {#name_rev})
|
|
531
|
-
#
|
|
532
|
-
# @example Name the current commit
|
|
533
|
-
# repo.namerev('HEAD') #=> "main"
|
|
534
|
-
#
|
|
535
|
-
# @param commit_ish [String] the commit to name (SHA, ref, `HEAD`, etc.)
|
|
536
|
-
#
|
|
537
|
-
# @return [String, nil] the symbolic name, or `nil` if git finds none
|
|
538
|
-
#
|
|
539
|
-
# @raise [ArgumentError] if commit_ish starts with a hyphen
|
|
540
|
-
#
|
|
541
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
542
|
-
#
|
|
543
|
-
# @deprecated Use {#name_rev} instead. Will be removed in v6.0.0.
|
|
544
|
-
#
|
|
545
|
-
# @see #name_rev
|
|
546
|
-
#
|
|
547
|
-
def namerev(commit_ish)
|
|
548
|
-
Git::Deprecation.warn(
|
|
549
|
-
'Git::Repository#namerev is deprecated and will be removed in v6.0.0. ' \
|
|
550
|
-
'Use Git::Repository#name_rev instead.'
|
|
551
|
-
)
|
|
552
|
-
name_rev(commit_ish)
|
|
553
|
-
end
|
|
554
|
-
|
|
555
370
|
# Option keys accepted by {#ls_tree}
|
|
556
371
|
LS_TREE_ALLOWED_OPTS = %i[recursive path].freeze
|
|
557
372
|
private_constant :LS_TREE_ALLOWED_OPTS
|
|
@@ -771,20 +586,21 @@ module Git
|
|
|
771
586
|
#
|
|
772
587
|
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
773
588
|
#
|
|
589
|
+
# @raise [Git::Error] if the archive file cannot be written or gzip
|
|
590
|
+
# compression fails
|
|
591
|
+
#
|
|
592
|
+
# @note A failure leaves nothing behind: the staging and temporary files
|
|
593
|
+
# are removed and an existing `file` is not replaced.
|
|
594
|
+
#
|
|
774
595
|
# @see https://git-scm.com/docs/git-archive git-archive documentation
|
|
775
596
|
#
|
|
776
597
|
def archive(treeish, file = nil, opts = {})
|
|
777
598
|
SharedPrivate.assert_valid_opts!(ARCHIVE_ALLOWED_OPTS, **opts)
|
|
778
599
|
raise ArgumentError, "#{file.inspect} is a directory" if file && File.directory?(file)
|
|
779
600
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
Private.atomic_replace(tmp, file)
|
|
784
|
-
file
|
|
785
|
-
rescue StandardError
|
|
786
|
-
FileUtils.rm_f(tmp) if tmp
|
|
787
|
-
raise
|
|
601
|
+
Git::SystemCallGuard.call('Failed to write the archive') do
|
|
602
|
+
Private.write_archive(@execution_context, treeish, file, opts)
|
|
603
|
+
end
|
|
788
604
|
end
|
|
789
605
|
|
|
790
606
|
# Returns a blob object for the given object reference
|
|
@@ -851,50 +667,6 @@ module Git
|
|
|
851
667
|
Git::Object.new(self, objectish, 'tree')
|
|
852
668
|
end
|
|
853
669
|
|
|
854
|
-
# Returns a tag object for the given tag name
|
|
855
|
-
#
|
|
856
|
-
# Returns a {Git::Object::Tag} for `tag_name`. The returned object is
|
|
857
|
-
# either an annotated or a lightweight tag depending on the underlying
|
|
858
|
-
# ref type.
|
|
859
|
-
#
|
|
860
|
-
# @example Get a tag object
|
|
861
|
-
# repo.tag('v1.0')
|
|
862
|
-
# #=> #<Git::Object::Tag name="v1.0" ...>
|
|
863
|
-
#
|
|
864
|
-
# @param tag_name [String] the name of the tag
|
|
865
|
-
#
|
|
866
|
-
# @return [Git::Object::Tag] the tag object
|
|
867
|
-
#
|
|
868
|
-
# @raise [Git::UnexpectedResultError] if `tag_name` does not name an
|
|
869
|
-
# existing tag
|
|
870
|
-
#
|
|
871
|
-
# @raise [Git::FailedError] if the underlying `git show-ref` invocation
|
|
872
|
-
# exits with an unexpected status (i.e., outside the allowed 0..1 range)
|
|
873
|
-
#
|
|
874
|
-
# @deprecated Use `tag_list(name).first` instead
|
|
875
|
-
#
|
|
876
|
-
# {#tag_list} returns immutable {Git::TagInfo} value objects rather
|
|
877
|
-
# than {Git::Object::Tag}. `tag_list(name).first` is `nil` when the tag
|
|
878
|
-
# does not exist, where this method raises
|
|
879
|
-
# {Git::UnexpectedResultError}. Call the corresponding
|
|
880
|
-
# {Git::Repository} method (e.g. {#archive}, {#log}, {#diff},
|
|
881
|
-
# {#cat_file_contents}) with `info.oid || info.target_oid` for
|
|
882
|
-
# operations on a tag; that is the object this method's return value
|
|
883
|
-
# pins at construction, so a later move of the tag does not redirect
|
|
884
|
-
# it, whereas the tag name would. The
|
|
885
|
-
# {Git::Object::Tag} constructor is deprecated too; this method
|
|
886
|
-
# silences it so one call emits one warning.
|
|
887
|
-
#
|
|
888
|
-
# @see #tag_list
|
|
889
|
-
#
|
|
890
|
-
def tag(tag_name)
|
|
891
|
-
Git::Deprecation.warn(
|
|
892
|
-
'Git::Repository#tag is deprecated and will be removed in v6.0.0. ' \
|
|
893
|
-
'Use Git::Repository#tag_list(name).first instead.'
|
|
894
|
-
)
|
|
895
|
-
Git::Deprecation.silence { Git::Object::Tag.new(self, tag_name) }
|
|
896
|
-
end
|
|
897
|
-
|
|
898
670
|
# Returns the appropriate git object for the given object reference
|
|
899
671
|
#
|
|
900
672
|
# Runs `git cat-file -t` to determine the object type, then constructs
|
|
@@ -964,46 +736,7 @@ module Git
|
|
|
964
736
|
Git::Parsers::Tag.parse_list(result.stdout)
|
|
965
737
|
end
|
|
966
738
|
|
|
967
|
-
#
|
|
968
|
-
#
|
|
969
|
-
# Runs `git tag --list` with a machine-readable format, parses the output,
|
|
970
|
-
# and returns a {Git::Object::Tag} for each tag name.
|
|
971
|
-
#
|
|
972
|
-
# @example List the names of all tags
|
|
973
|
-
# repo.tags.map(&:name) #=> ["v1.0.0", "v2.0.0"]
|
|
974
|
-
#
|
|
975
|
-
# @example No tags exist
|
|
976
|
-
# repo.tags #=> []
|
|
977
|
-
#
|
|
978
|
-
# @return [Array<Git::Object::Tag>] one tag object per tag in the
|
|
979
|
-
# repository; empty when there are none
|
|
980
|
-
#
|
|
981
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
982
|
-
#
|
|
983
|
-
# @deprecated Use {#tag_list} instead
|
|
984
|
-
#
|
|
985
|
-
# {#tag_list} returns `Array<Git::TagInfo>` (immutable value objects)
|
|
986
|
-
# rather than `Array<Git::Object::Tag>`. Look a tag up by name with
|
|
987
|
-
# `tag_list(name).first`, and call the corresponding {Git::Repository}
|
|
988
|
-
# method (e.g. {#archive}, {#log}, {#diff}, {#cat_file_contents}) with
|
|
989
|
-
# `info.oid || info.target_oid` for operations on a tag; that is the
|
|
990
|
-
# object each returned {Git::Object::Tag} pins at construction, so a
|
|
991
|
-
# later move of the tag does not redirect it, whereas the tag name
|
|
992
|
-
# would. The {Git::Object::Tag}
|
|
993
|
-
# constructor is deprecated too; this method silences it so one call
|
|
994
|
-
# emits one warning.
|
|
995
|
-
#
|
|
996
|
-
# @see #tag_list
|
|
997
|
-
#
|
|
998
|
-
def tags
|
|
999
|
-
Git::Deprecation.warn(
|
|
1000
|
-
'Git::Repository#tags is deprecated and will be removed in v6.0.0. ' \
|
|
1001
|
-
'Use Git::Repository#tag_list instead.'
|
|
1002
|
-
)
|
|
1003
|
-
Git::Deprecation.silence { tag_list.map { |info| Git::Object::Tag.new(self, info.name) } }
|
|
1004
|
-
end
|
|
1005
|
-
|
|
1006
|
-
# Option keys accepted by {#tag_create} and {#tag_add}
|
|
739
|
+
# Option keys accepted by {#tag_create}
|
|
1007
740
|
TAG_CREATE_ALLOWED_OPTS = %i[
|
|
1008
741
|
annotate a sign s no_sign local_user u force f message m file F
|
|
1009
742
|
edit e no_edit trailer cleanup create_reflog
|
|
@@ -1078,9 +811,7 @@ module Git
|
|
|
1078
811
|
# @option options [Boolean, nil] :create_reflog (nil) create a reflog for
|
|
1079
812
|
# the tag
|
|
1080
813
|
#
|
|
1081
|
-
# @return [Git::TagInfo
|
|
1082
|
-
# is missing from the tag list after the create (for example, another
|
|
1083
|
-
# process deleted it before the lookup)
|
|
814
|
+
# @return [Git::TagInfo] the newly created tag
|
|
1084
815
|
#
|
|
1085
816
|
# @overload tag_create(name, target, options = {})
|
|
1086
817
|
#
|
|
@@ -1097,169 +828,31 @@ module Git
|
|
|
1097
828
|
# @param options [Hash] options for creating the tag (same keys as the
|
|
1098
829
|
# first overload)
|
|
1099
830
|
#
|
|
1100
|
-
# @return [Git::TagInfo
|
|
1101
|
-
# is missing from the tag list after the create (for example, another
|
|
1102
|
-
# process deleted it before the lookup)
|
|
831
|
+
# @return [Git::TagInfo] the newly created tag
|
|
1103
832
|
#
|
|
1104
833
|
# @raise [ArgumentError] if unsupported options are provided, including the
|
|
1105
|
-
# `:d` and `:delete` keys
|
|
1106
|
-
# delete a tag
|
|
834
|
+
# `:d` and `:delete` keys; use {#tag_delete} to delete a tag
|
|
1107
835
|
#
|
|
1108
836
|
# @raise [ArgumentError] if an annotated or signed tag is requested without
|
|
1109
837
|
# a message
|
|
1110
838
|
#
|
|
1111
839
|
# @raise [ArgumentError] if more than one positional argument follows the
|
|
1112
|
-
# name (before any options hash)
|
|
1113
|
-
# arguments and tagged the first
|
|
840
|
+
# name (before any options hash)
|
|
1114
841
|
#
|
|
1115
842
|
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
1116
843
|
#
|
|
1117
|
-
# @
|
|
1118
|
-
#
|
|
1119
|
-
#
|
|
1120
|
-
# the result for `nil` today can be left as is; the check becomes
|
|
1121
|
-
# unreachable in v6.0.0.
|
|
844
|
+
# @raise [Git::UnexpectedResultError] if the tag listing cannot be parsed
|
|
845
|
+
# or does not contain the new tag (for example, it was deleted by another
|
|
846
|
+
# process before the lookup)
|
|
1122
847
|
#
|
|
1123
848
|
# @see https://git-scm.com/docs/git-tag git-tag
|
|
1124
849
|
#
|
|
1125
850
|
def tag_create(name, *args)
|
|
1126
|
-
target, options = Private.tag_target_and_options(args, strict: true)
|
|
1127
|
-
SharedPrivate.assert_valid_opts!(TAG_CREATE_ALLOWED_OPTS, **options)
|
|
1128
|
-
Private.create_tag(@execution_context, name, target, options)
|
|
1129
|
-
tag_list(name).first
|
|
1130
|
-
end
|
|
1131
|
-
|
|
1132
|
-
# Create a new tag
|
|
1133
|
-
#
|
|
1134
|
-
# @overload tag_add(name, options = {})
|
|
1135
|
-
#
|
|
1136
|
-
# @example Create a lightweight tag on HEAD
|
|
1137
|
-
# repo.tag_add('v1.0.0')
|
|
1138
|
-
#
|
|
1139
|
-
# @example Create an annotated tag on HEAD
|
|
1140
|
-
# repo.tag_add('v1.0.0', annotate: true, message: 'Release 1.0.0')
|
|
1141
|
-
#
|
|
1142
|
-
# @example Replace an existing tag on HEAD
|
|
1143
|
-
# repo.tag_add('v1.0.0', force: true)
|
|
1144
|
-
#
|
|
1145
|
-
# @param name [String] the name of the tag to create
|
|
1146
|
-
#
|
|
1147
|
-
# @param options [Hash] options for creating the tag (same keys as
|
|
1148
|
-
# {#tag_create})
|
|
1149
|
-
#
|
|
1150
|
-
# @return [Git::Object::Tag] the newly created tag
|
|
1151
|
-
#
|
|
1152
|
-
# @overload tag_add(name, target, options = {})
|
|
1153
|
-
#
|
|
1154
|
-
# @example Create a lightweight tag on a specific commit
|
|
1155
|
-
# repo.tag_add('v1.0.0', 'abc123')
|
|
1156
|
-
#
|
|
1157
|
-
# @example Create an annotated tag on a specific commit
|
|
1158
|
-
# repo.tag_add('v1.0.0', 'abc123', annotate: true, message: 'Release 1.0.0')
|
|
1159
|
-
#
|
|
1160
|
-
# @param name [String] the name of the tag to create
|
|
1161
|
-
#
|
|
1162
|
-
# @param target [String] the object to tag (commit SHA, branch name, etc.)
|
|
1163
|
-
#
|
|
1164
|
-
# @param options [Hash] options for creating the tag (same keys as
|
|
1165
|
-
# {#tag_create})
|
|
1166
|
-
#
|
|
1167
|
-
# @return [Git::Object::Tag] the newly created tag
|
|
1168
|
-
#
|
|
1169
|
-
# @overload tag_add(name, delete_options)
|
|
1170
|
-
#
|
|
1171
|
-
# @deprecated Use {#tag_delete} instead.
|
|
1172
|
-
#
|
|
1173
|
-
# @example Delete a tag (deprecated)
|
|
1174
|
-
# repo.tag_add('v1.0.0', d: true)
|
|
1175
|
-
#
|
|
1176
|
-
# @param name [String] the name of the tag to delete
|
|
1177
|
-
#
|
|
1178
|
-
# @param delete_options [Hash{Symbol => Boolean}] deletion options;
|
|
1179
|
-
# only `:d` or `:delete` (set to `true`) is accepted — no other keys
|
|
1180
|
-
# and no `target` argument may be combined with this form
|
|
1181
|
-
#
|
|
1182
|
-
# @return [String] git's stdout from the delete
|
|
1183
|
-
#
|
|
1184
|
-
# @raise [ArgumentError] if a target is also provided
|
|
1185
|
-
#
|
|
1186
|
-
# @raise [ArgumentError] if options other than `:d`/`:delete` are also
|
|
1187
|
-
# provided
|
|
1188
|
-
#
|
|
1189
|
-
# @raise [ArgumentError] if unsupported options are provided
|
|
1190
|
-
#
|
|
1191
|
-
# @raise [ArgumentError] if an annotated or signed tag is requested without
|
|
1192
|
-
# a message
|
|
1193
|
-
#
|
|
1194
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
1195
|
-
#
|
|
1196
|
-
# @deprecated Use {#tag_create} instead
|
|
1197
|
-
#
|
|
1198
|
-
# {#tag_create} accepts the same `name`, `target`, and options and
|
|
1199
|
-
# returns a {Git::TagInfo} (an immutable value object) rather than a
|
|
1200
|
-
# {Git::Object::Tag}. It does not accept the `:d`/`:delete` form; use
|
|
1201
|
-
# {#tag_delete} for that. The {Git::Object::Tag} constructor is
|
|
1202
|
-
# deprecated too; this method silences it so one call emits one
|
|
1203
|
-
# warning, except that the `:d`/`:delete` form emits a second warning
|
|
1204
|
-
# of its own.
|
|
1205
|
-
#
|
|
1206
|
-
# @see #tag_create
|
|
1207
|
-
#
|
|
1208
|
-
def tag_add(name, *args)
|
|
1209
|
-
Git::Deprecation.warn(
|
|
1210
|
-
'Git::Repository#tag_add is deprecated and will be removed in v6.0.0. ' \
|
|
1211
|
-
'Use Git::Repository#tag_create instead.'
|
|
1212
|
-
)
|
|
1213
851
|
target, options = Private.tag_target_and_options(args)
|
|
1214
|
-
|
|
1215
|
-
return Private.tag_add_delete_deprecated(self, name, target, options) if options[:d] || options[:delete]
|
|
1216
|
-
|
|
1217
|
-
options = options.except(:d, :delete)
|
|
1218
852
|
SharedPrivate.assert_valid_opts!(TAG_CREATE_ALLOWED_OPTS, **options)
|
|
1219
853
|
Private.create_tag(@execution_context, name, target, options)
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
# @overload add_tag(name, options = {})
|
|
1224
|
-
#
|
|
1225
|
-
# @param name [String] the name of the tag to create
|
|
1226
|
-
#
|
|
1227
|
-
# @param options [Hash] options for creating the tag
|
|
1228
|
-
#
|
|
1229
|
-
# @return [Git::Object::Tag] the newly created tag
|
|
1230
|
-
#
|
|
1231
|
-
# @overload add_tag(name, target, options = {})
|
|
1232
|
-
#
|
|
1233
|
-
# @param name [String] the name of the tag to create
|
|
1234
|
-
#
|
|
1235
|
-
# @param target [String] the object to tag (commit SHA, branch name, etc.)
|
|
1236
|
-
#
|
|
1237
|
-
# @param options [Hash] options for creating the tag
|
|
1238
|
-
#
|
|
1239
|
-
# @return [Git::Object::Tag] the newly created tag
|
|
1240
|
-
#
|
|
1241
|
-
# @raise [ArgumentError] if unsupported options are provided
|
|
1242
|
-
#
|
|
1243
|
-
# @raise [ArgumentError] if an annotated or signed tag is requested without
|
|
1244
|
-
# a message
|
|
1245
|
-
#
|
|
1246
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
1247
|
-
#
|
|
1248
|
-
# @deprecated Use {#tag_create} instead
|
|
1249
|
-
#
|
|
1250
|
-
# The warning names {#tag_add}, the replacement this method shipped
|
|
1251
|
-
# with, and {#tag_add} is deprecated as well, so a creation call emits
|
|
1252
|
-
# two warnings: one for this method and one for {#tag_add}. The delete
|
|
1253
|
-
# form `add_tag(name, d: true)` emits a third, for the deprecated `:d`
|
|
1254
|
-
# and `:delete` options on {#tag_add}; use {#tag_delete} for that. Go
|
|
1255
|
-
# straight to {#tag_create} for creation.
|
|
1256
|
-
#
|
|
1257
|
-
def add_tag(name, *)
|
|
1258
|
-
Git::Deprecation.warn(
|
|
1259
|
-
'Git::Repository#add_tag is deprecated and will be removed in v6.0.0. ' \
|
|
1260
|
-
'Use Git::Repository#tag_add instead.'
|
|
1261
|
-
)
|
|
1262
|
-
tag_add(name, *)
|
|
854
|
+
tag_list(name).first ||
|
|
855
|
+
raise(Git::UnexpectedResultError, "tag was created but not found in the tag list: #{name}")
|
|
1263
856
|
end
|
|
1264
857
|
|
|
1265
858
|
# Delete a tag
|
|
@@ -1280,54 +873,34 @@ module Git
|
|
|
1280
873
|
result.stdout
|
|
1281
874
|
end
|
|
1282
875
|
|
|
1283
|
-
# @param name [String] the name of the tag to delete
|
|
1284
|
-
#
|
|
1285
|
-
# @return [String] git's stdout from the delete
|
|
1286
|
-
#
|
|
1287
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
1288
|
-
#
|
|
1289
|
-
# @deprecated Use {#tag_delete} instead
|
|
1290
|
-
#
|
|
1291
|
-
def delete_tag(name)
|
|
1292
|
-
Git::Deprecation.warn(
|
|
1293
|
-
'Git::Repository#delete_tag is deprecated and will be removed in v6.0.0. ' \
|
|
1294
|
-
'Use Git::Repository#tag_delete instead.'
|
|
1295
|
-
)
|
|
1296
|
-
tag_delete(name)
|
|
1297
|
-
end
|
|
1298
|
-
|
|
1299
876
|
# Private helpers
|
|
1300
877
|
#
|
|
1301
878
|
# @api private
|
|
1302
879
|
#
|
|
1303
|
-
module Private
|
|
880
|
+
module Private # rubocop:disable Metrics/ModuleLength
|
|
1304
881
|
module_function
|
|
1305
882
|
|
|
1306
|
-
# Splits the variadic `*args` of {ObjectOperations#tag_create}
|
|
1307
|
-
#
|
|
883
|
+
# Splits the variadic `*args` of {ObjectOperations#tag_create} into the
|
|
884
|
+
# target and the options hash
|
|
1308
885
|
#
|
|
1309
|
-
#
|
|
886
|
+
# `tag_create` accepts `(name, opts = {})` and `(name, target, opts = {})`,
|
|
1310
887
|
# so a trailing `Hash` is the options and anything before it is the
|
|
1311
888
|
# target.
|
|
1312
889
|
#
|
|
1313
890
|
# @param args [Array] the arguments after the tag name
|
|
1314
891
|
#
|
|
1315
|
-
# @param strict [Boolean] when `true`, raise instead of silently ignoring
|
|
1316
|
-
# a second positional argument before the options; `tag_create` is
|
|
1317
|
-
# strict, while the deprecated `tag_add` keeps its lenient behavior
|
|
1318
|
-
#
|
|
1319
892
|
# @return [Array((String, nil), Hash)] the two-element tuple
|
|
1320
893
|
# `[target, options]`; `target` is `nil` when only options were given
|
|
1321
894
|
#
|
|
1322
|
-
# @raise [ArgumentError] if
|
|
1323
|
-
#
|
|
895
|
+
# @raise [ArgumentError] if more than one positional argument precedes
|
|
896
|
+
# the options hash
|
|
1324
897
|
#
|
|
1325
898
|
# @api private
|
|
1326
899
|
#
|
|
1327
|
-
def tag_target_and_options(args
|
|
900
|
+
def tag_target_and_options(args)
|
|
1328
901
|
args = args.dup
|
|
1329
902
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
1330
|
-
if
|
|
903
|
+
if args.size > 1
|
|
1331
904
|
raise ArgumentError,
|
|
1332
905
|
"Expected at most one target before the options, got #{args.size}: #{args.inspect}"
|
|
1333
906
|
end
|
|
@@ -1418,39 +991,25 @@ module Git
|
|
|
1418
991
|
raise ArgumentError, 'Cannot create an annotated or signed tag without a message.'
|
|
1419
992
|
end
|
|
1420
993
|
|
|
1421
|
-
#
|
|
1422
|
-
#
|
|
1423
|
-
# Issues a deprecation warning and delegates to tag_delete. Raises
|
|
1424
|
-
# ArgumentError if a target or incompatible options are also supplied.
|
|
1425
|
-
#
|
|
1426
|
-
# @param facade [ObjectOperations] the calling facade instance
|
|
994
|
+
# Read the SHA from a loose tag ref file without forking git
|
|
1427
995
|
#
|
|
1428
|
-
#
|
|
996
|
+
# Returns `nil` when the ref is not a loose file under `tags_dir` or when
|
|
997
|
+
# the file cannot be read, so the caller falls back to `git show-ref`.
|
|
1429
998
|
#
|
|
1430
|
-
# @param
|
|
999
|
+
# @param tags_dir [String] absolute path of the `refs/tags` directory
|
|
1431
1000
|
#
|
|
1432
|
-
# @param
|
|
1001
|
+
# @param head [String] absolute path of the candidate loose ref file
|
|
1433
1002
|
#
|
|
1434
|
-
# @
|
|
1435
|
-
# `tag_add` form
|
|
1436
|
-
#
|
|
1437
|
-
# @option opts [Boolean] :delete (true) alias for `:d`
|
|
1438
|
-
#
|
|
1439
|
-
# @return [String] stdout from tag_delete
|
|
1003
|
+
# @return [String, nil] the SHA in the file, or `nil` to fall back to git
|
|
1440
1004
|
#
|
|
1441
1005
|
# @api private
|
|
1442
1006
|
#
|
|
1443
|
-
def
|
|
1444
|
-
|
|
1445
|
-
'Passing :d or :delete to tag_add is deprecated and will be removed in v6.0.0. ' \
|
|
1446
|
-
'Use tag_delete instead.'
|
|
1447
|
-
)
|
|
1448
|
-
raise ArgumentError, 'Cannot pass a target when using the :d/:delete option.' if target
|
|
1007
|
+
def loose_tag_sha(tags_dir, head)
|
|
1008
|
+
return nil unless head.start_with?("#{tags_dir}#{File::SEPARATOR}") && File.file?(head)
|
|
1449
1009
|
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
facade.tag_delete(name)
|
|
1010
|
+
File.read(head).chomp
|
|
1011
|
+
rescue SystemCallError
|
|
1012
|
+
nil
|
|
1454
1013
|
end
|
|
1455
1014
|
|
|
1456
1015
|
# Returns the direct SHA for a tag reference
|
|
@@ -1511,6 +1070,45 @@ module Git
|
|
|
1511
1070
|
File.dirname(File.expand_path(file))
|
|
1512
1071
|
end
|
|
1513
1072
|
|
|
1073
|
+
# Write a git archive, staging it in a temporary file first
|
|
1074
|
+
#
|
|
1075
|
+
# @param execution_context [Git::ExecutionContext] the execution context
|
|
1076
|
+
#
|
|
1077
|
+
# @param treeish [String] tree-ish to archive
|
|
1078
|
+
#
|
|
1079
|
+
# @param file [String, nil] destination path, or `nil` to keep the
|
|
1080
|
+
# temporary file
|
|
1081
|
+
#
|
|
1082
|
+
# @param opts [Hash] caller-supplied options (read-only)
|
|
1083
|
+
#
|
|
1084
|
+
# @option opts [String] :format ('zip') archive format (`'tar'`, `'zip'`,
|
|
1085
|
+
# or `'tgz'`)
|
|
1086
|
+
#
|
|
1087
|
+
# @option opts [Boolean, nil] :add_gzip (nil) apply gzip post-processing
|
|
1088
|
+
# to the generated archive
|
|
1089
|
+
#
|
|
1090
|
+
# @option opts [String] :prefix (nil) prefix for entries in the archive
|
|
1091
|
+
#
|
|
1092
|
+
# @option opts [String] :path (nil) path within `treeish` to archive
|
|
1093
|
+
#
|
|
1094
|
+
# @option opts [String] :remote (nil) remote repository from which to
|
|
1095
|
+
# retrieve the archive
|
|
1096
|
+
#
|
|
1097
|
+
# @return [String] path to the written archive file
|
|
1098
|
+
#
|
|
1099
|
+
# @api private
|
|
1100
|
+
#
|
|
1101
|
+
def write_archive(execution_context, treeish, file, opts)
|
|
1102
|
+
tmp = write_archive_tmp(execution_context, treeish, opts, dest_dir: staging_dir_for(file))
|
|
1103
|
+
return tmp unless file
|
|
1104
|
+
|
|
1105
|
+
atomic_replace(tmp, file)
|
|
1106
|
+
file
|
|
1107
|
+
rescue StandardError
|
|
1108
|
+
FileUtils.rm_f(tmp) if tmp
|
|
1109
|
+
raise
|
|
1110
|
+
end
|
|
1111
|
+
|
|
1514
1112
|
# Write a git archive to a fresh temporary file and return its path
|
|
1515
1113
|
#
|
|
1516
1114
|
# Always writes to a new temporary file so that on error the caller's
|
|
@@ -1693,17 +1291,42 @@ module Git
|
|
|
1693
1291
|
#
|
|
1694
1292
|
# @return [void]
|
|
1695
1293
|
#
|
|
1294
|
+
# @raise [Git::Error] if zlib fails during compression, with the
|
|
1295
|
+
# `Zlib::Error` as `cause`
|
|
1296
|
+
#
|
|
1696
1297
|
# @api private
|
|
1697
1298
|
#
|
|
1698
1299
|
def apply_gzip(file)
|
|
1699
1300
|
gz_tmp = Tempfile.create('archive_gz', File.dirname(file)).tap(&:close).path
|
|
1700
|
-
|
|
1301
|
+
gzip_file(file, gz_tmp)
|
|
1701
1302
|
FileUtils.rm_f(file)
|
|
1702
1303
|
File.rename(gz_tmp, file)
|
|
1703
1304
|
rescue StandardError
|
|
1704
1305
|
FileUtils.rm_f(gz_tmp) if gz_tmp
|
|
1705
1306
|
raise
|
|
1706
1307
|
end
|
|
1308
|
+
|
|
1309
|
+
# Stream `src` through a {Zlib::GzipWriter} into `dest`
|
|
1310
|
+
#
|
|
1311
|
+
# A `Zlib::Error` is not a `SystemCallError`, so {Git::SystemCallGuard}
|
|
1312
|
+
# does not convert it; this method does, per ADR-0008.
|
|
1313
|
+
#
|
|
1314
|
+
# @param src [String] path to the file to compress
|
|
1315
|
+
#
|
|
1316
|
+
# @param dest [String] path to write the gzip stream to
|
|
1317
|
+
#
|
|
1318
|
+
# @return [void]
|
|
1319
|
+
#
|
|
1320
|
+
# @raise [Git::Error] if zlib fails during compression, with the
|
|
1321
|
+
# `Zlib::Error` as `cause`
|
|
1322
|
+
#
|
|
1323
|
+
# @api private
|
|
1324
|
+
#
|
|
1325
|
+
def gzip_file(src, dest)
|
|
1326
|
+
Zlib::GzipWriter.open(dest) { |gz| File.open(src, 'rb') { |f| IO.copy_stream(f, gz) } }
|
|
1327
|
+
rescue Zlib::Error => e
|
|
1328
|
+
raise Git::Error, "Failed to gzip the archive: #{e.message}"
|
|
1329
|
+
end
|
|
1707
1330
|
end
|
|
1708
1331
|
private_constant :Private
|
|
1709
1332
|
end
|