schacon-github 0.3.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.
@@ -0,0 +1,370 @@
1
+ helper :user_and_repo_from do |url|
2
+ case url
3
+ when %r|^git://github\.com/([^/]+/[^/]+)$|: $1.split('/')
4
+ when %r|^(?:ssh://)?(?:git@)?github\.com:([^/]+/[^/]+)$|: $1.split('/')
5
+ end
6
+ end
7
+
8
+ helper :user_and_repo_for do |remote|
9
+ user_and_repo_from(url_for(remote))
10
+ end
11
+
12
+ helper :user_for do |remote|
13
+ user_and_repo_for(remote).try.first
14
+ end
15
+
16
+ helper :repo_for do |remote|
17
+ user_and_repo_for(remote).try.last
18
+ end
19
+
20
+ helper :project do
21
+ repo = repo_for(:origin)
22
+ if repo.nil?
23
+ if url_for(:origin) == ""
24
+ STDERR.puts "Error: missing remote 'origin'"
25
+ else
26
+ STDERR.puts "Error: remote 'origin' is not a github URL"
27
+ end
28
+ exit 1
29
+ end
30
+ repo.chomp('.git')
31
+ end
32
+
33
+ helper :url_for do |remote|
34
+ `git config --get remote.#{remote}.url`.chomp
35
+ end
36
+
37
+ helper :local_heads do
38
+ `git show-ref --heads --hash`.split("\n")
39
+ end
40
+
41
+ helper :has_commit? do |sha|
42
+ `git show #{sha} >/dev/null 2>/dev/null`
43
+ $?.exitstatus == 0
44
+ end
45
+
46
+ helper :resolve_commits do |treeish|
47
+ if treeish
48
+ if treeish.match(/\.\./)
49
+ commits = `git rev-list #{treeish}`.split("\n")
50
+ else
51
+ commits = `git rev-parse #{treeish}`.split("\n")
52
+ end
53
+ else
54
+ # standard in
55
+ puts 'reading from stdin...'
56
+ commits = $stdin.read.split("\n")
57
+ end
58
+ commits.select { |a| a.size == 40 } # only the shas, not the ^SHAs
59
+ end
60
+
61
+ helper :ignore_file_path do
62
+ dir = `git rev-parse --git-dir`.chomp
63
+ File.join(dir, 'ignore-shas')
64
+ end
65
+
66
+ helper :ignore_sha_array do
67
+ File.open( ignore_file_path ) { |yf| YAML::load( yf ) } rescue {}
68
+ end
69
+
70
+ helper :remove_ignored do |array, ignore_array|
71
+ array.reject { |id| ignore_array[id] }
72
+ end
73
+
74
+ helper :ignore_shas do |shas|
75
+ ignores = ignore_sha_array
76
+ shas.each do |sha|
77
+ puts 'ignoring ' + sha
78
+ ignores[sha] = true
79
+ end
80
+ File.open( ignore_file_path, 'w' ) do |out|
81
+ YAML.dump( ignores, out )
82
+ end
83
+ end
84
+
85
+ helper :get_commits do |rev_array|
86
+ list = rev_array.select { |a| has_commit?(a) }.join(' ')
87
+ `git log --pretty=format:"%H::%ae::%s::%ar::%ad" --no-merges #{list}`.split("\n").map { |a| a.split('::') }
88
+ end
89
+
90
+ helper :get_cherry do |branch|
91
+ `git cherry HEAD #{branch} | git name-rev --stdin`.split("\n").map { |a| a.split(' ') }
92
+ end
93
+
94
+ helper :get_common do |branch|
95
+ `git rev-list ..#{branch} --boundary | tail -1 | git name-rev --stdin`.split(' ')[1] rescue 'unknown'
96
+ end
97
+
98
+ helper :print_commits do |our_commits, options|
99
+ ignores = ignore_sha_array
100
+
101
+ case options[:sort]
102
+ when 'branch'
103
+ our_commits.sort! { |a, b| a[0][2] <=> b[0][2] }
104
+ when 'author'
105
+ our_commits.sort! { |a, b| a[1][1] <=> b[1][1] }
106
+ else
107
+ our_commits.sort! { |a, b| Date.parse(a[1][4]) <=> Date.parse(b[1][4]) } rescue 'cant parse dates'
108
+ end
109
+
110
+ shown_commits = {}
111
+ before = Date.parse(options[:before]) if options[:before] rescue puts 'cant parse before date'
112
+ after = Date.parse(options[:after]) if options[:after] rescue puts 'cant parse after date'
113
+ our_commits.each do |cherry, commit|
114
+ status, sha, ref_name = cherry
115
+ next if shown_commits[sha] || ignores[sha]
116
+ next if options[:project] && !ref_name.match(Regexp.new(options[:project]))
117
+ ref_name = ref_name.gsub('remotes/', '')
118
+ if status == '+' && commit
119
+ next if options[:author] && !commit[1].match(Regexp.new(options[:author]))
120
+ next if options[:before] && before && (before < Date.parse(commit[4])) rescue false
121
+ next if options[:after] && after && (after > Date.parse(commit[4])) rescue false
122
+ applies = applies_cleanly(sha)
123
+ next if options[:applies] && !applies
124
+ next if options[:noapply] && applies
125
+ if options[:shas]
126
+ puts sha
127
+ else
128
+ common = options[:common] ? get_common(sha) : ''
129
+ puts [sha[0,6], ref_name.ljust(25), commit[1][0,20].ljust(21),
130
+ commit[2][0, 36].ljust(38), commit[3][0,15], common].join(" ")
131
+ end
132
+ end
133
+ shown_commits[sha] = true
134
+ end
135
+ end
136
+
137
+ helper :applies_cleanly do |sha|
138
+ `git diff ...#{sha} | git apply --check >/dev/null 2>/dev/null`
139
+ $?.exitstatus == 0
140
+ end
141
+
142
+ helper :remotes do
143
+ regexp = '^remote\.(.+)\.url$'
144
+ `git config --get-regexp '#{regexp}'`.split("\n").inject({}) do |memo, line|
145
+ name_string, url = line.split(/ /, 2)
146
+ m, name = *name_string.match(/#{regexp}/)
147
+ memo[name.to_sym] = url
148
+ memo
149
+ end
150
+ end
151
+
152
+ helper :remote_branches_for do |user|
153
+ `git ls-remote -h #{user} 2> /dev/null`.split(/\n/).inject({}) do |memo, line|
154
+ hash, head = line.split(/\t/, 2)
155
+ head = head[%r{refs/heads/(.+)$},1] unless head.nil?
156
+ memo[head] = hash unless head.nil?
157
+ memo
158
+ end if !(user.nil? || user.strip.empty?)
159
+ end
160
+
161
+ helper :remote_branch? do |user, branch|
162
+ remote_branches_for(user).key?(branch)
163
+ end
164
+
165
+ helper :branch_dirty? do
166
+ # see if there are any cached or tracked files that have been modified
167
+ # originally, we were going to use git-ls-files but that could only
168
+ # report modified track files...not files that have been staged
169
+ # for committal
170
+ !(system("git diff --quiet 2>/dev/null") or !system("git diff --cached --quiet 2>/dev/null"))
171
+ end
172
+
173
+ helper :tracking do
174
+ remotes.inject({}) do |memo, (name, url)|
175
+ if ur = user_and_repo_from(url)
176
+ memo[name] = ur.first
177
+ else
178
+ memo[name] = url
179
+ end
180
+ memo
181
+ end
182
+ end
183
+
184
+ helper :tracking? do |user|
185
+ tracking.values.include?(user)
186
+ end
187
+
188
+ helper :owner do
189
+ user_for(:origin)
190
+ end
191
+
192
+ helper :current_branch do
193
+ `git rev-parse --symbolic-full-name HEAD`.chomp.sub(/^refs\/heads\//, '')
194
+ end
195
+
196
+ helper :user_and_branch do
197
+ raw_branch = current_branch
198
+ user, branch = raw_branch.split(/\//, 2)
199
+ if branch
200
+ [user, branch]
201
+ else
202
+ [owner, user]
203
+ end
204
+ end
205
+
206
+ helper :branch_user do
207
+ user_and_branch.first
208
+ end
209
+
210
+ helper :branch_name do
211
+ user_and_branch.last
212
+ end
213
+
214
+ helper :public_url_for_user_and_repo do |user, repo|
215
+ "git://github.com/#{user}/#{repo}.git"
216
+ end
217
+
218
+ helper :private_url_for_user_and_repo do |user, repo|
219
+ "git@github.com:#{user}/#{repo}.git"
220
+ end
221
+
222
+ helper :public_url_for do |user|
223
+ public_url_for_user_and_repo user, project
224
+ end
225
+
226
+ helper :private_url_for do |user|
227
+ private_url_for_user_and_repo user, project
228
+ end
229
+
230
+ helper :homepage_for do |user, branch|
231
+ "https://github.com/#{user}/#{project}/tree/#{branch}"
232
+ end
233
+
234
+ helper :network_page_for do |user|
235
+ "https://github.com/#{user}/#{project}/network"
236
+ end
237
+
238
+ helper :network_meta_for do |user|
239
+ "http://github.com/#{user}/#{project}/network_meta"
240
+ end
241
+
242
+
243
+ helper :has_launchy? do |blk|
244
+ begin
245
+ gem 'launchy'
246
+ require 'launchy'
247
+ blk.call
248
+ rescue Gem::LoadError
249
+ STDERR.puts "Sorry, you need to install launchy: `gem install launchy`"
250
+ end
251
+ end
252
+
253
+ helper :open do |url|
254
+ has_launchy? proc {
255
+ Launchy::Browser.new.visit url
256
+ }
257
+ end
258
+
259
+ helper :print_network_help do
260
+ puts "
261
+ You have to provide a command :
262
+
263
+ web [user] - opens your web browser to the network graph page for this
264
+ project, or for the graph page for [user] if provided
265
+
266
+ list - shows the projects in your network that have commits
267
+ that you have not pulled in yet, and branch names
268
+
269
+ fetch - adds all projects in your network as remotes and fetches
270
+ any objects from them that you don't have yet
271
+
272
+ commits - will show you a list of all commits in your network that
273
+ you have not ignored or have not merged or cherry-picked.
274
+ This will automatically fetch objects you don't have yet.
275
+
276
+ --project (user/branch) - only show projects that match string
277
+ --author (email) - only show projects that match string
278
+ --after (date) - only show commits after date
279
+ --before (date) - only show commits before date
280
+ --shas - only print shas (can pipe through 'github ignore')
281
+ --applies - filter to patches that still apply cleanly
282
+ --sort - how to sort the commits (date, branch, author)
283
+ "
284
+ end
285
+
286
+ helper :print_network_cherry_help do
287
+ $stderr.puts "
288
+ =========================================================================================
289
+ These are all the commits that other people have pushed that you have not
290
+ applied or ignored yet (see 'github ignore'). Some things you might want to do:
291
+
292
+ * You can run 'github fetch user/branch' (sans '~N') to pull into a local branch for testing
293
+ * You can run 'git cherry-pick [SHA]' to apply a single patch
294
+ * You can run 'git merge user/branch' to merge a commit and all the '~N' variants.
295
+ * You can ignore all of a projects commits with 'github ignore ..user/branch'
296
+ =========================================================================================
297
+
298
+ "
299
+ end
300
+
301
+ helper :argv do
302
+ GitHub.original_args
303
+ end
304
+
305
+
306
+ helper :get_network_data do |user, options|
307
+ if options[:cache] && has_cache?
308
+ return get_cache
309
+ end
310
+ if cache_network_data(options)
311
+ return cache_data(user)
312
+ else
313
+ return get_cache
314
+ end
315
+ end
316
+
317
+ helper :cache_commits do |commits|
318
+ File.open( commits_cache_path, 'w' ) do |out|
319
+ out.write(commits.to_yaml)
320
+ end
321
+ end
322
+
323
+ helper :commits_cache do
324
+ YAML.load(File.open(commits_cache_path))
325
+ end
326
+
327
+ helper :cache_commits_data do |options|
328
+ cache_expired? || options[:nocache] || !has_commits_cache?
329
+ end
330
+
331
+ helper :cache_network_data do |options|
332
+ cache_expired? || options[:nocache] || !has_cache?
333
+ end
334
+
335
+ helper :network_cache_path do
336
+ dir = `git rev-parse --git-dir`.chomp
337
+ File.join(dir, 'network-cache')
338
+ end
339
+
340
+ helper :commits_cache_path do
341
+ dir = `git rev-parse --git-dir`.chomp
342
+ File.join(dir, 'commits-cache')
343
+ end
344
+
345
+ helper :cache_data do |user|
346
+ raw_data = Kernel.open(network_meta_for(user)).read
347
+ File.open( network_cache_path, 'w' ) do |out|
348
+ out.write(raw_data)
349
+ end
350
+ data = JSON.parse(raw_data)
351
+ end
352
+
353
+ helper :cache_expired? do
354
+ return true if !has_cache?
355
+ age = Time.now - File.stat(network_cache_path).mtime
356
+ return true if age > (60 * 60) # 1 hour
357
+ false
358
+ end
359
+
360
+ helper :has_cache? do
361
+ File.file?(network_cache_path)
362
+ end
363
+
364
+ helper :has_commits_cache? do
365
+ File.file?(commits_cache_path)
366
+ end
367
+
368
+ helper :get_cache do
369
+ JSON.parse(File.read(network_cache_path))
370
+ end
@@ -0,0 +1,26 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{github}
3
+ s.version = "0.3.0"
4
+
5
+ s.specification_version = 2 if s.respond_to? :specification_version=
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Chris Wanstrath, Kevin Ballard, Scott Chacon"]
9
+ s.date = %q{2008-05-18}
10
+ s.default_executable = %q{gh}
11
+ s.description = %q{The official `github` command line helper for simplifying your GitHub experience.}
12
+ s.email = %q{chris@ozmm.org}
13
+ s.executables = ["github", "gh"]
14
+ s.extra_rdoc_files = ["bin/github", "bin/gh", "lib/github/extensions.rb", "lib/github/command.rb", "lib/github/helper.rb", "lib/github.rb", "LICENSE", "README"]
15
+ s.files = ["bin/github", "commands/commands.rb", "commands/helpers.rb", "lib/github/extensions.rb", "lib/github/command.rb", "lib/github/helper.rb", "lib/github.rb", "LICENSE", "Manifest", "README", "spec/command_spec.rb", "spec/extensions_spec.rb", "spec/github_spec.rb", "spec/helper_spec.rb", "spec/spec_helper.rb", "spec/ui_spec.rb", "spec/windoze_spec.rb", "github-gem.gemspec"]
16
+ s.has_rdoc = true
17
+ s.homepage = %q{http://github.com/}
18
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Github", "--main", "README"]
19
+ s.require_paths = ["lib"]
20
+ s.rubyforge_project = %q{github}
21
+ s.rubygems_version = %q{1.1.1}
22
+ s.summary = %q{The official `github` command line helper for simplifying your GitHub experience.}
23
+
24
+ # s.add_dependency(%q<launchy>, [">= 0"])
25
+ s.add_dependency('json', [">= 0"])
26
+ end
@@ -0,0 +1,146 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ require 'github/extensions'
3
+ require 'github/command'
4
+ require 'github/helper'
5
+ require 'rubygems'
6
+ require 'open-uri'
7
+ require 'json'
8
+ require 'yaml'
9
+
10
+ ##
11
+ # Starting simple.
12
+ #
13
+ # $ github <command> <args>
14
+ #
15
+ # GitHub.command <command> do |*args|
16
+ # whatever
17
+ # end
18
+ #
19
+ # We'll probably want to use the `choice` gem for concise, tasty DSL
20
+ # arg parsing action.
21
+ #
22
+
23
+ module GitHub
24
+ extend self
25
+
26
+ BasePath = File.expand_path(File.dirname(__FILE__) + '/..')
27
+
28
+ def command(command, &block)
29
+ debug "Registered `#{command}`"
30
+ descriptions[command] = @next_description if @next_description
31
+ @next_description = nil
32
+ flag_descriptions[command].update @next_flags if @next_flags
33
+ @next_flags = nil
34
+ commands[command.to_s] = Command.new(block)
35
+ end
36
+
37
+ def desc(str)
38
+ @next_description = str
39
+ end
40
+
41
+ def flags(hash)
42
+ @next_flags ||= {}
43
+ @next_flags.update hash
44
+ end
45
+
46
+ def helper(command, &block)
47
+ debug "Helper'd `#{command}`"
48
+ Helper.send :define_method, command, &block
49
+ end
50
+
51
+ def activate(args)
52
+ @@original_args = args.clone
53
+ @options = parse_options(args)
54
+ @debug = @options[:debug]
55
+ load 'helpers.rb'
56
+ load 'commands.rb'
57
+ invoke(args.shift, *args)
58
+ end
59
+
60
+ def invoke(command, *args)
61
+ block = find_command(command)
62
+ debug "Invoking `#{command}`"
63
+ block.call(*args)
64
+ end
65
+
66
+ def find_command(name)
67
+ name = name.to_s
68
+ commands[name] || GitCommand.new(name) || commands['default']
69
+ end
70
+
71
+ def commands
72
+ @commands ||= {}
73
+ end
74
+
75
+ def descriptions
76
+ @descriptions ||= {}
77
+ end
78
+
79
+ def flag_descriptions
80
+ @flagdescs ||= Hash.new { |h, k| h[k] = {} }
81
+ end
82
+
83
+ def options
84
+ @options
85
+ end
86
+
87
+ def original_args
88
+ @@original_args ||= []
89
+ end
90
+
91
+ def parse_options(args)
92
+ idx = 0
93
+ args.clone.inject({}) do |memo, arg|
94
+ case arg
95
+ when /^--(.+?)=(.*)/
96
+ args.delete_at(idx)
97
+ memo.merge($1.to_sym => $2)
98
+ when /^--(.+)/
99
+ args.delete_at(idx)
100
+ memo.merge($1.to_sym => true)
101
+ when "--"
102
+ args.delete_at(idx)
103
+ return memo
104
+ else
105
+ idx += 1
106
+ memo
107
+ end
108
+ end
109
+ end
110
+
111
+ def load(file)
112
+ file[0] == ?/ ? path = file : path = BasePath + "/commands/#{file}"
113
+ data = File.read(path)
114
+ GitHub.module_eval data, path
115
+ end
116
+
117
+ def debug(*messages)
118
+ puts *messages.map { |m| "== #{m}" } if debug?
119
+ end
120
+
121
+ def debug?
122
+ !!@debug
123
+ end
124
+ end
125
+
126
+ GitHub.command :default do
127
+ puts "Usage: github command <space separated arguments>", ''
128
+ puts "Available commands:", ''
129
+ longest = GitHub.descriptions.map { |d,| d.to_s.size }.max
130
+ GitHub.descriptions.each do |command, desc|
131
+ cmdstr = "%-#{longest}s" % command
132
+ puts " #{cmdstr} => #{desc}"
133
+ flongest = GitHub.flag_descriptions[command].map { |d,| "--#{d}".size }.max
134
+ GitHub.flag_descriptions[command].each do |flag, fdesc|
135
+ flagstr = "#{" " * longest} %-#{flongest}s" % "--#{flag}"
136
+ puts " #{flagstr}: #{fdesc}"
137
+ end
138
+ end
139
+ puts
140
+ end
141
+
142
+ GitHub.commands[''] = GitHub.commands['default']
143
+ GitHub.commands['-h'] = GitHub.commands['default']
144
+ GitHub.commands['--help'] = GitHub.commands['default']
145
+ GitHub.commands['-help'] = GitHub.commands['default']
146
+ GitHub.commands['help'] = GitHub.commands['default']