git 4.4.5 → 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 (282) 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 -67
  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 -1
  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 -98
  45. data/CONTRIBUTING.md +694 -53
  46. data/LICENSE +1 -1
  47. data/README.md +17 -17
  48. data/Rakefile +61 -9
  49. data/commitlint.test +4 -0
  50. data/git.gemspec +15 -9
  51. data/lib/git/args_builder.rb +0 -8
  52. data/lib/git/base.rb +488 -424
  53. data/lib/git/branch.rb +117 -59
  54. data/lib/git/branch_delete_failure.rb +31 -0
  55. data/lib/git/branch_delete_result.rb +63 -0
  56. data/lib/git/branch_info.rb +178 -0
  57. data/lib/git/branches.rb +130 -24
  58. data/lib/git/command_line/base.rb +245 -0
  59. data/lib/git/command_line/capturing.rb +249 -0
  60. data/lib/git/command_line/result.rb +96 -0
  61. data/lib/git/command_line/streaming.rb +194 -0
  62. data/lib/git/command_line.rb +43 -322
  63. data/lib/git/command_line_result.rb +4 -88
  64. data/lib/git/commands/add.rb +131 -0
  65. data/lib/git/commands/am/abort.rb +43 -0
  66. data/lib/git/commands/am/apply.rb +252 -0
  67. data/lib/git/commands/am/continue.rb +43 -0
  68. data/lib/git/commands/am/quit.rb +43 -0
  69. data/lib/git/commands/am/retry.rb +47 -0
  70. data/lib/git/commands/am/show_current_patch.rb +64 -0
  71. data/lib/git/commands/am/skip.rb +42 -0
  72. data/lib/git/commands/am.rb +33 -0
  73. data/lib/git/commands/apply.rb +237 -0
  74. data/lib/git/commands/archive/list_formats.rb +46 -0
  75. data/lib/git/commands/archive.rb +140 -0
  76. data/lib/git/commands/arguments.rb +3510 -0
  77. data/lib/git/commands/base.rb +403 -0
  78. data/lib/git/commands/branch/copy.rb +94 -0
  79. data/lib/git/commands/branch/create.rb +173 -0
  80. data/lib/git/commands/branch/delete.rb +80 -0
  81. data/lib/git/commands/branch/list.rb +162 -0
  82. data/lib/git/commands/branch/move.rb +94 -0
  83. data/lib/git/commands/branch/set_upstream.rb +86 -0
  84. data/lib/git/commands/branch/show_current.rb +49 -0
  85. data/lib/git/commands/branch/unset_upstream.rb +57 -0
  86. data/lib/git/commands/branch.rb +34 -0
  87. data/lib/git/commands/cat_file/batch.rb +364 -0
  88. data/lib/git/commands/cat_file/filtered.rb +105 -0
  89. data/lib/git/commands/cat_file/raw.rb +210 -0
  90. data/lib/git/commands/cat_file.rb +49 -0
  91. data/lib/git/commands/checkout/branch.rb +151 -0
  92. data/lib/git/commands/checkout/files.rb +115 -0
  93. data/lib/git/commands/checkout.rb +38 -0
  94. data/lib/git/commands/checkout_index.rb +105 -0
  95. data/lib/git/commands/clean.rb +100 -0
  96. data/lib/git/commands/clone.rb +240 -0
  97. data/lib/git/commands/commit.rb +272 -0
  98. data/lib/git/commands/commit_tree.rb +100 -0
  99. data/lib/git/commands/config_option_syntax/add.rb +83 -0
  100. data/lib/git/commands/config_option_syntax/get.rb +117 -0
  101. data/lib/git/commands/config_option_syntax/get_all.rb +115 -0
  102. data/lib/git/commands/config_option_syntax/get_color.rb +91 -0
  103. data/lib/git/commands/config_option_syntax/get_color_bool.rb +93 -0
  104. data/lib/git/commands/config_option_syntax/get_regexp.rb +115 -0
  105. data/lib/git/commands/config_option_syntax/get_urlmatch.rb +102 -0
  106. data/lib/git/commands/config_option_syntax/list.rb +107 -0
  107. data/lib/git/commands/config_option_syntax/remove_section.rb +74 -0
  108. data/lib/git/commands/config_option_syntax/rename_section.rb +78 -0
  109. data/lib/git/commands/config_option_syntax/replace_all.rb +104 -0
  110. data/lib/git/commands/config_option_syntax/set.rb +114 -0
  111. data/lib/git/commands/config_option_syntax/unset.rb +89 -0
  112. data/lib/git/commands/config_option_syntax/unset_all.rb +89 -0
  113. data/lib/git/commands/config_option_syntax.rb +56 -0
  114. data/lib/git/commands/describe.rb +155 -0
  115. data/lib/git/commands/diff.rb +656 -0
  116. data/lib/git/commands/diff_files.rb +518 -0
  117. data/lib/git/commands/diff_index.rb +496 -0
  118. data/lib/git/commands/fetch.rb +352 -0
  119. data/lib/git/commands/fsck.rb +136 -0
  120. data/lib/git/commands/gc.rb +132 -0
  121. data/lib/git/commands/grep.rb +338 -0
  122. data/lib/git/commands/init.rb +99 -0
  123. data/lib/git/commands/log.rb +632 -0
  124. data/lib/git/commands/ls_files.rb +191 -0
  125. data/lib/git/commands/ls_remote.rb +155 -0
  126. data/lib/git/commands/ls_tree.rb +131 -0
  127. data/lib/git/commands/maintenance/register.rb +75 -0
  128. data/lib/git/commands/maintenance/run.rb +104 -0
  129. data/lib/git/commands/maintenance/start.rb +66 -0
  130. data/lib/git/commands/maintenance/stop.rb +55 -0
  131. data/lib/git/commands/maintenance/unregister.rb +79 -0
  132. data/lib/git/commands/maintenance.rb +31 -0
  133. data/lib/git/commands/merge/abort.rb +44 -0
  134. data/lib/git/commands/merge/continue.rb +44 -0
  135. data/lib/git/commands/merge/quit.rb +46 -0
  136. data/lib/git/commands/merge/start.rb +245 -0
  137. data/lib/git/commands/merge.rb +28 -0
  138. data/lib/git/commands/merge_base.rb +86 -0
  139. data/lib/git/commands/mv.rb +77 -0
  140. data/lib/git/commands/name_rev.rb +114 -0
  141. data/lib/git/commands/pull.rb +377 -0
  142. data/lib/git/commands/push.rb +246 -0
  143. data/lib/git/commands/read_tree.rb +149 -0
  144. data/lib/git/commands/remote/add.rb +91 -0
  145. data/lib/git/commands/remote/get_url.rb +66 -0
  146. data/lib/git/commands/remote/list.rb +54 -0
  147. data/lib/git/commands/remote/prune.rb +61 -0
  148. data/lib/git/commands/remote/remove.rb +52 -0
  149. data/lib/git/commands/remote/rename.rb +69 -0
  150. data/lib/git/commands/remote/set_branches.rb +63 -0
  151. data/lib/git/commands/remote/set_head.rb +82 -0
  152. data/lib/git/commands/remote/set_url.rb +71 -0
  153. data/lib/git/commands/remote/set_url_add.rb +61 -0
  154. data/lib/git/commands/remote/set_url_delete.rb +64 -0
  155. data/lib/git/commands/remote/show.rb +71 -0
  156. data/lib/git/commands/remote/update.rb +72 -0
  157. data/lib/git/commands/remote.rb +42 -0
  158. data/lib/git/commands/repack.rb +277 -0
  159. data/lib/git/commands/reset.rb +147 -0
  160. data/lib/git/commands/rev_parse.rb +297 -0
  161. data/lib/git/commands/revert/abort.rb +45 -0
  162. data/lib/git/commands/revert/continue.rb +57 -0
  163. data/lib/git/commands/revert/quit.rb +47 -0
  164. data/lib/git/commands/revert/skip.rb +44 -0
  165. data/lib/git/commands/revert/start.rb +153 -0
  166. data/lib/git/commands/revert.rb +29 -0
  167. data/lib/git/commands/rm.rb +114 -0
  168. data/lib/git/commands/show.rb +632 -0
  169. data/lib/git/commands/show_ref/exclude_existing.rb +120 -0
  170. data/lib/git/commands/show_ref/exists.rb +78 -0
  171. data/lib/git/commands/show_ref/list.rb +145 -0
  172. data/lib/git/commands/show_ref/verify.rb +120 -0
  173. data/lib/git/commands/show_ref.rb +42 -0
  174. data/lib/git/commands/stash/apply.rb +75 -0
  175. data/lib/git/commands/stash/branch.rb +65 -0
  176. data/lib/git/commands/stash/clear.rb +41 -0
  177. data/lib/git/commands/stash/create.rb +58 -0
  178. data/lib/git/commands/stash/drop.rb +67 -0
  179. data/lib/git/commands/stash/list.rb +39 -0
  180. data/lib/git/commands/stash/pop.rb +78 -0
  181. data/lib/git/commands/stash/push.rb +103 -0
  182. data/lib/git/commands/stash/show.rb +149 -0
  183. data/lib/git/commands/stash/store.rb +63 -0
  184. data/lib/git/commands/stash.rb +38 -0
  185. data/lib/git/commands/status.rb +169 -0
  186. data/lib/git/commands/symbolic_ref/delete.rb +68 -0
  187. data/lib/git/commands/symbolic_ref/read.rb +95 -0
  188. data/lib/git/commands/symbolic_ref/update.rb +76 -0
  189. data/lib/git/commands/symbolic_ref.rb +38 -0
  190. data/lib/git/commands/tag/create.rb +139 -0
  191. data/lib/git/commands/tag/delete.rb +55 -0
  192. data/lib/git/commands/tag/list.rb +143 -0
  193. data/lib/git/commands/tag/verify.rb +71 -0
  194. data/lib/git/commands/tag.rb +26 -0
  195. data/lib/git/commands/update_ref/batch.rb +140 -0
  196. data/lib/git/commands/update_ref/delete.rb +92 -0
  197. data/lib/git/commands/update_ref/update.rb +106 -0
  198. data/lib/git/commands/update_ref.rb +42 -0
  199. data/lib/git/commands/version.rb +52 -0
  200. data/lib/git/commands/worktree/add.rb +140 -0
  201. data/lib/git/commands/worktree/list.rb +64 -0
  202. data/lib/git/commands/worktree/lock.rb +58 -0
  203. data/lib/git/commands/worktree/management_base.rb +51 -0
  204. data/lib/git/commands/worktree/move.rb +66 -0
  205. data/lib/git/commands/worktree/prune.rb +67 -0
  206. data/lib/git/commands/worktree/remove.rb +63 -0
  207. data/lib/git/commands/worktree/repair.rb +76 -0
  208. data/lib/git/commands/worktree/unlock.rb +47 -0
  209. data/lib/git/commands/worktree.rb +43 -0
  210. data/lib/git/commands/write_tree.rb +68 -0
  211. data/lib/git/commands.rb +89 -0
  212. data/lib/git/detached_head_info.rb +54 -0
  213. data/lib/git/diff.rb +297 -7
  214. data/lib/git/diff_file_numstat_info.rb +29 -0
  215. data/lib/git/diff_file_patch_info.rb +134 -0
  216. data/lib/git/diff_file_raw_info.rb +127 -0
  217. data/lib/git/diff_info.rb +169 -0
  218. data/lib/git/diff_path_status.rb +78 -19
  219. data/lib/git/diff_result.rb +32 -0
  220. data/lib/git/diff_stats.rb +59 -14
  221. data/lib/git/dirstat_info.rb +86 -0
  222. data/lib/git/errors.rb +65 -2
  223. data/lib/git/execution_context/global.rb +56 -0
  224. data/lib/git/execution_context/repository.rb +147 -0
  225. data/lib/git/execution_context.rb +482 -0
  226. data/lib/git/file_ref.rb +74 -0
  227. data/lib/git/fsck_object.rb +9 -9
  228. data/lib/git/fsck_result.rb +1 -1
  229. data/lib/git/lib.rb +1606 -1084
  230. data/lib/git/log.rb +15 -2
  231. data/lib/git/object.rb +92 -22
  232. data/lib/git/parsers/branch.rb +224 -0
  233. data/lib/git/parsers/cat_file.rb +111 -0
  234. data/lib/git/parsers/diff.rb +585 -0
  235. data/lib/git/parsers/fsck.rb +133 -0
  236. data/lib/git/parsers/grep.rb +42 -0
  237. data/lib/git/parsers/ls_tree.rb +58 -0
  238. data/lib/git/parsers/stash.rb +208 -0
  239. data/lib/git/parsers/tag.rb +257 -0
  240. data/lib/git/remote.rb +133 -9
  241. data/lib/git/repository/branching.rb +572 -0
  242. data/lib/git/repository/committing.rb +191 -0
  243. data/lib/git/repository/configuring.rb +156 -0
  244. data/lib/git/repository/diffing.rb +775 -0
  245. data/lib/git/repository/inspecting.rb +153 -0
  246. data/lib/git/repository/logging.rb +247 -0
  247. data/lib/git/repository/merging.rb +295 -0
  248. data/lib/git/repository/object_operations.rb +1101 -0
  249. data/lib/git/repository/path_resolver.rb +207 -0
  250. data/lib/git/repository/remote_operations.rb +753 -0
  251. data/lib/git/repository/shared_private.rb +51 -0
  252. data/lib/git/repository/staging.rb +390 -0
  253. data/lib/git/repository/stashing.rb +107 -0
  254. data/lib/git/repository/status_operations.rb +180 -0
  255. data/lib/git/repository/worktree_operations.rb +159 -0
  256. data/lib/git/repository.rb +264 -1
  257. data/lib/git/stash.rb +85 -4
  258. data/lib/git/stash_info.rb +104 -0
  259. data/lib/git/stashes.rb +130 -13
  260. data/lib/git/status.rb +226 -18
  261. data/lib/git/tag_delete_failure.rb +31 -0
  262. data/lib/git/tag_delete_result.rb +63 -0
  263. data/lib/git/tag_info.rb +105 -0
  264. data/lib/git/version.rb +109 -2
  265. data/lib/git/version_constraint.rb +81 -0
  266. data/lib/git/worktree.rb +120 -5
  267. data/lib/git/worktrees.rb +107 -7
  268. data/lib/git.rb +118 -69
  269. data/redesign/1_architecture_existing.md +54 -18
  270. data/redesign/2_architecture_redesign.md +365 -46
  271. data/redesign/3_architecture_implementation.md +1451 -54
  272. data/tasks/gem_tasks.rake +4 -0
  273. data/tasks/npm_tasks.rake +7 -0
  274. data/tasks/rspec.rake +48 -0
  275. data/tasks/rubocop.rake +2 -9
  276. data/tasks/test.rake +13 -1
  277. data/tasks/yard.rake +34 -7
  278. metadata +351 -22
  279. data/lib/git/index.rb +0 -6
  280. data/lib/git/path.rb +0 -38
  281. data/lib/git/working_directory.rb +0 -6
  282. /data/{release-please-config.json → .release-please-config.json} +0 -0
data/lib/git.rb CHANGED
@@ -1,57 +1,63 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support'
3
4
  require 'active_support/deprecation'
4
5
 
5
- # Define Git::Deprecation before requiring the rest of the library to ensure that
6
- # any deprecation warnings emitted during the loading of the library are properly
7
- # configured according to the GIT_DEPRECATION_BEHAVIOR environment variable.
8
- #
6
+ require 'git/version'
7
+
9
8
  module Git
10
- # The deprecation instance used to emit deprecation warnings for the Git gem
11
- #
12
- # @api public
13
9
  Deprecation = ActiveSupport::Deprecation.new('5.0.0', 'Git')
14
10
 
15
- if (behavior = ENV.fetch('GIT_DEPRECATION_BEHAVIOR', nil))
16
- behavior = behavior.strip
17
- allowed_behaviors = ActiveSupport::Deprecation::DEFAULT_BEHAVIORS.keys.map(&:to_s)
18
-
19
- unless allowed_behaviors.include?(behavior)
20
- raise ArgumentError,
21
- "Invalid GIT_DEPRECATION_BEHAVIOR=#{behavior.inspect}; " \
22
- "expected one of: #{allowed_behaviors.join(', ')}"
23
- end
24
-
25
- Deprecation.behavior = behavior.to_sym
26
- end
11
+ # Minimum git version required by this gem
12
+ #
13
+ # Commands and features may require newer versions, but this is the absolute
14
+ # minimum supported version for the gem as a whole.
15
+ #
16
+ # @return [Git::Version]
17
+ #
18
+ # @api public
19
+ #
20
+ MINIMUM_GIT_VERSION = Version.parse('2.28.0')
27
21
  end
28
22
 
29
23
  require 'git/author'
30
- require 'git/base'
31
24
  require 'git/branch'
25
+ require 'git/branch_info'
32
26
  require 'git/branches'
33
27
  require 'git/command_line_result'
34
28
  require 'git/command_line'
29
+ require 'git/commands/init'
35
30
  require 'git/config'
36
31
  require 'git/diff'
32
+ require 'git/diff_file_numstat_info'
33
+ require 'git/diff_file_patch_info'
34
+ require 'git/diff_file_raw_info'
35
+ require 'git/diff_info'
36
+ require 'git/parsers/diff'
37
+ require 'git/diff_result'
38
+ require 'git/dirstat_info'
37
39
  require 'git/encoding_utils'
38
40
  require 'git/errors'
39
41
  require 'git/escaped_path'
42
+ require 'git/execution_context'
43
+ require 'git/file_ref'
40
44
  require 'git/fsck_object'
41
45
  require 'git/fsck_result'
42
- require 'git/index'
46
+ require 'git/version_constraint'
43
47
  require 'git/lib'
44
48
  require 'git/log'
45
49
  require 'git/object'
46
- require 'git/path'
47
50
  require 'git/remote'
48
51
  require 'git/repository'
52
+ require 'git/base'
49
53
  require 'git/status'
50
54
  require 'git/stash'
55
+ require 'git/stash_info'
51
56
  require 'git/stashes'
57
+ require 'git/tag_delete_failure'
58
+ require 'git/tag_delete_result'
59
+ require 'git/tag_info'
52
60
  require 'git/url'
53
- require 'git/version'
54
- require 'git/working_directory'
55
61
  require 'git/worktree'
56
62
  require 'git/worktrees'
57
63
 
@@ -62,29 +68,13 @@ require 'git/worktrees'
62
68
  #
63
69
  # @author Scott Chacon (mailto:schacon@gmail.com)
64
70
  #
65
- module Git # rubocop:disable Style/OneClassPerFile
66
- # Internal alias for Git::Lib, used by the gem itself after the public constant
67
- # is deprecated. Code outside the gem should not reference this constant.
68
- # @api private
69
- LibImpl = remove_const(:Lib)
70
-
71
- # @api private
72
- def self.const_missing(name)
73
- return super unless name == :Lib
74
-
75
- Git::Deprecation.warn(
76
- 'Git::Lib is deprecated and will be removed in version 5.x. ' \
77
- 'Use the #lib accessor on the object returned by Git.init, Git.open, or Git.clone instead.'
78
- )
79
- const_set(:Lib, LibImpl)
80
- end
81
-
71
+ module Git
82
72
  # g.config('user.name', 'Scott Chacon') # sets value
83
73
  # g.config('user.email', 'email@email.com') # sets value
84
74
  # g.config('user.name') # returns 'Scott Chacon'
85
75
  # g.config # returns whole config hash
86
76
  def config(name = nil, value = nil)
87
- lib = LibImpl.new
77
+ lib = Git::Lib.new
88
78
  if name && value
89
79
  # set value
90
80
  lib.config_set(name, value)
@@ -158,9 +148,9 @@ module Git # rubocop:disable Style/OneClassPerFile
158
148
  #
159
149
  # @param directory [Pathname, nil] The directory to clone into
160
150
  #
161
- # If `directory` is a relative directory it is relative to the `path` option if
162
- # given. If `path` is not given, `directory` is relative to the current working
163
- # directory.
151
+ # If `directory` is a relative path it is relative to the `:chdir` option if
152
+ # given. If `:chdir` is not given, `directory` is relative to the current
153
+ # working directory.
164
154
  #
165
155
  # If `nil`, `directory` will be set to the basename of the last component of
166
156
  # the path from the `repository_url`. For example, for the URL:
@@ -211,9 +201,14 @@ module Git # rubocop:disable Style/OneClassPerFile
211
201
  # @option options [String] :origin Use the value instead `origin` to track
212
202
  # the upstream repository.
213
203
  #
214
- # @option options [Pathname] :path The directory to clone into. May be used
215
- # as an alternative to the `directory` parameter. If specified, the
216
- # `path` option is used instead of the `directory` parameter.
204
+ # @option options [Pathname] :chdir Run `git clone` from within this directory.
205
+ #
206
+ # The `directory` parameter (or the repository basename when `directory` is nil)
207
+ # is resolved relative to `:chdir`, just as if you had `cd`'d into it before
208
+ # running `git clone`. The returned path is the join of `:chdir` and the
209
+ # cloned directory path.
210
+ #
211
+ # @option options [Pathname] :path Deprecated — use `:chdir` instead.
217
212
  #
218
213
  # @option options [Boolean] :recursive After the clone is created, initialize
219
214
  # all submodules within, using their default settings.
@@ -226,8 +221,10 @@ module Git # rubocop:disable Style/OneClassPerFile
226
221
  #
227
222
  # @example Clone into a different directory `my-ruby-git`
228
223
  # git = Git.clone('https://github.com/ruby-git/ruby-git.git', 'my-ruby-git')
229
- # # or:
230
- # git = Git.clone('https://github.com/ruby-git/ruby-git.git', path: 'my-ruby-git')
224
+ #
225
+ # @example Clone into a specific parent directory
226
+ # git = Git.clone('https://github.com/ruby-git/ruby-git.git', chdir: '/path/to/projects')
227
+ # # clones into /path/to/projects/ruby-git
231
228
  #
232
229
  # @example Create a bare repository in the directory `ruby-git.git`
233
230
  # git = Git.clone('https://github.com/ruby-git/ruby-git.git', bare: true)
@@ -255,8 +252,6 @@ module Git # rubocop:disable Style/OneClassPerFile
255
252
  # of the cloned local working copy or cloned repository.
256
253
  #
257
254
  def self.clone(repository_url, directory = nil, options = {})
258
- clone_to_options = options.slice(:bare, :mirror)
259
- directory ||= Git::URL.clone_to(repository_url, **clone_to_options)
260
255
  Base.clone(repository_url, directory, options)
261
256
  end
262
257
 
@@ -308,22 +303,10 @@ module Git # rubocop:disable Style/OneClassPerFile
308
303
  # See +clone+ for options. Does not obey the <tt>:remote</tt> option,
309
304
  # since the .git info will be deleted anyway; always uses the default
310
305
  # remote, 'origin.'
311
- #
312
- # <tt>options[:branch]</tt> is the short name of the ref: a branch name such as
313
- # 'main' or a tag name such as 'v1.0.0'. A full ref path such as
314
- # 'refs/tags/v1.0.0' or a commit SHA is not accepted.
315
- #
316
- # Removing +.git+ is not atomic. If it fails, the exported files are complete and
317
- # usable, but the directory keeps whatever part of +.git+ could not be deleted.
318
- # Nothing is cleaned up, because the exported files are the deliverable and the
319
- # leftover has to be removed by hand once the cause of the failure is fixed.
320
- #
321
- # @raise [SystemCallError] if the +.git+ directory cannot be removed. The exported
322
- # files are left in place, and the directory keeps whatever part of +.git+ could
323
- # not be deleted.
324
306
  def self.export(repository, name, options = {})
325
307
  options.delete(:remote)
326
308
  repo = clone(repository, name, { depth: 1 }.merge(options))
309
+ repo.checkout("origin/#{options[:branch]}") if options[:branch]
327
310
  FileUtils.rm_r File.join(repo.dir.to_s, '.git')
328
311
  end
329
312
 
@@ -334,7 +317,7 @@ module Git # rubocop:disable Style/OneClassPerFile
334
317
  # g.config('user.name') # returns 'Scott Chacon'
335
318
  # g.config # returns whole config hash
336
319
  def self.global_config(name = nil, value = nil)
337
- lib = LibImpl.new(nil, nil)
320
+ lib = Git::Lib.new(nil, nil)
338
321
  if name && value
339
322
  # set value
340
323
  lib.global_config_set(name, value)
@@ -376,6 +359,8 @@ module Git # rubocop:disable Style/OneClassPerFile
376
359
  # and converted to an absolute path using
377
360
  # [File.expand_path](https://www.rubydoc.info/stdlib/core/File.expand_path).
378
361
  #
362
+ # @option options [Pathname] :separate_git_dir Alias for `:repository`.
363
+ #
379
364
  # @option options [String, nil] :git_ssh An optional custom SSH command
380
365
  #
381
366
  # - If not specified, uses the global config (Git.configure { |c| c.git_ssh = ... }).
@@ -404,7 +389,30 @@ module Git # rubocop:disable Style/OneClassPerFile
404
389
  # @see https://git-scm.com/docs/git-init git init
405
390
  #
406
391
  def self.init(directory = '.', options = {})
407
- Base.init(directory, options)
392
+ require_relative 'git/commands/init'
393
+
394
+ options = options.dup
395
+ options[:repository] ||= options.delete(:separate_git_dir)
396
+ init_opts = options.slice(:bare, :initial_branch)
397
+ init_opts[:separate_git_dir] = options[:repository] if options.key?(:repository)
398
+ Git::Commands::Init.new(Git::Lib.new(nil, options[:log])).call(directory, **init_opts)
399
+
400
+ open_initialized_repository(directory, options)
401
+ end
402
+
403
+ # Open the repository after initialization
404
+ #
405
+ # @param directory [String] the directory containing the repository
406
+ # @param options [Hash] the options hash
407
+ # @return [Git::Base] the opened repository
408
+ # @api private
409
+ #
410
+ private_class_method def self.open_initialized_repository(directory, options)
411
+ if options[:bare]
412
+ Git.bare(options[:repository] || directory, options.slice(:log, :git_ssh).compact)
413
+ else
414
+ Git.open(directory, options.slice(:log, :git_ssh, :index, :repository).compact)
415
+ end
408
416
  end
409
417
 
410
418
  # returns a Hash containing information about the references
@@ -416,7 +424,7 @@ module Git # rubocop:disable Style/OneClassPerFile
416
424
  # @param [String|NilClass] location the target repository location or nil for '.'
417
425
  # @return [{String=>Hash}] the available references of the target repo.
418
426
  def self.ls_remote(location = nil, options = {})
419
- LibImpl.new.ls_remote(location, options)
427
+ Git::Lib.new.ls_remote(location, options)
420
428
  end
421
429
 
422
430
  # Open a an existing Git working directory
@@ -473,6 +481,37 @@ module Git # rubocop:disable Style/OneClassPerFile
473
481
  Base.open(working_dir, options)
474
482
  end
475
483
 
484
+ # Return the version of a git binary as a {Git::Version}
485
+ #
486
+ # @param binary_path [String, nil] path to the git binary; defaults to
487
+ # `Git::Base.config.binary_path`
488
+ #
489
+ # @return [Git::Version] the parsed git version
490
+ #
491
+ # @raise [Git::UnexpectedResultError] if the version output cannot be parsed
492
+ #
493
+ # @raise [Git::FailedError] if the git binary exits with a non-zero status
494
+ #
495
+ # @raise [Git::Error] if the binary is not found or fails to launch
496
+ #
497
+ # @example Default binary
498
+ # Git.git_version #=> #<Git::Version 2.42.0>
499
+ #
500
+ # @example Explicit binary path
501
+ # Git.git_version('/opt/homebrew/bin/git') #=> #<Git::Version 2.42.0>
502
+ #
503
+ def self.git_version(binary_path = nil)
504
+ path = binary_path || Git::Base.config.binary_path
505
+ Git::Lib.cached_git_version(path) { run_git_version(path) }
506
+ end
507
+
508
+ # @api private
509
+ def self.run_git_version(path)
510
+ output = Git::Commands::Version.new(Git::ExecutionContext::Global.new(binary_path: path)).call.stdout
511
+ Git::Version.parse(output)
512
+ end
513
+ private_class_method :run_git_version
514
+
476
515
  # Return the version of the git binary
477
516
  #
478
517
  # @example
@@ -480,7 +519,17 @@ module Git # rubocop:disable Style/OneClassPerFile
480
519
  #
481
520
  # @return [Array<Integer>] the version of the git binary
482
521
  #
522
+ # @deprecated Use {Git.git_version} instead, which returns a {Git::Version} (not an Array).
523
+ # For the legacy array shape, call: `Git.git_version.to_a`.
524
+ # The optional binary_path argument is preserved: `Git.git_version(binary_path)`.
525
+ #
483
526
  def self.binary_version(binary_path = Git::Base.config.binary_path)
484
- Base.binary_version(binary_path)
527
+ Git::Deprecation.warn(
528
+ 'Git.binary_version is deprecated and will be removed in 6.0. ' \
529
+ 'Use Git.git_version instead, which returns a Git::Version ' \
530
+ '(not an Array). For the legacy array shape, call: Git.git_version.to_a. ' \
531
+ 'The optional binary_path argument is preserved: Git.git_version(binary_path).'
532
+ )
533
+ git_version(binary_path).to_a
485
534
  end
486
535
  end
@@ -1,6 +1,9 @@
1
1
  # Analysis of the Current Git Gem Architecture and Its Challenges
2
2
 
3
- This document provides an in-depth look at the current architecture of the `git` gem, outlining its primary components and the design challenges that have emerged over time. Understanding these challenges is the key motivation for the proposed architectural redesign.
3
+ This document provides an in-depth look at the current architecture of the `git` gem,
4
+ outlining its primary components and the design challenges that have emerged over
5
+ time. Understanding these challenges is the key motivation for the proposed
6
+ architectural redesign.
4
7
 
5
8
  - [1. Overview of the Current Architecture](#1-overview-of-the-current-architecture)
6
9
  - [2. Key Architectural Challenges](#2-key-architectural-challenges)
@@ -11,27 +14,43 @@ This document provides an in-depth look at the current architecture of the `git`
11
14
 
12
15
  ## 1. Overview of the Current Architecture
13
16
 
14
- The gem's current design is centered around three main classes: `Git`, `Git::Base`, and `Git::Lib`.
17
+ The gem's current design is centered around three main classes: `Git`, `Git::Base`,
18
+ and `Git::Lib`.
15
19
 
16
- - **`Git` (Top-Level Module)**: This module serves as the primary public entry point for creating repository objects. It contains class-level factory methods like `Git.open`, `Git.clone`, and `Git.init`. It also provides an interface for accessing global git configuration settings.
20
+ - **`Git` (Top-Level Module)**: This module serves as the primary public entry point
21
+ for creating repository objects. It contains class-level factory methods like
22
+ `Git.open`, `Git.clone`, and `Git.init`. It also provides an interface for
23
+ accessing global git configuration settings.
17
24
 
18
- **`Git::Base`**: This is the main object that users interact with after creating or opening a repository. It holds the high-level public API for most git operations (e.g., `g.commit`, `g.add`, `g.status`). It is responsible for managing the repository's state, such as the paths to the working directory and the `.git` directory.
25
+ - **`Git::Base`**: This is the main object that users interact with after creating or
26
+ opening a repository. It holds the high-level public API for most git operations
27
+ (e.g., `g.commit`, `g.add`, `g.status`). It is responsible for managing the
28
+ repository's state, such as the paths to the working directory and the `.git`
29
+ directory.
19
30
 
20
- **`Git::Lib`**: This class is intended to be the low-level wrapper around the `git` command-line tool. It contains the methods that build the specific command-line arguments and execute the `git` binary. In practice, it also contains a significant amount of logic for parsing the output of these commands.
31
+ - **`Git::Lib`**: This class is intended to be the low-level wrapper around the `git`
32
+ command-line tool. It contains the methods that build the specific command-line
33
+ arguments and execute the `git` binary. In practice, it also contains a significant
34
+ amount of logic for parsing the output of these commands.
21
35
 
22
36
  ## 2. Key Architectural Challenges
23
37
 
24
- While this structure has been functional, several significant design challenges make the codebase difficult to maintain, test, and evolve.
38
+ While this structure has been functional, several significant design challenges make
39
+ the codebase difficult to maintain, test, and evolve.
25
40
 
26
41
  ### A. Unclear Separation of Concerns
27
42
 
28
- The responsibilities between Git::Base and Git::Lib are "muddy" and overlap significantly.
43
+ The responsibilities between Git::Base and Git::Lib are "muddy" and overlap
44
+ significantly.
29
45
 
30
46
  - `Git::Base` sometimes contains logic that feels like it should be lower-level.
31
47
 
32
- - `Git::Lib`, which should ideally only be concerned with command execution, is filled with high-level logic for parsing command output into specific Ruby objects (e.g., parsing log output, diff stats, and branch lists).
48
+ - `Git::Lib`, which should ideally only be concerned with command execution, is
49
+ filled with high-level logic for parsing command output into specific Ruby objects
50
+ (e.g., parsing log output, diff stats, and branch lists).
33
51
 
34
- This blending of responsibilities makes it hard to determine where a specific piece of logic should reside, leading to an inconsistent and confusing internal structure.
52
+ This blending of responsibilities makes it hard to determine where a specific piece
53
+ of logic should reside, leading to an inconsistent and confusing internal structure.
35
54
 
36
55
  ### B. Circular Dependency
37
56
 
@@ -39,28 +58,45 @@ This is the most critical architectural flaw in the current design.
39
58
 
40
59
  - A `Git::Base` instance is created.
41
60
 
42
- - The first time a command is run, `Git::Base` lazily initializes a `Git::Lib` instance via its `.lib` accessor method.
61
+ - The first time a command is run, `Git::Base` lazily initializes a `Git::Lib`
62
+ instance via its `.lib` accessor method.
43
63
 
44
- - The `Git::Lib` constructor is passed the `Git::Base` instance (`self`) so that it can read the repository's path configuration back from the object that is creating it.
64
+ - The `Git::Lib` constructor is passed the `Git::Base` instance (`self`) so that it
65
+ can read the repository's path configuration back from the object that is creating
66
+ it.
45
67
 
46
- This creates a tight, circular coupling: `Git::Base` depends on `Git::Lib` to execute commands, but `Git::Lib` depends on `Git::Base` for its own configuration. This pattern makes the classes difficula to instantiate or test in isolation and creates a fragile system where changes in one class can have unexpected side effects in the other.
68
+ This creates a tight, circular coupling: `Git::Base` depends on `Git::Lib` to execute
69
+ commands, but `Git::Lib` depends on `Git::Base` for its own configuration. This
70
+ pattern makes the classes difficult to instantiate or test in isolation and creates a
71
+ fragile system where changes in one class can have unexpected side effects in the
72
+ other.
47
73
 
48
74
  ### C. Undefined Public API Boundary
49
75
 
50
- The gem lacks a formally defined public interface. Because `Git::Base` exposes its internal `Git::Lib` instance via the public `g.lib` accessor, many users have come to rely on `Git::Lib` and its methods as if they were part of the public API.
76
+ The gem lacks a formally defined public interface. Because `Git::Base` exposes its
77
+ internal `Git::Lib` instance via the public `g.lib` accessor, many users have come to
78
+ rely on `Git::Lib` and its methods as if they were part of the public API.
51
79
 
52
80
  This has two negative consequences:
53
81
 
54
- 1. It prevents the gem's maintainers from refactoring or changing the internal implementation of `Git::Lib` without causing breaking changes for users.
82
+ 1. It prevents the gem's maintainers from refactoring or changing the internal
83
+ implementation of `Git::Lib` without causing breaking changes for users.
55
84
 
56
- 2. It exposes complex, internal methods to users, creating a confusing and inconsistent user experience.
85
+ 2. It exposes complex, internal methods to users, creating a confusing and
86
+ inconsistent user experience.
57
87
 
58
88
  ### D. Slow and Brittle Test Suite
59
89
 
60
90
  The current testing strategy, built on `TestUnit`, suffers from two major issues:
61
91
 
62
- - **Over-reliance on Fixtures**: Most tests depend on having a complete, physical git repository on the filesystem to run against. Managing these fixtures is cumbersome.
92
+ - **Over-reliance on Fixtures**: Most tests depend on having a complete, physical git
93
+ repository on the filesystem to run against. Managing these fixtures is cumbersome.
63
94
 
64
- - **Excessive Shelling Out**: Because the logic for command execution and output parsing are tightly coupled, nearly every test must shell out to the actual `git` command-line tool.
95
+ - **Excessive Shelling Out**: Because the logic for command execution and output
96
+ parsing are tightly coupled, nearly every test must shell out to the actual `git`
97
+ command-line tool.
65
98
 
66
- This makes the test suite extremely slow, especially on non-UNIX platforms like Windows where process creation is more expensive. The slow feedback loop discourages frequent testing and makes development more difficult. The brittleness of filesystem-dependent tests also leads to flickering or unreliable test runs.
99
+ This makes the test suite extremely slow, especially on non-UNIX platforms like
100
+ Windows where process creation is more expensive. The slow feedback loop discourages
101
+ frequent testing and makes development more difficult. The brittleness of
102
+ filesystem-dependent tests also leads to flickering or unreliable test runs.