chronicler 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e5c6897edb863ebbc3182d2b256a6c26d7d6763a
4
+ data.tar.gz: a31860fa336b1002d751ecb8df6ae2834a529c6e
5
+ SHA512:
6
+ metadata.gz: 4261895f88d52900c8e451eee93b04d40dfdf6feb30dfc889b95f75c41e087be53051e48abd47de3765e12753158d0070963866844f5700bfe2b1204e60778ba
7
+ data.tar.gz: 90758496816f9b8516406ad909cb4dd138b00d50ec3a9191e9b322966a1fe7b9dff7c36862731f0570992626850278897ca8a2b75c86bb936d93e7532201ff10
@@ -0,0 +1,7 @@
1
+ .DS_Store
2
+ .bundle
3
+ .yardoc
4
+ .rvmrc
5
+ Gemfile.lock
6
+ doc
7
+ pkg
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2016 Paul Engel
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,48 @@
1
+ # Chronicler
2
+
3
+ Version control your (development) databases using Git
4
+
5
+ ## Installation
6
+
7
+ Run the following command to install `Chronicler`:
8
+
9
+ $ gem install "chronicler"
10
+
11
+ ## Usage
12
+
13
+ Chronicler is a command-line-tool so for starters, you can print help instructions:
14
+
15
+ $ crn --help
16
+ Commands:
17
+ crn branch # List branches
18
+ crn checkout [IDENTIFIER] # Switch and load the specified branch or commit or tag (IDENTIFIER is optional)
19
+ crn commit # Commit current state of databases
20
+ crn destroy # Remove Chronicler entirely
21
+ crn help [COMMAND] # Describe available commands or one specific command
22
+ crn init [NAME] # Create a new repository (NAME defaults to current Git repository or current user)
23
+ crn list # List repositories
24
+ crn log [INTERFACE] # Show commit logs (INTERFACE is optional)
25
+ crn new [BRANCH] # Create a new branch (BRANCH defaults to current Git branch)
26
+ crn open # Open current repository
27
+ crn reset # Reset database(s) to last commit
28
+ crn select # Select which databases to store
29
+ crn status # Show current status
30
+ crn tree # Print branch graph tree
31
+ crn use [NAME] # Use existing repository (NAME is optional)
32
+
33
+ ## TODO
34
+
35
+ * Watch and unwatch switching branches
36
+ * Added memoization within CLI and Repository
37
+
38
+ ## License
39
+
40
+ Copyright (c) 2016 Paul Engel, released under the MIT license
41
+
42
+ http://github.com/archan937 – http://twitter.com/archan937 – pm_engel@icloud.com
43
+
44
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
45
+
46
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
47
+
48
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/bin/crn ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+ require "chronicler/cli"
5
+
6
+ begin
7
+ Chronicler::CLI.start
8
+ rescue Interrupt
9
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.authors = ["Paul Engel"]
5
+ gem.email = ["pm_engel@icloud.com"]
6
+ gem.summary = %q{Version control your (development) databases using Git}
7
+ gem.description = %q{Version control your (development) databases using Git}
8
+ gem.homepage = "https://github.com/archan937/chronicler"
9
+
10
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
11
+ gem.files = `git ls-files`.split("\n")
12
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
13
+ gem.name = "chronicler"
14
+ gem.require_paths = ["lib"]
15
+ gem.version = "0.1.0"
16
+
17
+ gem.add_dependency "thor"
18
+ gem.add_dependency "toml-rb"
19
+ gem.add_dependency "inquirer"
20
+
21
+ gem.add_development_dependency "pry"
22
+ end
@@ -0,0 +1,48 @@
1
+ require "toml"
2
+
3
+ require "chronicler/core_ext"
4
+ require "chronicler/config"
5
+ require "chronicler/git"
6
+ require "chronicler/repository"
7
+
8
+ class Chronicler
9
+
10
+ CONFIG = ".chronicler"
11
+ DEFAULT_PATH = "~/Chronicles"
12
+
13
+ def self.setup(path)
14
+ path = DEFAULT_PATH if path.to_s.strip.empty?
15
+ path = File.expand_path(path)
16
+
17
+ FileUtils.mkdir_p(path)
18
+ config[:store] = path
19
+ end
20
+
21
+ def self.store
22
+ config[:store]
23
+ end
24
+
25
+ def self.repositories
26
+ if store
27
+ Dir[File.join(store, "*")]
28
+ .select{|file| File.directory?(file)}
29
+ .collect{|dir| File.basename(dir)}
30
+ else
31
+ []
32
+ end
33
+ end
34
+
35
+ def self.config_path
36
+ File.join(Dir.home, CONFIG)
37
+ end
38
+
39
+ def self.config
40
+ Config[config_path]
41
+ end
42
+
43
+ def self.destroy!
44
+ FileUtils.rm_rf(store) if File.exists?(store)
45
+ File.delete(config_path) if File.exists?(config_path)
46
+ end
47
+
48
+ end
@@ -0,0 +1,235 @@
1
+ require "thor"
2
+ require "inquirer"
3
+ require "chronicler"
4
+
5
+ class Chronicler
6
+ class CLI < Thor
7
+
8
+ desc "init [NAME]", "Create a new repository (NAME defaults to current Git repository or current user)"
9
+ def init(name = defacto_name)
10
+ ensure_store
11
+ if path = repository(name).init
12
+ puts "Created empty Chronicler repository at #{path}"
13
+ end
14
+ config_use name
15
+ end
16
+
17
+ desc "use [NAME]", "Use existing repository (NAME is optional)"
18
+ def use(name = nil)
19
+ name ||= begin
20
+ current = current_name
21
+ repositories = Chronicler.repositories - [current]
22
+
23
+ no_repository_available! if repositories.empty?
24
+
25
+ postfix = " (on #{current})" if repositories.include?(current)
26
+ repositories[Ask.list("Which repository do you want to use?#{postfix}", repositories)]
27
+ end
28
+
29
+ config_use name
30
+ end
31
+
32
+ desc "open", "Open current repository"
33
+ def open
34
+ repository.run "open ."
35
+ end
36
+
37
+ desc "list", "List repositories"
38
+ def list
39
+ if Chronicler.repositories.any?
40
+ name = [current_name, whoami].detect do |x|
41
+ Chronicler.repositories.include?(x)
42
+ end
43
+
44
+ puts "Located at #{Chronicler.store.gsub(Dir.home, "~")}"
45
+
46
+ Chronicler.repositories.each do |repo|
47
+ current = (repo == name)
48
+ prefix = current ? "* " : " "
49
+ repo = repo.green if current
50
+ puts "#{prefix}#{repo}"
51
+ end
52
+ else
53
+ puts "No Chronicler repositories initialized yet."
54
+ end
55
+ end
56
+
57
+ desc "branch", "List branches"
58
+ def branch
59
+ puts "On repository #{repository.name}"
60
+ repository.git("branch", :system)
61
+ end
62
+
63
+ desc "status", "Show current status"
64
+ def status
65
+ puts "On branch #{repository.name}:#{repository.branch}"
66
+ if (changes = repository.changes).empty?
67
+ puts "Nothing to commit."
68
+ else
69
+ puts "Changes for commit:"
70
+ puts
71
+ (changes[:added] || []).each do |(table, checksum)|
72
+ puts " added: #{table}"
73
+ end
74
+ (changes[:modified] || []).each do |(table, checksum)|
75
+ puts " modified: #{table} (#{checksum})"
76
+ end
77
+ (changes[:deleted] || []).each do |(table, checksum)|
78
+ puts " deleted: #{table}"
79
+ end
80
+ puts
81
+ end
82
+ end
83
+
84
+ desc "log [INTERFACE]", "Show commit logs (INTERFACE is optional)"
85
+ def log(interface = nil)
86
+ repository # check available repository
87
+ interface ||= begin
88
+ ensure_interface
89
+ Chronicler.config[:interface]
90
+ end
91
+ command = (interface == "git") ? "git log" : interface
92
+ repository.run(command, :system)
93
+ end
94
+
95
+ desc "tree", "Print branch graph tree"
96
+ def tree
97
+ puts "On repository #{repository.name}"
98
+ repository.git("log --graph --oneline --decorate --date=relative --all", :system)
99
+ end
100
+
101
+ desc "new [BRANCH]", "Create a new branch (BRANCH defaults to current Git branch)"
102
+ def new(branch = nil)
103
+ if repository && branch.nil? && Git.current.nil?
104
+ puts "Please specify the branch name as Chronicler cannot determine one."
105
+ else
106
+ repository.new(branch || Git.branch)
107
+ end
108
+ end
109
+
110
+ desc "select", "Select which databases to store"
111
+ def select
112
+ repository_databases = repository.databases
113
+ databases = []
114
+
115
+ options = `mysql -u root -e "SHOW DATABASES" -sN`.split(/\s+/).reject do |database|
116
+ %w(information_schema mysql performance_schema test).include? database
117
+ end
118
+ selected = options.collect{|database| repository_databases.include?(database)}
119
+
120
+ Ask.checkbox("Which databases would you like to store?", options, default: selected).each_with_index do |checked, index|
121
+ databases << options[index] if checked
122
+ end
123
+
124
+ repository.select databases
125
+ end
126
+
127
+ desc "commit", "Commit current state of databases"
128
+ method_options [:message, "-m"] => :string, [:tag, "-t"] => :string
129
+ def commit
130
+ if repository.new?
131
+ select
132
+ elsif !repository.dirty?
133
+ puts "Nothing to commit."
134
+ return
135
+ end
136
+ message = options[:message] || "Updated databases"
137
+ repository.commit message, options[:tag]
138
+ end
139
+
140
+ desc "reset", "Reset database(s) to last commit"
141
+ def reset
142
+ if repository && Ask.confirm("Are you sure you want to reset to the last commit? (Changes will be lost)")
143
+ repository.reset
144
+ end
145
+ end
146
+
147
+ desc "checkout [IDENTIFIER]", "Switch and load the specified branch or commit or tag (IDENTIFIER is optional)"
148
+ def checkout(identifier = nil)
149
+ identifier ||= begin
150
+ branches = repository.branches - [repository.branch]
151
+ branches[Ask.list("Which branch do you want to checkout? (on #{repository.branch})", branches)]
152
+ end
153
+
154
+ if repository.dirty? && Ask.confirm("Current state is dirty. Do you want to commit first?")
155
+ commit
156
+ end
157
+
158
+ repository.checkout identifier
159
+ puts "Successfully loaded database#{"s" unless repository.databases.size == 1} of '#{identifier}'"
160
+ end
161
+
162
+ desc "destroy", "Remove Chronicler entirely"
163
+ def destroy
164
+ if File.exists?(Chronicler.config_path)
165
+ if Ask.confirm("Are you sure you want to continue? (Everything will be LOST)")
166
+ Chronicler.destroy!
167
+ end
168
+ else
169
+ puts "Nothing to destroy."
170
+ end
171
+ end
172
+
173
+ private
174
+
175
+ def ensure_store
176
+ if Chronicler.store.nil?
177
+ path = Ask.input("Where should Chronicler repositories be located at?", default: Chronicler::DEFAULT_PATH)
178
+ Chronicler.setup(path)
179
+ end
180
+ end
181
+
182
+ def ensure_interface
183
+ if Chronicler.config[:interface].nil?
184
+ default = `which gitx`.strip.empty? ? "git" : "gitx"
185
+ interface = Ask.input("Which Git interface would you like to use?", default: default)
186
+ Chronicler.config[:interface] = interface
187
+ end
188
+ end
189
+
190
+ def config_use(selected)
191
+ defacto = defacto_name
192
+ if defacto == selected
193
+ use = (Chronicler.config[:use] || {}).reject{|k, v| k == defacto}
194
+ Chronicler.config[:use] = (use unless use.empty?)
195
+ else
196
+ Chronicler.config[:"use.#{defacto}"] = selected
197
+ end
198
+ end
199
+
200
+ def whoami
201
+ `whoami`.strip
202
+ end
203
+
204
+ def defacto_name
205
+ Git.current || whoami
206
+ end
207
+
208
+ def current_name
209
+ defacto = defacto_name
210
+ (Chronicler.config[:use] || {})[defacto] || defacto
211
+ end
212
+
213
+ def repository(name = nil)
214
+ name ||= [current_name, whoami].detect do |x|
215
+ Chronicler.repositories.include?(x)
216
+ end
217
+
218
+ if name
219
+ Repository.new name
220
+ else
221
+ no_repository_available!
222
+ end
223
+ end
224
+
225
+ def no_repository_available!
226
+ puts "fatal: No Chronicler repository available."
227
+ exit!
228
+ end
229
+
230
+ def method_missing(method, *args)
231
+ raise Error, "Unrecognized command \"#{method}\". Please consult `ds help`."
232
+ end
233
+
234
+ end
235
+ end
@@ -0,0 +1,44 @@
1
+ class Chronicler
2
+ class Config
3
+ attr_reader :file
4
+
5
+ def self.[](file)
6
+ new(file)
7
+ end
8
+
9
+ def initialize(file)
10
+ @file = file
11
+ end
12
+
13
+ def [](key)
14
+ split(key).inject(load_file){|h, k| (h || {})[k]}
15
+ end
16
+
17
+ def []=(key, value)
18
+ config = load_file
19
+
20
+ keys = split(key)
21
+ hash = keys[0..-2].inject(config){|h, k| h[k] ||= {}}
22
+ hash[keys.last] = value
23
+
24
+ dump_file config.reject{|k, v| v.nil?}
25
+ end
26
+
27
+ private
28
+
29
+ def load_file
30
+ (TOML.load_file(file) if File.exists?(file)) || {}
31
+ end
32
+
33
+ def dump_file(config)
34
+ File.open(file, "w") do |f|
35
+ f.write TOML.dump(config)
36
+ end
37
+ end
38
+
39
+ def split(key)
40
+ key.to_s.split(".")
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1 @@
1
+ require "chronicler/core_ext/string"
@@ -0,0 +1,18 @@
1
+ class String
2
+
3
+ COLORS = {:red => 31, :green => 32, :yellow => 33, :blue => 36, :white => 37}
4
+
5
+ COLORS.keys.each do |color|
6
+ define_method color do
7
+ colorize color
8
+ end
9
+ end
10
+
11
+ private
12
+
13
+ def colorize(color)
14
+ color = COLORS[color]
15
+ "\e[1m\e[#{color}m#{self}\e[0m"
16
+ end
17
+
18
+ end
@@ -0,0 +1,32 @@
1
+ class Chronicler
2
+ module Git
3
+ extend self
4
+
5
+ def current(path = nil)
6
+ if git("rev-parse --is-inside-work-tree 2> /dev/null", path) == "true"
7
+ File.basename git("rev-parse --show-toplevel", path)
8
+ end
9
+ end
10
+
11
+ def head(path = nil)
12
+ git "rev-parse HEAD", path
13
+ end
14
+
15
+ def branch(path = nil)
16
+ git "rev-parse --abbrev-ref HEAD", path
17
+ end
18
+
19
+ def branches(path = nil)
20
+ git("for-each-ref refs/heads/ --format='%(refname:short)'", path).split("\n")
21
+ end
22
+
23
+ # private
24
+
25
+ def git(command, path)
26
+ `cd #{path || "."} && git #{command}`.strip
27
+ end
28
+
29
+ private_class_method :git
30
+
31
+ end
32
+ end
@@ -0,0 +1,144 @@
1
+ class Chronicler
2
+ class Repository
3
+ attr_reader :name
4
+
5
+ def initialize(name)
6
+ @name = name
7
+ end
8
+
9
+ def path
10
+ File.join(Chronicler.store, name)
11
+ end
12
+
13
+ def branch
14
+ Git.branch(path)
15
+ end
16
+
17
+ def branches
18
+ Git.branches(path)
19
+ end
20
+
21
+ def init
22
+ return if File.exists?(path)
23
+
24
+ FileUtils.mkdir(path)
25
+ File.open(File.join(path, ".gitignore"), "w+") do |file|
26
+ file.puts ".DS_Store"
27
+ end
28
+
29
+ git "init"
30
+ git "add .gitignore"
31
+ git "commit -m 'Initial commit'"
32
+
33
+ path
34
+ end
35
+
36
+ def new(branch)
37
+ config[:"repository.origin"] = Git.head(path)
38
+ git "checkout -b #{branch}"
39
+ end
40
+
41
+ def select(*databases)
42
+ config[:"repository.databases"] = [databases].flatten.sort
43
+ end
44
+
45
+ def commit(message, tag = nil)
46
+ dump
47
+ config[:"repository.origin"] = Git.head(path) if config[:"repository.origin"].nil?
48
+ config[:"repository.checksums"] = checksums
49
+ git "add ."
50
+ git "commit -m #{message.inspect}"
51
+ git "tag #{tag}" if tag
52
+ end
53
+
54
+ def reset
55
+ load
56
+ end
57
+
58
+ def checkout(branch)
59
+ git "checkout #{branch}"
60
+ load
61
+ end
62
+
63
+ def git(command, output = :silence)
64
+ run "git #{command}", output
65
+ end
66
+
67
+ def run(command, output = :silence)
68
+ command = "cd #{path} && #{command}"
69
+ command << " > /dev/null" if output == :silence
70
+ if output == :system
71
+ system command
72
+ else
73
+ `#{command}`.strip
74
+ end
75
+ end
76
+
77
+ def new?
78
+ [nil, Git.head(path)].include?(config[:"repository.origin"])
79
+ end
80
+
81
+ def dirty?
82
+ changes.any?
83
+ end
84
+
85
+ def databases
86
+ config[:"repository.databases"] || []
87
+ end
88
+
89
+ def changes
90
+ selected_databases = databases
91
+ stored = (config[:"repository.checksums"] || {}).select{|table, checksum| selected_databases.include?(table.gsub(/-.*/, ""))}
92
+ current = checksums
93
+
94
+ added = current.reject{|table, checksum| stored.keys.include?(table)}
95
+ modified = current.select{|table, checksum| stored.keys.include?(table) && (stored[table] != checksum)}
96
+ deleted = stored.reject{|table, checksum| current.keys.include?(table)}
97
+
98
+ {}.tap do |changes|
99
+ changes[:added] = added if added.any?
100
+ changes[:modified] = modified if modified.any?
101
+ changes[:deleted] = deleted if deleted.any?
102
+ end
103
+ end
104
+
105
+ def checksums
106
+ checksums = databases.collect do |database|
107
+ `mysql -u root #{database} -e "SHOW TABLES" -sN`.split("\n").collect do |table|
108
+ `mysql -u root #{database} -e "CHECKSUM TABLE #{table}"`.split("\n").last
109
+ end
110
+ end.flatten.collect do |checksum|
111
+ table, size = checksum.split("\t")
112
+ [table.gsub(".", "-"), size.to_i]
113
+ end
114
+ Hash[checksums]
115
+ end
116
+
117
+ private
118
+
119
+ def config
120
+ Config[File.join(path, Chronicler::CONFIG)]
121
+ end
122
+
123
+ def dump
124
+ Dir[File.join(path, "*.sql.gz")].each do |file|
125
+ unless databases.include?(File.basename(file, ".sql.gz"))
126
+ File.delete(file)
127
+ end
128
+ end
129
+
130
+ changed = changes.values.flatten
131
+ changed_databases = changed.collect{|h| h.keys.collect{|t| t.gsub(/-.*/, "")}}.flatten.uniq
132
+ changed_databases.each do |database|
133
+ run "mysqldump -u root #{database} | gzip -c | cat > #{database}.sql.gz", true
134
+ end
135
+ end
136
+
137
+ def load
138
+ databases.each do |database|
139
+ run "gunzip < #{database}.sql.gz | mysql -u root #{database}", true
140
+ end
141
+ end
142
+
143
+ end
144
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chronicler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Paul Engel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-03-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: toml-rb
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: inquirer
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: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Version control your (development) databases using Git
70
+ email:
71
+ - pm_engel@icloud.com
72
+ executables:
73
+ - crn
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - MIT-LICENSE
80
+ - README.md
81
+ - Rakefile
82
+ - VERSION
83
+ - bin/crn
84
+ - chronicler.gemspec
85
+ - lib/chronicler.rb
86
+ - lib/chronicler/cli.rb
87
+ - lib/chronicler/config.rb
88
+ - lib/chronicler/core_ext.rb
89
+ - lib/chronicler/core_ext/string.rb
90
+ - lib/chronicler/git.rb
91
+ - lib/chronicler/repository.rb
92
+ homepage: https://github.com/archan937/chronicler
93
+ licenses: []
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.4.5.1
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Version control your (development) databases using Git
115
+ test_files: []