svn_record 1.2.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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +72 -0
  3. data/Rakefile +7 -0
  4. data/app/assets/images/svn_record/file_icon/bullet_add.png +0 -0
  5. data/app/assets/images/svn_record/file_icon/bullet_black.png +0 -0
  6. data/app/assets/images/svn_record/file_icon/bullet_blue.png +0 -0
  7. data/app/assets/images/svn_record/file_icon/bullet_delete.png +0 -0
  8. data/app/assets/images/svn_record/file_icon/bullet_diamond.png +0 -0
  9. data/app/assets/images/svn_record/file_icon/bullet_end.png +0 -0
  10. data/app/assets/images/svn_record/file_icon/bullet_go.png +0 -0
  11. data/app/assets/images/svn_record/file_icon/bullet_orange.png +0 -0
  12. data/app/assets/images/svn_record/file_icon/bullet_purple.png +0 -0
  13. data/app/assets/images/svn_record/file_icon/bullet_toggle_minus.png +0 -0
  14. data/app/assets/images/svn_record/file_icon/bullet_toggle_plus.png +0 -0
  15. data/app/assets/images/svn_record/file_icon/dir.png +0 -0
  16. data/app/assets/images/svn_record/file_icon/file.png +0 -0
  17. data/app/assets/images/svn_record/file_icon/folder.png +0 -0
  18. data/app/assets/images/svn_record/file_icon/folder_open.png +0 -0
  19. data/app/assets/images/svn_record/file_icon/folder_open_add.png +0 -0
  20. data/app/assets/images/svn_record/file_icon/folder_open_orange.png +0 -0
  21. data/app/assets/javascripts/svn_record/application.js +16 -0
  22. data/app/assets/javascripts/svn_record/change.js.coffee +4 -0
  23. data/app/assets/stylesheets/svn_record/application.css +14 -0
  24. data/app/assets/stylesheets/svn_record/site.css +396 -0
  25. data/app/controllers/svn_record/application_controller.rb +3 -0
  26. data/app/controllers/svn_record/repository/application_controller.rb +3 -0
  27. data/app/controllers/svn_record/repository/changes_controller.rb +38 -0
  28. data/app/models/svn_record/repository/change.rb +39 -0
  29. data/app/models/svn_record/repository/file.rb +20 -0
  30. data/app/models/svn_record/repository/user.rb +14 -0
  31. data/app/views/layouts/svn_record/subversion.html.slim +22 -0
  32. data/app/views/svn_record/repository/changes/_changesets_list.slim +29 -0
  33. data/app/views/svn_record/repository/changes/diff.html.slim +30 -0
  34. data/app/views/svn_record/repository/changes/entry.html.slim +18 -0
  35. data/app/views/svn_record/repository/changes/index.html.slim +34 -0
  36. data/app/views/svn_record/repository/changes/list.html.slim +2 -0
  37. data/app/views/svn_record/repository/changes/revisions.html.slim +18 -0
  38. data/config/configuration.yml +5 -0
  39. data/config/routes.rb +17 -0
  40. data/lib/Svn/helper.rb +108 -0
  41. data/lib/Svn/mime_type.rb +492 -0
  42. data/lib/Svn/scm/adapters/subversion.rb +303 -0
  43. data/lib/generators/svn_record/install_generator.rb +16 -0
  44. data/lib/generators/svn_record/templates/create_repository_changes.rb +17 -0
  45. data/lib/generators/svn_record/templates/create_repository_files.rb +21 -0
  46. data/lib/generators/svn_record/templates/create_repository_user.rb +8 -0
  47. data/lib/svn_record/engine.rb +11 -0
  48. data/lib/svn_record/version.rb +3 -0
  49. data/lib/svn_record.rb +4 -0
  50. metadata +161 -0
@@ -0,0 +1,303 @@
1
+ require 'uri'
2
+ module Svn
3
+ module Scm
4
+ module Adapters
5
+ class CommandFailed < StandardError; end
6
+ class Subversion < Struct.new(:url, :login, :password, :root_name, :root_url)
7
+ class ScmCommandAborted < CommandFailed; end
8
+
9
+ def initialize
10
+ options = YAML.load(File.open("#{Rails.root}/config/configuration.yml")).values.first
11
+ self.url = options['url']
12
+ self.login = options['login']
13
+ self.password = options['password']
14
+ self.root_url = options['root_url']
15
+ self.root_name = url.gsub(Regexp.new(root_url),'')
16
+ end
17
+
18
+ # shellout
19
+ def shellout(cmd, options = {}, &block)
20
+ Rails.logger.debug("Shelling out: #{strip_credential(cmd)}" )
21
+ IO.popen(cmd, "r+") do |io|
22
+ io.set_encoding("ASCII-8BIT") if io.respond_to?(:set_encoding)
23
+ io.close_write unless options[:write_stdin]
24
+ if options[:mark]
25
+ block.call(io) if block_given?
26
+ else
27
+ output = io.read
28
+ output.force_encoding('UTF-8') if output.respond_to?(:force_encoding)
29
+ doc = parse_xml(output)
30
+ block.call(doc,output) if block_given?
31
+ end
32
+ end
33
+ rescue
34
+ end
35
+
36
+ def info
37
+ info = nil
38
+ cmd = "svn info --xml #{target}"
39
+ cmd << credentials_string
40
+ shellout(cmd){ |doc,_| info = Info.get_instance(doc) }
41
+ info
42
+ end
43
+
44
+ def entries(path='', identifier=nil, options={})
45
+ identifier = identifier.to_i > 0 ? identifier.to_i : "HEAD"
46
+ entries = Entries.new
47
+ cmd = " svn list --xml #{target(path)}@#{identifier} #{credentials_string}"
48
+ shellout(cmd) do |doc,_|
49
+ begin
50
+ changes = SvnRecord::Repository::Change.all.dup
51
+ each_xml_element(doc['lists']['list'], 'entry') do |entry|
52
+ commit = entry['commit']
53
+ commit_date = commit['date']
54
+ next if entry['kind'] == 'dir' && commit_date.nil?
55
+ name = entry['name']['__content__']
56
+ entries << Entry.new({:name => URI.unescape(name),
57
+ :path => ((path.empty? ? "" : "#{path}/") + name),
58
+ :kind => entry['kind'],
59
+ :size => ((s = entry['size']) ? s['__content__'].to_i : nil),
60
+ :lastrev => Revision.new({
61
+ :identifier => commit['revision'],
62
+ :time => Time.parse(commit_date['__content__'].to_s).localtime,
63
+ :author => ((a = commit['author']) ? a['__content__'] : nil),
64
+ :message => (change = changes.detect{|a| a.revision == commit['revision'].to_i}) ? change.comment : ""
65
+ })
66
+ })
67
+ end
68
+ rescue Exception => e
69
+ Rails.logger.error("Error parsing svn output: #{e.message}")
70
+ end
71
+ end
72
+ return nil if $? && $?.exitstatus != 0
73
+ entries.sort_by_name
74
+ end
75
+
76
+ def revisions(path='', identifier_from=nil, identifier_to=nil, options={})
77
+ identifier_from = (identifier_from && identifier_from.to_i > 0) ? identifier_from.to_i : "HEAD"
78
+ identifier_to = (identifier_to && identifier_to.to_i > 0) ? identifier_to.to_i : 1
79
+ revisions = Revisions.new
80
+ cmd = "svn log --xml -r #{identifier_from}:#{identifier_to}"
81
+ cmd << credentials_string
82
+ cmd << " --verbose " if options[:with_paths]
83
+ cmd << " --limit #{options[:limit].to_i}" if options[:limit]
84
+ cmd << ' ' + target(path)
85
+ shellout(cmd) do |doc,_|
86
+ begin
87
+ each_xml_element(doc['log'], 'logentry') do |logentry|
88
+ paths = []
89
+ each_xml_element(logentry['paths'], 'path') do |path|
90
+ paths << { :action => path['action'],
91
+ :path => path['__content__'],
92
+ :from_path => path['copyfrom-path'],
93
+ :from_revision => path['copyfrom-rev']
94
+ }
95
+ end if logentry['paths'] && logentry['paths']['path']
96
+ paths.sort! { |x,y| x[:path] <=> y[:path] }
97
+ revisions << Revision.new({:identifier => logentry['revision'],
98
+ :author => (logentry['author'] ? logentry['author']['__content__'] : ""),
99
+ :time => Time.parse(logentry['date']['__content__'].to_s).localtime,
100
+ :message => logentry['msg']['__content__'],
101
+ :paths => paths
102
+ })
103
+ end
104
+ rescue
105
+ end
106
+ end
107
+ return nil if $? && $?.exitstatus != 0
108
+ revisions
109
+ end
110
+
111
+ def diff(path, identifier_from, identifier_to=nil)
112
+ path ||= ''
113
+ identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : ''
114
+ identifier_to = (identifier_to and identifier_to.to_i > 0) ? identifier_to.to_i : (identifier_from.to_i - 1)
115
+ cmd = "svn diff -r "
116
+ cmd << "#{identifier_to}:"
117
+ cmd << "#{identifier_from}"
118
+ cmd << " #{target(path)}@#{identifier_from} #{credentials_string}"
119
+ diff = []
120
+ shellout(cmd, {mark: true}) {|io| io.each_line { |line| diff << line } }
121
+ return nil if $? && $?.exitstatus != 0
122
+ diff
123
+ rescue
124
+ nil
125
+ end
126
+
127
+ def cat(path, identifier=nil, cat=nil)
128
+ identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
129
+ path = path.gsub(Regexp.new("#{root_name}/"),'')
130
+ cmd = "svn cat #{target(path)}@#{identifier} #{credentials_string}"
131
+ shellout(cmd, {mark: true}) {|io| io.binmode; cat = io.read }
132
+ return nil if $? && $?.exitstatus != 0
133
+ cat
134
+ end
135
+
136
+
137
+ def latest_changesets(path, rev, limit=10)
138
+ revisions = self.class.scm.revisions(path, rev, nil, :limit => limit).map(&:identifier).compact
139
+ evisions.present? ? SvnRecord::Repository::Change.where(revision: revisions).order('revision DESC').all : []
140
+ end
141
+
142
+ def fetch_changesets
143
+ if (scm_info = self.class.new.info)
144
+ change = SvnRecord::Repository::Change.last
145
+ db_revision = change.present? ? change.revision.to_i : 0
146
+ scm_revision = scm_info.lastrev.identifier.to_i
147
+ branch = scm_info.lastrev.branch.present? ? scm_info.lastrev.branch : "master"
148
+ if db_revision < scm_revision
149
+ # debug("Fetching changesets for repository #{url}" )
150
+ identifier_from = db_revision + 1
151
+ while (identifier_from <= scm_revision)
152
+ identifier_to = [identifier_from + 199, scm_revision].min
153
+ revisions('', identifier_to, identifier_from, :with_paths => true).reverse_each do |revision|
154
+ ActiveRecord::Base.transaction do
155
+ user = SvnRecord::Repository::User.find_by_name(revision.author.to_s.strip)
156
+ user = SvnRecord::Repository::User.create(name: revision.author.to_s.strip) if user.blank? && revision.author.to_s.strip.present?
157
+ changeset = SvnRecord::Repository::Change.create(user_id: user.id, revision:revision.identifier, committer:revision.author, committed_at: revision.time, comment: revision.message)
158
+ revision.paths.each { |change| changeset.create_change(change,branch)} unless changeset.new_record?
159
+ end
160
+ end if revisions.present?
161
+ identifier_from = identifier_to + 1
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ def self.load_entries_changesets(entries)
168
+ return unless entries.present?
169
+ entries_with_identifier = entries.select {|entry| entry.lastrev && entry.lastrev.identifier.present?}
170
+ identifiers = entries_with_identifier.map {|entry| entry.lastrev.identifier}.compact.uniq
171
+ if identifiers.any?
172
+ change_ident = SvnRecord::Repository::Change.where(revision: identifiers).dup.group_by(&:revision)
173
+ entries_with_identifier.each {|entry| entry.changeset = change_indent[entry.lastrev.identifier] if change_indent[entry.lastrev.identifier] }
174
+ end
175
+ end
176
+
177
+ private
178
+
179
+ def strip_credential(cmd,q = "'")
180
+ cmd.to_s.gsub(/(\-\-(password|username))\s+(#{q}[^#{q}]+#{q}|[^#{q}]\S+)/, '\\1 xxxx')
181
+ end
182
+
183
+ def shell_quote(str)
184
+ "'" + str.gsub(/'/, "'\"'\"'") + "'"
185
+ end
186
+
187
+ def target(path = '')
188
+ base = path.match(/^\//) ? root_url : url
189
+ uri = "#{base}/#{path}"
190
+ uri = URI.escape(URI.escape(uri), '[]')
191
+ shell_quote(uri.gsub(/[?<>\*]/, ''))
192
+ end
193
+
194
+ def credentials_string
195
+ str = ''
196
+ str << " --username #{shell_quote(@login)}" unless @login.blank?
197
+ str << " --password #{shell_quote(@password)}" unless @login.blank? || @password.blank?
198
+ str << " --no-auth-cache --non-interactive"
199
+ str
200
+ end
201
+
202
+ def each_xml_element(node, name, &block)
203
+ node[name].is_a?(Hash) ? yield(node[name]) : node[name].each{|element| yield(element)} if node && node[name]
204
+ end
205
+
206
+ def parse_xml(xml)
207
+ ActiveSupport::XmlMini.parse(xml)
208
+ end
209
+ end
210
+
211
+ class Entries < Array
212
+ def sort_by_name
213
+ dup.sort! {|x,y| x.kind == y.kind ? x.name.to_s <=> y.name.to_s : x.kind <=> y.kind}
214
+ end
215
+
216
+ def revisions
217
+ revisions ||= Revisions.new(collect{|entry| entry.lastrev}.compact)
218
+ end
219
+ end
220
+
221
+ class Info
222
+ attr_accessor :root_url, :lastrev
223
+ def initialize(attributes={})
224
+ self.root_url = attributes[:root_url] if attributes[:root_url]
225
+ self.lastrev = attributes[:lastrev]
226
+ end
227
+ def self.get_instance(doc)
228
+ new({:root_url => doc['info']['entry']['repository']['root']['__content__'],
229
+ :lastrev => Revision.new({
230
+ :identifier => doc['info']['entry']['commit']['revision'],
231
+ :time => Time.parse(doc['info']['entry']['commit']['date']['__content__']).localtime,
232
+ :author => (doc['info']['entry']['commit']['author'] ? doc['info']['entry']['commit']['author']['__content__'] : "")
233
+ })
234
+ })
235
+ end
236
+ end
237
+
238
+ class Entry
239
+ attr_accessor :name, :path, :kind, :size, :lastrev, :changeset
240
+ def initialize(attributes={})
241
+ self.name = attributes[:name] if attributes[:name]
242
+ self.path = attributes[:path] if attributes[:path]
243
+ self.kind = attributes[:kind] if attributes[:kind]
244
+ self.size = attributes[:size].to_i if attributes[:size]
245
+ self.lastrev = attributes[:lastrev]
246
+ end
247
+ def is_file?
248
+ 'file' == self.kind
249
+ end
250
+ def is_dir?
251
+ 'dir' == self.kind
252
+ end
253
+ end
254
+
255
+ class Revisions < Array
256
+ def latest
257
+ sort {|x,y|
258
+ unless x.time.nil? or y.time.nil?
259
+ x.time <=> y.time
260
+ else
261
+ 0
262
+ end
263
+ }.last
264
+ end
265
+ end
266
+
267
+ class Revision
268
+ attr_accessor :scmid, :name, :author, :time, :message,
269
+ :paths, :revision, :branch, :identifier,:parents
270
+
271
+ def initialize(attributes={})
272
+ self.identifier = attributes[:identifier]
273
+ self.scmid = attributes[:scmid]
274
+ self.name = attributes[:name] || self.identifier
275
+ self.author = attributes[:author]
276
+ self.time = attributes[:time]
277
+ self.message = attributes[:message] || ""
278
+ self.paths = attributes[:paths]
279
+ self.revision = attributes[:revision]
280
+ self.branch = attributes[:branch]
281
+ self.parents = attributes[:parents]
282
+ end
283
+
284
+ # Returns the readable identifier.
285
+ def format_identifier
286
+ self.identifier.to_s
287
+ end
288
+
289
+ def ==(other)
290
+ if other.nil?
291
+ false
292
+ elsif scmid.present?
293
+ scmid == other.scmid
294
+ elsif identifier.present?
295
+ identifier == other.identifier
296
+ elsif revision.present?
297
+ revision == other.revision
298
+ end
299
+ end
300
+ end
301
+ end
302
+ end
303
+ end
@@ -0,0 +1,16 @@
1
+ module SvnRecord
2
+ class InstallGenerator < Rails::Generators::Base
3
+ include Rails::Generators::Migration
4
+ source_root File.expand_path('../templates', __FILE__)
5
+ argument :langs, type: :array, :default => ['en']
6
+ def self.next_migration_number(path)
7
+ @migration_number = Time.now.utc.strftime("%Y%m%d%H%M%S%6N").to_i.to_s
8
+ end
9
+ def install_translation
10
+ migration_template '../templates/create_repository_changes.rb', 'db/migrate/create_repository_changes.rb'
11
+ migration_template '../templates/create_repository_files.rb', 'db/migrate/create_repository_files.rb'
12
+ migration_template '../templates/create_repository_user.rb', 'db/migrate/create_repository_user.rb'
13
+ copy_file '../../../../config/configuration.yml', 'config/configuration.yml'
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ class CreateRepositoryChanges < ActiveRecord::Migration
2
+
3
+ def change
4
+ create_table :repository_changes do |t|
5
+ t.column :user_id, :integer # user_id :: 用户ID
6
+ t.column :revision, :integer # revision :: 版本号
7
+ t.column :committer, :string # committer :: 提交人
8
+ t.column :comment, :string # comment :: 注释
9
+ t.column :committed_at, :string # committed_at :: 提交时间
10
+ end
11
+ add_index :repository_changes, :user_id
12
+ end
13
+ end
14
+
15
+
16
+
17
+
@@ -0,0 +1,21 @@
1
+ class CreateRepositoryFiles < ActiveRecord::Migration
2
+
3
+ def change
4
+ create_table :repository_files do |t|
5
+ t.column :change_id, :integer # change_id :: 修订ID
6
+ t.column :action, :string # action :: 状态 可为=> %w[A C D I M R X]
7
+ t.column :path, :string # path :: 目录
8
+ t.column :from_path, :string # from_path :: 目录来源
9
+ t.column :from_revision, :string # from_revision :: 版本来源
10
+ t.column :revision, :string # revision :: 版本号
11
+ t.column :branch, :string # branch :: 分支
12
+ end
13
+ add_index :repository_files, :change_id # index
14
+ end
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
@@ -0,0 +1,8 @@
1
+ class CreateRepositoryUser < ActiveRecord::Migration
2
+
3
+ def change
4
+ create_table :repository_users do |t|
5
+ t.column :name, :string
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,11 @@
1
+ require 'jquery-rails'
2
+ require 'slim'
3
+ require 'svn/mime_type'
4
+ require 'svn/scm/adapters/subversion'
5
+ require 'svn/helper'
6
+
7
+ module SvnRecord
8
+ class Engine < ::Rails::Engine
9
+ isolate_namespace SvnRecord
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module SvnRecord
2
+ VERSION = "1.2.0"
3
+ end
data/lib/svn_record.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "svn_record/engine"
2
+
3
+ module SvnRecord
4
+ end
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: svn_record
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Lijia Tong
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-04-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 3.2.13
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 3.2.13
27
+ - !ruby/object:Gem::Dependency
28
+ name: jquery-rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: jquery-ui-rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: slim
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: coderay
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Used to manage them in the svn project
84
+ email:
85
+ - wtuyuupe@163.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - app/assets/images/svn_record/file_icon/bullet_add.png
91
+ - app/assets/images/svn_record/file_icon/bullet_black.png
92
+ - app/assets/images/svn_record/file_icon/bullet_blue.png
93
+ - app/assets/images/svn_record/file_icon/bullet_delete.png
94
+ - app/assets/images/svn_record/file_icon/bullet_diamond.png
95
+ - app/assets/images/svn_record/file_icon/bullet_end.png
96
+ - app/assets/images/svn_record/file_icon/bullet_go.png
97
+ - app/assets/images/svn_record/file_icon/bullet_orange.png
98
+ - app/assets/images/svn_record/file_icon/bullet_purple.png
99
+ - app/assets/images/svn_record/file_icon/bullet_toggle_minus.png
100
+ - app/assets/images/svn_record/file_icon/bullet_toggle_plus.png
101
+ - app/assets/images/svn_record/file_icon/dir.png
102
+ - app/assets/images/svn_record/file_icon/file.png
103
+ - app/assets/images/svn_record/file_icon/folder.png
104
+ - app/assets/images/svn_record/file_icon/folder_open.png
105
+ - app/assets/images/svn_record/file_icon/folder_open_add.png
106
+ - app/assets/images/svn_record/file_icon/folder_open_orange.png
107
+ - app/assets/javascripts/svn_record/application.js
108
+ - app/assets/javascripts/svn_record/change.js.coffee
109
+ - app/assets/stylesheets/svn_record/application.css
110
+ - app/assets/stylesheets/svn_record/site.css
111
+ - app/controllers/svn_record/application_controller.rb
112
+ - app/controllers/svn_record/repository/application_controller.rb
113
+ - app/controllers/svn_record/repository/changes_controller.rb
114
+ - app/models/svn_record/repository/change.rb
115
+ - app/models/svn_record/repository/file.rb
116
+ - app/models/svn_record/repository/user.rb
117
+ - app/views/layouts/svn_record/subversion.html.slim
118
+ - app/views/svn_record/repository/changes/_changesets_list.slim
119
+ - app/views/svn_record/repository/changes/diff.html.slim
120
+ - app/views/svn_record/repository/changes/entry.html.slim
121
+ - app/views/svn_record/repository/changes/index.html.slim
122
+ - app/views/svn_record/repository/changes/list.html.slim
123
+ - app/views/svn_record/repository/changes/revisions.html.slim
124
+ - config/configuration.yml
125
+ - config/routes.rb
126
+ - lib/generators/svn_record/install_generator.rb
127
+ - lib/generators/svn_record/templates/create_repository_changes.rb
128
+ - lib/generators/svn_record/templates/create_repository_files.rb
129
+ - lib/generators/svn_record/templates/create_repository_user.rb
130
+ - lib/Svn/helper.rb
131
+ - lib/Svn/mime_type.rb
132
+ - lib/Svn/scm/adapters/subversion.rb
133
+ - lib/svn_record/engine.rb
134
+ - lib/svn_record/version.rb
135
+ - lib/svn_record.rb
136
+ - Rakefile
137
+ - README.md
138
+ homepage: http://github.com/wtuyuupe/svn_record
139
+ licenses: []
140
+ metadata: {}
141
+ post_install_message:
142
+ rdoc_options: []
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - '>='
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ requirements: []
156
+ rubyforge_project:
157
+ rubygems_version: 2.0.0
158
+ signing_key:
159
+ specification_version: 4
160
+ summary: svn version control program
161
+ test_files: []