git 5.6.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +132 -16
- data/CONTRIBUTING.md +44 -31
- data/README.md +135 -67
- data/UPGRADING.md +915 -268
- 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 +2 -2
- data/lib/git/encoding_utils.rb +25 -0
- data/lib/git/errors.rb +23 -5
- 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 -44
- data/lib/git/repository/merging.rb +32 -54
- data/lib/git/repository/object_operations.rb +121 -338
- 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 +31 -118
- 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 +25 -19
- 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
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'git/commands/worktree'
|
|
4
4
|
require 'git/parsers/worktree'
|
|
5
|
-
require 'git/worktree'
|
|
6
5
|
require 'git/worktree_info'
|
|
7
|
-
require 'git/worktrees'
|
|
8
6
|
|
|
9
7
|
module Git
|
|
10
8
|
class Repository
|
|
@@ -46,66 +44,56 @@ module Git
|
|
|
46
44
|
Git::Parsers::Worktree.parse_list(result.stdout)
|
|
47
45
|
end
|
|
48
46
|
|
|
49
|
-
# Returns all worktrees as an array of directory and SHA pairs
|
|
50
|
-
#
|
|
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.
|
|
53
|
-
#
|
|
54
|
-
# @example List all worktrees
|
|
55
|
-
# repo.worktrees_all
|
|
56
|
-
# #=> [["/path/to/main", "4bef5ab..."], ["/tmp/worktree-1", "b8c6320..."]]
|
|
57
|
-
#
|
|
58
|
-
# @return [Array<Array(String, String)>] array of `[directory, sha]` pairs
|
|
59
|
-
#
|
|
60
|
-
# `directory` is the worktree path reported by git (absolute or relative,
|
|
61
|
-
# depending on repository configuration); `sha` is the full SHA of the
|
|
62
|
-
# checked-out HEAD commit
|
|
63
|
-
#
|
|
64
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
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
|
-
#
|
|
74
|
-
# @see https://git-scm.com/docs/git-worktree git-worktree documentation
|
|
75
|
-
#
|
|
76
|
-
def worktrees_all
|
|
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] }
|
|
82
|
-
end
|
|
83
|
-
|
|
84
47
|
# Create a new linked worktree at the given directory
|
|
85
48
|
#
|
|
49
|
+
# Returns the entry that {#worktree_list} reports for the new worktree.
|
|
50
|
+
# Its `path` is the directory as git records it: absolute, with symlinks
|
|
51
|
+
# resolved, so `/tmp/feature` on macOS comes back as
|
|
52
|
+
# `/private/tmp/feature`.
|
|
53
|
+
#
|
|
86
54
|
# @example Create a worktree at a path (auto-creates a branch)
|
|
87
|
-
# repo.worktree_add('/tmp/feature')
|
|
55
|
+
# info = repo.worktree_add('/tmp/feature')
|
|
56
|
+
# info.path #=> "/private/tmp/feature"
|
|
57
|
+
# info.branch #=> "refs/heads/feature"
|
|
88
58
|
#
|
|
89
59
|
# @example Create a worktree and check out an existing commitish
|
|
90
|
-
# repo.worktree_add('/tmp/hotfix', 'main')
|
|
60
|
+
# repo.worktree_add('/tmp/hotfix', 'main').head
|
|
61
|
+
# #=> "4bef5ab0c8e7c19c6be2c0f55ccd45eec1f3d32a"
|
|
91
62
|
#
|
|
92
|
-
# @param dir [String] filesystem path for the new worktree
|
|
63
|
+
# @param dir [String] filesystem path for the new worktree, absolute or
|
|
64
|
+
# relative to the current directory
|
|
93
65
|
#
|
|
94
66
|
# @param commitish [String, nil] branch, tag, or commit to check out
|
|
95
67
|
#
|
|
96
|
-
# When `nil`, git
|
|
68
|
+
# When `nil`, git checks out the branch named after the final path
|
|
69
|
+
# component, creating it when no such branch exists
|
|
97
70
|
#
|
|
98
|
-
# @return [
|
|
71
|
+
# @return [Git::WorktreeInfo] the new worktree's entry from {#worktree_list}
|
|
99
72
|
#
|
|
100
73
|
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
101
74
|
#
|
|
75
|
+
# @raise [Git::UnexpectedResultError] if the worktree listing cannot be
|
|
76
|
+
# parsed or does not contain an entry for the new worktree's directory
|
|
77
|
+
# (for example, it was removed by another process before the lookup)
|
|
78
|
+
#
|
|
79
|
+
# @note When `commitish` is `nil` and git creates the branch, it does so
|
|
80
|
+
# before it creates the worktree, so a failure after that point leaves
|
|
81
|
+
# the new branch in place without a worktree. A branch that already
|
|
82
|
+
# existed is left as it was.
|
|
83
|
+
#
|
|
102
84
|
# @see https://git-scm.com/docs/git-worktree git-worktree documentation
|
|
103
85
|
#
|
|
104
86
|
def worktree_add(dir, commitish = nil)
|
|
105
87
|
args = [dir]
|
|
106
88
|
args << commitish unless commitish.nil?
|
|
107
89
|
|
|
108
|
-
Git::Commands::Worktree::Add.new(@execution_context).call(*args)
|
|
90
|
+
Git::Commands::Worktree::Add.new(@execution_context).call(*args)
|
|
91
|
+
|
|
92
|
+
# git records the path as given with symlinks resolved, so compare by
|
|
93
|
+
# identity rather than by string: this covers symlinks, case-insensitive
|
|
94
|
+
# filesystems, and Unicode normalization on macOS
|
|
95
|
+
worktree_list.find { |worktree| File.identical?(worktree.path, dir) } ||
|
|
96
|
+
raise(Git::UnexpectedResultError, "worktree was created but not found in the worktree list: #{dir}")
|
|
109
97
|
end
|
|
110
98
|
|
|
111
99
|
# Remove a linked worktree
|
|
@@ -235,8 +223,6 @@ module Git
|
|
|
235
223
|
#
|
|
236
224
|
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
237
225
|
#
|
|
238
|
-
# @raise [Git::VersionError] if the installed git is older than 2.29.0
|
|
239
|
-
#
|
|
240
226
|
# @see https://git-scm.com/docs/git-worktree git-worktree documentation
|
|
241
227
|
#
|
|
242
228
|
def worktree_repair(*paths)
|
|
@@ -261,79 +247,6 @@ module Git
|
|
|
261
247
|
def worktree_prune
|
|
262
248
|
Git::Commands::Worktree::Prune.new(@execution_context).call.stdout
|
|
263
249
|
end
|
|
264
|
-
|
|
265
|
-
# Return a {Git::Worktree} object for the given directory and optional commitish
|
|
266
|
-
#
|
|
267
|
-
# This is a factory method — it constructs the domain object but does not
|
|
268
|
-
# immediately execute any git commands.
|
|
269
|
-
#
|
|
270
|
-
# @example Get a worktree object for a new path
|
|
271
|
-
# wt = repo.worktree('/tmp/feature')
|
|
272
|
-
#
|
|
273
|
-
# @example Get a worktree object for a specific branch or commit
|
|
274
|
-
# wt = repo.worktree('/tmp/hotfix', 'main')
|
|
275
|
-
#
|
|
276
|
-
# @param dir [String] filesystem path for the worktree
|
|
277
|
-
#
|
|
278
|
-
# @param commitish [String, nil] branch, tag, or commit to associate with
|
|
279
|
-
# the worktree; `nil` means no commitish is specified
|
|
280
|
-
#
|
|
281
|
-
# @return [Git::Worktree] a worktree domain object for the given path
|
|
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
|
-
#
|
|
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
|
-
)
|
|
299
|
-
Git::Worktree.new(self, dir, commitish)
|
|
300
|
-
end
|
|
301
|
-
|
|
302
|
-
# Return a {Git::Worktrees} collection of all worktrees (main and linked)
|
|
303
|
-
#
|
|
304
|
-
# The collection is populated eagerly when this method is called (git runs
|
|
305
|
-
# at construction time). It is enumerable and supports indexed access by
|
|
306
|
-
# worktree path.
|
|
307
|
-
#
|
|
308
|
-
# @example Iterate over all worktrees
|
|
309
|
-
# repo.worktrees.each { |wt| puts wt.dir }
|
|
310
|
-
#
|
|
311
|
-
# @example Count worktrees
|
|
312
|
-
# repo.worktrees.size
|
|
313
|
-
#
|
|
314
|
-
# @example Access a specific worktree by path
|
|
315
|
-
# repo.worktrees['/tmp/feature']
|
|
316
|
-
#
|
|
317
|
-
# @return [Git::Worktrees] an enumerable collection of all worktrees
|
|
318
|
-
#
|
|
319
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
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
|
-
#
|
|
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
|
-
)
|
|
335
|
-
Git::Worktrees.new(self)
|
|
336
|
-
end
|
|
337
250
|
end
|
|
338
251
|
end
|
|
339
252
|
end
|
data/lib/git/repository.rb
CHANGED
|
@@ -15,11 +15,11 @@ require 'git/repository/maintenance'
|
|
|
15
15
|
require 'git/repository/merging'
|
|
16
16
|
require 'git/repository/object_operations'
|
|
17
17
|
require 'git/repository/remote_operations'
|
|
18
|
-
require 'git/repository/shared_private'
|
|
19
18
|
require 'git/repository/staging'
|
|
20
19
|
require 'git/repository/stashing'
|
|
21
20
|
require 'git/repository/status_operations'
|
|
22
21
|
require 'git/repository/worktree_operations'
|
|
22
|
+
require 'git/system_call_guard'
|
|
23
23
|
|
|
24
24
|
module Git
|
|
25
25
|
# The main public interface for interacting with a Git repository
|
|
@@ -46,7 +46,7 @@ module Git
|
|
|
46
46
|
#
|
|
47
47
|
# @api public
|
|
48
48
|
#
|
|
49
|
-
class Repository
|
|
49
|
+
class Repository
|
|
50
50
|
include Git::Configuring
|
|
51
51
|
include Git::Repository::Branching
|
|
52
52
|
include Git::Repository::ContextHelpers
|
|
@@ -63,39 +63,6 @@ module Git
|
|
|
63
63
|
include Git::Repository::StatusOperations
|
|
64
64
|
include Git::Repository::WorktreeOperations
|
|
65
65
|
|
|
66
|
-
# Allowed keyword options for deprecated config write operations
|
|
67
|
-
#
|
|
68
|
-
# @return [Array<Symbol>] accepted option keys
|
|
69
|
-
#
|
|
70
|
-
CONFIG_SET_ALLOWED_OPTS = %i[file].freeze
|
|
71
|
-
private_constant :CONFIG_SET_ALLOWED_OPTS
|
|
72
|
-
|
|
73
|
-
# Allowed keyword options for deprecated config read operations
|
|
74
|
-
#
|
|
75
|
-
# @return [Array<Symbol>] accepted option keys
|
|
76
|
-
#
|
|
77
|
-
CONFIG_READ_ALLOWED_OPTS = %i[file].freeze
|
|
78
|
-
private_constant :CONFIG_READ_ALLOWED_OPTS
|
|
79
|
-
|
|
80
|
-
# Deprecation warning emitted by {#config}
|
|
81
|
-
#
|
|
82
|
-
# @return [String] the warning message shown to callers
|
|
83
|
-
#
|
|
84
|
-
CONFIG_DEPRECATION_WARNING =
|
|
85
|
-
'Git::Repository#config is deprecated and will be removed in v6.0.0. ' \
|
|
86
|
-
'Use config_get(name), config_set(name, value), or config_list instead.'
|
|
87
|
-
private_constant :CONFIG_DEPRECATION_WARNING
|
|
88
|
-
|
|
89
|
-
# Deprecation warning emitted by {#global_config}
|
|
90
|
-
#
|
|
91
|
-
# @return [String] the warning message shown to callers
|
|
92
|
-
#
|
|
93
|
-
GLOBAL_CONFIG_DEPRECATION_WARNING =
|
|
94
|
-
'Git::Repository#global_config is deprecated and will be removed in v6.0.0. ' \
|
|
95
|
-
'Use config_get(name, global: true), config_set(name, value, global: true), ' \
|
|
96
|
-
'or config_list(global: true) instead.'
|
|
97
|
-
private_constant :GLOBAL_CONFIG_DEPRECATION_WARNING
|
|
98
|
-
|
|
99
66
|
# @return [Git::ExecutionContext::Repository] the execution context used to run
|
|
100
67
|
# git commands for this repository
|
|
101
68
|
#
|
|
@@ -149,24 +116,6 @@ module Git
|
|
|
149
116
|
index_file && Pathname.new(index_file)
|
|
150
117
|
end
|
|
151
118
|
|
|
152
|
-
# Returns `self` after emitting a deprecation warning.
|
|
153
|
-
#
|
|
154
|
-
# Legacy callers that used `git.lib.some_method` can migrate to calling the
|
|
155
|
-
# facade method directly on the repository object. This shim will be removed
|
|
156
|
-
# in v6.0.0.
|
|
157
|
-
#
|
|
158
|
-
# @return [self]
|
|
159
|
-
#
|
|
160
|
-
# @api private
|
|
161
|
-
#
|
|
162
|
-
def lib
|
|
163
|
-
Git::Deprecation.warn(
|
|
164
|
-
'Git::Repository#lib is deprecated and will be removed in v6.0.0. ' \
|
|
165
|
-
'Use the repository object directly.'
|
|
166
|
-
)
|
|
167
|
-
self
|
|
168
|
-
end
|
|
169
|
-
|
|
170
119
|
# @return [String, nil] the git directory path
|
|
171
120
|
#
|
|
172
121
|
# @api private
|
|
@@ -202,111 +151,6 @@ module Git
|
|
|
202
151
|
# @api private
|
|
203
152
|
def binary_path = execution_context.binary_path
|
|
204
153
|
|
|
205
|
-
# Reads or writes a git configuration entry
|
|
206
|
-
#
|
|
207
|
-
# Dispatches to one of three modes depending on the arguments supplied:
|
|
208
|
-
#
|
|
209
|
-
# * **List** — `config()` returns all visible config entries as a `Hash`.
|
|
210
|
-
# * **Get** — `config(name)` returns the value for a single key as a `String`.
|
|
211
|
-
# * **Set** — `config(name, value)` writes a value and returns the raw
|
|
212
|
-
# command result.
|
|
213
|
-
#
|
|
214
|
-
# @example List all config entries
|
|
215
|
-
# repo.config #=> { "user.name" => "Alice", "core.bare" => "false" }
|
|
216
|
-
#
|
|
217
|
-
# @example Read a config value
|
|
218
|
-
# repo.config('user.name') #=> "Alice"
|
|
219
|
-
#
|
|
220
|
-
# @example Set a config value
|
|
221
|
-
# repo.config('user.name', 'Alice')
|
|
222
|
-
#
|
|
223
|
-
# @param name [String, Hash, nil] the dotted config key, or an options hash
|
|
224
|
-
# for list mode when `value` and `options` are omitted
|
|
225
|
-
#
|
|
226
|
-
# @param value [#to_s, Hash, nil] the value to set, or an options hash in the
|
|
227
|
-
# legacy `config(name, options)` call shape
|
|
228
|
-
#
|
|
229
|
-
# @param options [Hash] options forwarded to git config
|
|
230
|
-
#
|
|
231
|
-
# @option options [String, nil] :file (nil) path to a custom config file
|
|
232
|
-
#
|
|
233
|
-
# @return [Hash{String => String}, String, Git::CommandLine::Result] all config
|
|
234
|
-
# entries, a single value, or the command result for set mode
|
|
235
|
-
#
|
|
236
|
-
# @raise [ArgumentError] if unsupported options are provided
|
|
237
|
-
#
|
|
238
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
239
|
-
#
|
|
240
|
-
def config(name = nil, value = nil, options = {})
|
|
241
|
-
Git::Deprecation.warn(CONFIG_DEPRECATION_WARNING)
|
|
242
|
-
name, value, options = deprecated_normalize_config_args(name, value, options)
|
|
243
|
-
|
|
244
|
-
if !name.nil? && !value.nil?
|
|
245
|
-
deprecated_config_set(name, value, **options)
|
|
246
|
-
elsif name
|
|
247
|
-
deprecated_config_get(name, **options)
|
|
248
|
-
else
|
|
249
|
-
deprecated_config_list(**options)
|
|
250
|
-
end
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
# Read or write a global git configuration entry
|
|
254
|
-
#
|
|
255
|
-
# Dispatches to one of three modes depending on the arguments supplied,
|
|
256
|
-
# targeting the git global config scope (`git config --global`):
|
|
257
|
-
#
|
|
258
|
-
# * **List** — `global_config()` returns all global config entries as a `Hash`.
|
|
259
|
-
# * **Get** — `global_config(name)` returns the value for a single key as a `String`.
|
|
260
|
-
# * **Set** — `global_config(name, value)` writes a value and returns the raw
|
|
261
|
-
# command result.
|
|
262
|
-
#
|
|
263
|
-
# @overload global_config
|
|
264
|
-
#
|
|
265
|
-
# @example List all global config entries
|
|
266
|
-
# repo.global_config #=> { "user.name" => "Alice", "core.autocrlf" => "false" }
|
|
267
|
-
#
|
|
268
|
-
# @return [Hash{String => String}] all global config entries, keyed by their
|
|
269
|
-
# full dotted key names (e.g. `"user.name"`)
|
|
270
|
-
#
|
|
271
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
272
|
-
#
|
|
273
|
-
# @overload global_config(name)
|
|
274
|
-
#
|
|
275
|
-
# @example Read the global committer name
|
|
276
|
-
# repo.global_config('user.name') #=> "Alice"
|
|
277
|
-
#
|
|
278
|
-
# @param name [String] the dotted config key to look up (e.g. `"user.name"`)
|
|
279
|
-
#
|
|
280
|
-
# @return [String] the value of the global config entry
|
|
281
|
-
#
|
|
282
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
283
|
-
#
|
|
284
|
-
# @overload global_config(name, value)
|
|
285
|
-
#
|
|
286
|
-
# @example Set the global committer name
|
|
287
|
-
# repo.global_config('user.name', 'Alice')
|
|
288
|
-
#
|
|
289
|
-
# @param name [String] the dotted config key to write (e.g. `"user.name"`)
|
|
290
|
-
#
|
|
291
|
-
# @param value [#to_s] the value to assign; any object is accepted and
|
|
292
|
-
# converted to a String via `#to_s` before being passed to git
|
|
293
|
-
#
|
|
294
|
-
# @return [Git::CommandLine::Result] the raw result of
|
|
295
|
-
# `git config --global <name> <value>`
|
|
296
|
-
#
|
|
297
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
298
|
-
#
|
|
299
|
-
def global_config(name = nil, value = nil)
|
|
300
|
-
Git::Deprecation.warn(GLOBAL_CONFIG_DEPRECATION_WARNING)
|
|
301
|
-
if !name.nil? && !value.nil?
|
|
302
|
-
deprecated_global_config_set(name, value)
|
|
303
|
-
elsif !name.nil?
|
|
304
|
-
deprecated_global_config_get(name)
|
|
305
|
-
else
|
|
306
|
-
deprecated_global_config_list
|
|
307
|
-
end
|
|
308
|
-
end
|
|
309
|
-
|
|
310
154
|
# Returns the size of the repository directory in bytes
|
|
311
155
|
#
|
|
312
156
|
# Sums the sizes of every regular file under the repository (`.git`)
|
|
@@ -320,161 +164,38 @@ module Git
|
|
|
320
164
|
#
|
|
321
165
|
# @return [Integer] the total size in bytes of the repository directory
|
|
322
166
|
#
|
|
167
|
+
# @raise [Git::Error] if the repository directory cannot be traversed, for
|
|
168
|
+
# example when a subdirectory cannot be searched
|
|
169
|
+
#
|
|
323
170
|
def repo_size
|
|
324
171
|
repository = repo
|
|
325
172
|
return 0 unless repository&.directory?
|
|
326
173
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
stat = File.lstat(path)
|
|
330
|
-
total += stat.size if stat.file?
|
|
331
|
-
rescue Errno::ENOENT
|
|
332
|
-
next
|
|
174
|
+
Git::SystemCallGuard.call('Failed to compute the repository size') do
|
|
175
|
+
repo_size_of_directory(repository.to_s)
|
|
333
176
|
end
|
|
334
|
-
total
|
|
335
177
|
end
|
|
336
178
|
|
|
337
179
|
private
|
|
338
180
|
|
|
339
|
-
#
|
|
340
|
-
#
|
|
341
|
-
# @param name [String, Hash, nil] config key or an options hash
|
|
342
|
-
#
|
|
343
|
-
# @param value [#to_s, Hash, nil] config value or an options hash
|
|
344
|
-
#
|
|
345
|
-
# @param options [Hash] explicit options hash argument
|
|
346
|
-
#
|
|
347
|
-
# @option options [String, nil] :file (nil) path to a custom config file
|
|
348
|
-
#
|
|
349
|
-
# @return [Array((String, nil), (#to_s, nil), Hash)] normalized
|
|
350
|
-
# `[name, value, options]`
|
|
351
|
-
#
|
|
352
|
-
# @raise [ArgumentError] if deprecated arguments mix an options hash with
|
|
353
|
-
# unexpected additional positional arguments
|
|
354
|
-
#
|
|
355
|
-
def deprecated_normalize_config_args(name, value, options)
|
|
356
|
-
if name.is_a?(Hash)
|
|
357
|
-
raise ArgumentError, 'unexpected positional arguments after options hash' if !value.nil? || !options.empty?
|
|
358
|
-
|
|
359
|
-
[nil, nil, name]
|
|
360
|
-
elsif value.is_a?(Hash)
|
|
361
|
-
raise ArgumentError, 'unexpected third argument when second argument is options hash' unless options.empty?
|
|
362
|
-
|
|
363
|
-
[name, nil, value]
|
|
364
|
-
else
|
|
365
|
-
[name, value, options]
|
|
366
|
-
end
|
|
367
|
-
end
|
|
368
|
-
|
|
369
|
-
# Writes a config value using the deprecated `config(name, value, ...)` path
|
|
370
|
-
#
|
|
371
|
-
# @overload deprecated_config_set(name, value, **options)
|
|
372
|
-
#
|
|
373
|
-
# @param name [String] the dotted config key to write
|
|
374
|
-
#
|
|
375
|
-
# @param value [#to_s] the value to assign
|
|
376
|
-
#
|
|
377
|
-
# @param options [Hash] command options
|
|
378
|
-
#
|
|
379
|
-
# @option options [String, nil] :file (nil) path to a custom config file
|
|
380
|
-
#
|
|
381
|
-
# @return [Git::CommandLine::Result] the command result
|
|
382
|
-
#
|
|
383
|
-
# @raise [ArgumentError] if unsupported options are provided
|
|
384
|
-
#
|
|
385
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
386
|
-
#
|
|
387
|
-
def deprecated_config_set(name, value, **)
|
|
388
|
-
SharedPrivate.assert_valid_opts!(CONFIG_SET_ALLOWED_OPTS, **)
|
|
389
|
-
Git::Commands::ConfigOptionSyntax::Set.new(@execution_context).call(name, value, **)
|
|
390
|
-
end
|
|
391
|
-
|
|
392
|
-
# Reads a config value using the deprecated `config(name, ...)` path
|
|
393
|
-
#
|
|
394
|
-
# @param name [String] the dotted config key to read
|
|
395
|
-
#
|
|
396
|
-
# @param options [Hash] command options
|
|
397
|
-
#
|
|
398
|
-
# @option options [String, nil] :file (nil) path to a custom config file
|
|
399
|
-
#
|
|
400
|
-
# @return [String] the config value
|
|
401
|
-
#
|
|
402
|
-
# @raise [ArgumentError] if unsupported options are provided
|
|
403
|
-
#
|
|
404
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
405
|
-
#
|
|
406
|
-
def deprecated_config_get(name, **options)
|
|
407
|
-
SharedPrivate.assert_valid_opts!(CONFIG_READ_ALLOWED_OPTS, **options)
|
|
408
|
-
opts = options[:file] ? { file: options[:file] } : {}
|
|
409
|
-
result = Git::Commands::ConfigOptionSyntax::Get.new(@execution_context).call(name, **opts)
|
|
410
|
-
raise Git::FailedError, result if result.status.exitstatus != 0
|
|
411
|
-
|
|
412
|
-
result.stdout
|
|
413
|
-
end
|
|
414
|
-
|
|
415
|
-
# Lists config entries using the deprecated `config(...)` path
|
|
416
|
-
#
|
|
417
|
-
# @param options [Hash] command options
|
|
418
|
-
#
|
|
419
|
-
# @option options [String, nil] :file (nil) path to a custom config file
|
|
420
|
-
#
|
|
421
|
-
# @return [Hash{String => String}] all visible config entries keyed by name
|
|
422
|
-
#
|
|
423
|
-
# @raise [ArgumentError] if unsupported options are provided
|
|
424
|
-
#
|
|
425
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
426
|
-
#
|
|
427
|
-
def deprecated_config_list(**options)
|
|
428
|
-
SharedPrivate.assert_valid_opts!(CONFIG_READ_ALLOWED_OPTS, **options)
|
|
429
|
-
opts = options[:file] ? { file: options[:file] } : {}
|
|
430
|
-
lines = Git::Commands::ConfigOptionSyntax::List.new(@execution_context).call(**opts).stdout.split("\n")
|
|
431
|
-
lines.each_with_object({}) do |line, hsh|
|
|
432
|
-
key, value = line.split('=', 2)
|
|
433
|
-
hsh[key] = value || ''
|
|
434
|
-
end
|
|
435
|
-
end
|
|
436
|
-
|
|
437
|
-
# Reads a global config value using the deprecated `global_config(name)` path
|
|
181
|
+
# Sums the size of every regular file under `dir`
|
|
438
182
|
#
|
|
439
|
-
#
|
|
183
|
+
# Symbolic links are not followed. Entries that disappear between the
|
|
184
|
+
# traversal and the `lstat` call are skipped.
|
|
440
185
|
#
|
|
441
|
-
# @
|
|
186
|
+
# @param dir [String] the directory to traverse
|
|
442
187
|
#
|
|
443
|
-
# @
|
|
444
|
-
#
|
|
445
|
-
def deprecated_global_config_get(name)
|
|
446
|
-
result = Git::Commands::ConfigOptionSyntax::Get.new(@execution_context).call(name, global: true)
|
|
447
|
-
raise Git::FailedError, result if result.status.exitstatus != 0
|
|
448
|
-
|
|
449
|
-
result.stdout
|
|
450
|
-
end
|
|
451
|
-
|
|
452
|
-
# Lists global config entries using the deprecated `global_config` path
|
|
188
|
+
# @return [Integer] the total size in bytes of the files under `dir`
|
|
453
189
|
#
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
key, value = line.split('=', 2)
|
|
462
|
-
hsh[key] = value || ''
|
|
190
|
+
def repo_size_of_directory(dir)
|
|
191
|
+
total = 0
|
|
192
|
+
Find.find(dir) do |path|
|
|
193
|
+
stat = File.lstat(path)
|
|
194
|
+
total += stat.size if stat.file?
|
|
195
|
+
rescue Errno::ENOENT
|
|
196
|
+
next
|
|
463
197
|
end
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
# Writes a global config value using the deprecated `global_config` path
|
|
467
|
-
#
|
|
468
|
-
# @param name [String] the dotted config key to write
|
|
469
|
-
#
|
|
470
|
-
# @param value [#to_s] the value to assign
|
|
471
|
-
#
|
|
472
|
-
# @return [Git::CommandLine::Result] the command result
|
|
473
|
-
#
|
|
474
|
-
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
475
|
-
#
|
|
476
|
-
def deprecated_global_config_set(name, value)
|
|
477
|
-
Git::Commands::ConfigOptionSyntax::Set.new(@execution_context).call(name, value, global: true)
|
|
198
|
+
total
|
|
478
199
|
end
|
|
479
200
|
|
|
480
201
|
# All git config scopes are valid in a repository context
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'git/errors'
|
|
4
|
+
|
|
5
|
+
module Git
|
|
6
|
+
# Convert `SystemCallError` raised by the gem's own filesystem calls to {Git::Error}
|
|
7
|
+
#
|
|
8
|
+
# The gem promises to raise only `ArgumentError` or a subclass of {Git::Error}.
|
|
9
|
+
# Filesystem calls such as `File.read`, `Dir.chdir`, or `Tempfile.create` can
|
|
10
|
+
# raise a bare `SystemCallError` (`Errno::ENOENT`, `Errno::EACCES`, and so on).
|
|
11
|
+
# Wrapping those calls in {.call} keeps the promise: the `SystemCallError` is
|
|
12
|
+
# re-raised as a {Git::Error} with the original error available as `cause`.
|
|
13
|
+
#
|
|
14
|
+
# Helpers that yield to caller-supplied blocks must not relabel errors raised
|
|
15
|
+
# by the caller's own code. {#unguarded} marks that region so a
|
|
16
|
+
# `SystemCallError` raised inside it propagates unchanged.
|
|
17
|
+
#
|
|
18
|
+
# @example Guard a single filesystem call
|
|
19
|
+
# content = Git::SystemCallGuard.call('Failed to read the gitdir pointer file') do
|
|
20
|
+
# File.read(path)
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# @example Guard setup and cleanup but not the caller's block
|
|
24
|
+
# def with_tempfile(&block)
|
|
25
|
+
# Git::SystemCallGuard.call('Failed to create a temporary file') do |guard|
|
|
26
|
+
# Tempfile.create do |file|
|
|
27
|
+
# guard.unguarded { block.call(file) }
|
|
28
|
+
# end
|
|
29
|
+
# end
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# @api private
|
|
33
|
+
#
|
|
34
|
+
class SystemCallGuard
|
|
35
|
+
# Run the block, converting any `SystemCallError` it raises to {Git::Error}
|
|
36
|
+
#
|
|
37
|
+
# @param message [String] prefix for the {Git::Error} message
|
|
38
|
+
#
|
|
39
|
+
# @return [Object] the value returned by the block
|
|
40
|
+
#
|
|
41
|
+
# @raise [Git::Error] if the block raises a `SystemCallError` outside an
|
|
42
|
+
# unguarded region
|
|
43
|
+
#
|
|
44
|
+
# @yield [guard] the guarded region
|
|
45
|
+
#
|
|
46
|
+
# @yieldparam guard [Git::SystemCallGuard] use {#unguarded} to exempt a region
|
|
47
|
+
#
|
|
48
|
+
# @yieldreturn [Object] returned as the method's return value
|
|
49
|
+
#
|
|
50
|
+
def self.call(message, &)
|
|
51
|
+
new.call(message, &)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Run the block, converting any `SystemCallError` it raises to {Git::Error}
|
|
55
|
+
#
|
|
56
|
+
# @param (see .call)
|
|
57
|
+
#
|
|
58
|
+
# @return (see .call)
|
|
59
|
+
#
|
|
60
|
+
# @raise (see .call)
|
|
61
|
+
#
|
|
62
|
+
# @yield (see .call)
|
|
63
|
+
#
|
|
64
|
+
# @yieldparam (see .call)
|
|
65
|
+
#
|
|
66
|
+
# @yieldreturn (see .call)
|
|
67
|
+
#
|
|
68
|
+
def call(message)
|
|
69
|
+
yield self
|
|
70
|
+
rescue SystemCallError => e
|
|
71
|
+
raise if e.equal?(@unguarded_error)
|
|
72
|
+
|
|
73
|
+
raise Git::Error, "#{message}: #{e.message}"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Run the block with `SystemCallError` conversion suspended
|
|
77
|
+
#
|
|
78
|
+
# If the block raises a `SystemCallError`, the enclosing {#call} re-raises
|
|
79
|
+
# that same error unchanged. Any other `SystemCallError` reaching {#call},
|
|
80
|
+
# including one raised while unwinding from the block's error, is converted.
|
|
81
|
+
#
|
|
82
|
+
# @return [Object] the value returned by the block
|
|
83
|
+
#
|
|
84
|
+
# @yield the region exempt from conversion
|
|
85
|
+
#
|
|
86
|
+
# @yieldreturn [Object] returned as the method's return value
|
|
87
|
+
#
|
|
88
|
+
def unguarded
|
|
89
|
+
yield
|
|
90
|
+
rescue SystemCallError => e
|
|
91
|
+
@unguarded_error = e
|
|
92
|
+
raise
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|