git 4.0.6 → 4.3.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/.github/copilot-instructions.md +2733 -0
- data/.github/pull_request_template.md +12 -3
- data/.github/workflows/continuous_integration.yml +2 -2
- data/.github/workflows/enforce_conventional_commits.yml +1 -0
- data/.github/workflows/release.yml +2 -1
- data/.release-please-manifest.json +1 -1
- data/.rubocop_todo.yml +1 -1
- data/.yardopts +4 -0
- data/AI_POLICY.md +24 -0
- data/CHANGELOG.md +75 -0
- data/CODE_OF_CONDUCT.md +25 -0
- data/CONTRIBUTING.md +35 -2
- data/GOVERNANCE.md +106 -0
- data/MAINTAINERS.md +17 -4
- data/README.md +433 -315
- data/git.gemspec +1 -0
- data/lib/git/args_builder.rb +8 -0
- data/lib/git/base.rb +138 -14
- data/lib/git/diff.rb +40 -2
- data/lib/git/diff_path_status.rb +1 -1
- data/lib/git/fsck_object.rb +48 -0
- data/lib/git/fsck_result.rb +121 -0
- data/lib/git/lib.rb +269 -17
- data/lib/git/log.rb +6 -1
- data/lib/git/path.rb +7 -1
- data/lib/git/version.rb +1 -1
- data/lib/git.rb +41 -0
- data/redesign/index.md +34 -0
- metadata +25 -4
data/lib/git/lib.rb
CHANGED
|
@@ -5,6 +5,7 @@ require_relative 'args_builder'
|
|
|
5
5
|
require 'git/command_line'
|
|
6
6
|
require 'git/errors'
|
|
7
7
|
require 'logger'
|
|
8
|
+
require 'pathname'
|
|
8
9
|
require 'pp'
|
|
9
10
|
require 'process_executer'
|
|
10
11
|
require 'stringio'
|
|
@@ -64,6 +65,7 @@ module Git
|
|
|
64
65
|
#
|
|
65
66
|
def initialize(base = nil, logger = nil)
|
|
66
67
|
@logger = logger || Logger.new(nil)
|
|
68
|
+
@git_ssh = :use_global_config
|
|
67
69
|
|
|
68
70
|
case base
|
|
69
71
|
when Git::Base
|
|
@@ -98,6 +100,21 @@ module Git
|
|
|
98
100
|
{ keys: [:filter], flag: '--filter', type: :valued_space },
|
|
99
101
|
{ keys: %i[remote origin], flag: '--origin', type: :valued_space },
|
|
100
102
|
{ keys: [:config], flag: '--config', type: :repeatable_valued_space },
|
|
103
|
+
{
|
|
104
|
+
keys: [:single_branch],
|
|
105
|
+
type: :custom,
|
|
106
|
+
validator: ->(value) { [nil, true, false].include?(value) },
|
|
107
|
+
builder: lambda do |value|
|
|
108
|
+
case value
|
|
109
|
+
when true
|
|
110
|
+
['--single-branch']
|
|
111
|
+
when false
|
|
112
|
+
['--no-single-branch']
|
|
113
|
+
else
|
|
114
|
+
[]
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
},
|
|
101
118
|
{
|
|
102
119
|
keys: [:depth],
|
|
103
120
|
type: :custom,
|
|
@@ -255,7 +272,8 @@ module Git
|
|
|
255
272
|
#
|
|
256
273
|
# Only :between or :object options can be used, not both.
|
|
257
274
|
#
|
|
258
|
-
# @option opts :path_limiter [Array<String
|
|
275
|
+
# @option opts :path_limiter [String, Pathname, Array<String, Pathname>] only
|
|
276
|
+
# include commits that impact files from the specified paths
|
|
259
277
|
#
|
|
260
278
|
# @return [Array<String>] the log output
|
|
261
279
|
#
|
|
@@ -310,7 +328,7 @@ module Git
|
|
|
310
328
|
#
|
|
311
329
|
# Only :between or :object options can be used, not both.
|
|
312
330
|
#
|
|
313
|
-
# @option opts :path_limiter [Array<String
|
|
331
|
+
# @option opts :path_limiter [String, Pathname, Array<String, Pathname>] only include commits that
|
|
314
332
|
# impact files from the specified paths
|
|
315
333
|
#
|
|
316
334
|
# @option opts :skip [Integer]
|
|
@@ -719,17 +737,17 @@ module Git
|
|
|
719
737
|
end
|
|
720
738
|
|
|
721
739
|
def worktree_add(dir, commitish = nil)
|
|
722
|
-
return
|
|
740
|
+
return worktree_command('worktree', 'add', dir, commitish) unless commitish.nil?
|
|
723
741
|
|
|
724
|
-
|
|
742
|
+
worktree_command('worktree', 'add', dir)
|
|
725
743
|
end
|
|
726
744
|
|
|
727
745
|
def worktree_remove(dir)
|
|
728
|
-
|
|
746
|
+
worktree_command('worktree', 'remove', dir)
|
|
729
747
|
end
|
|
730
748
|
|
|
731
749
|
def worktree_prune
|
|
732
|
-
|
|
750
|
+
worktree_command('worktree', 'prune')
|
|
733
751
|
end
|
|
734
752
|
|
|
735
753
|
def list_files(ref_dir)
|
|
@@ -824,6 +842,53 @@ module Git
|
|
|
824
842
|
raise ArgumentError, "Invalid #{arg_name}: '#{invalid_args.join("', '")}'"
|
|
825
843
|
end
|
|
826
844
|
|
|
845
|
+
# Normalizes path specifications for Git commands
|
|
846
|
+
#
|
|
847
|
+
# Converts a single path or array of paths into a consistent array format
|
|
848
|
+
# suitable for appending to Git command arguments after '--'. Empty strings
|
|
849
|
+
# are filtered out after conversion.
|
|
850
|
+
#
|
|
851
|
+
# @param pathspecs [String, Pathname, Array<String, Pathname>, nil] path(s) to normalize
|
|
852
|
+
# @param arg_name [String] name of the argument for error messages
|
|
853
|
+
# @return [Array<String>, nil] normalized array of path strings, or nil if empty/nil input
|
|
854
|
+
# @raise [ArgumentError] if any path is not a String or Pathname
|
|
855
|
+
#
|
|
856
|
+
def normalize_pathspecs(pathspecs, arg_name)
|
|
857
|
+
return nil unless pathspecs
|
|
858
|
+
|
|
859
|
+
normalized = Array(pathspecs)
|
|
860
|
+
validate_pathspec_types(normalized, arg_name)
|
|
861
|
+
|
|
862
|
+
normalized = normalized.map(&:to_s).reject(&:empty?)
|
|
863
|
+
return nil if normalized.empty?
|
|
864
|
+
|
|
865
|
+
normalized
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
# Validates that all pathspecs are String or Pathname objects
|
|
869
|
+
#
|
|
870
|
+
# @param pathspecs [Array] the pathspecs to validate
|
|
871
|
+
# @param arg_name [String] name of the argument for error messages
|
|
872
|
+
# @raise [ArgumentError] if any path is not a String or Pathname
|
|
873
|
+
#
|
|
874
|
+
def validate_pathspec_types(pathspecs, arg_name)
|
|
875
|
+
return if pathspecs.all? { |path| path.is_a?(String) || path.is_a?(Pathname) }
|
|
876
|
+
|
|
877
|
+
raise ArgumentError, "Invalid #{arg_name}: must be a String, Pathname, or Array of Strings/Pathnames"
|
|
878
|
+
end
|
|
879
|
+
|
|
880
|
+
# Handle deprecated :path option in favor of :path_limiter
|
|
881
|
+
def handle_deprecated_path_option(opts)
|
|
882
|
+
if opts.key?(:path_limiter)
|
|
883
|
+
opts[:path_limiter]
|
|
884
|
+
elsif opts.key?(:path)
|
|
885
|
+
Git::Deprecation.warn(
|
|
886
|
+
'Git::Lib#diff_path_status :path option is deprecated. Use :path_limiter instead.'
|
|
887
|
+
)
|
|
888
|
+
opts[:path]
|
|
889
|
+
end
|
|
890
|
+
end
|
|
891
|
+
|
|
827
892
|
DIFF_FULL_OPTION_MAP = [
|
|
828
893
|
{ type: :static, flag: '-p' },
|
|
829
894
|
{ keys: [:path_limiter], type: :validate_only }
|
|
@@ -836,8 +901,8 @@ module Git
|
|
|
836
901
|
args = build_args(opts, DIFF_FULL_OPTION_MAP)
|
|
837
902
|
args.push(obj1, obj2).compact!
|
|
838
903
|
|
|
839
|
-
if (
|
|
840
|
-
args.push('--',
|
|
904
|
+
if (pathspecs = normalize_pathspecs(opts[:path_limiter], 'path limiter'))
|
|
905
|
+
args.push('--', *pathspecs)
|
|
841
906
|
end
|
|
842
907
|
|
|
843
908
|
command('diff', *args)
|
|
@@ -855,8 +920,8 @@ module Git
|
|
|
855
920
|
args = build_args(opts, DIFF_STATS_OPTION_MAP)
|
|
856
921
|
args.push(obj1, obj2).compact!
|
|
857
922
|
|
|
858
|
-
if (
|
|
859
|
-
args.push('--',
|
|
923
|
+
if (pathspecs = normalize_pathspecs(opts[:path_limiter], 'path limiter'))
|
|
924
|
+
args.push('--', *pathspecs)
|
|
860
925
|
end
|
|
861
926
|
|
|
862
927
|
output_lines = command_lines('diff', *args)
|
|
@@ -865,6 +930,7 @@ module Git
|
|
|
865
930
|
|
|
866
931
|
DIFF_PATH_STATUS_OPTION_MAP = [
|
|
867
932
|
{ type: :static, flag: '--name-status' },
|
|
933
|
+
{ keys: [:path_limiter], type: :validate_only },
|
|
868
934
|
{ keys: [:path], type: :validate_only }
|
|
869
935
|
].freeze
|
|
870
936
|
|
|
@@ -874,7 +940,11 @@ module Git
|
|
|
874
940
|
|
|
875
941
|
args = build_args(opts, DIFF_PATH_STATUS_OPTION_MAP)
|
|
876
942
|
args.push(reference1, reference2).compact!
|
|
877
|
-
|
|
943
|
+
|
|
944
|
+
path_limiter = handle_deprecated_path_option(opts)
|
|
945
|
+
if (pathspecs = normalize_pathspecs(path_limiter, 'path limiter'))
|
|
946
|
+
args.push('--', *pathspecs)
|
|
947
|
+
end
|
|
878
948
|
|
|
879
949
|
parse_diff_path_status(args)
|
|
880
950
|
end
|
|
@@ -1319,6 +1389,20 @@ module Git
|
|
|
1319
1389
|
command('remote', *command_args)
|
|
1320
1390
|
end
|
|
1321
1391
|
|
|
1392
|
+
REMOTE_SET_BRANCHES_OPTION_MAP = [
|
|
1393
|
+
{ keys: [:add], flag: '--add', type: :boolean }
|
|
1394
|
+
].freeze
|
|
1395
|
+
|
|
1396
|
+
def remote_set_branches(name, branches, opts = {})
|
|
1397
|
+
ArgsBuilder.validate!(opts, REMOTE_SET_BRANCHES_OPTION_MAP)
|
|
1398
|
+
|
|
1399
|
+
flags = build_args(opts, REMOTE_SET_BRANCHES_OPTION_MAP)
|
|
1400
|
+
branch_args = Array(branches).flatten
|
|
1401
|
+
command_args = ['set-branches'] + flags + [name] + branch_args
|
|
1402
|
+
|
|
1403
|
+
command('remote', *command_args)
|
|
1404
|
+
end
|
|
1405
|
+
|
|
1322
1406
|
def remote_set_url(name, url)
|
|
1323
1407
|
arr_opts = ['set-url']
|
|
1324
1408
|
arr_opts << name
|
|
@@ -1446,6 +1530,38 @@ module Git
|
|
|
1446
1530
|
command('gc', '--prune', '--aggressive', '--auto')
|
|
1447
1531
|
end
|
|
1448
1532
|
|
|
1533
|
+
FSCK_OPTION_MAP = [
|
|
1534
|
+
{ flag: '--no-progress', type: :static },
|
|
1535
|
+
{ keys: [:unreachable], flag: '--unreachable', type: :boolean },
|
|
1536
|
+
{ keys: [:strict], flag: '--strict', type: :boolean },
|
|
1537
|
+
{ keys: [:connectivity_only], flag: '--connectivity-only', type: :boolean },
|
|
1538
|
+
{ keys: [:root], flag: '--root', type: :boolean },
|
|
1539
|
+
{ keys: [:tags], flag: '--tags', type: :boolean },
|
|
1540
|
+
{ keys: [:cache], flag: '--cache', type: :boolean },
|
|
1541
|
+
{ keys: [:no_reflogs], flag: '--no-reflogs', type: :boolean },
|
|
1542
|
+
{ keys: [:lost_found], flag: '--lost-found', type: :boolean },
|
|
1543
|
+
{ keys: [:dangling], flag: '--dangling', type: :boolean_negatable },
|
|
1544
|
+
{ keys: [:full], flag: '--full', type: :boolean_negatable },
|
|
1545
|
+
{ keys: [:name_objects], flag: '--name-objects', type: :boolean_negatable },
|
|
1546
|
+
{ keys: [:references], flag: '--references', type: :boolean_negatable }
|
|
1547
|
+
].freeze
|
|
1548
|
+
|
|
1549
|
+
def fsck(*objects, **opts)
|
|
1550
|
+
args = ArgsBuilder.build(opts, FSCK_OPTION_MAP)
|
|
1551
|
+
args.concat(objects) unless objects.empty?
|
|
1552
|
+
# fsck returns non-zero exit status when issues are found:
|
|
1553
|
+
# 1 = errors found, 2 = missing objects, 4 = warnings
|
|
1554
|
+
# We still want to parse the output in these cases
|
|
1555
|
+
output = begin
|
|
1556
|
+
command('fsck', *args)
|
|
1557
|
+
rescue Git::FailedError => e
|
|
1558
|
+
raise unless [1, 2, 4].include?(e.result.status.exitstatus)
|
|
1559
|
+
|
|
1560
|
+
e.result.stdout
|
|
1561
|
+
end
|
|
1562
|
+
parse_fsck_output(output)
|
|
1563
|
+
end
|
|
1564
|
+
|
|
1449
1565
|
READ_TREE_OPTION_MAP = [
|
|
1450
1566
|
{ keys: [:prefix], flag: '--prefix', type: :valued_equals }
|
|
1451
1567
|
].freeze
|
|
@@ -1600,8 +1716,52 @@ module Git
|
|
|
1600
1716
|
{ keys: [:between], type: :custom, builder: ->(value) { "#{value[0]}..#{value[1]}" if value } }
|
|
1601
1717
|
].freeze
|
|
1602
1718
|
|
|
1719
|
+
FSCK_OBJECT_PATTERN = /\A(dangling|missing|unreachable) (\w+) ([0-9a-f]{40})(?: \((.+)\))?\z/
|
|
1720
|
+
FSCK_WARNING_PATTERN = /\Awarning in (\w+) ([0-9a-f]{40}): (.+)\z/
|
|
1721
|
+
FSCK_ROOT_PATTERN = /\Aroot ([0-9a-f]{40})\z/
|
|
1722
|
+
FSCK_TAGGED_PATTERN = /\Atagged (\w+) ([0-9a-f]{40}) \((.+)\) in ([0-9a-f]{40})\z/
|
|
1723
|
+
|
|
1724
|
+
private_constant :FSCK_OBJECT_PATTERN, :FSCK_WARNING_PATTERN, :FSCK_ROOT_PATTERN, :FSCK_TAGGED_PATTERN
|
|
1725
|
+
|
|
1603
1726
|
private
|
|
1604
1727
|
|
|
1728
|
+
def parse_fsck_output(output)
|
|
1729
|
+
result = { dangling: [], missing: [], unreachable: [], warnings: [], root: [], tagged: [] }
|
|
1730
|
+
output.each_line { |line| parse_fsck_line(line.strip, result) }
|
|
1731
|
+
Git::FsckResult.new(**result)
|
|
1732
|
+
end
|
|
1733
|
+
|
|
1734
|
+
def parse_fsck_line(line, result)
|
|
1735
|
+
parse_fsck_object_line(line, result) ||
|
|
1736
|
+
parse_fsck_warning_line(line, result) ||
|
|
1737
|
+
parse_fsck_root_line(line, result) ||
|
|
1738
|
+
parse_fsck_tagged_line(line, result)
|
|
1739
|
+
end
|
|
1740
|
+
|
|
1741
|
+
def parse_fsck_object_line(line, result)
|
|
1742
|
+
return unless (match = FSCK_OBJECT_PATTERN.match(line))
|
|
1743
|
+
|
|
1744
|
+
result[match[1].to_sym] << Git::FsckObject.new(type: match[2].to_sym, sha: match[3], name: match[4])
|
|
1745
|
+
end
|
|
1746
|
+
|
|
1747
|
+
def parse_fsck_warning_line(line, result)
|
|
1748
|
+
return unless (match = FSCK_WARNING_PATTERN.match(line))
|
|
1749
|
+
|
|
1750
|
+
result[:warnings] << Git::FsckObject.new(type: match[1].to_sym, sha: match[2], message: match[3])
|
|
1751
|
+
end
|
|
1752
|
+
|
|
1753
|
+
def parse_fsck_root_line(line, result)
|
|
1754
|
+
return unless (match = FSCK_ROOT_PATTERN.match(line))
|
|
1755
|
+
|
|
1756
|
+
result[:root] << Git::FsckObject.new(type: :commit, sha: match[1])
|
|
1757
|
+
end
|
|
1758
|
+
|
|
1759
|
+
def parse_fsck_tagged_line(line, result)
|
|
1760
|
+
return unless (match = FSCK_TAGGED_PATTERN.match(line))
|
|
1761
|
+
|
|
1762
|
+
result[:tagged] << Git::FsckObject.new(type: match[1].to_sym, sha: match[2], name: match[3])
|
|
1763
|
+
end
|
|
1764
|
+
|
|
1605
1765
|
def parse_diff_path_status(args)
|
|
1606
1766
|
command_lines('diff', *args).each_with_object({}) do |line, memo|
|
|
1607
1767
|
status, path = split_status_line(line)
|
|
@@ -1625,15 +1785,17 @@ module Git
|
|
|
1625
1785
|
end
|
|
1626
1786
|
|
|
1627
1787
|
def initialize_from_base(base_object)
|
|
1628
|
-
@git_dir = base_object.repo.
|
|
1629
|
-
@git_index_file = base_object.index&.
|
|
1630
|
-
@git_work_dir = base_object.dir&.
|
|
1788
|
+
@git_dir = base_object.repo.to_s
|
|
1789
|
+
@git_index_file = base_object.index&.to_s
|
|
1790
|
+
@git_work_dir = base_object.dir&.to_s
|
|
1791
|
+
@git_ssh = base_object.git_ssh
|
|
1631
1792
|
end
|
|
1632
1793
|
|
|
1633
1794
|
def initialize_from_hash(base_hash)
|
|
1634
1795
|
@git_dir = base_hash[:repository]
|
|
1635
1796
|
@git_index_file = base_hash[:index]
|
|
1636
1797
|
@git_work_dir = base_hash[:working_directory]
|
|
1798
|
+
@git_ssh = base_hash.key?(:git_ssh) ? base_hash[:git_ssh] : :use_global_config
|
|
1637
1799
|
end
|
|
1638
1800
|
|
|
1639
1801
|
def return_base_opts_from_clone(clone_dir, opts)
|
|
@@ -1641,6 +1803,7 @@ module Git
|
|
|
1641
1803
|
base_opts[:repository] = clone_dir if opts[:bare] || opts[:mirror]
|
|
1642
1804
|
base_opts[:working_directory] = clone_dir unless opts[:bare] || opts[:mirror]
|
|
1643
1805
|
base_opts[:log] = opts[:log] if opts[:log]
|
|
1806
|
+
base_opts[:git_ssh] = opts[:git_ssh] if opts.key?(:git_ssh)
|
|
1644
1807
|
base_opts
|
|
1645
1808
|
end
|
|
1646
1809
|
|
|
@@ -1883,14 +2046,63 @@ module Git
|
|
|
1883
2046
|
op.split("\n")
|
|
1884
2047
|
end
|
|
1885
2048
|
|
|
1886
|
-
|
|
2049
|
+
# Returns a hash of environment variable overrides for git commands
|
|
2050
|
+
#
|
|
2051
|
+
# This method builds a hash of environment variables that control git's behavior,
|
|
2052
|
+
# such as the git directory, working tree, and index file locations.
|
|
2053
|
+
#
|
|
2054
|
+
# @param additional_overrides [Hash] additional environment variables to set or unset
|
|
2055
|
+
#
|
|
2056
|
+
# Keys should be environment variable names (String) and values should be either:
|
|
2057
|
+
# * A String value to set the environment variable
|
|
2058
|
+
# * `nil` to unset the environment variable
|
|
2059
|
+
#
|
|
2060
|
+
# Per Process.spawn semantics, setting a key to `nil` will unset that environment
|
|
2061
|
+
# variable, removing it from the environment passed to the git command.
|
|
2062
|
+
#
|
|
2063
|
+
# @return [Hash<String, String|nil>] environment variable overrides
|
|
2064
|
+
#
|
|
2065
|
+
# @example Basic usage with default environment variables
|
|
2066
|
+
# env_overrides
|
|
2067
|
+
# # => { 'GIT_DIR' => '/path/to/.git', 'GIT_WORK_TREE' => '/path/to/worktree', ... }
|
|
2068
|
+
#
|
|
2069
|
+
# @example Adding a custom environment variable
|
|
2070
|
+
# env_overrides('GIT_TRACE' => '1')
|
|
2071
|
+
# # => { 'GIT_DIR' => '/path/to/.git', ..., 'GIT_TRACE' => '1' }
|
|
2072
|
+
#
|
|
2073
|
+
# @example Unsetting an environment variable (used by worktree_command_line)
|
|
2074
|
+
# env_overrides('GIT_INDEX_FILE' => nil)
|
|
2075
|
+
# # => { 'GIT_DIR' => '/path/to/.git', 'GIT_WORK_TREE' => '/path/to/worktree',
|
|
2076
|
+
# # 'GIT_INDEX_FILE' => nil, 'GIT_SSH' => <git_ssh_value>, 'LC_ALL' => 'en_US.UTF-8' }
|
|
2077
|
+
# # When passed to Process.spawn, GIT_INDEX_FILE will be unset in the environment
|
|
2078
|
+
#
|
|
2079
|
+
# @see https://ruby-doc.org/core/Process.html#method-c-spawn Process.spawn
|
|
2080
|
+
#
|
|
2081
|
+
# @api private
|
|
2082
|
+
#
|
|
2083
|
+
def env_overrides(**additional_overrides)
|
|
1887
2084
|
{
|
|
1888
2085
|
'GIT_DIR' => @git_dir,
|
|
1889
2086
|
'GIT_WORK_TREE' => @git_work_dir,
|
|
1890
2087
|
'GIT_INDEX_FILE' => @git_index_file,
|
|
1891
|
-
'GIT_SSH' =>
|
|
2088
|
+
'GIT_SSH' => resolved_git_ssh,
|
|
1892
2089
|
'LC_ALL' => 'en_US.UTF-8'
|
|
1893
|
-
}
|
|
2090
|
+
}.merge(additional_overrides)
|
|
2091
|
+
end
|
|
2092
|
+
|
|
2093
|
+
# Resolve the git_ssh value to use for this instance
|
|
2094
|
+
#
|
|
2095
|
+
# @return [String, nil] the resolved git_ssh value
|
|
2096
|
+
#
|
|
2097
|
+
# Returns the global config value if @git_ssh is the sentinel :use_global_config,
|
|
2098
|
+
# otherwise returns @git_ssh (which may be nil or a string)
|
|
2099
|
+
#
|
|
2100
|
+
# @api private
|
|
2101
|
+
#
|
|
2102
|
+
def resolved_git_ssh
|
|
2103
|
+
return Git::Base.config.git_ssh if @git_ssh == :use_global_config
|
|
2104
|
+
|
|
2105
|
+
@git_ssh
|
|
1894
2106
|
end
|
|
1895
2107
|
|
|
1896
2108
|
def global_opts
|
|
@@ -1906,6 +2118,46 @@ module Git
|
|
|
1906
2118
|
Git::CommandLine.new(env_overrides, Git::Base.config.binary_path, global_opts, @logger)
|
|
1907
2119
|
end
|
|
1908
2120
|
|
|
2121
|
+
# Returns a command line instance without GIT_INDEX_FILE for worktree commands
|
|
2122
|
+
#
|
|
2123
|
+
# Git worktrees manage their own index files and setting GIT_INDEX_FILE
|
|
2124
|
+
# causes corruption of both the main worktree and new worktree indexes.
|
|
2125
|
+
#
|
|
2126
|
+
# @return [Git::CommandLine]
|
|
2127
|
+
# @api private
|
|
2128
|
+
#
|
|
2129
|
+
def worktree_command_line
|
|
2130
|
+
@worktree_command_line ||=
|
|
2131
|
+
Git::CommandLine.new(env_overrides('GIT_INDEX_FILE' => nil), Git::Base.config.binary_path, global_opts,
|
|
2132
|
+
@logger)
|
|
2133
|
+
end
|
|
2134
|
+
|
|
2135
|
+
# @overload worktree_command(*args, **options_hash)
|
|
2136
|
+
# Runs a git worktree command and returns the output
|
|
2137
|
+
#
|
|
2138
|
+
# This method is similar to #command but uses a command line instance
|
|
2139
|
+
# that excludes GIT_INDEX_FILE from the environment to prevent index corruption.
|
|
2140
|
+
#
|
|
2141
|
+
# @param args [Array<String>] the command arguments
|
|
2142
|
+
# @param options_hash [Hash] the options to pass to the command
|
|
2143
|
+
#
|
|
2144
|
+
# @return [String] the command's stdout
|
|
2145
|
+
#
|
|
2146
|
+
# @see #command
|
|
2147
|
+
#
|
|
2148
|
+
# @api private
|
|
2149
|
+
#
|
|
2150
|
+
def worktree_command(*, **options_hash)
|
|
2151
|
+
options_hash = COMMAND_ARG_DEFAULTS.merge(options_hash)
|
|
2152
|
+
options_hash[:timeout] ||= Git.config.timeout
|
|
2153
|
+
|
|
2154
|
+
extra_options = options_hash.keys - COMMAND_ARG_DEFAULTS.keys
|
|
2155
|
+
raise ArgumentError, "Unknown options: #{extra_options.join(', ')}" if extra_options.any?
|
|
2156
|
+
|
|
2157
|
+
result = worktree_command_line.run(*, **options_hash)
|
|
2158
|
+
result.stdout
|
|
2159
|
+
end
|
|
2160
|
+
|
|
1909
2161
|
# Runs a git command and returns the output
|
|
1910
2162
|
#
|
|
1911
2163
|
# Additional args are passed to the command line. They should exclude the 'git'
|
data/lib/git/log.rb
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Git
|
|
4
|
-
# Builds and executes a `git log` query
|
|
4
|
+
# Builds and executes a `git log` query
|
|
5
5
|
#
|
|
6
6
|
# This class provides a fluent interface for building complex `git log` queries.
|
|
7
|
+
#
|
|
8
|
+
# Queries default to returning 30 commits; call {#max_count} with `:all` to
|
|
9
|
+
# return every matching commit. Calling {#all} adds the `--all` flag to include
|
|
10
|
+
# all refs in the search but does not change the number of commits returned.
|
|
11
|
+
#
|
|
7
12
|
# The query is lazily executed when results are requested either via the modern
|
|
8
13
|
# `#execute` method or the deprecated Enumerable methods.
|
|
9
14
|
#
|
data/lib/git/path.rb
CHANGED
|
@@ -7,7 +7,13 @@ module Git
|
|
|
7
7
|
# directory or index file.
|
|
8
8
|
#
|
|
9
9
|
class Path
|
|
10
|
-
|
|
10
|
+
def path
|
|
11
|
+
Git::Deprecation.warn(
|
|
12
|
+
'The .path accessor is deprecated and will be removed in v5.0. ' \
|
|
13
|
+
'Use .to_s instead.'
|
|
14
|
+
)
|
|
15
|
+
@path
|
|
16
|
+
end
|
|
11
17
|
|
|
12
18
|
def initialize(path, must_exist: true)
|
|
13
19
|
path = File.expand_path(path)
|
data/lib/git/version.rb
CHANGED
data/lib/git.rb
CHANGED
|
@@ -18,6 +18,8 @@ require 'git/diff'
|
|
|
18
18
|
require 'git/encoding_utils'
|
|
19
19
|
require 'git/errors'
|
|
20
20
|
require 'git/escaped_path'
|
|
21
|
+
require 'git/fsck_object'
|
|
22
|
+
require 'git/fsck_result'
|
|
21
23
|
require 'git/index'
|
|
22
24
|
require 'git/lib'
|
|
23
25
|
require 'git/log'
|
|
@@ -93,6 +95,12 @@ module Git
|
|
|
93
95
|
# @param [Hash] options The options for this command (see list of valid
|
|
94
96
|
# options below)
|
|
95
97
|
#
|
|
98
|
+
# @option options [String, nil] :git_ssh An optional custom SSH command
|
|
99
|
+
#
|
|
100
|
+
# - If not specified, uses the global config (Git.configure { |c| c.git_ssh = ... }).
|
|
101
|
+
# - If nil, disables SSH for this instance.
|
|
102
|
+
# - If a non-empty string, uses that value for this instance.
|
|
103
|
+
#
|
|
96
104
|
# @option options [Logger] :log A logger to use for Git operations. Git commands
|
|
97
105
|
# are logged at the `:info` level. Additional logging is done at the `:debug`
|
|
98
106
|
# level.
|
|
@@ -145,6 +153,20 @@ module Git
|
|
|
145
153
|
# @option options [String] :filter Request that the server send a partial
|
|
146
154
|
# clone according to the given filter
|
|
147
155
|
#
|
|
156
|
+
# @option options [Boolean, nil] :single_branch Control whether the clone
|
|
157
|
+
# limits fetch refspecs to a single branch.
|
|
158
|
+
# - If nil (default), no flag is passed and the Git default is used.
|
|
159
|
+
# - If true, `--single-branch` is passed to limit the refspec to the
|
|
160
|
+
# checkout branch.
|
|
161
|
+
# - If false, `--no-single-branch` is passed to broaden the refspec (useful
|
|
162
|
+
# for shallow clones that should include all branches).
|
|
163
|
+
#
|
|
164
|
+
# @option options [String, nil] :git_ssh An optional custom SSH command
|
|
165
|
+
#
|
|
166
|
+
# - If not specified, uses the global config (Git.configure { |c| c.git_ssh = ... }).
|
|
167
|
+
# - If nil, disables SSH for this instance.
|
|
168
|
+
# - If a non-empty string, uses that value for this instance.
|
|
169
|
+
#
|
|
148
170
|
# @option options [Logger] :log A logger to use for Git operations. Git
|
|
149
171
|
# commands are logged at the `:info` level. Additional logging is done
|
|
150
172
|
# at the `:debug` level.
|
|
@@ -187,6 +209,13 @@ module Git
|
|
|
187
209
|
# config: ['user.name=John Doe', 'user.email=john@doe.com']
|
|
188
210
|
# )
|
|
189
211
|
#
|
|
212
|
+
# @example Clone using a specific SSH key
|
|
213
|
+
# git = Git.clone(
|
|
214
|
+
# 'git@github.com:ruby-git/ruby-git.git',
|
|
215
|
+
# 'local-dir',
|
|
216
|
+
# git_ssh: 'ssh -i /path/to/private_key'
|
|
217
|
+
# )
|
|
218
|
+
#
|
|
190
219
|
# @return [Git::Base] an object that can execute git commands in the context
|
|
191
220
|
# of the cloned local working copy or cloned repository.
|
|
192
221
|
#
|
|
@@ -300,6 +329,12 @@ module Git
|
|
|
300
329
|
# and converted to an absolute path using
|
|
301
330
|
# [File.expand_path](https://www.rubydoc.info/stdlib/core/File.expand_path).
|
|
302
331
|
#
|
|
332
|
+
# @option options [String, nil] :git_ssh An optional custom SSH command
|
|
333
|
+
#
|
|
334
|
+
# - If not specified, uses the global config (Git.configure { |c| c.git_ssh = ... }).
|
|
335
|
+
# - If nil, disables SSH for this instance.
|
|
336
|
+
# - If a non-empty string, uses that value for this instance.
|
|
337
|
+
#
|
|
303
338
|
# @option options [Logger] :log A logger to use for Git operations. Git
|
|
304
339
|
# commands are logged at the `:info` level. Additional logging is done
|
|
305
340
|
# at the `:debug` level.
|
|
@@ -374,6 +409,12 @@ module Git
|
|
|
374
409
|
# @option options [Pathname] :index used to specify a non-standard path to an
|
|
375
410
|
# index file. The default is `"#{working_dir}/.git/index"`
|
|
376
411
|
#
|
|
412
|
+
# @option options [String, nil] :git_ssh An optional custom SSH command
|
|
413
|
+
#
|
|
414
|
+
# - If not specified, uses the global config (Git.configure { |c| c.git_ssh = ... }).
|
|
415
|
+
# - If nil, disables SSH for this instance.
|
|
416
|
+
# - If a non-empty string, uses that value for this instance.
|
|
417
|
+
#
|
|
377
418
|
# @option options [Logger] :log A logger to use for Git operations. Git
|
|
378
419
|
# commands are logged at the `:info` level. Additional logging is done
|
|
379
420
|
# at the `:debug` level.
|
data/redesign/index.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Architectural Redesign Project
|
|
2
|
+
|
|
3
|
+
[This project was announced in the project's README](../README.md#2025-07-09-architectural-redesign)
|
|
4
|
+
|
|
5
|
+
The git gem is undergoing a significant architectural redesign for the upcoming
|
|
6
|
+
v5.0.0 release. The current architecture has several design challenges that make it
|
|
7
|
+
difficult to maintain and evolve. This redesign aims to address these issues by
|
|
8
|
+
introducing a clearer, more robust, and more testable structure.
|
|
9
|
+
|
|
10
|
+
We have prepared detailed documents outlining the analysis of the current
|
|
11
|
+
architecture and the proposed changes. We encourage our community and contributors to
|
|
12
|
+
review them:
|
|
13
|
+
|
|
14
|
+
1. [Analysis of the Current Architecture](1_architecture_existing.md): A
|
|
15
|
+
breakdown of the existing design and its challenges.
|
|
16
|
+
2. [The Proposed Redesign](2_architecture_redesign.md): An overview of the
|
|
17
|
+
new three-layered architecture.
|
|
18
|
+
3. [Implementation Plan](3_architecture_implementation.md): The step-by-step
|
|
19
|
+
plan for implementing the redesign.
|
|
20
|
+
|
|
21
|
+
Your feedback is welcome! Please feel free to open an issue to discuss the proposed
|
|
22
|
+
changes.
|
|
23
|
+
|
|
24
|
+
> **DON'T PANIC!**
|
|
25
|
+
>
|
|
26
|
+
> While this is a major internal refactoring, our goal is to keep the primary public
|
|
27
|
+
API on the main repository object as stable as possible. Most users who rely on
|
|
28
|
+
documented methods like `g.commit`, `g.add`, and `g.status` should find the
|
|
29
|
+
transition to v5.0.0 straightforward.
|
|
30
|
+
>
|
|
31
|
+
> The breaking changes will primarily affect users who have been relying on the
|
|
32
|
+
internal g.lib accessor, which will be removed as part of this cleanup. For more
|
|
33
|
+
details, please see the "Impact on Users" section in [the redesign
|
|
34
|
+
> document](2_architecture_redesign.md).
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: git
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.0
|
|
4
|
+
version: 4.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Scott Chacon and others
|
|
@@ -163,6 +163,20 @@ dependencies:
|
|
|
163
163
|
- - "~>"
|
|
164
164
|
- !ruby/object:Gem::Version
|
|
165
165
|
version: '3.6'
|
|
166
|
+
- !ruby/object:Gem::Dependency
|
|
167
|
+
name: irb
|
|
168
|
+
requirement: !ruby/object:Gem::Requirement
|
|
169
|
+
requirements:
|
|
170
|
+
- - "~>"
|
|
171
|
+
- !ruby/object:Gem::Version
|
|
172
|
+
version: '1.6'
|
|
173
|
+
type: :development
|
|
174
|
+
prerelease: false
|
|
175
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
176
|
+
requirements:
|
|
177
|
+
- - "~>"
|
|
178
|
+
- !ruby/object:Gem::Version
|
|
179
|
+
version: '1.6'
|
|
166
180
|
- !ruby/object:Gem::Dependency
|
|
167
181
|
name: redcarpet
|
|
168
182
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -223,6 +237,7 @@ extensions: []
|
|
|
223
237
|
extra_rdoc_files: []
|
|
224
238
|
files:
|
|
225
239
|
- ".commitlintrc.yml"
|
|
240
|
+
- ".github/copilot-instructions.md"
|
|
226
241
|
- ".github/issue_template.md"
|
|
227
242
|
- ".github/pull_request_template.md"
|
|
228
243
|
- ".github/workflows/continuous_integration.yml"
|
|
@@ -235,8 +250,11 @@ files:
|
|
|
235
250
|
- ".rubocop.yml"
|
|
236
251
|
- ".rubocop_todo.yml"
|
|
237
252
|
- ".yardopts"
|
|
253
|
+
- AI_POLICY.md
|
|
238
254
|
- CHANGELOG.md
|
|
255
|
+
- CODE_OF_CONDUCT.md
|
|
239
256
|
- CONTRIBUTING.md
|
|
257
|
+
- GOVERNANCE.md
|
|
240
258
|
- Gemfile
|
|
241
259
|
- LICENSE
|
|
242
260
|
- MAINTAINERS.md
|
|
@@ -258,6 +276,8 @@ files:
|
|
|
258
276
|
- lib/git/encoding_utils.rb
|
|
259
277
|
- lib/git/errors.rb
|
|
260
278
|
- lib/git/escaped_path.rb
|
|
279
|
+
- lib/git/fsck_object.rb
|
|
280
|
+
- lib/git/fsck_result.rb
|
|
261
281
|
- lib/git/index.rb
|
|
262
282
|
- lib/git/lib.rb
|
|
263
283
|
- lib/git/log.rb
|
|
@@ -277,6 +297,7 @@ files:
|
|
|
277
297
|
- redesign/1_architecture_existing.md
|
|
278
298
|
- redesign/2_architecture_redesign.md
|
|
279
299
|
- redesign/3_architecture_implementation.md
|
|
300
|
+
- redesign/index.md
|
|
280
301
|
- release-please-config.json
|
|
281
302
|
- tasks/gem_tasks.rake
|
|
282
303
|
- tasks/rubocop.rake
|
|
@@ -289,8 +310,8 @@ licenses:
|
|
|
289
310
|
metadata:
|
|
290
311
|
homepage_uri: http://github.com/ruby-git/ruby-git
|
|
291
312
|
source_code_uri: http://github.com/ruby-git/ruby-git
|
|
292
|
-
changelog_uri: https://rubydoc.info/gems/git/4.0
|
|
293
|
-
documentation_uri: https://rubydoc.info/gems/git/4.0
|
|
313
|
+
changelog_uri: https://rubydoc.info/gems/git/4.3.0/file/CHANGELOG.md
|
|
314
|
+
documentation_uri: https://rubydoc.info/gems/git/4.3.0
|
|
294
315
|
rubygems_mfa_required: 'true'
|
|
295
316
|
rdoc_options: []
|
|
296
317
|
require_paths:
|
|
@@ -307,7 +328,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
307
328
|
version: '0'
|
|
308
329
|
requirements:
|
|
309
330
|
- git 2.28.0 or greater
|
|
310
|
-
rubygems_version:
|
|
331
|
+
rubygems_version: 4.0.3
|
|
311
332
|
specification_version: 4
|
|
312
333
|
summary: An API to create, read, and manipulate Git repositories
|
|
313
334
|
test_files: []
|