git 1.19.1 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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