git 4.3.2 → 5.0.0.beta.2

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 (297) hide show
  1. checksums.yaml +4 -4
  2. data/.github/copilot-instructions.md +73 -2705
  3. data/.github/prompts/iteratively-address-copilot-reviews.prompt.md +188 -0
  4. data/.github/pull_request_template.md +3 -1
  5. data/.github/skills/breaking-change-analysis/SKILL.md +102 -0
  6. data/.github/skills/ci-cd-troubleshooting/SKILL.md +264 -0
  7. data/.github/skills/command-implementation/REFERENCE.md +993 -0
  8. data/.github/skills/command-implementation/SKILL.md +229 -0
  9. data/.github/skills/command-test-conventions/SKILL.md +660 -0
  10. data/.github/skills/command-yard-documentation/SKILL.md +426 -0
  11. data/.github/skills/dependency-management/SKILL.md +72 -0
  12. data/.github/skills/development-workflow/SKILL.md +506 -0
  13. data/.github/skills/extract-command-from-lib/SKILL.md +487 -0
  14. data/.github/skills/extract-facade-from-base-lib/KEYWORD_ARG_REMEDIATION.md +22 -0
  15. data/.github/skills/extract-facade-from-base-lib/SKILL.md +600 -0
  16. data/.github/skills/facade-implementation/REFERENCE.md +840 -0
  17. data/.github/skills/facade-implementation/SKILL.md +274 -0
  18. data/.github/skills/facade-test-conventions/SKILL.md +394 -0
  19. data/.github/skills/facade-yard-documentation/SKILL.md +429 -0
  20. data/.github/skills/make-skill-template/SKILL.md +176 -0
  21. data/.github/skills/pr-readiness-review/SKILL.md +185 -0
  22. data/.github/skills/project-context/SKILL.md +313 -0
  23. data/.github/skills/pull-request-review/SKILL.md +168 -0
  24. data/.github/skills/refactor-command-to-commandlineresult/SKILL.md +131 -0
  25. data/.github/skills/release-management/SKILL.md +125 -0
  26. data/.github/skills/review-arguments-dsl/CHECKLIST.md +788 -0
  27. data/.github/skills/review-arguments-dsl/SKILL.md +214 -0
  28. data/.github/skills/review-backward-compatibility/SKILL.md +275 -0
  29. data/.github/skills/review-cross-command-consistency/SKILL.md +139 -0
  30. data/.github/skills/reviewing-skills/SKILL.md +189 -0
  31. data/.github/skills/rspec-unit-testing-standards/SKILL.md +639 -0
  32. data/.github/skills/tdd-refactor-step/SKILL.md +236 -0
  33. data/.github/skills/test-debugging/SKILL.md +160 -0
  34. data/.github/skills/yard-documentation/SKILL.md +793 -0
  35. data/.github/workflows/continuous_integration.yml +3 -2
  36. data/.github/workflows/enforce_conventional_commits.yml +1 -1
  37. data/.github/workflows/experimental_continuous_integration.yml +2 -2
  38. data/.github/workflows/release.yml +3 -4
  39. data/.gitignore +8 -0
  40. data/.husky/pre-commit +13 -0
  41. data/.release-please-manifest.json +1 -1
  42. data/.rspec +3 -0
  43. data/.rubocop.yml +12 -3
  44. data/.rubocop_todo.yml +23 -5
  45. data/.yardopts +1 -0
  46. data/CHANGELOG.md +0 -40
  47. data/CONTRIBUTING.md +694 -53
  48. data/README.md +67 -15
  49. data/Rakefile +61 -9
  50. data/UPGRADING.md +141 -0
  51. data/commitlint.test +4 -0
  52. data/git.gemspec +18 -7
  53. data/lib/git/args_builder.rb +0 -8
  54. data/lib/git/branch.rb +369 -43
  55. data/lib/git/branch_delete_failure.rb +31 -0
  56. data/lib/git/branch_delete_result.rb +63 -0
  57. data/lib/git/branch_info.rb +178 -0
  58. data/lib/git/branches.rb +122 -24
  59. data/lib/git/command_line/base.rb +255 -0
  60. data/lib/git/command_line/capturing.rb +251 -0
  61. data/lib/git/command_line/result.rb +96 -0
  62. data/lib/git/command_line/streaming.rb +196 -0
  63. data/lib/git/command_line.rb +43 -322
  64. data/lib/git/command_line_result.rb +4 -88
  65. data/lib/git/commands/add.rb +131 -0
  66. data/lib/git/commands/am/abort.rb +43 -0
  67. data/lib/git/commands/am/apply.rb +252 -0
  68. data/lib/git/commands/am/continue.rb +43 -0
  69. data/lib/git/commands/am/quit.rb +43 -0
  70. data/lib/git/commands/am/retry.rb +47 -0
  71. data/lib/git/commands/am/show_current_patch.rb +64 -0
  72. data/lib/git/commands/am/skip.rb +42 -0
  73. data/lib/git/commands/am.rb +33 -0
  74. data/lib/git/commands/apply.rb +237 -0
  75. data/lib/git/commands/archive/list_formats.rb +46 -0
  76. data/lib/git/commands/archive.rb +140 -0
  77. data/lib/git/commands/arguments.rb +3510 -0
  78. data/lib/git/commands/base.rb +404 -0
  79. data/lib/git/commands/branch/copy.rb +94 -0
  80. data/lib/git/commands/branch/create.rb +173 -0
  81. data/lib/git/commands/branch/delete.rb +80 -0
  82. data/lib/git/commands/branch/list.rb +162 -0
  83. data/lib/git/commands/branch/move.rb +94 -0
  84. data/lib/git/commands/branch/set_upstream.rb +86 -0
  85. data/lib/git/commands/branch/show_current.rb +49 -0
  86. data/lib/git/commands/branch/unset_upstream.rb +57 -0
  87. data/lib/git/commands/branch.rb +34 -0
  88. data/lib/git/commands/cat_file/batch.rb +369 -0
  89. data/lib/git/commands/cat_file/filtered.rb +105 -0
  90. data/lib/git/commands/cat_file/raw.rb +216 -0
  91. data/lib/git/commands/cat_file.rb +49 -0
  92. data/lib/git/commands/checkout/branch.rb +151 -0
  93. data/lib/git/commands/checkout/files.rb +115 -0
  94. data/lib/git/commands/checkout.rb +38 -0
  95. data/lib/git/commands/checkout_index.rb +105 -0
  96. data/lib/git/commands/clean.rb +100 -0
  97. data/lib/git/commands/clone.rb +240 -0
  98. data/lib/git/commands/commit.rb +272 -0
  99. data/lib/git/commands/commit_tree.rb +100 -0
  100. data/lib/git/commands/config_option_syntax/add.rb +83 -0
  101. data/lib/git/commands/config_option_syntax/get.rb +117 -0
  102. data/lib/git/commands/config_option_syntax/get_all.rb +115 -0
  103. data/lib/git/commands/config_option_syntax/get_color.rb +91 -0
  104. data/lib/git/commands/config_option_syntax/get_color_bool.rb +93 -0
  105. data/lib/git/commands/config_option_syntax/get_regexp.rb +115 -0
  106. data/lib/git/commands/config_option_syntax/get_urlmatch.rb +107 -0
  107. data/lib/git/commands/config_option_syntax/list.rb +107 -0
  108. data/lib/git/commands/config_option_syntax/remove_section.rb +74 -0
  109. data/lib/git/commands/config_option_syntax/rename_section.rb +78 -0
  110. data/lib/git/commands/config_option_syntax/replace_all.rb +104 -0
  111. data/lib/git/commands/config_option_syntax/set.rb +114 -0
  112. data/lib/git/commands/config_option_syntax/unset.rb +89 -0
  113. data/lib/git/commands/config_option_syntax/unset_all.rb +89 -0
  114. data/lib/git/commands/config_option_syntax.rb +56 -0
  115. data/lib/git/commands/describe.rb +155 -0
  116. data/lib/git/commands/diff.rb +656 -0
  117. data/lib/git/commands/diff_files.rb +518 -0
  118. data/lib/git/commands/diff_index.rb +496 -0
  119. data/lib/git/commands/fetch.rb +352 -0
  120. data/lib/git/commands/fsck.rb +136 -0
  121. data/lib/git/commands/gc.rb +132 -0
  122. data/lib/git/commands/grep.rb +338 -0
  123. data/lib/git/commands/init.rb +99 -0
  124. data/lib/git/commands/log.rb +632 -0
  125. data/lib/git/commands/ls_files.rb +191 -0
  126. data/lib/git/commands/ls_remote.rb +155 -0
  127. data/lib/git/commands/ls_tree.rb +131 -0
  128. data/lib/git/commands/maintenance/register.rb +75 -0
  129. data/lib/git/commands/maintenance/run.rb +104 -0
  130. data/lib/git/commands/maintenance/start.rb +66 -0
  131. data/lib/git/commands/maintenance/stop.rb +55 -0
  132. data/lib/git/commands/maintenance/unregister.rb +79 -0
  133. data/lib/git/commands/maintenance.rb +31 -0
  134. data/lib/git/commands/merge/abort.rb +44 -0
  135. data/lib/git/commands/merge/continue.rb +44 -0
  136. data/lib/git/commands/merge/quit.rb +46 -0
  137. data/lib/git/commands/merge/start.rb +245 -0
  138. data/lib/git/commands/merge.rb +28 -0
  139. data/lib/git/commands/merge_base.rb +86 -0
  140. data/lib/git/commands/mv.rb +77 -0
  141. data/lib/git/commands/name_rev.rb +114 -0
  142. data/lib/git/commands/pull.rb +377 -0
  143. data/lib/git/commands/push.rb +246 -0
  144. data/lib/git/commands/read_tree.rb +149 -0
  145. data/lib/git/commands/remote/add.rb +91 -0
  146. data/lib/git/commands/remote/get_url.rb +66 -0
  147. data/lib/git/commands/remote/list.rb +54 -0
  148. data/lib/git/commands/remote/prune.rb +61 -0
  149. data/lib/git/commands/remote/remove.rb +52 -0
  150. data/lib/git/commands/remote/rename.rb +69 -0
  151. data/lib/git/commands/remote/set_branches.rb +63 -0
  152. data/lib/git/commands/remote/set_head.rb +82 -0
  153. data/lib/git/commands/remote/set_url.rb +71 -0
  154. data/lib/git/commands/remote/set_url_add.rb +61 -0
  155. data/lib/git/commands/remote/set_url_delete.rb +64 -0
  156. data/lib/git/commands/remote/show.rb +71 -0
  157. data/lib/git/commands/remote/update.rb +72 -0
  158. data/lib/git/commands/remote.rb +42 -0
  159. data/lib/git/commands/repack.rb +277 -0
  160. data/lib/git/commands/reset.rb +147 -0
  161. data/lib/git/commands/rev_parse.rb +297 -0
  162. data/lib/git/commands/revert/abort.rb +45 -0
  163. data/lib/git/commands/revert/continue.rb +57 -0
  164. data/lib/git/commands/revert/quit.rb +47 -0
  165. data/lib/git/commands/revert/skip.rb +44 -0
  166. data/lib/git/commands/revert/start.rb +153 -0
  167. data/lib/git/commands/revert.rb +29 -0
  168. data/lib/git/commands/rm.rb +114 -0
  169. data/lib/git/commands/show.rb +632 -0
  170. data/lib/git/commands/show_ref/exclude_existing.rb +120 -0
  171. data/lib/git/commands/show_ref/exists.rb +78 -0
  172. data/lib/git/commands/show_ref/list.rb +145 -0
  173. data/lib/git/commands/show_ref/verify.rb +120 -0
  174. data/lib/git/commands/show_ref.rb +42 -0
  175. data/lib/git/commands/stash/apply.rb +75 -0
  176. data/lib/git/commands/stash/branch.rb +65 -0
  177. data/lib/git/commands/stash/clear.rb +41 -0
  178. data/lib/git/commands/stash/create.rb +58 -0
  179. data/lib/git/commands/stash/drop.rb +67 -0
  180. data/lib/git/commands/stash/list.rb +39 -0
  181. data/lib/git/commands/stash/pop.rb +78 -0
  182. data/lib/git/commands/stash/push.rb +103 -0
  183. data/lib/git/commands/stash/show.rb +149 -0
  184. data/lib/git/commands/stash/store.rb +63 -0
  185. data/lib/git/commands/stash.rb +38 -0
  186. data/lib/git/commands/status.rb +169 -0
  187. data/lib/git/commands/symbolic_ref/delete.rb +68 -0
  188. data/lib/git/commands/symbolic_ref/read.rb +95 -0
  189. data/lib/git/commands/symbolic_ref/update.rb +76 -0
  190. data/lib/git/commands/symbolic_ref.rb +38 -0
  191. data/lib/git/commands/tag/create.rb +139 -0
  192. data/lib/git/commands/tag/delete.rb +55 -0
  193. data/lib/git/commands/tag/list.rb +143 -0
  194. data/lib/git/commands/tag/verify.rb +71 -0
  195. data/lib/git/commands/tag.rb +26 -0
  196. data/lib/git/commands/update_ref/batch.rb +140 -0
  197. data/lib/git/commands/update_ref/delete.rb +92 -0
  198. data/lib/git/commands/update_ref/update.rb +106 -0
  199. data/lib/git/commands/update_ref.rb +42 -0
  200. data/lib/git/commands/version.rb +57 -0
  201. data/lib/git/commands/worktree/add.rb +140 -0
  202. data/lib/git/commands/worktree/list.rb +64 -0
  203. data/lib/git/commands/worktree/lock.rb +58 -0
  204. data/lib/git/commands/worktree/management_base.rb +51 -0
  205. data/lib/git/commands/worktree/move.rb +66 -0
  206. data/lib/git/commands/worktree/prune.rb +67 -0
  207. data/lib/git/commands/worktree/remove.rb +63 -0
  208. data/lib/git/commands/worktree/repair.rb +76 -0
  209. data/lib/git/commands/worktree/unlock.rb +47 -0
  210. data/lib/git/commands/worktree.rb +43 -0
  211. data/lib/git/commands/write_tree.rb +68 -0
  212. data/lib/git/commands.rb +87 -0
  213. data/lib/git/config.rb +17 -0
  214. data/lib/git/config_entry_info.rb +106 -0
  215. data/lib/git/configuring.rb +665 -0
  216. data/lib/git/deprecation.rb +9 -0
  217. data/lib/git/detached_head_info.rb +54 -0
  218. data/lib/git/diff.rb +293 -7
  219. data/lib/git/diff_file_numstat_info.rb +29 -0
  220. data/lib/git/diff_file_patch_info.rb +134 -0
  221. data/lib/git/diff_file_raw_info.rb +127 -0
  222. data/lib/git/diff_info.rb +169 -0
  223. data/lib/git/diff_path_status.rb +67 -19
  224. data/lib/git/diff_result.rb +32 -0
  225. data/lib/git/diff_stats.rb +51 -14
  226. data/lib/git/dirstat_info.rb +86 -0
  227. data/lib/git/errors.rb +65 -2
  228. data/lib/git/execution_context/global.rb +31 -0
  229. data/lib/git/execution_context/repository.rb +136 -0
  230. data/lib/git/execution_context.rb +501 -0
  231. data/lib/git/file_ref.rb +74 -0
  232. data/lib/git/fsck_object.rb +9 -9
  233. data/lib/git/fsck_result.rb +1 -1
  234. data/lib/git/log.rb +9 -2
  235. data/lib/git/object.rb +90 -27
  236. data/lib/git/parsers/branch.rb +224 -0
  237. data/lib/git/parsers/cat_file.rb +111 -0
  238. data/lib/git/parsers/config_entry.rb +110 -0
  239. data/lib/git/parsers/diff.rb +585 -0
  240. data/lib/git/parsers/fsck.rb +133 -0
  241. data/lib/git/parsers/grep.rb +42 -0
  242. data/lib/git/parsers/ls_remote.rb +79 -0
  243. data/lib/git/parsers/ls_tree.rb +58 -0
  244. data/lib/git/parsers/stash.rb +208 -0
  245. data/lib/git/parsers/tag.rb +257 -0
  246. data/lib/git/remote.rb +120 -9
  247. data/lib/git/repository/branching.rb +743 -0
  248. data/lib/git/repository/committing.rb +187 -0
  249. data/lib/git/repository/configuring.rb +351 -0
  250. data/lib/git/repository/context_helpers.rb +264 -0
  251. data/lib/git/repository/diffing.rb +775 -0
  252. data/lib/git/repository/factories.rb +682 -0
  253. data/lib/git/repository/inspecting.rb +252 -0
  254. data/lib/git/repository/logging.rb +247 -0
  255. data/lib/git/repository/maintenance.rb +65 -0
  256. data/lib/git/repository/merging.rb +357 -0
  257. data/lib/git/repository/object_operations.rb +1199 -0
  258. data/lib/git/repository/path_resolver.rb +207 -0
  259. data/lib/git/repository/remote_operations.rb +898 -0
  260. data/lib/git/repository/shared_private.rb +51 -0
  261. data/lib/git/repository/staging.rb +554 -0
  262. data/lib/git/repository/stashing.rb +143 -0
  263. data/lib/git/repository/status_operations.rb +201 -0
  264. data/lib/git/repository/worktree_operations.rb +159 -0
  265. data/lib/git/repository.rb +203 -1
  266. data/lib/git/stash.rb +78 -4
  267. data/lib/git/stash_info.rb +104 -0
  268. data/lib/git/stashes.rb +125 -13
  269. data/lib/git/status.rb +215 -18
  270. data/lib/git/tag_delete_failure.rb +31 -0
  271. data/lib/git/tag_delete_result.rb +63 -0
  272. data/lib/git/tag_info.rb +105 -0
  273. data/lib/git/version.rb +109 -2
  274. data/lib/git/version_constraint.rb +81 -0
  275. data/lib/git/worktree.rb +108 -6
  276. data/lib/git/worktrees.rb +94 -7
  277. data/lib/git.rb +260 -61
  278. data/redesign/1_architecture_existing.md +54 -18
  279. data/redesign/2_architecture_redesign.md +365 -46
  280. data/redesign/3_architecture_implementation.md +1497 -63
  281. data/redesign/Phase 4 - Step A.md +360 -0
  282. data/redesign/beta_release.md +107 -0
  283. data/redesign/c1c2_audit.md +566 -0
  284. data/redesign/c1c2_bucket6_lib_orphans.md +626 -0
  285. data/redesign/config_design.rb +501 -0
  286. data/tasks/gem_tasks.rake +4 -0
  287. data/tasks/npm_tasks.rake +7 -0
  288. data/tasks/rspec.rake +48 -0
  289. data/tasks/test.rake +13 -1
  290. data/tasks/yard.rake +34 -7
  291. metadata +365 -22
  292. data/lib/git/base.rb +0 -1128
  293. data/lib/git/index.rb +0 -6
  294. data/lib/git/lib.rb +0 -2277
  295. data/lib/git/path.rb +0 -38
  296. data/lib/git/working_directory.rb +0 -6
  297. /data/{release-please-config.json → .release-please-config.json} +0 -0
@@ -0,0 +1,252 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'git/commands/describe'
4
+ require 'git/commands/fsck'
5
+ require 'git/commands/show'
6
+ require 'git/parsers/fsck'
7
+ require 'git/repository/shared_private'
8
+
9
+ module Git
10
+ class Repository
11
+ # Facade methods for read-only repository inspection operations
12
+ #
13
+ # These methods report on the contents and integrity of the repository.
14
+ #
15
+ # Included by {Git::Repository}.
16
+ #
17
+ # @api public
18
+ #
19
+ module Inspecting
20
+ # Give a human-readable name to a commit based on the most recent reachable tag
21
+ #
22
+ # Runs `git describe` to find the nearest tag reachable from `committish` and
23
+ # formats a version string. When the tag points directly at the commit, only the
24
+ # tag name is shown. Otherwise, the tag name is suffixed with the number of
25
+ # additional commits and the abbreviated commit SHA (e.g. `v1.0.0-3-gabcdef1`).
26
+ #
27
+ # @example Describe HEAD
28
+ # repo.describe #=> "v1.0.0"
29
+ #
30
+ # @example Describe a specific commit
31
+ # repo.describe('abc123') #=> "v1.0.0-3-gabcdef1"
32
+ #
33
+ # @example Describe using any tag (not just annotated tags)
34
+ # repo.describe(nil, tags: true) #=> "v1.0.0-lightweight"
35
+ #
36
+ # @example Require an exact tag match
37
+ # repo.describe(nil, exact_match: true)
38
+ #
39
+ # @example Use the legacy hyphenated key (still accepted)
40
+ # repo.describe(nil, :'exact-match' => true)
41
+ #
42
+ # @param committish [String, nil] the commit-ish to describe; defaults to HEAD
43
+ # when `nil`
44
+ #
45
+ # @param opts [Hash] options forwarded to `git describe`
46
+ #
47
+ # @option opts [Boolean, nil] :all (nil) use any ref in `refs/`, not just tags
48
+ #
49
+ # @option opts [Boolean, nil] :tags (nil) use lightweight tags as well as
50
+ # annotated ones
51
+ #
52
+ # @option opts [Boolean, nil] :contains (nil) describe the tag that contains the
53
+ # commit, rather than the nearest reachable one
54
+ #
55
+ # @option opts [Boolean, String, nil] :abbrev (nil) number of hex digits for the
56
+ # abbreviated object name; `true` uses git's default length
57
+ #
58
+ # @option opts [Boolean, String, nil] :dirty (nil) append a dirty-state mark to
59
+ # the description; `true` appends `-dirty`, a String appends that string
60
+ #
61
+ # @option opts [Boolean, String, nil] :broken (nil) like `:dirty` but treats
62
+ # broken repository links as dirty
63
+ #
64
+ # @option opts [Integer, String, nil] :candidates (nil) number of candidate tags
65
+ # to consider; increasing above 10 may yield a more accurate result
66
+ #
67
+ # @option opts [Boolean, nil] :exact_match (nil) only succeed when the commit is
68
+ # pointed to by a tag directly (no suffix)
69
+ #
70
+ # The legacy hyphenated key `:"exact-match"` is also accepted and is
71
+ # automatically translated to `:exact_match`.
72
+ #
73
+ # @option opts [Boolean, nil] :debug (nil) verbosely display the search strategy
74
+ #
75
+ # @option opts [Boolean, nil] :long (nil) always output the long format even when
76
+ # the commit matches a tag exactly
77
+ #
78
+ # @option opts [String, Array<String>, nil] :match (nil) only consider tags
79
+ # matching the given `glob(7)` pattern; pass an array for multiple patterns
80
+ #
81
+ # @option opts [String, Array<String>, nil] :exclude (nil) do not consider tags
82
+ # matching the given `glob(7)` pattern; pass an array for multiple patterns
83
+ #
84
+ # @option opts [Boolean, nil] :always (nil) show the abbreviated commit SHA as
85
+ # fallback when the commit cannot be described
86
+ #
87
+ # @option opts [Boolean, nil] :first_parent (nil) follow only the first parent of
88
+ # merge commits when searching for the nearest tag
89
+ #
90
+ # @return [String] the human-readable description of the commit, with trailing
91
+ # newlines preserved
92
+ #
93
+ # @raise [Git::FailedError] when git exits with a non-zero exit status
94
+ #
95
+ # @raise [ArgumentError] when `committish` looks like a command-line flag (starts
96
+ # with `-`), or when `opts` contains any key not in the documented option list
97
+ #
98
+ def describe(committish = nil, opts = {})
99
+ raise ArgumentError, "Invalid commit-ish object: '#{committish}'" if committish&.start_with?('-')
100
+
101
+ opts = opts.dup
102
+ if opts.key?(:'exact-match')
103
+ opts[:exact_match] ||= opts[:'exact-match']
104
+ opts.delete(:'exact-match')
105
+ end
106
+ SharedPrivate.assert_valid_opts!(DESCRIBE_ALLOWED_OPTS, **opts)
107
+ commit_ishes = Array(committish).compact
108
+ Git::Commands::Describe.new(@execution_context).call(*commit_ishes, **opts).stdout
109
+ end
110
+
111
+ # Show a single git object (a commit, tag, tree, or blob)
112
+ #
113
+ # @example Show the HEAD commit
114
+ # repo.show
115
+ #
116
+ # @example Show a specific commit
117
+ # repo.show('HEAD~1')
118
+ #
119
+ # @example Show the contents of a file at a revision
120
+ # repo.show('HEAD', 'README.md')
121
+ #
122
+ # @param objectish [String, nil] the object to show; a ref, SHA, or
123
+ # `objectish:path` expression
124
+ #
125
+ # Defaults to `HEAD` when `nil`.
126
+ #
127
+ # @param path [String, nil] the file whose contents to show at `objectish`,
128
+ # when given
129
+ #
130
+ # Combined with `objectish` as `objectish:path`. When `objectish` is `nil`
131
+ # and `path` is given, `HEAD` is used as the objectish, so
132
+ # `show(nil, 'README.md')` resolves to `HEAD:README.md`.
133
+ #
134
+ # @return [String] git's stdout from the show, with trailing newlines
135
+ # preserved
136
+ #
137
+ # @raise [Git::FailedError] when git exits with a non-zero exit status
138
+ #
139
+ def show(objectish = nil, path = nil)
140
+ object = path ? "#{objectish || 'HEAD'}:#{path}" : objectish
141
+ Git::Commands::Show.new(@execution_context).call(*[object].compact).stdout
142
+ end
143
+
144
+ # Option keys accepted by {#describe}
145
+ DESCRIBE_ALLOWED_OPTS = %i[
146
+ all tags contains abbrev dirty broken candidates
147
+ exact_match debug long match exclude always first_parent
148
+ ].freeze
149
+ private_constant :DESCRIBE_ALLOWED_OPTS
150
+
151
+ # Option keys accepted by {#fsck}
152
+ #
153
+ # `:progress`/`:no_progress` are intentionally excluded: progress output is
154
+ # always suppressed (see {#fsck}), so callers may not toggle it.
155
+ FSCK_ALLOWED_OPTS = %i[
156
+ tags root unreachable cache no_reflogs
157
+ full no_full strict verbose lost_found dangling no_dangling
158
+ connectivity_only name_objects no_name_objects references no_references
159
+ ].freeze
160
+ private_constant :FSCK_ALLOWED_OPTS
161
+
162
+ # Verify the connectivity and validity of the objects in the database
163
+ #
164
+ # Runs `git fsck` and returns the categorized objects it flags. Progress
165
+ # output is always suppressed (`--no-progress`) so that stdout contains only
166
+ # the machine-parsable findings.
167
+ #
168
+ # @overload fsck(*objects, **options)
169
+ #
170
+ # @example Check repository integrity
171
+ # result = repo.fsck
172
+ # result.dangling.each { |obj| puts "#{obj.type}: #{obj.oid}" }
173
+ #
174
+ # @example Check if the repository is clean
175
+ # repo.fsck.empty? #=> true
176
+ #
177
+ # @example List root commits
178
+ # repo.fsck(root: true).root.each { |obj| puts obj.oid }
179
+ #
180
+ # @example Check specific objects
181
+ # repo.fsck('abc1234', 'def5678')
182
+ #
183
+ # @param objects [Array<String>] specific objects to treat as heads for the
184
+ # unreachability trace
185
+ #
186
+ # When none are given, git fsck defaults to the index file, all refs, and
187
+ # all reflogs.
188
+ #
189
+ # @param options [Hash] options for the fsck command
190
+ #
191
+ # @option options [Boolean, nil] :tags (nil) report tags
192
+ #
193
+ # @option options [Boolean, nil] :root (nil) report root nodes
194
+ #
195
+ # @option options [Boolean, nil] :unreachable (nil) print objects that exist
196
+ # but are not reachable from any reference node
197
+ #
198
+ # @option options [Boolean, nil] :cache (nil) consider objects recorded in the
199
+ # index as head nodes for reachability
200
+ #
201
+ # @option options [Boolean, nil] :no_reflogs (nil) do not consider commits
202
+ # referenced only by reflogs to be reachable
203
+ #
204
+ # @option options [Boolean, nil] :full (nil) also check alternate object
205
+ # pools and packed archives, not just the local store
206
+ #
207
+ # @option options [Boolean, nil] :no_full (nil) skip alternate object pools and
208
+ # packed archives
209
+ #
210
+ # @option options [Boolean, nil] :strict (nil) enable stricter checking
211
+ #
212
+ # @option options [Boolean, nil] :verbose (nil) be chatty
213
+ #
214
+ # @option options [Boolean, nil] :lost_found (nil) write dangling objects
215
+ # into `.git/lost-found`
216
+ #
217
+ # This modifies the repository by creating files.
218
+ #
219
+ # @option options [Boolean, nil] :dangling (nil) print dangling objects
220
+ #
221
+ # @option options [Boolean, nil] :no_dangling (nil) suppress dangling object
222
+ # reporting
223
+ #
224
+ # @option options [Boolean, nil] :connectivity_only (nil) check only
225
+ # connectivity; faster but does not validate blob content
226
+ #
227
+ # @option options [Boolean, nil] :name_objects (nil) show the name of each
228
+ # reachable object alongside its identifier
229
+ #
230
+ # @option options [Boolean, nil] :no_name_objects (nil) suppress object name
231
+ # display
232
+ #
233
+ # @option options [Boolean, nil] :references (nil) check reference database
234
+ # consistency
235
+ #
236
+ # @option options [Boolean, nil] :no_references (nil) skip reference checking
237
+ #
238
+ # @return [Git::FsckResult] the objects flagged by fsck, categorized by status
239
+ #
240
+ # @raise [ArgumentError] when unsupported options are provided
241
+ #
242
+ # @raise [Git::FailedError] when git exits outside the allowed range (exit
243
+ # code > 7)
244
+ #
245
+ def fsck(*objects, **)
246
+ SharedPrivate.assert_valid_opts!(FSCK_ALLOWED_OPTS, **)
247
+ result = Git::Commands::Fsck.new(@execution_context).call(*objects, **, no_progress: true)
248
+ Git::Parsers::Fsck.parse(result.stdout)
249
+ end
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,247 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'git/commands/log'
4
+ require 'git/log'
5
+ require 'git/repository/shared_private'
6
+
7
+ module Git
8
+ class Repository
9
+ # Facade methods for querying commit history
10
+ #
11
+ # Included by {Git::Repository}.
12
+ #
13
+ # @api public
14
+ #
15
+ module Logging
16
+ FULL_LOG_COMMITS_ALLOWED_OPTS = %i[
17
+ count all cherry since until grep author between object path_limiter skip merges
18
+ ].freeze
19
+ private_constant :FULL_LOG_COMMITS_ALLOWED_OPTS
20
+
21
+ # Returns commits within the given revision range
22
+ #
23
+ # @overload full_log_commits(opts = {})
24
+ #
25
+ # @example Return commits from all refs
26
+ # repo.full_log_commits(all: true).first['sha']
27
+ # #=> "a1b2c3d4..."
28
+ #
29
+ # @example Return commits between two revisions
30
+ # repo.full_log_commits(between: ['v1.0.0', 'HEAD']).map { |c| c['sha'] }
31
+ # #=> ["d4e5f6...", "a1b2c3..."]
32
+ #
33
+ # @param opts [Hash] options for the log query
34
+ #
35
+ # @option opts [Integer, nil] :count (nil) maximum number of commits to return
36
+ #
37
+ # @option opts [Boolean, nil] :all (nil) include commits reachable from any ref
38
+ #
39
+ # @option opts [Boolean, nil] :cherry (nil) omit commits equivalent to cherry-picked commits
40
+ #
41
+ # @option opts [String] :since (nil) include commits newer than this date expression
42
+ #
43
+ # @option opts [String] :until (nil) include commits older than this date expression
44
+ #
45
+ # @option opts [String] :grep (nil) only include commits whose message matches this pattern
46
+ #
47
+ # @option opts [String] :author (nil) only include commits whose author matches this pattern
48
+ #
49
+ # @option opts [Array<String>] :between (nil) revision range as two commit-ish values
50
+ #
51
+ # When both `:between` and `:object` are provided, `:between` takes precedence.
52
+ #
53
+ # @option opts [String] :object (nil) single revision range expression for git log
54
+ #
55
+ # Ignored when `:between` is provided.
56
+ #
57
+ # @option opts [String, Pathname, Array<String, Pathname>] :path_limiter (nil)
58
+ # only include commits that impact files from the specified path(s)
59
+ #
60
+ # @option opts [Integer] :skip (nil) skip this many commits before output
61
+ #
62
+ # @option opts [Boolean, nil] :merges (nil) include only merge commits
63
+ #
64
+ # @return [Array<Hash>] the parsed raw log output for each commit
65
+ #
66
+ # @raise [ArgumentError] if unsupported options are provided
67
+ #
68
+ # @raise [ArgumentError] if `:count` is not an Integer
69
+ #
70
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
71
+ #
72
+ # @see https://git-scm.com/docs/git-log git-log
73
+ #
74
+ def full_log_commits(opts = {})
75
+ SharedPrivate.assert_valid_opts!(FULL_LOG_COMMITS_ALLOWED_OPTS, **opts)
76
+ Private.validate_log_count_option!(opts)
77
+ Private.validate_log_between_option!(opts)
78
+
79
+ call_opts = Private.log_base_call_options(opts, skip: opts[:skip], merges: opts[:merges])
80
+ revision_range_args = Private.log_revision_range_args(opts)
81
+ Private.run_log_command(@execution_context, revision_range_args, call_opts)
82
+ end
83
+
84
+ # Returns a new {Git::Log} query builder scoped to this repository
85
+ #
86
+ # @example Build a log query and execute it
87
+ # results = repo.log(50).author('Alice').since('2 weeks ago').execute
88
+ # results.each { |commit| puts commit.sha }
89
+ #
90
+ # @param count [Integer, Symbol, nil] the maximum number of commits to return,
91
+ # or `:all` / `nil` to return all commits; passed directly to {Git::Log#initialize}
92
+ #
93
+ # @return [Git::Log] a new log query builder
94
+ #
95
+ # @see Git::Log
96
+ #
97
+ def log(count = 30)
98
+ Git::Log.new(self, count)
99
+ end
100
+
101
+ # Internal helpers for {Logging} that should not be mixed into
102
+ # {Git::Repository} instances
103
+ #
104
+ # @api private
105
+ #
106
+ module Private
107
+ module_function
108
+
109
+ def validate_log_count_option!(opts)
110
+ return if opts[:count].nil? || opts[:count].is_a?(Integer)
111
+
112
+ raise ArgumentError, "The log count option must be an Integer but was #{opts[:count].inspect}"
113
+ end
114
+
115
+ def validate_log_between_option!(opts)
116
+ between = opts[:between]
117
+ return if between.nil?
118
+ return if between.is_a?(Array) && between.length == 2 && between.none?(&:nil?)
119
+
120
+ raise ArgumentError,
121
+ "The log between option must be an Array with exactly two non-nil values but was #{between.inspect}"
122
+ end
123
+
124
+ def log_revision_range_args(opts)
125
+ if opts[:between]
126
+ ["#{opts[:between][0]}..#{opts[:between][1]}"]
127
+ elsif opts[:object].is_a?(String)
128
+ [opts[:object]]
129
+ else
130
+ []
131
+ end
132
+ end
133
+
134
+ def log_base_call_options(opts, extra = {})
135
+ {
136
+ all: opts[:all],
137
+ cherry: opts[:cherry],
138
+ since: opts[:since],
139
+ until: opts[:until],
140
+ grep: opts[:grep],
141
+ author: opts[:author],
142
+ max_count: opts[:count],
143
+ path: opts[:path_limiter] ? Array(opts[:path_limiter]) : nil
144
+ }.merge(extra).compact
145
+ end
146
+
147
+ def run_log_command(execution_context, revision_range_args, call_opts)
148
+ log_or_empty_on_unborn do
149
+ result = Git::Commands::Log.new(execution_context).call(
150
+ *revision_range_args,
151
+ no_color: true,
152
+ pretty: 'raw',
153
+ **call_opts
154
+ )
155
+ RawLogParser.new(result.stdout.split("\n")).parse
156
+ end
157
+ end
158
+
159
+ def log_or_empty_on_unborn
160
+ yield
161
+ rescue Git::FailedError => e
162
+ raise unless e.result.status.exitstatus == 128 &&
163
+ e.result.stderr =~ /does not have any commits yet/
164
+
165
+ []
166
+ end
167
+
168
+ # Parser for `git log --pretty=raw` output into commit hashes
169
+ #
170
+ # @api private
171
+ #
172
+ class RawLogParser
173
+ def initialize(lines)
174
+ @lines = lines
175
+ @commits = []
176
+ @current_commit = nil
177
+ @in_message = false
178
+ @last_metadata_key = nil
179
+ end
180
+
181
+ # Parse raw `git log --pretty=raw` lines into commit hashes
182
+ #
183
+ # @return [Array<Hash>] the parsed commits in command output order
184
+ #
185
+ def parse
186
+ @lines.each { |line| process_line(line.chomp) }
187
+ finalize_commit
188
+ @commits
189
+ end
190
+
191
+ private
192
+
193
+ def process_line(line)
194
+ if line.empty?
195
+ @in_message = !@in_message
196
+ return
197
+ end
198
+
199
+ @in_message = false if @in_message && !line.start_with?(' ')
200
+
201
+ @in_message ? process_message_line(line) : process_metadata_line(line)
202
+ end
203
+
204
+ def process_message_line(line)
205
+ @current_commit['message'] << "#{line[4..]}\n"
206
+ end
207
+
208
+ def process_metadata_line(line)
209
+ if line.start_with?(' ') && @last_metadata_key
210
+ @current_commit[@last_metadata_key] << "\n#{line[1..]}"
211
+ return
212
+ end
213
+
214
+ key, *value = line.split
215
+ value = value.join(' ')
216
+ @last_metadata_key = nil
217
+ dispatch_metadata_key(key, value)
218
+ end
219
+
220
+ def dispatch_metadata_key(key, value)
221
+ case key
222
+ when 'commit'
223
+ start_new_commit(value)
224
+ when 'parent'
225
+ @current_commit['parent'] << value
226
+ else
227
+ @current_commit[key] = value
228
+ @last_metadata_key = key
229
+ end
230
+ end
231
+
232
+ def start_new_commit(sha)
233
+ finalize_commit
234
+ @current_commit = { 'sha' => sha, 'message' => +'', 'parent' => [] }
235
+ @last_metadata_key = nil
236
+ end
237
+
238
+ def finalize_commit
239
+ @commits << @current_commit if @current_commit
240
+ end
241
+ end
242
+ private_constant :RawLogParser
243
+ end
244
+ private_constant :Private
245
+ end
246
+ end
247
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'git/commands/gc'
4
+ require 'git/commands/repack'
5
+
6
+ module Git
7
+ class Repository
8
+ # Facade methods for repository maintenance and optimization operations
9
+ #
10
+ # These methods pack objects, compress history, prune unreachable objects, and
11
+ # otherwise keep the repository in good health.
12
+ #
13
+ # Included by {Git::Repository}.
14
+ #
15
+ # @api public
16
+ #
17
+ module Maintenance
18
+ # Repack loose objects into pack files
19
+ #
20
+ # Packs all unpacked objects and removes redundant pack files. This is
21
+ # equivalent to running `git repack -a -d`, which packs all objects into a
22
+ # single pack and deletes any packs that become redundant.
23
+ #
24
+ # This method uses the fixed options `a: true, d: true` (matching the 4.x
25
+ # behavior). No additional options are exposed.
26
+ #
27
+ # @example Repack the repository
28
+ # repo.repack
29
+ #
30
+ # @return [String] the stdout from `git repack`. Git writes all progress
31
+ # and summary output to stderr, so the returned string is typically empty.
32
+ # Returns `String` to match the 4.x public contract (`Git::Lib#command`
33
+ # returned `result.stdout`).
34
+ #
35
+ # @raise [Git::FailedError] when git exits with a non-zero exit status
36
+ #
37
+ def repack
38
+ Git::Commands::Repack.new(@execution_context).call(a: true, d: true).stdout
39
+ end
40
+
41
+ # Run garbage collection to optimize and clean up the repository
42
+ #
43
+ # Runs `git gc` to perform housekeeping tasks including object compression,
44
+ # pruning of unreachable objects, and ref packing. This is equivalent to
45
+ # running `git gc --prune --aggressive --auto`.
46
+ #
47
+ # This method uses the fixed options `prune: true, aggressive: true, auto: true`
48
+ # (matching the 4.x behavior). No additional options are exposed.
49
+ #
50
+ # @example Run garbage collection
51
+ # repo.gc
52
+ #
53
+ # @return [String] the stdout from `git gc`. Git writes all progress
54
+ # and summary output to stderr, so the returned string is typically empty.
55
+ # Returns `String` to match the 4.x public contract (`Git::Lib#command`
56
+ # returned `result.stdout`).
57
+ #
58
+ # @raise [Git::FailedError] when git exits with a non-zero exit status
59
+ #
60
+ def gc
61
+ Git::Commands::Gc.new(@execution_context).call(prune: true, aggressive: true, auto: true).stdout
62
+ end
63
+ end
64
+ end
65
+ end