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,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
|