Skip to content

Commit 918487c

Browse files
committed
chore: run git commands using RubyGit::CommandLine.run
1 parent 8a5b204 commit 918487c

File tree

7 files changed

+177
-199
lines changed

7 files changed

+177
-199
lines changed

lib/ruby_git.rb

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
# frozen_string_literal: true
22

3-
require 'ruby_git/command_line'
4-
require 'ruby_git/encoding_normalizer'
5-
require 'ruby_git/errors'
6-
require 'ruby_git/git_binary'
7-
require 'ruby_git/version'
8-
require 'ruby_git/working_tree'
3+
require_relative 'ruby_git/command_line'
4+
require_relative 'ruby_git/encoding_normalizer'
5+
require_relative 'ruby_git/errors'
6+
require_relative 'ruby_git/version'
7+
require_relative 'ruby_git/working_tree'
98

109
require 'logger'
1110

@@ -28,22 +27,6 @@
2827
# @api public
2928
#
3029
module RubyGit
31-
@git = RubyGit::GitBinary.new
32-
33-
class << self
34-
# Information about the git binary used by the RubyGit gem
35-
#
36-
# Use this object to set the path to the git binary to use or to see the
37-
# path being used.
38-
#
39-
# @example Setting the git binary path
40-
# RubyGit.git.path = '/usr/local/bin/git'
41-
#
42-
# @return [RubyGit::GitBinary] the git binary object
43-
#
44-
attr_reader :git
45-
end
46-
4730
@logger = Logger.new(nil)
4831

4932
class << self
@@ -66,6 +49,27 @@ class << self
6649
attr_accessor :logger
6750
end
6851

52+
@binary_path = 'git'
53+
54+
class << self
55+
# The path to the git binary used by the RubyGit gem
56+
#
57+
# * If the path is a command name, the command is search for in the PATH.
58+
#
59+
# * If the path is a relative path, it relative to the current directory (not
60+
# recommended as this will change as the current directory is changed).
61+
#
62+
# * If the path is an absolute path, it is used as is.
63+
#
64+
# @example
65+
# RubyGit.binary_path
66+
# => "git"
67+
#
68+
# @return [String]
69+
#
70+
attr_accessor :binary_path
71+
end
72+
6973
# Create an empty Git repository under the root working tree `path`
7074
#
7175
# If the repository already exists, it will not be overwritten.
@@ -141,4 +145,21 @@ def self.open(working_tree_path)
141145
def self.clone(repository_url, to_path: '')
142146
RubyGit::WorkingTree.clone(repository_url, to_path:)
143147
end
148+
149+
# The version of git referred to by the path
150+
#
151+
# @example for version 2.28.0
152+
# RubyGit.binary_version #=> [2, 28, 0]
153+
#
154+
# @return [Array<Integer>] an array of integers representing the version.
155+
#
156+
# @raise [RuntimeError] if path was not set via `path=` and either PATH is not set
157+
# or git was not found on the path.
158+
#
159+
def self.binary_version
160+
command = %w[version]
161+
options = { out: StringIO.new, err: StringIO.new }
162+
version_string = RubyGit::CommandLine.run(*command, **options).stdout[/\d+\.\d+(\.\d+)+/]
163+
version_string.split('.').collect(&:to_i)
164+
end
144165
end

lib/ruby_git/command_line.rb

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,93 @@
33
require_relative 'command_line/options'
44
require_relative 'command_line/result'
55
require_relative 'command_line/runner'
6+
7+
module RubyGit
8+
# Run git commands via the command line
9+
#
10+
# @api public
11+
#
12+
module CommandLine
13+
# Run a git command
14+
#
15+
# @example A simple example
16+
# RubyGit::CommandLine.run('version') #=> outputs "git version 2.30.2\n" to stdout
17+
#
18+
# @example Capture stdout
19+
# command = %w[version]
20+
# options = { out: StringIO.new }
21+
# result = RubyGit::CommandLine.run(*command, **options) #=> #<Process::Status: pid 21742 exit 0>
22+
# result.stdout #=> "git version 2.30.2\n"
23+
#
24+
# @example A more complex example
25+
# command = %w[rev-parse --show-toplevel]
26+
# options = { chdir: working_tree_path, chomp: true, out: StringIO.new, err: StringIO.new }
27+
# RubyGit::CommandLine.run(*command, **options).stdout #=> "/path/to/working/tree"
28+
#
29+
# @param args [Array<String>] the git command and it arguments
30+
# @param repository_path [String] the path to the git repository
31+
# @param working_tree_path [String] the path to the working tree
32+
# @param options [Hash<Symbol, Object>] options to pass to the command line runner
33+
#
34+
# @return [RubyGit::CommandLine::Result] the result of running the command
35+
#
36+
# @raise [RubyGit::Error] if the command fails for any of the following reasons
37+
# @raise [RubyGit::FailedError] if the command returns with non-zero exitstatus
38+
# @raise [RubyGit::TimeoutError] if the command times out
39+
# @raise [RubyGit::SignaledError] if the command terminates due to an uncaught signal
40+
# @raise [RubyGit::ProcessIOError] if an exception is raised while collecting subprocess output
41+
#
42+
def self.run(*args, repository_path: nil, working_tree_path: nil, **options)
43+
runner = RubyGit::CommandLine::Runner.new(
44+
env,
45+
binary_path,
46+
global_options(repository_path:, working_tree_path:),
47+
logger
48+
)
49+
runner.call(*args, **options)
50+
end
51+
52+
# The environment variables that will be set for all git commands
53+
# @return [Hash<String, String>]
54+
# @api private
55+
def self.env
56+
{
57+
'GIT_DIR' => nil,
58+
'GIT_WORK_TREE' => nil,
59+
'GIT_INDEX_FILE' => nil,
60+
# 'GIT_SSH' => Git::Base.config.git_ssh,
61+
'LC_ALL' => 'en_US.UTF-8'
62+
}
63+
end
64+
65+
# The path to the git binary
66+
# @return [String]
67+
# @api private
68+
def self.binary_path = RubyGit.binary_path
69+
70+
# The global options that will be set for all git commands
71+
# @return [Array<String>]
72+
# @api private
73+
def self.global_options(repository_path:, working_tree_path:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
74+
[].tap do |global_opts|
75+
global_opts << "--git-dir=#{repository_path}" unless repository_path.nil?
76+
global_opts << "--work-tree=#{working_tree_path}" unless working_tree_path.nil?
77+
global_opts << '-c' << 'core.quotePath=true'
78+
global_opts << '-c' << 'color.ui=false'
79+
global_opts << '-c' << 'color.advice=false'
80+
global_opts << '-c' << 'color.diff=false'
81+
global_opts << '-c' << 'color.grep=false'
82+
global_opts << '-c' << 'color.push=false'
83+
global_opts << '-c' << 'color.remote=false'
84+
global_opts << '-c' << 'color.showBranch=false'
85+
global_opts << '-c' << 'color.status=false'
86+
global_opts << '-c' << 'color.transport=false'
87+
end
88+
end
89+
90+
# The logger to use for logging git commands
91+
# @return [Logger]
92+
# @api private
93+
def self.logger = RubyGit.logger
94+
end
95+
end

lib/ruby_git/command_line/runner.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class Runner
2323
# cli.run('version') #=> #<RubyGit::CommandLineResult:0x00007f9b0c0b0e00
2424
#
2525
# @param env [Hash<String, String>] environment variables to set
26+
# @param binary_path [String] the path to the git binary
2627
# @param global_options [Array<String>] global options to pass to git
2728
# @param logger [Logger] the logger to use
2829
#

lib/ruby_git/git_binary.rb

Lines changed: 0 additions & 90 deletions
This file was deleted.

lib/ruby_git/working_tree.rb

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ class WorkingTree
3939
def self.init(working_tree_path)
4040
raise RubyGit::Error, "Path '#{working_tree_path}' not valid." unless File.directory?(working_tree_path)
4141

42-
command = [RubyGit.git.path.to_s, 'init']
43-
_out, err, status = Open3.capture3(*command, chdir: working_tree_path)
44-
raise RubyGit::Error, err unless status.success?
42+
command = ['init']
43+
options = { chdir: working_tree_path, out: StringIO.new, err: StringIO.new }
44+
RubyGit::CommandLine.run(*command, **options)
4545

46-
WorkingTree.new(working_tree_path)
46+
new(working_tree_path)
4747
end
4848

4949
# Open an existing Git working tree that contains working_tree_path
@@ -94,16 +94,15 @@ def self.open(working_tree_path)
9494
# `to_path` will be created if it does not exist. An error is raised if `to_path` exists and
9595
# not an empty directory.
9696
#
97-
# @raise [RubyGit::Error] if (1) `repository_url` is not valid or does not point to a valid repository OR
97+
# @raise [RubyGit::FailedError] if (1) `repository_url` is not valid or does not point to a valid repository OR
9898
# (2) `to_path` is not an empty directory.
9999
#
100100
# @return [RubyGit::WorkingTree] the Git working tree checked out from the cloned repository
101101
#
102102
def self.clone(repository_url, to_path: '')
103-
command = [RubyGit.git.path.to_s, 'clone', '--', repository_url, to_path]
104-
_out, err, status = Open3.capture3(*command)
105-
raise RubyGit::Error, err unless status.success?
106-
103+
command = ['clone', '--', repository_url, to_path]
104+
options = { out: StringIO.new, err: StringIO.new }
105+
RubyGit::CommandLine.run(*command, **options)
107106
new(to_path)
108107
end
109108

@@ -121,18 +120,20 @@ def initialize(working_tree_path)
121120

122121
# Find the root path of a Git working tree containing `path`
123122
#
124-
# @raise [RubyGit::Error] if the path is not in a Git working tree
123+
# @raise [RubyGit::FailedError] if the path is not in a Git working tree
125124
#
126125
# @return [String] the root path of the Git working tree containing `path`
127126
#
128127
# @api private
129128
#
130129
def root_path(working_tree_path)
131-
command = [RubyGit.git.path.to_s, 'rev-parse', '--show-toplevel']
132-
out, err, status = Open3.capture3(*command, chdir: working_tree_path)
133-
raise RubyGit::Error, err unless status.success?
134-
135-
out.chomp
130+
command = %w[rev-parse --show-toplevel]
131+
options = { chdir: working_tree_path, chomp: true, out: StringIO.new, err: StringIO.new }
132+
RubyGit::CommandLine.run(*command, **options).stdout
136133
end
134+
135+
# def run(*command, **options)
136+
# RubyGit::CommandLine.run(*command, working_tree_path: path, **options)
137+
# end
137138
end
138139
end

0 commit comments

Comments
 (0)