git 5.0.0.beta.2 → 5.0.0.beta.4

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 (270) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +27 -0
  3. data/.github/copilot-instructions.md +3 -8
  4. data/.github/hooks/bin-setup-on-worktree.json +11 -0
  5. data/.github/hooks/run-bin-setup-once.sh +14 -0
  6. data/.github/skills/breaking-change-analysis/SKILL.md +6 -9
  7. data/.github/skills/ci-cd-troubleshooting/SKILL.md +2 -2
  8. data/.github/skills/command-implementation/REFERENCE.md +34 -33
  9. data/.github/skills/command-implementation/SKILL.md +5 -4
  10. data/.github/skills/command-test-conventions/SKILL.md +13 -9
  11. data/.github/skills/command-yard-documentation/SKILL.md +14 -6
  12. data/.github/skills/development-workflow/SKILL.md +20 -14
  13. data/.github/skills/facade-implementation/REFERENCE.md +19 -22
  14. data/.github/skills/facade-implementation/SKILL.md +0 -5
  15. data/.github/skills/facade-test-conventions/SKILL.md +4 -7
  16. data/.github/skills/facade-yard-documentation/SKILL.md +8 -2
  17. data/.github/skills/make-skill-template/SKILL.md +53 -3
  18. data/.github/skills/pr-readiness-review/SKILL.md +22 -2
  19. data/.github/skills/project-context/SKILL.md +16 -23
  20. data/.github/skills/pull-request-review/SKILL.md +2 -2
  21. data/.github/skills/rebase/SKILL.md +148 -0
  22. data/.github/skills/refactor-command-to-commandlineresult/SKILL.md +1 -1
  23. data/.github/skills/resolve-feedback/SKILL.md +288 -0
  24. data/.github/skills/review-arguments-dsl/CHECKLIST.md +3 -3
  25. data/.github/skills/reviewing-skills/SKILL.md +28 -3
  26. data/.github/skills/test-debugging/SKILL.md +20 -19
  27. data/.github/skills/yard-documentation/SKILL.md +671 -483
  28. data/.github/skills/yard-documentation/element-rules.md +162 -0
  29. data/.github/skills-deprecated/README.md +21 -0
  30. data/.github/{skills → skills-deprecated}/extract-command-from-lib/SKILL.md +9 -9
  31. data/.github/{skills → skills-deprecated}/extract-facade-from-base-lib/SKILL.md +12 -12
  32. data/.github/workflows/continuous_integration.yml +3 -2
  33. data/.rubocop.yml +7 -39
  34. data/.yard-lint.yml +75 -0
  35. data/CONTRIBUTING.md +186 -174
  36. data/Gemfile +17 -0
  37. data/README.md +86 -23
  38. data/Rakefile +3 -21
  39. data/UPGRADING.md +219 -70
  40. data/docker/test/Dockerfile +32 -0
  41. data/docker/test/docker-compose.yml +0 -0
  42. data/git.gemspec +3 -4
  43. data/lib/git/author.rb +20 -1
  44. data/lib/git/branch.rb +26 -6
  45. data/lib/git/branch_delete_failure.rb +3 -0
  46. data/lib/git/branch_delete_result.rb +6 -3
  47. data/lib/git/branch_info.rb +6 -6
  48. data/lib/git/command_line/base.rb +6 -13
  49. data/lib/git/command_line/capturing.rb +67 -10
  50. data/lib/git/command_line/result.rb +0 -8
  51. data/lib/git/command_line/streaming.rb +45 -5
  52. data/lib/git/command_line.rb +4 -3
  53. data/lib/git/commands/add.rb +61 -53
  54. data/lib/git/commands/am/abort.rb +2 -2
  55. data/lib/git/commands/am/apply.rb +109 -98
  56. data/lib/git/commands/am/continue.rb +2 -2
  57. data/lib/git/commands/am/quit.rb +2 -2
  58. data/lib/git/commands/am/retry.rb +4 -2
  59. data/lib/git/commands/am/show_current_patch.rb +1 -1
  60. data/lib/git/commands/am/skip.rb +2 -2
  61. data/lib/git/commands/am.rb +2 -2
  62. data/lib/git/commands/apply.rb +7 -2
  63. data/lib/git/commands/archive/list_formats.rb +3 -3
  64. data/lib/git/commands/archive.rb +7 -2
  65. data/lib/git/commands/arguments.rb +1370 -359
  66. data/lib/git/commands/base.rb +111 -13
  67. data/lib/git/commands/branch/copy.rb +29 -21
  68. data/lib/git/commands/branch/create.rb +72 -68
  69. data/lib/git/commands/branch/delete.rb +25 -17
  70. data/lib/git/commands/branch/list.rb +76 -60
  71. data/lib/git/commands/branch/move.rb +29 -21
  72. data/lib/git/commands/branch/set_upstream.rb +8 -8
  73. data/lib/git/commands/branch/show_current.rb +3 -3
  74. data/lib/git/commands/branch/unset_upstream.rb +4 -8
  75. data/lib/git/commands/branch.rb +2 -2
  76. data/lib/git/commands/cat_file/batch.rb +36 -20
  77. data/lib/git/commands/cat_file/filtered.rb +12 -12
  78. data/lib/git/commands/cat_file/raw.rb +13 -13
  79. data/lib/git/commands/cat_file.rb +4 -4
  80. data/lib/git/commands/checkout/branch.rb +7 -5
  81. data/lib/git/commands/checkout/files.rb +5 -4
  82. data/lib/git/commands/checkout.rb +4 -4
  83. data/lib/git/commands/checkout_index.rb +5 -4
  84. data/lib/git/commands/clean.rb +6 -4
  85. data/lib/git/commands/clone.rb +5 -4
  86. data/lib/git/commands/commit.rb +5 -4
  87. data/lib/git/commands/commit_tree.rb +5 -4
  88. data/lib/git/commands/config_option_syntax/add.rb +7 -4
  89. data/lib/git/commands/config_option_syntax/get.rb +9 -5
  90. data/lib/git/commands/config_option_syntax/get_all.rb +7 -4
  91. data/lib/git/commands/config_option_syntax/get_color.rb +8 -4
  92. data/lib/git/commands/config_option_syntax/get_color_bool.rb +7 -4
  93. data/lib/git/commands/config_option_syntax/get_regexp.rb +8 -4
  94. data/lib/git/commands/config_option_syntax/get_urlmatch.rb +8 -4
  95. data/lib/git/commands/config_option_syntax/list.rb +8 -4
  96. data/lib/git/commands/config_option_syntax/remove_section.rb +7 -2
  97. data/lib/git/commands/config_option_syntax/rename_section.rb +7 -2
  98. data/lib/git/commands/config_option_syntax/replace_all.rb +7 -2
  99. data/lib/git/commands/config_option_syntax/set.rb +7 -2
  100. data/lib/git/commands/config_option_syntax/unset.rb +7 -4
  101. data/lib/git/commands/config_option_syntax/unset_all.rb +7 -2
  102. data/lib/git/commands/describe.rb +5 -4
  103. data/lib/git/commands/diff.rb +13 -12
  104. data/lib/git/commands/diff_files.rb +7 -6
  105. data/lib/git/commands/diff_index.rb +10 -7
  106. data/lib/git/commands/fetch.rb +6 -4
  107. data/lib/git/commands/fsck.rb +6 -4
  108. data/lib/git/commands/gc.rb +6 -4
  109. data/lib/git/commands/grep.rb +6 -5
  110. data/lib/git/commands/init.rb +6 -4
  111. data/lib/git/commands/log.rb +287 -285
  112. data/lib/git/commands/ls_files.rb +69 -65
  113. data/lib/git/commands/ls_remote.rb +62 -56
  114. data/lib/git/commands/ls_tree.rb +50 -46
  115. data/lib/git/commands/maintenance/register.rb +25 -23
  116. data/lib/git/commands/maintenance/run.rb +7 -2
  117. data/lib/git/commands/maintenance/start.rb +7 -2
  118. data/lib/git/commands/maintenance/stop.rb +7 -2
  119. data/lib/git/commands/maintenance/unregister.rb +7 -2
  120. data/lib/git/commands/merge/abort.rb +2 -2
  121. data/lib/git/commands/merge/continue.rb +2 -2
  122. data/lib/git/commands/merge/quit.rb +2 -2
  123. data/lib/git/commands/merge/start.rb +7 -2
  124. data/lib/git/commands/merge.rb +2 -2
  125. data/lib/git/commands/merge_base.rb +7 -2
  126. data/lib/git/commands/mv.rb +7 -2
  127. data/lib/git/commands/name_rev.rb +7 -2
  128. data/lib/git/commands/pull.rb +7 -2
  129. data/lib/git/commands/push.rb +7 -2
  130. data/lib/git/commands/read_tree.rb +7 -2
  131. data/lib/git/commands/remote/add.rb +7 -2
  132. data/lib/git/commands/remote/get_url.rb +4 -2
  133. data/lib/git/commands/remote/list.rb +4 -2
  134. data/lib/git/commands/remote/prune.rb +4 -2
  135. data/lib/git/commands/remote/remove.rb +4 -2
  136. data/lib/git/commands/remote/rename.rb +4 -2
  137. data/lib/git/commands/remote/set_branches.rb +9 -2
  138. data/lib/git/commands/remote/set_head.rb +12 -5
  139. data/lib/git/commands/remote/set_url.rb +9 -2
  140. data/lib/git/commands/remote/set_url_add.rb +11 -2
  141. data/lib/git/commands/remote/set_url_delete.rb +9 -2
  142. data/lib/git/commands/remote/show.rb +18 -12
  143. data/lib/git/commands/remote/update.rb +19 -12
  144. data/lib/git/commands/remote.rb +2 -2
  145. data/lib/git/commands/repack.rb +67 -63
  146. data/lib/git/commands/reset.rb +51 -44
  147. data/lib/git/commands/rev_parse.rb +135 -128
  148. data/lib/git/commands/revert/abort.rb +2 -2
  149. data/lib/git/commands/revert/continue.rb +7 -2
  150. data/lib/git/commands/revert/quit.rb +2 -2
  151. data/lib/git/commands/revert/skip.rb +2 -2
  152. data/lib/git/commands/revert/start.rb +7 -2
  153. data/lib/git/commands/revert.rb +2 -2
  154. data/lib/git/commands/rm.rb +35 -36
  155. data/lib/git/commands/show.rb +1 -1
  156. data/lib/git/commands/show_ref/exclude_existing.rb +3 -4
  157. data/lib/git/commands/show_ref/exists.rb +15 -13
  158. data/lib/git/commands/show_ref/list.rb +39 -35
  159. data/lib/git/commands/show_ref/verify.rb +29 -27
  160. data/lib/git/commands/show_ref.rb +4 -4
  161. data/lib/git/commands/stash/apply.rb +31 -25
  162. data/lib/git/commands/stash/branch.rb +10 -8
  163. data/lib/git/commands/stash/clear.rb +7 -5
  164. data/lib/git/commands/stash/create.rb +10 -8
  165. data/lib/git/commands/stash/drop.rb +15 -9
  166. data/lib/git/commands/stash/list.rb +9 -5
  167. data/lib/git/commands/stash/pop.rb +21 -12
  168. data/lib/git/commands/stash/push.rb +20 -11
  169. data/lib/git/commands/stash/show.rb +21 -12
  170. data/lib/git/commands/stash/store.rb +19 -10
  171. data/lib/git/commands/stash.rb +2 -2
  172. data/lib/git/commands/status.rb +7 -2
  173. data/lib/git/commands/symbolic_ref/delete.rb +18 -14
  174. data/lib/git/commands/symbolic_ref/read.rb +24 -20
  175. data/lib/git/commands/symbolic_ref/update.rb +20 -17
  176. data/lib/git/commands/symbolic_ref.rb +4 -4
  177. data/lib/git/commands/tag/create.rb +45 -42
  178. data/lib/git/commands/tag/delete.rb +5 -3
  179. data/lib/git/commands/tag/list.rb +47 -44
  180. data/lib/git/commands/tag/verify.rb +12 -12
  181. data/lib/git/commands/tag.rb +2 -2
  182. data/lib/git/commands/update_ref/batch.rb +6 -1
  183. data/lib/git/commands/update_ref/delete.rb +19 -21
  184. data/lib/git/commands/update_ref/update.rb +22 -25
  185. data/lib/git/commands/update_ref.rb +4 -4
  186. data/lib/git/commands/version.rb +14 -11
  187. data/lib/git/commands/worktree/add.rb +50 -51
  188. data/lib/git/commands/worktree/list.rb +17 -17
  189. data/lib/git/commands/worktree/lock.rb +12 -12
  190. data/lib/git/commands/worktree/move.rb +16 -16
  191. data/lib/git/commands/worktree/prune.rb +2 -2
  192. data/lib/git/commands/worktree/remove.rb +2 -2
  193. data/lib/git/commands/worktree/repair.rb +2 -2
  194. data/lib/git/commands/worktree/unlock.rb +2 -2
  195. data/lib/git/commands/write_tree.rb +2 -2
  196. data/lib/git/commands.rb +4 -3
  197. data/lib/git/config.rb +47 -1
  198. data/lib/git/configuring.rb +89 -15
  199. data/lib/git/detached_head_info.rb +4 -1
  200. data/lib/git/diff.rb +2 -5
  201. data/lib/git/diff_file_numstat_info.rb +3 -1
  202. data/lib/git/diff_file_patch_info.rb +3 -1
  203. data/lib/git/diff_file_raw_info.rb +3 -1
  204. data/lib/git/diff_info.rb +8 -15
  205. data/lib/git/diff_path_status.rb +16 -3
  206. data/lib/git/diff_result.rb +3 -1
  207. data/lib/git/diff_stats.rb +15 -0
  208. data/lib/git/dirstat_info.rb +24 -8
  209. data/lib/git/encoding_utils.rb +32 -1
  210. data/lib/git/errors.rb +19 -9
  211. data/lib/git/escaped_path.rb +55 -3
  212. data/lib/git/execution_context/repository.rb +15 -0
  213. data/lib/git/execution_context.rb +76 -74
  214. data/lib/git/file_ref.rb +6 -3
  215. data/lib/git/fsck_object.rb +8 -0
  216. data/lib/git/fsck_result.rb +26 -15
  217. data/lib/git/log.rb +152 -5
  218. data/lib/git/object.rb +284 -8
  219. data/lib/git/parsers/branch.rb +18 -0
  220. data/lib/git/parsers/cat_file.rb +2 -2
  221. data/lib/git/parsers/diff.rb +213 -5
  222. data/lib/git/parsers/fsck.rb +11 -0
  223. data/lib/git/parsers/ls_remote.rb +1 -1
  224. data/lib/git/parsers/stash.rb +51 -0
  225. data/lib/git/parsers/tag.rb +38 -0
  226. data/lib/git/remote.rb +34 -2
  227. data/lib/git/repository/branching.rb +23 -21
  228. data/lib/git/repository/committing.rb +46 -6
  229. data/lib/git/repository/context_helpers.rb +45 -16
  230. data/lib/git/repository/diffing.rb +32 -23
  231. data/lib/git/repository/factories.rb +153 -28
  232. data/lib/git/repository/inspecting.rb +1 -1
  233. data/lib/git/repository/logging.rb +169 -30
  234. data/lib/git/repository/maintenance.rb +1 -1
  235. data/lib/git/repository/merging.rb +3 -3
  236. data/lib/git/repository/object_operations.rb +108 -14
  237. data/lib/git/repository/remote_operations.rb +150 -171
  238. data/lib/git/repository/shared_private.rb +7 -5
  239. data/lib/git/repository/staging.rb +36 -12
  240. data/lib/git/repository/stashing.rb +16 -16
  241. data/lib/git/repository/status_operations.rb +1 -1
  242. data/lib/git/repository/worktree_operations.rb +1 -1
  243. data/lib/git/repository.rb +287 -6
  244. data/lib/git/stash.rb +16 -21
  245. data/lib/git/stash_info.rb +4 -4
  246. data/lib/git/stashes.rb +20 -26
  247. data/lib/git/status.rb +25 -13
  248. data/lib/git/tag_delete_failure.rb +3 -0
  249. data/lib/git/tag_delete_result.rb +6 -3
  250. data/lib/git/tag_info.rb +1 -0
  251. data/lib/git/url.rb +12 -5
  252. data/lib/git/version.rb +6 -4
  253. data/lib/git/version_constraint.rb +4 -0
  254. data/lib/git.rb +400 -314
  255. data/redesign/3_architecture_implementation.md +81 -30
  256. data/redesign/Phase 4 - Step A.md +6 -0
  257. data/redesign/Phase 4 - Step B.md +921 -0
  258. data/redesign/Phase 4 - Step C.md +833 -0
  259. data/redesign/c1a-public-api-scope.tsv +256 -0
  260. data/redesign/phase-4-step-b-test-audit.tsv +485 -0
  261. data/tasks/yard.rake +24 -17
  262. metadata +24 -58
  263. data/commitlint.test +0 -4
  264. data/lib/git/args_builder.rb +0 -103
  265. data/lib/git/command_line_result.rb +0 -8
  266. data/lib/git/deprecation.rb +0 -9
  267. data/lib/git/repository/configuring.rb +0 -351
  268. data/tasks/test.rake +0 -25
  269. /data/.github/{skills → skills-deprecated}/extract-facade-from-base-lib/KEYWORD_ARG_REMEDIATION.md +0 -0
  270. /data/.github/{skills → skills-deprecated}/review-backward-compatibility/SKILL.md +0 -0
@@ -57,6 +57,8 @@ module Git
57
57
  # \\{old_dir => new_dir}/file.rb
58
58
  # dir/\\{old_name.rb => new_name.rb}
59
59
  RENAME_PATTERN = /\A(.+) => (.+)\z/
60
+
61
+ # Brace-expanded rename format from git numstat -M output
60
62
  BRACE_RENAME_PATTERN = /\A(.*)\{(.+) => (.+)\}(.*)\z/
61
63
 
62
64
  module_function
@@ -64,8 +66,11 @@ module Git
64
66
  # Build a DiffResult from parsed components
65
67
  #
66
68
  # @param files [Array] array of file info objects
69
+ #
67
70
  # @param shortstat [Hash] parsed shortstat data
71
+ #
68
72
  # @param dirstat [Git::DirstatInfo, nil] parsed dirstat data
73
+ #
69
74
  # @return [Git::DiffResult]
70
75
  #
71
76
  def build_result(files:, shortstat:, dirstat:)
@@ -96,6 +101,7 @@ module Git
96
101
  # # => { files_changed: 3, insertions: 10, deletions: 5 }
97
102
  #
98
103
  # @param line [String, nil] the shortstat line
104
+ #
99
105
  # @return [Hash] { files_changed:, insertions:, deletions: }
100
106
  #
101
107
  def parse_shortstat(line)
@@ -115,6 +121,7 @@ module Git
115
121
  # # => #<Git::DirstatInfo entries: [...]>
116
122
  #
117
123
  # @param lines [Array<String>] dirstat output lines
124
+ #
118
125
  # @return [Git::DirstatInfo]
119
126
  #
120
127
  def parse_dirstat(lines)
@@ -129,6 +136,7 @@ module Git
129
136
  # Parse a stat value (handles '-' for binary files)
130
137
  #
131
138
  # @param value [String] the stat value string
139
+ #
132
140
  # @return [Integer] the numeric value (0 for binary files)
133
141
  #
134
142
  def parse_stat_value(value)
@@ -138,6 +146,7 @@ module Git
138
146
  # Unescape quoted path from git output
139
147
  #
140
148
  # @param path [String] potentially quoted path
149
+ #
141
150
  # @return [String] unescaped path
142
151
  #
143
152
  def unescape_path(path)
@@ -153,7 +162,9 @@ module Git
153
162
  # Parse numstat output into DiffResult
154
163
  #
155
164
  # @param output [String] raw numstat + shortstat output
165
+ #
156
166
  # @param include_dirstat [Boolean] whether dirstat output is expected
167
+ #
157
168
  # @return [Git::DiffResult]
158
169
  #
159
170
  def parse(output, include_dirstat: false)
@@ -170,6 +181,7 @@ module Git
170
181
  # Parse numstat lines into DiffFileNumstatInfo array
171
182
  #
172
183
  # @param lines [Array<String>] numstat lines
184
+ #
173
185
  # @return [Array<Git::DiffFileNumstatInfo>]
174
186
  #
175
187
  def parse_file_stats(lines)
@@ -188,7 +200,9 @@ module Git
188
200
  # Parse numstat lines into a path -> stats hash (for combining with other formats)
189
201
  #
190
202
  # @param lines [Array<String>] numstat lines
203
+ #
191
204
  # @param include_binary [Boolean] whether to include binary flag
205
+ #
192
206
  # @return [Hash<String, Hash>] path to stats mapping (keyed by destination path)
193
207
  #
194
208
  def parse_as_map(lines, include_binary: false)
@@ -200,6 +214,16 @@ module Git
200
214
  end
201
215
  end
202
216
 
217
+ # Builds stats data from a numstat entry
218
+ #
219
+ # @param insertions_s [String] insertions field from numstat output
220
+ #
221
+ # @param deletions_s [String] deletions field from numstat output
222
+ #
223
+ # @param include_binary [Boolean] whether to include the `:binary` key
224
+ #
225
+ # @return [Hash] stats hash with insertion and deletion totals
226
+ #
203
227
  def build_stats(insertions_s, deletions_s, include_binary)
204
228
  stats = { insertions: Diff.parse_stat_value(insertions_s),
205
229
  deletions: Diff.parse_stat_value(deletions_s) }
@@ -210,7 +234,9 @@ module Git
210
234
  # Split output into numstat, shortstat, and dirstat sections
211
235
  #
212
236
  # @param lines [Array<String>] all output lines
237
+ #
213
238
  # @param include_dirstat [Boolean] whether to expect dirstat section
239
+ #
214
240
  # @return [Array] [numstat_lines, shortstat_line, dirstat_lines]
215
241
  #
216
242
  def split_sections(lines, include_dirstat)
@@ -224,7 +250,8 @@ module Git
224
250
  # Parse potential rename path into [dst_path, src_path]
225
251
  #
226
252
  # @param filename [String] the path string from numstat output
227
- # @return [Array<String, String|nil>] [destination_path, source_path_or_nil]
253
+ #
254
+ # @return [Array(String, (String, nil))] [destination_path, source_path_or_nil]
228
255
  #
229
256
  def parse_rename_path(filename)
230
257
  if (match = filename.match(BRACE_RENAME_PATTERN))
@@ -245,7 +272,9 @@ module Git
245
272
  # Parse combined raw + numstat + shortstat output into DiffResult
246
273
  #
247
274
  # @param output [String] combined output
275
+ #
248
276
  # @param include_dirstat [Boolean] whether dirstat output is expected
277
+ #
249
278
  # @return [Git::DiffResult]
250
279
  #
251
280
  def parse(output, include_dirstat: false)
@@ -262,8 +291,10 @@ module Git
262
291
  # Split output into raw, numstat, shortstat, and dirstat sections
263
292
  #
264
293
  # @param output [String] combined output
294
+ #
265
295
  # @param include_dirstat [Boolean] whether to expect dirstat section
266
- # @return [Array<Array<String>, Array<String>, String|nil, Array<String>>]
296
+ #
297
+ # @return [Array(Array<String>, Array<String>, (String, nil), Array<String>)]
267
298
  #
268
299
  def split_sections(output, include_dirstat)
269
300
  lines = output.split("\n").reject(&:empty?)
@@ -279,7 +310,9 @@ module Git
279
310
  # Parse a single --raw output line
280
311
  #
281
312
  # @param line [String] a single raw output line
313
+ #
282
314
  # @param numstat_map [Hash<String, Hash>] path to stats mapping
315
+ #
283
316
  # @return [Git::DiffFileRawInfo]
284
317
  #
285
318
  def parse_raw_line(line, numstat_map)
@@ -289,6 +322,12 @@ module Git
289
322
  build_raw_info(parsed, stats)
290
323
  end
291
324
 
325
+ # Parses the fields from a raw diff line
326
+ #
327
+ # @param line [String] a single raw output line
328
+ #
329
+ # @return [Hash] parsed metadata for one raw diff entry
330
+ #
292
331
  def parse_raw_line_parts(line)
293
332
  parts = line[1..].split(/\s+/, 5)
294
333
  status_char, *paths = parts[4].split("\t")
@@ -298,6 +337,14 @@ module Git
298
337
  similarity: similarity, src_path: src_path, dst_path: dst_path }
299
338
  end
300
339
 
340
+ # Builds a raw diff file info object from parsed metadata and stats
341
+ #
342
+ # @param parsed [Hash] parsed metadata from {#parse_raw_line_parts}
343
+ #
344
+ # @param stats [Hash] insertion, deletion, and binary information
345
+ #
346
+ # @return [Git::DiffFileRawInfo]
347
+ #
301
348
  def build_raw_info(parsed, stats)
302
349
  Git::DiffFileRawInfo.new(
303
350
  src: build_file_ref(parsed[:modes][0], parsed[:shas][0], parsed[:src_path]),
@@ -309,7 +356,8 @@ module Git
309
356
  # Parse status character and optional similarity percentage
310
357
  #
311
358
  # @param status_char [String] e.g., 'M', 'A', 'R075'
312
- # @return [Array<Symbol, Integer|nil>] [status, similarity]
359
+ #
360
+ # @return [Array(Symbol, (Integer, nil))] [status, similarity]
313
361
  #
314
362
  def parse_status(status_char)
315
363
  letter = status_char[0]
@@ -320,7 +368,8 @@ module Git
320
368
  # Extract source and destination paths from raw output paths
321
369
  #
322
370
  # @param paths [Array<String>] paths array
323
- # @return [Array<String|nil, String|nil>] [src_path, dst_path]
371
+ #
372
+ # @return [Array((String, nil), (String, nil))] [src_path, dst_path]
324
373
  #
325
374
  def extract_paths(paths)
326
375
  if paths.length == 2
@@ -334,8 +383,11 @@ module Git
334
383
  # Build a FileRef, returning nil if the file doesn't exist on this side
335
384
  #
336
385
  # @param mode [String] file mode
386
+ #
337
387
  # @param sha [String] file SHA
388
+ #
338
389
  # @param path [String, nil] file path
390
+ #
339
391
  # @return [Git::FileRef, nil]
340
392
  #
341
393
  def build_file_ref(mode, sha, path)
@@ -347,19 +399,43 @@ module Git
347
399
 
348
400
  # Parser for --patch output (combined with numstat)
349
401
  module Patch
402
+ # Header line pattern for each patch file section
350
403
  DIFF_HEADER_PATTERN = %r{\Adiff --git ("?)a/(.+?)\1 ("?)b/(.+?)\3\z}
404
+
405
+ # Index line pattern with source/destination SHAs and optional mode
351
406
  INDEX_PATTERN = /^index ([0-9a-f]{4,40})\.\.([0-9a-f]{4,40})( ......)?/
407
+
408
+ # File mode line pattern for file creation and deletion
352
409
  FILE_MODE_PATTERN = /^(new|deleted) file mode (......)/
410
+
411
+ # Old mode line pattern for mode changes
353
412
  OLD_MODE_PATTERN = /^old mode (......)/
413
+
414
+ # New mode line pattern for mode changes
354
415
  NEW_MODE_PATTERN = /^new mode (......)/
416
+
417
+ # Binary patch marker line pattern
355
418
  BINARY_PATTERN = /^Binary files /
419
+
420
+ # Git binary patch header line pattern
356
421
  GIT_BINARY_PATCH_PATTERN = /^GIT binary patch$/
422
+
423
+ # Rename source path line pattern
357
424
  RENAME_FROM_PATTERN = /^rename from (.+)$/
425
+
426
+ # Rename destination path line pattern
358
427
  RENAME_TO_PATTERN = /^rename to (.+)$/
428
+
429
+ # Copy source path line pattern
359
430
  COPY_FROM_PATTERN = /^copy from (.+)$/
431
+
432
+ # Copy destination path line pattern
360
433
  COPY_TO_PATTERN = /^copy to (.+)$/
434
+
435
+ # Similarity percentage line pattern
361
436
  SIMILARITY_PATTERN = /^similarity index (\d+)%$/
362
437
 
438
+ # Maps patch metadata status words to diff status symbols
363
439
  PATCH_STATUS_MAP = {
364
440
  'new' => :added,
365
441
  'deleted' => :deleted,
@@ -374,7 +450,9 @@ module Git
374
450
  # Parse combined patch + numstat + shortstat output into DiffResult
375
451
  #
376
452
  # @param output [String] combined output
453
+ #
377
454
  # @param include_dirstat [Boolean] whether dirstat output is expected
455
+ #
378
456
  # @return [Git::DiffResult]
379
457
  #
380
458
  def parse(output, include_dirstat: false)
@@ -393,8 +471,10 @@ module Git
393
471
  # Split output into numstat, shortstat, dirstat, and patch sections
394
472
  #
395
473
  # @param output [String] combined output
474
+ #
396
475
  # @param include_dirstat [Boolean] whether to expect dirstat section
397
- # @return [Array<Array<String>, String|nil, Array<String>, String>]
476
+ #
477
+ # @return [Array(Array<String>, (String, nil), Array<String>, String)]
398
478
  #
399
479
  def split_sections(output, include_dirstat)
400
480
  lines = output.lines
@@ -404,6 +484,16 @@ module Git
404
484
  split_pre_diff(pre_diff_lines, include_dirstat, patch_text)
405
485
  end
406
486
 
487
+ # Splits pre-patch lines into numstat, shortstat, and dirstat sections
488
+ #
489
+ # @param pre_diff_lines [Array<String>] lines before the first diff header
490
+ #
491
+ # @param include_dirstat [Boolean] whether dirstat output is expected
492
+ #
493
+ # @param patch_text [String] patch text starting at the first diff header
494
+ #
495
+ # @return [Array(Array<String>, (String, nil), Array<String>, String)]
496
+ #
407
497
  def split_pre_diff(pre_diff_lines, include_dirstat, patch_text)
408
498
  shortstat_index = pre_diff_lines.index { |l| l.match?(/^\s*\d+\s+files?\s+changed/) }
409
499
  return [pre_diff_lines, nil, [], patch_text] unless shortstat_index
@@ -413,10 +503,17 @@ module Git
413
503
  end
414
504
 
415
505
  # Methods for parsing patch metadata lines (index, mode, rename, etc.)
506
+ #
416
507
  # @api private
417
508
  module PatchMetadataParser
418
509
  private
419
510
 
511
+ # Parses a metadata line for the current patch file
512
+ #
513
+ # @param line [String] a metadata line from the patch block
514
+ #
515
+ # @return [void]
516
+ #
420
517
  def parse_metadata_line(line)
421
518
  try_parse_index(line)
422
519
  try_parse_file_mode(line)
@@ -426,6 +523,12 @@ module Git
426
523
  try_mark_binary(line)
427
524
  end
428
525
 
526
+ # Parses index metadata and updates mode defaults when present
527
+ #
528
+ # @param line [String] a metadata line from the patch block
529
+ #
530
+ # @return [void]
531
+ #
429
532
  def try_parse_index(line)
430
533
  return unless (match = line.match(INDEX_PATTERN))
431
534
 
@@ -437,6 +540,12 @@ module Git
437
540
  @current_file[:src_mode] = @current_file[:dst_mode] = mode
438
541
  end
439
542
 
543
+ # Parses file creation and deletion mode lines
544
+ #
545
+ # @param line [String] a metadata line from the patch block
546
+ #
547
+ # @return [void]
548
+ #
440
549
  def try_parse_file_mode(line)
441
550
  return unless (match = line.match(FILE_MODE_PATTERN))
442
551
 
@@ -445,6 +554,12 @@ module Git
445
554
  apply_file_mode(type, mode)
446
555
  end
447
556
 
557
+ # Parses old/new mode lines and detects type changes
558
+ #
559
+ # @param line [String] a metadata line from the patch block
560
+ #
561
+ # @return [void]
562
+ #
448
563
  def try_parse_old_new_mode(line)
449
564
  if (match = line.match(OLD_MODE_PATTERN))
450
565
  @current_file[:src_mode] = match[1]
@@ -455,6 +570,10 @@ module Git
455
570
  end
456
571
  end
457
572
 
573
+ # Marks the current entry as a type change when type bits differ
574
+ #
575
+ # @return [void]
576
+ #
458
577
  def detect_type_change
459
578
  src_mode = @current_file[:src_mode]
460
579
  dst_mode = @current_file[:dst_mode]
@@ -465,6 +584,14 @@ module Git
465
584
  @current_file[:status] = :type_changed if src_mode[0, 3] != dst_mode[0, 3]
466
585
  end
467
586
 
587
+ # Applies source or destination mode updates based on file operation type
588
+ #
589
+ # @param type [String] operation type (`'new'` or `'deleted'`)
590
+ #
591
+ # @param mode [String] file mode from patch metadata
592
+ #
593
+ # @return [void]
594
+ #
468
595
  def apply_file_mode(type, mode)
469
596
  case type
470
597
  when 'new'
@@ -476,11 +603,29 @@ module Git
476
603
  end
477
604
  end
478
605
 
606
+ # Parses rename or copy metadata for the current patch file
607
+ #
608
+ # @param line [String] a metadata line from the patch block
609
+ #
610
+ # @return [Symbol, nil] parsed status when a rename or copy line matches
611
+ #
479
612
  def try_parse_rename(line)
480
613
  try_parse_rename_or_copy(line, RENAME_FROM_PATTERN, RENAME_TO_PATTERN, :renamed) ||
481
614
  try_parse_rename_or_copy(line, COPY_FROM_PATTERN, COPY_TO_PATTERN, :copied)
482
615
  end
483
616
 
617
+ # Parses one side of rename or copy metadata
618
+ #
619
+ # @param line [String] a metadata line from the patch block
620
+ #
621
+ # @param from_pattern [Regexp] pattern for source path lines
622
+ #
623
+ # @param to_pattern [Regexp] pattern for destination path lines
624
+ #
625
+ # @param status [Symbol] status to set when a pattern matches
626
+ #
627
+ # @return [Symbol, nil] the assigned status when matched
628
+ #
484
629
  def try_parse_rename_or_copy(line, from_pattern, to_pattern, status)
485
630
  if (match = line.match(from_pattern))
486
631
  @current_file[:src_path] = Diff.unescape_path(match[1])
@@ -491,22 +636,43 @@ module Git
491
636
  end
492
637
  end
493
638
 
639
+ # Parses similarity index metadata
640
+ #
641
+ # @param line [String] a metadata line from the patch block
642
+ #
643
+ # @return [Integer, nil] parsed similarity percentage when matched
644
+ #
494
645
  def try_parse_similarity(line)
495
646
  return unless (match = line.match(SIMILARITY_PATTERN))
496
647
 
497
648
  @current_file[:similarity] = match[1].to_i
498
649
  end
499
650
 
651
+ # Marks the current file as binary when binary markers are present
652
+ #
653
+ # @param line [String] a metadata line from the patch block
654
+ #
655
+ # @return [Boolean, nil] true when binary metadata is detected
656
+ #
500
657
  def try_mark_binary(line)
501
658
  @current_file[:binary] = true if line.match?(BINARY_PATTERN) || line.match?(GIT_BINARY_PATCH_PATTERN)
502
659
  end
503
660
  end
504
661
 
505
662
  # Stateful parser for unified diff patch output
663
+ #
506
664
  # @api private
507
665
  class PatchFileParser
508
666
  include PatchMetadataParser
509
667
 
668
+ # Initializes parser state for a patch text block
669
+ #
670
+ # @param patch_text [String] unified diff patch text
671
+ #
672
+ # @param numstat_map [Hash<String, Hash>] path to insert/delete stats
673
+ #
674
+ # @return [void]
675
+ #
510
676
  def initialize(patch_text, numstat_map = {})
511
677
  @patch_text = patch_text
512
678
  @numstat_map = numstat_map
@@ -514,6 +680,10 @@ module Git
514
680
  @current_file = nil
515
681
  end
516
682
 
683
+ # Parses patch text into file patch info objects
684
+ #
685
+ # @return [Array<Git::DiffFilePatchInfo>]
686
+ #
517
687
  def parse
518
688
  @patch_text.split("\n").each { |line| process_line(line) }
519
689
  finalize_current_file
@@ -522,6 +692,12 @@ module Git
522
692
 
523
693
  private
524
694
 
695
+ # Processes one patch line and updates parser state
696
+ #
697
+ # @param line [String] a single line from patch text
698
+ #
699
+ # @return [void]
700
+ #
525
701
  def process_line(line)
526
702
  if (match = line.match(DIFF_HEADER_PATTERN))
527
703
  start_new_file(match, line)
@@ -530,6 +706,14 @@ module Git
530
706
  end
531
707
  end
532
708
 
709
+ # Starts a new file section from a `diff --git` header
710
+ #
711
+ # @param match [MatchData] parsed header match for current diff file
712
+ #
713
+ # @param line [String] raw `diff --git` header line
714
+ #
715
+ # @return [void]
716
+ #
533
717
  def start_new_file(match, line)
534
718
  finalize_current_file
535
719
  @current_file = default_file_state.merge(
@@ -539,16 +723,30 @@ module Git
539
723
  )
540
724
  end
541
725
 
726
+ # Builds the default state hash for a new patch file entry
727
+ #
728
+ # @return [Hash] default metadata for an in-progress file parse
729
+ #
542
730
  def default_file_state
543
731
  { src_mode: nil, dst_mode: nil, src_sha: '', dst_sha: '',
544
732
  src_path: nil, dst_path: nil, status: :modified, similarity: nil, binary: false }
545
733
  end
546
734
 
735
+ # Appends metadata and patch text to the current file entry
736
+ #
737
+ # @param line [String] a metadata or patch content line
738
+ #
739
+ # @return [void]
740
+ #
547
741
  def append_to_current_file(line)
548
742
  parse_metadata_line(line)
549
743
  @current_file[:patch] = "#{@current_file[:patch]}\n#{line}"
550
744
  end
551
745
 
746
+ # Finalizes and stores the current file entry
747
+ #
748
+ # @return [void]
749
+ #
552
750
  def finalize_current_file
553
751
  return unless @current_file
554
752
 
@@ -556,6 +754,10 @@ module Git
556
754
  @current_file = nil
557
755
  end
558
756
 
757
+ # Builds a finalized patch info object for the current file
758
+ #
759
+ # @return [Git::DiffFilePatchInfo]
760
+ #
559
761
  def build_patch_info
560
762
  path = @current_file[:dst_path] || @current_file[:src_path]
561
763
  stats = @numstat_map.fetch(path, { insertions: 0, deletions: 0 })
@@ -568,6 +770,12 @@ module Git
568
770
  )
569
771
  end
570
772
 
773
+ # Builds a file reference for one side of the diff entry
774
+ #
775
+ # @param side [Symbol] diff side key (`:src` or `:dst`)
776
+ #
777
+ # @return [Git::FileRef, nil]
778
+ #
571
779
  def build_file_ref(side)
572
780
  path = @current_file[:"#{side}_path"]
573
781
  return nil if path.nil?
@@ -60,6 +60,7 @@ module Git
60
60
  # # => #<Git::FsckResult dangling: [...], missing: [...]>
61
61
  #
62
62
  # @param stdout [String] output from git fsck command
63
+ #
63
64
  # @return [Git::FsckResult] the parsed result
64
65
  #
65
66
  def parse(stdout)
@@ -71,7 +72,9 @@ module Git
71
72
  # Parse a single line of fsck output
72
73
  #
73
74
  # @param line [String] a line of output
75
+ #
74
76
  # @param result [Hash] the result hash to populate
77
+ #
75
78
  # @return [Boolean] true if the line was parsed
76
79
  #
77
80
  def parse_line(line, result)
@@ -84,7 +87,9 @@ module Git
84
87
  # Parse a dangling/missing/unreachable object line
85
88
  #
86
89
  # @param line [String] a line of output
90
+ #
87
91
  # @param result [Hash] the result hash to populate
92
+ #
88
93
  # @return [Boolean] true if the line was parsed
89
94
  #
90
95
  def parse_object_line(line, result)
@@ -96,7 +101,9 @@ module Git
96
101
  # Parse a warning line
97
102
  #
98
103
  # @param line [String] a line of output
104
+ #
99
105
  # @param result [Hash] the result hash to populate
106
+ #
100
107
  # @return [Boolean] true if the line was parsed
101
108
  #
102
109
  def parse_warning_line(line, result)
@@ -108,7 +115,9 @@ module Git
108
115
  # Parse a root line
109
116
  #
110
117
  # @param line [String] a line of output
118
+ #
111
119
  # @param result [Hash] the result hash to populate
120
+ #
112
121
  # @return [Boolean] true if the line was parsed
113
122
  #
114
123
  def parse_root_line(line, result)
@@ -120,7 +129,9 @@ module Git
120
129
  # Parse a tagged line
121
130
  #
122
131
  # @param line [String] a line of output
132
+ #
123
133
  # @param result [Hash] the result hash to populate
134
+ #
124
135
  # @return [Boolean] true if the line was parsed
125
136
  #
126
137
  def parse_tagged_line(line, result)
@@ -36,7 +36,7 @@ module Git
36
36
  #
37
37
  # @param line [String] a single line from ls-remote stdout
38
38
  #
39
- # @return [Array(String, String|nil, Hash)] `[type, name, value]` where
39
+ # @return [Array(String, (String, nil), Hash)] `[type, name, value]` where
40
40
  # `value` is `{ ref: String, sha: String }`
41
41
  #
42
42
  # @raise [Git::UnexpectedResultError] if the line is not in `<sha>\t<ref>` format