git 5.0.0.beta.3 → 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 (268) 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 +47 -12
  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 +123 -63
  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 +21 -7
  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 +398 -314
  255. data/redesign/3_architecture_implementation.md +59 -29
  256. data/redesign/Phase 4 - Step B.md +921 -0
  257. data/redesign/Phase 4 - Step C.md +833 -0
  258. data/redesign/c1a-public-api-scope.tsv +256 -0
  259. data/redesign/phase-4-step-b-test-audit.tsv +485 -0
  260. data/tasks/yard.rake +24 -17
  261. metadata +24 -57
  262. data/commitlint.test +0 -4
  263. data/lib/git/args_builder.rb +0 -103
  264. data/lib/git/command_line_result.rb +0 -8
  265. data/lib/git/deprecation.rb +0 -9
  266. data/tasks/test.rake +0 -25
  267. /data/.github/{skills → skills-deprecated}/extract-facade-from-base-lib/KEYWORD_ARG_REMEDIATION.md +0 -0
  268. /data/.github/{skills → skills-deprecated}/review-backward-compatibility/SKILL.md +0 -0
data/lib/git.rb CHANGED
@@ -1,29 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_support'
4
3
  require 'active_support/deprecation'
5
4
 
6
- require 'git/deprecation'
7
- require 'git/version'
8
-
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
+ #
9
9
  module Git
10
- # Minimum git version required by this gem
10
+ # The deprecation instance used to emit deprecation warnings for the Git gem
11
11
  #
12
- # Commands and features may require newer versions, but this is the absolute
13
- # minimum supported version for the gem as a whole.
12
+ # @api public
13
+ Deprecation = ActiveSupport::Deprecation.new('6.0.0', 'Git')
14
+
15
+ # Configure a deprecation instance from a GIT_DEPRECATION_BEHAVIOR value
14
16
  #
15
- # @return [Git::Version]
17
+ # @param deprecation [ActiveSupport::Deprecation] the deprecation instance to configure
16
18
  #
17
- # @api public
19
+ # @param behavior [String, nil] the desired behavior name (e.g. `'silence'`); when
20
+ # `nil` the deprecation instance is left unchanged
18
21
  #
19
- MINIMUM_GIT_VERSION = Version.parse('2.28.0')
22
+ # @return [void]
23
+ #
24
+ # @raise [ArgumentError] if `behavior` is not one of the keys of
25
+ # `ActiveSupport::Deprecation::DEFAULT_BEHAVIORS`
26
+ #
27
+ # @api private
28
+ #
29
+ def self.configure_deprecation_behavior(deprecation, behavior)
30
+ return if behavior.nil?
31
+
32
+ behavior = behavior.strip
33
+ allowed_behaviors = ActiveSupport::Deprecation::DEFAULT_BEHAVIORS.keys.map(&:to_s)
34
+
35
+ unless allowed_behaviors.include?(behavior)
36
+ raise ArgumentError,
37
+ "Invalid GIT_DEPRECATION_BEHAVIOR=#{behavior.inspect}; " \
38
+ "expected one of: #{allowed_behaviors.join(', ')}"
39
+ end
40
+
41
+ deprecation.behavior = behavior.to_sym
42
+ end
43
+ private_class_method :configure_deprecation_behavior
44
+
45
+ configure_deprecation_behavior(Deprecation, ENV.fetch('GIT_DEPRECATION_BEHAVIOR', nil))
20
46
  end
21
47
 
22
48
  require 'git/author'
23
49
  require 'git/branch'
24
50
  require 'git/branch_info'
25
51
  require 'git/branches'
26
- require 'git/command_line_result'
27
52
  require 'git/command_line'
28
53
  require 'process_executer'
29
54
  require 'git/config'
@@ -61,6 +86,7 @@ require 'git/tag_delete_failure'
61
86
  require 'git/tag_delete_result'
62
87
  require 'git/tag_info'
63
88
  require 'git/url'
89
+ require 'git/version'
64
90
  require 'git/worktree'
65
91
  require 'git/worktrees'
66
92
 
@@ -69,19 +95,77 @@ require 'git/worktrees'
69
95
  # open a bare repository, initialize a new repo or clone an
70
96
  # existing remote repository.
71
97
  #
72
- # @author Scott Chacon (mailto:schacon@gmail.com)
73
- #
74
98
  module Git
75
99
  extend Git::Configuring
100
+ extend Git::Repository::Factories
101
+
102
+ # Minimum git version required by this gem
103
+ #
104
+ # Commands and features may require newer versions, but this is the absolute
105
+ # minimum supported version for the gem as a whole.
106
+ #
107
+ # @return [Git::Version]
108
+ #
109
+ # @api public
110
+ #
111
+ MINIMUM_GIT_VERSION = Version.parse('2.28.0')
76
112
 
113
+ # Intercept the first lookup of the deprecated `Git::CommandLineResult` constant
114
+ #
115
+ # When `name` is `:CommandLineResult`, caches and returns {Git::CommandLine::Result}
116
+ # after emitting a deprecation warning. Calls `super` for any other unknown constant,
117
+ # preserving normal Ruby `NameError` behavior.
118
+ #
119
+ # @param name [Symbol] the name of the missing constant
120
+ #
121
+ # @return [Class] the resolved constant value
122
+ #
123
+ # @api private
124
+ def self.const_missing(name)
125
+ return super unless name == :CommandLineResult
126
+
127
+ # Cache the constant first so subsequent accesses are zero-cost even if
128
+ # the deprecation behavior raises (e.g. in the test suite).
129
+ const_set(:CommandLineResult, Git::CommandLine::Result)
130
+ Git::Deprecation.warn(
131
+ 'Git::CommandLineResult is deprecated and will be removed in v6.0.0. ' \
132
+ 'Use Git::CommandLine::Result instead.'
133
+ )
134
+ Git::CommandLine::Result
135
+ end
136
+
137
+ # Gets or sets local git configuration options
138
+ #
139
+ # @overload config(name, value)
140
+ # Set the value for the git named configuration option
141
+ #
142
+ # @param name [String] the name of the git configuration option
143
+ #
144
+ # @param value [String, Boolean] the value to set for the git configuration option
145
+ #
146
+ # @return [Git::CommandLine::Result] the result of the git configuration command
147
+ #
148
+ # @overload config(name)
149
+ # Get the value for the git named configuration option
150
+ #
151
+ # @param name [String] the name of the git configuration option
152
+ #
153
+ # @return [String] the value of the git configuration option
154
+ #
155
+ # @overload config()
156
+ # List all git configuration options
157
+ #
158
+ # @return [Hash{String => String}] a hash of all git configuration options
159
+ #
77
160
  # @deprecated Mixing in the `Git` module is deprecated and will be removed in v6.0.0.
78
161
  # Use `Git.config_get(name)`, `Git.config_set(name, value)`, or `Git.config_list` instead.
162
+ #
79
163
  def config(name = nil, value = nil)
80
164
  Git::Deprecation.warn(
81
165
  'Git#config is deprecated and will be removed in v6.0.0. ' \
82
166
  'Use Git.config_get(name), Git.config_set(name, value), or Git.config_list instead.'
83
167
  )
84
- Git.__send__(:run_config_utility, name, value, global: false)
168
+ Git.__send__(:legacy_config_set_get_list, name, value, global: false)
85
169
  end
86
170
 
87
171
  # Configures the gem by yielding {Git::Config.instance} to the block
@@ -89,14 +173,14 @@ module Git
89
173
  # @example Set the global git binary path
90
174
  # Git.configure { |c| c.binary_path = '/usr/local/bin/git' }
91
175
  #
176
+ # @return [void]
177
+ #
92
178
  # @yield [config] yields the singleton config object
93
179
  #
94
180
  # @yieldparam config [Git::Config] the singleton config object
95
181
  #
96
182
  # @yieldreturn [void]
97
183
  #
98
- # @return [void]
99
- #
100
184
  def self.configure
101
185
  yield Git::Config.instance
102
186
  nil
@@ -113,172 +197,39 @@ module Git
113
197
  Git::Config.instance
114
198
  end
115
199
 
116
- # @deprecated Mixing in the `Git` module is deprecated and will be removed in v6.0.0.
117
- # Use `Git.config_get(name, global: true)`, `Git.config_set(name, value, global: true)`, or
118
- # `Git.config_list(global: true)` instead.
119
- def global_config(name = nil, value = nil)
120
- Git::Deprecation.warn(
121
- 'Git#global_config is deprecated and will be removed in v6.0.0. ' \
122
- 'Use Git.config_get(name, global: true), Git.config_set(name, value, global: true), ' \
123
- 'or Git.config_list(global: true) instead.'
124
- )
125
- Git.global_config(name, value)
126
- end
127
-
128
- # Open a bare repository
129
- #
130
- # Opens a bare repository located in the `git_dir` directory.
131
- # Since there is no working copy, you can not checkout or commit
132
- # but you can do most read operations.
133
- #
134
- # @see https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefbarerepositoryabarerepository
135
- # What is a bare repository?
136
- #
137
- # @example Open a bare repository and retrieve the first commit SHA
138
- # repository = Git.bare('ruby-git.git')
139
- # puts repository.log[0].sha #=> "64c6fa011d3287bab9158049c85f3e85718854a0"
140
- #
141
- # @param [Pathname] git_dir The path to the bare repository directory
142
- # containing an initialized Git repository. If a relative path is given, it
143
- # is converted to an absolute path using
144
- # [File.expand_path](https://www.rubydoc.info/stdlib/core/File.expand_path).
145
- #
146
- # @param [Hash] options The options for this command (see list of valid
147
- # options below)
148
- #
149
- # @option options [String, nil] :git_ssh An optional custom SSH command
150
- #
151
- # - If not specified, uses the global config (Git.configure { |c| c.git_ssh = ... }).
152
- # - If nil, disables SSH for this instance.
153
- # - If a non-empty string, uses that value for this instance.
154
- #
155
- # @option options [Logger] :log A logger to use for Git operations. Git commands
156
- # are logged at the `:info` level. Additional logging is done at the `:debug`
157
- # level.
158
- #
159
- # @return [Git::Repository] an object that can execute git commands in the context
160
- # of the bare repository.
161
- #
162
- def self.bare(git_dir, options = {})
163
- Git::Repository.bare(git_dir, options)
164
- end
165
-
166
- # Clone a repository into an empty or newly created directory
167
- #
168
- # @see https://git-scm.com/docs/git-clone git clone
169
- # @see https://git-scm.com/docs/git-clone#_git_urls_a_id_urls_a GIT URLs
200
+ # Gets or sets global git configuration options
170
201
  #
171
- # @param repository_url [URI, Pathname] The (possibly remote) repository url to clone
172
- # from. See [GIT URLS](https://git-scm.com/docs/git-clone#_git_urls_a_id_urls_a)
173
- # for more information.
174
- #
175
- # @param directory [Pathname, nil] The directory to clone into
176
- #
177
- # If `directory` is a relative path it is relative to the `:chdir` option if
178
- # given. If `:chdir` is not given, `directory` is relative to the current
179
- # working directory.
180
- #
181
- # If `nil`, `directory` will be set to the basename of the last component of
182
- # the path from the `repository_url`. For example, for the URL:
183
- # `https://github.com/org/repo.git`, `directory` will be set to `repo`.
184
- #
185
- # If the last component of the path is `.git`, the next-to-last component of
186
- # the path is used. For example, for the URL `/Users/me/foo/.git`, `directory`
187
- # will be set to `foo`.
188
- #
189
- # @param [Hash] options The options for this command (see list of valid
190
- # options below)
191
- #
192
- # @option options [Boolean] :bare Make a bare Git repository. See
193
- # [what is a bare repository?](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefbarerepositoryabarerepository).
194
- #
195
- # @option options [String] :branch The name of a branch or tag to checkout
196
- # instead of the default branch.
202
+ # @overload global_config(name, value)
203
+ # Set the value for the git named configuration option
197
204
  #
198
- # @option options [Array, String] :config A list of configuration options to
199
- # set on the newly created repository.
205
+ # @param name [String] the name of the git configuration option
200
206
  #
201
- # @option options [Integer] :depth Create a shallow clone with a history
202
- # truncated to the specified number of commits.
203
- #
204
- # @option options [String] :filter Request that the server send a partial
205
- # clone according to the given filter
206
- #
207
- # @option options [Boolean, nil] :single_branch Control whether the clone
208
- # limits fetch refspecs to a single branch.
209
- # - If nil (default), no flag is passed and the Git default is used.
210
- # - If true, `--single-branch` is passed to limit the refspec to the
211
- # checkout branch.
212
- # - If false, `--no-single-branch` is passed to broaden the refspec (useful
213
- # for shallow clones that should include all branches).
214
- #
215
- # @option options [String, nil] :git_ssh An optional custom SSH command
216
- #
217
- # - If not specified, uses the global config (Git.configure { |c| c.git_ssh = ... }).
218
- # - If nil, disables SSH for this instance.
219
- # - If a non-empty string, uses that value for this instance.
220
- #
221
- # @option options [Logger] :log A logger to use for Git operations. Git
222
- # commands are logged at the `:info` level. Additional logging is done
223
- # at the `:debug` level.
207
+ # @param value [String, Boolean] the value to set for the git configuration option
224
208
  #
225
- # @option options [Boolean] :mirror Set up a mirror of the source repository.
209
+ # @return [Git::CommandLine::Result] the result of the git configuration command
226
210
  #
227
- # @option options [String] :origin Use the value instead `origin` to track
228
- # the upstream repository.
211
+ # @overload global_config(name)
212
+ # Get the value for the git named configuration option
229
213
  #
230
- # @option options [Pathname] :chdir Run `git clone` from within this directory.
214
+ # @param name [String] the name of the git configuration option
231
215
  #
232
- # The `directory` parameter (or the repository basename when `directory` is nil)
233
- # is resolved relative to `:chdir`, just as if you had `cd`'d into it before
234
- # running `git clone`. The returned path is the join of `:chdir` and the
235
- # cloned directory path.
216
+ # @return [String] the value of the git configuration option
236
217
  #
237
- # @option options [Pathname] :path Deprecated — use `:chdir` instead.
218
+ # @overload global_config()
219
+ # List all git configuration options
238
220
  #
239
- # @option options [Boolean] :recursive After the clone is created, initialize
240
- # all submodules within, using their default settings.
221
+ # @return [Hash{String => String}] a hash of all git configuration options
241
222
  #
242
- # @example Clone into the default directory `ruby-git`
243
- # git = Git.clone('https://github.com/ruby-git/ruby-git.git')
244
- #
245
- # @example Clone and then checkout the `development` branch
246
- # git = Git.clone('https://github.com/ruby-git/ruby-git.git', branch: 'development')
247
- #
248
- # @example Clone into a different directory `my-ruby-git`
249
- # git = Git.clone('https://github.com/ruby-git/ruby-git.git', 'my-ruby-git')
250
- #
251
- # @example Clone into a specific parent directory
252
- # git = Git.clone('https://github.com/ruby-git/ruby-git.git', chdir: '/path/to/projects')
253
- # # clones into /path/to/projects/ruby-git
254
- #
255
- # @example Create a bare repository in the directory `ruby-git.git`
256
- # git = Git.clone('https://github.com/ruby-git/ruby-git.git', bare: true)
257
- #
258
- # @example Clone a repository and set a single config option
259
- # git = Git.clone(
260
- # 'https://github.com/ruby-git/ruby-git.git',
261
- # config: 'submodule.recurse=true'
262
- # )
263
- #
264
- # @example Clone a repository and set multiple config options
265
- # git = Git.clone(
266
- # 'https://github.com/ruby-git/ruby-git.git',
267
- # config: ['user.name=John Doe', 'user.email=john@doe.com']
268
- # )
269
- #
270
- # @example Clone using a specific SSH key
271
- # git = Git.clone(
272
- # 'git@github.com:ruby-git/ruby-git.git',
273
- # 'local-dir',
274
- # git_ssh: 'ssh -i /path/to/private_key'
275
- # )
276
- #
277
- # @return [Git::Repository] an object that can execute git commands in the context
278
- # of the cloned local working copy or cloned repository.
279
- #
280
- def self.clone(repository_url, directory = nil, options = {})
281
- Git::Repository.clone(repository_url, directory, options)
223
+ # @deprecated Mixing in the `Git` module is deprecated and will be removed in v6.0.0.
224
+ # Use `Git.config_get(name, global: true)`, `Git.config_set(name, value, global: true)`, or
225
+ # `Git.config_list(global: true)` instead.
226
+ def global_config(name = nil, value = nil)
227
+ Git::Deprecation.warn(
228
+ 'Git#global_config is deprecated and will be removed in v6.0.0. ' \
229
+ 'Use Git.config_get(name, global: true), Git.config_set(name, value, global: true), ' \
230
+ 'or Git.config_list(global: true) instead.'
231
+ )
232
+ Git.__send__(:legacy_config_set_get_list, name, value, global: true)
282
233
  end
283
234
 
284
235
  # Returns the name of the default branch of the given repository
@@ -301,15 +252,16 @@ module Git
301
252
  # @example with the logging option
302
253
  # logger = Logger.new(STDOUT, level: Logger::INFO)
303
254
  # Git.default_branch('.', log: logger) # => 'master'
304
- # I, [2022-04-13T16:01:33.221596 #18415] INFO -- : git '-c' 'core.quotePath=true'
305
- # '-c' 'color.ui=false' ls-remote '--symref' '--' '.' 'HEAD' 2>&1
255
+ # # Logs the executed git command to STDOUT, for example:
256
+ # # I, [2022-04-13T16:01:33.221596 #18415] INFO -- : git '-c' 'core.quotePath=true'
257
+ # # '-c' 'color.ui=false' ls-remote '--symref' '--' '.' 'HEAD' 2>&1
306
258
  #
307
259
  # @param repository [URI, Pathname, String] The (possibly remote) repository to get the default branch name for
308
260
  #
309
261
  # See [GIT URLS](https://git-scm.com/docs/git-clone#_git_urls_a_id_urls_a)
310
262
  # for more information.
311
263
  #
312
- # @param [Hash] options The options for this command (see list of valid
264
+ # @param options [Hash] The options for this command (see list of valid
313
265
  # options below)
314
266
  #
315
267
  # @option options [Logger] :log A logger to use for Git operations. Git
@@ -324,94 +276,64 @@ module Git
324
276
  Git::Parsers::LsRemote.parse_default_branch(output)
325
277
  end
326
278
 
327
- # Export the current HEAD (or a branch, if <tt>options[:branch]</tt>
328
- # is specified) into the +name+ directory, then remove all traces of git from the
329
- # directory.
330
- #
331
- # See +clone+ for options. Does not obey the <tt>:remote</tt> option,
332
- # since the .git info will be deleted anyway; always uses the default
333
- # remote, 'origin.'
334
- def self.export(repository, name, options = {})
335
- options.delete(:remote)
336
- repo = clone(repository, name, { depth: 1 }.merge(options))
337
- repo.checkout("origin/#{options[:branch]}") if options[:branch]
338
- FileUtils.rm_r File.join(repo.dir.to_s, '.git')
339
- end
340
-
341
- # Same as g.config, but forces it to be at the global level
342
- #
343
- # g.config('user.name', 'Scott Chacon') # sets value
344
- # g.config('user.email', 'email@email.com') # sets value
345
- # g.config('user.name') # returns 'Scott Chacon'
346
- # g.config # returns whole config hash
347
- def self.global_config(name = nil, value = nil)
348
- run_config_utility(name, value, global: true)
349
- end
350
-
351
- # Create an empty Git repository or reinitialize an existing Git repository
352
- #
353
- # @param [Pathname] directory If the `:bare` option is NOT given or is not
354
- # `true`, the repository will be created in `"#{directory}/.git"`.
355
- # Otherwise, the repository is created in `"#{directory}"`.
279
+ # Clone a repository into `directory` then remove its `.git` directory
356
280
  #
357
- # All directories along the path to `directory` are created if they do not exist.
281
+ # Exports the current HEAD (or the specific branch given in <tt>options[:branch]</tt>)
282
+ # into the given `directory`. It then removes all traces of git from the directory.
358
283
  #
359
- # A relative path is referenced from the current working directory of the process
360
- # and converted to an absolute path using
361
- # [File.expand_path](https://www.rubydoc.info/stdlib/core/File.expand_path).
284
+ # Takes the same options as {Git.clone} except that `:remote` is silently ignored
285
+ # and `:depth` defaults to 1.
362
286
  #
363
- # @param [Hash] options The options for this command (see list of valid
364
- # options below)
365
- #
366
- # @option options [Boolean] :bare Instead of creating a repository at
367
- # `"#{directory}/.git"`, create a bare repository at `"#{directory}"`.
368
- # See [what is a bare repository?](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefbarerepositoryabarerepository).
287
+ # @param repository_url [String, URI, Pathname] the repository to export from
369
288
  #
370
- # @option options [String] :initial_branch Use the specified name for the
371
- # initial branch in the newly created repository.
289
+ # @param directory [String, Pathname, nil] the directory to export into; defaults to the
290
+ # repository basename
372
291
  #
373
- # @option options [Pathname] :repository the path to put the newly initialized
374
- # Git repository. The default for non-bare repository is `"#{directory}/.git"`.
292
+ # @param options [Hash] options forwarded to {Git.clone} (`:remote` is ignored;
293
+ # `:depth` defaults to 1)
375
294
  #
376
- # A relative path is referenced from the current working directory of the process
377
- # and converted to an absolute path using
378
- # [File.expand_path](https://www.rubydoc.info/stdlib/core/File.expand_path).
295
+ # @option options [String] :branch the branch or tag to export instead of HEAD
379
296
  #
380
- # @option options [Pathname] :separate_git_dir Alias for `:repository`.
381
- #
382
- # @option options [String, nil] :git_ssh An optional custom SSH command
297
+ # @return [void]
383
298
  #
384
- # - If not specified, uses the global config (Git.configure { |c| c.git_ssh = ... }).
385
- # - If nil, disables SSH for this instance.
386
- # - If a non-empty string, uses that value for this instance.
299
+ def self.export(repository_url, directory = nil, options = {})
300
+ options.delete(:remote)
301
+ repo = clone(repository_url, directory, { depth: 1 }.merge(options))
302
+ repo.checkout("origin/#{options[:branch]}") if options[:branch]
303
+ FileUtils.rm_r File.join(repo.dir.to_s, '.git')
304
+ end
305
+
306
+ # Get or set a git global configuration value
387
307
  #
388
- # @option options [String, :use_global_config] :binary_path path to the git
389
- # binary; defaults to `Git::Config.instance.binary_path` when not specified.
390
- # Raises `ArgumentError` if set to `nil`.
308
+ # @example Set a value
309
+ # Git.global_config('user.name', 'Scott Chacon')
391
310
  #
392
- # @option options [Logger] :log A logger to use for Git operations. Git
393
- # commands are logged at the `:info` level. Additional logging is done
394
- # at the `:debug` level.
311
+ # @example Get a value
312
+ # Git.global_config('user.name') # => 'Scott Chacon'
395
313
  #
396
- # @return [Git::Repository] an object that can execute git commands in the context
397
- # of the newly initialized repository
314
+ # @example List all global config entries
315
+ # Git.global_config # => { 'user.name' => 'Scott Chacon', ... }
398
316
  #
399
- # @example Initialize a repository in the current directory
400
- # git = Git.init
317
+ # @param name [String, nil] the config key to get or set; omit to list all
401
318
  #
402
- # @example Initialize a repository in some other directory
403
- # git = Git.init '~/code/ruby-git'
319
+ # @param value [Object, nil] the value to set; omit to get or list
404
320
  #
405
- # @example Initialize a bare repository
406
- # git = Git.init '~/code/ruby-git.git', bare: true
321
+ # @return [String, Hash, Git::CommandLine::Result] the config value, all entries,
322
+ # or the result of the set command
407
323
  #
408
- # @example Initialize a repository in a non-default location (outside of the working copy)
409
- # git = Git.init '~/code/ruby-git', repository: '~/code/ruby-git.git'
324
+ # @deprecated Use {Git.config_get}, {Git.config_set}, or {Git.config_list} instead.
410
325
  #
411
- # @see https://git-scm.com/docs/git-init git init
326
+ # - `Git.global_config('user.name')` → `Git.config_get('user.name', global: true)`
327
+ # - `Git.global_config('user.name', 'Bob')` → `Git.config_set('user.name', 'Bob', global: true)`
328
+ # - `Git.global_config` → `Git.config_list(global: true)`
412
329
  #
413
- def self.init(directory = '.', options = {})
414
- Git::Repository.init(directory, options)
330
+ def self.global_config(name = nil, value = nil)
331
+ Git::Deprecation.warn(
332
+ 'Git.global_config is deprecated and will be removed in v6.0.0. ' \
333
+ 'Use Git.config_get(name, global: true), Git.config_set(name, value, global: true), ' \
334
+ 'or Git.config_list(global: true) instead.'
335
+ )
336
+ legacy_config_set_get_list(name, value, global: true)
415
337
  end
416
338
 
417
339
  # Option keys accepted by {.ls_remote}
@@ -429,84 +351,129 @@ module Git
429
351
  ].freeze
430
352
  private_constant :LS_REMOTE_ALLOWED_OPTS
431
353
 
432
- # returns a Hash containing information about the references
433
- # of the target repository
354
+ # Displays references available in a remote repository along with the associated commit IDs
434
355
  #
435
- # options
436
- # :refs
356
+ # @example From a remote repository given its URL
357
+ # references = Git.ls_remote('https://github.com/user/repo.git')
437
358
  #
438
- # @param [String|NilClass] location the target repository location or nil for '.'
439
- # @return [{String=>Hash}] the available references of the target repo.
440
- def self.ls_remote(location = nil, options = {})
441
- options = options.dup
442
- log = options.delete(:log)
443
- unknown = options.keys - LS_REMOTE_ALLOWED_OPTS
444
- raise ArgumentError, "Unknown options: #{unknown.join(', ')}" unless unknown.empty?
445
-
446
- context = Git::ExecutionContext::Global.new(logger: log)
447
- repository = location || '.'
448
- output_lines = Git::Commands::LsRemote.new(context).call(repository, **options).stdout.split("\n")
449
- Git::Parsers::LsRemote.parse_output(output_lines)
450
- end
451
-
452
- # Open a an existing Git working directory
359
+ # @example From the default remote of the current repository
360
+ # references = Git.ls_remote
453
361
  #
454
- # Git.open will most likely be the most common way to create
455
- # a git reference, referring to an existing working directory.
362
+ # @example From a specific remote of the current repository
363
+ # references = Git.ls_remote('origin')
456
364
  #
457
- # If not provided in the options, the library will assume
458
- # the repository and index are in the default places (`.git/`, `.git/index`).
365
+ # @param repository [String, nil] the target repository location or the name of a remote
459
366
  #
460
- # @example Open the Git working directory in the current directory
461
- # git = Git.open
367
+ # Defaults to `'.'` (the current directory). Passing `nil` explicitly is
368
+ # deprecated and will be removed in v6.0.0; pass `'.'` or omit the argument.
462
369
  #
463
- # @example Open a Git working directory in some other directory
464
- # git = Git.open('~/Projects/ruby-git')
370
+ # @param options [Hash] the options to pass to the git command
465
371
  #
466
- # @example Use a logger to see what is going on
467
- # logger = Logger.new(STDOUT)
468
- # git = Git.open('~/Projects/ruby-git', log: logger)
372
+ # @option options [Boolean, nil] :branches (nil) limit output to refs under `refs/heads/`
469
373
  #
470
- # @example Open a working copy whose repository is in a non-standard directory
471
- # git = Git.open('~/Projects/ruby-git', repository: '~/Project/ruby-git.git')
374
+ # Alias: `:b`
472
375
  #
473
- # @param [Pathname] working_dir the path to the working directory to use
474
- # for git commands.
376
+ # @option options [Boolean, nil] :heads (nil) limit output to refs under `refs/heads/`
475
377
  #
476
- # A relative path is referenced from the current working directory of the process
477
- # and converted to an absolute path using
478
- # [File.expand_path](https://www.rubydoc.info/stdlib/core/File.expand_path).
378
+ # Deprecated: use `:branches` instead. Kept for backward compatibility with
379
+ # older git versions where `--heads` is the only supported flag.
479
380
  #
480
- # @param [Hash] options The options for this command (see list of valid
481
- # options below)
381
+ # Alias: `:h`
482
382
  #
483
- # @option options [Pathname] :repository used to specify a non-standard path to
484
- # the repository directory. The default is `"#{working_dir}/.git"`.
383
+ # @option options [Boolean, nil] :tags (nil) limit output to refs under `refs/tags/`
485
384
  #
486
- # @option options [Pathname] :index used to specify a non-standard path to an
487
- # index file. The default is `"#{working_dir}/.git/index"`
385
+ # Alias: `:t`
488
386
  #
489
- # @option options [String, nil] :git_ssh An optional custom SSH command
387
+ # @option options [Boolean, nil] :refs (nil) exclude peeled tags and pseudorefs
388
+ # like `HEAD` from the output
490
389
  #
491
- # - If not specified, uses the global config (Git.configure { |c| c.git_ssh = ... }).
492
- # - If nil, disables SSH for this instance.
493
- # - If a non-empty string, uses that value for this instance.
390
+ # @option options [String] :upload_pack (nil) full path to `git-upload-pack` on the
391
+ # remote host
494
392
  #
495
- # @option options [Logger] :log A logger to use for Git operations. Git
496
- # commands are logged at the `:info` level. Additional logging is done
497
- # at the `:debug` level.
393
+ # Useful when accessing repositories via SSH where the daemon does not use the
394
+ # PATH configured by the user.
395
+ #
396
+ # @option options [Boolean, nil] :quiet (nil) do not print the remote URL to stderr
397
+ #
398
+ # Alias: `:q`
399
+ #
400
+ # @option options [Boolean, nil] :exit_code (nil) exit with status `2` when no
401
+ # matching refs are found in the remote repository
402
+ #
403
+ # Without this option, the command exits `0` whenever it successfully
404
+ # communicates with the remote, even if no refs match.
405
+ #
406
+ # @option options [String] :sort (nil) sort output by the given key
407
+ #
408
+ # Prefix `-` for descending order. Supports `"version:refname"` or `"v:refname"`.
409
+ # See `git for-each-ref` for sort key documentation.
410
+ #
411
+ # @option options [String, Array<String>] :server_option (nil) transmit a string to
412
+ # the server when communicating using protocol version 2
498
413
  #
499
- # @return [Git::Repository] an object that can execute git commands in the context
500
- # of the opened working copy
414
+ # The string must not contain NUL or LF characters. Repeatable by passing an
415
+ # Array. Alias: `:o`
501
416
  #
502
- def self.open(working_dir, options = {})
503
- Git::Repository.open(working_dir, options)
417
+ # @option options [Numeric] :timeout (nil) execution timeout in seconds
418
+ #
419
+ # @option options [Logger] :log (nil) a logger to use for Git operations
420
+ #
421
+ # Git commands are logged at the `:info` level. Additional logging is done at
422
+ # the `:debug` level.
423
+ #
424
+ # @return [Hash{String => Hash}] the available references of the target repo
425
+ #
426
+ def self.ls_remote(repository = '.', options = {})
427
+ repository = normalize_ls_remote_repository(repository)
428
+ options = options.dup
429
+ log = options.delete(:log)
430
+ unknown = options.keys - LS_REMOTE_ALLOWED_OPTS
431
+ raise ArgumentError, "Unknown options: #{unknown.join(', ')}" unless unknown.empty?
432
+
433
+ context = Git::ExecutionContext::Global.new(logger: log)
434
+ output_lines = Git::Commands::LsRemote.new(context).call(repository, **options).stdout.split("\n")
435
+ Git::Parsers::LsRemote.parse_output(output_lines)
436
+ end
437
+
438
+ # Normalize the repository argument for {.ls_remote}
439
+ #
440
+ # Returns the repository unchanged unless it is nil, in which case a
441
+ # deprecation warning is emitted and `'.'` is returned.
442
+ #
443
+ # @param repository [String, nil] the repository argument passed by the caller
444
+ #
445
+ # @return [String] the normalized repository value (`'.'` when nil was given)
446
+ #
447
+ # @api private
448
+ #
449
+ def self.normalize_ls_remote_repository(repository)
450
+ return repository unless repository.nil?
451
+
452
+ Git::Deprecation.warn(
453
+ 'Passing nil as the repository to Git.ls_remote is deprecated and will ' \
454
+ "be removed in v6.0.0. Pass '.' explicitly or omit the argument instead."
455
+ )
456
+
457
+ '.'
504
458
  end
459
+ private_class_method :normalize_ls_remote_repository
505
460
 
506
461
  # Thread-safe cache for git versions, keyed by binary path.
507
462
  @git_version_cache_mutex = Mutex.new
508
463
  @git_version_cache = {}
509
464
 
465
+ # Return the cached git version for the given binary path
466
+ #
467
+ # If it isn't already known, compute it using the given block.
468
+ #
469
+ # @param binary_path [String] the path to the git binary
470
+ #
471
+ # @return [Git::Version] the git version
472
+ #
473
+ # @yield [] compute the git version if it is not cached
474
+ #
475
+ # @yieldreturn [Git::Version] the computed git version
476
+ #
510
477
  # @api private
511
478
  def self.cached_git_version(binary_path, &block)
512
479
  @git_version_cache_mutex.synchronize do
@@ -514,6 +481,10 @@ module Git
514
481
  end
515
482
  end
516
483
 
484
+ # Clear the cached git version for all binary paths
485
+ #
486
+ # @return [void]
487
+ #
517
488
  # @api private
518
489
  def self.clear_git_version_cache
519
490
  @git_version_cache_mutex.synchronize do
@@ -545,35 +516,135 @@ module Git
545
516
  cached_git_version(path) { run_git_version(path) }
546
517
  end
547
518
 
519
+ # Return the version of the git binary
520
+ #
521
+ # @param path [String] the path to the git binary
522
+ #
523
+ # @return [Git::Version] the parsed git version
524
+ #
525
+ # @raise [Git::UnexpectedResultError] if the version output cannot be parsed
526
+ #
527
+ # @raise [Git::FailedError] if the git binary exits with a non-zero status
528
+ #
529
+ # @raise [Git::Error] if the binary is not found or fails to launch
530
+ #
548
531
  # @api private
532
+ #
549
533
  def self.run_git_version(path)
550
534
  output = Git::Commands::Version.new(Git::ExecutionContext::Global.new(binary_path: path)).call.stdout
551
535
  Git::Version.parse(output)
552
536
  end
553
537
  private_class_method :run_git_version
554
538
 
539
+ # Get or set a git config value
540
+ #
541
+ # @overload legacy_config_set_get_list(name, value, global:)
542
+ #
543
+ # Set the value of a git configuration option
544
+ #
545
+ # @param name [String] the name of the git configuration value to set
546
+ #
547
+ # @param value [String, Boolean] the value to set
548
+ #
549
+ # @param global [Boolean] true to use the global git configuration, false for the
550
+ # local repo config
551
+ #
552
+ # @return [Git::CommandLine::Result] the result of the git config command
553
+ #
554
+ # @overload legacy_config_set_get_list(name, global:)
555
+ #
556
+ # Get the value of a git configuration option
557
+ #
558
+ # @param name [String] the name of the git configuration value to get
559
+ #
560
+ # @param global [Boolean] true to use the global git configuration, false for the
561
+ # local repo config
562
+ #
563
+ # @return [String] the value of the git configuration option
564
+ #
565
+ # @overload legacy_config_set_get_list(global:)
566
+ #
567
+ # Get all git configuration options
568
+ #
569
+ # @param global [Boolean] true to use the global git configuration, false for the
570
+ # local repo config
571
+ #
572
+ # @return [Hash{String => String}] all git configuration options
573
+ #
574
+ # @raise [Git::FailedError] if the git config command fails
575
+ #
555
576
  # @api private
556
- def self.run_config_utility(name, value, global:)
557
- context = Git::ExecutionContext::Global.new
558
- options = global ? { global: true } : {}
559
-
560
- return Git::Commands::ConfigOptionSyntax::Set.new(context).call(name, value, **options) if !name.nil? && !value.nil?
561
- return run_config_get(context, name, options) if name
577
+ #
578
+ def self.legacy_config_set_get_list(name, value, global:)
579
+ if !name.nil? && !value.nil?
580
+ legacy_config_set(name, value, global:)
581
+ elsif !name.nil?
582
+ legacy_config_get(name, global:)
583
+ else
584
+ legacy_config_list(global:)
585
+ end
586
+ end
587
+ private_class_method :legacy_config_set_get_list
562
588
 
563
- output = Git::Commands::ConfigOptionSyntax::List.new(context).call(**options).stdout
564
- parse_config_list(output.split("\n"))
589
+ # Set the value of a git configuration option
590
+ #
591
+ # @param name [String] the name of the git configuration value to set
592
+ #
593
+ # @param value [String, Boolean] the value to set
594
+ #
595
+ # @param global [Boolean] whether to use the global git configuration
596
+ #
597
+ # @api private
598
+ #
599
+ def self.legacy_config_set(name, value, global:)
600
+ options = global ? { global: true } : {}
601
+ Git::Commands::ConfigOptionSyntax::Set.new(execution_context).call(name, value, **options)
565
602
  end
566
- private_class_method :run_config_utility
603
+ private_class_method :legacy_config_set
567
604
 
568
- def self.run_config_get(context, name, options)
569
- result = Git::Commands::ConfigOptionSyntax::Get.new(context).call(name, **options)
605
+ # Get the value of a git configuration option
606
+ #
607
+ # @param name [String] the name of the git configuration option
608
+ #
609
+ # @param global [Boolean] whether to use the global git configuration
610
+ #
611
+ # @return [String] the value of the git configuration option
612
+ #
613
+ # @api private
614
+ #
615
+ def self.legacy_config_get(name, global:)
616
+ options = global ? { global: true } : {}
617
+ result = Git::Commands::ConfigOptionSyntax::Get.new(execution_context).call(name, **options)
570
618
  raise Git::FailedError, result if result.status.exitstatus != 0
571
619
 
572
620
  result.stdout
573
621
  end
574
- private_class_method :run_config_get
622
+ private_class_method :legacy_config_get
575
623
 
624
+ # Get a list of all git configuration options
625
+ #
626
+ # @param global [Boolean] true to use the global git configuration, false for the
627
+ # local repo config
628
+ #
629
+ # @return [Hash{String => String}] all git configuration options
630
+ #
576
631
  # @api private
632
+ #
633
+ def self.legacy_config_list(global:)
634
+ options = global ? { global: true } : {}
635
+ output = Git::Commands::ConfigOptionSyntax::List.new(execution_context).call(**options).stdout
636
+ parse_config_list(output.split("\n"))
637
+ end
638
+ private_class_method :legacy_config_list
639
+
640
+ # Parse the output of `git config --list` into a hash
641
+ #
642
+ # @param lines [Array<String>] the lines of output from `git config --list`
643
+ #
644
+ # @return [Hash{String => String}] the parsed git configuration options
645
+ #
646
+ # @api private
647
+ #
577
648
  def self.parse_config_list(lines)
578
649
  lines.each_with_object({}) do |line, hsh|
579
650
  key, value = line.split('=', 2)
@@ -600,10 +671,23 @@ module Git
600
671
  # The +:local+, +:worktree+, and +:blob+ scopes require an active git
601
672
  # repository and are therefore not valid at the Git module level.
602
673
  #
674
+ # @param options_to_check [Hash{Symbol => Object}] the scope options to check
675
+ #
676
+ # If any of the options listed in +REPOSITORY_SPECIFIC_SCOPES+ are present and
677
+ # truthy, an +ArgumentError+ will be raised.
678
+ #
679
+ # @option options_to_check [Object] :local truthy value requests local scope
680
+ #
681
+ # @option options_to_check [Object] :worktree truthy value requests worktree scope
682
+ #
683
+ # @option options_to_check [Object] :blob truthy value requests blob scope
684
+ #
685
+ # @raise [ArgumentError] if a repository-specific scope is requested
686
+ #
603
687
  # @api private
604
688
  #
605
- def self.assert_valid_scope!(**opts)
606
- invalid = REPOSITORY_SPECIFIC_SCOPES.select { |s| opts[s] }
689
+ def self.assert_valid_scope!(**options_to_check)
690
+ invalid = REPOSITORY_SPECIFIC_SCOPES.select { |s| options_to_check[s] }
607
691
  return if invalid.empty?
608
692
 
609
693
  raise ArgumentError, "#{invalid.join(', ')} scope requires a repository"