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 Attribute Summary collapse

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::Config.instance.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::Config.instance.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



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

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 Attribute Details

#loggerLogger (readonly)

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 logger used by this context

Examples:

context = Git::ExecutionContext::Repository.new(git_dir: '/repo/.git', logger: my_logger)
context.logger  #=> my_logger

Returns:

  • (Logger)

    the logger instance; never nil



216
217
218
# File 'lib/git/execution_context.rb', line 216

def logger
  @logger
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::Config.instance.binary_path each time a command method is called, so runtime changes to Git.configure { |c| c.binary_path = ... } are reflected per command invocation.

Examples:

With the default sentinel (resolves from Git::Config.instance 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



176
177
178
179
180
# File 'lib/git/execution_context.rb', line 176

def binary_path
  return Git::Config.instance.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:



311
312
313
314
315
316
317
318
319
320
321
# File 'lib/git/execution_context.rb', line 311

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:



398
399
400
401
402
403
404
405
406
407
408
# File 'lib/git/execution_context.rb', line 398

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

#env_overrides(**additional_overrides) ⇒ Hash<String, String|nil>

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 a Hash of environment variable overrides for this context

Builds the standard git environment from the public accessor methods (#git_dir, #git_work_dir, #git_index_file, #git_ssh), then merges any per-call additional_overrides on top.

Per Process.spawn semantics, a value of nil unsets the variable.

Parameters:

  • additional_overrides (Hash<String, String|nil>)

    per-call overrides

Returns:

  • (Hash<String, String|nil>)

    the merged environment variable overrides



444
445
446
447
448
449
450
451
452
453
# File 'lib/git/execution_context.rb', line 444

def env_overrides(**additional_overrides)
  {
    'GIT_DIR' => git_dir,
    'GIT_WORK_TREE' => git_work_dir,
    'GIT_INDEX_FILE' => git_index_file,
    'GIT_SSH' => git_ssh,
    'GIT_EDITOR' => 'true',
    'LC_ALL' => 'en_US.UTF-8'
  }.merge(additional_overrides)
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



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

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



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

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::Config.instance.git_ssh each time a command method is called, so runtime changes to Git.configure { |c| c.git_ssh = ... } are reflected per command invocation. nil means the variable will be explicitly unset.

Examples:

With the default sentinel (resolves from Git::Config.instance 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



200
201
202
203
204
# File 'lib/git/execution_context.rb', line 200

def git_ssh
  return Git::Config.instance.git_ssh if @git_ssh == :use_global_config

  @git_ssh
end

#git_version(timeout: nil) ⇒ Git::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. Accepts an optional timeout used only when the version has not yet been fetched for this context.

Parameters:

  • timeout (Numeric, nil) (defaults to: nil)

    seconds to wait for git version; nil falls back to the global Config timeout; 0 disables the timeout entirely (the command runs until it completes or the process is killed)

Returns:

Raises:



423
424
425
426
427
428
# File 'lib/git/execution_context.rb', line 423

def git_version(timeout: nil)
  @git_version ||= begin
    call_opts = timeout.nil? ? {} : { timeout: timeout }
    Git::Version.parse(Git::Commands::Version.new(self).call(**call_opts).stdout)
  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



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

def git_work_dir = nil