git 5.7.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +132 -35
- data/CONTRIBUTING.md +44 -31
- data/README.md +138 -100
- data/UPGRADING.md +867 -262
- data/git.gemspec +6 -8
- data/lib/git/author_info.rb +1 -2
- data/lib/git/commands/base.rb +22 -2
- data/lib/git/commands/cat_file/raw.rb +0 -68
- data/lib/git/commands/maintenance/register.rb +0 -4
- data/lib/git/commands/maintenance/run.rb +0 -4
- data/lib/git/commands/maintenance/start.rb +0 -4
- data/lib/git/commands/maintenance/stop.rb +0 -4
- data/lib/git/commands/maintenance/unregister.rb +0 -4
- data/lib/git/commands/maintenance.rb +0 -2
- data/lib/git/commands/show_ref/exclude_existing.rb +1 -1
- data/lib/git/commands/show_ref/exists.rb +0 -5
- data/lib/git/commands/show_ref/list.rb +1 -1
- data/lib/git/commands/show_ref/verify.rb +1 -1
- data/lib/git/commands/show_ref.rb +1 -1
- data/lib/git/commands/worktree/repair.rb +0 -5
- data/lib/git/diff_path_status.rb +1 -1
- data/lib/git/encoding_utils.rb +25 -0
- data/lib/git/errors.rb +24 -18
- data/lib/git/factories.rb +361 -125
- data/lib/git/log.rb +3 -75
- data/lib/git/object.rb +4 -185
- data/lib/git/path_resolver.rb +104 -36
- data/lib/git/repository/branching.rb +21 -189
- data/lib/git/repository/committing.rb +0 -25
- data/lib/git/repository/context_helpers.rb +249 -155
- data/lib/git/repository/diffing.rb +2 -72
- data/lib/git/repository/merging.rb +32 -54
- data/lib/git/repository/object_operations.rb +120 -497
- data/lib/git/repository/remote_operations.rb +7 -219
- data/lib/git/repository/staging.rb +6 -184
- data/lib/git/repository/stashing.rb +126 -130
- data/lib/git/repository/status_operations.rb +1 -54
- data/lib/git/repository/worktree_operations.rb +30 -125
- data/lib/git/repository.rb +20 -299
- data/lib/git/system_call_guard.rb +95 -0
- data/lib/git/version.rb +1 -1
- data/lib/git.rb +47 -364
- metadata +24 -18
- data/lib/git/author.rb +0 -46
- data/lib/git/branch.rb +0 -693
- data/lib/git/branches.rb +0 -194
- data/lib/git/remote.rb +0 -191
- data/lib/git/stash.rb +0 -124
- data/lib/git/stashes.rb +0 -198
- data/lib/git/status.rb +0 -384
- data/lib/git/worktree.rb +0 -180
- data/lib/git/worktrees.rb +0 -152
data/lib/git/log.rb
CHANGED
|
@@ -9,10 +9,10 @@ module Git
|
|
|
9
9
|
# return every matching commit. Calling {#all} adds the `--all` flag to include
|
|
10
10
|
# all refs in the search but does not change the number of commits returned.
|
|
11
11
|
#
|
|
12
|
-
# The query
|
|
13
|
-
#
|
|
12
|
+
# The query runs when {#execute} is called and returns a {Git::Log::Result}
|
|
13
|
+
# holding the matching commits.
|
|
14
14
|
#
|
|
15
|
-
# @example
|
|
15
|
+
# @example Running a query
|
|
16
16
|
# log = git.log.max_count(50).between('v1.0', 'v1.1').author('Scott')
|
|
17
17
|
# results = log.execute
|
|
18
18
|
# puts "Found #{results.size} commits."
|
|
@@ -21,8 +21,6 @@ module Git
|
|
|
21
21
|
# @api public
|
|
22
22
|
#
|
|
23
23
|
class Log
|
|
24
|
-
include Enumerable
|
|
25
|
-
|
|
26
24
|
# An immutable, Enumerable collection of `Git::Object::Commit` objects.
|
|
27
25
|
# Returned by `Git::Log#execute`.
|
|
28
26
|
#
|
|
@@ -236,76 +234,6 @@ module Git
|
|
|
236
234
|
Result.new(@commits)
|
|
237
235
|
end
|
|
238
236
|
|
|
239
|
-
# @!group Deprecated Enumerable Interface
|
|
240
|
-
|
|
241
|
-
# @deprecated Use {#execute} and call `each` on the result.
|
|
242
|
-
def each(&)
|
|
243
|
-
Git::Deprecation.warn(
|
|
244
|
-
'Calling Git::Log#each is deprecated and will be removed in v6.0.0. ' \
|
|
245
|
-
'Call #execute and then #each on the result object.'
|
|
246
|
-
)
|
|
247
|
-
run_log_if_dirty
|
|
248
|
-
@commits.each(&)
|
|
249
|
-
end
|
|
250
|
-
|
|
251
|
-
# @deprecated Use {#execute} and call `size` on the result.
|
|
252
|
-
def size
|
|
253
|
-
Git::Deprecation.warn(
|
|
254
|
-
'Calling Git::Log#size is deprecated and will be removed in v6.0.0. ' \
|
|
255
|
-
'Call #execute and then #size on the result object.'
|
|
256
|
-
)
|
|
257
|
-
run_log_if_dirty
|
|
258
|
-
@commits.size
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
# @deprecated Use {#execute} and call `to_s` on the result.
|
|
262
|
-
def to_s
|
|
263
|
-
Git::Deprecation.warn(
|
|
264
|
-
'Calling Git::Log#to_s is deprecated and will be removed in v6.0.0. ' \
|
|
265
|
-
'Call #execute and then #to_s on the result object.'
|
|
266
|
-
)
|
|
267
|
-
run_log_if_dirty
|
|
268
|
-
@commits.join("\n")
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
# @deprecated Use {#execute} and call the method on the result.
|
|
272
|
-
def first
|
|
273
|
-
Git::Deprecation.warn(
|
|
274
|
-
'Calling Git::Log#first is deprecated and will be removed in v6.0.0. ' \
|
|
275
|
-
'Call #execute and then #first on the result object.'
|
|
276
|
-
)
|
|
277
|
-
run_log_if_dirty
|
|
278
|
-
@commits.first
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
# @deprecated Use {#execute} and call the method on the result.
|
|
282
|
-
def last
|
|
283
|
-
Git::Deprecation.warn(
|
|
284
|
-
'Calling Git::Log#last is deprecated and will be removed in v6.0.0. ' \
|
|
285
|
-
'Call #execute and then #last on the result object.'
|
|
286
|
-
)
|
|
287
|
-
run_log_if_dirty
|
|
288
|
-
@commits.last
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
# @param index [Integer, Range] the commit index or range to retrieve
|
|
292
|
-
#
|
|
293
|
-
# @return [Git::Object::Commit, Array<Git::Object::Commit>, nil] the selected
|
|
294
|
-
# commit or commits
|
|
295
|
-
#
|
|
296
|
-
# @deprecated Use {#execute} and call the method on the result.
|
|
297
|
-
#
|
|
298
|
-
def [](index)
|
|
299
|
-
Git::Deprecation.warn(
|
|
300
|
-
'Calling Git::Log#[] is deprecated and will be removed in v6.0.0. ' \
|
|
301
|
-
'Call #execute and then #[] on the result object.'
|
|
302
|
-
)
|
|
303
|
-
run_log_if_dirty
|
|
304
|
-
@commits[index]
|
|
305
|
-
end
|
|
306
|
-
|
|
307
|
-
# @!endgroup
|
|
308
|
-
|
|
309
237
|
private
|
|
310
238
|
|
|
311
239
|
# Sets a log query option and marks cached results dirty
|
data/lib/git/object.rb
CHANGED
|
@@ -223,6 +223,9 @@ module Git
|
|
|
223
223
|
#
|
|
224
224
|
# @raise [Git::FailedError] if `git archive` fails
|
|
225
225
|
#
|
|
226
|
+
# @note A failure leaves nothing behind and does not replace an existing
|
|
227
|
+
# `file`; see {Git::Repository::ObjectOperations#archive}.
|
|
228
|
+
#
|
|
226
229
|
# @api public
|
|
227
230
|
#
|
|
228
231
|
def archive(file = nil, opts = {})
|
|
@@ -481,22 +484,6 @@ module Git
|
|
|
481
484
|
diff(parent)
|
|
482
485
|
end
|
|
483
486
|
|
|
484
|
-
# Sets parsed commit data on this commit object
|
|
485
|
-
#
|
|
486
|
-
# @param data [Hash] parsed commit data
|
|
487
|
-
#
|
|
488
|
-
# @return [void]
|
|
489
|
-
#
|
|
490
|
-
# @deprecated use {#from_data} instead
|
|
491
|
-
#
|
|
492
|
-
def set_commit(data) # rubocop:disable Naming/AccessorMethodName
|
|
493
|
-
Git::Deprecation.warn(
|
|
494
|
-
'Git::Object::Commit#set_commit is deprecated and will be removed in v6.0.0. ' \
|
|
495
|
-
'Use #from_data instead.'
|
|
496
|
-
)
|
|
497
|
-
from_data(data)
|
|
498
|
-
end
|
|
499
|
-
|
|
500
487
|
# Loads parsed commit data into this commit object
|
|
501
488
|
#
|
|
502
489
|
# @param data [Hash] parsed commit data from `git cat-file commit`
|
|
@@ -531,146 +518,6 @@ module Git
|
|
|
531
518
|
end
|
|
532
519
|
end
|
|
533
520
|
|
|
534
|
-
# A Git tag object
|
|
535
|
-
#
|
|
536
|
-
# This class represents a tag in Git, which can be either annotated or lightweight.
|
|
537
|
-
#
|
|
538
|
-
# Annotated tags contain additional metadata such as the tagger's name, email, and
|
|
539
|
-
# the date when the tag was created, along with a message.
|
|
540
|
-
#
|
|
541
|
-
# @deprecated Use {Git::Repository::ObjectOperations#tag_list} and
|
|
542
|
-
# {Git::TagInfo} instead
|
|
543
|
-
#
|
|
544
|
-
# {Git::TagInfo} is an immutable value object carrying the tag's `name`,
|
|
545
|
-
# `oid`, `target_oid`, `annotated?`, `message`, and `tagger`. Call the
|
|
546
|
-
# corresponding {Git::Repository} method (e.g. `archive`, `log`, `diff`,
|
|
547
|
-
# `cat_file_contents`) with `info.oid || info.target_oid` for operations
|
|
548
|
-
# on a tag; that is the object this class resolves and pins at
|
|
549
|
-
# construction, so a later move of the tag does not redirect an existing
|
|
550
|
-
# object, whereas the tag name would. Constructing a `Git::Object::Tag`
|
|
551
|
-
# emits a deprecation warning.
|
|
552
|
-
#
|
|
553
|
-
class Tag < AbstractObject
|
|
554
|
-
# @return [String] the tag name
|
|
555
|
-
#
|
|
556
|
-
attr_accessor :name
|
|
557
|
-
|
|
558
|
-
# @overload initialize(base, name)
|
|
559
|
-
#
|
|
560
|
-
# @param base [Git::Repository] the git repository
|
|
561
|
-
#
|
|
562
|
-
# @param name [String] the name of the tag
|
|
563
|
-
#
|
|
564
|
-
# @overload initialize(base, sha, name)
|
|
565
|
-
#
|
|
566
|
-
# `sha` is kept as the object that the inherited operations (`size`,
|
|
567
|
-
# `contents`, `grep`, `diff`, `log`, `archive`) run against; `annotated?`,
|
|
568
|
-
# `message`, and `tagger` read the ref `name`. {Git::TagInfo} describes a
|
|
569
|
-
# ref, so there is no OID-based replacement for this form: pass `sha` to
|
|
570
|
-
# the {Git::Repository} operation directly, or read the tag object with
|
|
571
|
-
# {Git::Repository::ObjectOperations#cat_file_tag}.
|
|
572
|
-
#
|
|
573
|
-
# @param base [Git::Repository] the git repository
|
|
574
|
-
#
|
|
575
|
-
# @param sha [String] the SHA of the tag object
|
|
576
|
-
#
|
|
577
|
-
# @param name [String] the name of the tag
|
|
578
|
-
#
|
|
579
|
-
def initialize(base, sha, name = nil)
|
|
580
|
-
Git::Deprecation.warn(
|
|
581
|
-
'Git::Object::Tag is deprecated and will be removed in v6.0.0. ' \
|
|
582
|
-
'Use Git::Repository#tag_list and Git::TagInfo instead.'
|
|
583
|
-
)
|
|
584
|
-
sha, name = resolve_sha_and_name(base, sha, name)
|
|
585
|
-
super(base, sha)
|
|
586
|
-
|
|
587
|
-
@name = name
|
|
588
|
-
@annotated = nil
|
|
589
|
-
@loaded = false
|
|
590
|
-
end
|
|
591
|
-
|
|
592
|
-
# Returns whether this tag is annotated
|
|
593
|
-
#
|
|
594
|
-
# @return [Boolean] `true` when the tag has an annotated tag object
|
|
595
|
-
#
|
|
596
|
-
def annotated?
|
|
597
|
-
@annotated = @annotated.nil? ? (object_repository.cat_file_type(name) == 'tag') : @annotated
|
|
598
|
-
end
|
|
599
|
-
|
|
600
|
-
# Returns the tag message
|
|
601
|
-
#
|
|
602
|
-
# @return [String, nil] the annotated tag message, or `nil` for a
|
|
603
|
-
# lightweight tag
|
|
604
|
-
#
|
|
605
|
-
def message
|
|
606
|
-
check_tag
|
|
607
|
-
@message
|
|
608
|
-
end
|
|
609
|
-
|
|
610
|
-
# Returns whether this object is a tag
|
|
611
|
-
#
|
|
612
|
-
# @return [Boolean] `true`
|
|
613
|
-
#
|
|
614
|
-
def tag?
|
|
615
|
-
true
|
|
616
|
-
end
|
|
617
|
-
|
|
618
|
-
# Returns the tagger identity
|
|
619
|
-
#
|
|
620
|
-
# @return [Git::AuthorInfo, nil] the tagger for an annotated tag, or `nil`
|
|
621
|
-
# for a lightweight tag
|
|
622
|
-
#
|
|
623
|
-
def tagger
|
|
624
|
-
check_tag
|
|
625
|
-
@tagger
|
|
626
|
-
end
|
|
627
|
-
|
|
628
|
-
private
|
|
629
|
-
|
|
630
|
-
# Resolves the two-argument constructor form to a SHA and a tag name
|
|
631
|
-
#
|
|
632
|
-
# In the two-argument form `sha` carries the tag name and the SHA is
|
|
633
|
-
# looked up from the repository.
|
|
634
|
-
#
|
|
635
|
-
# @param base [Git::Repository] the git repository
|
|
636
|
-
#
|
|
637
|
-
# @param sha [String] the SHA of the tag object, or the tag name in the
|
|
638
|
-
# two-argument form
|
|
639
|
-
#
|
|
640
|
-
# @param name [String, nil] the tag name, or `nil` in the two-argument form
|
|
641
|
-
#
|
|
642
|
-
# @return [Array(String, String)] the resolved `[sha, name]` pair
|
|
643
|
-
#
|
|
644
|
-
# @raise [Git::UnexpectedResultError] if the tag does not exist
|
|
645
|
-
#
|
|
646
|
-
def resolve_sha_and_name(base, sha, name)
|
|
647
|
-
return [sha, name] unless name.nil?
|
|
648
|
-
|
|
649
|
-
resolved = base.tag_sha(sha)
|
|
650
|
-
raise Git::UnexpectedResultError, "Tag '#{sha}' does not exist." if resolved == ''
|
|
651
|
-
|
|
652
|
-
[resolved, sha]
|
|
653
|
-
end
|
|
654
|
-
|
|
655
|
-
# Loads annotated tag data when available
|
|
656
|
-
#
|
|
657
|
-
# @return [void]
|
|
658
|
-
#
|
|
659
|
-
def check_tag
|
|
660
|
-
return if @loaded
|
|
661
|
-
|
|
662
|
-
if annotated?
|
|
663
|
-
tdata = object_repository.cat_file_tag(@name)
|
|
664
|
-
@message = tdata['message'].chomp
|
|
665
|
-
@tagger = Git::AuthorInfo.parse(tdata['tagger'])
|
|
666
|
-
else
|
|
667
|
-
@message = @tagger = nil
|
|
668
|
-
end
|
|
669
|
-
|
|
670
|
-
@loaded = true
|
|
671
|
-
end
|
|
672
|
-
end
|
|
673
|
-
|
|
674
521
|
# if we're calling this, we don't know what type it is yet
|
|
675
522
|
# so this is our little factory method
|
|
676
523
|
#
|
|
@@ -680,15 +527,10 @@ module Git
|
|
|
680
527
|
#
|
|
681
528
|
# @param type [String, nil] object type hint: `blob`, `commit`, or `tree`
|
|
682
529
|
#
|
|
683
|
-
# @param is_tag [Boolean] whether to construct a tag object
|
|
684
|
-
#
|
|
685
530
|
# @return [Git::Object::AbstractObject] the concrete object wrapper
|
|
686
531
|
#
|
|
687
|
-
def self.new(base, objectish, type = nil
|
|
688
|
-
return new_tag(base, objectish) if is_tag
|
|
689
|
-
|
|
532
|
+
def self.new(base, objectish, type = nil)
|
|
690
533
|
type ||= object_repository_for(base).cat_file_type(objectish)
|
|
691
|
-
# TODO: why not handle tag case here too?
|
|
692
534
|
klass =
|
|
693
535
|
case type
|
|
694
536
|
when /blob/ then Blob
|
|
@@ -698,29 +540,6 @@ module Git
|
|
|
698
540
|
klass.new(base, objectish)
|
|
699
541
|
end
|
|
700
542
|
|
|
701
|
-
# Creates a tag object through the deprecated factory path
|
|
702
|
-
#
|
|
703
|
-
# @param base [Git::Repository] the repository used to query object data
|
|
704
|
-
#
|
|
705
|
-
# @param objectish [String] the tag name or SHA
|
|
706
|
-
#
|
|
707
|
-
# @return [Git::Object::Tag] the tag object wrapper
|
|
708
|
-
#
|
|
709
|
-
# @deprecated Use {Git::Repository::ObjectOperations#tag_list} instead
|
|
710
|
-
#
|
|
711
|
-
# The warning names `Git::Object::Tag.new`, the replacement this path
|
|
712
|
-
# shipped with, and the {Git::Object::Tag} constructor is deprecated as
|
|
713
|
-
# well; this method silences it so one call emits one warning. Go
|
|
714
|
-
# straight to `Git::Repository#tag_list(name).first`.
|
|
715
|
-
#
|
|
716
|
-
private_class_method def self.new_tag(base, objectish)
|
|
717
|
-
Git::Deprecation.warn(
|
|
718
|
-
'Git::Object.new with is_tag argument is deprecated and will be removed in v6.0.0. ' \
|
|
719
|
-
'Use Git::Object::Tag.new instead.'
|
|
720
|
-
)
|
|
721
|
-
Git::Deprecation.silence { Git::Object::Tag.new(base, objectish) }
|
|
722
|
-
end
|
|
723
|
-
|
|
724
543
|
# Returns the repository used for object lookup
|
|
725
544
|
#
|
|
726
545
|
# @param base [Git::Repository] the repository to return
|
data/lib/git/path_resolver.rb
CHANGED
|
@@ -4,20 +4,37 @@ require 'git/commands/rev_parse'
|
|
|
4
4
|
require 'git/errors'
|
|
5
5
|
require 'git/execution_context'
|
|
6
6
|
require 'git/execution_context/global'
|
|
7
|
+
require 'git/system_call_guard'
|
|
7
8
|
|
|
8
9
|
module Git
|
|
9
10
|
# Resolves and normalizes the filesystem paths that locate a Git repository
|
|
10
11
|
#
|
|
11
12
|
# `PathResolver` is the single home for the path-resolution logic used by the
|
|
12
|
-
# `Git
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
13
|
+
# `Git` factory methods ({Git.open}, {Git.bare}, {Git.init}, and {Git.clone}).
|
|
14
|
+
# It computes the absolute working-directory, repository (`.git`), and index
|
|
15
|
+
# paths from the caller-supplied values, following the same rules Git itself
|
|
16
|
+
# uses (including gitdir-pointer files for submodules and linked worktrees).
|
|
17
|
+
#
|
|
18
|
+
# A relative path given by the caller is expanded against the process working
|
|
19
|
+
# directory, as git does for a path on its command line. Only the defaults are
|
|
20
|
+
# derived from the layout: an omitted repository is `<working_directory>/.git`
|
|
21
|
+
# and an omitted index is `<repository>/index`.
|
|
17
22
|
#
|
|
18
23
|
# @api private
|
|
19
24
|
#
|
|
20
25
|
module PathResolver
|
|
26
|
+
# The {Git::Error} message when a working directory path cannot be expanded
|
|
27
|
+
WORKING_DIRECTORY_MESSAGE = 'Failed to resolve the working directory'
|
|
28
|
+
|
|
29
|
+
# The {Git::Error} message when a repository directory path cannot be expanded
|
|
30
|
+
REPOSITORY_MESSAGE = 'Failed to resolve the repository directory'
|
|
31
|
+
|
|
32
|
+
# The {Git::Error} message when an index file path cannot be expanded
|
|
33
|
+
INDEX_MESSAGE = 'Failed to resolve the index file'
|
|
34
|
+
|
|
35
|
+
# The {Git::Error} message when the `:chdir` option of `Git.clone` cannot be expanded
|
|
36
|
+
CHDIR_MESSAGE = 'Failed to resolve the chdir directory'
|
|
37
|
+
|
|
21
38
|
module_function
|
|
22
39
|
|
|
23
40
|
# Resolve and normalize the paths that locate a Git repository
|
|
@@ -44,10 +61,12 @@ module Git
|
|
|
44
61
|
# @return [Hash{Symbol => (String, nil)}] a hash with `:working_directory`,
|
|
45
62
|
# `:repository`, and `:index` keys
|
|
46
63
|
#
|
|
64
|
+
# @raise [Git::Error] if the repository path is a gitdir pointer file that
|
|
65
|
+
# cannot be read
|
|
66
|
+
#
|
|
47
67
|
def resolve_paths(working_directory: nil, repository: nil, index: nil, bare: false)
|
|
48
68
|
working_dir = resolve_working_directory(working_directory, bare: bare)
|
|
49
|
-
|
|
50
|
-
repo_path = resolve_repository(repository, working_dir, bare: bare, bare_default: working_directory)
|
|
69
|
+
repo_path = resolve_repository(repository, working_dir)
|
|
51
70
|
index_path = resolve_index(index, repo_path)
|
|
52
71
|
|
|
53
72
|
{
|
|
@@ -81,17 +100,20 @@ module Git
|
|
|
81
100
|
# @raise [ArgumentError] if `working_dir` does not exist, is not a
|
|
82
101
|
# directory, or is not inside a git working tree
|
|
83
102
|
#
|
|
84
|
-
#
|
|
103
|
+
# @raise [Git::Error] if the git binary cannot be found or fails to launch, or
|
|
104
|
+
# if `working_dir` cannot be expanded to an absolute path
|
|
85
105
|
#
|
|
86
106
|
def root_of_worktree(working_dir, binary_path: :use_global_config, git_ssh: :use_global_config)
|
|
87
|
-
|
|
107
|
+
expanded_dir = expand_path(working_dir, 'Failed to expand the working directory path')
|
|
108
|
+
raise ArgumentError, "'#{working_dir}' does not exist or is not a directory" unless Dir.exist?(expanded_dir)
|
|
88
109
|
|
|
89
|
-
execute_rev_parse_toplevel(
|
|
110
|
+
execute_rev_parse_toplevel(expanded_dir, binary_path: binary_path, git_ssh: git_ssh)
|
|
90
111
|
end
|
|
91
112
|
|
|
92
113
|
# Run `git rev-parse --show-toplevel` from `working_dir` and return stdout
|
|
93
114
|
#
|
|
94
|
-
# @param working_dir [String]
|
|
115
|
+
# @param working_dir [String] the absolute path of a directory inside the
|
|
116
|
+
# working tree
|
|
95
117
|
#
|
|
96
118
|
# @param binary_path [String, :use_global_config] path to the git binary
|
|
97
119
|
#
|
|
@@ -99,18 +121,16 @@ module Git
|
|
|
99
121
|
#
|
|
100
122
|
# @return [String] the top-level directory reported by git
|
|
101
123
|
#
|
|
102
|
-
# @raise [ArgumentError] if
|
|
103
|
-
#
|
|
124
|
+
# @raise [ArgumentError] if `working_dir` is not inside a git working tree
|
|
125
|
+
#
|
|
126
|
+
# @raise [Git::Error] if the git binary cannot be found or fails to launch
|
|
104
127
|
#
|
|
105
128
|
# @api private
|
|
106
129
|
#
|
|
107
130
|
def execute_rev_parse_toplevel(working_dir, binary_path: :use_global_config, git_ssh: :use_global_config)
|
|
108
131
|
execution_context = Git::ExecutionContext::Global.new(binary_path: binary_path, git_ssh: git_ssh)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
).stdout
|
|
112
|
-
rescue Errno::ENOENT
|
|
113
|
-
raise ArgumentError, 'Failed to find the root of the worktree: git binary not found'
|
|
132
|
+
|
|
133
|
+
Git::Commands::RevParse.new(execution_context).call(show_toplevel: true, chdir: working_dir).stdout
|
|
114
134
|
rescue Git::FailedError
|
|
115
135
|
raise ArgumentError, "'#{working_dir}' is not in a git working tree"
|
|
116
136
|
end
|
|
@@ -124,12 +144,15 @@ module Git
|
|
|
124
144
|
#
|
|
125
145
|
# @return [String, nil] the absolute path, or `nil` for bare repos
|
|
126
146
|
#
|
|
147
|
+
# @raise [Git::Error] if the path cannot be expanded, which happens when the
|
|
148
|
+
# process working directory has been removed
|
|
149
|
+
#
|
|
127
150
|
# @api private
|
|
128
151
|
#
|
|
129
152
|
def resolve_working_directory(path, bare:)
|
|
130
153
|
return nil if bare
|
|
131
154
|
|
|
132
|
-
|
|
155
|
+
expand_path(path, WORKING_DIRECTORY_MESSAGE)
|
|
133
156
|
end
|
|
134
157
|
private_class_method :resolve_working_directory
|
|
135
158
|
|
|
@@ -139,24 +162,24 @@ module Git
|
|
|
139
162
|
#
|
|
140
163
|
# @param path [String, nil] the repository path or `nil`
|
|
141
164
|
#
|
|
142
|
-
# @param working_dir [String, nil] the working directory
|
|
143
|
-
# path
|
|
144
|
-
#
|
|
145
|
-
# @param bare [Boolean] whether this is a bare repository
|
|
146
|
-
#
|
|
147
|
-
# @param bare_default [String, nil] for bare repos, used as the default when
|
|
148
|
-
# `path` is `nil`
|
|
165
|
+
# @param working_dir [String, nil] the working directory whose `.git` is the
|
|
166
|
+
# default when `path` is `nil`, or `nil` for a bare repository, whose
|
|
167
|
+
# default is the process working directory
|
|
149
168
|
#
|
|
150
169
|
# @return [String] the absolute path to the repository
|
|
151
170
|
#
|
|
171
|
+
# @raise [Git::Error] if the path cannot be expanded, which happens when the
|
|
172
|
+
# process working directory has been removed
|
|
173
|
+
#
|
|
152
174
|
# @api private
|
|
153
175
|
#
|
|
154
|
-
def resolve_repository(path, working_dir
|
|
155
|
-
initial_path =
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
176
|
+
def resolve_repository(path, working_dir)
|
|
177
|
+
initial_path =
|
|
178
|
+
if working_dir && path.nil?
|
|
179
|
+
File.join(working_dir, '.git')
|
|
180
|
+
else
|
|
181
|
+
expand_path(path, REPOSITORY_MESSAGE)
|
|
182
|
+
end
|
|
160
183
|
|
|
161
184
|
resolve_gitdir_pointer(initial_path)
|
|
162
185
|
end
|
|
@@ -171,6 +194,8 @@ module Git
|
|
|
171
194
|
#
|
|
172
195
|
# @return [String] the resolved absolute path
|
|
173
196
|
#
|
|
197
|
+
# @raise [Git::Error] if `path` is a file that cannot be read
|
|
198
|
+
#
|
|
174
199
|
# Relative pointer targets are resolved from the directory containing the
|
|
175
200
|
# pointer file itself, matching git's pointer-file semantics.
|
|
176
201
|
#
|
|
@@ -179,7 +204,7 @@ module Git
|
|
|
179
204
|
def resolve_gitdir_pointer(path)
|
|
180
205
|
return path unless File.file?(path)
|
|
181
206
|
|
|
182
|
-
gitdir_content = File.read(path).strip
|
|
207
|
+
gitdir_content = Git::SystemCallGuard.call('Failed to read the gitdir pointer file') { File.read(path) }.strip
|
|
183
208
|
return path unless gitdir_content.start_with?('gitdir: ')
|
|
184
209
|
|
|
185
210
|
gitdir_path = gitdir_content.sub(/\Agitdir: /, '')
|
|
@@ -191,16 +216,59 @@ module Git
|
|
|
191
216
|
#
|
|
192
217
|
# @param path [String, nil] the index path or `nil`
|
|
193
218
|
#
|
|
194
|
-
# @param repository [String] the repository directory
|
|
195
|
-
# path
|
|
219
|
+
# @param repository [String] the repository directory whose `index` is the
|
|
220
|
+
# default when `path` is `nil`
|
|
196
221
|
#
|
|
197
222
|
# @return [String] the absolute path to the index file
|
|
198
223
|
#
|
|
224
|
+
# @raise [Git::Error] if the path cannot be expanded, which happens when the
|
|
225
|
+
# process working directory has been removed
|
|
226
|
+
#
|
|
199
227
|
# @api private
|
|
200
228
|
#
|
|
201
229
|
def resolve_index(path, repository)
|
|
202
|
-
File.
|
|
230
|
+
return File.join(repository, 'index') unless path
|
|
231
|
+
|
|
232
|
+
expand_path(path, INDEX_MESSAGE)
|
|
203
233
|
end
|
|
204
234
|
private_class_method :resolve_index
|
|
235
|
+
|
|
236
|
+
# Expand a path the way a shell prompt would before git runs
|
|
237
|
+
#
|
|
238
|
+
# Follows `File.expand_path`: `~` is the home directory, a relative path is
|
|
239
|
+
# joined onto `base` (or onto the process working directory when `base` is
|
|
240
|
+
# `nil`), `.` and `..` are normalized, and an absolute path is unchanged.
|
|
241
|
+
#
|
|
242
|
+
# @example Expand a relative path against the process working directory
|
|
243
|
+
# Git::PathResolver.expand_path('repo', 'Failed to resolve the working directory')
|
|
244
|
+
# #=> '/current/dir/repo'
|
|
245
|
+
#
|
|
246
|
+
# @example Expand a relative path against another directory
|
|
247
|
+
# Git::PathResolver.expand_path('repo', 'Failed to resolve the working directory', base: '/output')
|
|
248
|
+
# #=> '/output/repo'
|
|
249
|
+
#
|
|
250
|
+
# @param path [String, Pathname, nil] the path to expand, or `nil` for
|
|
251
|
+
# `base` itself
|
|
252
|
+
#
|
|
253
|
+
# @param message [String] the message of the {Git::Error} raised when the
|
|
254
|
+
# expansion fails
|
|
255
|
+
#
|
|
256
|
+
# @param base [String, Pathname, nil] the directory a relative `path` is
|
|
257
|
+
# expanded against, or `nil` for the process working directory
|
|
258
|
+
#
|
|
259
|
+
# @return [String] the absolute path
|
|
260
|
+
#
|
|
261
|
+
# @raise [ArgumentError] if `~` cannot be expanded because no home directory
|
|
262
|
+
# can be determined, or if the path begins with `~user` for a user that
|
|
263
|
+
# does not exist
|
|
264
|
+
#
|
|
265
|
+
# @raise [Git::Error] if the path cannot be expanded, which happens when the
|
|
266
|
+
# process working directory has been removed
|
|
267
|
+
#
|
|
268
|
+
# @api private
|
|
269
|
+
#
|
|
270
|
+
def expand_path(path, message, base: nil)
|
|
271
|
+
Git::SystemCallGuard.call(message) { File.expand_path(path || '.', base) }
|
|
272
|
+
end
|
|
205
273
|
end
|
|
206
274
|
end
|