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,236 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'git/command_line/base'
4
+ require 'stringio'
5
+
6
+ module Git
7
+ module CommandLine
8
+ # Executes a git command in streaming mode without buffering stdout in memory
9
+ #
10
+ # {Git::CommandLine::Streaming} is the non-buffering strategy: it calls
11
+ # `ProcessExecuter.run` and streams stdout directly to the caller-supplied `out:`
12
+ # IO object. Stderr is always captured internally in a `StringIO` for error
13
+ # diagnostics and is available as `result.stderr`.
14
+ #
15
+ # Use this class (via {Git::ExecutionContext#command_streaming}) for commands such as
16
+ # `cat-file -p <blob>` whose stdout may be too large to buffer in memory.
17
+ #
18
+ # {Git::CommandLine::Capturing} is the complementary strategy for the common case
19
+ # where buffering stdout is acceptable.
20
+ #
21
+ # @example Stream a blob to a file
22
+ # streaming = Git::CommandLine::Streaming.new(
23
+ # {}, '/usr/bin/git', %w[--git-dir /repo/.git], Logger.new($stdout)
24
+ # )
25
+ # File.open('/tmp/blob', 'wb') do |f|
26
+ # streaming.run('cat-file', 'blob', sha, out: f)
27
+ # end
28
+ #
29
+ # @see Git::ExecutionContext#command_streaming
30
+ #
31
+ # @see Git::CommandLine::Capturing
32
+ #
33
+ # @api private
34
+ #
35
+ class Streaming < Git::CommandLine::Base
36
+ # Default options accepted by {#run}
37
+ #
38
+ # @api private
39
+ RUN_OPTION_DEFAULTS = {
40
+ in: nil,
41
+ out: nil,
42
+ err: nil,
43
+ chdir: nil,
44
+ timeout: nil,
45
+ raise_on_failure: true,
46
+ env: {}
47
+ }.freeze
48
+
49
+ # Execute a git command in streaming mode and return the result
50
+ #
51
+ # Unlike {Git::CommandLine::Capturing#run}, this method does **not** buffer
52
+ # stdout in memory. Stdout is written only to the IO object provided via the
53
+ # `out:` option. Stderr is captured internally via a `StringIO` for error
54
+ # diagnostics.
55
+ #
56
+ # Use this entry point for commands that stream large content (e.g. blobs)
57
+ # where capturing stdout in memory would be unacceptable.
58
+ #
59
+ # @example Stream a blob to a file
60
+ # file = File.open('/tmp/blob', 'wb')
61
+ # streaming.run('cat-file', 'blob', sha, out: file)
62
+ #
63
+ # @param options_hash [Hash] the options to pass to the command
64
+ #
65
+ # @option options_hash [IO, nil] :in the IO object to use as stdin for the
66
+ # command, or nil to inherit the parent process stdin. Must be a real IO
67
+ # object with a file descriptor (not StringIO).
68
+ #
69
+ # @option options_hash [#write, nil] :out the IO/object to stream stdout into.
70
+ # Stdout is NOT buffered in the returned result; this is the only way to
71
+ # read it.
72
+ #
73
+ # @option options_hash [#write, nil] :err an optional additional destination to
74
+ # receive stderr output in real time (e.g. `$stderr` or a `File`). Stderr is
75
+ # always captured internally in a `StringIO` for error diagnostics. When
76
+ # `err:` is provided, writes are teed to both the internal buffer and this
77
+ # destination. `result.stderr` always reflects what was captured in the
78
+ # internal buffer, regardless of whether `err:` is supplied.
79
+ #
80
+ # @option options_hash [String, nil] :chdir the directory to run the command in
81
+ #
82
+ # @option options_hash [Numeric, nil] :timeout the maximum seconds to wait for
83
+ # the command to complete. Zero means no timeout. A timeout kills the
84
+ # process via `SIGKILL` and raises {Git::TimeoutError}.
85
+ #
86
+ # @option options_hash [Boolean] :raise_on_failure (true) whether to raise
87
+ # {Git::FailedError} on non-zero exit status.
88
+ # {Git::TimeoutError} and {Git::SignaledError} are always raised regardless.
89
+ #
90
+ # @option options_hash [Hash] :env ({}) additional environment variable
91
+ # overrides for this command. String keys map to String values (to set) or
92
+ # `nil` (to unset).
93
+ #
94
+ # @return [Git::CommandLine::Result] the result of the command
95
+ #
96
+ # `result.stdout` will always be `''` (empty) — stdout was streamed to `out:`.
97
+ # `result.stderr` contains any stderr output captured for diagnostics.
98
+ #
99
+ # @raise [ArgumentError] if `args` contains an array or an unknown option is
100
+ # passed
101
+ #
102
+ # @raise [Git::SignaledError] if the command was terminated by an uncaught signal
103
+ #
104
+ # @raise [Git::FailedError] if the command returned a non-zero exit status
105
+ #
106
+ # @raise [Git::ProcessIOError] if an exception was raised while collecting
107
+ # subprocess output, or (Ruby 4.0+) if a timeout-handling race causes
108
+ # `Errno::ESRCH` when the spawned process exits between the timeout
109
+ # firing and the kill signal being delivered
110
+ #
111
+ # @raise [Git::TimeoutError] if the command times out
112
+ #
113
+ def run(*, **options_hash)
114
+ options = merge_and_validate_options(RUN_OPTION_DEFAULTS, options_hash)
115
+
116
+ internal_err = StringIO.new
117
+ # Tee stderr to the caller-provided destination (if any) AND the internal
118
+ # StringIO. This ensures result.stderr is always available even when err:
119
+ # is a non-StringIO IO object.
120
+ err_dest = options[:err] ? build_stderr_tee(internal_err, options[:err]) : internal_err
121
+ result = execute(*, err_io: err_dest, **options)
122
+ process_result(result, internal_err, options)
123
+ end
124
+
125
+ private
126
+
127
+ # Execute the git command in streaming mode
128
+ #
129
+ # @param args [Array<String>] the git command arguments
130
+ #
131
+ # @param err_io [StringIO, #write] the internal stderr destination
132
+ #
133
+ # @param options_hash [Hash] the merged run options forwarded from {#run}
134
+ #
135
+ # Only the keys consumed by this method are listed below; `:err` and
136
+ # `:raise_on_failure` are present in the hash but not used here (`:err`
137
+ # is handled via the separate `err_io:` argument).
138
+ #
139
+ # @option options_hash [IO, nil] :in stdin IO object for the subprocess
140
+ #
141
+ # @option options_hash [#write, nil] :out stdout streaming destination
142
+ #
143
+ # @option options_hash [String, nil] :chdir working directory for the subprocess
144
+ #
145
+ # @option options_hash [Numeric, nil] :timeout execution timeout in seconds
146
+ #
147
+ # @option options_hash [Hash] :env ({}) environment variable overrides
148
+ #
149
+ # @return [ProcessExecuter::Result] the result of running the command (non-capturing)
150
+ #
151
+ # @api private
152
+ def execute(*args, err_io:, **options_hash)
153
+ git_cmd = build_git_cmd(args)
154
+ options = execute_options(err_io:, **options_hash)
155
+ run_process_executer do
156
+ ProcessExecuter.run(merged_env(options_hash), *git_cmd, **options)
157
+ end
158
+ end
159
+
160
+ # Build the ProcessExecuter options hash for a streaming run
161
+ #
162
+ # @param err_io [StringIO, #write] the stderr destination (internal buffer or tee)
163
+ #
164
+ # @param options_hash [Hash] the merged run options forwarded from {#run}
165
+ #
166
+ # @option options_hash [IO, nil] :in stdin IO object for the subprocess
167
+ #
168
+ # @option options_hash [#write, nil] :out stdout streaming destination
169
+ #
170
+ # @option options_hash [String, nil] :chdir working directory for the subprocess
171
+ #
172
+ # @option options_hash [Numeric, nil] :timeout execution timeout in seconds
173
+ #
174
+ # @return [Hash]
175
+ #
176
+ # @api private
177
+ def execute_options(err_io:, **options_hash)
178
+ chdir = options_hash[:chdir] || :not_set
179
+ timeout_after = options_hash[:timeout]
180
+
181
+ { chdir:, timeout_after:, raise_errors: false, err: err_io }.tap do |options|
182
+ options[:in] = options_hash[:in] unless options_hash[:in].nil?
183
+ options[:out] = options_hash[:out] unless options_hash[:out].nil?
184
+ end
185
+ end
186
+
187
+ # Build a tee writer that forwards #write calls to two destinations simultaneously.
188
+ #
189
+ # Used to capture stderr in an internal StringIO while also streaming to a
190
+ # caller-provided destination.
191
+ #
192
+ # @param primary [StringIO] the internal capture buffer
193
+ #
194
+ # @param secondary [#write] the caller-supplied destination
195
+ #
196
+ # @return [#write] an object whose #write method delegates to both destinations
197
+ #
198
+ # @api private
199
+ def build_stderr_tee(primary, secondary)
200
+ ::Object.new.tap do |tee|
201
+ tee.define_singleton_method(:write) do |data|
202
+ primary.write(data)
203
+ secondary.write(data)
204
+ data.bytesize
205
+ end
206
+ end
207
+ end
208
+
209
+ # Process the result of a streaming command and return a Git::CommandLine::Result
210
+ #
211
+ # Constructs stdout as `''` (not captured) and stderr from the internal StringIO.
212
+ #
213
+ # @param result [ProcessExecuter::Result] the raw process result
214
+ #
215
+ # @param err_io [StringIO] the internal StringIO that captured stderr
216
+ #
217
+ # @param options [Hash] the merged run options forwarded from {#run}
218
+ #
219
+ # @option options [Numeric, nil] :timeout execution timeout used to construct the result
220
+ #
221
+ # @option options [Boolean] :raise_on_failure (true) raise {Git::FailedError} on non-zero exit
222
+ #
223
+ # @return [Git::CommandLine::Result]
224
+ #
225
+ # @api private
226
+ def process_result(result, err_io, options)
227
+ command = result.command
228
+ stderr = err_io.string
229
+ log_result(result, command, '', stderr)
230
+ command_line_result(
231
+ command, result, '', stderr, options[:timeout], options[:raise_on_failure]
232
+ )
233
+ end
234
+ end
235
+ end
236
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ # Namespace module for git command-line execution strategies
5
+ #
6
+ # This module groups the classes responsible for invoking git subprocesses
7
+ # and handling their output. Choose a concrete class based on your buffering
8
+ # needs:
9
+ #
10
+ # * {Git::CommandLine::Capturing} — buffers stdout and stderr in memory.
11
+ # Use this for the vast majority of git commands whose output fits in memory.
12
+ #
13
+ # * {Git::CommandLine::Streaming} — streams stdout to a caller-supplied IO.
14
+ # Use this for commands (e.g. `cat-file -p <blob>`) whose output may be
15
+ # too large to buffer.
16
+ #
17
+ # Both classes inherit from {Git::CommandLine::Base} and are used internally
18
+ # by {Git::ExecutionContext#command_capturing} and
19
+ # {Git::ExecutionContext#command_streaming}.
20
+ #
21
+ # Results are returned as {Git::CommandLine::Result} objects (also accessible
22
+ # as {Git::CommandLine::Result} for backward compatibility).
23
+ #
24
+ # @example Buffered command via Git::CommandLine::Capturing
25
+ # cli = Git::CommandLine::Capturing.new(
26
+ # {}, '/usr/bin/git', [], Logger.new(nil)
27
+ # )
28
+ # result = cli.run('version')
29
+ # result.stdout #=> "git version 2.39.1\n"
30
+ #
31
+ # @example Streaming command via Git::CommandLine::Streaming
32
+ # cli = Git::CommandLine::Streaming.new(
33
+ # {}, '/usr/bin/git', [], Logger.new(nil)
34
+ # )
35
+ # File.open('/tmp/blob', 'wb') do |f|
36
+ # cli.run('cat-file', 'blob', sha, out: f)
37
+ # end
38
+ #
39
+ # @see Git::CommandLine::Base
40
+ #
41
+ # @see Git::CommandLine::Result
42
+ #
43
+ # @api private
44
+ #
45
+ module CommandLine
46
+ end
47
+ end
48
+
49
+ require 'git/command_line/result'
50
+ require 'git/command_line/base'
51
+ require 'git/command_line/capturing'
52
+ require 'git/command_line/streaming'
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'git/commands/base'
4
+
5
+ module Git
6
+ module Commands
7
+ # Implements the `git add` command
8
+ #
9
+ # This command updates the index using the current content found in the working tree,
10
+ # to prepare the content staged for the next commit.
11
+ #
12
+ # @example Basic usage
13
+ # add = Git::Commands::Add.new(execution_context)
14
+ # add.call('path/to/file')
15
+ # add.call('file1.rb', 'file2.rb')
16
+ # add.call(all: true)
17
+ #
18
+ # @note `arguments` block audited against https://git-scm.com/docs/git-add/2.53.0
19
+ #
20
+ # @see https://git-scm.com/docs/git-add git-add
21
+ #
22
+ # @see Git::Commands
23
+ #
24
+ # @api private
25
+ #
26
+ class Add < Git::Commands::Base
27
+ arguments do
28
+ literal 'add'
29
+ flag_option %i[verbose v]
30
+ flag_option %i[dry_run n]
31
+ flag_option %i[force f]
32
+ flag_option %i[all A], negatable: true
33
+ flag_option :ignore_removal, negatable: true
34
+ flag_option %i[update u]
35
+ flag_option :sparse
36
+ flag_option %i[intent_to_add N]
37
+ flag_option :refresh
38
+ flag_option :ignore_errors
39
+ flag_option :ignore_missing
40
+ flag_option :renormalize
41
+ flag_option :no_warn_embedded_repo
42
+ value_option :chmod, inline: true
43
+ value_option :pathspec_from_file, inline: true
44
+ flag_option :pathspec_file_nul
45
+ end_of_options
46
+ operand :pathspec, repeatable: true
47
+ end
48
+
49
+ # @overload call(*pathspec, **options)
50
+ #
51
+ # Execute the `git add` command
52
+ #
53
+ # @param pathspec [Array<String>] files to be added to the repository
54
+ # (relative to the worktree root)
55
+ #
56
+ # @param options [Hash] command options
57
+ #
58
+ # @option options [Boolean, nil] :verbose (nil) be verbose
59
+ #
60
+ # Alias: :v
61
+ #
62
+ # @option options [Boolean, nil] :dry_run (nil) don't actually add files;
63
+ # show what would be added
64
+ #
65
+ # Alias: :n
66
+ #
67
+ # @option options [Boolean, nil] :force (nil) allow adding otherwise ignored
68
+ # files
69
+ #
70
+ # Alias: :f
71
+ #
72
+ # @option options [Boolean, nil] :all (nil) add, modify, and remove index
73
+ # entries to match the worktree (--all)
74
+ #
75
+ # Alias: :A
76
+ #
77
+ # @option options [Boolean, nil] :no_all (nil) add and modify index entries
78
+ # without staging removals (--no-all)
79
+ #
80
+ # @option options [Boolean, nil] :ignore_removal (nil) add and modify files;
81
+ # ignore removals (--ignore-removal)
82
+ #
83
+ # @option options [Boolean, nil] :no_ignore_removal (nil) include file removals
84
+ # (--no-ignore-removal)
85
+ #
86
+ # @option options [Boolean, nil] :update (nil) update tracked files only; does
87
+ # not add new files
88
+ #
89
+ # Alias: :u
90
+ #
91
+ # @option options [Boolean, nil] :sparse (nil) allow updating index entries
92
+ # outside the sparse-checkout cone
93
+ #
94
+ # @option options [Boolean, nil] :intent_to_add (nil) record that the path
95
+ # will be added later, placing an empty entry in the index
96
+ #
97
+ # Alias: :N
98
+ #
99
+ # @option options [Boolean, nil] :refresh (nil) refresh stat() information in
100
+ # the index without adding files
101
+ #
102
+ # @option options [Boolean, nil] :ignore_errors (nil) continue adding other
103
+ # files if some files cannot be added due to indexing errors
104
+ #
105
+ # @option options [Boolean, nil] :ignore_missing (nil) check whether any given
106
+ # files would be ignored
107
+ #
108
+ # @option options [Boolean, nil] :renormalize (nil) apply the "clean" process
109
+ # freshly to all tracked files to forcibly re-add them with correct line
110
+ # endings
111
+ #
112
+ # @option options [Boolean, nil] :no_warn_embedded_repo (nil) suppress warning
113
+ # when adding an embedded repository without using `git submodule add`
114
+ #
115
+ # @option options [String] :chmod (nil) override the executable bit of added
116
+ # files in the index
117
+ #
118
+ # Value must be `'+x'` or `'-x'`
119
+ #
120
+ # @option options [String] :pathspec_from_file (nil) read pathspec from the
121
+ # given file (use `'-'` for stdin)
122
+ #
123
+ # @option options [Boolean, nil] :pathspec_file_nul (nil) separate pathspec
124
+ # elements with NUL when reading from a file
125
+ #
126
+ # @return [Git::CommandLine::Result] the result of calling `git add`
127
+ #
128
+ # @raise [ArgumentError] if unsupported options are provided
129
+ #
130
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
131
+ #
132
+ # @api public
133
+ #
134
+ def call(*, **)
135
+ super
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'git/commands/base'
4
+
5
+ module Git
6
+ module Commands
7
+ module Am
8
+ # Implements `git am --abort` to abort an in-progress am session
9
+ #
10
+ # Aborts the in-progress patch application and restores the branch to
11
+ # the state it was in before the `git am` session started.
12
+ #
13
+ # @example Abort an am session
14
+ # abort_cmd = Git::Commands::Am::Abort.new(execution_context)
15
+ # abort_cmd.call
16
+ #
17
+ # @note `arguments` block audited against https://git-scm.com/docs/git-am/2.53.0
18
+ #
19
+ # @see Git::Commands::Am
20
+ #
21
+ # @see https://git-scm.com/docs/git-am git-am
22
+ #
23
+ # @api private
24
+ #
25
+ class Abort < Git::Commands::Base
26
+ arguments do
27
+ literal 'am'
28
+ literal '--abort'
29
+ end
30
+
31
+ # @!method call()
32
+ #
33
+ # @overload call()
34
+ #
35
+ # Abort the in-progress am session and restore the branch
36
+ #
37
+ # @return [Git::CommandLine::Result] the result of calling `git am --abort`
38
+ #
39
+ # @raise [Git::FailedError] if git exits with a non-zero exit status
40
+ end
41
+ end
42
+ end
43
+ end