zomgit 0.0.1

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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 80f6335b31391049698c6dcacedaae4f23301607
4
+ data.tar.gz: 55cb63a2f815c6d983d52d08d400e11e135b9953
5
+ SHA512:
6
+ metadata.gz: 502a4016e94784734b0321846739ec227d556bbacd302eac6cc224d179b4716d360cca67c4f1615a55b84eb0a2996c1581e170c554492efd79fff03f90166f06
7
+ data.tar.gz: 38725c3df10035e239802e5f89cd56002a8f66de45640f06b986936d1e3a49a14f2ecdbc7617f018979b4119787683b5e0d0085d10fe6146ab227447d8567cf3
@@ -0,0 +1,21 @@
1
+ *.a
2
+ *.bundle
3
+ *.gem
4
+ *.o
5
+ *.rbc
6
+ *.so
7
+ .bundle
8
+ .config
9
+ /.bundle/
10
+ /.config
11
+ /InstalledFiles
12
+ /lib/bundler/man/
13
+ /pkg/
14
+ /tmp/
15
+ Gemfile.lock
16
+ InstalledFiles
17
+ lib/bundler/man
18
+ mkmf.log
19
+ pkg
20
+ tmp
21
+
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Robert Audi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,51 @@
1
+ ZOMGit
2
+ ======
3
+
4
+ ZOMGit is a git wrapper written in ruby. It was hugely inspired by [SCM Breeze](https://github.com/ndbroadbent/scm_breeze).
5
+
6
+ Installation
7
+ ------------
8
+
9
+ ```sh
10
+ > gem install zomgit
11
+ ```
12
+
13
+ Usage
14
+ -----
15
+
16
+ TODO: Write usage instructions here
17
+
18
+ Contributing
19
+ ------------
20
+
21
+ 1. Fork it ( https://github.com/RobertAudi/zomgit/fork )
22
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
23
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
24
+ 4. Push to the branch (`git push origin my-new-feature`)
25
+ 5. Create a new Pull Request
26
+
27
+ License
28
+ -------
29
+
30
+ Copyright (c) 2014 Robert Audi
31
+
32
+ MIT License
33
+
34
+ Permission is hereby granted, free of charge, to any person obtaining
35
+ a copy of this software and associated documentation files (the
36
+ "Software"), to deal in the Software without restriction, including
37
+ without limitation the rights to use, copy, modify, merge, publish,
38
+ distribute, sublicense, and/or sell copies of the Software, and to
39
+ permit persons to whom the Software is furnished to do so, subject to
40
+ the following conditions:
41
+
42
+ The above copyright notice and this permission notice shall be
43
+ included in all copies or substantial portions of the Software.
44
+
45
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
46
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
47
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
48
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
49
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
50
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
51
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,10 @@
1
+ require "rake/clean"
2
+ require "rubygems"
3
+ require "rubygems/package_task"
4
+
5
+ spec = eval(File.read("zomgit.gemspec"))
6
+
7
+ Gem::PackageTask.new(spec) do |pkg|
8
+ end
9
+
10
+ task default: %i(clean clobber gem)
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative File.join("..", "lib", "zomgit")
4
+
5
+ Signal.trap("SIGINT") do
6
+ puts "\nTerminating"
7
+ exit 1
8
+ end
9
+
10
+ zomgit = Zomgit::CLI
11
+
12
+ exit zomgit.run(ARGV)
@@ -0,0 +1,81 @@
1
+ # Dev shit
2
+ if ENV["ZOMGIT_DEBUG_MODE"] == "enabled"
3
+ require "awesome_print" rescue nil
4
+ end
5
+
6
+ # Require gems shit
7
+ require "gli"
8
+ require "rainbow/ext/string"
9
+
10
+
11
+ %w(helpers concerns).each { |f| Dir.glob(File.join(File.dirname(File.realpath(__FILE__)), "zomgit", f, "*.rb")).each { |ff| require ff } }
12
+
13
+ require_relative File.join(".", "zomgit", "version")
14
+ require_relative File.join(".", "zomgit", "persistor")
15
+ require_relative File.join(".", "zomgit", "exceptions")
16
+ require_relative File.join(".", "zomgit", "commands")
17
+
18
+ module Zomgit
19
+ def project_root
20
+ @project_root
21
+ end
22
+ module_function :project_root
23
+
24
+ def project_root=(value)
25
+ if value.empty?
26
+ raise Zomgit::Exceptions::NoGitRepoFoundError.new("Directory is not a git repository (#{Dir.getwd})")
27
+ end
28
+
29
+ @project_root = value
30
+ end
31
+ module_function :project_root=
32
+
33
+ class CLI
34
+ extend GLI::App
35
+
36
+ program_desc "git wrapper for the Z shell"
37
+ version Zomgit::VERSION
38
+
39
+ pre do |global_options,command,options,args|
40
+ Zomgit::project_root = File.directory?(File.join(Dir.getwd, ".git")) ? Dir.getwd : `\git rev-parse --show-toplevel 2> /dev/null`.strip
41
+ end
42
+
43
+ command :config do |c|
44
+ desc "Show the location of the zomgit file to source"
45
+
46
+ c.action do |global_options, options, args|
47
+ puts File.expand_path(File.join("..", "share", "zomgit.zsh"))
48
+ end
49
+ end
50
+
51
+ Zomgit::Commands::LIST.each do |cname|
52
+ cmd = Zomgit::Commands.const_get("#{cname.capitalize}Command")
53
+
54
+ desc cmd::DESCRIPTION
55
+ command cname do |c|
56
+ if cmd.const_defined?("FLAGS")
57
+ cmd::FLAGS.each { |names, params| c.flag(*names, params) }
58
+ end
59
+
60
+ if cmd.const_defined?("SWITCHES")
61
+ cmd::SWITCHES.each { |names, params| c.switch(*names, params) }
62
+ end
63
+
64
+ c.action do |global_options, options, args|
65
+ the_command = cmd.new(args, options)
66
+ puts the_command.send(Zomgit::Commands::EXECUTION_METHOD)
67
+ end
68
+ end
69
+ end
70
+
71
+ on_error do |exception|
72
+ case exception
73
+ when Zomgit::Exceptions::BaseError
74
+ $stderr.puts exception.message.color(:red)
75
+ false
76
+ else
77
+ true
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,18 @@
1
+ module Zomgit
2
+ module Commands
3
+ EXECUTION_METHOD = :execute!
4
+ FILES = Dir.glob(File.join(File.dirname(File.realpath(__FILE__)), "commands", "*.rb"))
5
+ LIST = FILES.map { |c| File.basename(c, ".rb") }
6
+
7
+ class BasicCommand
8
+ attr_reader :arguments, :options
9
+
10
+ def initialize(arguments = [], options = {})
11
+ @arguments = arguments
12
+ @options = options
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ Zomgit::Commands::FILES.each { |f| require f }
@@ -0,0 +1,49 @@
1
+ module Zomgit
2
+ module Commands
3
+ class FindCommand < BasicCommand
4
+ include Zomgit::Concerns::Findable
5
+
6
+ DESCRIPTION = "Find files in the git repo"
7
+ SWITCHES = {
8
+ %i(greedy g) => {
9
+ default_value: true,
10
+ negatable: true,
11
+ desc: "Let the finder be greedy (less accurate, more results)"
12
+ },
13
+ %i(G) => {
14
+ default_value: false,
15
+ negatable: false,
16
+ desc: "Alias to --no-greedy",
17
+ },
18
+ %i(r refine) => {
19
+ default_value: false,
20
+ negatable: false,
21
+ desc: "Let the finder be selective (more accurate, less results)"
22
+ }
23
+ }
24
+
25
+ FLAGS = {
26
+ %i(filter f) => {
27
+ arg_name: "filter",
28
+ desc: "Limit the search to a specific state (tracked, untracked, etc)",
29
+ must_match: %w(all untracked tracked unstaged staged modified)
30
+ }
31
+ }
32
+
33
+ def find
34
+ if self.arguments.empty?
35
+ raise Zomgit::Exceptions::MissingQueryError.new("You need to supply a search query!")
36
+ end
37
+
38
+ files = self.search(self.arguments, self.options)
39
+
40
+ if files.empty?
41
+ raise Zomgit::Exceptions::FileOrDirectoryNotFoundError.new("Nothing found matching your query")
42
+ end
43
+
44
+ files.join("\n")
45
+ end
46
+ alias_method Zomgit::Commands::EXECUTION_METHOD, :find
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,248 @@
1
+ module Zomgit
2
+ module Commands
3
+ class StatusCommand < BasicCommand
4
+ include Zomgit::Helpers::FileHelper
5
+ include Zomgit::Helpers::RainbowHelper
6
+
7
+ attr_reader :filter, :files
8
+
9
+ DESCRIPTION = "Show the status of the git repo"
10
+ FLAGS = {
11
+ %i(filter f) => {
12
+ arg_name: "filter",
13
+ desc: "Status filter",
14
+ must_match: %w(staged unmerged unstaged untracked)
15
+ }
16
+ }
17
+
18
+ MAX_CHANGES = 150
19
+
20
+ COLORS = {
21
+ reset: [:white, { bold: false }],
22
+ deleted: [:red, { bold: false }],
23
+ modified: [:green, { bold: false }],
24
+ added: [:yellow, { bold: false }],
25
+ renamed: [:blue, { bold: false }],
26
+ copied: [:yellow, { bold: false }],
27
+ retyped: [:purple, { bold: false }],
28
+ untracked: [:cyan, { bold: false }],
29
+ dark: [:black, { bold: true }],
30
+ branch: [:gray, { bold: true }],
31
+ header: [:white, { bold: false }]
32
+ }
33
+
34
+ GROUPS = {
35
+ staged: { color: :yellow, message: "Changes to be committed" },
36
+ unmerged: { color: :red, message: "Unmerged paths" },
37
+ unstaged: { color: :green, message: "Changes not staged for commit" },
38
+ untracked: { color: :cyan, message: "Untracked files" }
39
+ }
40
+
41
+ def initialize(arguments = [], options = {})
42
+ super arguments, options
43
+
44
+ @filter = options[:filter].to_sym if options[:filter]
45
+ @index = 0
46
+ end
47
+
48
+ def show
49
+ status = `command git status --porcelain`.split("\n")
50
+
51
+ if status.count > self.max_changes
52
+ raise Zomgit::Exceptions::TooManyChangesError.new("Too many changes")
53
+ end
54
+
55
+ git_branch_output = `command git branch -v 2> /dev/null`
56
+ branch = git_branch_output[/^\* (\(no branch\)|[^ ]*)/, 1]
57
+ ahead = git_branch_output[/^\* [^ ]* *[^ ]* *\[ahead ?(\d+).*\]/, 1]
58
+ behind = git_branch_output[/^\* [^ ]* *[^ ]* *\[.*behind ?(\d+)\]/, 1]
59
+
60
+ difference = ["-#{behind}", "+#{ahead}"].select{ |diff| diff.length > 1 }.join("/")
61
+ if difference.length > 0
62
+ diff = ""
63
+ diff << dark_color(" | ")
64
+ diff << added_color(difference)
65
+ difference = diff
66
+ else
67
+ difference = ""
68
+ end
69
+
70
+ output = ""
71
+ output << dark_color("#")
72
+ output << " On branch: "
73
+ output << branch_color(branch)
74
+ output << difference
75
+ output << dark_color(" | ")
76
+
77
+ if status.empty?
78
+ output << modified_color("No changes (working directory clean)")
79
+ else
80
+ output << self.stats_for(status).gsub(/(\d+)/, modified_color('\1'))
81
+ output << dark_color("\n#\n")
82
+
83
+ changes = self.changes_for status
84
+
85
+ if self.has_filter? && GROUPS.has_key?(self.filter)
86
+ if changes[self.filter].empty?
87
+ raise Zomgit::Exceptions::NoChangesError.new("No changes matching this filter")
88
+ else
89
+ self.index! changes[self.filter]
90
+ output << self.output_for(self.filter, changes[self.filter])
91
+ end
92
+ else
93
+ self.index! changes
94
+ GROUPS.keys.each { |g| output << self.output_for(g, changes[g]) unless changes[g].empty? }
95
+ end
96
+ end
97
+
98
+ output
99
+ end
100
+ alias_method Zomgit::Commands::EXECUTION_METHOD, :show
101
+
102
+ # Dynamically create color methods
103
+ # i.e.: `branch_color`
104
+ COLORS.each do |type, spec|
105
+ define_method "#{type}_color" do |message|
106
+ send(spec.first, message, spec.last)
107
+ end
108
+ end
109
+
110
+ # Same as above, but for groups
111
+ GROUPS.each do |group, spec|
112
+ define_method "#{group}_group_color" do |message, options = {}|
113
+ send(spec[:color], message, options)
114
+ end
115
+ end
116
+
117
+ def max_changes
118
+ unless @max_changes
119
+ max = ENV["ZOMGIT_STATUS_MAX_CHANGES"].to_i
120
+ @max_changes = max > 0 ? max : MAX_CHANGES
121
+ end
122
+
123
+ @max_changes
124
+ end
125
+
126
+ def has_modules?
127
+ @has_modules ||= File.exists?(File.join(Zomgit::project_root, ".gitmodules"))
128
+ end
129
+
130
+ def has_dirty_module?
131
+ !!@has_dirty_module
132
+ end
133
+
134
+ def has_dirty_module!
135
+ @has_dirty_module = true
136
+ end
137
+
138
+ def has_filter?
139
+ !self.filter.nil?
140
+ end
141
+
142
+ def long_status
143
+ @long_status ||= `command git status`
144
+ end
145
+
146
+ def stats_for(status)
147
+ stats = "#{status.count} changes ("
148
+ staged = status.grep(/\A[^ ?]/).count
149
+ unstaged = status.grep(/\A[ ?]/).count
150
+ stats << "#{staged} staged, #{unstaged} unstaged"
151
+ stats << ")"
152
+ end
153
+
154
+ def changes_for(status)
155
+ changes = {
156
+ staged: [],
157
+ unmerged: [],
158
+ unstaged: [],
159
+ untracked: []
160
+ }
161
+
162
+ modules = self.has_modules? ? File.read(File.join(Zomgit::project_root, ".gitmodules")) : ""
163
+
164
+ status.each do |raw_change|
165
+ change = { left: raw_change[0], right: raw_change[1], file: raw_change[3..-1] }
166
+
167
+ if !self.has_dirty_module? && modules.include?(change[:file])
168
+ self.has_dirty_module!
169
+ end
170
+
171
+ case raw_change[0..1]
172
+ when "DD"; changes[:unmerged] << { message: " both deleted", color: :deleted, file: change[:file] }
173
+ when "AU"; changes[:unmerged] << { message: " added by us", color: :added, file: change[:file] }
174
+ when "UD"; changes[:unmerged] << { message: "deleted by them", color: :deleted, file: change[:file] }
175
+ when "UA"; changes[:unmerged] << { message: " added by them", color: :added, file: change[:file] }
176
+ when "DU"; changes[:unmerged] << { message: " deleted by us", color: :deleted, file: change[:file] }
177
+ when "AA"; changes[:unmerged] << { message: " both added", color: :added, file: change[:file] }
178
+ when "UU"; changes[:unmerged] << { message: " both modified", color: :modified, file: change[:file] }
179
+ when /M./; changes[:staged] << { message: " modified", color: :modified, file: change[:file] }
180
+ when /A./; changes[:staged] << { message: " new file", color: :added, file: change[:file] }
181
+ when /D./; changes[:staged] << { message: " deleted", color: :deleted, file: change[:file] }
182
+ when /R./; changes[:staged] << { message: " renamed", color: :renamed, file: change[:file] }
183
+ when /C./; changes[:staged] << { message: " copied", color: :copied, file: change[:file] }
184
+ when /T./; changes[:staged] << { message: "typechange", color: :retyped, file: change[:file] }
185
+ when "??"; changes[:untracked] << { message: " untracked", color: :untracked, file: change[:file] }
186
+ end
187
+
188
+ if change[:right] == "M"
189
+ changes[:unstaged] << { message: " modified", color: :modified, file: change[:file] }
190
+ elsif change[:right] == "D" && change[:left] != "D" && change[:left] != "U"
191
+ changes[:unstaged] << { message: " deleted", color: :deleted, file: change[:file] }
192
+ elsif change[:right] == "T"
193
+ changes[:unstaged] << { message: "typechange", color: :retyped, file: change[:file] }
194
+ end
195
+ end
196
+
197
+ changes
198
+ end
199
+
200
+ def output_for(group, changes)
201
+ output = ""
202
+
203
+ output << send("#{group}_group_color", "\u27A4".encode("utf-8"), bold: true)
204
+ output << header_color(" #{GROUPS[group][:message]}\n")
205
+ output << send("#{group}_group_color", "#")
206
+ output << "\n"
207
+
208
+ changes.each do |change|
209
+ relative_file = relative_path(Dir.pwd, File.join(Zomgit::project_root, change[:file]))
210
+
211
+ submodule_change = nil
212
+ if self.has_dirty_module?
213
+ submodule_change = self.long_status[/#{change[:file]} \((.*)\)/, 1]
214
+
215
+ unless submodule_change.nil?
216
+ submodule_change = "(#{submodule_change})"
217
+ end
218
+ end
219
+
220
+ output << send("#{group}_group_color", "# ")
221
+ output << send("#{change[:color]}_color", change[:message])
222
+ output << ": "
223
+
224
+ index = self.files.index(change[:file]) + 1
225
+
226
+ padding = ""
227
+ padding << " " if changes.count >= 10 && index < 10
228
+ padding << " " if changes.count >= 100 && index < 100
229
+
230
+ output << "[#{padding}#{index}] "
231
+
232
+ output << send("#{group}_group_color", relative_file)
233
+ output << " #{submodule_change}\n"
234
+ end
235
+
236
+ output << send("#{group}_group_color", "#")
237
+ output << "\n"
238
+ output
239
+ end
240
+
241
+ def index!(changes, persist: true)
242
+ @files = changes.values.flatten.map { |c| c[:file] }
243
+
244
+ Zomgit::Persistor.instance.cache_index(@files) if persist
245
+ end
246
+ end
247
+ end
248
+ end