bringit 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/bringit.rb +96 -0
- data/lib/bringit/attributes.rb +129 -0
- data/lib/bringit/blame.rb +73 -0
- data/lib/bringit/blob.rb +175 -0
- data/lib/bringit/blob_snippet.rb +30 -0
- data/lib/bringit/branch.rb +7 -0
- data/lib/bringit/cloning.rb +79 -0
- data/lib/bringit/commit.rb +331 -0
- data/lib/bringit/commit_stats.rb +24 -0
- data/lib/bringit/committing.rb +334 -0
- data/lib/bringit/committing/merge.rb +125 -0
- data/lib/bringit/compare.rb +41 -0
- data/lib/bringit/diff.rb +320 -0
- data/lib/bringit/diff_collection.rb +127 -0
- data/lib/bringit/encoding_helper.rb +56 -0
- data/lib/bringit/hook.rb +87 -0
- data/lib/bringit/index.rb +128 -0
- data/lib/bringit/path_helper.rb +14 -0
- data/lib/bringit/popen.rb +34 -0
- data/lib/bringit/pulling.rb +43 -0
- data/lib/bringit/ref.rb +56 -0
- data/lib/bringit/repository.rb +1230 -0
- data/lib/bringit/rev_list.rb +40 -0
- data/lib/bringit/tag.rb +19 -0
- data/lib/bringit/tree.rb +104 -0
- data/lib/bringit/util.rb +16 -0
- data/lib/bringit/version.rb +5 -0
- data/lib/bringit/version_info.rb +54 -0
- data/lib/bringit/wrapper.rb +136 -0
- metadata +137 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
module Bringit
|
2
|
+
class Compare
|
3
|
+
attr_reader :head, :base, :straight
|
4
|
+
|
5
|
+
def initialize(repository, base, head, straight = false)
|
6
|
+
@repository = repository
|
7
|
+
@straight = straight
|
8
|
+
|
9
|
+
unless base && head
|
10
|
+
@commits = []
|
11
|
+
return
|
12
|
+
end
|
13
|
+
|
14
|
+
@base = Bringit::Commit.find(repository, base.try(:strip))
|
15
|
+
@head = Bringit::Commit.find(repository, head.try(:strip))
|
16
|
+
|
17
|
+
@commits = [] unless @base && @head
|
18
|
+
@commits = [] if same
|
19
|
+
end
|
20
|
+
|
21
|
+
def same
|
22
|
+
@base && @head && @base.id == @head.id
|
23
|
+
end
|
24
|
+
|
25
|
+
def commits
|
26
|
+
return @commits if defined?(@commits)
|
27
|
+
|
28
|
+
@commits = Bringit::Commit.between(@repository, @base.id, @head.id)
|
29
|
+
end
|
30
|
+
|
31
|
+
def diffs(options = {})
|
32
|
+
unless @head && @base
|
33
|
+
return Bringit::DiffCollection.new([])
|
34
|
+
end
|
35
|
+
|
36
|
+
paths = options.delete(:paths) || []
|
37
|
+
options[:straight] = @straight
|
38
|
+
Bringit::Diff.between(@repository, @head.id, @base.id, options, *paths)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/bringit/diff.rb
ADDED
@@ -0,0 +1,320 @@
|
|
1
|
+
# Bringit::Diff is a wrapper around native Rugged::Diff object
|
2
|
+
module Bringit
|
3
|
+
class Diff
|
4
|
+
TimeoutError = Class.new(StandardError)
|
5
|
+
include Bringit::EncodingHelper
|
6
|
+
|
7
|
+
# Diff properties
|
8
|
+
attr_accessor :old_path, :new_path, :a_mode, :b_mode, :diff
|
9
|
+
|
10
|
+
# Stats properties
|
11
|
+
attr_accessor :new_file, :renamed_file, :deleted_file
|
12
|
+
|
13
|
+
attr_accessor :too_large
|
14
|
+
|
15
|
+
# The maximum size of a diff to display.
|
16
|
+
DIFF_SIZE_LIMIT = 102400 # 100 KB
|
17
|
+
|
18
|
+
# The maximum size before a diff is collapsed.
|
19
|
+
DIFF_COLLAPSE_LIMIT = 10240 # 10 KB
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def between(repo, head, base, options = {}, *paths)
|
23
|
+
straight = options.delete(:straight) || false
|
24
|
+
|
25
|
+
common_commit = if straight
|
26
|
+
base
|
27
|
+
else
|
28
|
+
# Only show what is new in the source branch
|
29
|
+
# compared to the target branch, not the other way
|
30
|
+
# around. The linex below with merge_base is
|
31
|
+
# equivalent to diff with three dots (git diff
|
32
|
+
# branch1...branch2) From the git documentation:
|
33
|
+
# "git diff A...B" is equivalent to "git diff
|
34
|
+
# $(git-merge-base A B) B"
|
35
|
+
repo.merge_base_commit(head, base)
|
36
|
+
end
|
37
|
+
|
38
|
+
options ||= {}
|
39
|
+
actual_options = filter_diff_options(options)
|
40
|
+
repo.diff(common_commit, head, actual_options, *paths)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Return a copy of the +options+ hash containing only keys that can be
|
44
|
+
# passed to Rugged. Allowed options are:
|
45
|
+
#
|
46
|
+
# :max_size ::
|
47
|
+
# An integer specifying the maximum byte size of a file before a it
|
48
|
+
# will be treated as binary. The default value is 512MB.
|
49
|
+
#
|
50
|
+
# :context_lines ::
|
51
|
+
# The number of unchanged lines that define the boundary of a hunk
|
52
|
+
# (and to display before and after the actual changes). The default is
|
53
|
+
# 3.
|
54
|
+
#
|
55
|
+
# :interhunk_lines ::
|
56
|
+
# The maximum number of unchanged lines between hunk boundaries before
|
57
|
+
# the hunks will be merged into a one. The default is 0.
|
58
|
+
#
|
59
|
+
# :old_prefix ::
|
60
|
+
# The virtual "directory" to prefix to old filenames in hunk headers.
|
61
|
+
# The default is "a".
|
62
|
+
#
|
63
|
+
# :new_prefix ::
|
64
|
+
# The virtual "directory" to prefix to new filenames in hunk headers.
|
65
|
+
# The default is "b".
|
66
|
+
#
|
67
|
+
# :reverse ::
|
68
|
+
# If true, the sides of the diff will be reversed.
|
69
|
+
#
|
70
|
+
# :force_text ::
|
71
|
+
# If true, all files will be treated as text, disabling binary
|
72
|
+
# attributes & detection.
|
73
|
+
#
|
74
|
+
# :ignore_whitespace ::
|
75
|
+
# If true, all whitespace will be ignored.
|
76
|
+
#
|
77
|
+
# :ignore_whitespace_change ::
|
78
|
+
# If true, changes in amount of whitespace will be ignored.
|
79
|
+
#
|
80
|
+
# :ignore_whitespace_eol ::
|
81
|
+
# If true, whitespace at end of line will be ignored.
|
82
|
+
#
|
83
|
+
# :ignore_submodules ::
|
84
|
+
# if true, submodules will be excluded from the diff completely.
|
85
|
+
#
|
86
|
+
# :patience ::
|
87
|
+
# If true, the "patience diff" algorithm will be used (currenlty
|
88
|
+
# unimplemented).
|
89
|
+
#
|
90
|
+
# :include_ignored ::
|
91
|
+
# If true, ignored files will be included in the diff.
|
92
|
+
#
|
93
|
+
# :include_untracked ::
|
94
|
+
# If true, untracked files will be included in the diff.
|
95
|
+
#
|
96
|
+
# :include_unmodified ::
|
97
|
+
# If true, unmodified files will be included in the diff.
|
98
|
+
#
|
99
|
+
# :recurse_untracked_dirs ::
|
100
|
+
# Even if +:include_untracked+ is true, untracked directories will
|
101
|
+
# only be marked with a single entry in the diff. If this flag is set
|
102
|
+
# to true, all files under ignored directories will be included in the
|
103
|
+
# diff, too.
|
104
|
+
#
|
105
|
+
# :disable_pathspec_match ::
|
106
|
+
# If true, the given +*paths+ will be applied as exact matches,
|
107
|
+
# instead of as fnmatch patterns.
|
108
|
+
#
|
109
|
+
# :deltas_are_icase ::
|
110
|
+
# If true, filename comparisons will be made with case-insensitivity.
|
111
|
+
#
|
112
|
+
# :include_untracked_content ::
|
113
|
+
# if true, untracked content will be contained in the the diff patch
|
114
|
+
# text.
|
115
|
+
#
|
116
|
+
# :skip_binary_check ::
|
117
|
+
# If true, diff deltas will be generated without spending time on
|
118
|
+
# binary detection. This is useful to improve performance in cases
|
119
|
+
# where the actual file content difference is not needed.
|
120
|
+
#
|
121
|
+
# :include_typechange ::
|
122
|
+
# If true, type changes for files will not be interpreted as deletion
|
123
|
+
# of the "old file" and addition of the "new file", but will generate
|
124
|
+
# typechange records.
|
125
|
+
#
|
126
|
+
# :include_typechange_trees ::
|
127
|
+
# Even if +:include_typechange+ is true, blob -> tree changes will
|
128
|
+
# still usually be handled as a deletion of the blob. If this flag is
|
129
|
+
# set to true, blob -> tree changes will be marked as typechanges.
|
130
|
+
#
|
131
|
+
# :ignore_filemode ::
|
132
|
+
# If true, file mode changes will be ignored.
|
133
|
+
#
|
134
|
+
# :recurse_ignored_dirs ::
|
135
|
+
# Even if +:include_ignored+ is true, ignored directories will only be
|
136
|
+
# marked with a single entry in the diff. If this flag is set to true,
|
137
|
+
# all files under ignored directories will be included in the diff,
|
138
|
+
# too.
|
139
|
+
def filter_diff_options(options, default_options = {})
|
140
|
+
allowed_options = [:max_size, :context_lines, :interhunk_lines,
|
141
|
+
:old_prefix, :new_prefix, :reverse, :force_text,
|
142
|
+
:ignore_whitespace, :ignore_whitespace_change,
|
143
|
+
:ignore_whitespace_eol, :ignore_submodules,
|
144
|
+
:patience, :include_ignored, :include_untracked,
|
145
|
+
:include_unmodified, :recurse_untracked_dirs,
|
146
|
+
:disable_pathspec_match, :deltas_are_icase,
|
147
|
+
:include_untracked_content, :skip_binary_check,
|
148
|
+
:include_typechange, :include_typechange_trees,
|
149
|
+
:ignore_filemode, :recurse_ignored_dirs, :paths,
|
150
|
+
:max_files, :max_lines, :all_diffs, :no_collapse]
|
151
|
+
|
152
|
+
if default_options
|
153
|
+
actual_defaults = default_options.dup
|
154
|
+
actual_defaults.keep_if do |key|
|
155
|
+
allowed_options.include?(key)
|
156
|
+
end
|
157
|
+
else
|
158
|
+
actual_defaults = {}
|
159
|
+
end
|
160
|
+
|
161
|
+
if options
|
162
|
+
filtered_opts = options.dup
|
163
|
+
filtered_opts.keep_if do |key|
|
164
|
+
allowed_options.include?(key)
|
165
|
+
end
|
166
|
+
filtered_opts = actual_defaults.merge(filtered_opts)
|
167
|
+
else
|
168
|
+
filtered_opts = actual_defaults
|
169
|
+
end
|
170
|
+
|
171
|
+
filtered_opts
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def initialize(raw_diff, collapse: false)
|
176
|
+
case raw_diff
|
177
|
+
when Hash
|
178
|
+
init_from_hash(raw_diff, collapse: collapse)
|
179
|
+
when Rugged::Patch, Rugged::Diff::Delta
|
180
|
+
init_from_rugged(raw_diff, collapse: collapse)
|
181
|
+
when nil
|
182
|
+
raise "Nil as raw diff passed"
|
183
|
+
else
|
184
|
+
raise "Invalid raw diff type: #{raw_diff.class}"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def serialize_keys
|
189
|
+
@serialize_keys ||= %i(diff new_path old_path a_mode b_mode new_file renamed_file deleted_file too_large)
|
190
|
+
end
|
191
|
+
|
192
|
+
def to_hash
|
193
|
+
hash = {}
|
194
|
+
|
195
|
+
keys = serialize_keys
|
196
|
+
|
197
|
+
keys.each do |key|
|
198
|
+
hash[key] = send(key)
|
199
|
+
end
|
200
|
+
|
201
|
+
hash
|
202
|
+
end
|
203
|
+
|
204
|
+
def submodule?
|
205
|
+
a_mode == '160000' || b_mode == '160000'
|
206
|
+
end
|
207
|
+
|
208
|
+
def line_count
|
209
|
+
@line_count ||= Util.count_lines(@diff)
|
210
|
+
end
|
211
|
+
|
212
|
+
def too_large?
|
213
|
+
if @too_large.nil?
|
214
|
+
@too_large = @diff.bytesize >= DIFF_SIZE_LIMIT
|
215
|
+
else
|
216
|
+
@too_large
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def collapsible?
|
221
|
+
@diff.bytesize >= DIFF_COLLAPSE_LIMIT
|
222
|
+
end
|
223
|
+
|
224
|
+
def prune_large_diff!
|
225
|
+
@diff = ''
|
226
|
+
@line_count = 0
|
227
|
+
@too_large = true
|
228
|
+
end
|
229
|
+
|
230
|
+
def collapsed?
|
231
|
+
return @collapsed if defined?(@collapsed)
|
232
|
+
false
|
233
|
+
end
|
234
|
+
|
235
|
+
def prune_collapsed_diff!
|
236
|
+
@diff = ''
|
237
|
+
@line_count = 0
|
238
|
+
@collapsed = true
|
239
|
+
end
|
240
|
+
|
241
|
+
private
|
242
|
+
|
243
|
+
def init_from_rugged(rugged, collapse: false)
|
244
|
+
if rugged.is_a?(Rugged::Patch)
|
245
|
+
init_from_rugged_patch(rugged, collapse: collapse)
|
246
|
+
d = rugged.delta
|
247
|
+
else
|
248
|
+
d = rugged
|
249
|
+
end
|
250
|
+
|
251
|
+
@new_path = encode!(d.new_file[:path])
|
252
|
+
@old_path = encode!(d.old_file[:path])
|
253
|
+
@a_mode = d.old_file[:mode].to_s(8)
|
254
|
+
@b_mode = d.new_file[:mode].to_s(8)
|
255
|
+
@new_file = d.added?
|
256
|
+
@renamed_file = d.renamed?
|
257
|
+
@deleted_file = d.deleted?
|
258
|
+
end
|
259
|
+
|
260
|
+
def init_from_rugged_patch(patch, collapse: false)
|
261
|
+
# Don't bother initializing diffs that are too large. If a diff is
|
262
|
+
# binary we're not going to display anything so we skip the size check.
|
263
|
+
return if !patch.delta.binary? && prune_large_patch(patch, collapse)
|
264
|
+
|
265
|
+
@diff = encode!(strip_diff_headers(patch.to_s))
|
266
|
+
end
|
267
|
+
|
268
|
+
def init_from_hash(hash, collapse: false)
|
269
|
+
raw_diff = hash.symbolize_keys
|
270
|
+
|
271
|
+
serialize_keys.each do |key|
|
272
|
+
send(:"#{key}=", raw_diff[key.to_sym])
|
273
|
+
end
|
274
|
+
|
275
|
+
prune_large_diff! if too_large?
|
276
|
+
prune_collapsed_diff! if collapse && collapsible?
|
277
|
+
end
|
278
|
+
|
279
|
+
# If the patch surpasses any of the diff limits it calls the appropiate
|
280
|
+
# prune method and returns true. Otherwise returns false.
|
281
|
+
def prune_large_patch(patch, collapse)
|
282
|
+
size = 0
|
283
|
+
|
284
|
+
patch.each_hunk do |hunk|
|
285
|
+
hunk.each_line do |line|
|
286
|
+
size += line.content.bytesize
|
287
|
+
|
288
|
+
if size >= DIFF_SIZE_LIMIT
|
289
|
+
prune_large_diff!
|
290
|
+
return true
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
if collapse && size >= DIFF_COLLAPSE_LIMIT
|
296
|
+
prune_collapsed_diff!
|
297
|
+
return true
|
298
|
+
end
|
299
|
+
|
300
|
+
false
|
301
|
+
end
|
302
|
+
|
303
|
+
# Strip out the information at the beginning of the patch's text to match
|
304
|
+
# Grit's output
|
305
|
+
def strip_diff_headers(diff_text)
|
306
|
+
# Delete everything up to the first line that starts with '---' or
|
307
|
+
# 'Binary'
|
308
|
+
diff_text.sub!(/\A.*?^(---|Binary)/m, '\1')
|
309
|
+
|
310
|
+
if diff_text.start_with?('---', 'Binary')
|
311
|
+
diff_text
|
312
|
+
else
|
313
|
+
# If the diff_text did not contain a line starting with '---' or
|
314
|
+
# 'Binary', return the empty string. No idea why; we are just
|
315
|
+
# preserving behavior from before the refactor.
|
316
|
+
''
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Bringit
|
2
|
+
class DiffCollection
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
DEFAULT_LIMITS = { max_files: 100, max_lines: 5000 }.freeze
|
6
|
+
|
7
|
+
def initialize(iterator, options = {})
|
8
|
+
@iterator = iterator
|
9
|
+
@max_files = options.fetch(:max_files, DEFAULT_LIMITS[:max_files])
|
10
|
+
@max_lines = options.fetch(:max_lines, DEFAULT_LIMITS[:max_lines])
|
11
|
+
@max_bytes = @max_files * 5120 # Average 5 KB per file
|
12
|
+
@safe_max_files = [@max_files, DEFAULT_LIMITS[:max_files]].min
|
13
|
+
@safe_max_lines = [@max_lines, DEFAULT_LIMITS[:max_lines]].min
|
14
|
+
@safe_max_bytes = @safe_max_files * 5120 # Average 5 KB per file
|
15
|
+
@all_diffs = !!options.fetch(:all_diffs, false)
|
16
|
+
@no_collapse = !!options.fetch(:no_collapse, true)
|
17
|
+
@deltas_only = !!options.fetch(:deltas_only, false)
|
18
|
+
|
19
|
+
@line_count = 0
|
20
|
+
@byte_count = 0
|
21
|
+
@overflow = false
|
22
|
+
@array = Array.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def each(&block)
|
26
|
+
if @populated
|
27
|
+
# @iterator.each is slower than just iterating the array in place
|
28
|
+
@array.each(&block)
|
29
|
+
elsif @deltas_only
|
30
|
+
each_delta(&block)
|
31
|
+
else
|
32
|
+
each_patch(&block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def empty?
|
37
|
+
!@iterator.any?
|
38
|
+
end
|
39
|
+
|
40
|
+
def overflow?
|
41
|
+
populate!
|
42
|
+
!!@overflow
|
43
|
+
end
|
44
|
+
|
45
|
+
def size
|
46
|
+
@size ||= count # forces a loop using each method
|
47
|
+
end
|
48
|
+
|
49
|
+
def real_size
|
50
|
+
populate!
|
51
|
+
|
52
|
+
if @overflow
|
53
|
+
"#{size}+"
|
54
|
+
else
|
55
|
+
size.to_s
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def decorate!
|
60
|
+
collection = each_with_index do |element, i|
|
61
|
+
@array[i] = yield(element)
|
62
|
+
end
|
63
|
+
@populated = true
|
64
|
+
collection
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def populate!
|
70
|
+
return if @populated
|
71
|
+
|
72
|
+
each { nil } # force a loop through all diffs
|
73
|
+
@populated = true
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
77
|
+
def over_safe_limits?(files)
|
78
|
+
files >= @safe_max_files || @line_count > @safe_max_lines || @byte_count >= @safe_max_bytes
|
79
|
+
end
|
80
|
+
|
81
|
+
def each_delta
|
82
|
+
@iterator.each_delta.with_index do |delta, i|
|
83
|
+
diff = Bringit::Diff.new(delta)
|
84
|
+
|
85
|
+
yield @array[i] = diff
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def each_patch
|
90
|
+
@iterator.each_with_index do |raw, i|
|
91
|
+
# First yield cached Diff instances from @array
|
92
|
+
if @array[i]
|
93
|
+
yield @array[i]
|
94
|
+
next
|
95
|
+
end
|
96
|
+
|
97
|
+
# We have exhausted @array, time to create new Diff instances or stop.
|
98
|
+
break if @overflow
|
99
|
+
|
100
|
+
if !@all_diffs && i >= @max_files
|
101
|
+
@overflow = true
|
102
|
+
break
|
103
|
+
end
|
104
|
+
|
105
|
+
collapse = !@all_diffs && !@no_collapse
|
106
|
+
|
107
|
+
diff = Bringit::Diff.new(raw, collapse: collapse)
|
108
|
+
|
109
|
+
if collapse && over_safe_limits?(i)
|
110
|
+
diff.prune_collapsed_diff!
|
111
|
+
end
|
112
|
+
|
113
|
+
@line_count += diff.line_count
|
114
|
+
@byte_count += diff.diff.bytesize
|
115
|
+
|
116
|
+
if !@all_diffs && (@line_count >= @max_lines || @byte_count >= @max_bytes)
|
117
|
+
# This last Diff instance pushes us over the lines limit. We stop and
|
118
|
+
# discard it.
|
119
|
+
@overflow = true
|
120
|
+
break
|
121
|
+
end
|
122
|
+
|
123
|
+
yield @array[i] = diff
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|