git 1.7.0 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of git might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/CONTRIBUTING.md +6 -1
- data/MAINTAINERS.md +5 -0
- data/README.md +258 -214
- data/lib/git.rb +180 -41
- data/lib/git/base.rb +192 -147
- data/lib/git/base/factory.rb +40 -22
- data/lib/git/diff.rb +6 -5
- data/lib/git/lib.rb +187 -63
- data/lib/git/log.rb +8 -1
- data/lib/git/status.rb +1 -1
- data/lib/git/version.rb +1 -1
- metadata +74 -33
data/lib/git/base/factory.rb
CHANGED
@@ -3,74 +3,92 @@ module Git
|
|
3
3
|
class Base
|
4
4
|
|
5
5
|
module Factory
|
6
|
-
|
7
|
-
#
|
6
|
+
|
7
|
+
# @return [Git::Branch] an object for branch_name
|
8
8
|
def branch(branch_name = 'master')
|
9
9
|
Git::Branch.new(self, branch_name)
|
10
10
|
end
|
11
11
|
|
12
|
-
#
|
13
|
-
#
|
12
|
+
# @return [Git::Branches] a collection of all the branches in the repository.
|
13
|
+
# Each branch is represented as a {Git::Branch}.
|
14
14
|
def branches
|
15
15
|
Git::Branches.new(self)
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
|
+
# returns a Git::Worktree object for dir, commitish
|
19
|
+
def worktree(dir, commitish = nil)
|
20
|
+
Git::Worktree.new(self, dir, commitish)
|
21
|
+
end
|
22
|
+
|
23
|
+
# returns a Git::worktrees object of all the Git::Worktrees
|
24
|
+
# objects for this repo
|
25
|
+
def worktrees
|
26
|
+
Git::Worktrees.new(self)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Git::Object::Commit] a commit object
|
18
30
|
def commit_tree(tree = nil, opts = {})
|
19
31
|
Git::Object::Commit.new(self, self.lib.commit_tree(tree, opts))
|
20
32
|
end
|
21
33
|
|
22
|
-
#
|
34
|
+
# @return [Git::Diff] a Git::Diff object
|
23
35
|
def diff(objectish = 'HEAD', obj2 = nil)
|
24
36
|
Git::Diff.new(self, objectish, obj2)
|
25
37
|
end
|
26
|
-
|
38
|
+
|
39
|
+
# @return [Git::Object] a Git object
|
27
40
|
def gblob(objectish)
|
28
41
|
Git::Object.new(self, objectish, 'blob')
|
29
42
|
end
|
30
|
-
|
43
|
+
|
44
|
+
# @return [Git::Object] a Git object
|
31
45
|
def gcommit(objectish)
|
32
46
|
Git::Object.new(self, objectish, 'commit')
|
33
47
|
end
|
34
48
|
|
49
|
+
# @return [Git::Object] a Git object
|
35
50
|
def gtree(objectish)
|
36
51
|
Git::Object.new(self, objectish, 'tree')
|
37
52
|
end
|
38
|
-
|
39
|
-
#
|
53
|
+
|
54
|
+
# @return [Git::Log] a log with the specified number of commits
|
40
55
|
def log(count = 30)
|
41
56
|
Git::Log.new(self, count)
|
42
57
|
end
|
43
|
-
|
58
|
+
|
44
59
|
# returns a Git::Object of the appropriate type
|
45
|
-
# you can also call @git.gtree('tree'), but that's
|
60
|
+
# you can also call @git.gtree('tree'), but that's
|
46
61
|
# just for readability. If you call @git.gtree('HEAD') it will
|
47
|
-
# still return a Git::Object::Commit object.
|
62
|
+
# still return a Git::Object::Commit object.
|
48
63
|
#
|
49
|
-
#
|
50
|
-
# on the objectish and determine the type of the object and return
|
51
|
-
# an appropriate object for that type
|
64
|
+
# object calls a factory method that will run a rev-parse
|
65
|
+
# on the objectish and determine the type of the object and return
|
66
|
+
# an appropriate object for that type
|
67
|
+
#
|
68
|
+
# @return [Git::Object] an instance of the appropriate type of Git::Object
|
52
69
|
def object(objectish)
|
53
70
|
Git::Object.new(self, objectish)
|
54
71
|
end
|
55
|
-
|
56
|
-
#
|
72
|
+
|
73
|
+
# @return [Git::Remote] a remote of the specified name
|
57
74
|
def remote(remote_name = 'origin')
|
58
75
|
Git::Remote.new(self, remote_name)
|
59
76
|
end
|
60
77
|
|
61
|
-
#
|
78
|
+
# @return [Git::Status] a status object
|
62
79
|
def status
|
63
80
|
Git::Status.new(self)
|
64
81
|
end
|
65
|
-
|
66
|
-
#
|
82
|
+
|
83
|
+
# @return [Git::Object::Tag] a tag object
|
67
84
|
def tag(tag_name)
|
68
85
|
Git::Object.new(self, tag_name, 'tag', true)
|
69
86
|
end
|
70
87
|
|
71
88
|
# Find as good common ancestors as possible for a merge
|
72
89
|
# example: g.merge_base('master', 'some_branch', 'some_sha', octopus: true)
|
73
|
-
#
|
90
|
+
#
|
91
|
+
# @return [Array<Git::Object::Commit>] a collection of common ancestors
|
74
92
|
def merge_base(*args)
|
75
93
|
shas = self.lib.merge_base(*args)
|
76
94
|
shas.map { |sha| gcommit(sha) }
|
data/lib/git/diff.rb
CHANGED
@@ -72,6 +72,7 @@ module Git
|
|
72
72
|
class DiffFile
|
73
73
|
attr_accessor :patch, :path, :mode, :src, :dst, :type
|
74
74
|
@base = nil
|
75
|
+
NIL_BLOB_REGEXP = /\A0{4,40}\z/.freeze
|
75
76
|
|
76
77
|
def initialize(base, hash)
|
77
78
|
@base = base
|
@@ -89,10 +90,10 @@ module Git
|
|
89
90
|
end
|
90
91
|
|
91
92
|
def blob(type = :dst)
|
92
|
-
if type == :src
|
93
|
-
@base.object(@src)
|
94
|
-
|
95
|
-
@base.object(@dst)
|
93
|
+
if type == :src && !NIL_BLOB_REGEXP.match(@src)
|
94
|
+
@base.object(@src)
|
95
|
+
elsif !NIL_BLOB_REGEXP.match(@dst)
|
96
|
+
@base.object(@dst)
|
96
97
|
end
|
97
98
|
end
|
98
99
|
end
|
@@ -132,7 +133,7 @@ module Git
|
|
132
133
|
current_file = m[1]
|
133
134
|
final[current_file] = defaults.merge({:patch => line, :path => current_file})
|
134
135
|
else
|
135
|
-
if m = /^index (
|
136
|
+
if m = /^index ([0-9a-f]{4,40})\.\.([0-9a-f]{4,40})( ......)*/.match(line)
|
136
137
|
final[current_file][:src] = m[1]
|
137
138
|
final[current_file][:dst] = m[2]
|
138
139
|
final[current_file][:mode] = m[3].strip if m[3]
|
data/lib/git/lib.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rchardet'
|
2
2
|
require 'tempfile'
|
3
|
+
require 'zlib'
|
3
4
|
|
4
5
|
module Git
|
5
6
|
|
@@ -10,6 +11,43 @@ module Git
|
|
10
11
|
|
11
12
|
@@semaphore = Mutex.new
|
12
13
|
|
14
|
+
# The path to the Git working copy. The default is '"./.git"'.
|
15
|
+
#
|
16
|
+
# @return [Pathname] the path to the Git working copy.
|
17
|
+
#
|
18
|
+
# @see [Git working tree](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefworkingtreeaworkingtree)
|
19
|
+
#
|
20
|
+
attr_reader :git_work_dir
|
21
|
+
|
22
|
+
# The path to the Git repository directory. The default is
|
23
|
+
# `"#{git_work_dir}/.git"`.
|
24
|
+
#
|
25
|
+
# @return [Pathname] the Git repository directory.
|
26
|
+
#
|
27
|
+
# @see [Git repository](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefrepositoryarepository)
|
28
|
+
#
|
29
|
+
attr_reader :git_dir
|
30
|
+
|
31
|
+
# The Git index file used to stage changes (using `git add`) before they
|
32
|
+
# are committed.
|
33
|
+
#
|
34
|
+
# @return [Pathname] the Git index file
|
35
|
+
#
|
36
|
+
# @see [Git index file](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefindexaindex)
|
37
|
+
#
|
38
|
+
attr_reader :git_index_file
|
39
|
+
|
40
|
+
# Create a new Git::Lib object
|
41
|
+
#
|
42
|
+
# @param [Git::Base, Hash] base An object that passes in values for
|
43
|
+
# @git_work_dir, @git_dir, and @git_index_file
|
44
|
+
#
|
45
|
+
# @param [Logger] logger
|
46
|
+
#
|
47
|
+
# @option base [Pathname] :working_directory
|
48
|
+
# @option base [Pathname] :repository
|
49
|
+
# @option base [Pathname] :index
|
50
|
+
#
|
13
51
|
def initialize(base = nil, logger = nil)
|
14
52
|
@git_dir = nil
|
15
53
|
@git_index_file = nil
|
@@ -38,14 +76,11 @@ module Git
|
|
38
76
|
arr_opts = []
|
39
77
|
arr_opts << '--bare' if opts[:bare]
|
40
78
|
|
41
|
-
command('init', arr_opts
|
79
|
+
command('init', arr_opts)
|
42
80
|
end
|
43
81
|
|
44
82
|
# tries to clone the given repo
|
45
83
|
#
|
46
|
-
# returns {:repository} (if bare)
|
47
|
-
# {:working_directory} otherwise
|
48
|
-
#
|
49
84
|
# accepts options:
|
50
85
|
# :bare:: no working directory
|
51
86
|
# :branch:: name of branch to track (rather than 'master')
|
@@ -57,6 +92,8 @@ module Git
|
|
57
92
|
#
|
58
93
|
# TODO - make this work with SSH password or auth_key
|
59
94
|
#
|
95
|
+
# @return [Hash] the options to pass to {Git::Base.new}
|
96
|
+
#
|
60
97
|
def clone(repository, name, opts = {})
|
61
98
|
@path = opts[:path] || '.'
|
62
99
|
clone_dir = opts[:path] ? File.join(@path, name) : name
|
@@ -77,9 +114,16 @@ module Git
|
|
77
114
|
|
78
115
|
command('clone', arr_opts)
|
79
116
|
|
80
|
-
(
|
117
|
+
return_base_opts_from_clone(clone_dir, opts)
|
81
118
|
end
|
82
119
|
|
120
|
+
def return_base_opts_from_clone(clone_dir, opts)
|
121
|
+
base_opts = {}
|
122
|
+
base_opts[:repository] = clone_dir if (opts[:bare] || opts[:mirror])
|
123
|
+
base_opts[:working_directory] = clone_dir unless (opts[:bare] || opts[:mirror])
|
124
|
+
base_opts[:log] = opts[:log] if opts[:log]
|
125
|
+
base_opts
|
126
|
+
end
|
83
127
|
|
84
128
|
## READ COMMANDS ##
|
85
129
|
|
@@ -133,7 +177,7 @@ module Git
|
|
133
177
|
|
134
178
|
arr_opts += log_path_options(opts)
|
135
179
|
|
136
|
-
command_lines('log', arr_opts
|
180
|
+
command_lines('log', arr_opts).map { |l| l.split.first }
|
137
181
|
end
|
138
182
|
|
139
183
|
def full_log_commits(opts={})
|
@@ -144,7 +188,7 @@ module Git
|
|
144
188
|
|
145
189
|
arr_opts += log_path_options(opts)
|
146
190
|
|
147
|
-
full_log = command_lines('log', arr_opts
|
191
|
+
full_log = command_lines('log', arr_opts)
|
148
192
|
|
149
193
|
process_commit_log_data(full_log)
|
150
194
|
end
|
@@ -165,17 +209,17 @@ module Git
|
|
165
209
|
end
|
166
210
|
|
167
211
|
def object_type(sha)
|
168
|
-
command('cat-file',
|
212
|
+
command('cat-file', '-t', sha)
|
169
213
|
end
|
170
214
|
|
171
215
|
def object_size(sha)
|
172
|
-
command('cat-file',
|
216
|
+
command('cat-file', '-s', sha).to_i
|
173
217
|
end
|
174
218
|
|
175
219
|
# returns useful array of raw commit object data
|
176
220
|
def commit_data(sha)
|
177
221
|
sha = sha.to_s
|
178
|
-
cdata = command_lines('cat-file',
|
222
|
+
cdata = command_lines('cat-file', 'commit', sha)
|
179
223
|
process_commit_data(cdata, sha, 0)
|
180
224
|
end
|
181
225
|
|
@@ -205,7 +249,7 @@ module Git
|
|
205
249
|
|
206
250
|
def tag_data(name)
|
207
251
|
sha = sha.to_s
|
208
|
-
tdata = command_lines('cat-file',
|
252
|
+
tdata = command_lines('cat-file', 'tag', name)
|
209
253
|
process_tag_data(tdata, name, 0)
|
210
254
|
end
|
211
255
|
|
@@ -243,6 +287,8 @@ module Git
|
|
243
287
|
next
|
244
288
|
end
|
245
289
|
|
290
|
+
in_message = false if in_message && line[0..3] != " "
|
291
|
+
|
246
292
|
if in_message
|
247
293
|
hsh['message'] << "#{line[4..-1]}\n"
|
248
294
|
next
|
@@ -268,7 +314,7 @@ module Git
|
|
268
314
|
end
|
269
315
|
|
270
316
|
def object_contents(sha, &block)
|
271
|
-
command('cat-file',
|
317
|
+
command('cat-file', '-p', sha, &block)
|
272
318
|
end
|
273
319
|
|
274
320
|
def ls_tree(sha)
|
@@ -284,11 +330,11 @@ module Git
|
|
284
330
|
end
|
285
331
|
|
286
332
|
def mv(file1, file2)
|
287
|
-
command_lines('mv',
|
333
|
+
command_lines('mv', '--', file1, file2)
|
288
334
|
end
|
289
335
|
|
290
336
|
def full_tree(sha)
|
291
|
-
command_lines('ls-tree',
|
337
|
+
command_lines('ls-tree', '-r', sha)
|
292
338
|
end
|
293
339
|
|
294
340
|
def tree_depth(sha)
|
@@ -296,7 +342,7 @@ module Git
|
|
296
342
|
end
|
297
343
|
|
298
344
|
def change_head_branch(branch_name)
|
299
|
-
command('symbolic-ref',
|
345
|
+
command('symbolic-ref', 'HEAD', "refs/heads/#{branch_name}")
|
300
346
|
end
|
301
347
|
|
302
348
|
def branches_all
|
@@ -308,6 +354,39 @@ module Git
|
|
308
354
|
arr
|
309
355
|
end
|
310
356
|
|
357
|
+
def worktrees_all
|
358
|
+
arr = []
|
359
|
+
directory = ''
|
360
|
+
# Output example for `worktree list --porcelain`:
|
361
|
+
# worktree /code/public/ruby-git
|
362
|
+
# HEAD 4bef5abbba073c77b4d0ccc1ffcd0ed7d48be5d4
|
363
|
+
# branch refs/heads/master
|
364
|
+
#
|
365
|
+
# worktree /tmp/worktree-1
|
366
|
+
# HEAD b8c63206f8d10f57892060375a86ae911fad356e
|
367
|
+
# detached
|
368
|
+
#
|
369
|
+
command_lines('worktree',['list', '--porcelain']).each do |w|
|
370
|
+
s = w.split("\s")
|
371
|
+
directory = s[1] if s[0] == 'worktree'
|
372
|
+
arr << [directory, s[1]] if s[0] == 'HEAD'
|
373
|
+
end
|
374
|
+
arr
|
375
|
+
end
|
376
|
+
|
377
|
+
def worktree_add(dir, commitish = nil)
|
378
|
+
return command('worktree', ['add', dir, commitish]) if !commitish.nil?
|
379
|
+
command('worktree', ['add', dir])
|
380
|
+
end
|
381
|
+
|
382
|
+
def worktree_remove(dir)
|
383
|
+
command('worktree', ['remove', dir])
|
384
|
+
end
|
385
|
+
|
386
|
+
def worktree_prune
|
387
|
+
command('worktree', ['prune'])
|
388
|
+
end
|
389
|
+
|
311
390
|
def list_files(ref_dir)
|
312
391
|
dir = File.join(@git_dir, 'refs', ref_dir)
|
313
392
|
files = []
|
@@ -403,7 +482,7 @@ module Git
|
|
403
482
|
def ls_files(location=nil)
|
404
483
|
location ||= '.'
|
405
484
|
hsh = {}
|
406
|
-
command_lines('ls-files',
|
485
|
+
command_lines('ls-files', '--stage', location).each do |line|
|
407
486
|
(info, file) = line.split("\t")
|
408
487
|
(mode, sha, stage) = info.split
|
409
488
|
file = eval(file) if file =~ /^\".*\"$/ # This takes care of quoted strings returned from git
|
@@ -412,10 +491,13 @@ module Git
|
|
412
491
|
hsh
|
413
492
|
end
|
414
493
|
|
415
|
-
def ls_remote(location=nil)
|
416
|
-
|
494
|
+
def ls_remote(location=nil, opts={})
|
495
|
+
arr_opts = []
|
496
|
+
arr_opts << ['--refs'] if opts[:refs]
|
497
|
+
arr_opts << (location || '.')
|
498
|
+
|
417
499
|
Hash.new{ |h,k| h[k] = {} }.tap do |hsh|
|
418
|
-
command_lines('ls-remote',
|
500
|
+
command_lines('ls-remote', arr_opts).each do |line|
|
419
501
|
(sha, info) = line.split("\t")
|
420
502
|
(ref, type, name) = info.split('/', 3)
|
421
503
|
type ||= 'head'
|
@@ -427,7 +509,7 @@ module Git
|
|
427
509
|
end
|
428
510
|
|
429
511
|
def ignored_files
|
430
|
-
command_lines('ls-files',
|
512
|
+
command_lines('ls-files', '--others', '-i', '--exclude-standard')
|
431
513
|
end
|
432
514
|
|
433
515
|
|
@@ -442,8 +524,8 @@ module Git
|
|
442
524
|
end
|
443
525
|
|
444
526
|
def config_get(name)
|
445
|
-
do_get =
|
446
|
-
command('config',
|
527
|
+
do_get = Proc.new do |path|
|
528
|
+
command('config', '--get', name)
|
447
529
|
end
|
448
530
|
|
449
531
|
if @git_dir
|
@@ -454,12 +536,12 @@ module Git
|
|
454
536
|
end
|
455
537
|
|
456
538
|
def global_config_get(name)
|
457
|
-
command('config',
|
539
|
+
command('config', '--global', '--get', name)
|
458
540
|
end
|
459
541
|
|
460
542
|
def config_list
|
461
|
-
build_list =
|
462
|
-
parse_config_list command_lines('config',
|
543
|
+
build_list = Proc.new do |path|
|
544
|
+
parse_config_list command_lines('config', '--list')
|
463
545
|
end
|
464
546
|
|
465
547
|
if @git_dir
|
@@ -470,7 +552,7 @@ module Git
|
|
470
552
|
end
|
471
553
|
|
472
554
|
def global_config_list
|
473
|
-
parse_config_list command_lines('config',
|
555
|
+
parse_config_list command_lines('config', '--global', '--list')
|
474
556
|
end
|
475
557
|
|
476
558
|
def parse_config_list(lines)
|
@@ -483,7 +565,7 @@ module Git
|
|
483
565
|
end
|
484
566
|
|
485
567
|
def parse_config(file)
|
486
|
-
parse_config_list command_lines('config',
|
568
|
+
parse_config_list command_lines('config', '--list', '--file', file)
|
487
569
|
end
|
488
570
|
|
489
571
|
# Shows objects
|
@@ -496,17 +578,17 @@ module Git
|
|
496
578
|
|
497
579
|
arr_opts << (path ? "#{objectish}:#{path}" : objectish)
|
498
580
|
|
499
|
-
command('show', arr_opts.compact)
|
581
|
+
command('show', arr_opts.compact, chomp: false)
|
500
582
|
end
|
501
583
|
|
502
584
|
## WRITE COMMANDS ##
|
503
585
|
|
504
586
|
def config_set(name, value)
|
505
|
-
command('config',
|
587
|
+
command('config', name, value)
|
506
588
|
end
|
507
589
|
|
508
590
|
def global_config_set(name, value)
|
509
|
-
command('config',
|
591
|
+
command('config', '--global', name, value)
|
510
592
|
end
|
511
593
|
|
512
594
|
# updates the repository index using the working directory content
|
@@ -559,9 +641,10 @@ module Git
|
|
559
641
|
# :author
|
560
642
|
# :date
|
561
643
|
# :no_verify
|
644
|
+
# :allow_empty_message
|
562
645
|
#
|
563
646
|
# @param [String] message the commit message to be used
|
564
|
-
# @param [
|
647
|
+
# @param [Hash] opts the commit options to be used
|
565
648
|
def commit(message, opts = {})
|
566
649
|
arr_opts = []
|
567
650
|
arr_opts << "--message=#{message}" if message
|
@@ -571,6 +654,7 @@ module Git
|
|
571
654
|
arr_opts << "--author=#{opts[:author]}" if opts[:author]
|
572
655
|
arr_opts << "--date=#{opts[:date]}" if opts[:date].is_a? String
|
573
656
|
arr_opts << '--no-verify' if opts[:no_verify]
|
657
|
+
arr_opts << '--allow-empty-message' if opts[:allow_empty_message]
|
574
658
|
|
575
659
|
command('commit', arr_opts)
|
576
660
|
end
|
@@ -629,13 +713,13 @@ module Git
|
|
629
713
|
end
|
630
714
|
|
631
715
|
def stash_save(message)
|
632
|
-
output = command('stash save',
|
716
|
+
output = command('stash save', message)
|
633
717
|
output =~ /HEAD is now at/
|
634
718
|
end
|
635
719
|
|
636
720
|
def stash_apply(id = nil)
|
637
721
|
if id
|
638
|
-
command('stash apply',
|
722
|
+
command('stash apply', id)
|
639
723
|
else
|
640
724
|
command('stash apply')
|
641
725
|
end
|
@@ -654,7 +738,7 @@ module Git
|
|
654
738
|
end
|
655
739
|
|
656
740
|
def branch_delete(branch)
|
657
|
-
command('branch',
|
741
|
+
command('branch', '-D', branch)
|
658
742
|
end
|
659
743
|
|
660
744
|
def checkout(branch, opts = {})
|
@@ -673,8 +757,9 @@ module Git
|
|
673
757
|
command('checkout', arr_opts)
|
674
758
|
end
|
675
759
|
|
676
|
-
def merge(branch, message = nil)
|
760
|
+
def merge(branch, message = nil, opts = {})
|
677
761
|
arr_opts = []
|
762
|
+
arr_opts << '--no-ff' if opts[:no_ff]
|
678
763
|
arr_opts << '-m' << message if message
|
679
764
|
arr_opts += [branch]
|
680
765
|
command('merge', arr_opts)
|
@@ -697,7 +782,7 @@ module Git
|
|
697
782
|
|
698
783
|
def unmerged
|
699
784
|
unmerged = []
|
700
|
-
command_lines('diff',
|
785
|
+
command_lines('diff', "--cached").each do |line|
|
701
786
|
unmerged << $1 if line =~ /^\* Unmerged path (.*)/
|
702
787
|
end
|
703
788
|
unmerged
|
@@ -705,11 +790,15 @@ module Git
|
|
705
790
|
|
706
791
|
def conflicts # :yields: file, your, their
|
707
792
|
self.unmerged.each do |f|
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
793
|
+
your_tempfile = Tempfile.new("YOUR-#{File.basename(f)}")
|
794
|
+
your = your_tempfile.path
|
795
|
+
your_tempfile.close # free up file for git command process
|
796
|
+
command('show', ":2:#{f}", redirect: "> #{escape your}")
|
797
|
+
|
798
|
+
their_tempfile = Tempfile.new("THEIR-#{File.basename(f)}")
|
799
|
+
their = their_tempfile.path
|
800
|
+
their_tempfile.close # free up file for git command process
|
801
|
+
command('show', ":3:#{f}", redirect: "> #{escape their}")
|
713
802
|
yield(f, your, their)
|
714
803
|
end
|
715
804
|
end
|
@@ -734,7 +823,7 @@ module Git
|
|
734
823
|
end
|
735
824
|
|
736
825
|
def remote_remove(name)
|
737
|
-
command('remote',
|
826
|
+
command('remote', 'rm', name)
|
738
827
|
end
|
739
828
|
|
740
829
|
def remotes
|
@@ -800,22 +889,22 @@ module Git
|
|
800
889
|
end
|
801
890
|
|
802
891
|
def pull(remote='origin', branch='master')
|
803
|
-
command('pull',
|
892
|
+
command('pull', remote, branch)
|
804
893
|
end
|
805
894
|
|
806
895
|
def tag_sha(tag_name)
|
807
896
|
head = File.join(@git_dir, 'refs', 'tags', tag_name)
|
808
897
|
return File.read(head).chomp if File.exist?(head)
|
809
898
|
|
810
|
-
command('show-ref',
|
899
|
+
command('show-ref', '--tags', '-s', tag_name)
|
811
900
|
end
|
812
901
|
|
813
902
|
def repack
|
814
|
-
command('repack',
|
903
|
+
command('repack', '-a', '-d')
|
815
904
|
end
|
816
905
|
|
817
906
|
def gc
|
818
|
-
command('gc',
|
907
|
+
command('gc', '--prune', '--aggressive', '--auto')
|
819
908
|
end
|
820
909
|
|
821
910
|
# reads a tree into the current index file
|
@@ -840,11 +929,11 @@ module Git
|
|
840
929
|
arr_opts << tree
|
841
930
|
arr_opts << '-p' << opts[:parent] if opts[:parent]
|
842
931
|
arr_opts += [opts[:parents]].map { |p| ['-p', p] }.flatten if opts[:parents]
|
843
|
-
command('commit-tree', arr_opts,
|
932
|
+
command('commit-tree', arr_opts, redirect: "< #{escape t.path}")
|
844
933
|
end
|
845
934
|
|
846
935
|
def update_ref(branch, commit)
|
847
|
-
command('update-ref',
|
936
|
+
command('update-ref', branch, commit)
|
848
937
|
end
|
849
938
|
|
850
939
|
def checkout_index(opts = {})
|
@@ -886,13 +975,19 @@ module Git
|
|
886
975
|
arr_opts << "--remote=#{opts[:remote]}" if opts[:remote]
|
887
976
|
arr_opts << sha
|
888
977
|
arr_opts << '--' << opts[:path] if opts[:path]
|
889
|
-
command('archive', arr_opts,
|
978
|
+
command('archive', arr_opts, redirect: " > #{escape file}")
|
979
|
+
if opts[:add_gzip]
|
980
|
+
file_content = File.read(file)
|
981
|
+
Zlib::GzipWriter.open(file) do |gz|
|
982
|
+
gz.write(file_content)
|
983
|
+
end
|
984
|
+
end
|
890
985
|
return file
|
891
986
|
end
|
892
987
|
|
893
988
|
# returns the current version of git, as an Array of Fixnums.
|
894
989
|
def current_command_version
|
895
|
-
output = command('version'
|
990
|
+
output = command('version')
|
896
991
|
version = output[/\d+\.\d+(\.\d+)+/]
|
897
992
|
version.split('.').collect {|i| i.to_i}
|
898
993
|
end
|
@@ -913,8 +1008,14 @@ module Git
|
|
913
1008
|
# @return [<String>] the names of the EVN variables involved in the git commands
|
914
1009
|
ENV_VARIABLE_NAMES = ['GIT_DIR', 'GIT_WORK_TREE', 'GIT_INDEX_FILE', 'GIT_SSH']
|
915
1010
|
|
916
|
-
def command_lines(cmd, opts
|
917
|
-
command(cmd, opts
|
1011
|
+
def command_lines(cmd, *opts)
|
1012
|
+
cmd_op = command(cmd, *opts)
|
1013
|
+
if cmd_op.encoding.name != "UTF-8"
|
1014
|
+
op = cmd_op.encode("UTF-8", "binary", :invalid => :replace, :undef => :replace)
|
1015
|
+
else
|
1016
|
+
op = cmd_op
|
1017
|
+
end
|
1018
|
+
op.split("\n")
|
918
1019
|
end
|
919
1020
|
|
920
1021
|
# Takes the current git's system ENV variables and store them.
|
@@ -954,7 +1055,15 @@ module Git
|
|
954
1055
|
restore_git_system_env_variables()
|
955
1056
|
end
|
956
1057
|
|
957
|
-
def command(cmd, opts
|
1058
|
+
def command(cmd, *opts, &block)
|
1059
|
+
command_opts = { chomp: true, redirect: '' }
|
1060
|
+
if opts.last.is_a?(Hash)
|
1061
|
+
command_opts.merge!(opts.pop)
|
1062
|
+
end
|
1063
|
+
command_opts.keys.each do |k|
|
1064
|
+
raise ArgumentError.new("Unsupported option: #{k}") unless [:chomp, :redirect].include?(k)
|
1065
|
+
end
|
1066
|
+
|
958
1067
|
global_opts = []
|
959
1068
|
global_opts << "--git-dir=#{@git_dir}" if !@git_dir.nil?
|
960
1069
|
global_opts << "--work-tree=#{@git_work_dir}" if !@git_work_dir.nil?
|
@@ -964,7 +1073,7 @@ module Git
|
|
964
1073
|
|
965
1074
|
global_opts = global_opts.flatten.map {|s| escape(s) }.join(' ')
|
966
1075
|
|
967
|
-
git_cmd = "#{Git::Base.config.binary_path} #{global_opts} #{cmd} #{opts} #{redirect} 2>&1"
|
1076
|
+
git_cmd = "#{Git::Base.config.binary_path} #{global_opts} #{cmd} #{opts} #{command_opts[:redirect]} 2>&1"
|
968
1077
|
|
969
1078
|
output = nil
|
970
1079
|
|
@@ -985,11 +1094,12 @@ module Git
|
|
985
1094
|
@logger.debug(output)
|
986
1095
|
end
|
987
1096
|
|
988
|
-
|
989
|
-
|
990
|
-
end
|
1097
|
+
raise Git::GitExecuteError, "#{git_cmd}:#{output}" if
|
1098
|
+
exitstatus > 1 || (exitstatus == 1 && output != '')
|
991
1099
|
|
992
|
-
|
1100
|
+
output.chomp! if output && command_opts[:chomp] && !block_given?
|
1101
|
+
|
1102
|
+
output
|
993
1103
|
end
|
994
1104
|
|
995
1105
|
# Takes the diff command line output (as Array) and parse it into a Hash
|
@@ -998,6 +1108,8 @@ module Git
|
|
998
1108
|
# @param [Array] opts the diff options to be used
|
999
1109
|
# @return [Hash] the diff as Hash
|
1000
1110
|
def diff_as_hash(diff_command, opts=[])
|
1111
|
+
# update index before diffing to avoid spurious diffs
|
1112
|
+
command('status')
|
1001
1113
|
command_lines(diff_command, opts).inject({}) do |memo, line|
|
1002
1114
|
info, file = line.split("\t")
|
1003
1115
|
mode_src, mode_dest, sha_src, sha_dest, type = info.split
|
@@ -1024,6 +1136,7 @@ module Git
|
|
1024
1136
|
|
1025
1137
|
arr_opts << "-#{opts[:count]}" if opts[:count]
|
1026
1138
|
arr_opts << "--no-color"
|
1139
|
+
arr_opts << "--cherry" if opts[:cherry]
|
1027
1140
|
arr_opts << "--since=#{opts[:since]}" if opts[:since].is_a? String
|
1028
1141
|
arr_opts << "--until=#{opts[:until]}" if opts[:until].is_a? String
|
1029
1142
|
arr_opts << "--grep=#{opts[:grep]}" if opts[:grep].is_a? String
|
@@ -1073,15 +1186,26 @@ module Git
|
|
1073
1186
|
def run_command(git_cmd, &block)
|
1074
1187
|
return IO.popen(git_cmd, &block) if block_given?
|
1075
1188
|
|
1076
|
-
`#{git_cmd}`.
|
1189
|
+
`#{git_cmd}`.lines.map { |l| normalize_encoding(l) }.join
|
1077
1190
|
end
|
1078
1191
|
|
1079
1192
|
def escape(s)
|
1080
|
-
|
1193
|
+
windows_platform? ? escape_for_windows(s) : escape_for_sh(s)
|
1194
|
+
end
|
1195
|
+
|
1196
|
+
def escape_for_sh(s)
|
1197
|
+
"'#{s && s.to_s.gsub('\'','\'"\'"\'')}'"
|
1198
|
+
end
|
1199
|
+
|
1200
|
+
def escape_for_windows(s)
|
1201
|
+
# Windows does not need single quote escaping inside double quotes
|
1202
|
+
%Q{"#{s}"}
|
1203
|
+
end
|
1081
1204
|
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1205
|
+
def windows_platform?
|
1206
|
+
# Check if on Windows via RUBY_PLATFORM (CRuby) and RUBY_DESCRIPTION (JRuby)
|
1207
|
+
win_platform_regex = /mingw|mswin/
|
1208
|
+
RUBY_PLATFORM =~ win_platform_regex || RUBY_DESCRIPTION =~ win_platform_regex
|
1085
1209
|
end
|
1086
1210
|
|
1087
1211
|
end
|