git 1.19.1 → 5.5.0

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 (265) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +5 -1
  3. data/AI_POLICY.md +24 -0
  4. data/CHANGELOG.md +689 -0
  5. data/CODE_OF_CONDUCT.md +25 -0
  6. data/CONTRIBUTING.md +1175 -97
  7. data/GOVERNANCE.md +106 -0
  8. data/LICENSE +1 -1
  9. data/MAINTAINERS.md +17 -4
  10. data/README.md +476 -320
  11. data/UPGRADING.md +1138 -0
  12. data/git.gemspec +124 -36
  13. data/lib/git/author.rb +39 -7
  14. data/lib/git/author_info.rb +66 -0
  15. data/lib/git/branch.rb +615 -65
  16. data/lib/git/branch_delete_failure.rb +34 -0
  17. data/lib/git/branch_delete_result.rb +66 -0
  18. data/lib/git/branch_info.rb +237 -0
  19. data/lib/git/branches.rb +167 -44
  20. data/lib/git/command_line/base.rb +247 -0
  21. data/lib/git/command_line/capturing.rb +308 -0
  22. data/lib/git/command_line/result.rb +88 -0
  23. data/lib/git/command_line/streaming.rb +236 -0
  24. data/lib/git/command_line.rb +52 -0
  25. data/lib/git/commands/add.rb +139 -0
  26. data/lib/git/commands/am/abort.rb +43 -0
  27. data/lib/git/commands/am/apply.rb +263 -0
  28. data/lib/git/commands/am/continue.rb +43 -0
  29. data/lib/git/commands/am/quit.rb +43 -0
  30. data/lib/git/commands/am/retry.rb +49 -0
  31. data/lib/git/commands/am/show_current_patch.rb +64 -0
  32. data/lib/git/commands/am/skip.rb +42 -0
  33. data/lib/git/commands/am.rb +33 -0
  34. data/lib/git/commands/apply.rb +242 -0
  35. data/lib/git/commands/archive/list_formats.rb +46 -0
  36. data/lib/git/commands/archive.rb +145 -0
  37. data/lib/git/commands/arguments.rb +4521 -0
  38. data/lib/git/commands/base.rb +502 -0
  39. data/lib/git/commands/branch/copy.rb +102 -0
  40. data/lib/git/commands/branch/create.rb +177 -0
  41. data/lib/git/commands/branch/delete.rb +88 -0
  42. data/lib/git/commands/branch/list.rb +178 -0
  43. data/lib/git/commands/branch/move.rb +102 -0
  44. data/lib/git/commands/branch/set_upstream.rb +86 -0
  45. data/lib/git/commands/branch/show_current.rb +49 -0
  46. data/lib/git/commands/branch/unset_upstream.rb +53 -0
  47. data/lib/git/commands/branch.rb +34 -0
  48. data/lib/git/commands/cat_file/batch.rb +385 -0
  49. data/lib/git/commands/cat_file/filtered.rb +105 -0
  50. data/lib/git/commands/cat_file/raw.rb +271 -0
  51. data/lib/git/commands/cat_file.rb +49 -0
  52. data/lib/git/commands/checkout/branch.rb +153 -0
  53. data/lib/git/commands/checkout/files.rb +116 -0
  54. data/lib/git/commands/checkout.rb +38 -0
  55. data/lib/git/commands/checkout_index.rb +106 -0
  56. data/lib/git/commands/clean.rb +102 -0
  57. data/lib/git/commands/clone.rb +241 -0
  58. data/lib/git/commands/commit.rb +273 -0
  59. data/lib/git/commands/commit_tree.rb +101 -0
  60. data/lib/git/commands/config_option_syntax/add.rb +86 -0
  61. data/lib/git/commands/config_option_syntax/get.rb +121 -0
  62. data/lib/git/commands/config_option_syntax/get_all.rb +118 -0
  63. data/lib/git/commands/config_option_syntax/get_color.rb +95 -0
  64. data/lib/git/commands/config_option_syntax/get_color_bool.rb +96 -0
  65. data/lib/git/commands/config_option_syntax/get_regexp.rb +119 -0
  66. data/lib/git/commands/config_option_syntax/get_urlmatch.rb +111 -0
  67. data/lib/git/commands/config_option_syntax/list.rb +111 -0
  68. data/lib/git/commands/config_option_syntax/remove_section.rb +79 -0
  69. data/lib/git/commands/config_option_syntax/rename_section.rb +83 -0
  70. data/lib/git/commands/config_option_syntax/replace_all.rb +109 -0
  71. data/lib/git/commands/config_option_syntax/set.rb +119 -0
  72. data/lib/git/commands/config_option_syntax/unset.rb +92 -0
  73. data/lib/git/commands/config_option_syntax/unset_all.rb +94 -0
  74. data/lib/git/commands/config_option_syntax.rb +56 -0
  75. data/lib/git/commands/describe.rb +156 -0
  76. data/lib/git/commands/diff.rb +657 -0
  77. data/lib/git/commands/diff_files.rb +519 -0
  78. data/lib/git/commands/diff_index.rb +499 -0
  79. data/lib/git/commands/fetch.rb +354 -0
  80. data/lib/git/commands/fsck.rb +138 -0
  81. data/lib/git/commands/gc.rb +134 -0
  82. data/lib/git/commands/grep.rb +339 -0
  83. data/lib/git/commands/init.rb +101 -0
  84. data/lib/git/commands/log.rb +634 -0
  85. data/lib/git/commands/ls_files.rb +195 -0
  86. data/lib/git/commands/ls_remote.rb +161 -0
  87. data/lib/git/commands/ls_tree.rb +135 -0
  88. data/lib/git/commands/maintenance/register.rb +77 -0
  89. data/lib/git/commands/maintenance/run.rb +109 -0
  90. data/lib/git/commands/maintenance/start.rb +71 -0
  91. data/lib/git/commands/maintenance/stop.rb +60 -0
  92. data/lib/git/commands/maintenance/unregister.rb +84 -0
  93. data/lib/git/commands/maintenance.rb +31 -0
  94. data/lib/git/commands/merge/abort.rb +44 -0
  95. data/lib/git/commands/merge/continue.rb +44 -0
  96. data/lib/git/commands/merge/quit.rb +46 -0
  97. data/lib/git/commands/merge/start.rb +250 -0
  98. data/lib/git/commands/merge.rb +28 -0
  99. data/lib/git/commands/merge_base.rb +91 -0
  100. data/lib/git/commands/mv.rb +82 -0
  101. data/lib/git/commands/name_rev.rb +119 -0
  102. data/lib/git/commands/pull.rb +382 -0
  103. data/lib/git/commands/push.rb +251 -0
  104. data/lib/git/commands/read_tree.rb +154 -0
  105. data/lib/git/commands/remote/add.rb +96 -0
  106. data/lib/git/commands/remote/get_url.rb +68 -0
  107. data/lib/git/commands/remote/list.rb +56 -0
  108. data/lib/git/commands/remote/prune.rb +63 -0
  109. data/lib/git/commands/remote/remove.rb +52 -0
  110. data/lib/git/commands/remote/rename.rb +76 -0
  111. data/lib/git/commands/remote/set_branches.rb +70 -0
  112. data/lib/git/commands/remote/set_head.rb +89 -0
  113. data/lib/git/commands/remote/set_url.rb +78 -0
  114. data/lib/git/commands/remote/set_url_add.rb +70 -0
  115. data/lib/git/commands/remote/set_url_delete.rb +71 -0
  116. data/lib/git/commands/remote/show.rb +77 -0
  117. data/lib/git/commands/remote/update.rb +79 -0
  118. data/lib/git/commands/remote.rb +42 -0
  119. data/lib/git/commands/repack.rb +281 -0
  120. data/lib/git/commands/reset.rb +154 -0
  121. data/lib/git/commands/rev_parse.rb +304 -0
  122. data/lib/git/commands/revert/abort.rb +45 -0
  123. data/lib/git/commands/revert/continue.rb +62 -0
  124. data/lib/git/commands/revert/quit.rb +47 -0
  125. data/lib/git/commands/revert/skip.rb +44 -0
  126. data/lib/git/commands/revert/start.rb +158 -0
  127. data/lib/git/commands/revert.rb +29 -0
  128. data/lib/git/commands/rm.rb +113 -0
  129. data/lib/git/commands/show.rb +632 -0
  130. data/lib/git/commands/show_ref/exclude_existing.rb +119 -0
  131. data/lib/git/commands/show_ref/exists.rb +80 -0
  132. data/lib/git/commands/show_ref/list.rb +149 -0
  133. data/lib/git/commands/show_ref/verify.rb +122 -0
  134. data/lib/git/commands/show_ref.rb +42 -0
  135. data/lib/git/commands/stash/apply.rb +81 -0
  136. data/lib/git/commands/stash/branch.rb +67 -0
  137. data/lib/git/commands/stash/clear.rb +43 -0
  138. data/lib/git/commands/stash/create.rb +60 -0
  139. data/lib/git/commands/stash/drop.rb +73 -0
  140. data/lib/git/commands/stash/list.rb +43 -0
  141. data/lib/git/commands/stash/pop.rb +87 -0
  142. data/lib/git/commands/stash/push.rb +112 -0
  143. data/lib/git/commands/stash/show.rb +158 -0
  144. data/lib/git/commands/stash/store.rb +72 -0
  145. data/lib/git/commands/stash.rb +38 -0
  146. data/lib/git/commands/status.rb +174 -0
  147. data/lib/git/commands/symbolic_ref/delete.rb +72 -0
  148. data/lib/git/commands/symbolic_ref/read.rb +99 -0
  149. data/lib/git/commands/symbolic_ref/update.rb +79 -0
  150. data/lib/git/commands/symbolic_ref.rb +38 -0
  151. data/lib/git/commands/tag/create.rb +142 -0
  152. data/lib/git/commands/tag/delete.rb +57 -0
  153. data/lib/git/commands/tag/list.rb +146 -0
  154. data/lib/git/commands/tag/verify.rb +71 -0
  155. data/lib/git/commands/tag.rb +26 -0
  156. data/lib/git/commands/update_ref/batch.rb +145 -0
  157. data/lib/git/commands/update_ref/delete.rb +90 -0
  158. data/lib/git/commands/update_ref/update.rb +103 -0
  159. data/lib/git/commands/update_ref.rb +42 -0
  160. data/lib/git/commands/version.rb +60 -0
  161. data/lib/git/commands/worktree/add.rb +139 -0
  162. data/lib/git/commands/worktree/list.rb +64 -0
  163. data/lib/git/commands/worktree/lock.rb +58 -0
  164. data/lib/git/commands/worktree/management_base.rb +51 -0
  165. data/lib/git/commands/worktree/move.rb +66 -0
  166. data/lib/git/commands/worktree/prune.rb +67 -0
  167. data/lib/git/commands/worktree/remove.rb +63 -0
  168. data/lib/git/commands/worktree/repair.rb +76 -0
  169. data/lib/git/commands/worktree/unlock.rb +47 -0
  170. data/lib/git/commands/worktree.rb +43 -0
  171. data/lib/git/commands/write_tree.rb +68 -0
  172. data/lib/git/commands.rb +88 -0
  173. data/lib/git/config.rb +72 -5
  174. data/lib/git/config_entry_info.rb +106 -0
  175. data/lib/git/configuring.rb +795 -0
  176. data/lib/git/detached_head_info.rb +57 -0
  177. data/lib/git/diff.rb +437 -86
  178. data/lib/git/diff_file_numstat_info.rb +31 -0
  179. data/lib/git/diff_file_patch_info.rb +136 -0
  180. data/lib/git/diff_file_raw_info.rb +129 -0
  181. data/lib/git/diff_info.rb +162 -0
  182. data/lib/git/diff_path_status.rb +107 -0
  183. data/lib/git/diff_result.rb +34 -0
  184. data/lib/git/diff_stats.rb +111 -0
  185. data/lib/git/dirstat_info.rb +102 -0
  186. data/lib/git/encoding_utils.rb +32 -1
  187. data/lib/git/errors.rb +285 -0
  188. data/lib/git/escaped_path.rb +57 -5
  189. data/lib/git/execution_context/global.rb +31 -0
  190. data/lib/git/execution_context/repository.rb +151 -0
  191. data/lib/git/execution_context.rb +559 -0
  192. data/lib/git/factories.rb +813 -0
  193. data/lib/git/file_ref.rb +77 -0
  194. data/lib/git/fsck_object.rb +56 -0
  195. data/lib/git/fsck_result.rb +132 -0
  196. data/lib/git/log.rb +306 -90
  197. data/lib/git/object.rb +563 -141
  198. data/lib/git/parsers/branch.rb +240 -0
  199. data/lib/git/parsers/cat_file.rb +111 -0
  200. data/lib/git/parsers/config_entry.rb +110 -0
  201. data/lib/git/parsers/diff.rb +792 -0
  202. data/lib/git/parsers/fsck.rb +144 -0
  203. data/lib/git/parsers/grep.rb +42 -0
  204. data/lib/git/parsers/ls_remote.rb +79 -0
  205. data/lib/git/parsers/ls_tree.rb +58 -0
  206. data/lib/git/parsers/remote.rb +162 -0
  207. data/lib/git/parsers/stash.rb +292 -0
  208. data/lib/git/parsers/status.rb +251 -0
  209. data/lib/git/parsers/tag.rb +341 -0
  210. data/lib/git/parsers/worktree.rb +185 -0
  211. data/lib/git/path_resolver.rb +206 -0
  212. data/lib/git/remote.rb +165 -12
  213. data/lib/git/remote_info.rb +203 -0
  214. data/lib/git/repository/branching.rb +964 -0
  215. data/lib/git/repository/committing.rb +246 -0
  216. data/lib/git/repository/context_helpers.rb +293 -0
  217. data/lib/git/repository/diffing.rb +785 -0
  218. data/lib/git/repository/inspecting.rb +252 -0
  219. data/lib/git/repository/logging.rb +410 -0
  220. data/lib/git/repository/maintenance.rb +65 -0
  221. data/lib/git/repository/merging.rb +451 -0
  222. data/lib/git/repository/object_operations.rb +1551 -0
  223. data/lib/git/repository/remote_operations.rb +984 -0
  224. data/lib/git/repository/shared_private.rb +120 -0
  225. data/lib/git/repository/staging.rb +587 -0
  226. data/lib/git/repository/stashing.rb +623 -0
  227. data/lib/git/repository/status_operations.rb +249 -0
  228. data/lib/git/repository/worktree_operations.rb +339 -0
  229. data/lib/git/repository.rb +484 -2
  230. data/lib/git/stash.rb +109 -12
  231. data/lib/git/stash_info.rb +102 -0
  232. data/lib/git/stashes.rb +169 -26
  233. data/lib/git/status.rb +308 -122
  234. data/lib/git/status_file_info.rb +258 -0
  235. data/lib/git/status_info.rb +189 -0
  236. data/lib/git/tag_delete_failure.rb +34 -0
  237. data/lib/git/tag_delete_result.rb +66 -0
  238. data/lib/git/tag_info.rb +99 -0
  239. data/lib/git/url.rb +15 -8
  240. data/lib/git/version.rb +113 -2
  241. data/lib/git/version_constraint.rb +85 -0
  242. data/lib/git/worktree.rb +150 -8
  243. data/lib/git/worktree_info.rb +128 -0
  244. data/lib/git/worktrees.rb +118 -13
  245. data/lib/git.rb +632 -234
  246. metadata +369 -54
  247. data/.github/stale.yml +0 -25
  248. data/.github/workflows/continuous_integration.yml +0 -49
  249. data/.gitignore +0 -10
  250. data/Dockerfile.changelog-rs +0 -12
  251. data/Gemfile +0 -5
  252. data/ISSUE_TEMPLATE.md +0 -15
  253. data/PULL_REQUEST_TEMPLATE.md +0 -9
  254. data/RELEASING.md +0 -70
  255. data/Rakefile +0 -60
  256. data/lib/git/base/factory.rb +0 -99
  257. data/lib/git/base.rb +0 -711
  258. data/lib/git/command_line_result.rb +0 -86
  259. data/lib/git/failed_error.rb +0 -53
  260. data/lib/git/git_execute_error.rb +0 -7
  261. data/lib/git/index.rb +0 -5
  262. data/lib/git/lib.rb +0 -1328
  263. data/lib/git/path.rb +0 -31
  264. data/lib/git/signaled_error.rb +0 -50
  265. data/lib/git/working_directory.rb +0 -4
@@ -0,0 +1,795 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'git/config_entry_info'
4
+ require 'git/parsers/config_entry'
5
+ require 'git/commands/config_option_syntax'
6
+
7
+ module Git
8
+ # Mixin that adds structured `git config` read and write operations
9
+ #
10
+ # Include or extend this module to gain the full suite of `config_*` methods.
11
+ # The including/extending class must implement two private methods:
12
+ #
13
+ # - {#execution_context} — returns a `Git::ExecutionContext` used to run commands
14
+ # - {#assert_valid_scope!} — raises `ArgumentError` if a requested scope is not
15
+ # valid in this context (e.g., `:local` is not valid without a repository)
16
+ #
17
+ # Read methods that return {Git::ConfigEntryInfo} objects merge
18
+ # `show_scope: true, show_origin: true, null: true` into the options so that
19
+ # every returned entry carries its full provenance. Two exceptions apply:
20
+ # {#config_get_urlmatch} merges only `show_scope: true, null: true` because
21
+ # git does not support `--show-origin` with `--get-urlmatch` (those entries
22
+ # always have `origin: nil`); {#config_get_colorbool} returns a plain `String`
23
+ # and does not use these output-format options at all.
24
+ #
25
+ # @example Include in a repository class
26
+ # class MyRepo
27
+ # include Git::Configuring
28
+ # private
29
+ # def execution_context = @ctx
30
+ # def assert_valid_scope!(**) = nil # all scopes allowed
31
+ # end
32
+ #
33
+ # @example Extend the Git module for global/system config
34
+ # extend Git::Configuring
35
+ # def self.execution_context = Git::ExecutionContext::Global.new
36
+ # private_class_method :execution_context
37
+ # def self.assert_valid_scope!(**opts)
38
+ # # reject :local, :worktree, :blob when called without a repository
39
+ # end
40
+ # private_class_method :assert_valid_scope!
41
+ #
42
+ # @api private
43
+ #
44
+ module Configuring # rubocop:disable Metrics/ModuleLength
45
+ # @!group Read Operations
46
+
47
+ # @api private
48
+ CONFIG_GET_ALLOWED_OPTS = %i[global system local worktree file f blob includes no_includes type default].freeze
49
+ private_constant :CONFIG_GET_ALLOWED_OPTS
50
+
51
+ # Retrieve a single config entry by key name
52
+ #
53
+ # Wraps `git config --get --show-scope --show-origin --null`.
54
+ #
55
+ # @example Get a single config entry
56
+ # entry = repo.config_get('user.name')
57
+ # entry&.value # => "Alice"
58
+ #
59
+ # @param name [String] the full dotted config key (e.g. `"user.name"`)
60
+ #
61
+ # @param value_regex [String, nil] optional regex to filter by value
62
+ #
63
+ # @param options [Hash] scope and filter options forwarded to the command
64
+ #
65
+ # @option options [Boolean, nil] :global (nil) read from `~/.gitconfig`
66
+ #
67
+ # @option options [Boolean, nil] :system (nil) read from the system config file
68
+ #
69
+ # @option options [Boolean, nil] :local (nil) read from `.git/config`
70
+ #
71
+ # @option options [Boolean, nil] :worktree (nil) read from the worktree config
72
+ #
73
+ # @option options [String, nil] :file (nil) path to a custom config file (alias: `:f`)
74
+ #
75
+ # @option options [String, nil] :blob (nil) read from a git blob object
76
+ #
77
+ # @option options [Boolean, nil] :includes (nil) follow include directives
78
+ #
79
+ # @option options [Boolean, nil] :no_includes (nil) suppress include directives
80
+ #
81
+ # @option options [String, nil] :type (nil) enforce a type constraint on the value
82
+ #
83
+ # @option options [String, nil] :default (nil) value to return when the key is missing
84
+ #
85
+ # @return [Git::ConfigEntryInfo, nil] the matching entry, or `nil` when not found
86
+ #
87
+ # @raise [ArgumentError] if unsupported options are provided
88
+ #
89
+ # @raise [Git::FailedError] if git exits with an unexpected non-zero status
90
+ #
91
+ # @note On Git for Windows, git's default regex engine matches *bytes* rather than
92
+ # characters, so a metacharacter such as `.` or a POSIX class such as
93
+ # `[[:alpha:]]` in `value_regex` never matches a whole multi-byte character. The
94
+ # failure is silent: nothing raises, and the outcome is indistinguishable from a
95
+ # `value_regex` that genuinely matches nothing. `git config` value patterns are
96
+ # POSIX extended regular expressions with no PCRE mode, so unlike
97
+ # {Git::Repository#grep} there is no alternate regex engine to select here.
98
+ #
99
+ def config_get(name, value_regex = nil, **options)
100
+ Private.assert_valid_opts!(CONFIG_GET_ALLOWED_OPTS, **options)
101
+ assert_valid_scope!(**options)
102
+ options = options.merge(show_scope: true, show_origin: true, null: true)
103
+ cmd = Git::Commands::ConfigOptionSyntax::Get.new(execution_context)
104
+ output = cmd.call(name, value_regex, **options).stdout
105
+ Git::Parsers::ConfigEntry.parse_get(name, output)
106
+ end
107
+
108
+ # @api private
109
+ CONFIG_GET_ALL_ALLOWED_OPTS = %i[global system local worktree file f blob includes no_includes type].freeze
110
+ private_constant :CONFIG_GET_ALL_ALLOWED_OPTS
111
+
112
+ # Retrieve all values for a multi-valued config key
113
+ #
114
+ # Wraps `git config --get-all --show-scope --show-origin --null`.
115
+ #
116
+ # @example Get all values for a multi-valued key
117
+ # entries = repo.config_get_all('remote.origin.url')
118
+ # entries.map(&:value) # => ["https://...", "git@..."]
119
+ #
120
+ # @param name [String] the full dotted config key
121
+ #
122
+ # @param value_regex [String, nil] optional regex to filter by value
123
+ #
124
+ # @param options [Hash] scope and filter options
125
+ #
126
+ # @option options [Boolean, nil] :global (nil) read from `~/.gitconfig`
127
+ #
128
+ # @option options [Boolean, nil] :system (nil) read from the system config file
129
+ #
130
+ # @option options [Boolean, nil] :local (nil) read from `.git/config`
131
+ #
132
+ # @option options [Boolean, nil] :worktree (nil) read from the worktree config
133
+ #
134
+ # @option options [String, nil] :file (nil) path to a custom config file (alias: `:f`)
135
+ #
136
+ # @option options [String, nil] :blob (nil) read from a git blob object
137
+ #
138
+ # @option options [Boolean, nil] :includes (nil) follow include directives
139
+ #
140
+ # @option options [Boolean, nil] :no_includes (nil) suppress include directives
141
+ #
142
+ # @option options [String, nil] :type (nil) enforce a type constraint on the value
143
+ #
144
+ # @return [Array<Git::ConfigEntryInfo>] all entries matching the key
145
+ #
146
+ # @raise [ArgumentError] if unsupported options are provided
147
+ #
148
+ # @raise [Git::FailedError] if git exits with an unexpected non-zero status
149
+ #
150
+ # @note On Git for Windows, git's default regex engine matches *bytes* rather than
151
+ # characters, so a metacharacter such as `.` or a POSIX class such as
152
+ # `[[:alpha:]]` in `value_regex` never matches a whole multi-byte character. The
153
+ # failure is silent: nothing raises, and the outcome is indistinguishable from a
154
+ # `value_regex` that genuinely matches nothing. `git config` value patterns are
155
+ # POSIX extended regular expressions with no PCRE mode, so unlike
156
+ # {Git::Repository#grep} there is no alternate regex engine to select here.
157
+ #
158
+ def config_get_all(name, value_regex = nil, **options)
159
+ Private.assert_valid_opts!(CONFIG_GET_ALL_ALLOWED_OPTS, **options)
160
+ assert_valid_scope!(**options)
161
+ options = options.merge(show_scope: true, show_origin: true, null: true)
162
+ cmd = Git::Commands::ConfigOptionSyntax::GetAll.new(execution_context)
163
+ output = cmd.call(name, value_regex, **options).stdout
164
+ Git::Parsers::ConfigEntry.parse_get_all(name, output)
165
+ end
166
+
167
+ # @api private
168
+ CONFIG_GET_COLORBOOL_ALLOWED_OPTS = %i[global system local worktree file f blob includes no_includes].freeze
169
+ private_constant :CONFIG_GET_COLORBOOL_ALLOWED_OPTS
170
+
171
+ # @overload config_get_colorbool(name, stdout_is_tty = nil, **options)
172
+ #
173
+ # Query whether color output is enabled for a given config slot
174
+ #
175
+ # Wraps `git config --get-colorbool`.
176
+ #
177
+ # @example Check color status for color.ui
178
+ # repo.config_get_colorbool('color.ui') # => "true"
179
+ #
180
+ # @param name [String] the config key to check (e.g. `"color.ui"`)
181
+ #
182
+ # @param stdout_is_tty [Boolean, nil] whether stdout is a TTY
183
+ #
184
+ # @param options [Hash] scope and filter options
185
+ #
186
+ # @option options [Boolean, nil] :global (nil) read from `~/.gitconfig`
187
+ #
188
+ # @option options [Boolean, nil] :system (nil) read from the system config file
189
+ #
190
+ # @option options [Boolean, nil] :local (nil) read from `.git/config`
191
+ #
192
+ # @option options [Boolean, nil] :worktree (nil) read from the worktree config
193
+ #
194
+ # @option options [String, nil] :file (nil) path to a custom config file (alias: `:f`)
195
+ #
196
+ # @option options [String, nil] :blob (nil) read from a git blob object
197
+ #
198
+ # @option options [Boolean, nil] :includes (nil) follow include directives (`--includes`)
199
+ #
200
+ # @option options [Boolean, nil] :no_includes (nil) suppress include directives (`--no-includes`)
201
+ #
202
+ # @return [String] `"true"` or `"false"`
203
+ #
204
+ # @raise [ArgumentError] if unsupported options are provided
205
+ #
206
+ # @raise [Git::FailedError] if git exits with an unexpected non-zero status
207
+ #
208
+ def config_get_colorbool(name, stdout_is_tty = nil, **)
209
+ Private.assert_valid_opts!(CONFIG_GET_COLORBOOL_ALLOWED_OPTS, **)
210
+ assert_valid_scope!(**)
211
+ cmd = Git::Commands::ConfigOptionSyntax::GetColorBool.new(execution_context)
212
+ cmd.call(name, stdout_is_tty, **).stdout.chomp
213
+ end
214
+
215
+ # @api private
216
+ CONFIG_GET_REGEXP_ALLOWED_OPTS = %i[global system local worktree file f blob includes no_includes type].freeze
217
+ private_constant :CONFIG_GET_REGEXP_ALLOWED_OPTS
218
+
219
+ # Retrieve all config entries whose key matches a regular expression
220
+ #
221
+ # Wraps `git config --get-regexp --show-scope --show-origin --null`.
222
+ #
223
+ # @example Get all remote-related config entries
224
+ # entries = repo.config_get_regexp('remote\\.')
225
+ # entries.map(&:key) # => ["remote.origin.url", ...]
226
+ #
227
+ # @param name_regex [String] regex matched against config key names
228
+ #
229
+ # @param value_regex [String, nil] optional regex to filter by value
230
+ #
231
+ # @param options [Hash] scope and filter options
232
+ #
233
+ # @option options [Boolean, nil] :global (nil) read from `~/.gitconfig`
234
+ #
235
+ # @option options [Boolean, nil] :system (nil) read from the system config file
236
+ #
237
+ # @option options [Boolean, nil] :local (nil) read from `.git/config`
238
+ #
239
+ # @option options [Boolean, nil] :worktree (nil) read from the worktree config
240
+ #
241
+ # @option options [String, nil] :file (nil) path to a custom config file (alias: `:f`)
242
+ #
243
+ # @option options [String, nil] :blob (nil) read from a git blob object
244
+ #
245
+ # @option options [Boolean, nil] :includes (nil) follow include directives
246
+ #
247
+ # @option options [Boolean, nil] :no_includes (nil) suppress include directives
248
+ #
249
+ # @option options [String, nil] :type (nil) enforce a type constraint on the value
250
+ #
251
+ # @return [Array<Git::ConfigEntryInfo>] all entries whose key matches the regex
252
+ #
253
+ # @raise [ArgumentError] if unsupported options are provided
254
+ #
255
+ # @raise [Git::FailedError] if git exits with an unexpected non-zero status
256
+ #
257
+ # @note On Git for Windows, git's default regex engine matches *bytes* rather than
258
+ # characters, so a metacharacter such as `.` or a POSIX class such as
259
+ # `[[:alpha:]]` in `value_regex` never matches a whole multi-byte character. The
260
+ # failure is silent: nothing raises, and the outcome is indistinguishable from a
261
+ # `value_regex` that genuinely matches nothing. `git config` value patterns are
262
+ # POSIX extended regular expressions with no PCRE mode, so unlike
263
+ # {Git::Repository#grep} there is no alternate regex engine to select here.
264
+ #
265
+ def config_get_regexp(name_regex, value_regex = nil, **options)
266
+ Private.assert_valid_opts!(CONFIG_GET_REGEXP_ALLOWED_OPTS, **options)
267
+ assert_valid_scope!(**options)
268
+ options = options.merge(show_scope: true, show_origin: true, null: true)
269
+ cmd = Git::Commands::ConfigOptionSyntax::GetRegexp.new(execution_context)
270
+ output = cmd.call(name_regex, value_regex, **options).stdout
271
+ Git::Parsers::ConfigEntry.parse_list(output)
272
+ end
273
+
274
+ # @api private
275
+ CONFIG_GET_URLMATCH_ALLOWED_OPTS = %i[global system local worktree file f blob includes no_includes type].freeze
276
+ private_constant :CONFIG_GET_URLMATCH_ALLOWED_OPTS
277
+
278
+ # Retrieve config entries whose URL pattern matches a given URL
279
+ #
280
+ # Wraps `git config --get-urlmatch --show-scope --null`.
281
+ #
282
+ # @example Get config entries for a specific URL
283
+ # entries = repo.config_get_urlmatch('http', 'https://github.com/user/repo')
284
+ # entries.map(&:key)
285
+ #
286
+ # @param name [String] the config section or key prefix to look up
287
+ #
288
+ # @param url [String] the URL to match against
289
+ #
290
+ # @param options [Hash] scope and filter options
291
+ #
292
+ # @option options [Boolean, nil] :global (nil) read from `~/.gitconfig`
293
+ #
294
+ # @option options [Boolean, nil] :system (nil) read from the system config file
295
+ #
296
+ # @option options [Boolean, nil] :local (nil) read from `.git/config`
297
+ #
298
+ # @option options [Boolean, nil] :worktree (nil) read from the worktree config
299
+ #
300
+ # @option options [String, nil] :file (nil) path to a custom config file (alias: `:f`)
301
+ #
302
+ # @option options [String, nil] :blob (nil) read from a git blob object
303
+ #
304
+ # @option options [Boolean, nil] :includes (nil) follow include directives
305
+ #
306
+ # @option options [Boolean, nil] :no_includes (nil) suppress include directives
307
+ #
308
+ # @option options [String, nil] :type (nil) enforce a type constraint on the value
309
+ #
310
+ # @return [Array<Git::ConfigEntryInfo>] all entries matching the URL; `origin` is `nil` on each
311
+ #
312
+ # @raise [ArgumentError] if unsupported options are provided
313
+ #
314
+ # @raise [Git::FailedError] if git exits with an unexpected non-zero status
315
+ #
316
+ # @note `--show-origin` is not supported by git for `--get-urlmatch`, so the
317
+ # {Git::ConfigEntryInfo} entries returned by this method always have
318
+ # `origin: nil`.
319
+ #
320
+ def config_get_urlmatch(name, url, **options)
321
+ Private.assert_valid_opts!(CONFIG_GET_URLMATCH_ALLOWED_OPTS, **options)
322
+ assert_valid_scope!(**options)
323
+ options = options.merge(show_scope: true, null: true)
324
+ cmd = Git::Commands::ConfigOptionSyntax::GetUrlmatch.new(execution_context)
325
+ output = cmd.call(name, url, **options).stdout
326
+ Git::Parsers::ConfigEntry.parse_urlmatch(output)
327
+ end
328
+
329
+ # @api private
330
+ CONFIG_LIST_ALLOWED_OPTS = %i[global system local worktree file f blob includes no_includes type].freeze
331
+ private_constant :CONFIG_LIST_ALLOWED_OPTS
332
+
333
+ # List all visible config entries
334
+ #
335
+ # Wraps `git config --list --show-scope --show-origin --null`.
336
+ #
337
+ # @example List all config entries
338
+ # entries = repo.config_list
339
+ # entries.first.scope # => "local"
340
+ #
341
+ # @param options [Hash] scope and filter options
342
+ #
343
+ # @option options [Boolean, nil] :global (nil) read from `~/.gitconfig`
344
+ #
345
+ # @option options [Boolean, nil] :system (nil) read from the system config file
346
+ #
347
+ # @option options [Boolean, nil] :local (nil) read from `.git/config`
348
+ #
349
+ # @option options [Boolean, nil] :worktree (nil) read from the worktree config
350
+ #
351
+ # @option options [String, nil] :file (nil) path to a custom config file (alias: `:f`)
352
+ #
353
+ # @option options [String, nil] :blob (nil) read from a git blob object
354
+ #
355
+ # @option options [Boolean, nil] :includes (nil) follow include directives
356
+ #
357
+ # @option options [Boolean, nil] :no_includes (nil) suppress include directives
358
+ #
359
+ # @option options [String, nil] :type (nil) enforce a type constraint on the value
360
+ #
361
+ # @return [Array<Git::ConfigEntryInfo>] all visible config entries
362
+ #
363
+ # @raise [ArgumentError] if unsupported options are provided
364
+ #
365
+ # @raise [Git::FailedError] if git exits with an unexpected non-zero status
366
+ #
367
+ def config_list(**options)
368
+ Private.assert_valid_opts!(CONFIG_LIST_ALLOWED_OPTS, **options)
369
+ assert_valid_scope!(**options)
370
+ options = options.merge(show_scope: true, show_origin: true, null: true)
371
+ cmd = Git::Commands::ConfigOptionSyntax::List.new(execution_context)
372
+ output = cmd.call(**options).stdout
373
+ Git::Parsers::ConfigEntry.parse_list(output)
374
+ end
375
+
376
+ # @!endgroup
377
+
378
+ # @!group Write Operations
379
+
380
+ # @api private
381
+ CONFIG_ADD_ALLOWED_OPTS = %i[global system local worktree file f blob type].freeze
382
+ private_constant :CONFIG_ADD_ALLOWED_OPTS
383
+
384
+ # @overload config_add(name, value, **options)
385
+ #
386
+ # Append a value to a multi-valued config key
387
+ #
388
+ # Wraps `git config --add`.
389
+ #
390
+ # @example Append a URL to a multi-valued remote key
391
+ # repo.config_add('remote.origin.url', 'git@github.com:user/repo.git')
392
+ #
393
+ # @param name [String] the full dotted config key
394
+ #
395
+ # @param value [String] the value to append
396
+ #
397
+ # @param options [Hash] scope options
398
+ #
399
+ # @option options [Boolean, nil] :global (nil) write to `~/.gitconfig`
400
+ #
401
+ # @option options [Boolean, nil] :system (nil) write to the system config file
402
+ #
403
+ # @option options [Boolean, nil] :local (nil) write to `.git/config`
404
+ #
405
+ # @option options [Boolean, nil] :worktree (nil) write to the worktree config
406
+ #
407
+ # @option options [String, nil] :file (nil) path to a custom config file (alias: `:f`)
408
+ #
409
+ # @option options [String, nil] :blob (nil) write to a git blob object
410
+ #
411
+ # @option options [String, nil] :type (nil) coerce the value to the given type (e.g. `"bool"`, `"int"`)
412
+ #
413
+ # @return [nil]
414
+ #
415
+ # @raise [ArgumentError] if unsupported options are provided
416
+ #
417
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
418
+ #
419
+ def config_add(name, value, **)
420
+ Private.assert_valid_opts!(CONFIG_ADD_ALLOWED_OPTS, **)
421
+ assert_valid_scope!(**)
422
+ cmd = Git::Commands::ConfigOptionSyntax::Add.new(execution_context)
423
+ cmd.call(name, value, **)
424
+ nil
425
+ end
426
+
427
+ # @api private
428
+ CONFIG_REMOVE_SECTION_ALLOWED_OPTS = %i[global system local worktree file f blob].freeze
429
+ private_constant :CONFIG_REMOVE_SECTION_ALLOWED_OPTS
430
+
431
+ # @overload config_remove_section(name, **options)
432
+ #
433
+ # Remove an entire config section
434
+ #
435
+ # Wraps `git config --remove-section`.
436
+ #
437
+ # @example Remove the origin remote section
438
+ # repo.config_remove_section('remote.origin')
439
+ #
440
+ # @param name [String] the section name to remove (e.g. `"remote.origin"`)
441
+ #
442
+ # @param options [Hash] scope options
443
+ #
444
+ # @option options [Boolean, nil] :global (nil) remove from `~/.gitconfig`
445
+ #
446
+ # @option options [Boolean, nil] :system (nil) remove from the system config file
447
+ #
448
+ # @option options [Boolean, nil] :local (nil) remove from `.git/config`
449
+ #
450
+ # @option options [Boolean, nil] :worktree (nil) remove from the worktree config
451
+ #
452
+ # @option options [String, nil] :file (nil) path to a custom config file (alias: `:f`)
453
+ #
454
+ # @option options [String, nil] :blob (nil) remove from a git blob object
455
+ #
456
+ # @return [nil]
457
+ #
458
+ # @raise [ArgumentError] if unsupported options are provided
459
+ #
460
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
461
+ #
462
+ def config_remove_section(name, **)
463
+ Private.assert_valid_opts!(CONFIG_REMOVE_SECTION_ALLOWED_OPTS, **)
464
+ assert_valid_scope!(**)
465
+ cmd = Git::Commands::ConfigOptionSyntax::RemoveSection.new(execution_context)
466
+ cmd.call(name, **)
467
+ nil
468
+ end
469
+
470
+ # @api private
471
+ CONFIG_RENAME_SECTION_ALLOWED_OPTS = %i[global system local worktree file f blob].freeze
472
+ private_constant :CONFIG_RENAME_SECTION_ALLOWED_OPTS
473
+
474
+ # @overload config_rename_section(old_name, new_name, **options)
475
+ #
476
+ # Rename a config section
477
+ #
478
+ # Wraps `git config --rename-section`.
479
+ #
480
+ # @example Rename a remote section
481
+ # repo.config_rename_section('remote.old', 'remote.new')
482
+ #
483
+ # @param old_name [String] the current section name
484
+ #
485
+ # @param new_name [String] the new section name
486
+ #
487
+ # @param options [Hash] scope options
488
+ #
489
+ # @option options [Boolean, nil] :global (nil) rename in `~/.gitconfig`
490
+ #
491
+ # @option options [Boolean, nil] :system (nil) rename in the system config file
492
+ #
493
+ # @option options [Boolean, nil] :local (nil) rename in `.git/config`
494
+ #
495
+ # @option options [Boolean, nil] :worktree (nil) rename in the worktree config
496
+ #
497
+ # @option options [String, nil] :file (nil) path to a custom config file (alias: `:f`)
498
+ #
499
+ # @option options [String, nil] :blob (nil) rename in a git blob object
500
+ #
501
+ # @return [nil]
502
+ #
503
+ # @raise [ArgumentError] if unsupported options are provided
504
+ #
505
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
506
+ #
507
+ def config_rename_section(old_name, new_name, **)
508
+ Private.assert_valid_opts!(CONFIG_RENAME_SECTION_ALLOWED_OPTS, **)
509
+ assert_valid_scope!(**)
510
+ cmd = Git::Commands::ConfigOptionSyntax::RenameSection.new(execution_context)
511
+ cmd.call(old_name, new_name, **)
512
+ nil
513
+ end
514
+
515
+ # @api private
516
+ CONFIG_REPLACE_ALL_ALLOWED_OPTS = %i[global system local worktree file f blob type].freeze
517
+ private_constant :CONFIG_REPLACE_ALL_ALLOWED_OPTS
518
+
519
+ # @overload config_replace_all(name, value, value_regex = nil, **options)
520
+ #
521
+ # Replace all values matching a key and optional value regex
522
+ #
523
+ # Wraps `git config --replace-all`.
524
+ #
525
+ # @example Replace all values for a key
526
+ # repo.config_replace_all('remote.origin.url', 'https://github.com/user/repo')
527
+ #
528
+ # @param name [String] the full dotted config key
529
+ #
530
+ # @param value [String] the new value
531
+ #
532
+ # @param value_regex [String, nil] optional regex; only matching values are replaced
533
+ #
534
+ # @param options [Hash] scope options
535
+ #
536
+ # @option options [Boolean, nil] :global (nil) write to `~/.gitconfig`
537
+ #
538
+ # @option options [Boolean, nil] :system (nil) write to the system config file
539
+ #
540
+ # @option options [Boolean, nil] :local (nil) write to `.git/config`
541
+ #
542
+ # @option options [Boolean, nil] :worktree (nil) write to the worktree config
543
+ #
544
+ # @option options [String, nil] :file (nil) path to a custom config file (alias: `:f`)
545
+ #
546
+ # @option options [String, nil] :blob (nil) write to a git blob object
547
+ #
548
+ # @option options [String, nil] :type (nil) coerce the value to the given type (e.g. `"bool"`, `"int"`)
549
+ #
550
+ # @return [nil]
551
+ #
552
+ # @raise [ArgumentError] if unsupported options are provided
553
+ #
554
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
555
+ #
556
+ # @note On Git for Windows, git's default regex engine matches *bytes* rather than
557
+ # characters, so a metacharacter such as `.` or a POSIX class such as
558
+ # `[[:alpha:]]` in `value_regex` never matches a whole multi-byte character. The
559
+ # failure is silent: nothing raises, and the outcome is indistinguishable from a
560
+ # `value_regex` that genuinely matches nothing. `git config` value patterns are
561
+ # POSIX extended regular expressions with no PCRE mode, so unlike
562
+ # {Git::Repository#grep} there is no alternate regex engine to select here.
563
+ #
564
+ # @note A `value_regex` that selects nothing does not make this method a no-op.
565
+ # `git config --replace-all` *adds* `value` as an additional entry when no
566
+ # existing value matches, and exits zero. On Git for Windows, a `value_regex`
567
+ # whose metacharacters span non-ASCII text therefore leaves the value it was
568
+ # meant to replace in place and silently creates a duplicate entry beside it.
569
+ # Confirm the result with {#config_get_all}, or match on ASCII text, when the
570
+ # key must end up single-valued.
571
+ #
572
+ def config_replace_all(name, value, value_regex = nil, **)
573
+ Private.assert_valid_opts!(CONFIG_REPLACE_ALL_ALLOWED_OPTS, **)
574
+ assert_valid_scope!(**)
575
+ cmd = Git::Commands::ConfigOptionSyntax::ReplaceAll.new(execution_context)
576
+ cmd.call(name, value, value_regex, **)
577
+ nil
578
+ end
579
+
580
+ # @api private
581
+ CONFIG_SET_ALLOWED_OPTS = %i[global system local worktree file f blob type].freeze
582
+ private_constant :CONFIG_SET_ALLOWED_OPTS
583
+
584
+ # @overload config_set(name, value, **options)
585
+ #
586
+ # Set a config entry to a new value
587
+ #
588
+ # Wraps the implicit set mode of `git config`.
589
+ #
590
+ # @example Set the user name in local config
591
+ # repo.config_set('user.name', 'Alice')
592
+ #
593
+ # @param name [String] the full dotted config key
594
+ #
595
+ # @param value [String] the value to set
596
+ #
597
+ # @param options [Hash] scope options
598
+ #
599
+ # @option options [Boolean, nil] :global (nil) write to `~/.gitconfig`
600
+ #
601
+ # @option options [Boolean, nil] :system (nil) write to the system config file
602
+ #
603
+ # @option options [Boolean, nil] :local (nil) write to `.git/config`
604
+ #
605
+ # @option options [Boolean, nil] :worktree (nil) write to the worktree config
606
+ #
607
+ # @option options [String, nil] :file (nil) path to a custom config file (alias: `:f`)
608
+ #
609
+ # @option options [String, nil] :blob (nil) write to a git blob object
610
+ #
611
+ # @option options [String, nil] :type (nil) coerce the value to the given type (e.g. `"bool"`, `"int"`)
612
+ #
613
+ # @return [nil]
614
+ #
615
+ # @raise [ArgumentError] if unsupported options are provided
616
+ #
617
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
618
+ #
619
+ def config_set(name, value, **)
620
+ Private.assert_valid_opts!(CONFIG_SET_ALLOWED_OPTS, **)
621
+ assert_valid_scope!(**)
622
+ cmd = Git::Commands::ConfigOptionSyntax::Set.new(execution_context)
623
+ cmd.call(name, value, **)
624
+ nil
625
+ end
626
+
627
+ # @api private
628
+ CONFIG_UNSET_ALLOWED_OPTS = %i[global system local worktree file f blob].freeze
629
+ private_constant :CONFIG_UNSET_ALLOWED_OPTS
630
+
631
+ # @overload config_unset(name, value_regex = nil, **options)
632
+ #
633
+ # Remove a config entry
634
+ #
635
+ # Wraps `git config --unset`.
636
+ #
637
+ # @example Remove a config entry
638
+ # repo.config_unset('user.name')
639
+ #
640
+ # @param name [String] the full dotted config key
641
+ #
642
+ # @param value_regex [String, nil] optional regex; only the matching value is removed
643
+ #
644
+ # @param options [Hash] scope options
645
+ #
646
+ # @option options [Boolean, nil] :global (nil) remove from `~/.gitconfig`
647
+ #
648
+ # @option options [Boolean, nil] :system (nil) remove from the system config file
649
+ #
650
+ # @option options [Boolean, nil] :local (nil) remove from `.git/config`
651
+ #
652
+ # @option options [Boolean, nil] :worktree (nil) remove from the worktree config
653
+ #
654
+ # @option options [String, nil] :file (nil) path to a custom config file (alias: `:f`)
655
+ #
656
+ # @option options [String, nil] :blob (nil) remove from a git blob object
657
+ #
658
+ # @return [nil]
659
+ #
660
+ # @raise [ArgumentError] if unsupported options are provided
661
+ #
662
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
663
+ #
664
+ # @note On Git for Windows, git's default regex engine matches *bytes* rather than
665
+ # characters, so a metacharacter such as `.` or a POSIX class such as
666
+ # `[[:alpha:]]` in `value_regex` never matches a whole multi-byte character. The
667
+ # failure is silent: nothing raises, and the outcome is indistinguishable from a
668
+ # `value_regex` that genuinely matches nothing. `git config` value patterns are
669
+ # POSIX extended regular expressions with no PCRE mode, so unlike
670
+ # {Git::Repository#grep} there is no alternate regex engine to select here.
671
+ #
672
+ def config_unset(name, value_regex = nil, **)
673
+ Private.assert_valid_opts!(CONFIG_UNSET_ALLOWED_OPTS, **)
674
+ assert_valid_scope!(**)
675
+ cmd = Git::Commands::ConfigOptionSyntax::Unset.new(execution_context)
676
+ cmd.call(name, value_regex, **)
677
+ nil
678
+ end
679
+
680
+ # @api private
681
+ CONFIG_UNSET_ALL_ALLOWED_OPTS = %i[global system local worktree file f blob].freeze
682
+ private_constant :CONFIG_UNSET_ALL_ALLOWED_OPTS
683
+
684
+ # @overload config_unset_all(name, value_regex = nil, **options)
685
+ #
686
+ # Remove all config entries for a key
687
+ #
688
+ # Wraps `git config --unset-all`.
689
+ #
690
+ # @example Remove all values for a multi-valued key
691
+ # repo.config_unset_all('remote.origin.url')
692
+ #
693
+ # @param name [String] the full dotted config key
694
+ #
695
+ # @param value_regex [String, nil] optional regex; only matching values are removed
696
+ #
697
+ # @param options [Hash] scope options
698
+ #
699
+ # @option options [Boolean, nil] :global (nil) remove from `~/.gitconfig`
700
+ #
701
+ # @option options [Boolean, nil] :system (nil) remove from the system config file
702
+ #
703
+ # @option options [Boolean, nil] :local (nil) remove from `.git/config`
704
+ #
705
+ # @option options [Boolean, nil] :worktree (nil) remove from the worktree config
706
+ #
707
+ # @option options [String, nil] :file (nil) path to a custom config file (alias: `:f`)
708
+ #
709
+ # @option options [String, nil] :blob (nil) remove from a git blob object
710
+ #
711
+ # @return [nil]
712
+ #
713
+ # @raise [ArgumentError] if unsupported options are provided
714
+ #
715
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
716
+ #
717
+ # @note On Git for Windows, git's default regex engine matches *bytes* rather than
718
+ # characters, so a metacharacter such as `.` or a POSIX class such as
719
+ # `[[:alpha:]]` in `value_regex` never matches a whole multi-byte character. The
720
+ # failure is silent: nothing raises, and the outcome is indistinguishable from a
721
+ # `value_regex` that genuinely matches nothing. `git config` value patterns are
722
+ # POSIX extended regular expressions with no PCRE mode, so unlike
723
+ # {Git::Repository#grep} there is no alternate regex engine to select here.
724
+ #
725
+ def config_unset_all(name, value_regex = nil, **)
726
+ Private.assert_valid_opts!(CONFIG_UNSET_ALL_ALLOWED_OPTS, **)
727
+ assert_valid_scope!(**)
728
+ cmd = Git::Commands::ConfigOptionSyntax::UnsetAll.new(execution_context)
729
+ cmd.call(name, value_regex, **)
730
+ nil
731
+ end
732
+
733
+ # @!endgroup
734
+
735
+ private
736
+
737
+ # @abstract
738
+ #
739
+ # Returns the execution context used to run git commands
740
+ #
741
+ # @return [Git::ExecutionContext]
742
+ #
743
+ def execution_context
744
+ raise NotImplementedError
745
+ end
746
+
747
+ # @abstract
748
+ #
749
+ # @overload assert_valid_scope!(**options)
750
+ #
751
+ # Validates that the requested scope options are appropriate for this context
752
+ #
753
+ # Called before every config operation. Raise `ArgumentError` when a scope
754
+ # (e.g. `:local`) is not permitted without a repository.
755
+ #
756
+ # @param options [Hash] scope options forwarded from the calling config method
757
+ #
758
+ # @raise [ArgumentError] if the scope is not permitted in this context
759
+ #
760
+ # @return [void]
761
+ #
762
+ def assert_valid_scope!(**)
763
+ raise NotImplementedError
764
+ end
765
+
766
+ # Internal helpers for {Git::Configuring}.
767
+ #
768
+ # @api private
769
+ #
770
+ module Private
771
+ module_function
772
+
773
+ # Validate that candidate option keys are listed in `allowed`
774
+ #
775
+ # @param allowed [Array<Symbol>] the permitted option keys
776
+ #
777
+ # @param candidate_keywords [Hash<Symbol, Object>] the keywords to validate
778
+ #
779
+ # @option candidate_keywords [Object] key a candidate keyword value
780
+ #
781
+ # @return [void]
782
+ #
783
+ # @raise [ArgumentError] when any candidate key is not in `allowed`
784
+ #
785
+ def assert_valid_opts!(allowed, **candidate_keywords)
786
+ unknown = candidate_keywords.keys - allowed
787
+ return if unknown.empty?
788
+
789
+ raise ArgumentError, "Unknown options: #{unknown.join(', ')}"
790
+ end
791
+ end
792
+
793
+ private_constant :Private
794
+ end
795
+ end