git 1.19.1 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/git/lib.rb CHANGED
@@ -1,14 +1,15 @@
1
- require 'git/failed_error'
1
+ require 'git/command_line'
2
+ require 'git/errors'
2
3
  require 'logger'
4
+ require 'pp'
5
+ require 'process_executer'
6
+ require 'stringio'
3
7
  require 'tempfile'
4
8
  require 'zlib'
5
9
  require 'open3'
6
10
 
7
11
  module Git
8
12
  class Lib
9
-
10
- @@semaphore = Mutex.new
11
-
12
13
  # The path to the Git working copy. The default is '"./.git"'.
13
14
  #
14
15
  # @return [Pathname] the path to the Git working copy.
@@ -37,14 +38,23 @@ module Git
37
38
 
38
39
  # Create a new Git::Lib object
39
40
  #
40
- # @param [Git::Base, Hash] base An object that passes in values for
41
- # @git_work_dir, @git_dir, and @git_index_file
41
+ # @overload initialize(base, logger)
42
+ #
43
+ # @param base [Hash] the hash containing paths to the Git working copy,
44
+ # the Git repository directory, and the Git index file.
45
+ #
46
+ # @option base [Pathname] :working_directory
47
+ # @option base [Pathname] :repository
48
+ # @option base [Pathname] :index
42
49
  #
43
- # @param [Logger] logger
50
+ # @param [Logger] logger
44
51
  #
45
- # @option base [Pathname] :working_directory
46
- # @option base [Pathname] :repository
47
- # @option base [Pathname] :index
52
+ # @overload initialize(base, logger)
53
+ #
54
+ # @param base [#dir, #repo, #index] an object with methods to get the Git worktree (#dir),
55
+ # the Git repository directory (#repo), and the Git index file (#index).
56
+ #
57
+ # @param [Logger] logger
48
58
  #
49
59
  def initialize(base = nil, logger = nil)
50
60
  @git_dir = nil
@@ -79,22 +89,34 @@ module Git
79
89
  command('init', *arr_opts)
80
90
  end
81
91
 
82
- # tries to clone the given repo
92
+ # Clones a repository into a newly created directory
83
93
  #
84
- # accepts options:
85
- # :bare:: no working directory
86
- # :branch:: name of branch to track (rather than 'master')
87
- # :depth:: the number of commits back to pull
88
- # :filter:: specify partial clone
89
- # :origin:: name of remote (same as remote)
90
- # :path:: directory where the repo will be cloned
91
- # :remote:: name of remote (rather than 'origin')
92
- # :recursive:: after the clone is created, initialize all submodules within, using their default settings.
94
+ # @param [String] repository_url the URL of the repository to clone
95
+ # @param [String, nil] directory the directory to clone into
93
96
  #
94
- # TODO - make this work with SSH password or auth_key
97
+ # If nil, the repository is cloned into a directory with the same name as
98
+ # the repository.
99
+ #
100
+ # @param [Hash] opts the options for this command
101
+ #
102
+ # @option opts [Boolean] :bare (false) if true, clone as a bare repository
103
+ # @option opts [String] :branch the branch to checkout
104
+ # @option opts [String, Array] :config one or more configuration options to set
105
+ # @option opts [Integer] :depth the number of commits back to pull
106
+ # @option opts [String] :filter specify partial clone
107
+ # @option opts [String] :mirror set up a mirror of the source repository
108
+ # @option opts [String] :origin the name of the remote
109
+ # @option opts [String] :path an optional prefix for the directory parameter
110
+ # @option opts [String] :remote the name of the remote
111
+ # @option opts [Boolean] :recursive after the clone is created, initialize all submodules within, using their default settings
112
+ # @option opts [Numeric, nil] :timeout the number of seconds to wait for the command to complete
113
+ #
114
+ # See {Git::Lib#command} for more information about :timeout
95
115
  #
96
116
  # @return [Hash] the options to pass to {Git::Base.new}
97
117
  #
118
+ # @todo make this work with SSH password or auth_key
119
+ #
98
120
  def clone(repository_url, directory, opts = {})
99
121
  @path = opts[:path] || '.'
100
122
  clone_dir = opts[:path] ? File.join(@path, directory) : directory
@@ -114,7 +136,7 @@ module Git
114
136
  arr_opts << repository_url
115
137
  arr_opts << clone_dir
116
138
 
117
- command('clone', *arr_opts)
139
+ command('clone', *arr_opts, timeout: opts[:timeout])
118
140
 
119
141
  return_base_opts_from_clone(clone_dir, opts)
120
142
  end
@@ -142,7 +164,7 @@ module Git
142
164
  match_data = output.match(%r{^ref: refs/heads/(?<default_branch>[^\t]+)\tHEAD$})
143
165
  return match_data[:default_branch] if match_data
144
166
 
145
- raise 'Unable to determine the default branch'
167
+ raise Git::UnexpectedResultError, 'Unable to determine the default branch'
146
168
  end
147
169
 
148
170
  ## READ COMMANDS ##
@@ -337,7 +359,19 @@ module Git
337
359
  end
338
360
 
339
361
  def object_contents(sha, &block)
340
- command('cat-file', '-p', sha, &block)
362
+ if block_given?
363
+ Tempfile.create do |file|
364
+ # If a block is given, write the output from the process to a temporary
365
+ # file and then yield the file to the block
366
+ #
367
+ command('cat-file', "-p", sha, out: file, err: file)
368
+ file.rewind
369
+ yield file
370
+ end
371
+ else
372
+ # If a block is not given, return stdout
373
+ command('cat-file', '-p', sha)
374
+ end
341
375
  end
342
376
 
343
377
  def ls_tree(sha)
@@ -395,7 +429,7 @@ module Git
395
429
  def branches_all
396
430
  command_lines('branch', '-a').map do |line|
397
431
  match_data = line.match(BRANCH_LINE_REGEXP)
398
- raise GitExecuteError, 'Unexpected branch line format' unless match_data
432
+ raise Git::UnexpectedResultError, 'Unexpected branch line format' unless match_data
399
433
  next nil if match_data[:not_a_branch] || match_data[:detached_ref]
400
434
  [
401
435
  match_data[:refname],
@@ -474,11 +508,15 @@ module Git
474
508
  grep_opts.push('--', *opts[:path_limiter]) if opts[:path_limiter].is_a?(Array)
475
509
 
476
510
  hsh = {}
477
- command_lines('grep', *grep_opts).each do |line|
478
- if m = /(.*?)\:(\d+)\:(.*)/.match(line)
479
- hsh[m[1]] ||= []
480
- hsh[m[1]] << [m[2].to_i, m[3]]
511
+ begin
512
+ command_lines('grep', *grep_opts).each do |line|
513
+ if m = /(.*?)\:(\d+)\:(.*)/.match(line)
514
+ hsh[m[1]] ||= []
515
+ hsh[m[1]] << [m[2].to_i, m[3]]
516
+ end
481
517
  end
518
+ rescue Git::FailedError => e
519
+ raise unless e.result.status.exitstatus == 1 && e.result.stderr == ''
482
520
  end
483
521
  hsh
484
522
  end
@@ -536,18 +574,52 @@ module Git
536
574
  diff_as_hash('diff-index', treeish)
537
575
  end
538
576
 
577
+ # List all files that are in the index
578
+ #
579
+ # @param location [String] the location to list the files from
580
+ #
581
+ # @return [Hash<String, Hash>] a hash of files in the index
582
+ # * key: file [String] the file path
583
+ # * value: file_info [Hash] the file information containing the following keys:
584
+ # * :path [String] the file path
585
+ # * :mode_index [String] the file mode
586
+ # * :sha_index [String] the file sha
587
+ # * :stage [String] the file stage
588
+ #
539
589
  def ls_files(location=nil)
540
590
  location ||= '.'
541
- hsh = {}
542
- command_lines('ls-files', '--stage', location).each do |line|
543
- (info, file) = line.split("\t")
544
- (mode, sha, stage) = info.split
545
- if file.start_with?('"') && file.end_with?('"')
546
- file = Git::EscapedPath.new(file[1..-2]).unescape
591
+ {}.tap do |files|
592
+ command_lines('ls-files', '--stage', location).each do |line|
593
+ (info, file) = line.split("\t")
594
+ (mode, sha, stage) = info.split
595
+ files[unescape_quoted_path(file)] = {
596
+ :path => file, :mode_index => mode, :sha_index => sha, :stage => stage
597
+ }
547
598
  end
548
- hsh[file] = {:path => file, :mode_index => mode, :sha_index => sha, :stage => stage}
549
599
  end
550
- hsh
600
+ end
601
+
602
+ # Unescape a path if it is quoted
603
+ #
604
+ # Git commands that output paths (e.g. ls-files, diff), will escape unusual
605
+ # characters.
606
+ #
607
+ # @example
608
+ # lib.unescape_if_quoted('"quoted_file_\\342\\230\\240"') # => 'quoted_file_☠'
609
+ # lib.unescape_if_quoted('unquoted_file') # => 'unquoted_file'
610
+ #
611
+ # @param path [String] the path to unescape if quoted
612
+ #
613
+ # @return [String] the unescaped path if quoted otherwise the original path
614
+ #
615
+ # @api private
616
+ #
617
+ def unescape_quoted_path(path)
618
+ if path.start_with?('"') && path.end_with?('"')
619
+ Git::EscapedPath.new(path[1..-2]).unescape
620
+ else
621
+ path
622
+ end
551
623
  end
552
624
 
553
625
  def ls_remote(location=nil, opts={})
@@ -568,9 +640,12 @@ module Git
568
640
  end
569
641
 
570
642
  def ignored_files
571
- command_lines('ls-files', '--others', '-i', '--exclude-standard')
643
+ command_lines('ls-files', '--others', '-i', '--exclude-standard').map { |f| unescape_quoted_path(f) }
572
644
  end
573
645
 
646
+ def untracked_files
647
+ command_lines('ls-files', '--others', '--exclude-standard', chdir: @git_work_dir)
648
+ end
574
649
 
575
650
  def config_remote(name)
576
651
  hsh = {}
@@ -638,18 +713,20 @@ module Git
638
713
  command('config', '--global', name, value)
639
714
  end
640
715
 
641
- # updates the repository index using the working directory content
642
- #
643
- # lib.add('path/to/file')
644
- # lib.add(['path/to/file1','path/to/file2'])
645
- # lib.add(:all => true)
716
+
717
+ # Update the index from the current worktree to prepare the for the next commit
646
718
  #
647
- # options:
648
- # :all => true
649
- # :force => true
719
+ # @example
720
+ # lib.add('path/to/file')
721
+ # lib.add(['path/to/file1','path/to/file2'])
722
+ # lib.add(:all => true)
650
723
  #
651
- # @param [String,Array] paths files paths to be added to the repository
724
+ # @param [String, Array<String>] paths files to be added to the repository (relative to the worktree root)
652
725
  # @param [Hash] options
726
+ #
727
+ # @option options [Boolean] :all Add, modify, and remove index entries to match the worktree
728
+ # @option options [Boolean] :force Allow adding otherwise ignored files
729
+ #
653
730
  def add(paths='.',options={})
654
731
  arr_opts = []
655
732
 
@@ -675,6 +752,19 @@ module Git
675
752
  command('rm', *arr_opts)
676
753
  end
677
754
 
755
+ # Returns true if the repository is empty (meaning it has no commits)
756
+ #
757
+ # @return [Boolean]
758
+ #
759
+ def empty?
760
+ command('rev-parse', '--verify', 'HEAD')
761
+ false
762
+ rescue Git::FailedError => e
763
+ raise unless e.result.status.exitstatus == 128 &&
764
+ e.result.stderr == 'fatal: Needed a single revision'
765
+ true
766
+ end
767
+
678
768
  # Takes the commit message with the options and executes the commit command
679
769
  #
680
770
  # accepts options:
@@ -865,16 +955,17 @@ module Git
865
955
 
866
956
  def conflicts # :yields: file, your, their
867
957
  self.unmerged.each do |f|
868
- your_tempfile = Tempfile.new("YOUR-#{File.basename(f)}")
869
- your = your_tempfile.path
870
- your_tempfile.close # free up file for git command process
871
- command('show', ":2:#{f}", redirect: "> #{escape your}")
872
-
873
- their_tempfile = Tempfile.new("THEIR-#{File.basename(f)}")
874
- their = their_tempfile.path
875
- their_tempfile.close # free up file for git command process
876
- command('show', ":3:#{f}", redirect: "> #{escape their}")
877
- yield(f, your, their)
958
+ Tempfile.create("YOUR-#{File.basename(f)}") do |your|
959
+ command('show', ":2:#{f}", out: your)
960
+ your.close
961
+
962
+ Tempfile.create("THEIR-#{File.basename(f)}") do |their|
963
+ command('show', ":3:#{f}", out: their)
964
+ their.close
965
+
966
+ yield(f, your.path, their.path)
967
+ end
968
+ end
878
969
  end
879
970
  end
880
971
 
@@ -915,7 +1006,7 @@ module Git
915
1006
  opts = opts.last.instance_of?(Hash) ? opts.last : {}
916
1007
 
917
1008
  if (opts[:a] || opts[:annotate]) && !(opts[:m] || opts[:message])
918
- raise "Can not create an [:a|:annotate] tag without the precense of [:m|:message]."
1009
+ raise ArgumentError, 'Cannot create an annotated tag without a message.'
919
1010
  end
920
1011
 
921
1012
  arr_opts = []
@@ -948,7 +1039,7 @@ module Git
948
1039
  arr_opts << remote if remote
949
1040
  arr_opts << opts[:ref] if opts[:ref]
950
1041
 
951
- command('fetch', *arr_opts)
1042
+ command('fetch', *arr_opts, merge: true)
952
1043
  end
953
1044
 
954
1045
  def push(remote = nil, branch = nil, opts = nil)
@@ -988,10 +1079,11 @@ module Git
988
1079
  end
989
1080
  end
990
1081
 
991
- def pull(remote = nil, branch = nil)
1082
+ def pull(remote = nil, branch = nil, opts = {})
992
1083
  raise ArgumentError, "You must specify a remote if a branch is specified" if remote.nil? && !branch.nil?
993
1084
 
994
1085
  arr_opts = []
1086
+ arr_opts << '--allow-unrelated-histories' if opts[:allow_unrelated_histories]
995
1087
  arr_opts << remote if remote
996
1088
  arr_opts << branch if branch
997
1089
  command('pull', *arr_opts)
@@ -1001,7 +1093,13 @@ module Git
1001
1093
  head = File.join(@git_dir, 'refs', 'tags', tag_name)
1002
1094
  return File.read(head).chomp if File.exist?(head)
1003
1095
 
1004
- command('show-ref', '--tags', '-s', tag_name)
1096
+ begin
1097
+ command('show-ref', '--tags', '-s', tag_name)
1098
+ rescue Git::FailedError => e
1099
+ raise unless e.result.status.exitstatus == 1 && e.result.stderr == ''
1100
+
1101
+ ''
1102
+ end
1005
1103
  end
1006
1104
 
1007
1105
  def repack
@@ -1026,15 +1124,12 @@ module Git
1026
1124
 
1027
1125
  def commit_tree(tree, opts = {})
1028
1126
  opts[:message] ||= "commit tree #{tree}"
1029
- t = Tempfile.new('commit-message')
1030
- t.write(opts[:message])
1031
- t.close
1032
-
1033
1127
  arr_opts = []
1034
1128
  arr_opts << tree
1035
1129
  arr_opts << '-p' << opts[:parent] if opts[:parent]
1036
- arr_opts += Array(opts[:parents]).map { |p| ['-p', p] }.flatten if opts[:parents]
1037
- command('commit-tree', *arr_opts, redirect: "< #{escape t.path}")
1130
+ Array(opts[:parents]).each { |p| arr_opts << '-p' << p } if opts[:parents]
1131
+ arr_opts << '-m' << opts[:message]
1132
+ command('commit-tree', *arr_opts)
1038
1133
  end
1039
1134
 
1040
1135
  def update_ref(ref, commit)
@@ -1080,7 +1175,11 @@ module Git
1080
1175
  arr_opts << "--remote=#{opts[:remote]}" if opts[:remote]
1081
1176
  arr_opts << sha
1082
1177
  arr_opts << '--' << opts[:path] if opts[:path]
1083
- command('archive', *arr_opts, redirect: " > #{escape file}")
1178
+
1179
+ f = File.open(file, 'wb')
1180
+ command('archive', *arr_opts, out: f)
1181
+ f.close
1182
+
1084
1183
  if opts[:add_gzip]
1085
1184
  file_content = File.read(file)
1086
1185
  Zlib::GzipWriter.open(file) do |gz|
@@ -1115,7 +1214,7 @@ module Git
1115
1214
  end
1116
1215
 
1117
1216
  def required_command_version
1118
- [1, 6]
1217
+ [2, 28]
1119
1218
  end
1120
1219
 
1121
1220
  def meets_required_version?
@@ -1133,11 +1232,6 @@ module Git
1133
1232
 
1134
1233
  private
1135
1234
 
1136
- # Systen ENV variables involved in the git commands.
1137
- #
1138
- # @return [<String>] the names of the EVN variables involved in the git commands
1139
- ENV_VARIABLE_NAMES = ['GIT_DIR', 'GIT_WORK_TREE', 'GIT_INDEX_FILE', 'GIT_SSH']
1140
-
1141
1235
  def command_lines(cmd, *opts, chdir: nil)
1142
1236
  cmd_op = command(cmd, *opts, chdir: chdir)
1143
1237
  if cmd_op.encoding.name != "UTF-8"
@@ -1148,84 +1242,90 @@ module Git
1148
1242
  op.split("\n")
1149
1243
  end
1150
1244
 
1151
- # Takes the current git's system ENV variables and store them.
1152
- def store_git_system_env_variables
1153
- @git_system_env_variables = {}
1154
- ENV_VARIABLE_NAMES.each do |env_variable_name|
1155
- @git_system_env_variables[env_variable_name] = ENV[env_variable_name]
1156
- end
1245
+ def env_overrides
1246
+ {
1247
+ 'GIT_DIR' => @git_dir,
1248
+ 'GIT_WORK_TREE' => @git_work_dir,
1249
+ 'GIT_INDEX_FILE' => @git_index_file,
1250
+ 'GIT_SSH' => Git::Base.config.git_ssh
1251
+ }
1157
1252
  end
1158
1253
 
1159
- # Takes the previously stored git's ENV variables and set them again on ENV.
1160
- def restore_git_system_env_variables
1161
- ENV_VARIABLE_NAMES.each do |env_variable_name|
1162
- ENV[env_variable_name] = @git_system_env_variables[env_variable_name]
1254
+ def global_opts
1255
+ Array.new.tap do |global_opts|
1256
+ global_opts << "--git-dir=#{@git_dir}" if !@git_dir.nil?
1257
+ global_opts << "--work-tree=#{@git_work_dir}" if !@git_work_dir.nil?
1258
+ global_opts << '-c' << 'core.quotePath=true'
1259
+ global_opts << '-c' << 'color.ui=false'
1260
+ global_opts << '-c' << 'color.advice=false'
1261
+ global_opts << '-c' << 'color.diff=false'
1262
+ global_opts << '-c' << 'color.grep=false'
1263
+ global_opts << '-c' << 'color.push=false'
1264
+ global_opts << '-c' << 'color.remote=false'
1265
+ global_opts << '-c' << 'color.showBranch=false'
1266
+ global_opts << '-c' << 'color.status=false'
1267
+ global_opts << '-c' << 'color.transport=false'
1163
1268
  end
1164
1269
  end
1165
1270
 
1166
- # Sets git's ENV variables to the custom values for the current instance.
1167
- def set_custom_git_env_variables
1168
- ENV['GIT_DIR'] = @git_dir
1169
- ENV['GIT_WORK_TREE'] = @git_work_dir
1170
- ENV['GIT_INDEX_FILE'] = @git_index_file
1171
- ENV['GIT_SSH'] = Git::Base.config.git_ssh
1271
+ def command_line
1272
+ @command_line ||=
1273
+ Git::CommandLine.new(env_overrides, Git::Base.config.binary_path, global_opts, @logger)
1172
1274
  end
1173
1275
 
1174
- # Runs a block inside an environment with customized ENV variables.
1175
- # It restores the ENV after execution.
1276
+ # Runs a git command and returns the output
1176
1277
  #
1177
- # @param [Proc] block block to be executed within the customized environment
1178
- def with_custom_env_variables(&block)
1179
- @@semaphore.synchronize do
1180
- store_git_system_env_variables()
1181
- set_custom_git_env_variables()
1182
- return block.call()
1183
- end
1184
- ensure
1185
- restore_git_system_env_variables()
1186
- end
1187
-
1188
- def command(*cmd, redirect: '', chomp: true, chdir: nil, &block)
1189
- Git::Lib.warn_if_old_command(self)
1190
-
1191
- raise 'cmd can not include a nested array' if cmd.any? { |o| o.is_a? Array }
1192
-
1193
- global_opts = []
1194
- global_opts << "--git-dir=#{@git_dir}" if !@git_dir.nil?
1195
- global_opts << "--work-tree=#{@git_work_dir}" if !@git_work_dir.nil?
1196
- global_opts << '-c' << 'core.quotePath=true'
1197
- global_opts << '-c' << 'color.ui=false'
1198
-
1199
- escaped_cmd = cmd.map { |part| escape(part) }.join(' ')
1200
-
1201
- global_opts = global_opts.map { |s| escape(s) }.join(' ')
1202
-
1203
- git_cmd = "#{Git::Base.config.binary_path} #{global_opts} #{escaped_cmd} #{redirect} 2>&1"
1204
-
1205
- output = nil
1206
-
1207
- command_thread = nil;
1208
-
1209
- status = nil
1210
-
1211
- with_custom_env_variables do
1212
- command_thread = Thread.new do
1213
- output, status = run_command(git_cmd, chdir, &block)
1214
- end
1215
- command_thread.join
1216
- end
1217
-
1218
- @logger.info(git_cmd)
1219
- @logger.debug(output)
1220
-
1221
- if status.exitstatus > 1 || (status.exitstatus == 1 && output != '')
1222
- result = Git::CommandLineResult.new(git_cmd, status, output, '')
1223
- raise Git::FailedError.new(result)
1224
- end
1225
-
1226
- output.chomp! if output && chomp && !block_given?
1227
-
1228
- output
1278
+ # @param args [Array] the git command to run and its arguments
1279
+ #
1280
+ # This should exclude the 'git' command itself and global options.
1281
+ #
1282
+ # For example, to run `git log --pretty=oneline`, you would pass `['log',
1283
+ # '--pretty=oneline']`
1284
+ #
1285
+ # @param out [String, nil] the path to a file or an IO to write the command's
1286
+ # stdout to
1287
+ #
1288
+ # @param err [String, nil] the path to a file or an IO to write the command's
1289
+ # stdout to
1290
+ #
1291
+ # @param normalize [Boolean] true to normalize the output encoding
1292
+ #
1293
+ # @param chomp [Boolean] true to remove trailing newlines from the output
1294
+ #
1295
+ # @param merge [Boolean] true to merge stdout and stderr
1296
+ #
1297
+ # @param chdir [String, nil] the directory to run the command in
1298
+ #
1299
+ # @param timeout [Numeric, nil] the maximum seconds to wait for the command to complete
1300
+ #
1301
+ # If timeout is nil, the global timeout from {Git::Config} is used.
1302
+ #
1303
+ # If timeout is zero, the timeout will not be enforced.
1304
+ #
1305
+ # If the command times out, it is killed via a `SIGKILL` signal and `Git::TimeoutError` is raised.
1306
+ #
1307
+ # If the command does not respond to SIGKILL, it will hang this method.
1308
+ #
1309
+ # @see Git::CommandLine#run
1310
+ #
1311
+ # @return [String] the command's stdout (or merged stdout and stderr if `merge`
1312
+ # is true)
1313
+ #
1314
+ # @raise [Git::FailedError] if the command failed
1315
+ # @raise [Git::SignaledError] if the command was signaled
1316
+ # @raise [Git::TimeoutError] if the command times out
1317
+ # @raise [Git::ProcessIOError] if an exception was raised while collecting subprocess output
1318
+ #
1319
+ # The exception's `result` attribute is a {Git::CommandLineResult} which will
1320
+ # contain the result of the command including the exit status, stdout, and
1321
+ # stderr.
1322
+ #
1323
+ # @api private
1324
+ #
1325
+ def command(*args, out: nil, err: nil, normalize: true, chomp: true, merge: false, chdir: nil, timeout: nil)
1326
+ timeout = timeout || Git.config.timeout
1327
+ result = command_line.run(*args, out: out, err: err, normalize: normalize, chomp: chomp, merge: merge, chdir: chdir, timeout: timeout)
1328
+ result.stdout
1229
1329
  end
1230
1330
 
1231
1331
  # Takes the diff command line output (as Array) and parse it into a Hash
@@ -1291,38 +1391,5 @@ module Git
1291
1391
  end
1292
1392
  arr_opts
1293
1393
  end
1294
-
1295
- def run_command(git_cmd, chdir=nil, &block)
1296
- block ||= Proc.new do |io|
1297
- io.readlines.map { |l| Git::EncodingUtils.normalize_encoding(l) }.join
1298
- end
1299
-
1300
- opts = {}
1301
- opts[:chdir] = File.expand_path(chdir) if chdir
1302
-
1303
- Open3.popen2(git_cmd, opts) do |stdin, stdout, wait_thr|
1304
- [block.call(stdout), wait_thr.value]
1305
- end
1306
- end
1307
-
1308
- def escape(s)
1309
- windows_platform? ? escape_for_windows(s) : escape_for_sh(s)
1310
- end
1311
-
1312
- def escape_for_sh(s)
1313
- "'#{s && s.to_s.gsub('\'','\'"\'"\'')}'"
1314
- end
1315
-
1316
- def escape_for_windows(s)
1317
- # Escape existing double quotes in s and then wrap the result with double quotes
1318
- escaped_string = s.to_s.gsub('"','\\"')
1319
- %Q{"#{escaped_string}"}
1320
- end
1321
-
1322
- def windows_platform?
1323
- # Check if on Windows via RUBY_PLATFORM (CRuby) and RUBY_DESCRIPTION (JRuby)
1324
- win_platform_regex = /mingw|mswin/
1325
- RUBY_PLATFORM =~ win_platform_regex || RUBY_DESCRIPTION =~ win_platform_regex
1326
- end
1327
1394
  end
1328
1395
  end