diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 302c5eed..dd27bbd2 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -13,17 +13,17 @@ jobs: strategy: fail-fast: false matrix: - ruby: [2.7, 3.0, 3.1, 3.2] + ruby: [2.7, 3.0, 3.1, 3.2, head] operating-system: [ubuntu-latest] include: - - ruby: head - operating-system: ubuntu-latest - - ruby: truffleruby-head - operating-system: ubuntu-latest - - ruby: 2.7 - operating-system: windows-latest - ruby: jruby-head + operating-system: ubuntu-latest + - ruby: 3.0 operating-system: windows-latest + # - ruby: jruby-head + # operating-system: windows-latest + # - ruby: truffleruby-head + # operating-system: ubuntu-latest name: Ruby ${{ matrix.ruby }} on ${{ matrix.operating-system }} diff --git a/git.gemspec b/git.gemspec index 50b9c140..b67a16a0 100644 --- a/git.gemspec +++ b/git.gemspec @@ -27,6 +27,7 @@ Gem::Specification.new do |s| s.requirements = ['git 1.6.0.0, or greater'] s.add_runtime_dependency 'addressable', '~> 2.8' + s.add_runtime_dependency 'process_executer', '~> 0.6' s.add_runtime_dependency 'rchardet', '~> 1.8' s.add_development_dependency 'bump', '~> 0.10' diff --git a/lib/git/lib.rb b/lib/git/lib.rb index 35791c02..10628671 100644 --- a/lib/git/lib.rb +++ b/lib/git/lib.rb @@ -1,15 +1,13 @@ +require 'process_executer' +require 'stringio' require 'tempfile' require 'zlib' module Git - class GitExecuteError < StandardError end class Lib - - @@semaphore = Mutex.new - # The path to the Git working copy. The default is '"./.git"'. # # @return [Pathname] the path to the Git working copy. @@ -113,7 +111,7 @@ def clone(repository_url, directory, opts = {}) arr_opts << repository_url arr_opts << clone_dir - command('clone', arr_opts) + command_merged_output('clone', arr_opts) return_base_opts_from_clone(clone_dir, opts) end @@ -318,7 +316,19 @@ def process_commit_log_data(data) end def object_contents(sha, &block) - command('cat-file', '-p', sha, &block) + if block_given? + Tempfile.create do |file| + # If a block is given, write the output from the process to a temporary + # file and then yield the file to the block + # + command_with_redirect('cat-file', "-p", sha, stdout_writer: file, stderr_writer: file) + file.rewind + yield file + end + else + # If a block is not given, return stdout + command('cat-file', '-p', sha) + end end def ls_tree(sha) @@ -739,24 +749,24 @@ def stashes_all end def stash_save(message) - output = command('stash save', message) + output = command('stash', 'save', message) output =~ /HEAD is now at/ end def stash_apply(id = nil) if id - command('stash apply', id) + command('stash', 'apply', id) else - command('stash apply') + command('stash', 'apply') end end def stash_clear - command('stash clear') + command('stash', 'clear') end def stash_list - command('stash list') + command('stash', 'list') end def branch_new(branch) @@ -798,7 +808,7 @@ def merge(branch, message = nil, opts = {}) arr_opts << '--no-commit' if opts[:no_commit] arr_opts << '--no-ff' if opts[:no_ff] arr_opts << '-m' << message if message - arr_opts += [branch] + arr_opts += Array(branch) command('merge', arr_opts) end @@ -827,16 +837,17 @@ def unmerged def conflicts # :yields: file, your, their self.unmerged.each do |f| - your_tempfile = Tempfile.new("YOUR-#{File.basename(f)}") - your = your_tempfile.path - your_tempfile.close # free up file for git command process - command('show', ":2:#{f}", redirect: "> #{escape your}") - - their_tempfile = Tempfile.new("THEIR-#{File.basename(f)}") - their = their_tempfile.path - their_tempfile.close # free up file for git command process - command('show', ":3:#{f}", redirect: "> #{escape their}") - yield(f, your, their) + Tempfile.create("YOUR-#{File.basename(f)}") do |your| + command_with_redirect('show', ":2:#{f}", stdout_writer: your) + your.close + + Tempfile.create("THEIR-#{File.basename(f)}") do |their| + command_with_redirect('show', ":3:#{f}", stdout_writer: their) + their.close + + yield(f, your.path, their.path) + end + end end end @@ -909,7 +920,7 @@ def fetch(remote, opts) arr_opts << remote if remote arr_opts << opts[:ref] if opts[:ref] - command('fetch', arr_opts) + command_merged_output('fetch', arr_opts) end def push(remote, branch = 'master', opts = {}) @@ -963,15 +974,12 @@ def write_tree def commit_tree(tree, opts = {}) opts[:message] ||= "commit tree #{tree}" - t = Tempfile.new('commit-message') - t.write(opts[:message]) - t.close - arr_opts = [] arr_opts << tree arr_opts << '-p' << opts[:parent] if opts[:parent] arr_opts += [opts[:parents]].map { |p| ['-p', p] }.flatten if opts[:parents] - command('commit-tree', arr_opts, redirect: "< #{escape t.path}") + arr_opts << '-m' << opts[:message] + command('commit-tree', arr_opts) end def update_ref(branch, commit) @@ -1017,7 +1025,11 @@ def archive(sha, file = nil, opts = {}) arr_opts << "--remote=#{opts[:remote]}" if opts[:remote] arr_opts << sha arr_opts << '--' << opts[:path] if opts[:path] - command('archive', arr_opts, redirect: " > #{escape file}") + + f = File.open(file, 'wb') + command_with_redirect('archive', arr_opts, stdout_writer: f) + f.close + if opts[:add_gzip] file_content = File.read(file) Zlib::GzipWriter.open(file) do |gz| @@ -1054,11 +1066,6 @@ def self.warn_if_old_command(lib) private - # Systen ENV variables involved in the git commands. - # - # @return [] the names of the EVN variables involved in the git commands - ENV_VARIABLE_NAMES = ['GIT_DIR', 'GIT_WORK_TREE', 'GIT_INDEX_FILE', 'GIT_SSH'] - def command_lines(cmd, *opts) cmd_op = command(cmd, *opts) if cmd_op.encoding.name != "UTF-8" @@ -1069,91 +1076,201 @@ def command_lines(cmd, *opts) op.split("\n") end - # Takes the current git's system ENV variables and store them. - def store_git_system_env_variables - @git_system_env_variables = {} - ENV_VARIABLE_NAMES.each do |env_variable_name| - @git_system_env_variables[env_variable_name] = ENV[env_variable_name] - end + def env_overrides + { + 'GIT_DIR' => @git_dir, + 'GIT_WORK_TREE' => @git_work_dir, + 'GIT_INDEX_FILE' => @git_index_file, + 'GIT_SSH' => Git::Base.config.git_ssh + } end - # Takes the previously stored git's ENV variables and set them again on ENV. - def restore_git_system_env_variables - ENV_VARIABLE_NAMES.each do |env_variable_name| - ENV[env_variable_name] = @git_system_env_variables[env_variable_name] + DEFAULT_COMMAND_OPTS = { chomp: true } + + def command_opts(given_command_opts) + DEFAULT_COMMAND_OPTS.merge(given_command_opts).tap do |command_options| + command_options.keys.each do |k| + raise ArgumentError.new("Unsupported command option: #{k}") unless DEFAULT_COMMAND_OPTS.keys.include?(k) + end end end - # Sets git's ENV variables to the custom values for the current instance. - def set_custom_git_env_variables - ENV['GIT_DIR'] = @git_dir - ENV['GIT_WORK_TREE'] = @git_work_dir - ENV['GIT_INDEX_FILE'] = @git_index_file - ENV['GIT_SSH'] = Git::Base.config.git_ssh + def global_opts + Array.new.tap do |global_opts| + global_opts << "--git-dir=#{@git_dir}" if !@git_dir.nil? + global_opts << "--work-tree=#{@git_work_dir}" if !@git_work_dir.nil? + global_opts << %w[-c core.quotePath=true] + global_opts << %w[-c color.ui=false] + end.flatten end - # Runs a block inside an environment with customized ENV variables. - # It restores the ENV after execution. - # - # @param [Proc] block block to be executed within the customized environment - def with_custom_env_variables(&block) - @@semaphore.synchronize do - store_git_system_env_variables() - set_custom_git_env_variables() - return block.call() - end - ensure - restore_git_system_env_variables() + def build_command(global_opts, cmd, opts) + [ + Git::Base.config.binary_path, + *Array(global_opts).flatten, + cmd, + *Array(opts).flatten + ].map { |e| e.to_s } end - def command(cmd, *opts, &block) - Git::Lib.warn_if_old_command(self) + def command_with_redirect(cmd, *opts, stdout_writer: nil, stderr_writer: nil) + given_command_opts = opts.last.is_a?(Hash) ? opts.pop : {} + command_opts = command_opts(given_command_opts) + + git_cmd = build_command(global_opts, cmd, opts).flatten + + stdout_writers = [] + stdout_writers << stdout_writer if stdout_writer + + stderr_writers = [] + stderr_writers << stderr_writer if stderr_writer - command_opts = { chomp: true, redirect: '' } - if opts.last.is_a?(Hash) - command_opts.merge!(opts.pop) + begin + stdout_pipe = ProcessExecuter::MonitoredPipe.new(*stdout_writers, chunk_size: 10_000) + stderr_pipe = ProcessExecuter::MonitoredPipe.new(*stderr_writers, chunk_size: 10_000) + + spawn_opts = { out: stdout_pipe, err: stderr_pipe } + + status = ProcessExecuter.spawn(env_overrides, *git_cmd, **spawn_opts) + ensure + stdout_pipe&.close + stderr_pipe&.close end - command_opts.keys.each do |k| - raise ArgumentError.new("Unsupported option: #{k}") unless [:chomp, :redirect].include?(k) + + warn "status is a #{status.class}" unless status.is_a?(Process::Status) + warn "stdout_pipe exception #{stdout_pipe.exception.inspect}, status is #{status.inspect}, exitstatus is #{status.exitstatus}" if stdout_pipe.exception + warn "stderr_pipe exception #{stderr_pipe.exception.inspect}, status is #{status.inspect}, exitstatus is #{status.exitstatus}" if stderr_pipe.exception + + if @logger + @logger.info("#{git_cmd} exited with status #{status.inspect}, output redirected") end - global_opts = [] - global_opts << "--git-dir=#{@git_dir}" if !@git_dir.nil? - global_opts << "--work-tree=#{@git_work_dir}" if !@git_work_dir.nil? - global_opts << %w[-c core.quotePath=true] - global_opts << %w[-c color.ui=false] + if stdout_pipe.exception || stderr_pipe.exception + message = if stdout_pipe.exception + message = "#{git_cmd} stdout_pipe.exception=#{stdout_pipe.exception.inspect}, status=#{status.inspect}" + elsif stderr_pipe.exception + message = "#{git_cmd} stderr_pipe.exception=#{stderr_pipe.exception.inspect}, status=#{status.inspect}" + end + raise Git::GitExecuteError, message if message + end + + raise Git::GitExecuteError, "#{git_cmd} exited with signal #{status.inspect}" if status.signaled? - opts = [opts].flatten.map {|s| escape(s) }.join(' ') + exitstatus = status.exitstatus + + raise Git::GitExecuteError, "#{git_cmd}\n#{status.inspect}\nstderr: #{stderr}" if + exitstatus > 1 || (exitstatus == 1 && stderr != '') + end - global_opts = global_opts.flatten.map {|s| escape(s) }.join(' ') + def command_merged_output(cmd, *opts) + given_command_opts = opts.last.is_a?(Hash) ? opts.pop : {} + command_opts = command_opts(given_command_opts) - git_cmd = "#{Git::Base.config.binary_path} #{global_opts} #{cmd} #{opts} #{command_opts[:redirect]} 2>&1" + git_cmd = build_command(global_opts, cmd, opts).flatten - output = nil + stdout_writer = StringIO.new + stdout_writers = [stdout_writer] - command_thread = nil; + begin + stdout_pipe = ProcessExecuter::MonitoredPipe.new(*stdout_writers, chunk_size: 10_000) + stderr_pipe = stdout_pipe - exitstatus = nil + spawn_opts = { out: stdout_pipe, err: stderr_pipe } - with_custom_env_variables do - command_thread = Thread.new do - output = run_command(git_cmd, &block) - exitstatus = $?.exitstatus + status = ProcessExecuter.spawn(env_overrides, *git_cmd, **spawn_opts) + ensure + stdout_pipe&.close + end + + warn "status is a #{status.class}" unless status.is_a?(Process::Status) + warn "stdout_pipe exception #{stdout_pipe.exception.inspect}, status is #{status.inspect}, exitstatus is #{status.exitstatus}" if stdout_pipe.exception + warn "stderr_pipe exception #{stderr_pipe.exception.inspect}, status is #{status.inspect}, exitstatus is #{status.exitstatus}" if stderr_pipe.exception + + if @logger + @logger.info("#{git_cmd} exited with status #{status.inspect}, stdout and stderr merged") + end + + if stdout_pipe.exception || stderr_pipe.exception + message = if stdout_pipe.exception + message = "#{git_cmd} stdout_pipe.exception=#{stdout_pipe.exception.inspect}, status=#{status.inspect}" + elsif stderr_pipe.exception + message = "#{git_cmd} stderr_pipe.exception=#{stderr_pipe.exception.inspect}, status=#{status.inspect}" end - command_thread.join + raise Git::GitExecuteError, message if message end + raise Git::GitExecuteError, "#{git_cmd} exited with signal #{status.inspect}" if status.signaled? + + stdout = stdout_writer.string.lines.map { |l| Git::EncodingUtils.normalize_encoding(l) }.join + stderr = '' + exitstatus = status.exitstatus + if @logger - @logger.info(git_cmd) - @logger.debug(output) + @logger.debug("Merged stdout and stderr: #{stdout}") end - raise Git::GitExecuteError, "#{git_cmd}:#{output}" if - exitstatus > 1 || (exitstatus == 1 && output != '') + raise Git::GitExecuteError, "#{git_cmd}\n#{status.inspect}\nstderr: #{stderr}" if + exitstatus > 1 || (exitstatus == 1 && stderr != '') + + command_opts[:chomp] ? stdout.chomp : stdout + end + + def command(cmd, *opts) + given_command_opts = opts.last.is_a?(Hash) ? opts.pop : {} + command_opts = command_opts(given_command_opts) + + git_cmd = build_command(global_opts, cmd, opts).flatten + + stdout_writer = StringIO.new + stdout_writers = [stdout_writer] + + stderr_writer = StringIO.new + stderr_writers = [stderr_writer] - output.chomp! if output && command_opts[:chomp] && !block_given? + begin + stdout_pipe = ProcessExecuter::MonitoredPipe.new(*stdout_writers, chunk_size: 10_000) + stderr_pipe = ProcessExecuter::MonitoredPipe.new(*stderr_writers, chunk_size: 10_000) - output + spawn_opts = { out: stdout_pipe, err: stderr_pipe } + + status = ProcessExecuter.spawn(env_overrides, *git_cmd, **spawn_opts) + ensure + stdout_pipe&.close + stderr_pipe&.close + end + + warn "status is a #{status.class}" unless status.is_a?(Process::Status) + warn "stdout_pipe exception #{stdout_pipe.exception.inspect}, status is #{status.inspect}, exitstatus is #{status.exitstatus}" if stdout_pipe.exception + warn "stderr_pipe exception #{stderr_pipe.exception.inspect}, status is #{status.inspect}, exitstatus is #{status.exitstatus}" if stderr_pipe.exception + + if @logger + @logger.info("#{git_cmd} exited with status #{status.inspect}") + end + + if stdout_pipe.exception || stderr_pipe.exception + message = if stdout_pipe.exception + message = "#{git_cmd} stdout_pipe.exception=#{stdout_pipe.exception.inspect}, status=#{status.inspect}" + elsif stderr_pipe.exception + message = "#{git_cmd} stderr_pipe.exception=#{stderr_pipe.exception.inspect}, status=#{status.inspect}" + end + raise Git::GitExecuteError, message if message + end + + raise Git::GitExecuteError, "#{git_cmd} exited with signal #{status.inspect}" if status.signaled? + + stdout = stdout_writer.string.lines.map { |l| Git::EncodingUtils.normalize_encoding(l) }.join + stderr = stderr_writer.string.lines.map { |l| Git::EncodingUtils.normalize_encoding(l) }.join + exitstatus = status.exitstatus + + if @logger + @logger.debug("stdout: #{stdout}") + @logger.debug("stderr: #{stderr}") + end + + raise Git::GitExecuteError, "#{git_cmd}\n#{status.inspect}\nstderr: #{stderr}" if + exitstatus > 1 || (exitstatus == 1 && stderr != '') + + command_opts[:chomp] ? stdout.chomp : stdout end # Takes the diff command line output (as Array) and parse it into a Hash @@ -1211,31 +1328,5 @@ def log_path_options(opts) arr_opts << '--' << opts[:path_limiter] if opts[:path_limiter] arr_opts end - - def run_command(git_cmd, &block) - return IO.popen(git_cmd, &block) if block_given? - - `#{git_cmd}`.lines.map { |l| Git::EncodingUtils.normalize_encoding(l) }.join - end - - def escape(s) - windows_platform? ? escape_for_windows(s) : escape_for_sh(s) - end - - def escape_for_sh(s) - "'#{s && s.to_s.gsub('\'','\'"\'"\'')}'" - end - - def escape_for_windows(s) - # Escape existing double quotes in s and then wrap the result with double quotes - escaped_string = s.to_s.gsub('"','\\"') - %Q{"#{escaped_string}"} - end - - def windows_platform? - # Check if on Windows via RUBY_PLATFORM (CRuby) and RUBY_DESCRIPTION (JRuby) - win_platform_regex = /mingw|mswin/ - RUBY_PLATFORM =~ win_platform_regex || RUBY_DESCRIPTION =~ win_platform_regex - end end end diff --git a/tests/test_helper.rb b/tests/test_helper.rb index c92f1892..d384fc58 100644 --- a/tests/test_helper.rb +++ b/tests/test_helper.rb @@ -6,6 +6,9 @@ require "git" +$stdout.sync = true +$stderr.sync = true + class Test::Unit::TestCase TEST_ROOT = File.expand_path(__dir__) @@ -92,21 +95,6 @@ def append_file(name, contents) end end - # Runs a block inside an environment with customized ENV variables. - # It restores the ENV after execution. - # - # @param [Proc] block block to be executed within the customized environment - # - def with_custom_env_variables(&block) - saved_env = {} - begin - Git::Lib::ENV_VARIABLE_NAMES.each { |k| saved_env[k] = ENV[k] } - return block.call - ensure - Git::Lib::ENV_VARIABLE_NAMES.each { |k| ENV[k] = saved_env[k] } - end - end - # Assert that the expected command line args are generated for a given Git::Lib method # # This assertion generates an empty git repository and then runs calls @@ -149,7 +137,7 @@ def with_custom_env_variables(&block) # # @return [void] # - def assert_command_line(expected_command_line, git_cmd, git_cmd_args) + def assert_command_line(expected_command_line, git_cmd, git_cmd_args, method: :command) actual_command_line = nil in_temp_dir do |path| @@ -159,7 +147,7 @@ def assert_command_line(expected_command_line, git_cmd, git_cmd_args) yield(git) if block_given? # Mock the Git::Lib#command method to capture the actual command line args - git.lib.define_singleton_method(:command) do |cmd, *opts, &block| + git.lib.define_singleton_method(method) do |cmd, *opts, &block| actual_command_line = [cmd, *opts.flatten] end diff --git a/tests/units/test_archive.rb b/tests/units/test_archive.rb index 68ef3a65..7f0e1eed 100644 --- a/tests/units/test_archive.rb +++ b/tests/units/test_archive.rb @@ -11,53 +11,79 @@ def setup end def teardown - @tempfiles.clear + @tempfiles.each { |f| File.delete(f) } end def tempfile - tempfile_object = Tempfile.new('archive-test') - @tempfiles << tempfile_object # prevent deletion until teardown - tempfile_object.close # close to avoid locking from git processes - tempfile_object.path + Dir::Tmpname.create('test-archive') { }.tap do |f| + @tempfiles << f + end end def test_archive f = @git.archive('v2.6', tempfile) assert(File.exist?(f)) + end + def test_archive_object f = @git.object('v2.6').archive(tempfile) # writes to given file assert(File.exist?(f)) + end + def test_archive_object_with_no_filename f = @git.object('v2.6').archive # returns path to temp file assert(File.exist?(f)) + end + def test_archive_to_tar f = @git.object('v2.6').archive(nil, :format => 'tar') # returns path to temp file assert(File.exist?(f)) - lines = Minitar::Input.open(f).each.to_a.map(&:full_name) + lines = [] + Minitar::Input.open(f) do |tar_reader| + lines = tar_reader.to_a.map(&:full_name) + end + assert_match(%r{ex_dir/}, lines[1]) assert_match(/ex_dir\/ex\.txt/, lines[2]) assert_match(/example\.txt/, lines[3]) + end + def test_archive_to_zip f = @git.object('v2.6').archive(tempfile, :format => 'zip') assert(File.file?(f)) + end + def test_archive_to_tgz f = @git.object('v2.6').archive(tempfile, :format => 'tgz', :prefix => 'test/') assert(File.exist?(f)) - lines = Minitar::Input.open(Zlib::GzipReader.new(File.open(f, 'rb'))).each.to_a.map(&:full_name) + lines = [] + File.open(f, 'rb') do |file_reader| + Zlib::GzipReader.open(file_reader) do |gz_reader| + Minitar::Input.open(gz_reader) do |tar_reader| + lines = tar_reader.to_a.map(&:full_name) + end + end + end + assert_match(%r{test/}, lines[1]) assert_match(%r{test/ex_dir/ex\.txt}, lines[3]) + end + def test_archive_with_prefix_and_path f = @git.object('v2.6').archive(tempfile, :format => 'tar', :prefix => 'test/', :path => 'ex_dir/') assert(File.exist?(f)) - lines = Minitar::Input.open(f).each.to_a.map(&:full_name) + tar_file = Minitar::Input.open(f) + lines = tar_file.each.to_a.map(&:full_name) + tar_file.close assert_match(%r{test/}, lines[1]) assert_match(%r{test/ex_dir/ex\.txt}, lines[3]) + end + def test_archive_branch f = @git.remote('working').branch('master').archive(tempfile, :format => 'tgz') assert(File.exist?(f)) end - end diff --git a/tests/units/test_commit_with_gpg.rb b/tests/units/test_commit_with_gpg.rb index f9e8bb28..c8b2b2a4 100644 --- a/tests/units/test_commit_with_gpg.rb +++ b/tests/units/test_commit_with_gpg.rb @@ -10,42 +10,42 @@ def setup def test_with_configured_gpg_keyid Dir.mktmpdir do |dir| git = Git.init(dir) - actual_cmd = nil - git.lib.define_singleton_method(:run_command) do |git_cmd, &block| - actual_cmd = git_cmd - `true` + actual_opts = nil + git.lib.define_singleton_method(:command) do |cmd, *opts, &block| + actual_opts = opts.flatten + '' end message = 'My commit message' git.commit(message, gpg_sign: true) - assert_match(/commit.*--gpg-sign['"]/, actual_cmd) + assert(actual_opts.include?('--gpg-sign')) end end def test_with_specific_gpg_keyid Dir.mktmpdir do |dir| git = Git.init(dir) - actual_cmd = nil - git.lib.define_singleton_method(:run_command) do |git_cmd, &block| - actual_cmd = git_cmd - `true` + actual_opts = nil + git.lib.define_singleton_method(:command) do |cmd, *opts, &block| + actual_opts = opts.flatten + '' end message = 'My commit message' git.commit(message, gpg_sign: 'keykeykey') - assert_match(/commit.*--gpg-sign=keykeykey['"]/, actual_cmd) + assert(actual_opts.include?('--gpg-sign=keykeykey')) end end def test_disabling_gpg_sign Dir.mktmpdir do |dir| git = Git.init(dir) - actual_cmd = nil - git.lib.define_singleton_method(:run_command) do |git_cmd, &block| - actual_cmd = git_cmd - `true` + actual_opts = nil + git.lib.define_singleton_method(:command) do |cmd, *opts, &block| + actual_opts = opts.flatten + '' end message = 'My commit message' git.commit(message, no_gpg_sign: true) - assert_match(/commit.*--no-gpg-sign['"]/, actual_cmd) + assert(actual_opts.include?('--no-gpg-sign')) end end diff --git a/tests/units/test_config.rb b/tests/units/test_config.rb index 35208d24..b60e6c83 100644 --- a/tests/units/test_config.rb +++ b/tests/units/test_config.rb @@ -38,34 +38,32 @@ def test_set_config_with_custom_file end def test_env_config - with_custom_env_variables do - begin - assert_equal(Git::Base.config.binary_path, 'git') - assert_equal(Git::Base.config.git_ssh, nil) + begin + assert_equal(Git::Base.config.binary_path, 'git') + assert_equal(Git::Base.config.git_ssh, nil) - ENV['GIT_PATH'] = '/env/bin' - ENV['GIT_SSH'] = '/env/git/ssh' + ENV['GIT_PATH'] = '/env/bin' + ENV['GIT_SSH'] = '/env/git/ssh' - assert_equal(Git::Base.config.binary_path, '/env/bin/git') - assert_equal(Git::Base.config.git_ssh, '/env/git/ssh') + assert_equal(Git::Base.config.binary_path, '/env/bin/git') + assert_equal(Git::Base.config.git_ssh, '/env/git/ssh') - Git.configure do |config| - config.binary_path = '/usr/bin/git' - config.git_ssh = '/path/to/ssh/script' - end + Git.configure do |config| + config.binary_path = '/usr/bin/git' + config.git_ssh = '/path/to/ssh/script' + end - assert_equal(Git::Base.config.binary_path, '/usr/bin/git') - assert_equal(Git::Base.config.git_ssh, '/path/to/ssh/script') + assert_equal(Git::Base.config.binary_path, '/usr/bin/git') + assert_equal(Git::Base.config.git_ssh, '/path/to/ssh/script') - @git.log - ensure - ENV['GIT_SSH'] = nil - ENV['GIT_PATH'] = nil + @git.log + ensure + ENV['GIT_SSH'] = nil + ENV['GIT_PATH'] = nil - Git.configure do |config| - config.binary_path = nil - config.git_ssh = nil - end + Git.configure do |config| + config.binary_path = nil + config.git_ssh = nil end end end diff --git a/tests/units/test_lib.rb b/tests/units/test_lib.rb index 5c1409e6..4f470ba2 100644 --- a/tests/units/test_lib.rb +++ b/tests/units/test_lib.rb @@ -91,13 +91,14 @@ def test_checkout_with_start_point assert(@lib.reset(nil, hard: true)) # to get around worktree status on windows actual_cmd = nil - @lib.define_singleton_method(:run_command) do |git_cmd, &block| - actual_cmd = git_cmd - super(git_cmd, &block) + @lib.define_singleton_method(:command) do |cmd, *options| + actual_cmd = [cmd, *options].flatten + super(cmd, *options) end assert(@lib.checkout('test_checkout_b2', {new_branch: true, start_point: 'master'})) - assert_match(%r/checkout ['"]-b['"] ['"]test_checkout_b2['"] ['"]master['"]/, actual_cmd) + + assert_equal(['checkout', '-b', 'test_checkout_b2', 'master'], actual_cmd) end # takes parameters, returns array of appropriate commit objects @@ -127,41 +128,27 @@ def test_log_commits assert_equal(20, a.size) end - def test_environment_reset - with_custom_env_variables do - ENV['GIT_DIR'] = '/my/git/dir' - ENV['GIT_WORK_TREE'] = '/my/work/tree' - ENV['GIT_INDEX_FILE'] = 'my_index' - - @lib.log_commits :count => 10 - - assert_equal(ENV['GIT_DIR'], '/my/git/dir') - assert_equal(ENV['GIT_WORK_TREE'], '/my/work/tree') - assert_equal(ENV['GIT_INDEX_FILE'],'my_index') - end - end - def test_git_ssh_from_environment_is_passed_to_binary - with_custom_env_variables do - begin - Dir.mktmpdir do |dir| - output_path = File.join(dir, 'git_ssh_value') - binary_path = File.join(dir, 'git.bat') # .bat so it works in Windows too - Git::Base.config.binary_path = binary_path - File.open(binary_path, 'w') { |f| - f << "echo \"my/git-ssh-wrapper\" > #{output_path}" - } - FileUtils.chmod(0700, binary_path) - @lib.checkout('something') - assert(File.read(output_path).include?("my/git-ssh-wrapper")) - end - ensure - Git.configure do |config| - config.binary_path = nil - config.git_ssh = nil - end - end + saved_binary_path = Git::Base.config.binary_path + saved_git_ssh = Git::Base.config.git_ssh + + Dir.mktmpdir do |dir| + output_path = File.join(dir, 'git_ssh_value') + binary_path = File.join(dir, 'my_own_git.bat') # .bat so it works in Windows too + Git::Base.config.binary_path = binary_path + Git::Base.config.git_ssh = 'GIT_SSH_VALUE' + File.write(binary_path, <<~SCRIPT) + #!/bin/sh + set > "#{output_path}" + SCRIPT + FileUtils.chmod(0700, binary_path) + @lib.checkout('something') + env = File.read(output_path) + assert_match(/^GIT_SSH=(["']?)GIT_SSH_VALUE\1$/, env, 'GIT_SSH should be set in the environment') end + ensure + Git::Base.config.binary_path = saved_binary_path + Git::Base.config.git_ssh = saved_git_ssh end def test_revparse diff --git a/tests/units/test_logger.rb b/tests/units/test_logger.rb index 931728ab..4c4e6c86 100644 --- a/tests/units/test_logger.rb +++ b/tests/units/test_logger.rb @@ -28,10 +28,10 @@ def test_logger logc = File.read(log.path) - expected_log_entry = /INFO -- : git (?.*?) branch ['"]-a['"]/ + expected_log_entry = /INFO -- : .*git.*branch.*-a/ assert_match(expected_log_entry, logc, missing_log_entry) - expected_log_entry = /DEBUG -- : cherry/ + expected_log_entry = /DEBUG -- : stdout: cherry/ assert_match(expected_log_entry, logc, missing_log_entry) end @@ -46,7 +46,7 @@ def test_logging_at_info_level_should_not_show_debug_messages logc = File.read(log.path) - expected_log_entry = /INFO -- : git (?.*?) branch ['"]-a['"]/ + expected_log_entry = /INFO -- : .*git.*branch.*-a/ assert_match(expected_log_entry, logc, missing_log_entry) expected_log_entry = /DEBUG -- : cherry/ diff --git a/tests/units/test_remotes.rb b/tests/units/test_remotes.rb index d51451e3..f60042ae 100644 --- a/tests/units/test_remotes.rb +++ b/tests/units/test_remotes.rb @@ -123,28 +123,28 @@ def test_fetch_cmd_with_no_args expected_command_line = ['fetch', '--', 'origin'] git_cmd = :fetch git_cmd_args = [] - assert_command_line(expected_command_line, git_cmd, git_cmd_args) + assert_command_line(expected_command_line, git_cmd, git_cmd_args, method: :command_merged_output) end def test_fetch_cmd_with_origin_and_branch expected_command_line = ['fetch', '--depth', '2', '--', 'origin', 'master'] git_cmd = :fetch git_cmd_args = ['origin', ref: 'master', depth: '2'] - assert_command_line(expected_command_line, git_cmd, git_cmd_args) + assert_command_line(expected_command_line, git_cmd, git_cmd_args, method: :command_merged_output) end def test_fetch_cmd_with_all expected_command_line = ['fetch', '--all'] git_cmd = :fetch git_cmd_args = [all: true] - assert_command_line(expected_command_line, git_cmd, git_cmd_args) + assert_command_line(expected_command_line, git_cmd, git_cmd_args, method: :command_merged_output) end def test_fetch_cmd_with_all_with_other_args expected_command_line = ['fetch', '--all', '--force', '--depth', '2'] git_cmd = :fetch git_cmd_args = [all: true, force: true, depth: '2'] - assert_command_line(expected_command_line, git_cmd, git_cmd_args) + assert_command_line(expected_command_line, git_cmd, git_cmd_args, method: :command_merged_output) end def test_fetch_command_injection pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy