bringit 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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