git 4.0.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +51 -0
- data/.rubocop_todo.yml +12 -0
- data/CHANGELOG.md +44 -0
- data/Rakefile +13 -1
- data/git.gemspec +35 -30
- data/lib/git/args_builder.rb +103 -0
- data/lib/git/author.rb +6 -5
- data/lib/git/base.rb +254 -165
- data/lib/git/branch.rb +14 -15
- data/lib/git/branches.rb +9 -13
- data/lib/git/command_line.rb +97 -55
- data/lib/git/config.rb +4 -6
- data/lib/git/diff.rb +75 -39
- data/lib/git/diff_path_status.rb +4 -3
- data/lib/git/diff_stats.rb +1 -1
- data/lib/git/errors.rb +8 -2
- data/lib/git/escaped_path.rb +1 -1
- data/lib/git/lib.rb +865 -557
- data/lib/git/log.rb +74 -210
- data/lib/git/object.rb +85 -66
- data/lib/git/path.rb +18 -8
- data/lib/git/remote.rb +3 -4
- data/lib/git/repository.rb +0 -2
- data/lib/git/stash.rb +13 -6
- data/lib/git/stashes.rb +5 -5
- data/lib/git/status.rb +108 -247
- data/lib/git/url.rb +3 -3
- data/lib/git/version.rb +1 -1
- data/lib/git/worktree.rb +4 -5
- data/lib/git/worktrees.rb +4 -6
- data/lib/git.rb +16 -15
- metadata +35 -3
data/lib/git/base.rb
CHANGED
@@ -16,7 +16,7 @@ module Git
|
|
16
16
|
# (see Git.bare)
|
17
17
|
def self.bare(git_dir, options = {})
|
18
18
|
normalize_paths(options, default_repository: git_dir, bare: true)
|
19
|
-
|
19
|
+
new(options)
|
20
20
|
end
|
21
21
|
|
22
22
|
# (see Git.clone)
|
@@ -35,40 +35,48 @@ module Git
|
|
35
35
|
#
|
36
36
|
# @return [Git::Config] the current config instance.
|
37
37
|
def self.config
|
38
|
-
|
38
|
+
@config ||= Config.new
|
39
39
|
end
|
40
40
|
|
41
41
|
def self.binary_version(binary_path)
|
42
|
-
result =
|
43
|
-
status = nil
|
44
|
-
|
45
|
-
begin
|
46
|
-
result, status = Open3.capture2e(binary_path, "-c", "core.quotePath=true", "-c", "color.ui=false", "version")
|
47
|
-
result = result.chomp
|
48
|
-
rescue Errno::ENOENT
|
49
|
-
raise RuntimeError, "Failed to get git version: #{binary_path} not found"
|
50
|
-
end
|
42
|
+
result, status = execute_git_version(binary_path)
|
51
43
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
44
|
+
raise "Failed to get git version: #{status}\n#{result}" unless status.success?
|
45
|
+
|
46
|
+
parse_version_string(result)
|
47
|
+
end
|
48
|
+
|
49
|
+
private_class_method def self.execute_git_version(binary_path)
|
50
|
+
Open3.capture2e(
|
51
|
+
binary_path,
|
52
|
+
'-c', 'core.quotePath=true',
|
53
|
+
'-c', 'color.ui=false',
|
54
|
+
'version'
|
55
|
+
)
|
56
|
+
rescue Errno::ENOENT
|
57
|
+
raise "Failed to get git version: #{binary_path} not found"
|
58
|
+
end
|
59
|
+
|
60
|
+
private_class_method def self.parse_version_string(raw_string)
|
61
|
+
version_match = raw_string.match(/\d+(\.\d+)+/)
|
62
|
+
return [0, 0, 0] unless version_match
|
63
|
+
|
64
|
+
version_parts = version_match[0].split('.').map(&:to_i)
|
65
|
+
version_parts.fill(0, version_parts.length...3)
|
59
66
|
end
|
60
67
|
|
61
68
|
# (see Git.init)
|
62
69
|
def self.init(directory = '.', options = {})
|
63
|
-
normalize_paths(options, default_working_directory: directory, default_repository: directory,
|
70
|
+
normalize_paths(options, default_working_directory: directory, default_repository: directory,
|
71
|
+
bare: options[:bare])
|
64
72
|
|
65
73
|
init_options = {
|
66
|
-
:
|
67
|
-
:
|
74
|
+
bare: options[:bare],
|
75
|
+
initial_branch: options[:initial_branch]
|
68
76
|
}
|
69
77
|
|
70
78
|
directory = options[:bare] ? options[:repository] : options[:working_directory]
|
71
|
-
FileUtils.mkdir_p(directory)
|
79
|
+
FileUtils.mkdir_p(directory)
|
72
80
|
|
73
81
|
# TODO: this dance seems awkward: this creates a Git::Lib so we can call
|
74
82
|
# init so we can create a new Git::Base which in turn (ultimately)
|
@@ -82,24 +90,32 @@ module Git
|
|
82
90
|
#
|
83
91
|
Git::Lib.new(options).init(init_options)
|
84
92
|
|
85
|
-
|
93
|
+
new(options)
|
86
94
|
end
|
87
95
|
|
88
96
|
def self.root_of_worktree(working_dir)
|
89
|
-
result = working_dir
|
90
|
-
status = nil
|
91
|
-
|
92
97
|
raise ArgumentError, "'#{working_dir}' does not exist" unless Dir.exist?(working_dir)
|
93
98
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
99
|
+
result, status = execute_rev_parse_toplevel(working_dir)
|
100
|
+
process_rev_parse_result(result, status, working_dir)
|
101
|
+
end
|
102
|
+
|
103
|
+
private_class_method def self.execute_rev_parse_toplevel(working_dir)
|
104
|
+
Open3.capture2e(
|
105
|
+
Git::Base.config.binary_path,
|
106
|
+
'-c', 'core.quotePath=true',
|
107
|
+
'-c', 'color.ui=false',
|
108
|
+
'rev-parse', '--show-toplevel',
|
109
|
+
chdir: File.expand_path(working_dir)
|
110
|
+
)
|
111
|
+
rescue Errno::ENOENT
|
112
|
+
raise ArgumentError, 'Failed to find the root of the worktree: git binary not found'
|
113
|
+
end
|
100
114
|
|
115
|
+
private_class_method def self.process_rev_parse_result(result, status, working_dir)
|
101
116
|
raise ArgumentError, "'#{working_dir}' is not in a git working tree" unless status.success?
|
102
|
-
|
117
|
+
|
118
|
+
result.chomp
|
103
119
|
end
|
104
120
|
|
105
121
|
# (see Git.open)
|
@@ -110,7 +126,7 @@ module Git
|
|
110
126
|
|
111
127
|
normalize_paths(options, default_working_directory: working_dir)
|
112
128
|
|
113
|
-
|
129
|
+
new(options)
|
114
130
|
end
|
115
131
|
|
116
132
|
# Create an object that executes Git commands in the context of a working
|
@@ -136,16 +152,9 @@ module Git
|
|
136
152
|
# of the opened working copy or bare repository
|
137
153
|
#
|
138
154
|
def initialize(options = {})
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
end
|
143
|
-
@logger = (options[:log] || Logger.new(nil))
|
144
|
-
@logger.info("Starting Git")
|
145
|
-
|
146
|
-
@working_directory = options[:working_directory] ? Git::WorkingDirectory.new(options[:working_directory]) : nil
|
147
|
-
@repository = options[:repository] ? Git::Repository.new(options[:repository]) : nil
|
148
|
-
@index = options[:index] ? Git::Index.new(options[:index], false) : nil
|
155
|
+
options = default_paths(options)
|
156
|
+
setup_logger(options[:log])
|
157
|
+
initialize_components(options)
|
149
158
|
end
|
150
159
|
|
151
160
|
# Update the index from the current worktree to prepare the for the next commit
|
@@ -162,7 +171,7 @@ module Git
|
|
162
171
|
# @option options [Boolean] :force Allow adding otherwise ignored files
|
163
172
|
#
|
164
173
|
def add(paths = '.', **options)
|
165
|
-
|
174
|
+
lib.add(paths, options)
|
166
175
|
end
|
167
176
|
|
168
177
|
# adds a new remote to this repository
|
@@ -177,33 +186,10 @@ module Git
|
|
177
186
|
# :track => <branch_name>
|
178
187
|
def add_remote(name, url, opts = {})
|
179
188
|
url = url.repo.path if url.is_a?(Git::Base)
|
180
|
-
|
189
|
+
lib.remote_add(name, url, opts)
|
181
190
|
Git::Remote.new(self, name)
|
182
191
|
end
|
183
192
|
|
184
|
-
# Create a new git tag
|
185
|
-
#
|
186
|
-
# @example
|
187
|
-
# repo.add_tag('tag_name', object_reference)
|
188
|
-
# repo.add_tag('tag_name', object_reference, {:options => 'here'})
|
189
|
-
# repo.add_tag('tag_name', {:options => 'here'})
|
190
|
-
#
|
191
|
-
# @param [String] name The name of the tag to add
|
192
|
-
# @param [Hash] options Opstions to pass to `git tag`.
|
193
|
-
# See [git-tag](https://git-scm.com/docs/git-tag) for more details.
|
194
|
-
# @option options [boolean] :annotate Make an unsigned, annotated tag object
|
195
|
-
# @option options [boolean] :a An alias for the `:annotate` option
|
196
|
-
# @option options [boolean] :d Delete existing tag with the given names.
|
197
|
-
# @option options [boolean] :f Replace an existing tag with the given name (instead of failing)
|
198
|
-
# @option options [String] :message Use the given tag message
|
199
|
-
# @option options [String] :m An alias for the `:message` option
|
200
|
-
# @option options [boolean] :s Make a GPG-signed tag.
|
201
|
-
#
|
202
|
-
def add_tag(name, *options)
|
203
|
-
self.lib.tag(name, *options)
|
204
|
-
self.tag(name)
|
205
|
-
end
|
206
|
-
|
207
193
|
# changes current working directory for a block
|
208
194
|
# to the git working directory
|
209
195
|
#
|
@@ -219,11 +205,11 @@ module Git
|
|
219
205
|
end
|
220
206
|
end
|
221
207
|
|
222
|
-
#g.config('user.name', 'Scott Chacon') # sets value
|
223
|
-
#g.config('user.email', 'email@email.com') # sets value
|
224
|
-
#g.config('user.email', 'email@email.com', file: 'path/to/custom/config) # sets value in file
|
225
|
-
#g.config('user.name') # returns 'Scott Chacon'
|
226
|
-
#g.config # returns whole config hash
|
208
|
+
# g.config('user.name', 'Scott Chacon') # sets value
|
209
|
+
# g.config('user.email', 'email@email.com') # sets value
|
210
|
+
# g.config('user.email', 'email@email.com', file: 'path/to/custom/config) # sets value in file
|
211
|
+
# g.config('user.name') # returns 'Scott Chacon'
|
212
|
+
# g.config # returns whole config hash
|
227
213
|
def config(name = nil, value = nil, options = {})
|
228
214
|
if name && value
|
229
215
|
# set value
|
@@ -245,9 +231,7 @@ module Git
|
|
245
231
|
end
|
246
232
|
|
247
233
|
# returns reference to the git index file
|
248
|
-
|
249
|
-
@index
|
250
|
-
end
|
234
|
+
attr_reader :index
|
251
235
|
|
252
236
|
# returns reference to the git repository directory
|
253
237
|
# @git.dir.path
|
@@ -257,43 +241,77 @@ module Git
|
|
257
241
|
|
258
242
|
# returns the repository size in bytes
|
259
243
|
def repo_size
|
260
|
-
Dir.glob(File.join(repo.path, '**', '*'), File::FNM_DOTMATCH)
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
end.reduce(:+)
|
244
|
+
all_files = Dir.glob(File.join(repo.path, '**', '*'), File::FNM_DOTMATCH)
|
245
|
+
|
246
|
+
all_files.reject { |file| file.include?('..') }
|
247
|
+
.map { |file| File.expand_path(file) }
|
248
|
+
.uniq
|
249
|
+
.sum { |file| File.stat(file).size.to_i }
|
267
250
|
end
|
268
251
|
|
269
|
-
def set_index(index_file, check =
|
252
|
+
def set_index(index_file, check = nil, must_exist: nil)
|
253
|
+
unless check.nil?
|
254
|
+
Git::Deprecation.warn(
|
255
|
+
'The "check" argument is deprecated and will be removed in a future version. ' \
|
256
|
+
'Use "must_exist:" instead.'
|
257
|
+
)
|
258
|
+
end
|
259
|
+
|
260
|
+
# default is true
|
261
|
+
must_exist = must_exist.nil? && check.nil? ? true : must_exist | check
|
262
|
+
|
270
263
|
@lib = nil
|
271
|
-
@index = Git::Index.new(index_file.to_s,
|
264
|
+
@index = Git::Index.new(index_file.to_s, must_exist:)
|
272
265
|
end
|
273
266
|
|
274
|
-
def set_working(work_dir, check =
|
267
|
+
def set_working(work_dir, check = nil, must_exist: nil)
|
268
|
+
unless check.nil?
|
269
|
+
Git::Deprecation.warn(
|
270
|
+
'The "check" argument is deprecated and will be removed in a future version. ' \
|
271
|
+
'Use "must_exist:" instead.'
|
272
|
+
)
|
273
|
+
end
|
274
|
+
|
275
|
+
# default is true
|
276
|
+
must_exist = must_exist.nil? && check.nil? ? true : must_exist | check
|
277
|
+
|
275
278
|
@lib = nil
|
276
|
-
@working_directory = Git::WorkingDirectory.new(work_dir.to_s,
|
279
|
+
@working_directory = Git::WorkingDirectory.new(work_dir.to_s, must_exist:)
|
277
280
|
end
|
278
281
|
|
279
282
|
# returns +true+ if the branch exists locally
|
280
|
-
def
|
281
|
-
branch_names =
|
283
|
+
def local_branch?(branch)
|
284
|
+
branch_names = branches.local.map(&:name)
|
282
285
|
branch_names.include?(branch)
|
283
286
|
end
|
284
287
|
|
288
|
+
def is_local_branch?(branch) # rubocop:disable Naming/PredicatePrefix
|
289
|
+
Git.deprecation('Git::Base#is_local_branch? is deprecated. Use Git::Base#local_branch? instead.')
|
290
|
+
local_branch?(branch)
|
291
|
+
end
|
292
|
+
|
285
293
|
# returns +true+ if the branch exists remotely
|
286
|
-
def
|
287
|
-
branch_names =
|
294
|
+
def remote_branch?(branch)
|
295
|
+
branch_names = branches.remote.map(&:name)
|
288
296
|
branch_names.include?(branch)
|
289
297
|
end
|
290
298
|
|
299
|
+
def is_remote_branch?(branch) # rubocop:disable Naming/PredicatePrefix
|
300
|
+
Git.deprecated('Git::Base#is_remote_branch? is deprecated. Use Git::Base#remote_branch? instead.')
|
301
|
+
remote_branch?(branch)
|
302
|
+
end
|
303
|
+
|
291
304
|
# returns +true+ if the branch exists
|
292
|
-
def
|
293
|
-
branch_names =
|
305
|
+
def branch?(branch)
|
306
|
+
branch_names = branches.map(&:name)
|
294
307
|
branch_names.include?(branch)
|
295
308
|
end
|
296
309
|
|
310
|
+
def is_branch?(branch) # rubocop:disable Naming/PredicatePrefix
|
311
|
+
Git.deprecated('Git::Base#is_branch? is deprecated. Use Git::Base#branch? instead.')
|
312
|
+
branch?(branch)
|
313
|
+
end
|
314
|
+
|
297
315
|
# this is a convenience method for accessing the class that wraps all the
|
298
316
|
# actual 'git' forked system calls. At some point I hope to replace the Git::Lib
|
299
317
|
# class with one that uses native methods or libgit C bindings
|
@@ -333,32 +351,32 @@ module Git
|
|
333
351
|
# ```
|
334
352
|
#
|
335
353
|
def grep(string, path_limiter = nil, opts = {})
|
336
|
-
|
354
|
+
object('HEAD').grep(string, path_limiter, opts)
|
337
355
|
end
|
338
356
|
|
339
357
|
# List the files in the worktree that are ignored by git
|
340
358
|
# @return [Array<String>] the list of ignored files relative to teh root of the worktree
|
341
359
|
#
|
342
360
|
def ignored_files
|
343
|
-
|
361
|
+
lib.ignored_files
|
344
362
|
end
|
345
363
|
|
346
364
|
# removes file(s) from the git repository
|
347
365
|
def rm(path = '.', opts = {})
|
348
|
-
|
366
|
+
lib.rm(path, opts)
|
349
367
|
end
|
350
368
|
|
351
369
|
alias remove rm
|
352
370
|
|
353
371
|
# resets the working directory to the provided commitish
|
354
372
|
def reset(commitish = nil, opts = {})
|
355
|
-
|
373
|
+
lib.reset(commitish, opts)
|
356
374
|
end
|
357
375
|
|
358
376
|
# resets the working directory to the commitish with '--hard'
|
359
377
|
def reset_hard(commitish = nil, opts = {})
|
360
|
-
opts = {:
|
361
|
-
|
378
|
+
opts = { hard: true }.merge(opts)
|
379
|
+
lib.reset(commitish, opts)
|
362
380
|
end
|
363
381
|
|
364
382
|
# cleans the working directory
|
@@ -369,7 +387,7 @@ module Git
|
|
369
387
|
# :ff
|
370
388
|
#
|
371
389
|
def clean(opts = {})
|
372
|
-
|
390
|
+
lib.clean(opts)
|
373
391
|
end
|
374
392
|
|
375
393
|
# returns the most recent tag that is reachable from a commit
|
@@ -387,8 +405,8 @@ module Git
|
|
387
405
|
# :always
|
388
406
|
# :match
|
389
407
|
#
|
390
|
-
def describe(committish=nil, opts={})
|
391
|
-
|
408
|
+
def describe(committish = nil, opts = {})
|
409
|
+
lib.describe(committish, opts)
|
392
410
|
end
|
393
411
|
|
394
412
|
# reverts the working directory to the provided commitish.
|
@@ -398,7 +416,7 @@ module Git
|
|
398
416
|
# :no_edit
|
399
417
|
#
|
400
418
|
def revert(commitish = nil, opts = {})
|
401
|
-
|
419
|
+
lib.revert(commitish, opts)
|
402
420
|
end
|
403
421
|
|
404
422
|
# commits all pending changes in the index file to the git repository
|
@@ -410,25 +428,25 @@ module Git
|
|
410
428
|
# :author
|
411
429
|
#
|
412
430
|
def commit(message, opts = {})
|
413
|
-
|
431
|
+
lib.commit(message, opts)
|
414
432
|
end
|
415
433
|
|
416
434
|
# commits all pending changes in the index file to the git repository,
|
417
435
|
# but automatically adds all modified files without having to explicitly
|
418
436
|
# calling @git.add() on them.
|
419
437
|
def commit_all(message, opts = {})
|
420
|
-
opts = {:
|
421
|
-
|
438
|
+
opts = { add_all: true }.merge(opts)
|
439
|
+
lib.commit(message, opts)
|
422
440
|
end
|
423
441
|
|
424
442
|
# checks out a branch as the new git working directory
|
425
|
-
def checkout(
|
426
|
-
|
443
|
+
def checkout(*, **)
|
444
|
+
lib.checkout(*, **)
|
427
445
|
end
|
428
446
|
|
429
447
|
# checks out an old version of a file
|
430
448
|
def checkout_file(version, file)
|
431
|
-
|
449
|
+
lib.checkout_file(version, file)
|
432
450
|
end
|
433
451
|
|
434
452
|
# fetches changes from a remote branch - this does not modify the working directory,
|
@@ -438,7 +456,7 @@ module Git
|
|
438
456
|
opts = remote
|
439
457
|
remote = nil
|
440
458
|
end
|
441
|
-
|
459
|
+
lib.fetch(remote, opts)
|
442
460
|
end
|
443
461
|
|
444
462
|
# Push changes to a remote repository
|
@@ -459,20 +477,20 @@ module Git
|
|
459
477
|
# @raise [Git::FailedError] if the push fails
|
460
478
|
# @raise [ArgumentError] if a branch is given without a remote
|
461
479
|
#
|
462
|
-
def push(
|
463
|
-
|
480
|
+
def push(*, **)
|
481
|
+
lib.push(*, **)
|
464
482
|
end
|
465
483
|
|
466
484
|
# merges one or more branches into the current working branch
|
467
485
|
#
|
468
486
|
# you can specify more than one branch to merge by passing an array of branches
|
469
487
|
def merge(branch, message = 'merge', opts = {})
|
470
|
-
|
488
|
+
lib.merge(branch, message, opts)
|
471
489
|
end
|
472
490
|
|
473
491
|
# iterates over the files which are unmerged
|
474
|
-
def each_conflict(&
|
475
|
-
|
492
|
+
def each_conflict(&) # :yields: file, your_version, their_version
|
493
|
+
lib.conflicts(&)
|
476
494
|
end
|
477
495
|
|
478
496
|
# Pulls the given branch from the given remote into the current branch
|
@@ -495,12 +513,12 @@ module Git
|
|
495
513
|
# @raise [Git::FailedError] if the pull fails
|
496
514
|
# @raise [ArgumentError] if a branch is given without a remote
|
497
515
|
def pull(remote = nil, branch = nil, opts = {})
|
498
|
-
|
516
|
+
lib.pull(remote, branch, opts)
|
499
517
|
end
|
500
518
|
|
501
519
|
# returns an array of Git:Remote objects
|
502
520
|
def remotes
|
503
|
-
|
521
|
+
lib.remotes.map { |r| Git::Remote.new(self, r) }
|
504
522
|
end
|
505
523
|
|
506
524
|
# sets the url for a remote
|
@@ -510,7 +528,7 @@ module Git
|
|
510
528
|
#
|
511
529
|
def set_remote_url(name, url)
|
512
530
|
url = url.repo.path if url.is_a?(Git::Base)
|
513
|
-
|
531
|
+
lib.remote_set_url(name, url)
|
514
532
|
Git::Remote.new(self, name)
|
515
533
|
end
|
516
534
|
|
@@ -518,12 +536,12 @@ module Git
|
|
518
536
|
#
|
519
537
|
# @git.remove_remote('scott_git')
|
520
538
|
def remove_remote(name)
|
521
|
-
|
539
|
+
lib.remote_remove(name)
|
522
540
|
end
|
523
541
|
|
524
542
|
# returns an array of all Git::Tag objects for this repository
|
525
543
|
def tags
|
526
|
-
|
544
|
+
lib.tags.map { |r| tag(r) }
|
527
545
|
end
|
528
546
|
|
529
547
|
# Create a new git tag
|
@@ -545,37 +563,37 @@ module Git
|
|
545
563
|
# @option options [boolean] :s Make a GPG-signed tag.
|
546
564
|
#
|
547
565
|
def add_tag(name, *options)
|
548
|
-
|
549
|
-
|
566
|
+
lib.tag(name, *options)
|
567
|
+
tag(name)
|
550
568
|
end
|
551
569
|
|
552
570
|
# deletes a tag
|
553
571
|
def delete_tag(name)
|
554
|
-
|
572
|
+
lib.tag(name, { d: true })
|
555
573
|
end
|
556
574
|
|
557
575
|
# creates an archive file of the given tree-ish
|
558
576
|
def archive(treeish, file = nil, opts = {})
|
559
|
-
|
577
|
+
object(treeish).archive(file, opts)
|
560
578
|
end
|
561
579
|
|
562
580
|
# repacks the repository
|
563
581
|
def repack
|
564
|
-
|
582
|
+
lib.repack
|
565
583
|
end
|
566
584
|
|
567
585
|
def gc
|
568
|
-
|
586
|
+
lib.gc
|
569
587
|
end
|
570
588
|
|
571
589
|
def apply(file)
|
572
|
-
|
573
|
-
|
574
|
-
|
590
|
+
return unless File.exist?(file)
|
591
|
+
|
592
|
+
lib.apply(file)
|
575
593
|
end
|
576
594
|
|
577
595
|
def apply_mail(file)
|
578
|
-
|
596
|
+
lib.apply_mail(file) if File.exist?(file)
|
579
597
|
end
|
580
598
|
|
581
599
|
# Shows objects
|
@@ -583,8 +601,8 @@ module Git
|
|
583
601
|
# @param [String|NilClass] objectish the target object reference (nil == HEAD)
|
584
602
|
# @param [String|NilClass] path the path of the file to be shown
|
585
603
|
# @return [String] the object information
|
586
|
-
def show(objectish=nil, path=nil)
|
587
|
-
|
604
|
+
def show(objectish = nil, path = nil)
|
605
|
+
lib.show(objectish, path)
|
588
606
|
end
|
589
607
|
|
590
608
|
## LOWER LEVEL INDEX OPERATIONS ##
|
@@ -597,11 +615,11 @@ module Git
|
|
597
615
|
return_value
|
598
616
|
end
|
599
617
|
|
600
|
-
def with_temp_index
|
618
|
+
def with_temp_index(&)
|
601
619
|
# Workaround for JRUBY, since they handle the TempFile path different.
|
602
620
|
# MUST be improved to be safer and OS independent.
|
603
621
|
if RUBY_PLATFORM == 'java'
|
604
|
-
temp_path = "/tmp/temp-index-#{(0...15).map{ ('a'..'z').to_a[rand(26)] }.join}"
|
622
|
+
temp_path = "/tmp/temp-index-#{(0...15).map { ('a'..'z').to_a[rand(26)] }.join}"
|
605
623
|
else
|
606
624
|
tempfile = Tempfile.new('temp-index')
|
607
625
|
temp_path = tempfile.path
|
@@ -609,19 +627,19 @@ module Git
|
|
609
627
|
tempfile.unlink
|
610
628
|
end
|
611
629
|
|
612
|
-
with_index(temp_path, &
|
630
|
+
with_index(temp_path, &)
|
613
631
|
end
|
614
632
|
|
615
633
|
def checkout_index(opts = {})
|
616
|
-
|
634
|
+
lib.checkout_index(opts)
|
617
635
|
end
|
618
636
|
|
619
637
|
def read_tree(treeish, opts = {})
|
620
|
-
|
638
|
+
lib.read_tree(treeish, opts)
|
621
639
|
end
|
622
640
|
|
623
641
|
def write_tree
|
624
|
-
|
642
|
+
lib.write_tree
|
625
643
|
end
|
626
644
|
|
627
645
|
def write_and_commit_tree(opts = {})
|
@@ -633,9 +651,8 @@ module Git
|
|
633
651
|
branch(branch).update_ref(commit)
|
634
652
|
end
|
635
653
|
|
636
|
-
|
637
|
-
|
638
|
-
self.lib.ls_files(location)
|
654
|
+
def ls_files(location = nil)
|
655
|
+
lib.ls_files(location)
|
639
656
|
end
|
640
657
|
|
641
658
|
def with_working(work_dir) # :yields: the Git::WorkingDirectory
|
@@ -649,13 +666,13 @@ module Git
|
|
649
666
|
return_value
|
650
667
|
end
|
651
668
|
|
652
|
-
def with_temp_working
|
653
|
-
tempfile = Tempfile.new(
|
669
|
+
def with_temp_working(&)
|
670
|
+
tempfile = Tempfile.new('temp-workdir')
|
654
671
|
temp_dir = tempfile.path
|
655
672
|
tempfile.close
|
656
673
|
tempfile.unlink
|
657
|
-
Dir.mkdir(temp_dir,
|
658
|
-
with_working(temp_dir, &
|
674
|
+
Dir.mkdir(temp_dir, 0o700)
|
675
|
+
with_working(temp_dir, &)
|
659
676
|
end
|
660
677
|
|
661
678
|
# runs git rev-parse to convert the objectish to a full sha
|
@@ -666,18 +683,18 @@ module Git
|
|
666
683
|
# git.rev_parse('v2.4:/doc/index.html')
|
667
684
|
#
|
668
685
|
def rev_parse(objectish)
|
669
|
-
|
686
|
+
lib.rev_parse(objectish)
|
670
687
|
end
|
671
688
|
|
672
689
|
# For backwards compatibility
|
673
690
|
alias revparse rev_parse
|
674
691
|
|
675
692
|
def ls_tree(objectish, opts = {})
|
676
|
-
|
693
|
+
lib.ls_tree(objectish, opts)
|
677
694
|
end
|
678
695
|
|
679
696
|
def cat_file(objectish)
|
680
|
-
|
697
|
+
lib.cat_file(objectish)
|
681
698
|
end
|
682
699
|
|
683
700
|
# The name of the branch HEAD refers to or 'HEAD' if detached
|
@@ -689,11 +706,11 @@ module Git
|
|
689
706
|
# @return [String] the name of the branch HEAD refers to or 'HEAD' if detached
|
690
707
|
#
|
691
708
|
def current_branch
|
692
|
-
|
709
|
+
lib.branch_current
|
693
710
|
end
|
694
711
|
|
695
712
|
# @return [Git::Branch] an object for branch_name
|
696
|
-
def branch(branch_name =
|
713
|
+
def branch(branch_name = current_branch)
|
697
714
|
Git::Branch.new(self, branch_name)
|
698
715
|
end
|
699
716
|
|
@@ -716,7 +733,7 @@ module Git
|
|
716
733
|
|
717
734
|
# @return [Git::Object::Commit] a commit object
|
718
735
|
def commit_tree(tree = nil, opts = {})
|
719
|
-
Git::Object::Commit.new(self,
|
736
|
+
Git::Object::Commit.new(self, lib.commit_tree(tree, opts))
|
720
737
|
end
|
721
738
|
|
722
739
|
# @return [Git::Diff] a Git::Diff object
|
@@ -770,19 +787,19 @@ module Git
|
|
770
787
|
|
771
788
|
# @return [Git::Object::Tag] a tag object
|
772
789
|
def tag(tag_name)
|
773
|
-
Git::Object.new(self, tag_name
|
790
|
+
Git::Object::Tag.new(self, tag_name)
|
774
791
|
end
|
775
792
|
|
776
793
|
# Find as good common ancestors as possible for a merge
|
777
794
|
# example: g.merge_base('master', 'some_branch', 'some_sha', octopus: true)
|
778
795
|
#
|
779
796
|
# @return [Array<Git::Object::Commit>] a collection of common ancestors
|
780
|
-
def merge_base(*
|
781
|
-
shas =
|
797
|
+
def merge_base(*)
|
798
|
+
shas = lib.merge_base(*)
|
782
799
|
shas.map { |sha| gcommit(sha) }
|
783
800
|
end
|
784
801
|
|
785
|
-
# Returns a Git::Diff::Stats object for accessing diff statistics.
|
802
|
+
# Returns a Git::Diff::Stats object for accessing diff statistics.
|
786
803
|
#
|
787
804
|
# @param objectish [String] The first commit or object to compare. Defaults to 'HEAD'.
|
788
805
|
# @param obj2 [String, nil] The second commit or object to compare.
|
@@ -805,6 +822,38 @@ module Git
|
|
805
822
|
|
806
823
|
private
|
807
824
|
|
825
|
+
# Sets default paths in the options hash for direct `Git::Base.new` calls
|
826
|
+
#
|
827
|
+
# Factory methods like `Git.open` pre-populate these options by calling
|
828
|
+
# `normalize_paths`, making this a fallback. It avoids mutating the
|
829
|
+
# original options hash by returning a new one.
|
830
|
+
#
|
831
|
+
# @param options [Hash] the original options hash
|
832
|
+
# @return [Hash] a new options hash with defaults applied
|
833
|
+
def default_paths(options)
|
834
|
+
return options unless (working_dir = options[:working_directory])
|
835
|
+
|
836
|
+
options.dup.tap do |opts|
|
837
|
+
opts[:repository] ||= File.join(working_dir, '.git')
|
838
|
+
opts[:index] ||= File.join(opts[:repository], 'index')
|
839
|
+
end
|
840
|
+
end
|
841
|
+
|
842
|
+
# Initializes the logger from the provided options
|
843
|
+
# @param log_option [Logger, nil] The logger instance from options.
|
844
|
+
def setup_logger(log_option)
|
845
|
+
@logger = log_option || Logger.new(nil)
|
846
|
+
@logger.info('Starting Git')
|
847
|
+
end
|
848
|
+
|
849
|
+
# Initializes the core git objects based on the provided options
|
850
|
+
# @param options [Hash] The processed options hash.
|
851
|
+
def initialize_components(options)
|
852
|
+
@working_directory = Git::WorkingDirectory.new(options[:working_directory]) if options[:working_directory]
|
853
|
+
@repository = Git::Repository.new(options[:repository]) if options[:repository]
|
854
|
+
@index = Git::Index.new(options[:index], false) if options[:index]
|
855
|
+
end
|
856
|
+
|
808
857
|
# Normalize options before they are sent to Git::Base.new
|
809
858
|
#
|
810
859
|
# Updates the options parameter by setting appropriate values for the following keys:
|
@@ -868,18 +917,58 @@ module Git
|
|
868
917
|
# 2. the working directory if NOT working with a bare repository
|
869
918
|
#
|
870
919
|
private_class_method def self.normalize_repository(options, default:, bare: false)
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
File.expand_path(options[:repository] || '.git', options[:working_directory])
|
876
|
-
end
|
920
|
+
initial_path = initial_repository_path(options, default: default, bare: bare)
|
921
|
+
final_path = resolve_gitdir_if_present(initial_path, options[:working_directory])
|
922
|
+
options[:repository] = final_path
|
923
|
+
end
|
877
924
|
|
878
|
-
|
879
|
-
|
925
|
+
# Determines the initial, potential path to the repository directory
|
926
|
+
#
|
927
|
+
# This path is considered 'initial' because it is not guaranteed to be the
|
928
|
+
# final repository location. For features like submodules or worktrees,
|
929
|
+
# this path may point to a text file containing a `gitdir:` pointer to the
|
930
|
+
# actual repository directory elsewhere. This initial path must be
|
931
|
+
# subsequently resolved.
|
932
|
+
#
|
933
|
+
# @api private
|
934
|
+
#
|
935
|
+
# @param options [Hash] The options hash, checked for `[:repository]`.
|
936
|
+
#
|
937
|
+
# @param default [String] A fallback path if `options[:repository]` is not set.
|
938
|
+
#
|
939
|
+
# @param bare [Boolean] Whether the repository is bare, which changes path resolution.
|
940
|
+
#
|
941
|
+
# @return [String] The initial, absolute path to the `.git` directory or file.
|
942
|
+
#
|
943
|
+
private_class_method def self.initial_repository_path(options, default:, bare:)
|
944
|
+
if bare
|
945
|
+
File.expand_path(options[:repository] || default || Dir.pwd)
|
946
|
+
else
|
947
|
+
File.expand_path(options[:repository] || '.git', options[:working_directory])
|
880
948
|
end
|
949
|
+
end
|
950
|
+
|
951
|
+
# Resolves the path to the actual repository if it's a `gitdir:` pointer file.
|
952
|
+
#
|
953
|
+
# If `path` points to a file (common in submodules and worktrees), this
|
954
|
+
# method reads the `gitdir:` path from it and returns the real repository
|
955
|
+
# path. Otherwise, it returns the original path.
|
956
|
+
#
|
957
|
+
# @api private
|
958
|
+
#
|
959
|
+
# @param path [String] The initial path to the repository, which may be a pointer file.
|
960
|
+
#
|
961
|
+
# @param working_dir [String] The working directory, used as a base to resolve the path.
|
962
|
+
#
|
963
|
+
# @return [String] The final, resolved absolute path to the repository directory.
|
964
|
+
#
|
965
|
+
private_class_method def self.resolve_gitdir_if_present(path, working_dir)
|
966
|
+
return path unless File.file?(path)
|
881
967
|
|
882
|
-
|
968
|
+
# The file contains `gitdir: <path>`, so we read the file,
|
969
|
+
# extract the path part, and expand it.
|
970
|
+
gitdir_pointer = File.read(path).sub(/\Agitdir: /, '').strip
|
971
|
+
File.expand_path(gitdir_pointer, working_dir)
|
883
972
|
end
|
884
973
|
|
885
974
|
# Normalize options[:index]
|