git 4.1.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 547a803a734580e23f5e1f6910f54c2437bcb165331d1d2d9048a2fb2ff1180f
4
- data.tar.gz: 9532b6e51177204f789aab40b2d551d8886abf524f50f9366a60045f00e2cc7f
3
+ metadata.gz: e100c7e550894cd173db6f141a4a79963d1ccb832d028a459cf7e4193ae968ec
4
+ data.tar.gz: 9275a29a6a88c441e44084a9ba26dbbd0d40889784225635d410965dafaea2af
5
5
  SHA512:
6
- metadata.gz: 31f1da3bc8b566f4990efd1651ed59202eabd424722693e0fae1b41ec194d4bbd2f7c613ed7552035b6ca42b6d501459b0d9d323bd188957ed5291675ff506de
7
- data.tar.gz: eb4a424bdfbc1fcda445e880b8497ff714fe2c575591a5c48ee8c37f36e721f38474dd409c0af85fddbd98570a8ac7e5e5ba8c4e6c994fce46108e9f349f8137
6
+ metadata.gz: e29086daa9c6089c6e7f1e4b889aae7cac9e1cd199937f3e8eb44e5b59dccaa3c484d85dab56f2da07172161b6e2f87269b78e5aab868b947a2f161e7c9de977
7
+ data.tar.gz: f09a9f3a7e7e9cdc218016737e2dc444392351191f456b6ace8dd32088c17517b43de0f93b6f6925e0d9ec2a8e8be9866d4b35b28420ebc7f6e43adc889e4bab
@@ -39,6 +39,7 @@ jobs:
39
39
  token: ${{ secrets.AUTO_RELEASE_TOKEN }}
40
40
  config-file: release-please-config.json
41
41
  manifest-file: .release-please-manifest.json
42
+ target-branch: ${{ github.ref_name }}
42
43
 
43
44
  - name: Setup ruby
44
45
  uses: ruby/setup-ruby@v1
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "4.1.2"
2
+ ".": "4.3.0"
3
3
  }
data/.rubocop_todo.yml CHANGED
@@ -9,4 +9,4 @@
9
9
  # Offense count: 2
10
10
  # Configuration parameters: CountComments, CountAsOne.
11
11
  Metrics/ClassLength:
12
- Max: 1150
12
+ Max: 1160
data/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@
5
5
 
6
6
  # Change Log
7
7
 
8
+ ## [4.3.0](https://github.com/ruby-git/ruby-git/compare/v4.2.0...v4.3.0) (2026-01-15)
9
+
10
+
11
+ ### Features
12
+
13
+ * Add support for git fsck command ([c402003](https://github.com/ruby-git/ruby-git/commit/c402003ce0c626a5c4737838cddc4b932638c172)), closes [#218](https://github.com/ruby-git/ruby-git/issues/218)
14
+
15
+ ## [4.2.0](https://github.com/ruby-git/ruby-git/compare/v4.1.2...v4.2.0) (2026-01-11)
16
+
17
+
18
+ ### Features
19
+
20
+ * Add deprecation warning for .path accessor ([8af4543](https://github.com/ruby-git/ruby-git/commit/8af4543a25a46bf40ed7a05b31cea1a5b17e3f0b))
21
+
22
+
23
+ ### Other Changes
24
+
25
+ * Add target-branch to release-please action ([13e041e](https://github.com/ruby-git/ruby-git/commit/13e041ebb76587b90549896a4c9b9b872f60696a))
26
+ * Set 4.x manifest to start from 4.1.1 ([1ccee62](https://github.com/ruby-git/ruby-git/commit/1ccee62cdd832a6de2307b0a61ceb593548e7fe1))
27
+
8
28
  ## [4.1.2](https://github.com/ruby-git/ruby-git/compare/v4.1.1...v4.1.2) (2026-01-10)
9
29
 
10
30
 
data/README.md CHANGED
@@ -277,6 +277,20 @@ repo.worktrees.each do |worktree|
277
277
  worktree.to_s
278
278
  end
279
279
 
280
+ # Check repository integrity with fsck
281
+ result = repo.fsck
282
+ result.dangling.each { |obj| puts "dangling #{obj.type}: #{obj.sha}" }
283
+ result.missing.each { |obj| puts "missing #{obj.type}: #{obj.sha}" }
284
+
285
+ # Check if repository has any issues
286
+ puts "Repository is clean" if result.empty?
287
+
288
+ # fsck with options
289
+ result = repo.fsck(unreachable: true, strict: true)
290
+
291
+ # Suppress dangling object output
292
+ result = repo.fsck(dangling: false)
293
+
280
294
  repo.config('user.name') # returns 'Scott Chacon'
281
295
  repo.config # returns whole config hash
282
296
 
@@ -12,6 +12,14 @@ module Git
12
12
  ARG_BUILDERS = {
13
13
  boolean: ->(config, value) { value ? config[:flag] : [] },
14
14
 
15
+ boolean_negatable: lambda do |config, value|
16
+ case value
17
+ when true then config[:flag]
18
+ when false then config[:flag].sub('--', '--no-')
19
+ else []
20
+ end
21
+ end,
22
+
15
23
  valued_equals: ->(config, value) { "#{config[:flag]}=#{value}" if value },
16
24
 
17
25
  valued_space: ->(config, value) { [config[:flag], value.to_s] if value },
data/lib/git/base.rb CHANGED
@@ -195,7 +195,7 @@ module Git
195
195
  # :fetch => true
196
196
  # :track => <branch_name>
197
197
  def add_remote(name, url, opts = {})
198
- url = url.repo.path if url.is_a?(Git::Base)
198
+ url = url.repo.to_s if url.is_a?(Git::Base)
199
199
  lib.remote_add(name, url, opts)
200
200
  Git::Remote.new(self, name)
201
201
  end
@@ -210,8 +210,8 @@ module Git
210
210
  # @git.commit('message')
211
211
  # end
212
212
  def chdir # :yields: the Git::Path
213
- Dir.chdir(dir.path) do
214
- yield dir.path
213
+ Dir.chdir(dir.to_s) do
214
+ yield dir.to_s
215
215
  end
216
216
  end
217
217
 
@@ -558,7 +558,7 @@ module Git
558
558
  # @git.set_remote_url('scotts_git', 'git://repo.or.cz/rubygit.git')
559
559
  #
560
560
  def set_remote_url(name, url)
561
- url = url.repo.path if url.is_a?(Git::Base)
561
+ url = url.repo.to_s if url.is_a?(Git::Base)
562
562
  lib.remote_set_url(name, url)
563
563
  Git::Remote.new(self, name)
564
564
  end
@@ -654,6 +654,57 @@ module Git
654
654
  lib.gc
655
655
  end
656
656
 
657
+ # Verifies the connectivity and validity of objects in the database
658
+ #
659
+ # Runs `git fsck` to check repository integrity and identify dangling,
660
+ # missing, or unreachable objects.
661
+ #
662
+ # @overload fsck(objects = [], options = {})
663
+ # @param objects [Array<String>] specific objects to treat as heads for unreachability trace.
664
+ # If no objects are given, git fsck defaults to using the index file, all SHA-1
665
+ # references in the refs namespace, and all reflogs.
666
+ # @param [Hash] options options to pass to the underlying `git fsck` command
667
+ #
668
+ # @option options [Boolean] :unreachable print unreachable objects
669
+ # @option options [Boolean] :strict enable strict checking
670
+ # @option options [Boolean] :connectivity_only check only connectivity (faster)
671
+ # @option options [Boolean] :root report root nodes
672
+ # @option options [Boolean] :tags report tags
673
+ # @option options [Boolean] :cache consider objects in the index
674
+ # @option options [Boolean] :no_reflogs do not consider reflogs
675
+ # @option options [Boolean] :lost_found write dangling objects to .git/lost-found
676
+ # (note: this modifies the repository by creating files)
677
+ # @option options [Boolean, nil] :dangling print dangling objects (true/false/nil for default)
678
+ # @option options [Boolean, nil] :full check objects in alternate pools (true/false/nil for default)
679
+ # @option options [Boolean, nil] :name_objects name objects by refs (true/false/nil for default)
680
+ # @option options [Boolean, nil] :references check refs database consistency (true/false/nil for default)
681
+ #
682
+ # @return [Git::FsckResult] categorized objects flagged by fsck
683
+ #
684
+ # @example Check repository integrity
685
+ # result = git.fsck
686
+ # result.dangling.each { |obj| puts "#{obj.type}: #{obj.sha}" }
687
+ #
688
+ # @example Check with strict mode and suppress dangling output
689
+ # result = git.fsck(strict: true, dangling: false)
690
+ #
691
+ # @example Check if repository has any issues
692
+ # result = git.fsck
693
+ # puts "Repository is clean" if result.empty?
694
+ #
695
+ # @example List root commits
696
+ # result = git.fsck(root: true)
697
+ # result.root.each { |obj| puts obj.sha }
698
+ #
699
+ # @example Check specific objects
700
+ # result = git.fsck('abc1234', 'def5678')
701
+ #
702
+ # rubocop:disable Style/ArgumentsForwarding
703
+ def fsck(*objects, **opts)
704
+ lib.fsck(*objects, **opts)
705
+ end
706
+ # rubocop:enable Style/ArgumentsForwarding
707
+
657
708
  def apply(file)
658
709
  return unless File.exist?(file)
659
710
 
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ # Represents an object returned by `git fsck`
5
+ #
6
+ # This class provides information about dangling, missing, unreachable, or
7
+ # problematic Git objects found during repository integrity checks.
8
+ #
9
+ # @api public
10
+ #
11
+ class FsckObject
12
+ # The type of the Git object
13
+ # @return [Symbol] one of :commit, :tree, :blob, or :tag
14
+ attr_reader :type
15
+
16
+ # The SHA-1 hash of the object
17
+ # @return [String] the 40-character SHA-1 hash
18
+ attr_reader :sha
19
+
20
+ # A warning or error message associated with this object
21
+ # @return [String, nil] the message, or nil if no message
22
+ attr_reader :message
23
+
24
+ # A name describing how the object is reachable (from --name-objects)
25
+ # @return [String, nil] the name, or nil if not provided
26
+ attr_reader :name
27
+
28
+ # Create a new FsckObject
29
+ #
30
+ # @param type [Symbol] the object type (:commit, :tree, :blob, or :tag)
31
+ # @param sha [String] the 40-character SHA-1 hash
32
+ # @param message [String, nil] optional warning/error message
33
+ # @param name [String, nil] optional name from --name-objects (e.g., "HEAD~2^2:src/")
34
+ #
35
+ def initialize(type:, sha:, message: nil, name: nil)
36
+ @type = type
37
+ @sha = sha
38
+ @message = message
39
+ @name = name
40
+ end
41
+
42
+ # Returns the SHA as the string representation
43
+ # @return [String] the SHA-1 hash
44
+ def to_s
45
+ sha
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ # Represents the result of running `git fsck`
5
+ #
6
+ # This class provides structured access to the objects found during a
7
+ # repository integrity check, categorized by their status.
8
+ #
9
+ # @api public
10
+ #
11
+ class FsckResult
12
+ # Objects not referenced by any other object
13
+ # @return [Array<Git::FsckObject>]
14
+ attr_reader :dangling
15
+
16
+ # Objects that are referenced but not present in the repository
17
+ # @return [Array<Git::FsckObject>]
18
+ attr_reader :missing
19
+
20
+ # Objects not reachable from any ref
21
+ # @return [Array<Git::FsckObject>]
22
+ attr_reader :unreachable
23
+
24
+ # Objects with warnings (each includes a message)
25
+ # @return [Array<Git::FsckObject>]
26
+ attr_reader :warnings
27
+
28
+ # Root nodes (commits with no parents) when --root is used
29
+ # @return [Array<Git::FsckObject>]
30
+ attr_reader :root
31
+
32
+ # Tagged objects when --tags is used
33
+ # @return [Array<Git::FsckObject>]
34
+ attr_reader :tagged
35
+
36
+ # rubocop:disable Metrics/ParameterLists
37
+
38
+ # Create a new FsckResult
39
+ #
40
+ # @param dangling [Array<Git::FsckObject>] dangling objects
41
+ # @param missing [Array<Git::FsckObject>] missing objects
42
+ # @param unreachable [Array<Git::FsckObject>] unreachable objects
43
+ # @param warnings [Array<Git::FsckObject>] objects with warnings
44
+ # @param root [Array<Git::FsckObject>] root nodes
45
+ # @param tagged [Array<Git::FsckObject>] tagged objects
46
+ #
47
+ def initialize(dangling: [], missing: [], unreachable: [], warnings: [], root: [], tagged: [])
48
+ @dangling = dangling
49
+ @missing = missing
50
+ @unreachable = unreachable
51
+ @warnings = warnings
52
+ @root = root
53
+ @tagged = tagged
54
+ end
55
+
56
+ # rubocop:enable Metrics/ParameterLists
57
+
58
+ # Returns true if any issues were found
59
+ #
60
+ # @return [Boolean]
61
+ #
62
+ # @example
63
+ # result = git.fsck
64
+ # puts "Repository has issues!" if result.any_issues?
65
+ #
66
+ def any_issues?
67
+ [dangling, missing, unreachable, warnings].any?(&:any?)
68
+ end
69
+
70
+ # Returns true if no issues were found
71
+ #
72
+ # @return [Boolean]
73
+ #
74
+ # @example
75
+ # result = git.fsck
76
+ # puts "Repository is clean" if result.empty?
77
+ #
78
+ def empty?
79
+ !any_issues?
80
+ end
81
+
82
+ # Returns all objects from all categories (excluding informational root/tagged)
83
+ #
84
+ # @return [Array<Git::FsckObject>]
85
+ #
86
+ # @example
87
+ # result = git.fsck
88
+ # result.all_objects.each { |obj| puts obj.sha }
89
+ #
90
+ def all_objects
91
+ dangling + missing + unreachable + warnings
92
+ end
93
+
94
+ # Returns the total number of issues found
95
+ #
96
+ # @return [Integer]
97
+ #
98
+ # @example
99
+ # result = git.fsck
100
+ # puts "Found #{result.count} issues"
101
+ #
102
+ def count
103
+ all_objects.size
104
+ end
105
+
106
+ # Returns a hash representation of the result
107
+ #
108
+ # @return [Hash{Symbol => Array<Git::FsckObject>}]
109
+ #
110
+ # @example
111
+ # result = git.fsck
112
+ # result.to_h # => { dangling: [...], missing: [...], ... }
113
+ #
114
+ def to_h
115
+ {
116
+ dangling: dangling, missing: missing, unreachable: unreachable,
117
+ warnings: warnings, root: root, tagged: tagged
118
+ }
119
+ end
120
+ end
121
+ end
data/lib/git/lib.rb CHANGED
@@ -1530,6 +1530,38 @@ module Git
1530
1530
  command('gc', '--prune', '--aggressive', '--auto')
1531
1531
  end
1532
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
+
1533
1565
  READ_TREE_OPTION_MAP = [
1534
1566
  { keys: [:prefix], flag: '--prefix', type: :valued_equals }
1535
1567
  ].freeze
@@ -1684,8 +1716,52 @@ module Git
1684
1716
  { keys: [:between], type: :custom, builder: ->(value) { "#{value[0]}..#{value[1]}" if value } }
1685
1717
  ].freeze
1686
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
+
1687
1726
  private
1688
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
+
1689
1765
  def parse_diff_path_status(args)
1690
1766
  command_lines('diff', *args).each_with_object({}) do |line, memo|
1691
1767
  status, path = split_status_line(line)
@@ -1709,9 +1785,9 @@ module Git
1709
1785
  end
1710
1786
 
1711
1787
  def initialize_from_base(base_object)
1712
- @git_dir = base_object.repo.path
1713
- @git_index_file = base_object.index&.path
1714
- @git_work_dir = base_object.dir&.path
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
1715
1791
  @git_ssh = base_object.git_ssh
1716
1792
  end
1717
1793
 
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
- attr_accessor :path
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
@@ -3,5 +3,5 @@
3
3
  module Git
4
4
  # The current gem version
5
5
  # @return [String] the current gem version.
6
- VERSION = '4.1.2'
6
+ VERSION = '4.3.0'
7
7
  end
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'
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.1.2
4
+ version: 4.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Chacon and others
@@ -276,6 +276,8 @@ files:
276
276
  - lib/git/encoding_utils.rb
277
277
  - lib/git/errors.rb
278
278
  - lib/git/escaped_path.rb
279
+ - lib/git/fsck_object.rb
280
+ - lib/git/fsck_result.rb
279
281
  - lib/git/index.rb
280
282
  - lib/git/lib.rb
281
283
  - lib/git/log.rb
@@ -308,8 +310,8 @@ licenses:
308
310
  metadata:
309
311
  homepage_uri: http://github.com/ruby-git/ruby-git
310
312
  source_code_uri: http://github.com/ruby-git/ruby-git
311
- changelog_uri: https://rubydoc.info/gems/git/4.1.2/file/CHANGELOG.md
312
- documentation_uri: https://rubydoc.info/gems/git/4.1.2
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
313
315
  rubygems_mfa_required: 'true'
314
316
  rdoc_options: []
315
317
  require_paths: