zomgit 0.0.1

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: 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