bringit 1.0.0
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 +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,30 @@
|
|
1
|
+
module Bringit
|
2
|
+
class BlobSnippet
|
3
|
+
include Linguist::BlobHelper
|
4
|
+
|
5
|
+
attr_accessor :ref
|
6
|
+
attr_accessor :lines
|
7
|
+
attr_accessor :filename
|
8
|
+
attr_accessor :startline
|
9
|
+
|
10
|
+
def initialize(ref, lines, startline, filename)
|
11
|
+
@ref, @lines, @startline, @filename = ref, lines, startline, filename
|
12
|
+
end
|
13
|
+
|
14
|
+
def data
|
15
|
+
lines&.join("\n")
|
16
|
+
end
|
17
|
+
|
18
|
+
def name
|
19
|
+
filename
|
20
|
+
end
|
21
|
+
|
22
|
+
def size
|
23
|
+
data.length
|
24
|
+
end
|
25
|
+
|
26
|
+
def mode
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bringit
|
4
|
+
# Methods for cloning
|
5
|
+
module Cloning
|
6
|
+
class Error < StandardError; end
|
7
|
+
class InvalidRemoteError < Error; end
|
8
|
+
|
9
|
+
# ... that are invoked from the class
|
10
|
+
module ClassMethods
|
11
|
+
def clone(path, remote)
|
12
|
+
if remote_git?(remote)
|
13
|
+
clone_git(path, remote)
|
14
|
+
# rubocop:disable Lint/AssignmentInCondition
|
15
|
+
elsif layout = remote_svn_layout(remote) # remote_svn?
|
16
|
+
clone_svn(path, remote, layout)
|
17
|
+
else
|
18
|
+
raise InvalidRemoteError
|
19
|
+
end
|
20
|
+
new(path)
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid_remote?(remote)
|
24
|
+
remote_git?(remote) || !!remote_svn_layout(remote)
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def clone_git(path, remote)
|
30
|
+
Popen.popen(%W(git clone --mirror #{remote} #{path}))
|
31
|
+
end
|
32
|
+
|
33
|
+
def clone_svn(path, remote, layout)
|
34
|
+
Dir.mktmpdir do |tmpdir|
|
35
|
+
tmppath = clone_svn_to_temppath(remote, layout, tmpdir)
|
36
|
+
convert_to_bare_and_move_to_path(path, tmppath)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def clone_svn_to_temppath(remote, layout, tmpdir)
|
41
|
+
tmppath = File.join(tmpdir, 'clone.git-svn')
|
42
|
+
if layout == :standard
|
43
|
+
Popen.popen(%W(git svn clone --stdlayout #{remote} #{tmppath}))
|
44
|
+
else
|
45
|
+
Popen.popen(%W(git svn clone #{remote} #{tmppath}))
|
46
|
+
end
|
47
|
+
tmppath
|
48
|
+
end
|
49
|
+
|
50
|
+
def convert_to_bare_and_move_to_path(path, tmppath)
|
51
|
+
FileUtils.mkdir_p(File.dirname(path))
|
52
|
+
FileUtils.mv(File.join(tmppath, '.git'), path)
|
53
|
+
Popen.popen(%w(git config --bool core.bare true), path)
|
54
|
+
end
|
55
|
+
|
56
|
+
def remote_git?(remote)
|
57
|
+
# GIT_ASKPASS is set to the 'true' executable. It simply returns
|
58
|
+
# successfully. This way, no credentials are supplied.
|
59
|
+
_out, status = Popen.popen(%w(git ls-remote -h) + [remote],
|
60
|
+
nil,
|
61
|
+
'GIT_ASKPASS' => 'true')
|
62
|
+
status.zero?
|
63
|
+
end
|
64
|
+
|
65
|
+
def remote_svn_layout(remote)
|
66
|
+
out, status = Popen.popen(%w(svn ls) + [remote])
|
67
|
+
if status.zero?
|
68
|
+
if out.split("\n") == %w(branches/ tags/ trunk/)
|
69
|
+
:standard
|
70
|
+
else
|
71
|
+
:non_standard
|
72
|
+
end
|
73
|
+
else
|
74
|
+
false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,331 @@
|
|
1
|
+
# Bringit::Commit is a wrapper around native Rugged::Commit object
|
2
|
+
module Bringit
|
3
|
+
class Commit
|
4
|
+
include Bringit::EncodingHelper
|
5
|
+
|
6
|
+
attr_reader :repository
|
7
|
+
attr_accessor :raw_commit, :head, :refs
|
8
|
+
|
9
|
+
SERIALIZE_KEYS = [
|
10
|
+
:id, :message, :parent_ids,
|
11
|
+
:authored_date, :author_name, :author_email,
|
12
|
+
:committed_date, :committer_name, :committer_email
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
attr_accessor *SERIALIZE_KEYS # rubocop:disable Lint/AmbiguousOperator
|
16
|
+
|
17
|
+
delegate :tree, to: :raw_commit
|
18
|
+
|
19
|
+
def ==(other)
|
20
|
+
return false unless other.is_a?(Bringit::Commit)
|
21
|
+
|
22
|
+
methods = [:message, :parent_ids, :authored_date, :author_name,
|
23
|
+
:author_email, :committed_date, :committer_name,
|
24
|
+
:committer_email]
|
25
|
+
|
26
|
+
methods.all? do |method|
|
27
|
+
send(method) == other.send(method)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class << self
|
32
|
+
# Get commits collection
|
33
|
+
#
|
34
|
+
# Ex.
|
35
|
+
# Commit.where(
|
36
|
+
# repo: repo,
|
37
|
+
# ref: 'master',
|
38
|
+
# path: 'app/models',
|
39
|
+
# limit: 10,
|
40
|
+
# offset: 5,
|
41
|
+
# )
|
42
|
+
#
|
43
|
+
def where(options)
|
44
|
+
repo = options.delete(:repo)
|
45
|
+
raise 'Bringit::Repository is required' unless repo.respond_to?(:log)
|
46
|
+
|
47
|
+
repo.log(options).map { |c| decorate(c, repo) }
|
48
|
+
end
|
49
|
+
|
50
|
+
# Get single commit
|
51
|
+
#
|
52
|
+
# Ex.
|
53
|
+
# Commit.find(repo, '29eda46b')
|
54
|
+
#
|
55
|
+
# Commit.find(repo, 'master')
|
56
|
+
#
|
57
|
+
def find(repo, commit_id = "HEAD")
|
58
|
+
return decorate(commit_id, repo) if commit_id.is_a?(Rugged::Commit)
|
59
|
+
|
60
|
+
obj = if commit_id.is_a?(String)
|
61
|
+
repo.rev_parse_target(commit_id)
|
62
|
+
else
|
63
|
+
Bringit::Ref.dereference_object(commit_id)
|
64
|
+
end
|
65
|
+
|
66
|
+
return nil unless obj.is_a?(Rugged::Commit)
|
67
|
+
|
68
|
+
decorate(obj, repo)
|
69
|
+
rescue Rugged::ReferenceError, Rugged::InvalidError, Rugged::ObjectError, Bringit::Repository::NoRepository
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
|
73
|
+
# Get last commit for HEAD
|
74
|
+
#
|
75
|
+
# Ex.
|
76
|
+
# Commit.last(repo)
|
77
|
+
#
|
78
|
+
def last(repo)
|
79
|
+
find(repo)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Get last commit for specified path and ref
|
83
|
+
#
|
84
|
+
# Ex.
|
85
|
+
# Commit.last_for_path(repo, '29eda46b', 'app/models')
|
86
|
+
#
|
87
|
+
# Commit.last_for_path(repo, 'master', 'Gemfile')
|
88
|
+
#
|
89
|
+
def last_for_path(repo, ref, path = nil)
|
90
|
+
where(
|
91
|
+
repo: repo,
|
92
|
+
ref: ref,
|
93
|
+
path: path,
|
94
|
+
limit: 1
|
95
|
+
).first
|
96
|
+
end
|
97
|
+
|
98
|
+
# Get commits between two revspecs
|
99
|
+
# See also #repository.commits_between
|
100
|
+
#
|
101
|
+
# Ex.
|
102
|
+
# Commit.between(repo, '29eda46b', 'master')
|
103
|
+
#
|
104
|
+
def between(repo, base, head)
|
105
|
+
repo.commits_between(base, head).map do |commit|
|
106
|
+
decorate(commit, repo)
|
107
|
+
end
|
108
|
+
rescue Rugged::ReferenceError
|
109
|
+
[]
|
110
|
+
end
|
111
|
+
|
112
|
+
# Delegate Repository#find_commits
|
113
|
+
def find_all(repo, options = {})
|
114
|
+
repo.find_commits(options)
|
115
|
+
end
|
116
|
+
|
117
|
+
def decorate(commit, repository, ref = nil)
|
118
|
+
Bringit::Commit.new(commit, repository, ref)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns a diff object for the changes introduced by +rugged_commit+.
|
122
|
+
# If +rugged_commit+ doesn't have a parent, then the diff is between
|
123
|
+
# this commit and an empty repo. See Repository#diff for the keys
|
124
|
+
# allowed in the +options+ hash.
|
125
|
+
def diff_from_parent(rugged_commit, options = {})
|
126
|
+
options ||= {}
|
127
|
+
break_rewrites = options[:break_rewrites]
|
128
|
+
actual_options = Bringit::Diff.filter_diff_options(options)
|
129
|
+
|
130
|
+
diff = if rugged_commit.parents.empty?
|
131
|
+
rugged_commit.diff(actual_options.merge(reverse: true))
|
132
|
+
else
|
133
|
+
rugged_commit.parents[0].diff(rugged_commit, actual_options)
|
134
|
+
end
|
135
|
+
|
136
|
+
diff.find_similar!(break_rewrites: break_rewrites)
|
137
|
+
diff
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def initialize(raw_commit, repository = nil, head = nil)
|
142
|
+
raise "Nil as raw commit passed" unless raw_commit
|
143
|
+
|
144
|
+
if raw_commit.is_a?(Hash)
|
145
|
+
init_from_hash(raw_commit)
|
146
|
+
elsif raw_commit.is_a?(Rugged::Commit)
|
147
|
+
init_from_rugged(raw_commit)
|
148
|
+
else
|
149
|
+
raise "Invalid raw commit type: #{raw_commit.class}"
|
150
|
+
end
|
151
|
+
|
152
|
+
@repository = repository
|
153
|
+
@head = head
|
154
|
+
end
|
155
|
+
|
156
|
+
def sha
|
157
|
+
id
|
158
|
+
end
|
159
|
+
|
160
|
+
def short_id(length = 10)
|
161
|
+
id.to_s[0..length]
|
162
|
+
end
|
163
|
+
|
164
|
+
def safe_message
|
165
|
+
@safe_message ||= message
|
166
|
+
end
|
167
|
+
|
168
|
+
def created_at
|
169
|
+
committed_date
|
170
|
+
end
|
171
|
+
|
172
|
+
# Was this commit committed by a different person than the original author?
|
173
|
+
def different_committer?
|
174
|
+
author_name != committer_name || author_email != committer_email
|
175
|
+
end
|
176
|
+
|
177
|
+
def parent_id
|
178
|
+
parent_ids.first
|
179
|
+
end
|
180
|
+
|
181
|
+
# Shows the diff between the commit's parent and the commit.
|
182
|
+
#
|
183
|
+
# Cuts out the header and stats from #to_patch and returns only the diff.
|
184
|
+
def to_diff(options = {})
|
185
|
+
diff_from_parent(options).patch
|
186
|
+
end
|
187
|
+
|
188
|
+
# Returns a diff object for the changes from this commit's first parent.
|
189
|
+
# If there is no parent, then the diff is between this commit and an
|
190
|
+
# empty repo. See Repository#diff for keys allowed in the +options+
|
191
|
+
# hash.
|
192
|
+
def diff_from_parent(options = {})
|
193
|
+
Commit.diff_from_parent(raw_commit, options)
|
194
|
+
end
|
195
|
+
|
196
|
+
def has_zero_stats?
|
197
|
+
stats.total.zero?
|
198
|
+
rescue
|
199
|
+
true
|
200
|
+
end
|
201
|
+
|
202
|
+
def no_commit_message
|
203
|
+
"--no commit message"
|
204
|
+
end
|
205
|
+
|
206
|
+
def to_hash
|
207
|
+
serialize_keys.map.with_object({}) do |key, hash|
|
208
|
+
hash[key] = send(key)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def date
|
213
|
+
committed_date
|
214
|
+
end
|
215
|
+
|
216
|
+
def diffs(options = {})
|
217
|
+
Bringit::DiffCollection.new(diff_from_parent(options), options)
|
218
|
+
end
|
219
|
+
|
220
|
+
def parents
|
221
|
+
raw_commit.parents.map { |c| Bringit::Commit.new(c, repository) }
|
222
|
+
end
|
223
|
+
|
224
|
+
def stats
|
225
|
+
Bringit::CommitStats.new(self)
|
226
|
+
end
|
227
|
+
|
228
|
+
def to_patch(options = {})
|
229
|
+
begin
|
230
|
+
raw_commit.to_mbox(options)
|
231
|
+
rescue Rugged::InvalidError => ex
|
232
|
+
if ex.message =~ /Commit \w+ is a merge commit/
|
233
|
+
'Patch format is not currently supported for merge commits.'
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# Get a collection of Rugged::Reference objects for this commit.
|
239
|
+
#
|
240
|
+
# Ex.
|
241
|
+
# commit.ref
|
242
|
+
#
|
243
|
+
def refs
|
244
|
+
repository.refs_hash[id]
|
245
|
+
end
|
246
|
+
|
247
|
+
# Get a collection of Bringit::Ref (its subclasses) objects
|
248
|
+
def references
|
249
|
+
refs.map do |ref|
|
250
|
+
if ref.name.match(%r{\Arefs/heads/})
|
251
|
+
Bringit::Branch.new(repository, ref.name, ref.target)
|
252
|
+
elsif ref.name.match(%r{\Arefs/tags/})
|
253
|
+
message = nil
|
254
|
+
|
255
|
+
if ref.target.is_a?(Rugged::Tag::Annotation)
|
256
|
+
tag_message = ref.target.message
|
257
|
+
|
258
|
+
if tag_message.respond_to?(:chomp)
|
259
|
+
message = tag_message.chomp
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
Bringit::Tag.new(self, ref.name, ref.target, message)
|
264
|
+
else
|
265
|
+
Bringit::Ref.new(repository, ref.name, ref.target)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
# Get ref names collection
|
271
|
+
#
|
272
|
+
# Ex.
|
273
|
+
# commit.ref_names
|
274
|
+
#
|
275
|
+
def ref_names
|
276
|
+
repository.refs_hash[id].map do |ref|
|
277
|
+
ref.name.sub(%r{^refs/(heads|remotes|tags)/}, "")
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def message
|
282
|
+
encode! @message
|
283
|
+
end
|
284
|
+
|
285
|
+
def author_name
|
286
|
+
encode! @author_name
|
287
|
+
end
|
288
|
+
|
289
|
+
def author_email
|
290
|
+
encode! @author_email
|
291
|
+
end
|
292
|
+
|
293
|
+
def committer_name
|
294
|
+
encode! @committer_name
|
295
|
+
end
|
296
|
+
|
297
|
+
def committer_email
|
298
|
+
encode! @committer_email
|
299
|
+
end
|
300
|
+
|
301
|
+
private
|
302
|
+
|
303
|
+
def init_from_hash(hash)
|
304
|
+
raw_commit = hash.symbolize_keys
|
305
|
+
|
306
|
+
serialize_keys.each do |key|
|
307
|
+
send("#{key}=", raw_commit[key])
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def init_from_rugged(commit)
|
312
|
+
author = commit.author
|
313
|
+
committer = commit.committer
|
314
|
+
|
315
|
+
@raw_commit = commit
|
316
|
+
@id = commit.oid
|
317
|
+
@message = commit.message
|
318
|
+
@authored_date = author[:time]
|
319
|
+
@committed_date = committer[:time]
|
320
|
+
@author_name = author[:name]
|
321
|
+
@author_email = author[:email]
|
322
|
+
@committer_name = committer[:name]
|
323
|
+
@committer_email = committer[:email]
|
324
|
+
@parent_ids = commit.parents.map(&:oid)
|
325
|
+
end
|
326
|
+
|
327
|
+
def serialize_keys
|
328
|
+
SERIALIZE_KEYS
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|