Skip to content

Commit 6267250

Browse files
The proper way to do subprocess I/O is to do less.
1 parent 6dac85a commit 6267250

File tree

1 file changed

+11
-46
lines changed

1 file changed

+11
-46
lines changed

lib/svn2git/migration.rb

+11-46
Original file line numberDiff line numberDiff line change
@@ -396,57 +396,22 @@ def run_command(cmd, exit_on_error=true, printout_output=false)
396396
log "Running command: #{cmd}\n"
397397

398398
ret = ''
399-
@stdin_queue ||= Queue.new
400-
401-
# We need to fetch input from the user to pass through to the underlying sub-process. We'll constantly listen
402-
# for input and place any received values on a queue for consumption by a pass-through thread that will forward
403-
# the contents to the underlying sub-process's stdin pipe.
404-
@stdin_thread ||= Thread.new do
405-
loop { @stdin_queue << $stdin.gets.chomp }
406-
end
407-
408399
# Open4 forks, which JRuby doesn't support. But JRuby added a popen4-compatible method on the IO class,
409-
# so we can use that instead.
410-
IO.popen("2>&1 #{cmd}") do |output|
411-
threads = []
412-
413-
threads << Thread.new(output) do |output|
414-
# git-svn seems to do all of its prompting for user input via STDERR. When it prompts for input, it will
415-
# not terminate the line with a newline character, so we can't split the input up by newline. It will,
416-
# however, use a space to separate the user input from the prompt. So we split on word boundaries here
417-
# while draining STDERR.
418-
output.each(' ') do |word|
419-
ret << word
420-
421-
if printout_output
422-
$stdout.print word
423-
else
424-
log word
425-
end
400+
# so we can use that instead. Each command inherits the parent stdin so there's no need to manage it or close it before reading stdout.
401+
IO.popen(cmd, :in => 0, :err => [:child, :out]) do |output|
402+
if printout_output || @options[:verbose]
403+
# Normally, `IO.copy_stream()` would be used, but we need compatibility with interactive supprocesses.
404+
while c = output.getc do
405+
ret << c
406+
putc c
407+
STDOUT.flush # `putc`'s `STDOUT` is buffered by default, and `STDOUT.sync = true` is the wrong solution because it's a global hack.
426408
end
409+
else # Save output only without logging or printing to stdout.
410+
ret = output.read
427411
end
428-
429-
# Simple pass-through thread to take anything the user types via STDIN and passes it through to the
430-
# sub-process's stdin pipe.
431-
Thread.new do
432-
loop do
433-
user_reply = @stdin_queue.pop
434-
435-
# nil is our cue to stop looping (pun intended).
436-
break if user_reply.nil?
437-
438-
stdin.puts user_reply
439-
stdin.close
440-
end
441-
end
442-
443-
threads.each(&:join)
444-
445-
# Push nil to the stdin_queue to gracefully exit the STDIN pass-through thread.
446-
@stdin_queue << nil
447412
end
448413

449-
if exit_on_error && $?.exitstatus != 0
414+
if exit_on_error && !$?.success?
450415
$stderr.puts "command failed:\n#{cmd}"
451416
exit -1
452417
end

0 commit comments

Comments
 (0)