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.
- checksums.yaml +4 -4
- data/.yardopts +5 -1
- data/AI_POLICY.md +24 -0
- data/CHANGELOG.md +689 -0
- data/CODE_OF_CONDUCT.md +25 -0
- data/CONTRIBUTING.md +1175 -97
- data/GOVERNANCE.md +106 -0
- data/LICENSE +1 -1
- data/MAINTAINERS.md +17 -4
- data/README.md +476 -320
- data/UPGRADING.md +1138 -0
- data/git.gemspec +124 -36
- data/lib/git/author.rb +39 -7
- data/lib/git/author_info.rb +66 -0
- data/lib/git/branch.rb +615 -65
- data/lib/git/branch_delete_failure.rb +34 -0
- data/lib/git/branch_delete_result.rb +66 -0
- data/lib/git/branch_info.rb +237 -0
- data/lib/git/branches.rb +167 -44
- data/lib/git/command_line/base.rb +247 -0
- data/lib/git/command_line/capturing.rb +308 -0
- data/lib/git/command_line/result.rb +88 -0
- data/lib/git/command_line/streaming.rb +236 -0
- data/lib/git/command_line.rb +52 -0
- data/lib/git/commands/add.rb +139 -0
- data/lib/git/commands/am/abort.rb +43 -0
- data/lib/git/commands/am/apply.rb +263 -0
- data/lib/git/commands/am/continue.rb +43 -0
- data/lib/git/commands/am/quit.rb +43 -0
- data/lib/git/commands/am/retry.rb +49 -0
- data/lib/git/commands/am/show_current_patch.rb +64 -0
- data/lib/git/commands/am/skip.rb +42 -0
- data/lib/git/commands/am.rb +33 -0
- data/lib/git/commands/apply.rb +242 -0
- data/lib/git/commands/archive/list_formats.rb +46 -0
- data/lib/git/commands/archive.rb +145 -0
- data/lib/git/commands/arguments.rb +4521 -0
- data/lib/git/commands/base.rb +502 -0
- data/lib/git/commands/branch/copy.rb +102 -0
- data/lib/git/commands/branch/create.rb +177 -0
- data/lib/git/commands/branch/delete.rb +88 -0
- data/lib/git/commands/branch/list.rb +178 -0
- data/lib/git/commands/branch/move.rb +102 -0
- data/lib/git/commands/branch/set_upstream.rb +86 -0
- data/lib/git/commands/branch/show_current.rb +49 -0
- data/lib/git/commands/branch/unset_upstream.rb +53 -0
- data/lib/git/commands/branch.rb +34 -0
- data/lib/git/commands/cat_file/batch.rb +385 -0
- data/lib/git/commands/cat_file/filtered.rb +105 -0
- data/lib/git/commands/cat_file/raw.rb +271 -0
- data/lib/git/commands/cat_file.rb +49 -0
- data/lib/git/commands/checkout/branch.rb +153 -0
- data/lib/git/commands/checkout/files.rb +116 -0
- data/lib/git/commands/checkout.rb +38 -0
- data/lib/git/commands/checkout_index.rb +106 -0
- data/lib/git/commands/clean.rb +102 -0
- data/lib/git/commands/clone.rb +241 -0
- data/lib/git/commands/commit.rb +273 -0
- data/lib/git/commands/commit_tree.rb +101 -0
- data/lib/git/commands/config_option_syntax/add.rb +86 -0
- data/lib/git/commands/config_option_syntax/get.rb +121 -0
- data/lib/git/commands/config_option_syntax/get_all.rb +118 -0
- data/lib/git/commands/config_option_syntax/get_color.rb +95 -0
- data/lib/git/commands/config_option_syntax/get_color_bool.rb +96 -0
- data/lib/git/commands/config_option_syntax/get_regexp.rb +119 -0
- data/lib/git/commands/config_option_syntax/get_urlmatch.rb +111 -0
- data/lib/git/commands/config_option_syntax/list.rb +111 -0
- data/lib/git/commands/config_option_syntax/remove_section.rb +79 -0
- data/lib/git/commands/config_option_syntax/rename_section.rb +83 -0
- data/lib/git/commands/config_option_syntax/replace_all.rb +109 -0
- data/lib/git/commands/config_option_syntax/set.rb +119 -0
- data/lib/git/commands/config_option_syntax/unset.rb +92 -0
- data/lib/git/commands/config_option_syntax/unset_all.rb +94 -0
- data/lib/git/commands/config_option_syntax.rb +56 -0
- data/lib/git/commands/describe.rb +156 -0
- data/lib/git/commands/diff.rb +657 -0
- data/lib/git/commands/diff_files.rb +519 -0
- data/lib/git/commands/diff_index.rb +499 -0
- data/lib/git/commands/fetch.rb +354 -0
- data/lib/git/commands/fsck.rb +138 -0
- data/lib/git/commands/gc.rb +134 -0
- data/lib/git/commands/grep.rb +339 -0
- data/lib/git/commands/init.rb +101 -0
- data/lib/git/commands/log.rb +634 -0
- data/lib/git/commands/ls_files.rb +195 -0
- data/lib/git/commands/ls_remote.rb +161 -0
- data/lib/git/commands/ls_tree.rb +135 -0
- data/lib/git/commands/maintenance/register.rb +77 -0
- data/lib/git/commands/maintenance/run.rb +109 -0
- data/lib/git/commands/maintenance/start.rb +71 -0
- data/lib/git/commands/maintenance/stop.rb +60 -0
- data/lib/git/commands/maintenance/unregister.rb +84 -0
- data/lib/git/commands/maintenance.rb +31 -0
- data/lib/git/commands/merge/abort.rb +44 -0
- data/lib/git/commands/merge/continue.rb +44 -0
- data/lib/git/commands/merge/quit.rb +46 -0
- data/lib/git/commands/merge/start.rb +250 -0
- data/lib/git/commands/merge.rb +28 -0
- data/lib/git/commands/merge_base.rb +91 -0
- data/lib/git/commands/mv.rb +82 -0
- data/lib/git/commands/name_rev.rb +119 -0
- data/lib/git/commands/pull.rb +382 -0
- data/lib/git/commands/push.rb +251 -0
- data/lib/git/commands/read_tree.rb +154 -0
- data/lib/git/commands/remote/add.rb +96 -0
- data/lib/git/commands/remote/get_url.rb +68 -0
- data/lib/git/commands/remote/list.rb +56 -0
- data/lib/git/commands/remote/prune.rb +63 -0
- data/lib/git/commands/remote/remove.rb +52 -0
- data/lib/git/commands/remote/rename.rb +76 -0
- data/lib/git/commands/remote/set_branches.rb +70 -0
- data/lib/git/commands/remote/set_head.rb +89 -0
- data/lib/git/commands/remote/set_url.rb +78 -0
- data/lib/git/commands/remote/set_url_add.rb +70 -0
- data/lib/git/commands/remote/set_url_delete.rb +71 -0
- data/lib/git/commands/remote/show.rb +77 -0
- data/lib/git/commands/remote/update.rb +79 -0
- data/lib/git/commands/remote.rb +42 -0
- data/lib/git/commands/repack.rb +281 -0
- data/lib/git/commands/reset.rb +154 -0
- data/lib/git/commands/rev_parse.rb +304 -0
- data/lib/git/commands/revert/abort.rb +45 -0
- data/lib/git/commands/revert/continue.rb +62 -0
- data/lib/git/commands/revert/quit.rb +47 -0
- data/lib/git/commands/revert/skip.rb +44 -0
- data/lib/git/commands/revert/start.rb +158 -0
- data/lib/git/commands/revert.rb +29 -0
- data/lib/git/commands/rm.rb +113 -0
- data/lib/git/commands/show.rb +632 -0
- data/lib/git/commands/show_ref/exclude_existing.rb +119 -0
- data/lib/git/commands/show_ref/exists.rb +80 -0
- data/lib/git/commands/show_ref/list.rb +149 -0
- data/lib/git/commands/show_ref/verify.rb +122 -0
- data/lib/git/commands/show_ref.rb +42 -0
- data/lib/git/commands/stash/apply.rb +81 -0
- data/lib/git/commands/stash/branch.rb +67 -0
- data/lib/git/commands/stash/clear.rb +43 -0
- data/lib/git/commands/stash/create.rb +60 -0
- data/lib/git/commands/stash/drop.rb +73 -0
- data/lib/git/commands/stash/list.rb +43 -0
- data/lib/git/commands/stash/pop.rb +87 -0
- data/lib/git/commands/stash/push.rb +112 -0
- data/lib/git/commands/stash/show.rb +158 -0
- data/lib/git/commands/stash/store.rb +72 -0
- data/lib/git/commands/stash.rb +38 -0
- data/lib/git/commands/status.rb +174 -0
- data/lib/git/commands/symbolic_ref/delete.rb +72 -0
- data/lib/git/commands/symbolic_ref/read.rb +99 -0
- data/lib/git/commands/symbolic_ref/update.rb +79 -0
- data/lib/git/commands/symbolic_ref.rb +38 -0
- data/lib/git/commands/tag/create.rb +142 -0
- data/lib/git/commands/tag/delete.rb +57 -0
- data/lib/git/commands/tag/list.rb +146 -0
- data/lib/git/commands/tag/verify.rb +71 -0
- data/lib/git/commands/tag.rb +26 -0
- data/lib/git/commands/update_ref/batch.rb +145 -0
- data/lib/git/commands/update_ref/delete.rb +90 -0
- data/lib/git/commands/update_ref/update.rb +103 -0
- data/lib/git/commands/update_ref.rb +42 -0
- data/lib/git/commands/version.rb +60 -0
- data/lib/git/commands/worktree/add.rb +139 -0
- data/lib/git/commands/worktree/list.rb +64 -0
- data/lib/git/commands/worktree/lock.rb +58 -0
- data/lib/git/commands/worktree/management_base.rb +51 -0
- data/lib/git/commands/worktree/move.rb +66 -0
- data/lib/git/commands/worktree/prune.rb +67 -0
- data/lib/git/commands/worktree/remove.rb +63 -0
- data/lib/git/commands/worktree/repair.rb +76 -0
- data/lib/git/commands/worktree/unlock.rb +47 -0
- data/lib/git/commands/worktree.rb +43 -0
- data/lib/git/commands/write_tree.rb +68 -0
- data/lib/git/commands.rb +88 -0
- data/lib/git/config.rb +72 -5
- data/lib/git/config_entry_info.rb +106 -0
- data/lib/git/configuring.rb +795 -0
- data/lib/git/detached_head_info.rb +57 -0
- data/lib/git/diff.rb +437 -86
- data/lib/git/diff_file_numstat_info.rb +31 -0
- data/lib/git/diff_file_patch_info.rb +136 -0
- data/lib/git/diff_file_raw_info.rb +129 -0
- data/lib/git/diff_info.rb +162 -0
- data/lib/git/diff_path_status.rb +107 -0
- data/lib/git/diff_result.rb +34 -0
- data/lib/git/diff_stats.rb +111 -0
- data/lib/git/dirstat_info.rb +102 -0
- data/lib/git/encoding_utils.rb +32 -1
- data/lib/git/errors.rb +285 -0
- data/lib/git/escaped_path.rb +57 -5
- data/lib/git/execution_context/global.rb +31 -0
- data/lib/git/execution_context/repository.rb +151 -0
- data/lib/git/execution_context.rb +559 -0
- data/lib/git/factories.rb +813 -0
- data/lib/git/file_ref.rb +77 -0
- data/lib/git/fsck_object.rb +56 -0
- data/lib/git/fsck_result.rb +132 -0
- data/lib/git/log.rb +306 -90
- data/lib/git/object.rb +563 -141
- data/lib/git/parsers/branch.rb +240 -0
- data/lib/git/parsers/cat_file.rb +111 -0
- data/lib/git/parsers/config_entry.rb +110 -0
- data/lib/git/parsers/diff.rb +792 -0
- data/lib/git/parsers/fsck.rb +144 -0
- data/lib/git/parsers/grep.rb +42 -0
- data/lib/git/parsers/ls_remote.rb +79 -0
- data/lib/git/parsers/ls_tree.rb +58 -0
- data/lib/git/parsers/remote.rb +162 -0
- data/lib/git/parsers/stash.rb +292 -0
- data/lib/git/parsers/status.rb +251 -0
- data/lib/git/parsers/tag.rb +341 -0
- data/lib/git/parsers/worktree.rb +185 -0
- data/lib/git/path_resolver.rb +206 -0
- data/lib/git/remote.rb +165 -12
- data/lib/git/remote_info.rb +203 -0
- data/lib/git/repository/branching.rb +964 -0
- data/lib/git/repository/committing.rb +246 -0
- data/lib/git/repository/context_helpers.rb +293 -0
- data/lib/git/repository/diffing.rb +785 -0
- data/lib/git/repository/inspecting.rb +252 -0
- data/lib/git/repository/logging.rb +410 -0
- data/lib/git/repository/maintenance.rb +65 -0
- data/lib/git/repository/merging.rb +451 -0
- data/lib/git/repository/object_operations.rb +1551 -0
- data/lib/git/repository/remote_operations.rb +984 -0
- data/lib/git/repository/shared_private.rb +120 -0
- data/lib/git/repository/staging.rb +587 -0
- data/lib/git/repository/stashing.rb +623 -0
- data/lib/git/repository/status_operations.rb +249 -0
- data/lib/git/repository/worktree_operations.rb +339 -0
- data/lib/git/repository.rb +484 -2
- data/lib/git/stash.rb +109 -12
- data/lib/git/stash_info.rb +102 -0
- data/lib/git/stashes.rb +169 -26
- data/lib/git/status.rb +308 -122
- data/lib/git/status_file_info.rb +258 -0
- data/lib/git/status_info.rb +189 -0
- data/lib/git/tag_delete_failure.rb +34 -0
- data/lib/git/tag_delete_result.rb +66 -0
- data/lib/git/tag_info.rb +99 -0
- data/lib/git/url.rb +15 -8
- data/lib/git/version.rb +113 -2
- data/lib/git/version_constraint.rb +85 -0
- data/lib/git/worktree.rb +150 -8
- data/lib/git/worktree_info.rb +128 -0
- data/lib/git/worktrees.rb +118 -13
- data/lib/git.rb +632 -234
- metadata +369 -54
- data/.github/stale.yml +0 -25
- data/.github/workflows/continuous_integration.yml +0 -49
- data/.gitignore +0 -10
- data/Dockerfile.changelog-rs +0 -12
- data/Gemfile +0 -5
- data/ISSUE_TEMPLATE.md +0 -15
- data/PULL_REQUEST_TEMPLATE.md +0 -9
- data/RELEASING.md +0 -70
- data/Rakefile +0 -60
- data/lib/git/base/factory.rb +0 -99
- data/lib/git/base.rb +0 -711
- data/lib/git/command_line_result.rb +0 -86
- data/lib/git/failed_error.rb +0 -53
- data/lib/git/git_execute_error.rb +0 -7
- data/lib/git/index.rb +0 -5
- data/lib/git/lib.rb +0 -1328
- data/lib/git/path.rb +0 -31
- data/lib/git/signaled_error.rb +0 -50
- data/lib/git/working_directory.rb +0 -4
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'git/command_line/result'
|
|
4
|
+
require 'git/errors'
|
|
5
|
+
require 'process_executer'
|
|
6
|
+
|
|
7
|
+
module Git
|
|
8
|
+
module CommandLine
|
|
9
|
+
# Abstract base class for git command-line execution strategies
|
|
10
|
+
#
|
|
11
|
+
# Concrete subclasses must implement {#run} to execute a git command and
|
|
12
|
+
# return a {Git::CommandLine::Result}. Two implementations are provided:
|
|
13
|
+
#
|
|
14
|
+
# * {Git::CommandLine::Capturing} — buffers stdout and stderr in memory
|
|
15
|
+
# * {Git::CommandLine::Streaming} — streams stdout to a caller-supplied IO
|
|
16
|
+
#
|
|
17
|
+
# @example Instantiate a concrete subclass
|
|
18
|
+
# env = { 'GIT_DIR' => '/path/to/git/dir' }
|
|
19
|
+
# binary_path = '/usr/bin/git'
|
|
20
|
+
# global_opts = %w[--git-dir /path/to/git/dir]
|
|
21
|
+
# logger = Logger.new($stdout)
|
|
22
|
+
# cli = Git::CommandLine::Capturing.new(env, binary_path, global_opts, logger)
|
|
23
|
+
# cli.run('version') #=> #<Git::CommandLine::Result ...>
|
|
24
|
+
#
|
|
25
|
+
# @abstract Subclass and implement {#run}
|
|
26
|
+
#
|
|
27
|
+
# @api private
|
|
28
|
+
#
|
|
29
|
+
class Base
|
|
30
|
+
# Create a Base (or subclass) object
|
|
31
|
+
#
|
|
32
|
+
# @param env [Hash{String => String, nil}] environment variables to set or
|
|
33
|
+
# unset. String values set the variable; `nil` values unset it.
|
|
34
|
+
#
|
|
35
|
+
# @param binary_path [String] the path to the git binary
|
|
36
|
+
#
|
|
37
|
+
# @param global_opts [Array<String>] global options to pass to git
|
|
38
|
+
#
|
|
39
|
+
# @param logger [Logger] used to log git commands and their results
|
|
40
|
+
#
|
|
41
|
+
def initialize(env, binary_path, global_opts, logger)
|
|
42
|
+
@env = env
|
|
43
|
+
@binary_path = binary_path
|
|
44
|
+
@global_opts = global_opts
|
|
45
|
+
@logger = logger
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Execute a git command and return the result
|
|
49
|
+
#
|
|
50
|
+
# Concrete subclasses must override this method.
|
|
51
|
+
#
|
|
52
|
+
# @raise [NotImplementedError] always — must be implemented by subclasses
|
|
53
|
+
#
|
|
54
|
+
def run(*)
|
|
55
|
+
raise NotImplementedError, "#{self.class}#run is not implemented"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Variables to set (or unset) in the git command's environment
|
|
59
|
+
#
|
|
60
|
+
# @example
|
|
61
|
+
# env = { 'GIT_DIR' => '/path/to/git/dir' }
|
|
62
|
+
# cli = Git::CommandLine::Capturing.new(env, '/usr/bin/git', [], Logger.new(nil))
|
|
63
|
+
# cli.env #=> { 'GIT_DIR' => '/path/to/git/dir' }
|
|
64
|
+
#
|
|
65
|
+
# @return [Hash{String => String, nil}]
|
|
66
|
+
#
|
|
67
|
+
# @see https://ruby-doc.org/3.2.1/Process.html#method-c-spawn Process.spawn
|
|
68
|
+
# for details on how to set environment variables using the `env` parameter
|
|
69
|
+
#
|
|
70
|
+
attr_reader :env
|
|
71
|
+
|
|
72
|
+
# The path to the command line binary to run
|
|
73
|
+
#
|
|
74
|
+
# @example
|
|
75
|
+
# cli = Git::CommandLine::Capturing.new({}, '/usr/bin/git', [], Logger.new(nil))
|
|
76
|
+
# cli.binary_path #=> '/usr/bin/git'
|
|
77
|
+
#
|
|
78
|
+
# @return [String]
|
|
79
|
+
#
|
|
80
|
+
attr_reader :binary_path
|
|
81
|
+
|
|
82
|
+
# The global options to pass to git
|
|
83
|
+
#
|
|
84
|
+
# These are options that are passed to git before the command name and
|
|
85
|
+
# arguments. For example, in `git --git-dir /path/to/git/dir version`, the
|
|
86
|
+
# global options are %w[--git-dir /path/to/git/dir].
|
|
87
|
+
#
|
|
88
|
+
# @example
|
|
89
|
+
# global_opts = %w[--git-dir /path/to/git/dir]
|
|
90
|
+
# cli = Git::CommandLine::Capturing.new({}, '/usr/bin/git', global_opts, Logger.new(nil))
|
|
91
|
+
# cli.global_opts #=> %w[--git-dir /path/to/git/dir]
|
|
92
|
+
#
|
|
93
|
+
# @return [Array<String>]
|
|
94
|
+
#
|
|
95
|
+
attr_reader :global_opts
|
|
96
|
+
|
|
97
|
+
# The logger to use for logging git commands and results
|
|
98
|
+
#
|
|
99
|
+
# @example
|
|
100
|
+
# logger = Logger.new(nil)
|
|
101
|
+
# cli = Git::CommandLine::Capturing.new({}, '/usr/bin/git', [], logger)
|
|
102
|
+
# cli.logger == logger #=> true
|
|
103
|
+
#
|
|
104
|
+
# @return [Logger]
|
|
105
|
+
#
|
|
106
|
+
attr_reader :logger
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
|
|
110
|
+
# Merge caller-supplied options into `defaults` and raise if any unknown keys are present
|
|
111
|
+
#
|
|
112
|
+
# @param defaults [Hash] the allowed keys and their default values (e.g. RUN_OPTION_DEFAULTS)
|
|
113
|
+
#
|
|
114
|
+
# @param options_hash [Hash] caller-supplied options
|
|
115
|
+
#
|
|
116
|
+
# @return [Hash] defaults with any supplied values overridden
|
|
117
|
+
#
|
|
118
|
+
# @raise [ArgumentError] if options_hash contains keys not present in defaults
|
|
119
|
+
#
|
|
120
|
+
# @api private
|
|
121
|
+
#
|
|
122
|
+
def merge_and_validate_options(defaults, options_hash)
|
|
123
|
+
merged = defaults.merge(options_hash)
|
|
124
|
+
extra = merged.keys - defaults.keys
|
|
125
|
+
raise ArgumentError, "Unknown options: #{extra.join(', ')}" if extra.any?
|
|
126
|
+
|
|
127
|
+
merged
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Merge the instance-level env with any per-call overrides in options_hash[:env]
|
|
131
|
+
#
|
|
132
|
+
# @param options_hash [Hash] options that may include an :env override
|
|
133
|
+
#
|
|
134
|
+
# @return [Hash{String => String, nil}]
|
|
135
|
+
#
|
|
136
|
+
# @api private
|
|
137
|
+
#
|
|
138
|
+
def merged_env(options_hash)
|
|
139
|
+
env.merge(options_hash[:env] || {})
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Yield to a block that calls ProcessExecuter and translate any ProcessExecuter
|
|
143
|
+
# errors to their ruby-git equivalents
|
|
144
|
+
#
|
|
145
|
+
# @return [Object] the return value of the block
|
|
146
|
+
#
|
|
147
|
+
# @raise [ArgumentError] in place of ProcessExecuter::ArgumentError
|
|
148
|
+
#
|
|
149
|
+
# @raise [Git::Error] in place of ProcessExecuter::SpawnError (binary not found or
|
|
150
|
+
# failed to launch)
|
|
151
|
+
#
|
|
152
|
+
# @raise [Git::ProcessIOError] in place of ProcessExecuter::ProcessIOError
|
|
153
|
+
#
|
|
154
|
+
# @raise [Git::ProcessIOError] when a timeout race causes Errno::ESRCH. On Ruby 4.0+,
|
|
155
|
+
# `Process.kill` raises `Errno::ESRCH` when the spawned process exits between the
|
|
156
|
+
# timeout firing and the kill signal being delivered. This is a race in
|
|
157
|
+
# process_executer's timeout handling and is semantically equivalent to a timeout.
|
|
158
|
+
#
|
|
159
|
+
# @api private
|
|
160
|
+
#
|
|
161
|
+
def run_process_executer
|
|
162
|
+
yield
|
|
163
|
+
rescue ProcessExecuter::ArgumentError => e
|
|
164
|
+
raise ::ArgumentError, e.message
|
|
165
|
+
rescue ProcessExecuter::SpawnError => e
|
|
166
|
+
raise Git::Error, e.message, cause: e.cause
|
|
167
|
+
rescue ProcessExecuter::ProcessIOError => e
|
|
168
|
+
raise Git::ProcessIOError, e.message, cause: e.cause
|
|
169
|
+
rescue Errno::ESRCH => e
|
|
170
|
+
# Ruby 4.0+: Process.kill raises Errno::ESRCH when the spawned process exits
|
|
171
|
+
# in the narrow window between the timeout firing and the kill signal being sent.
|
|
172
|
+
# This is a known race in process_executer's timeout handling.
|
|
173
|
+
raise Git::ProcessIOError, "Git process no longer exists (timeout race): #{e.message}", cause: e
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Build the git command line from the available sources to send to `Process.spawn`
|
|
177
|
+
#
|
|
178
|
+
# @param args [Array<String>] command-line arguments to append after global options
|
|
179
|
+
#
|
|
180
|
+
# @return [Array<String>]
|
|
181
|
+
#
|
|
182
|
+
# @raise [ArgumentError] if any element of args is itself an Array
|
|
183
|
+
#
|
|
184
|
+
# @api private
|
|
185
|
+
#
|
|
186
|
+
def build_git_cmd(args)
|
|
187
|
+
raise ArgumentError, 'The args array can not contain an array' if args.any?(Array)
|
|
188
|
+
|
|
189
|
+
[binary_path, *global_opts, *args].map(&:to_s)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Log the result of a git command at info/debug level
|
|
193
|
+
#
|
|
194
|
+
# @param result [ProcessExecuter::Result] the raw process result
|
|
195
|
+
#
|
|
196
|
+
# @param command [Array<String>] the full command that was run
|
|
197
|
+
#
|
|
198
|
+
# @param processed_out [String] the post-processed stdout string
|
|
199
|
+
#
|
|
200
|
+
# @param processed_err [String] the post-processed stderr string
|
|
201
|
+
#
|
|
202
|
+
# @return [void]
|
|
203
|
+
#
|
|
204
|
+
# @api private
|
|
205
|
+
#
|
|
206
|
+
def log_result(result, command, processed_out, processed_err)
|
|
207
|
+
logger.info { "#{command} exited with status #{result}" }
|
|
208
|
+
logger.debug { "stdout:\n#{processed_out.inspect}\nstderr:\n#{processed_err.inspect}" }
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Build a {Git::CommandLine::Result} and raise on timeout, signal, or failure
|
|
212
|
+
#
|
|
213
|
+
# @param command [Array<String>] the full command that was run
|
|
214
|
+
#
|
|
215
|
+
# @param result [ProcessExecuter::Result] the raw process result
|
|
216
|
+
#
|
|
217
|
+
# @param processed_out [String] processed stdout string
|
|
218
|
+
#
|
|
219
|
+
# @param processed_err [String] processed stderr string
|
|
220
|
+
#
|
|
221
|
+
# @param timeout [Numeric, nil] the timeout value (for error context)
|
|
222
|
+
#
|
|
223
|
+
# @param raise_on_failure [Boolean] whether to raise on non-zero exit status
|
|
224
|
+
#
|
|
225
|
+
# @return [Git::CommandLine::Result]
|
|
226
|
+
#
|
|
227
|
+
# @raise [Git::TimeoutError] if the command timed out
|
|
228
|
+
#
|
|
229
|
+
# @raise [Git::SignaledError] if the command was terminated by a signal
|
|
230
|
+
#
|
|
231
|
+
# @raise [Git::FailedError] if the command failed and raise_on_failure is true
|
|
232
|
+
#
|
|
233
|
+
# @api private
|
|
234
|
+
#
|
|
235
|
+
# rubocop:disable-next Metrics/ParameterLists
|
|
236
|
+
def command_line_result(command, result, processed_out, processed_err, timeout, raise_on_failure)
|
|
237
|
+
Git::CommandLine::Result.new(command, result, processed_out, processed_err).tap do |processed_result|
|
|
238
|
+
raise Git::TimeoutError.new(processed_result, timeout) if result.timed_out?
|
|
239
|
+
|
|
240
|
+
raise Git::SignaledError, processed_result if result.signaled?
|
|
241
|
+
|
|
242
|
+
raise Git::FailedError, processed_result if raise_on_failure && !result.success?
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'git/command_line/base'
|
|
4
|
+
require 'git/encoding_utils'
|
|
5
|
+
|
|
6
|
+
module Git
|
|
7
|
+
module CommandLine
|
|
8
|
+
# Executes a git command and captures both stdout and stderr in memory
|
|
9
|
+
#
|
|
10
|
+
# {Git::CommandLine::Capturing} is the buffering strategy: it calls
|
|
11
|
+
# `ProcessExecuter.run_with_capture`, which reads all subprocess output into
|
|
12
|
+
# `String` objects before returning. Use this class (via
|
|
13
|
+
# {Git::ExecutionContext#command_capturing}) for the vast majority of git subcommands whose
|
|
14
|
+
# output fits comfortably in memory.
|
|
15
|
+
#
|
|
16
|
+
# {Git::CommandLine::Streaming} is the complementary strategy for commands
|
|
17
|
+
# (such as `cat-file -p <blob>`) whose stdout may be too large to buffer.
|
|
18
|
+
#
|
|
19
|
+
# @example
|
|
20
|
+
# capturing = Git::CommandLine::Capturing.new(
|
|
21
|
+
# {}, '/usr/bin/git', %w[--git-dir /repo/.git], Logger.new($stdout)
|
|
22
|
+
# )
|
|
23
|
+
# result = capturing.run('log', '--oneline', '-5')
|
|
24
|
+
# result.stdout # => "abc1234 Initial commit\n..."
|
|
25
|
+
# result.stderr # => ""
|
|
26
|
+
#
|
|
27
|
+
# @see Git::ExecutionContext#command_capturing
|
|
28
|
+
#
|
|
29
|
+
# @see Git::CommandLine::Streaming
|
|
30
|
+
#
|
|
31
|
+
# @api private
|
|
32
|
+
#
|
|
33
|
+
class Capturing < Git::CommandLine::Base
|
|
34
|
+
# Default options accepted by {#run}
|
|
35
|
+
#
|
|
36
|
+
# @api private
|
|
37
|
+
RUN_OPTION_DEFAULTS = {
|
|
38
|
+
in: nil,
|
|
39
|
+
out: nil,
|
|
40
|
+
err: nil,
|
|
41
|
+
chdir: nil,
|
|
42
|
+
timeout: nil,
|
|
43
|
+
raise_on_failure: true,
|
|
44
|
+
env: {},
|
|
45
|
+
normalize: false,
|
|
46
|
+
chomp: false,
|
|
47
|
+
merge: false
|
|
48
|
+
}.freeze
|
|
49
|
+
|
|
50
|
+
# Execute a git command, capture stdout and stderr, and return the result
|
|
51
|
+
#
|
|
52
|
+
# Non-option command-line arguments to pass to git. If you collect the
|
|
53
|
+
# arguments in an array, splat the array into the parameter list.
|
|
54
|
+
#
|
|
55
|
+
# NORMALIZATION
|
|
56
|
+
#
|
|
57
|
+
# The command output is returned as a Unicode string containing the binary
|
|
58
|
+
# output from the command. If the binary output is not valid UTF-8, the
|
|
59
|
+
# output will cause problems because the encoding will be invalid.
|
|
60
|
+
#
|
|
61
|
+
# Normalization is a process that tries to convert the binary output to a
|
|
62
|
+
# valid UTF-8 string. It uses the `rchardet` gem to detect the encoding of
|
|
63
|
+
# the binary output and then converts it to UTF-8.
|
|
64
|
+
#
|
|
65
|
+
# Normalization is not enabled by default. Pass `normalize: true` to enable
|
|
66
|
+
# it. When enabled, normalization is applied to both stdout and stderr in
|
|
67
|
+
# the returned result object, regardless of the `out:` or `err:` options.
|
|
68
|
+
# Only the captured in-memory strings are normalized; any external IO you
|
|
69
|
+
# provide will receive the raw subprocess output.
|
|
70
|
+
#
|
|
71
|
+
# @example Run a command and return the output
|
|
72
|
+
# result = capturing.run('version')
|
|
73
|
+
# result.stdout #=> "git version 2.39.1\n"
|
|
74
|
+
#
|
|
75
|
+
# @example The args array should be splatted into the parameter list
|
|
76
|
+
# args = %w[log -n 1 --oneline]
|
|
77
|
+
# result = capturing.run(*args)
|
|
78
|
+
# result.stdout #=> "f5baa11 beginning of Ruby/Git project\n"
|
|
79
|
+
#
|
|
80
|
+
# @example Run a command and return the chomped output
|
|
81
|
+
# result = capturing.run('version', chomp: true)
|
|
82
|
+
# result.stdout #=> "git version 2.39.1"
|
|
83
|
+
#
|
|
84
|
+
# @example Run a command without normalizing the output
|
|
85
|
+
# capturing.run('version', normalize: false) #=> "git version 2.39.1\n"
|
|
86
|
+
#
|
|
87
|
+
# @example Capture stdout in a temporary file
|
|
88
|
+
# require 'tempfile'
|
|
89
|
+
# Tempfile.create('git') do |file|
|
|
90
|
+
# capturing.run('version', out: file)
|
|
91
|
+
# file.rewind
|
|
92
|
+
# file.read #=> "git version 2.39.1\n"
|
|
93
|
+
# end
|
|
94
|
+
#
|
|
95
|
+
# @example Capture stderr in a StringIO object
|
|
96
|
+
# require 'stringio'
|
|
97
|
+
# stderr = StringIO.new
|
|
98
|
+
# begin
|
|
99
|
+
# capturing.run('log', 'nonexistent-branch', err: stderr)
|
|
100
|
+
# rescue Git::FailedError => e
|
|
101
|
+
# stderr.string #=> "unknown revision or path not in the working tree.\n"
|
|
102
|
+
# end
|
|
103
|
+
#
|
|
104
|
+
# @param options_hash [Hash] the options to pass to the command
|
|
105
|
+
#
|
|
106
|
+
# @option options_hash [IO, nil] :in the IO object to use as stdin for the
|
|
107
|
+
# command, or nil to inherit the parent process stdin. Must be a real IO
|
|
108
|
+
# object with a file descriptor (not StringIO).
|
|
109
|
+
#
|
|
110
|
+
# @option options_hash [#write, nil] :out the object to write stdout to, or
|
|
111
|
+
# nil to capture stdout in the returned result.
|
|
112
|
+
#
|
|
113
|
+
# If this is a `StringIO` object, `stdout_writer.string` will be returned.
|
|
114
|
+
#
|
|
115
|
+
# In general, only specify a `stdout_writer` when you want to redirect
|
|
116
|
+
# stdout to a file or other `#write`-responding object. The default
|
|
117
|
+
# behaviour returns the command output.
|
|
118
|
+
#
|
|
119
|
+
# @option options_hash [#write, nil] :err the object to write stderr to, or
|
|
120
|
+
# nil to capture stderr in the returned result.
|
|
121
|
+
#
|
|
122
|
+
# @option options_hash [Boolean] :normalize (false) whether to normalize the
|
|
123
|
+
# encoding of stdout and stderr output
|
|
124
|
+
#
|
|
125
|
+
# @option options_hash [Boolean] :chomp (false) whether to chomp both stdout
|
|
126
|
+
# and stderr output
|
|
127
|
+
#
|
|
128
|
+
# @option options_hash [Boolean] :merge (false) whether to merge stdout and
|
|
129
|
+
# stderr in the returned string
|
|
130
|
+
#
|
|
131
|
+
# @option options_hash [String, nil] :chdir the directory to run the command in
|
|
132
|
+
#
|
|
133
|
+
# @option options_hash [Numeric, nil] :timeout the maximum seconds to wait for
|
|
134
|
+
# the command to complete. Zero means no timeout. A timeout kills the
|
|
135
|
+
# process via `SIGKILL` and raises {Git::TimeoutError}.
|
|
136
|
+
#
|
|
137
|
+
# @option options_hash [Boolean] :raise_on_failure (true) whether to raise
|
|
138
|
+
# {Git::FailedError} on non-zero exit status.
|
|
139
|
+
# {Git::TimeoutError} and {Git::SignaledError} are always raised regardless.
|
|
140
|
+
#
|
|
141
|
+
# @option options_hash [Hash] :env ({}) additional environment variable
|
|
142
|
+
# overrides for this command. String keys map to String values (to set) or
|
|
143
|
+
# `nil` (to unset).
|
|
144
|
+
#
|
|
145
|
+
# @return [Git::CommandLine::Result] the result of the command
|
|
146
|
+
#
|
|
147
|
+
# @raise [ArgumentError] if `args` contains an array or an unknown option is
|
|
148
|
+
# passed
|
|
149
|
+
#
|
|
150
|
+
# @raise [Git::SignaledError] if the command was terminated by an uncaught signal
|
|
151
|
+
#
|
|
152
|
+
# @raise [Git::FailedError] if the command returned a non-zero exit status
|
|
153
|
+
#
|
|
154
|
+
# @raise [Git::ProcessIOError] if an exception was raised while collecting
|
|
155
|
+
# subprocess output, or (Ruby 4.0+) if a timeout-handling race causes
|
|
156
|
+
# `Errno::ESRCH` when the spawned process exits between the timeout
|
|
157
|
+
# firing and the kill signal being delivered
|
|
158
|
+
#
|
|
159
|
+
# @raise [Git::TimeoutError] if the command times out
|
|
160
|
+
#
|
|
161
|
+
def run(*, **options_hash)
|
|
162
|
+
options = merge_and_validate_options(RUN_OPTION_DEFAULTS, options_hash)
|
|
163
|
+
|
|
164
|
+
result = execute(*, **options)
|
|
165
|
+
process_result(result, options)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
private
|
|
169
|
+
|
|
170
|
+
# Execute the git command with the given arguments and options, capturing the output
|
|
171
|
+
#
|
|
172
|
+
# @param args [Array<String>] the git command arguments
|
|
173
|
+
#
|
|
174
|
+
# @param options_hash [Hash] the merged run options forwarded from {#run}
|
|
175
|
+
#
|
|
176
|
+
# Only the keys consumed by this method are listed below; remaining keys
|
|
177
|
+
# from {RUN_OPTION_DEFAULTS} (`:normalize`, `:chomp`, `:raise_on_failure`)
|
|
178
|
+
# are present in the hash but are not used here.
|
|
179
|
+
#
|
|
180
|
+
# @option options_hash [IO, nil] :in stdin IO object for the subprocess
|
|
181
|
+
#
|
|
182
|
+
# @option options_hash [#write, nil] :out stdout redirect target
|
|
183
|
+
#
|
|
184
|
+
# @option options_hash [#write, nil] :err stderr redirect target
|
|
185
|
+
#
|
|
186
|
+
# @option options_hash [String, nil] :chdir working directory for the subprocess
|
|
187
|
+
#
|
|
188
|
+
# @option options_hash [Numeric, nil] :timeout execution timeout in seconds
|
|
189
|
+
#
|
|
190
|
+
# @option options_hash [Boolean] :merge (false) merge stdout into stderr
|
|
191
|
+
#
|
|
192
|
+
# @option options_hash [Hash] :env ({}) environment variable overrides
|
|
193
|
+
#
|
|
194
|
+
# @return [ProcessExecuter::ResultWithCapture] the process result with captured output
|
|
195
|
+
#
|
|
196
|
+
# @api private
|
|
197
|
+
def execute(*args, **options_hash)
|
|
198
|
+
git_cmd = build_git_cmd(args)
|
|
199
|
+
options = execution_options(**options_hash)
|
|
200
|
+
run_process_executer do
|
|
201
|
+
ProcessExecuter.run_with_capture(merged_env(options_hash), *git_cmd, **options)
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Build the ProcessExecuter options hash for a capturing run
|
|
206
|
+
#
|
|
207
|
+
# @param options_hash [Hash] the merged run options forwarded from {#run}
|
|
208
|
+
#
|
|
209
|
+
# @option options_hash [IO, nil] :in stdin IO object for the subprocess
|
|
210
|
+
#
|
|
211
|
+
# @option options_hash [#write, nil] :out stdout redirect target
|
|
212
|
+
#
|
|
213
|
+
# @option options_hash [#write, nil] :err stderr redirect target
|
|
214
|
+
#
|
|
215
|
+
# @option options_hash [String, nil] :chdir working directory for the subprocess
|
|
216
|
+
#
|
|
217
|
+
# @option options_hash [Numeric, nil] :timeout execution timeout in seconds
|
|
218
|
+
#
|
|
219
|
+
# @option options_hash [Boolean] :merge (false) merge stdout into stderr
|
|
220
|
+
#
|
|
221
|
+
# @return [Hash]
|
|
222
|
+
#
|
|
223
|
+
# @api private
|
|
224
|
+
def execution_options(**options_hash)
|
|
225
|
+
chdir = options_hash[:chdir] || :not_set
|
|
226
|
+
timeout_after = options_hash[:timeout]
|
|
227
|
+
merge_output = options_hash[:merge] || false
|
|
228
|
+
|
|
229
|
+
{ chdir:, timeout_after:, merge_output:, raise_errors: false }.tap do |options|
|
|
230
|
+
redirect_options(options_hash).each { |k, v| options[k] = v }
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Extract non-nil redirect options (`:in`, `:out`, `:err`) from `options`
|
|
235
|
+
#
|
|
236
|
+
# @param options [Hash] the options hash containing potential redirect options
|
|
237
|
+
#
|
|
238
|
+
# @option options [IO, nil] :in the input IO stream
|
|
239
|
+
#
|
|
240
|
+
# @option options [#write, nil] :out the output IO stream
|
|
241
|
+
#
|
|
242
|
+
# @option options [#write, nil] :err the error IO stream
|
|
243
|
+
#
|
|
244
|
+
# @return [Hash] the non-nil redirect options
|
|
245
|
+
#
|
|
246
|
+
# @api private
|
|
247
|
+
def redirect_options(options)
|
|
248
|
+
%i[in out err].filter_map do |key|
|
|
249
|
+
val = options[key]
|
|
250
|
+
[key, val] unless val.nil?
|
|
251
|
+
end.to_h
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# Post-process and return the stdout/stderr strings from the captured result,
|
|
255
|
+
# then log and raise on failure if required.
|
|
256
|
+
#
|
|
257
|
+
# @param result [ProcessExecuter::ResultWithCapture] the raw result
|
|
258
|
+
#
|
|
259
|
+
# @param options [Hash] the merged run options forwarded from {#run}
|
|
260
|
+
#
|
|
261
|
+
# @option options [Boolean] :normalize (false) normalize encoding of captured output
|
|
262
|
+
#
|
|
263
|
+
# @option options [Boolean] :chomp (false) chomp trailing newlines from captured output
|
|
264
|
+
#
|
|
265
|
+
# @option options [Numeric, nil] :timeout execution timeout used to construct the result
|
|
266
|
+
#
|
|
267
|
+
# @option options [Boolean] :raise_on_failure (true) raise {Git::FailedError} on non-zero exit
|
|
268
|
+
#
|
|
269
|
+
# @return [Git::CommandLine::Result]
|
|
270
|
+
#
|
|
271
|
+
# @raise [Git::FailedError] if the command failed and raise_on_failure is true
|
|
272
|
+
#
|
|
273
|
+
# @raise [Git::SignaledError] if the command was signaled
|
|
274
|
+
#
|
|
275
|
+
# @raise [Git::TimeoutError] if the command timed out
|
|
276
|
+
#
|
|
277
|
+
# @api private
|
|
278
|
+
def process_result(result, options)
|
|
279
|
+
command = result.command
|
|
280
|
+
processed_out, processed_err = post_process_output(result, options[:normalize], options[:chomp])
|
|
281
|
+
log_result(result, command, processed_out, processed_err)
|
|
282
|
+
command_line_result(
|
|
283
|
+
command, result, processed_out, processed_err, options[:timeout], options[:raise_on_failure]
|
|
284
|
+
)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Normalize and/or chomp the raw stdout and stderr strings.
|
|
288
|
+
#
|
|
289
|
+
# @param result [ProcessExecuter::ResultWithCapture] the raw result
|
|
290
|
+
#
|
|
291
|
+
# @param normalize [Boolean]
|
|
292
|
+
#
|
|
293
|
+
# @param chomp [Boolean]
|
|
294
|
+
#
|
|
295
|
+
# @return [Array<String>] two-element array: [processed_stdout, processed_stderr]
|
|
296
|
+
#
|
|
297
|
+
# @api private
|
|
298
|
+
def post_process_output(result, normalize, chomp)
|
|
299
|
+
[result.stdout, result.stderr].map do |raw_output|
|
|
300
|
+
output = raw_output.dup
|
|
301
|
+
output = output.lines.map { |l| Git::EncodingUtils.normalize_encoding(l) }.join if normalize
|
|
302
|
+
output.chomp! if chomp
|
|
303
|
+
output
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Git
|
|
4
|
+
module CommandLine
|
|
5
|
+
# The result of running a git command
|
|
6
|
+
#
|
|
7
|
+
# This object stores the Git command executed and its status, stdout, and stderr.
|
|
8
|
+
#
|
|
9
|
+
# @api public
|
|
10
|
+
#
|
|
11
|
+
class Result
|
|
12
|
+
# Create a Result object
|
|
13
|
+
#
|
|
14
|
+
# @example
|
|
15
|
+
# git_cmd = %w[git version]
|
|
16
|
+
# status = instance_double(ProcessExecuter::Result)
|
|
17
|
+
# stdout = "git version 2.39.1\n"
|
|
18
|
+
# stderr = ""
|
|
19
|
+
# result = Git::CommandLine::Result.new(git_cmd, status, stdout, stderr)
|
|
20
|
+
#
|
|
21
|
+
# @param git_cmd [Array<String>] the git command that was executed
|
|
22
|
+
#
|
|
23
|
+
# @param status [ProcessExecuter::Result] the process result object returned
|
|
24
|
+
# by `ProcessExecuter.run` or `ProcessExecuter.run_with_capture`.
|
|
25
|
+
# Responds to `timed_out?`, `signaled?`, and `success?`.
|
|
26
|
+
#
|
|
27
|
+
# @param stdout [String] the processed stdout of the process
|
|
28
|
+
#
|
|
29
|
+
# @param stderr [String] the processed stderr of the process
|
|
30
|
+
#
|
|
31
|
+
def initialize(git_cmd, status, stdout, stderr)
|
|
32
|
+
@git_cmd = git_cmd
|
|
33
|
+
@status = status
|
|
34
|
+
@stdout = stdout
|
|
35
|
+
@stderr = stderr
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# The git command that was executed
|
|
39
|
+
#
|
|
40
|
+
# @example
|
|
41
|
+
# git_cmd = %w[git version]
|
|
42
|
+
# result = Git::CommandLine::Result.new(git_cmd, nil, '', '')
|
|
43
|
+
# result.git_cmd #=> ["git", "version"]
|
|
44
|
+
#
|
|
45
|
+
# @return [Array<String>]
|
|
46
|
+
#
|
|
47
|
+
attr_reader :git_cmd
|
|
48
|
+
|
|
49
|
+
# The process result object returned by ProcessExecuter
|
|
50
|
+
#
|
|
51
|
+
# In practice this is a `ProcessExecuter::ResultWithCapture` (from
|
|
52
|
+
# {Git::CommandLine::Capturing}) or a `ProcessExecuter::Result` (from
|
|
53
|
+
# {Git::CommandLine::Streaming}). Both respond to `success?`, `timed_out?`,
|
|
54
|
+
# and `signaled?`.
|
|
55
|
+
#
|
|
56
|
+
# @example
|
|
57
|
+
# status = instance_double(ProcessExecuter::Result, success?: true)
|
|
58
|
+
# result = Git::CommandLine::Result.new(%w[git version], status, '', '')
|
|
59
|
+
# result.status == status #=> true
|
|
60
|
+
#
|
|
61
|
+
# @return [ProcessExecuter::Result]
|
|
62
|
+
#
|
|
63
|
+
attr_reader :status
|
|
64
|
+
|
|
65
|
+
# The output of the process
|
|
66
|
+
#
|
|
67
|
+
# @example
|
|
68
|
+
# stdout = "git version 2.39.1\n"
|
|
69
|
+
# result = Git::CommandLine::Result.new([], nil, stdout, '')
|
|
70
|
+
# result.stdout #=> "git version 2.39.1\n"
|
|
71
|
+
#
|
|
72
|
+
# @return [String]
|
|
73
|
+
#
|
|
74
|
+
attr_reader :stdout
|
|
75
|
+
|
|
76
|
+
# The error output of the process
|
|
77
|
+
#
|
|
78
|
+
# @example
|
|
79
|
+
# stderr = "Tag not found\n"
|
|
80
|
+
# result = Git::CommandLine::Result.new([], nil, '', stderr)
|
|
81
|
+
# result.stderr #=> "Tag not found\n"
|
|
82
|
+
#
|
|
83
|
+
# @return [String]
|
|
84
|
+
#
|
|
85
|
+
attr_reader :stderr
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|