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
@@ -1,6 +1,488 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'find'
4
+ require 'pathname'
5
+
6
+ require 'git/configuring'
7
+ require 'git/execution_context/repository'
8
+ require 'git/repository/branching'
9
+ require 'git/repository/context_helpers'
10
+ require 'git/repository/committing'
11
+ require 'git/repository/diffing'
12
+ require 'git/repository/inspecting'
13
+ require 'git/repository/logging'
14
+ require 'git/repository/maintenance'
15
+ require 'git/repository/merging'
16
+ require 'git/repository/object_operations'
17
+ require 'git/repository/remote_operations'
18
+ require 'git/repository/shared_private'
19
+ require 'git/repository/staging'
20
+ require 'git/repository/stashing'
21
+ require 'git/repository/status_operations'
22
+ require 'git/repository/worktree_operations'
23
+
1
24
  module Git
25
+ # The main public interface for interacting with a Git repository
26
+ #
27
+ # `Git::Repository` is the **orchestration layer** for all git operations. It acts
28
+ # as the glue between the user-facing API and the underlying components, but
29
+ # contains minimal domain logic itself. For each operation it:
30
+ #
31
+ # 1. **Pre-processes arguments** — transforms user-provided values into forms
32
+ # suitable for the command layer (e.g. path expansion, option normalization,
33
+ # Ruby-idiomatic defaults, deprecation handling, input validation).
34
+ # 2. **Calls commands** — invokes one or more `Git::Commands::*` classes via the
35
+ # injected `Git::ExecutionContext::Repository`.
36
+ # 3. **Builds rich return values** — passes raw command output through
37
+ # `Git::Parsers::*` classes and result-class factory methods to assemble the
38
+ # meaningful Ruby objects the caller expects.
39
+ #
40
+ # Some operations are genuinely one-line delegators when no pre/post-processing is
41
+ # needed (e.g. `add`, `reset`), but many are short orchestration sequences that
42
+ # coordinate argument preparation, one or more command calls, and result assembly.
43
+ #
44
+ # Facade methods are organized into focused modules under `lib/git/repository/`
45
+ # (e.g. {Git::Repository::Staging}) and included into this class.
46
+ #
47
+ # @api public
48
+ #
49
+ class Repository # rubocop:disable Metrics/ClassLength
50
+ include Git::Configuring
51
+ include Git::Repository::Branching
52
+ include Git::Repository::ContextHelpers
53
+ include Git::Repository::Committing
54
+ include Git::Repository::Diffing
55
+ include Git::Repository::Inspecting
56
+ include Git::Repository::Logging
57
+ include Git::Repository::Maintenance
58
+ include Git::Repository::Merging
59
+ include Git::Repository::ObjectOperations
60
+ include Git::Repository::RemoteOperations
61
+ include Git::Repository::Staging
62
+ include Git::Repository::Stashing
63
+ include Git::Repository::StatusOperations
64
+ include Git::Repository::WorktreeOperations
2
65
 
3
- class Repository < Path
4
- end
66
+ # Allowed keyword options for deprecated config write operations
67
+ #
68
+ # @return [Array<Symbol>] accepted option keys
69
+ #
70
+ CONFIG_SET_ALLOWED_OPTS = %i[file].freeze
71
+ private_constant :CONFIG_SET_ALLOWED_OPTS
72
+
73
+ # Allowed keyword options for deprecated config read operations
74
+ #
75
+ # @return [Array<Symbol>] accepted option keys
76
+ #
77
+ CONFIG_READ_ALLOWED_OPTS = %i[file].freeze
78
+ private_constant :CONFIG_READ_ALLOWED_OPTS
79
+
80
+ # Deprecation warning emitted by {#config}
81
+ #
82
+ # @return [String] the warning message shown to callers
83
+ #
84
+ CONFIG_DEPRECATION_WARNING =
85
+ 'Git::Repository#config is deprecated and will be removed in v6.0.0. ' \
86
+ 'Use config_get(name), config_set(name, value), or config_list instead.'
87
+ private_constant :CONFIG_DEPRECATION_WARNING
88
+
89
+ # Deprecation warning emitted by {#global_config}
90
+ #
91
+ # @return [String] the warning message shown to callers
92
+ #
93
+ GLOBAL_CONFIG_DEPRECATION_WARNING =
94
+ 'Git::Repository#global_config is deprecated and will be removed in v6.0.0. ' \
95
+ 'Use config_get(name, global: true), config_set(name, value, global: true), ' \
96
+ 'or config_list(global: true) instead.'
97
+ private_constant :GLOBAL_CONFIG_DEPRECATION_WARNING
98
+
99
+ # @return [Git::ExecutionContext::Repository] the execution context used to run
100
+ # git commands for this repository
101
+ #
102
+ # @api private
103
+ attr_reader :execution_context
104
+
105
+ # @param execution_context [Git::ExecutionContext::Repository] the context used
106
+ # to run git commands for this repository; must not be nil
107
+ #
108
+ # @raise [ArgumentError] if `execution_context` is nil
109
+ #
110
+ def initialize(execution_context:)
111
+ raise ArgumentError, 'execution_context must not be nil' if execution_context.nil?
112
+
113
+ @execution_context = execution_context
114
+ end
115
+
116
+ # Returns the root of the working tree, or `nil` for a bare repository
117
+ #
118
+ # @example Get the working directory path
119
+ # repository.dir #=> #<Pathname:/path/to/repo>
120
+ #
121
+ # @return [Pathname, nil] the working directory path, or `nil` when bare
122
+ #
123
+ def dir
124
+ working_dir = execution_context.git_work_dir
125
+ working_dir && Pathname.new(working_dir)
126
+ end
127
+
128
+ # Returns the repository (`.git`) directory
129
+ #
130
+ # @example Get the repository directory path
131
+ # repository.repo #=> #<Pathname:/path/to/repo/.git>
132
+ #
133
+ # @return [Pathname, nil] the repository directory path
134
+ #
135
+ def repo
136
+ repository = execution_context.git_dir
137
+ repository && Pathname.new(repository)
138
+ end
139
+
140
+ # Returns the git index file
141
+ #
142
+ # @example Get the index file path
143
+ # repository.index #=> #<Pathname:/path/to/repo/.git/index>
144
+ #
145
+ # @return [Pathname, nil] the index file path
146
+ #
147
+ def index
148
+ index_file = execution_context.git_index_file
149
+ index_file && Pathname.new(index_file)
150
+ end
151
+
152
+ # Returns `self` after emitting a deprecation warning.
153
+ #
154
+ # Legacy callers that used `git.lib.some_method` can migrate to calling the
155
+ # facade method directly on the repository object. This shim will be removed
156
+ # in v6.0.0.
157
+ #
158
+ # @return [self]
159
+ #
160
+ # @api private
161
+ #
162
+ def lib
163
+ Git::Deprecation.warn(
164
+ 'Git::Repository#lib is deprecated and will be removed in v6.0.0. ' \
165
+ 'Use the repository object directly.'
166
+ )
167
+ self
168
+ end
169
+
170
+ # @return [String, nil] the git directory path
171
+ #
172
+ # @api private
173
+ def git_dir = execution_context.git_dir
174
+
175
+ # @return [String, nil] the working directory path
176
+ #
177
+ # @api private
178
+ def git_work_dir = execution_context.git_work_dir
179
+
180
+ # @return [String, nil] the index file path
181
+ #
182
+ # @api private
183
+ def git_index_file = execution_context.git_index_file
184
+
185
+ # Returns the installed git version
186
+ #
187
+ # @param timeout [Numeric, nil] seconds to wait for `git version`; `nil`
188
+ # uses the default timeout for this execution context
189
+ #
190
+ # @return [Git::Version] the installed git version
191
+ #
192
+ # @api private
193
+ def git_version(timeout: nil) = execution_context.git_version(timeout: timeout)
194
+
195
+ # @return [String, nil] the SSH wrapper path
196
+ #
197
+ # @api private
198
+ def git_ssh = execution_context.git_ssh
5
199
 
200
+ # @return [String, :use_global_config] the path to the git binary
201
+ #
202
+ # @api private
203
+ def binary_path = execution_context.binary_path
204
+
205
+ # Reads or writes a git configuration entry
206
+ #
207
+ # Dispatches to one of three modes depending on the arguments supplied:
208
+ #
209
+ # * **List** — `config()` returns all visible config entries as a `Hash`.
210
+ # * **Get** — `config(name)` returns the value for a single key as a `String`.
211
+ # * **Set** — `config(name, value)` writes a value and returns the raw
212
+ # command result.
213
+ #
214
+ # @example List all config entries
215
+ # repo.config #=> { "user.name" => "Alice", "core.bare" => "false" }
216
+ #
217
+ # @example Read a config value
218
+ # repo.config('user.name') #=> "Alice"
219
+ #
220
+ # @example Set a config value
221
+ # repo.config('user.name', 'Alice')
222
+ #
223
+ # @param name [String, Hash, nil] the dotted config key, or an options hash
224
+ # for list mode when `value` and `options` are omitted
225
+ #
226
+ # @param value [#to_s, Hash, nil] the value to set, or an options hash in the
227
+ # legacy `config(name, options)` call shape
228
+ #
229
+ # @param options [Hash] options forwarded to git config
230
+ #
231
+ # @option options [String, nil] :file (nil) path to a custom config file
232
+ #
233
+ # @return [Hash{String => String}, String, Git::CommandLine::Result] all config
234
+ # entries, a single value, or the command result for set mode
235
+ #
236
+ # @raise [ArgumentError] if unsupported options are provided
237
+ #
238
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
239
+ #
240
+ def config(name = nil, value = nil, options = {})
241
+ Git::Deprecation.warn(CONFIG_DEPRECATION_WARNING)
242
+ name, value, options = deprecated_normalize_config_args(name, value, options)
243
+
244
+ if !name.nil? && !value.nil?
245
+ deprecated_config_set(name, value, **options)
246
+ elsif name
247
+ deprecated_config_get(name, **options)
248
+ else
249
+ deprecated_config_list(**options)
250
+ end
251
+ end
252
+
253
+ # Read or write a global git configuration entry
254
+ #
255
+ # Dispatches to one of three modes depending on the arguments supplied,
256
+ # targeting the git global config scope (`git config --global`):
257
+ #
258
+ # * **List** — `global_config()` returns all global config entries as a `Hash`.
259
+ # * **Get** — `global_config(name)` returns the value for a single key as a `String`.
260
+ # * **Set** — `global_config(name, value)` writes a value and returns the raw
261
+ # command result.
262
+ #
263
+ # @overload global_config
264
+ #
265
+ # @example List all global config entries
266
+ # repo.global_config #=> { "user.name" => "Alice", "core.autocrlf" => "false" }
267
+ #
268
+ # @return [Hash{String => String}] all global config entries, keyed by their
269
+ # full dotted key names (e.g. `"user.name"`)
270
+ #
271
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
272
+ #
273
+ # @overload global_config(name)
274
+ #
275
+ # @example Read the global committer name
276
+ # repo.global_config('user.name') #=> "Alice"
277
+ #
278
+ # @param name [String] the dotted config key to look up (e.g. `"user.name"`)
279
+ #
280
+ # @return [String] the value of the global config entry
281
+ #
282
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
283
+ #
284
+ # @overload global_config(name, value)
285
+ #
286
+ # @example Set the global committer name
287
+ # repo.global_config('user.name', 'Alice')
288
+ #
289
+ # @param name [String] the dotted config key to write (e.g. `"user.name"`)
290
+ #
291
+ # @param value [#to_s] the value to assign; any object is accepted and
292
+ # converted to a String via `#to_s` before being passed to git
293
+ #
294
+ # @return [Git::CommandLine::Result] the raw result of
295
+ # `git config --global <name> <value>`
296
+ #
297
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
298
+ #
299
+ def global_config(name = nil, value = nil)
300
+ Git::Deprecation.warn(GLOBAL_CONFIG_DEPRECATION_WARNING)
301
+ if !name.nil? && !value.nil?
302
+ deprecated_global_config_set(name, value)
303
+ elsif !name.nil?
304
+ deprecated_global_config_get(name)
305
+ else
306
+ deprecated_global_config_list
307
+ end
308
+ end
309
+
310
+ # Returns the size of the repository directory in bytes
311
+ #
312
+ # Sums the sizes of every regular file under the repository (`.git`)
313
+ # directory in a single traversal. Symbolic links are not followed, so files
314
+ # that physically live outside the repository (reached through a symlinked
315
+ # directory) are never counted. Files that disappear mid-traversal are
316
+ # silently skipped.
317
+ #
318
+ # @example Get the repository size in bytes
319
+ # repository.repo_size #=> 12345
320
+ #
321
+ # @return [Integer] the total size in bytes of the repository directory
322
+ #
323
+ def repo_size
324
+ repository = repo
325
+ return 0 unless repository&.directory?
326
+
327
+ total = 0
328
+ Find.find(repository.to_s) do |path|
329
+ stat = File.lstat(path)
330
+ total += stat.size if stat.file?
331
+ rescue Errno::ENOENT
332
+ next
333
+ end
334
+ total
335
+ end
336
+
337
+ private
338
+
339
+ # Normalizes deprecated `config` call shapes into positional arguments
340
+ #
341
+ # @param name [String, Hash, nil] config key or an options hash
342
+ #
343
+ # @param value [#to_s, Hash, nil] config value or an options hash
344
+ #
345
+ # @param options [Hash] explicit options hash argument
346
+ #
347
+ # @option options [String, nil] :file (nil) path to a custom config file
348
+ #
349
+ # @return [Array((String, nil), (#to_s, nil), Hash)] normalized
350
+ # `[name, value, options]`
351
+ #
352
+ # @raise [ArgumentError] if deprecated arguments mix an options hash with
353
+ # unexpected additional positional arguments
354
+ #
355
+ def deprecated_normalize_config_args(name, value, options)
356
+ if name.is_a?(Hash)
357
+ raise ArgumentError, 'unexpected positional arguments after options hash' if !value.nil? || !options.empty?
358
+
359
+ [nil, nil, name]
360
+ elsif value.is_a?(Hash)
361
+ raise ArgumentError, 'unexpected third argument when second argument is options hash' unless options.empty?
362
+
363
+ [name, nil, value]
364
+ else
365
+ [name, value, options]
366
+ end
367
+ end
368
+
369
+ # Writes a config value using the deprecated `config(name, value, ...)` path
370
+ #
371
+ # @overload deprecated_config_set(name, value, **options)
372
+ #
373
+ # @param name [String] the dotted config key to write
374
+ #
375
+ # @param value [#to_s] the value to assign
376
+ #
377
+ # @param options [Hash] command options
378
+ #
379
+ # @option options [String, nil] :file (nil) path to a custom config file
380
+ #
381
+ # @return [Git::CommandLine::Result] the command result
382
+ #
383
+ # @raise [ArgumentError] if unsupported options are provided
384
+ #
385
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
386
+ #
387
+ def deprecated_config_set(name, value, **)
388
+ SharedPrivate.assert_valid_opts!(CONFIG_SET_ALLOWED_OPTS, **)
389
+ Git::Commands::ConfigOptionSyntax::Set.new(@execution_context).call(name, value, **)
390
+ end
391
+
392
+ # Reads a config value using the deprecated `config(name, ...)` path
393
+ #
394
+ # @param name [String] the dotted config key to read
395
+ #
396
+ # @param options [Hash] command options
397
+ #
398
+ # @option options [String, nil] :file (nil) path to a custom config file
399
+ #
400
+ # @return [String] the config value
401
+ #
402
+ # @raise [ArgumentError] if unsupported options are provided
403
+ #
404
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
405
+ #
406
+ def deprecated_config_get(name, **options)
407
+ SharedPrivate.assert_valid_opts!(CONFIG_READ_ALLOWED_OPTS, **options)
408
+ opts = options[:file] ? { file: options[:file] } : {}
409
+ result = Git::Commands::ConfigOptionSyntax::Get.new(@execution_context).call(name, **opts)
410
+ raise Git::FailedError, result if result.status.exitstatus != 0
411
+
412
+ result.stdout
413
+ end
414
+
415
+ # Lists config entries using the deprecated `config(...)` path
416
+ #
417
+ # @param options [Hash] command options
418
+ #
419
+ # @option options [String, nil] :file (nil) path to a custom config file
420
+ #
421
+ # @return [Hash{String => String}] all visible config entries keyed by name
422
+ #
423
+ # @raise [ArgumentError] if unsupported options are provided
424
+ #
425
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
426
+ #
427
+ def deprecated_config_list(**options)
428
+ SharedPrivate.assert_valid_opts!(CONFIG_READ_ALLOWED_OPTS, **options)
429
+ opts = options[:file] ? { file: options[:file] } : {}
430
+ lines = Git::Commands::ConfigOptionSyntax::List.new(@execution_context).call(**opts).stdout.split("\n")
431
+ lines.each_with_object({}) do |line, hsh|
432
+ key, value = line.split('=', 2)
433
+ hsh[key] = value || ''
434
+ end
435
+ end
436
+
437
+ # Reads a global config value using the deprecated `global_config(name)` path
438
+ #
439
+ # @param name [String] the dotted config key to read
440
+ #
441
+ # @return [String] the config value
442
+ #
443
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
444
+ #
445
+ def deprecated_global_config_get(name)
446
+ result = Git::Commands::ConfigOptionSyntax::Get.new(@execution_context).call(name, global: true)
447
+ raise Git::FailedError, result if result.status.exitstatus != 0
448
+
449
+ result.stdout
450
+ end
451
+
452
+ # Lists global config entries using the deprecated `global_config` path
453
+ #
454
+ # @return [Hash{String => String}] all global config entries keyed by name
455
+ #
456
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
457
+ #
458
+ def deprecated_global_config_list
459
+ lines = Git::Commands::ConfigOptionSyntax::List.new(@execution_context).call(global: true).stdout.split("\n")
460
+ lines.each_with_object({}) do |line, hsh|
461
+ key, value = line.split('=', 2)
462
+ hsh[key] = value || ''
463
+ end
464
+ end
465
+
466
+ # Writes a global config value using the deprecated `global_config` path
467
+ #
468
+ # @param name [String] the dotted config key to write
469
+ #
470
+ # @param value [#to_s] the value to assign
471
+ #
472
+ # @return [Git::CommandLine::Result] the command result
473
+ #
474
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
475
+ #
476
+ def deprecated_global_config_set(name, value)
477
+ Git::Commands::ConfigOptionSyntax::Set.new(@execution_context).call(name, value, global: true)
478
+ end
479
+
480
+ # All git config scopes are valid in a repository context
481
+ #
482
+ # @return [void]
483
+ #
484
+ def assert_valid_scope!(**)
485
+ nil
486
+ end
487
+ end
6
488
  end
data/lib/git/stash.rb CHANGED
@@ -1,27 +1,124 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Git
4
+ # Represents a single stash entry in a Git repository
5
+ #
6
+ # This class is deprecated and will be removed in v6.0.0. Use the
7
+ # {Git::Repository} stash methods and {Git::StashInfo} instead:
8
+ # {Git::Repository#stash_push} replaces `Git::Stash.new(repo, message)` and
9
+ # returns a {Git::StashInfo}, or `nil` when there was nothing to stash.
10
+ #
11
+ # @example Create a stash and inspect the result (deprecated)
12
+ # stash = Git::Stash.new(repo, 'WIP: feature work')
13
+ # stash.message #=> "WIP: feature work"
14
+ # stash.saved? #=> true
15
+ #
16
+ # @example The replacement
17
+ # info = repo.stash_push(message: 'WIP: feature work')
18
+ # info.message #=> "On main: WIP: feature work"
19
+ # info.nil? #=> false
20
+ #
21
+ # @deprecated Use {Git::Repository#stash_push} and {Git::StashInfo} instead
22
+ #
23
+ # @see Git::Repository#stash_push
24
+ #
25
+ # @api public
26
+ #
2
27
  class Stash
3
-
4
- def initialize(base, message, existing=false)
28
+ # Initialize a Stash object
29
+ #
30
+ # When `existing` is `false` (the default), immediately calls {#save} to push
31
+ # the current working-directory state onto the stash stack.
32
+ #
33
+ # Emits one deprecation warning per object.
34
+ #
35
+ # @example Create a new stash entry
36
+ # stash = Git::Stash.new(repo, 'WIP: feature work')
37
+ # stash.saved? #=> true
38
+ #
39
+ # @example Reference an existing stash without pushing
40
+ # stash = Git::Stash.new(repo, 'WIP: feature work', existing: true)
41
+ # stash.saved? #=> nil
42
+ #
43
+ # @param base [Git::Repository] the git repository
44
+ #
45
+ # @param message [String] the stash message
46
+ #
47
+ # @param existing [Boolean] (false) when `true`, wraps an existing stash entry
48
+ # without pushing any changes
49
+ #
50
+ # @return [void]
51
+ #
52
+ # @deprecated Use {Git::Repository#stash_push} and {Git::StashInfo} instead
53
+ #
54
+ def initialize(base, message, existing: false)
55
+ Git::Deprecation.warn(
56
+ 'Git::Stash is deprecated and will be removed in v6.0.0. ' \
57
+ 'Use the Git::Repository stash methods (stash_push, stash_infos, stash_apply) ' \
58
+ 'and Git::StashInfo instead.'
59
+ )
5
60
  @base = base
6
61
  @message = message
7
62
  save unless existing
8
63
  end
9
-
64
+
65
+ # Saves the current working-directory state to the stash stack
66
+ #
67
+ # @example Save changes to the stash stack
68
+ # stash = Git::Stash.new(repo, 'WIP', existing: true)
69
+ # stash.save #=> true
70
+ #
71
+ # @return [Boolean] `true` if changes were stashed, `false` if there were no
72
+ # local changes to save
73
+ #
74
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
75
+ #
10
76
  def save
11
- @saved = @base.lib.stash_save(@message)
77
+ # stash_save is deprecated too; silence it so one Git::Stash call emits one warning
78
+ @saved = Git::Deprecation.silence { stash_repository.stash_save(@message) }
12
79
  end
13
-
80
+
81
+ # Returns whether the stash was saved successfully
82
+ #
83
+ # @example Check if changes were stashed
84
+ # stash = Git::Stash.new(repo, 'WIP: feature work')
85
+ # stash.saved? #=> true
86
+ #
87
+ # @return [Boolean, nil] `true` if changes were stashed, `false` if there were no
88
+ # local changes, `nil` if {#save} has not been called (e.g. `existing: true`)
89
+ #
14
90
  def saved?
15
91
  @saved
16
92
  end
17
-
18
- def message
19
- @message
20
- end
21
-
93
+
94
+ # Returns the stash description
95
+ #
96
+ # @example Read the stash message
97
+ # stash = Git::Stash.new(repo, 'WIP: feature work', existing: true)
98
+ # stash.message #=> "WIP: feature work"
99
+ #
100
+ # @return [String] the stash message
101
+ #
102
+ attr_reader :message
103
+
104
+ # Returns the stash description as a string
105
+ #
106
+ # @example Convert stash to string
107
+ # stash = Git::Stash.new(repo, 'WIP: feature work', existing: true)
108
+ # stash.to_s #=> "WIP: feature work"
109
+ #
110
+ # @return [String] the stash message
111
+ #
22
112
  def to_s
23
113
  message
24
114
  end
25
-
115
+
116
+ private
117
+
118
+ # @return [Git::Repository]
119
+ #
120
+ def stash_repository
121
+ @base
122
+ end
26
123
  end
27
- end
124
+ end