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.
@@ -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,7 @@
1
+ module Bringit
2
+ class Branch < Ref
3
+ def self.find(repository,name)
4
+ repository.branches.find { |branch| branch.name == name }
5
+ end
6
+ end
7
+ 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