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.
- checksums.yaml +4 -4
- data/.github/pull_request_template.md +8 -0
- data/.github/workflows/continuous_integration.yml +14 -20
- data/.github/workflows/experimental_continuous_integration.yml +43 -0
- data/CHANGELOG.md +88 -0
- data/CONTRIBUTING.md +22 -67
- data/README.md +166 -52
- data/RELEASING.md +49 -34
- data/git.gemspec +8 -8
- data/lib/git/base.rb +173 -47
- data/lib/git/command_line.rb +377 -0
- data/lib/git/config.rb +5 -1
- data/lib/git/errors.rb +206 -0
- data/lib/git/escaped_path.rb +1 -1
- data/lib/git/lib.rb +244 -177
- data/lib/git/log.rb +65 -4
- data/lib/git/object.rb +69 -67
- data/lib/git/status.rb +132 -24
- data/lib/git/version.rb +1 -1
- data/lib/git.rb +8 -7
- metadata +41 -30
- data/.github/stale.yml +0 -25
- data/Dockerfile.changelog-rs +0 -12
- data/PULL_REQUEST_TEMPLATE.md +0 -9
- data/lib/git/base/factory.rb +0 -99
- data/lib/git/failed_error.rb +0 -53
- data/lib/git/git_execute_error.rb +0 -7
- data/lib/git/signaled_error.rb +0 -50
- /data/{ISSUE_TEMPLATE.md → .github/issue_template.md} +0 -0
data/lib/git/lib.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
|
-
require 'git/
|
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
|
-
# @
|
41
|
-
#
|
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
|
-
#
|
50
|
+
# @param [Logger] logger
|
44
51
|
#
|
45
|
-
# @
|
46
|
-
#
|
47
|
-
#
|
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
|
-
#
|
92
|
+
# Clones a repository into a newly created directory
|
83
93
|
#
|
84
|
-
#
|
85
|
-
#
|
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
|
-
#
|
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
|
-
|
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
|
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
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
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
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
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
|
-
|
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
|
-
|
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
|
-
#
|
648
|
-
#
|
649
|
-
#
|
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
|
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
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
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
|
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
|
-
|
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
|
-
|
1037
|
-
|
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
|
-
|
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
|
-
[
|
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
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
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
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
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
|
-
|
1167
|
-
|
1168
|
-
|
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
|
1175
|
-
# It restores the ENV after execution.
|
1276
|
+
# Runs a git command and returns the output
|
1176
1277
|
#
|
1177
|
-
# @param [
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
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
|