Class: Git::ExecutionContext Private

Inherits:
Object
  • Object
show all
Defined in:
lib/git/execution_context.rb,
lib/git/execution_context/global.rb,
lib/git/execution_context/repository.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Base class for execution contexts that run git commands

An execution context bundles three concerns that together describe how and where a git command runs:

  1. Repository scope — the public accessors git_dir, git_work_dir, git_index_file, and git_ssh identify which repository git targets and which SSH wrapper to use. Their values are translated into GIT_* environment variable overrides by the private env_overrides method. A nil value unsets the variable (see Process.spawn semantics).

  2. CLI global options — the private global_opts method returns the array of git flags prepended to every invocation: --git-dir / --work-tree when those attributes are set, plus the static options in STATIC_GLOBAL_OPTS that ensure deterministic, script-friendly output.

  3. Execution defaultsCOMMAND_CAPTURING_ARG_DEFAULTS and COMMAND_STREAMING_ARG_DEFAULTS define the default values for I/O, encoding, and behavioral options (in:, out:, normalize:, timeout:, etc.) accepted by #command_capturing and #command_streaming.

Subclasses override the repository-scope accessors to supply context-specific values. The env_overrides and global_opts methods are implemented here and call those accessors, so subclasses do not need to override them directly.

Concrete subclasses:

  • Repository — for repository-bound commands (add, commit, …)
  • Global — for commands that do not require an existing repository (init, clone, version)

Examples:

Using a concrete subclass

context = Git::ExecutionContext::Global.new(binary_path: '/usr/local/bin/git2')
context.binary_path  #=> "/usr/local/bin/git2"

Direct Known Subclasses

Global, Repository

Defined Under Namespace

Classes: Global, Repository

Constant Summary collapse

COMMAND_CAPTURING_ARG_DEFAULTS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Default keyword arguments accepted by #command_capturing.

Derived from CommandLine::Capturing::RUN_OPTION_DEFAULTS with two overrides: normalize: true and chomp: true so callers receive clean UTF-8 strings by default. New options added to the CommandLine layer are automatically accepted here without requiring a coordinated edit.

timeout: nil is intentional — the global timeout from Git.config is applied at call-time so that changes to the config are respected.

Git::CommandLine::Capturing::RUN_OPTION_DEFAULTS
.merge(normalize: true, chomp: true)
.freeze
COMMAND_STREAMING_ARG_DEFAULTS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Default keyword arguments accepted by #command_streaming.

Identical to CommandLine::Streaming::RUN_OPTION_DEFAULTS. Defined here so callers interact with a stable constant on this class, and so that new options added to the CommandLine layer are automatically accepted.

Git::CommandLine::Streaming::RUN_OPTION_DEFAULTS.dup.freeze
STATIC_GLOBAL_OPTS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Static git global options applied to every invocation.

These ensure deterministic, script-friendly output regardless of the user's local git configuration.

%w[
  -c core.quotePath=true
  -c core.editor=false
  -c color.ui=false
  -c color.advice=false
  -c color.diff=false
  -c color.grep=false
  -c color.push=false
  -c color.remote=false
  -c color.showBranch=false
  -c color.status=false
  -c color.transport=false
].freeze

Instance Method Summary collapse

Constructor Details

#initialize(binary_path: :use_global_config, git_ssh: :use_global_config, logger: nil) ⇒ ExecutionContext

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Creates a new execution context

Parameters:

  • binary_path (String, :use_global_config) (defaults to: :use_global_config)

    path to the git binary

    Give :use_global_config (the default) to use Git::Base.config.binary_path.

    Passing nil raises ArgumentError — there is no "unset the binary" semantic.

  • git_ssh (String, nil, :use_global_config) (defaults to: :use_global_config)

    the SSH wrapper path

    Give nil to unset GIT_SSH, or :use_global_config (default) to use Git::Base.config.git_ssh.

  • logger (Logger, nil) (defaults to: nil)

    the logger to use in the CommandLine layer

    Give nil to use a null logger (Logger.new(nil)).

Raises:

  • (NotImplementedError)

    if called directly on Git::ExecutionContext rather than a subclass

  • (ArgumentError)

    if binary_path is nil



108
109
110
111
112
113
114
115
116
117
# File 'lib/git/execution_context.rb', line 108

def initialize(binary_path: :use_global_config, git_ssh: :use_global_config, logger: nil)
  if instance_of?(Git::ExecutionContext)
    raise NotImplementedError, 'Git::ExecutionContext is an abstract base class'
  end
  raise ArgumentError, 'binary_path must not be nil' if binary_path.nil?

  @binary_path = binary_path
  @git_ssh = git_ssh
  @logger = logger || Logger.new(nil)
end

Instance Method Details

#binary_pathString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the resolved git binary path for this context

:use_global_config is resolved to Git::Base.config.binary_path each time a command method is called, so runtime changes to Git::Base.config.binary_path are reflected per command invocation.

Examples:

With the default sentinel (resolves from Git::Base.config at call-time)

context = Git::ExecutionContext::Global.new
context.binary_path  #=> "git"

With an explicit path

context = Git::ExecutionContext::Global.new(binary_path: '/usr/local/bin/git2')
context.binary_path  #=> "/usr/local/bin/git2"

Returns:

  • (String)

    the resolved git binary path



173
174
175
176
177
# File 'lib/git/execution_context.rb', line 173

def binary_path
  return Git::Base.config.binary_path if @binary_path == :use_global_config

  @binary_path
end

#command_capturing(*args, **options_hash) ⇒ Git::CommandLineResult

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

Individual command classes (under Commands) can selectively expose :timeout and :env and other options to their callers by declaring them as execution options in their Arguments DSL definition and forwarding them to this method. See Commands::Clone#call for an example of a command that exposes :timeout.

Runs a git command and returns the result

By default, raises FailedError if the command exits with a non-zero status. Pass raise_on_failure: false to suppress this behavior.

Runs a git command and returns the result

Args should exclude the 'git' command itself and global options. Remember to splat the arguments if given as an array.

Examples:

Run git log

result = command_capturing('log', '--pretty=oneline')
result.stdout #=> "abc123 First commit\ndef456 Second commit\n"

Using an array of arguments

args = ['log', '--pretty=oneline']
result = command_capturing(*args)

Suppress raising on failure

result = command_capturing('show', 'nonexistent', raise_on_failure: false)
result.status.success? #=> false

Parameters:

  • args (Array<String>)

    the command and its arguments

  • options_hash (Hash)

    the options to pass to the command

Options Hash (**options_hash):

  • :in (IO, nil)

    the IO object to use as stdin, or nil to inherit the parent process stdin

    Must be a real IO object with a file descriptor.

  • :out (IO, String, #write, nil)

    the destination for captured stdout

  • :err (IO, String, #write, nil)

    the destination for captured stderr

  • :normalize (Boolean, nil) — default: true

    normalize the output encoding to UTF-8

  • :chomp (Boolean, nil) — default: true

    remove trailing newlines from the output

  • :merge (Boolean, nil) — default: false

    merge stdout and stderr into a single output

  • :chdir (String, nil)

    the directory to run the command in

  • :env (Hash)

    additional environment variable overrides for this command

  • :raise_on_failure (Boolean, nil) — default: true

    whether to raise on non-zero exit

  • :timeout (Numeric, nil)

    the maximum seconds to wait for the command to complete

    If timeout is nil, the global timeout from Config is used.

    If timeout is zero, the timeout will not be enforced.

    If the command times out, it is killed via a SIGKILL signal and Git::TimeoutError is raised.

    If the command does not respond to SIGKILL, it will hang this method.

Returns:

Raises:

  • (ArgumentError)

    if an unknown option is passed

  • (Git::FailedError)

    if the command failed (when raise_on_failure is true)

  • (Git::SignaledError)

    if the command was signaled

  • (Git::TimeoutError)

    if the command times out

  • (Git::ProcessIOError)

    if an exception was raised while collecting subprocess output

    The exception's result attribute is a CommandLineResult which will contain the result of the command including the exit status, stdout, and stderr.

Raises:

  • (ArgumentError)

See Also:



295
296
297
298
299
300
301
302
303
304
305
# File 'lib/git/execution_context.rb', line 295

def command_capturing(*, **options_hash)
  options_hash = COMMAND_CAPTURING_ARG_DEFAULTS.merge(options_hash)
  options_hash[:timeout] ||= Git.config.timeout

  extra_options = options_hash.keys - COMMAND_CAPTURING_ARG_DEFAULTS.keys
  raise ArgumentError, "Unknown options: #{extra_options.join(', ')}" if extra_options.any?

  env = options_hash.delete(:env)
  raise_on_failure = options_hash.delete(:raise_on_failure)
  command_line_capturing.run(*, raise_on_failure: raise_on_failure, env: env, **options_hash)
end

#command_streaming(*args, **options_hash) ⇒ Git::CommandLineResult

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Runs a git command using the streaming (non-capturing) execution path

Unlike #command_capturing, stdout is NOT buffered in memory. It is written only to the IO object provided via the out: option. Stderr is captured internally via a StringIO for error diagnostics.

Use this entry point when you want to stream large output (e.g. blob content from cat-file) without creating memory pressure.

Streams a git command's output to the provided IO object

Examples:

Stream blob content to a file

File.open('blob.bin', 'wb') do |f|
  command_streaming('cat-file', 'blob', 'HEAD:large_file.bin', out: f)
end

Parameters:

  • args (Array<String>)

    the git command and its arguments

  • options_hash (Hash)

    the options to pass to the command

Options Hash (**options_hash):

  • :in (IO, nil)

    the IO object to use as stdin, or nil to inherit the parent process stdin

    Must be a real IO object with a file descriptor.

  • :out (#write, nil)

    destination for streamed stdout

  • :err (#write, nil)

    an optional additional destination to receive stderr output in real time

    Stderr is always captured internally; when err: is supplied, writes are teed to both the internal buffer and this destination. result.stderr always reflects the internal capture.

  • :chdir (String, nil)

    the directory to run the command in

  • :env (Hash)

    additional environment variable overrides for this command

  • :raise_on_failure (Boolean, nil) — default: true

    whether to raise on non-zero exit

  • :timeout (Numeric, nil)

    the maximum seconds to wait for the command to complete

    If timeout is nil, the global timeout from Config is used.

    If timeout is zero, the timeout will not be enforced.

    If the command times out, it is killed via a SIGKILL signal and Git::TimeoutError is raised.

    If the command does not respond to SIGKILL, it will hang this method.

Returns:

  • (Git::CommandLineResult)

    the result of the command

    result.stdout will always be '' — stdout was streamed to out:.

    result.stderr contains any stderr output captured for diagnostics.

Raises:

Raises:

  • (ArgumentError)

See Also:



382
383
384
385
386
387
388
389
390
391
392
# File 'lib/git/execution_context.rb', line 382

def command_streaming(*, **options_hash)
  options_hash = COMMAND_STREAMING_ARG_DEFAULTS.merge(options_hash)
  options_hash[:timeout] ||= Git.config.timeout

  extra_options = options_hash.keys - COMMAND_STREAMING_ARG_DEFAULTS.keys
  raise ArgumentError, "Unknown options: #{extra_options.join(', ')}" if extra_options.any?

  env = options_hash.delete(:env)
  raise_on_failure = options_hash.delete(:raise_on_failure)
  command_line_streaming.run(*, raise_on_failure: raise_on_failure, env: env, **options_hash)
end

#git_dirString?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the GIT_DIR path for this context

nil means GIT_DIR will be explicitly unset in the child process (per Process.spawn semantics — unset is not the same as inherited). Subclasses override this to supply a repository-specific path.

Examples:

Base class returns nil; subclasses return the actual path

context = Git::ExecutionContext::Global.new
context.git_dir  #=> nil

Returns:

  • (String, nil)

    the GIT_DIR path, or nil to unset the variable



131
# File 'lib/git/execution_context.rb', line 131

def git_dir = nil

#git_index_fileString?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the GIT_INDEX_FILE path for this context

nil means GIT_INDEX_FILE will be explicitly unset in the child process.

Examples:

Base class returns nil; subclasses return the actual path

context = Git::ExecutionContext::Global.new
context.git_index_file  #=> nil

Returns:

  • (String, nil)

    the GIT_INDEX_FILE path, or nil to unset the variable



155
# File 'lib/git/execution_context.rb', line 155

def git_index_file = nil

#git_sshString?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the resolved GIT_SSH wrapper path for this context

:use_global_config is resolved to Git::Base.config.git_ssh each time a command method is called, so runtime changes to Git::Base.config.git_ssh are reflected per command invocation. nil means the variable will be explicitly unset.

Examples:

With the default sentinel (resolves from Git::Base.config at call-time)

context = Git::ExecutionContext::Global.new
context.git_ssh  #=> nil

With an explicit path

context = Git::ExecutionContext::Global.new(git_ssh: '/usr/bin/ssh-wrapper')
context.git_ssh  #=> "/usr/bin/ssh-wrapper"

Returns:

  • (String, nil)

    the resolved GIT_SSH wrapper path, or nil to unset



196
197
198
199
200
# File 'lib/git/execution_context.rb', line 196

def git_ssh
  return Git::Base.config.git_ssh if @git_ssh == :use_global_config

  @git_ssh
end

#git_versionGit::Version

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the installed git version

The result is memoized per instance.

Examples:

Get the installed git version

context = Git::ExecutionContext::Global.new
context.git_version  #=> #<Git::Version 2.42.0>

Returns:

Raises:



406
407
408
409
410
411
# File 'lib/git/execution_context.rb', line 406

def git_version
  @git_version ||= begin
    output = Git::Commands::Version.new(self).call.stdout
    Git::Version.parse(output)
  end
end

#git_work_dirString?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the GIT_WORK_TREE path for this context

nil means GIT_WORK_TREE will be explicitly unset in the child process.

Examples:

Base class returns nil; subclasses return the actual path

context = Git::ExecutionContext::Global.new
context.git_work_dir  #=> nil

Returns:

  • (String, nil)

    the GIT_WORK_TREE path, or nil to unset the variable



143
# File 'lib/git/execution_context.rb', line 143

def git_work_dir = nil