Class: Git::CommandLine

Inherits:
Object
  • Object
show all
Defined in:
lib/git/command_line.rb

Overview

Runs a git command and returns the result

Constant Summary collapse

RUN_ARGS =
{
  normalize: false,
  chomp: false,
  merge: false,
  out: nil,
  err: nil,
  chdir: nil,
  timeout: nil
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env, binary_path, global_opts, logger) ⇒ CommandLine

Create a Git::CommandLine object

Examples:

env = { 'GIT_DIR' => '/path/to/git/dir' }
binary_path = '/usr/bin/git'
global_opts = %w[--git-dir /path/to/git/dir]
logger = Logger.new(STDOUT)
cli = CommandLine.new(env, binary_path, global_opts, logger)
cli.run('version') #=> #<Git::CommandLineResult:0x00007f9b0c0b0e00

Parameters:

  • env (Hash<String, String>)

    environment variables to set

  • global_opts (Array<String>)

    global options to pass to git

  • logger (Logger)

    the logger to use



28
29
30
31
32
33
# File 'lib/git/command_line.rb', line 28

def initialize(env, binary_path, global_opts, logger)
  @env = env
  @binary_path = binary_path
  @global_opts = global_opts
  @logger = logger
end

Instance Attribute Details

#binary_pathString (readonly)

The path to the command line binary to run

Examples:

binary_path = '/usr/bin/git'
command_line = Git::CommandLine.new({}, binary_path, ['version'], Logger.new(STDOUT))
command_line.binary_path #=> '/usr/bin/git'

Returns:

  • (String)


62
63
64
# File 'lib/git/command_line.rb', line 62

def binary_path
  @binary_path
end

#envHash<String, String> (readonly)

Variables to set (or unset) in the git command's environment

Examples:

env = { 'GIT_DIR' => '/path/to/git/dir' }
command_line = Git::CommandLine.new(env, '/usr/bin/git', [], Logger.new(STDOUT))
command_line.env #=> { 'GIT_DIR' => '/path/to/git/dir' }

Returns:

  • (Hash<String, String>)

See Also:



49
50
51
# File 'lib/git/command_line.rb', line 49

def env
  @env
end

#global_optsArray<String> (readonly)

The global options to pass to git

These are options that are passed to git before the command name and arguments. For example, in git --git-dir /path/to/git/dir version, the global options are %w[--git-dir /path/to/git/dir].

Examples:

env = {}
global_opts = %w[--git-dir /path/to/git/dir]
logger = Logger.new(nil)
cli = CommandLine.new(env, '/usr/bin/git', global_opts, logger)
cli.global_opts #=> %w[--git-dir /path/to/git/dir]

Returns:

  • (Array<String>)


81
82
83
# File 'lib/git/command_line.rb', line 81

def global_opts
  @global_opts
end

#loggerLogger (readonly)

The logger to use for logging git commands and results

Examples:

env = {}
global_opts = %w[]
logger = Logger.new(STDOUT)
cli = CommandLine.new(env, '/usr/bin/git', global_opts, logger)
cli.logger == logger #=> true

Returns:

  • (Logger)


96
97
98
# File 'lib/git/command_line.rb', line 96

def logger
  @logger
end

Instance Method Details

#run(**options_hash) ⇒ Git::CommandLineResult

Execute a git command, wait for it to finish, and return the result

Non-option the command line arguements to pass to git. If you collect the command line arguments in an array, make sure you splat the array into the parameter list.

NORMALIZATION

The command output is returned as a Unicde string containing the binary output from the command. If the binary output is not valid UTF-8, the output will cause problems because the encoding will be invalid.

Normalization is a process that trys to convert the binary output to a valid UTF-8 string. It uses the rchardet gem to detect the encoding of the binary output and then converts it to UTF-8.

Normalization is not enabled by default. Pass normalize: true to Git::CommandLine#run to enable it. Normalization will only be performed on stdout and only if the out:` option is nil or is a StringIO object. If the out: option is set to a file or other IO object, the normalize option will be ignored.

Examples:

Run a command and return the output

cli.run('version') #=> "git version 2.39.1\n"

The args array should be splatted into the parameter list

args = %w[log -n 1 --oneline]
cli.run(*args) #=> "f5baa11 beginning of Ruby/Git project\n"

Run a command and return the chomped output

cli.run('version', chomp: true) #=> "git version 2.39.1"

Run a command and without normalizing the output

cli.run('version', normalize: false) #=> "git version 2.39.1\n"

Capture stdout in a temporary file

require 'tempfile'
tempfile = Tempfile.create('git') do |file|
  cli.run('version', out: file)
  file.rewind
  file.read #=> "git version 2.39.1\n"
end

Capture stderr in a StringIO object

require 'stringio'
stderr = StringIO.new
begin
  cli.run('log', 'nonexistent-branch', err: stderr)
rescue Git::FailedError => e
  stderr.string #=> "unknown revision or path not in the working tree.\n"
end

Parameters:

  • options_hash (Hash)

    the options to pass to the command

Options Hash (**options_hash):

  • :out (#write, nil)

    the object to write stdout to or nil to ignore stdout

    If this is a 'StringIO' object, then stdout_writer.string will be returned.

    In general, only specify a stdout_writer object when you want to redirect stdout to a file or some other object that responds to #write. The default behavior will return the output of the command.

  • :err (#write, nil)

    the object to write stderr to or nil to ignore stderr

    If this is a 'StringIO' object and merged_output is true, then stderr_writer.string will be merged into the output returned by this method.

  • :normalize (Boolean)

    whether to normalize the output of stdout and stderr

  • :chomp (Boolean)

    whether to chomp both stdout and stderr output

  • :merge (Boolean)

    whether to merge stdout and stderr in the string returned

  • :chdir (String, nil)

    the directory to run the command in

  • :timeout (Numeric, nil)

    the maximum seconds to wait for the command to complete

    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:



194
195
196
197
198
199
200
201
# File 'lib/git/command_line.rb', line 194

def run(*, **options_hash)
  options_hash = RUN_ARGS.merge(options_hash)
  extra_options = options_hash.keys - RUN_ARGS.keys
  raise ArgumentError, "Unknown options: #{extra_options.join(', ')}" if extra_options.any?

  result = run_with_capture(*, **options_hash)
  process_result(result, options_hash[:normalize], options_hash[:chomp], options_hash[:timeout])
end

#run_with_capture(*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.

Returns the result of running the command.

Returns:



207
208
209
210
211
212
213
# File 'lib/git/command_line.rb', line 207

def run_with_capture(*args, **options_hash)
  git_cmd = build_git_cmd(args)
  options = run_with_capture_options(**options_hash)
  ProcessExecuter.run_with_capture(env, *git_cmd, **options)
rescue ProcessExecuter::ProcessIOError => e
  raise Git::ProcessIOError.new(e.message), cause: e.exception.cause
end

#run_with_capture_options(**options_hash)



215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/git/command_line.rb', line 215

def run_with_capture_options(**options_hash)
  chdir = options_hash[:chdir] || :not_set
  timeout_after = options_hash[:timeout]
  out = options_hash[:out]
  err = options_hash[:err]
  merge_output = options_hash[:merge] || false

  { chdir:, timeout_after:, merge_output:, raise_errors: false }.tap do |options|
    options[:out] = out unless out.nil?
    options[:err] = err unless err.nil?
  end
end