git 4.3.2 → 5.0.0.beta.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.
Files changed (280) hide show
  1. checksums.yaml +4 -4
  2. data/.github/copilot-instructions.md +67 -2705
  3. data/.github/pull_request_template.md +3 -1
  4. data/.github/skills/breaking-change-analysis/SKILL.md +102 -0
  5. data/.github/skills/ci-cd-troubleshooting/SKILL.md +264 -0
  6. data/.github/skills/command-implementation/REFERENCE.md +993 -0
  7. data/.github/skills/command-implementation/SKILL.md +229 -0
  8. data/.github/skills/command-test-conventions/SKILL.md +660 -0
  9. data/.github/skills/command-yard-documentation/SKILL.md +426 -0
  10. data/.github/skills/dependency-management/SKILL.md +72 -0
  11. data/.github/skills/development-workflow/SKILL.md +506 -0
  12. data/.github/skills/extract-command-from-lib/SKILL.md +487 -0
  13. data/.github/skills/extract-facade-from-base-lib/SKILL.md +586 -0
  14. data/.github/skills/facade-implementation/REFERENCE.md +840 -0
  15. data/.github/skills/facade-implementation/SKILL.md +260 -0
  16. data/.github/skills/facade-test-conventions/SKILL.md +380 -0
  17. data/.github/skills/facade-yard-documentation/SKILL.md +429 -0
  18. data/.github/skills/make-skill-template/SKILL.md +176 -0
  19. data/.github/skills/pr-readiness-review/SKILL.md +185 -0
  20. data/.github/skills/project-context/SKILL.md +313 -0
  21. data/.github/skills/pull-request-review/SKILL.md +168 -0
  22. data/.github/skills/refactor-command-to-commandlineresult/SKILL.md +131 -0
  23. data/.github/skills/release-management/SKILL.md +125 -0
  24. data/.github/skills/review-arguments-dsl/CHECKLIST.md +788 -0
  25. data/.github/skills/review-arguments-dsl/SKILL.md +214 -0
  26. data/.github/skills/review-backward-compatibility/SKILL.md +275 -0
  27. data/.github/skills/review-cross-command-consistency/SKILL.md +139 -0
  28. data/.github/skills/reviewing-skills/SKILL.md +189 -0
  29. data/.github/skills/rspec-unit-testing-standards/SKILL.md +639 -0
  30. data/.github/skills/tdd-refactor-step/SKILL.md +236 -0
  31. data/.github/skills/test-debugging/SKILL.md +160 -0
  32. data/.github/skills/yard-documentation/SKILL.md +793 -0
  33. data/.github/workflows/continuous_integration.yml +3 -2
  34. data/.github/workflows/enforce_conventional_commits.yml +1 -1
  35. data/.github/workflows/experimental_continuous_integration.yml +2 -2
  36. data/.github/workflows/release.yml +3 -4
  37. data/.gitignore +8 -0
  38. data/.husky/pre-commit +13 -0
  39. data/.release-please-manifest.json +1 -1
  40. data/.rspec +3 -0
  41. data/.rubocop.yml +7 -3
  42. data/.rubocop_todo.yml +23 -5
  43. data/.yardopts +1 -0
  44. data/CHANGELOG.md +0 -40
  45. data/CONTRIBUTING.md +694 -53
  46. data/README.md +17 -5
  47. data/Rakefile +61 -9
  48. data/commitlint.test +4 -0
  49. data/git.gemspec +14 -8
  50. data/lib/git/args_builder.rb +0 -8
  51. data/lib/git/base.rb +486 -410
  52. data/lib/git/branch.rb +380 -43
  53. data/lib/git/branch_delete_failure.rb +31 -0
  54. data/lib/git/branch_delete_result.rb +63 -0
  55. data/lib/git/branch_info.rb +178 -0
  56. data/lib/git/branches.rb +130 -24
  57. data/lib/git/command_line/base.rb +245 -0
  58. data/lib/git/command_line/capturing.rb +249 -0
  59. data/lib/git/command_line/result.rb +96 -0
  60. data/lib/git/command_line/streaming.rb +194 -0
  61. data/lib/git/command_line.rb +43 -322
  62. data/lib/git/command_line_result.rb +4 -88
  63. data/lib/git/commands/add.rb +131 -0
  64. data/lib/git/commands/am/abort.rb +43 -0
  65. data/lib/git/commands/am/apply.rb +252 -0
  66. data/lib/git/commands/am/continue.rb +43 -0
  67. data/lib/git/commands/am/quit.rb +43 -0
  68. data/lib/git/commands/am/retry.rb +47 -0
  69. data/lib/git/commands/am/show_current_patch.rb +64 -0
  70. data/lib/git/commands/am/skip.rb +42 -0
  71. data/lib/git/commands/am.rb +33 -0
  72. data/lib/git/commands/apply.rb +237 -0
  73. data/lib/git/commands/archive/list_formats.rb +46 -0
  74. data/lib/git/commands/archive.rb +140 -0
  75. data/lib/git/commands/arguments.rb +3510 -0
  76. data/lib/git/commands/base.rb +403 -0
  77. data/lib/git/commands/branch/copy.rb +94 -0
  78. data/lib/git/commands/branch/create.rb +173 -0
  79. data/lib/git/commands/branch/delete.rb +80 -0
  80. data/lib/git/commands/branch/list.rb +162 -0
  81. data/lib/git/commands/branch/move.rb +94 -0
  82. data/lib/git/commands/branch/set_upstream.rb +86 -0
  83. data/lib/git/commands/branch/show_current.rb +49 -0
  84. data/lib/git/commands/branch/unset_upstream.rb +57 -0
  85. data/lib/git/commands/branch.rb +34 -0
  86. data/lib/git/commands/cat_file/batch.rb +364 -0
  87. data/lib/git/commands/cat_file/filtered.rb +105 -0
  88. data/lib/git/commands/cat_file/raw.rb +210 -0
  89. data/lib/git/commands/cat_file.rb +49 -0
  90. data/lib/git/commands/checkout/branch.rb +151 -0
  91. data/lib/git/commands/checkout/files.rb +115 -0
  92. data/lib/git/commands/checkout.rb +38 -0
  93. data/lib/git/commands/checkout_index.rb +105 -0
  94. data/lib/git/commands/clean.rb +100 -0
  95. data/lib/git/commands/clone.rb +240 -0
  96. data/lib/git/commands/commit.rb +272 -0
  97. data/lib/git/commands/commit_tree.rb +100 -0
  98. data/lib/git/commands/config_option_syntax/add.rb +83 -0
  99. data/lib/git/commands/config_option_syntax/get.rb +117 -0
  100. data/lib/git/commands/config_option_syntax/get_all.rb +115 -0
  101. data/lib/git/commands/config_option_syntax/get_color.rb +91 -0
  102. data/lib/git/commands/config_option_syntax/get_color_bool.rb +93 -0
  103. data/lib/git/commands/config_option_syntax/get_regexp.rb +115 -0
  104. data/lib/git/commands/config_option_syntax/get_urlmatch.rb +102 -0
  105. data/lib/git/commands/config_option_syntax/list.rb +107 -0
  106. data/lib/git/commands/config_option_syntax/remove_section.rb +74 -0
  107. data/lib/git/commands/config_option_syntax/rename_section.rb +78 -0
  108. data/lib/git/commands/config_option_syntax/replace_all.rb +104 -0
  109. data/lib/git/commands/config_option_syntax/set.rb +114 -0
  110. data/lib/git/commands/config_option_syntax/unset.rb +89 -0
  111. data/lib/git/commands/config_option_syntax/unset_all.rb +89 -0
  112. data/lib/git/commands/config_option_syntax.rb +56 -0
  113. data/lib/git/commands/describe.rb +155 -0
  114. data/lib/git/commands/diff.rb +656 -0
  115. data/lib/git/commands/diff_files.rb +518 -0
  116. data/lib/git/commands/diff_index.rb +496 -0
  117. data/lib/git/commands/fetch.rb +352 -0
  118. data/lib/git/commands/fsck.rb +136 -0
  119. data/lib/git/commands/gc.rb +132 -0
  120. data/lib/git/commands/grep.rb +338 -0
  121. data/lib/git/commands/init.rb +99 -0
  122. data/lib/git/commands/log.rb +632 -0
  123. data/lib/git/commands/ls_files.rb +191 -0
  124. data/lib/git/commands/ls_remote.rb +155 -0
  125. data/lib/git/commands/ls_tree.rb +131 -0
  126. data/lib/git/commands/maintenance/register.rb +75 -0
  127. data/lib/git/commands/maintenance/run.rb +104 -0
  128. data/lib/git/commands/maintenance/start.rb +66 -0
  129. data/lib/git/commands/maintenance/stop.rb +55 -0
  130. data/lib/git/commands/maintenance/unregister.rb +79 -0
  131. data/lib/git/commands/maintenance.rb +31 -0
  132. data/lib/git/commands/merge/abort.rb +44 -0
  133. data/lib/git/commands/merge/continue.rb +44 -0
  134. data/lib/git/commands/merge/quit.rb +46 -0
  135. data/lib/git/commands/merge/start.rb +245 -0
  136. data/lib/git/commands/merge.rb +28 -0
  137. data/lib/git/commands/merge_base.rb +86 -0
  138. data/lib/git/commands/mv.rb +77 -0
  139. data/lib/git/commands/name_rev.rb +114 -0
  140. data/lib/git/commands/pull.rb +377 -0
  141. data/lib/git/commands/push.rb +246 -0
  142. data/lib/git/commands/read_tree.rb +149 -0
  143. data/lib/git/commands/remote/add.rb +91 -0
  144. data/lib/git/commands/remote/get_url.rb +66 -0
  145. data/lib/git/commands/remote/list.rb +54 -0
  146. data/lib/git/commands/remote/prune.rb +61 -0
  147. data/lib/git/commands/remote/remove.rb +52 -0
  148. data/lib/git/commands/remote/rename.rb +69 -0
  149. data/lib/git/commands/remote/set_branches.rb +63 -0
  150. data/lib/git/commands/remote/set_head.rb +82 -0
  151. data/lib/git/commands/remote/set_url.rb +71 -0
  152. data/lib/git/commands/remote/set_url_add.rb +61 -0
  153. data/lib/git/commands/remote/set_url_delete.rb +64 -0
  154. data/lib/git/commands/remote/show.rb +71 -0
  155. data/lib/git/commands/remote/update.rb +72 -0
  156. data/lib/git/commands/remote.rb +42 -0
  157. data/lib/git/commands/repack.rb +277 -0
  158. data/lib/git/commands/reset.rb +147 -0
  159. data/lib/git/commands/rev_parse.rb +297 -0
  160. data/lib/git/commands/revert/abort.rb +45 -0
  161. data/lib/git/commands/revert/continue.rb +57 -0
  162. data/lib/git/commands/revert/quit.rb +47 -0
  163. data/lib/git/commands/revert/skip.rb +44 -0
  164. data/lib/git/commands/revert/start.rb +153 -0
  165. data/lib/git/commands/revert.rb +29 -0
  166. data/lib/git/commands/rm.rb +114 -0
  167. data/lib/git/commands/show.rb +632 -0
  168. data/lib/git/commands/show_ref/exclude_existing.rb +120 -0
  169. data/lib/git/commands/show_ref/exists.rb +78 -0
  170. data/lib/git/commands/show_ref/list.rb +145 -0
  171. data/lib/git/commands/show_ref/verify.rb +120 -0
  172. data/lib/git/commands/show_ref.rb +42 -0
  173. data/lib/git/commands/stash/apply.rb +75 -0
  174. data/lib/git/commands/stash/branch.rb +65 -0
  175. data/lib/git/commands/stash/clear.rb +41 -0
  176. data/lib/git/commands/stash/create.rb +58 -0
  177. data/lib/git/commands/stash/drop.rb +67 -0
  178. data/lib/git/commands/stash/list.rb +39 -0
  179. data/lib/git/commands/stash/pop.rb +78 -0
  180. data/lib/git/commands/stash/push.rb +103 -0
  181. data/lib/git/commands/stash/show.rb +149 -0
  182. data/lib/git/commands/stash/store.rb +63 -0
  183. data/lib/git/commands/stash.rb +38 -0
  184. data/lib/git/commands/status.rb +169 -0
  185. data/lib/git/commands/symbolic_ref/delete.rb +68 -0
  186. data/lib/git/commands/symbolic_ref/read.rb +95 -0
  187. data/lib/git/commands/symbolic_ref/update.rb +76 -0
  188. data/lib/git/commands/symbolic_ref.rb +38 -0
  189. data/lib/git/commands/tag/create.rb +139 -0
  190. data/lib/git/commands/tag/delete.rb +55 -0
  191. data/lib/git/commands/tag/list.rb +143 -0
  192. data/lib/git/commands/tag/verify.rb +71 -0
  193. data/lib/git/commands/tag.rb +26 -0
  194. data/lib/git/commands/update_ref/batch.rb +140 -0
  195. data/lib/git/commands/update_ref/delete.rb +92 -0
  196. data/lib/git/commands/update_ref/update.rb +106 -0
  197. data/lib/git/commands/update_ref.rb +42 -0
  198. data/lib/git/commands/version.rb +52 -0
  199. data/lib/git/commands/worktree/add.rb +140 -0
  200. data/lib/git/commands/worktree/list.rb +64 -0
  201. data/lib/git/commands/worktree/lock.rb +58 -0
  202. data/lib/git/commands/worktree/management_base.rb +51 -0
  203. data/lib/git/commands/worktree/move.rb +66 -0
  204. data/lib/git/commands/worktree/prune.rb +67 -0
  205. data/lib/git/commands/worktree/remove.rb +63 -0
  206. data/lib/git/commands/worktree/repair.rb +76 -0
  207. data/lib/git/commands/worktree/unlock.rb +47 -0
  208. data/lib/git/commands/worktree.rb +43 -0
  209. data/lib/git/commands/write_tree.rb +68 -0
  210. data/lib/git/commands.rb +89 -0
  211. data/lib/git/detached_head_info.rb +54 -0
  212. data/lib/git/diff.rb +297 -7
  213. data/lib/git/diff_file_numstat_info.rb +29 -0
  214. data/lib/git/diff_file_patch_info.rb +134 -0
  215. data/lib/git/diff_file_raw_info.rb +127 -0
  216. data/lib/git/diff_info.rb +169 -0
  217. data/lib/git/diff_path_status.rb +78 -19
  218. data/lib/git/diff_result.rb +32 -0
  219. data/lib/git/diff_stats.rb +59 -14
  220. data/lib/git/dirstat_info.rb +86 -0
  221. data/lib/git/errors.rb +65 -2
  222. data/lib/git/execution_context/global.rb +56 -0
  223. data/lib/git/execution_context/repository.rb +147 -0
  224. data/lib/git/execution_context.rb +482 -0
  225. data/lib/git/file_ref.rb +74 -0
  226. data/lib/git/fsck_object.rb +9 -9
  227. data/lib/git/fsck_result.rb +1 -1
  228. data/lib/git/lib.rb +1606 -1028
  229. data/lib/git/log.rb +15 -2
  230. data/lib/git/object.rb +92 -22
  231. data/lib/git/parsers/branch.rb +224 -0
  232. data/lib/git/parsers/cat_file.rb +111 -0
  233. data/lib/git/parsers/diff.rb +585 -0
  234. data/lib/git/parsers/fsck.rb +133 -0
  235. data/lib/git/parsers/grep.rb +42 -0
  236. data/lib/git/parsers/ls_tree.rb +58 -0
  237. data/lib/git/parsers/stash.rb +208 -0
  238. data/lib/git/parsers/tag.rb +257 -0
  239. data/lib/git/remote.rb +133 -9
  240. data/lib/git/repository/branching.rb +572 -0
  241. data/lib/git/repository/committing.rb +191 -0
  242. data/lib/git/repository/configuring.rb +156 -0
  243. data/lib/git/repository/diffing.rb +775 -0
  244. data/lib/git/repository/inspecting.rb +153 -0
  245. data/lib/git/repository/logging.rb +247 -0
  246. data/lib/git/repository/merging.rb +295 -0
  247. data/lib/git/repository/object_operations.rb +1101 -0
  248. data/lib/git/repository/path_resolver.rb +207 -0
  249. data/lib/git/repository/remote_operations.rb +753 -0
  250. data/lib/git/repository/shared_private.rb +51 -0
  251. data/lib/git/repository/staging.rb +390 -0
  252. data/lib/git/repository/stashing.rb +107 -0
  253. data/lib/git/repository/status_operations.rb +180 -0
  254. data/lib/git/repository/worktree_operations.rb +159 -0
  255. data/lib/git/repository.rb +264 -1
  256. data/lib/git/stash.rb +85 -4
  257. data/lib/git/stash_info.rb +104 -0
  258. data/lib/git/stashes.rb +130 -13
  259. data/lib/git/status.rb +224 -18
  260. data/lib/git/tag_delete_failure.rb +31 -0
  261. data/lib/git/tag_delete_result.rb +63 -0
  262. data/lib/git/tag_info.rb +105 -0
  263. data/lib/git/version.rb +109 -2
  264. data/lib/git/version_constraint.rb +81 -0
  265. data/lib/git/worktree.rb +120 -5
  266. data/lib/git/worktrees.rb +107 -7
  267. data/lib/git.rb +114 -18
  268. data/redesign/1_architecture_existing.md +54 -18
  269. data/redesign/2_architecture_redesign.md +365 -46
  270. data/redesign/3_architecture_implementation.md +1451 -54
  271. data/tasks/gem_tasks.rake +4 -0
  272. data/tasks/npm_tasks.rake +7 -0
  273. data/tasks/rspec.rake +48 -0
  274. data/tasks/test.rake +13 -1
  275. data/tasks/yard.rake +34 -7
  276. metadata +349 -20
  277. data/lib/git/index.rb +0 -6
  278. data/lib/git/path.rb +0 -38
  279. data/lib/git/working_directory.rb +0 -6
  280. /data/{release-please-config.json → .release-please-config.json} +0 -0
@@ -1,8 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Git
4
- # Provides access to the statistics of a diff between two commits,
5
- # including insertions, deletions, and file-level details.
4
+ # Lazy diff statistics for a comparison between two trees
5
+ #
6
+ # Supports comparing (1) two commits, (2) a commit against the working tree,
7
+ # or (3) the index against the working tree.
8
+ #
9
+ # @example Get the insertion and deletion counts
10
+ # stats = repo.diff_stats
11
+ # stats.insertions #=> 3
12
+ # stats.deletions #=> 1
13
+ #
14
+ # @api public
6
15
  class DiffStats
7
16
  # @private
8
17
  def initialize(base, from, to, path_limiter = nil)
@@ -15,45 +24,81 @@ module Git
15
24
  @from = from
16
25
  @to = to
17
26
  @path_limiter = path_limiter
18
- @stats = nil
27
+ @fetch_stats = nil
19
28
  end
20
29
 
21
- # Returns the total number of lines deleted.
30
+ # Returns the total number of lines deleted
31
+ #
32
+ # @example Get the deletion count
33
+ # stats = repo.diff_stats
34
+ # stats.deletions #=> 5
35
+ #
36
+ # @return [Integer] the total deletion count
22
37
  def deletions
23
38
  fetch_stats[:total][:deletions]
24
39
  end
25
40
 
26
- # Returns the total number of lines inserted.
41
+ # Returns the total number of lines inserted
42
+ #
43
+ # @example Get the insertion count
44
+ # stats = repo.diff_stats
45
+ # stats.insertions #=> 3
46
+ #
47
+ # @return [Integer] the total insertion count
27
48
  def insertions
28
49
  fetch_stats[:total][:insertions]
29
50
  end
30
51
 
31
- # Returns the total number of lines changed (insertions + deletions).
52
+ # Returns the total number of lines changed (insertions + deletions)
53
+ #
54
+ # @example Get the total changed-line count
55
+ # stats = repo.diff_stats
56
+ # stats.lines #=> 8
57
+ #
58
+ # @return [Integer] the total changed-line count
32
59
  def lines
33
60
  fetch_stats[:total][:lines]
34
61
  end
35
62
 
36
- # Returns a hash of statistics for each file in the diff.
63
+ # Returns a hash of statistics for each file in the diff
64
+ #
65
+ # @example Get per-file statistics
66
+ # stats = repo.diff_stats
67
+ # stats.files #=> { "lib/foo.rb" => { insertions: 3, deletions: 1 } }
37
68
  #
38
- # @return [Hash<String, {insertions: Integer, deletions: Integer}>]
69
+ # @return [Hash{String=>Hash{Symbol=>Integer}}]
70
+ # per-file statistics keyed by file path
39
71
  def files
40
72
  fetch_stats[:files]
41
73
  end
42
74
 
43
- # Returns a hash of the total statistics for the diff.
75
+ # Returns a hash of the total statistics for the diff
76
+ #
77
+ # @example Get total statistics
78
+ # stats = repo.diff_stats
79
+ # stats.total #=> { insertions: 3, deletions: 1, lines: 4, files: 1 }
44
80
  #
45
- # @return [{insertions: Integer, deletions: Integer, lines: Integer, files: Integer}]
81
+ # @return [Hash{Symbol=>Integer}]
82
+ # aggregate statistics for the entire diff
46
83
  def total
47
84
  fetch_stats[:total]
48
85
  end
49
86
 
50
87
  private
51
88
 
52
- # Lazily fetches and caches the stats from the git lib.
89
+ # Lazily fetches and caches the stats from the git lib
90
+ #
91
+ # When `@base` implements `#diff_numstat`, delegates directly to that
92
+ # method. Otherwise falls back to the legacy `@base.lib.diff_stats` call
93
+ # so that existing `Git::Base`-backed callers continue to work unchanged.
94
+ #
95
+ # @return [Hash] the fetched stats hash
53
96
  def fetch_stats
54
- @fetch_stats ||= @base.lib.diff_stats(
55
- @from, @to, { path_limiter: @path_limiter }
56
- )
97
+ @fetch_stats ||= if @base.respond_to?(:diff_numstat)
98
+ @base.diff_numstat(@from, @to, path_limiter: @path_limiter)
99
+ else
100
+ @base.lib.diff_stats(@from, @to, { path_limiter: @path_limiter })
101
+ end
57
102
  end
58
103
  end
59
104
  end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ # Immutable value object representing a single directory's contribution to a diff
5
+ #
6
+ # @api public
7
+ #
8
+ # @example
9
+ # info = Git::DirstatEntry.new(directory: 'lib/commands/', percentage: 45.2)
10
+ # info.directory #=> "lib/commands/"
11
+ # info.percentage #=> 45.2
12
+ #
13
+ # @!attribute [r] directory
14
+ # @return [String] the directory path (always ends with '/')
15
+ #
16
+ # @!attribute [r] percentage
17
+ # @return [Float] the percentage of changes in this directory (0.0-100.0)
18
+ #
19
+ DirstatEntry = Data.define(:directory, :percentage)
20
+
21
+ # Immutable result object from git --dirstat output
22
+ #
23
+ # Contains the list of directories and their contribution percentages to the diff.
24
+ #
25
+ # @api public
26
+ #
27
+ # @example
28
+ # dirstat = Git::DirstatInfo.new(
29
+ # entries: [
30
+ # Git::DirstatEntry.new(directory: 'lib/commands/', percentage: 45.2),
31
+ # Git::DirstatEntry.new(directory: 'spec/unit/', percentage: 30.1)
32
+ # ]
33
+ # )
34
+ # dirstat.entries.first.directory #=> "lib/commands/"
35
+ # dirstat['lib/commands/'] #=> 45.2
36
+ # dirstat.to_h #=> { "lib/commands/" => 45.2, "spec/unit/" => 30.1 }
37
+ #
38
+ # @!attribute [r] entries
39
+ # @return [Array<DirstatEntry>] directory statistics in order from git output
40
+ #
41
+ DirstatInfo = Data.define(:entries) do
42
+ # Look up percentage by directory path
43
+ #
44
+ # @param directory [String] the directory path
45
+ # @return [Float, nil] the percentage or nil if not found
46
+ #
47
+ def [](directory)
48
+ entries.find { |e| e.directory == directory }&.percentage
49
+ end
50
+
51
+ # Convert to a Hash mapping directory to percentage
52
+ #
53
+ # @return [Hash<String, Float>]
54
+ #
55
+ def to_h
56
+ entries.to_h { |e| [e.directory, e.percentage] }
57
+ end
58
+
59
+ # Number of directories in the dirstat
60
+ #
61
+ # @return [Integer]
62
+ #
63
+ def size
64
+ entries.size
65
+ end
66
+
67
+ # Check if dirstat is empty
68
+ #
69
+ # @return [Boolean]
70
+ #
71
+ def empty?
72
+ entries.empty?
73
+ end
74
+
75
+ # Iterate over entries
76
+ #
77
+ # @yield [DirstatEntry] each entry
78
+ # @return [Enumerator] if no block given
79
+ #
80
+ def each(&block)
81
+ entries.each(&block)
82
+ end
83
+
84
+ include Enumerable
85
+ end
86
+ end
data/lib/git/errors.rb CHANGED
@@ -21,7 +21,8 @@ module Git
21
21
  # │ └─> Git::SignaledError
22
22
  # │ └─> Git::TimeoutError
23
23
  # ├─> Git::ProcessIOError
24
- # └─> Git::UnexpectedResultError
24
+ # ├─> Git::UnexpectedResultError
25
+ # └─> Git::VersionError
25
26
  # ```
26
27
  #
27
28
  # | Error Class | Description |
@@ -33,6 +34,7 @@ module Git
33
34
  # | `TimeoutError` | This is a specific type of `SignaledError` that is raised when the git command line operation times out and is killed via the SIGKILL signal. This happens if the operation takes longer than the timeout duration configured in `Git.config.timeout` or via the `:timeout` parameter given in git methods that support timeouts. |
34
35
  # | `ProcessIOError` | An error was encountered reading or writing to a subprocess. |
35
36
  # | `UnexpectedResultError` | The command line ran without error but did not return the expected results. |
37
+ # | `VersionError` | The installed git version does not meet the requirements of the git gem or a specific command. |
36
38
  #
37
39
  # @example Rescuing a generic error
38
40
  # begin
@@ -57,6 +59,7 @@ module Git
57
59
  # @see Git::TimeoutError
58
60
  # @see Git::ProcessIOError
59
61
  # @see Git::UnexpectedResultError
62
+ # @see Git::VersionError
60
63
  #
61
64
  # @api public
62
65
  #
@@ -146,7 +149,7 @@ module Git
146
149
  # The git command executed, status, stdout, and stderr, and the timeout duration
147
150
  # are available from this object.
148
151
  #
149
- # result.status.timeout? will be `true`
152
+ # result.status.timed_out? will be `true`
150
153
  #
151
154
  # @api public
152
155
  #
@@ -209,4 +212,64 @@ module Git
209
212
  # @api public
210
213
  #
211
214
  class UnexpectedResultError < Git::Error; end
215
+
216
+ # Raised when the installed git version does not meet requirements
217
+ #
218
+ # This error is raised when:
219
+ # - The installed git version is below `Git::MINIMUM_GIT_VERSION`
220
+ # - A command requires a minimum git version that isn't met
221
+ # - A command was removed in a git version older than the installed version
222
+ #
223
+ # @example Rescuing a version error
224
+ # begin
225
+ # git.some_command
226
+ # rescue Git::VersionError => e
227
+ # puts "Git version #{e.actual_version} does not meet requirements"
228
+ # puts " #{e.subject}: requires #{e.constraint}"
229
+ # end
230
+ #
231
+ # @api public
232
+ #
233
+ class VersionError < Git::Error
234
+ # Create a VersionError
235
+ #
236
+ # @param subject [#to_s] the entity with the version requirement (e.g., "The git gem", a Class)
237
+ # @param constraint [Git::VersionConstraint] the version constraint that was violated
238
+ # @param actual_version [Git::Version] the installed git version
239
+ #
240
+ def initialize(subject:, constraint:, actual_version:)
241
+ @subject = subject
242
+ @constraint = constraint
243
+ @actual_version = actual_version
244
+ super(build_message)
245
+ end
246
+
247
+ # The entity that has the version requirement
248
+ #
249
+ # @return [#to_s]
250
+ #
251
+ attr_reader :subject
252
+
253
+ # The version constraint that was violated
254
+ #
255
+ # @return [Git::VersionConstraint]
256
+ #
257
+ attr_reader :constraint
258
+
259
+ # The installed git version that caused the error
260
+ #
261
+ # @return [Git::Version]
262
+ #
263
+ attr_reader :actual_version
264
+
265
+ private
266
+
267
+ def build_message
268
+ if constraint.too_new?(actual_version)
269
+ "#{subject} requires git < #{constraint.before} (found #{actual_version})"
270
+ else
271
+ "#{subject} requires git >= #{constraint.min} (found #{actual_version})"
272
+ end
273
+ end
274
+ end
212
275
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'git/execution_context'
4
+
5
+ module Git
6
+ class ExecutionContext
7
+ # Execution context for global git commands (no repository required)
8
+ #
9
+ # Used for commands that do not require an existing repository — such as
10
+ # `git init`, `git clone`, and `git version`. Unlike
11
+ # {Git::ExecutionContext::Repository}, this class leaves `GIT_DIR`,
12
+ # `GIT_WORK_TREE`, and `GIT_INDEX_FILE` as `nil` (which unsets them), so
13
+ # that the parent environment cannot leak an unintended repository context.
14
+ # `GIT_SSH` is still supported to allow SSH-based remote operations
15
+ # (e.g. `git clone git@github.com:...`).
16
+ #
17
+ # @example Create a context using the default git binary
18
+ # context = Git::ExecutionContext::Global.new
19
+ #
20
+ # @example Create a context targeting a specific binary
21
+ # context = Git::ExecutionContext::Global.new(binary_path: '/usr/local/bin/git2')
22
+ #
23
+ # @api private
24
+ #
25
+ class Global < ExecutionContext
26
+ # Creates a new global execution context
27
+ #
28
+ # @example Create with default settings
29
+ # Git::ExecutionContext::Global.new
30
+ #
31
+ # @example Create with an explicit binary path
32
+ # Git::ExecutionContext::Global.new(binary_path: '/usr/local/bin/git2')
33
+ #
34
+ # @param binary_path [String, :use_global_config] path to the git binary
35
+ #
36
+ # Give `:use_global_config` (the default) to use `Git::Base.config.binary_path`.
37
+ #
38
+ # Passing `nil` raises `ArgumentError` — there is no "unset the
39
+ # binary" semantic.
40
+ #
41
+ # @param git_ssh [String, nil, :use_global_config] the SSH wrapper path
42
+ #
43
+ # Give `nil` to unset `GIT_SSH`, or `:use_global_config` (default) to use `Git::Base.config.git_ssh`.
44
+ #
45
+ # @param logger [Logger, nil] the logger to use in the CommandLine layer
46
+ #
47
+ # Give `nil` to use a null logger (`Logger.new(nil)`).
48
+ #
49
+ # @raise [ArgumentError] if `binary_path` is `nil`
50
+ #
51
+ def initialize(binary_path: :use_global_config, git_ssh: :use_global_config, logger: nil)
52
+ super
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'git/execution_context'
4
+
5
+ module Git
6
+ class ExecutionContext
7
+ # Execution context for repository-bound git commands
8
+ #
9
+ # Manages the git environment for commands that operate within an existing
10
+ # repository — setting `GIT_DIR`, `GIT_WORK_TREE`, `GIT_INDEX_FILE`, and
11
+ # `GIT_SSH` — and prepending `--git-dir` / `--work-tree` to every git
12
+ # invocation via {#global_opts}.
13
+ #
14
+ # ### Construction
15
+ #
16
+ # Prefer the factory class methods over `new` when building from a
17
+ # {Git::Base} object or a hash:
18
+ #
19
+ # context = Git::ExecutionContext::Repository.from_base(base)
20
+ # context = Git::ExecutionContext::Repository.from_hash(repository: '/repo/.git', ...)
21
+ #
22
+ # @example Build from a Git::Base object
23
+ # context = Git::ExecutionContext::Repository.from_base(git)
24
+ #
25
+ # @example Build from a configuration hash
26
+ # context = Git::ExecutionContext::Repository.from_hash(
27
+ # repository: '/path/to/.git',
28
+ # working_directory: '/path/to'
29
+ # )
30
+ #
31
+ # @api private
32
+ #
33
+ class Repository < ExecutionContext
34
+ # Creates a Repository context from a {Git::Base} instance
35
+ #
36
+ # @example Build from a base object
37
+ # git = Git.open('/path/to/repo')
38
+ # context = Git::ExecutionContext::Repository.from_base(git)
39
+ #
40
+ # @param base_object [Git::Base] the base Git object to derive context from
41
+ #
42
+ # @param logger [Logger, nil] logger forwarded to the CommandLine layer;
43
+ # `nil` uses a null logger (see {Git::ExecutionContext#initialize})
44
+ #
45
+ # @return [Git::ExecutionContext::Repository] the new repository context
46
+ #
47
+ def self.from_base(base_object, logger: nil)
48
+ new(
49
+ git_dir: base_object.repo.to_s,
50
+ git_index_file: base_object.index&.to_s,
51
+ git_work_dir: base_object.dir&.to_s,
52
+ base_object: base_object,
53
+ git_ssh: base_object.git_ssh,
54
+ binary_path: base_object.binary_path,
55
+ logger: logger
56
+ )
57
+ end
58
+
59
+ # Creates a Repository context from a Hash
60
+ #
61
+ # Expected keys: `:repository`, `:working_directory`, `:index`, `:git_ssh`,
62
+ # `:binary_path`
63
+ #
64
+ # @example Build from a configuration hash
65
+ # context = Git::ExecutionContext::Repository.from_hash(
66
+ # repository: '/path/to/.git',
67
+ # working_directory: '/path/to'
68
+ # )
69
+ #
70
+ # @param base_hash [Hash] the hash of repository configuration values
71
+ #
72
+ # @param logger [Logger, nil] logger forwarded to the CommandLine layer;
73
+ # `nil` uses a null logger (see {Git::ExecutionContext#initialize})
74
+ #
75
+ # @return [Git::ExecutionContext::Repository] the new repository context
76
+ #
77
+ def self.from_hash(base_hash, logger: nil)
78
+ new(
79
+ git_dir: base_hash[:repository],
80
+ git_index_file: base_hash[:index],
81
+ git_work_dir: base_hash[:working_directory],
82
+ git_ssh: base_hash.key?(:git_ssh) ? base_hash[:git_ssh] : :use_global_config,
83
+ binary_path: base_hash.key?(:binary_path) ? base_hash[:binary_path] : :use_global_config,
84
+ logger: logger
85
+ )
86
+ end
87
+
88
+ # Creates a new repository execution context
89
+ #
90
+ # @example Create with required git_dir
91
+ # Git::ExecutionContext::Repository.new(git_dir: '/path/to/.git')
92
+ #
93
+ # @param git_dir [String, nil] path to the `.git` directory
94
+ #
95
+ # @param git_work_dir [String, nil] path to the working tree
96
+ #
97
+ # @param git_index_file [String, nil] path to the index file
98
+ #
99
+ # @param base_object [Git::Base, nil] originating base object
100
+ #
101
+ # @param binary_path [String, :use_global_config] path to the git binary
102
+ #
103
+ # Give `:use_global_config` (the default) to use `Git::Base.config.binary_path`.
104
+ #
105
+ # Passing `nil` raises `ArgumentError` — there is no "unset the
106
+ # binary" semantic.
107
+ #
108
+ # @param git_ssh [String, nil, :use_global_config] the SSH wrapper path
109
+ #
110
+ # Give `nil` to unset `GIT_SSH`, or `:use_global_config` (default) to use `Git::Base.config.git_ssh`.
111
+ #
112
+ # @param logger [Logger, nil] the logger to use in the CommandLine layer
113
+ #
114
+ # Give `nil` to use a null logger (`Logger.new(nil)`).
115
+ #
116
+ # @raise [ArgumentError] if `binary_path` is `nil`
117
+ #
118
+ def initialize( # rubocop:disable Metrics/ParameterLists
119
+ git_dir:,
120
+ git_work_dir: nil,
121
+ git_index_file: nil,
122
+ base_object: nil,
123
+ binary_path: :use_global_config,
124
+ git_ssh: :use_global_config,
125
+ logger: nil
126
+ )
127
+ super(binary_path: binary_path, git_ssh: git_ssh, logger: logger)
128
+ @git_dir = git_dir
129
+ @git_work_dir = git_work_dir
130
+ @git_index_file = git_index_file
131
+ @base_object = base_object
132
+ end
133
+
134
+ # @return [String, nil] path to the `.git` directory
135
+ attr_reader :git_dir
136
+
137
+ # @return [String, nil] path to the working tree
138
+ attr_reader :git_work_dir
139
+
140
+ # @return [String, nil] path to the index file
141
+ attr_reader :git_index_file
142
+
143
+ # @return [Git::Base, nil] originating base object
144
+ attr_reader :base_object
145
+ end
146
+ end
147
+ end