vertiginous-github 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2008 Chris Wanstrath
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest ADDED
@@ -0,0 +1,17 @@
1
+ bin/github
2
+ commands/commands.rb
3
+ commands/helpers.rb
4
+ lib/extensions.rb
5
+ lib/github/command.rb
6
+ lib/github/helper.rb
7
+ lib/github.rb
8
+ LICENSE
9
+ Manifest
10
+ README
11
+ spec/command_spec.rb
12
+ spec/extensions_spec.rb
13
+ spec/github_spec.rb
14
+ spec/helper_spec.rb
15
+ spec/spec_helper.rb
16
+ spec/ui_spec.rb
17
+ spec/windoze_spec.rb
data/README ADDED
@@ -0,0 +1,54 @@
1
+ The GitHub Gem
2
+ =============
3
+
4
+ This gem'll work hand-in-hand with GitHub's API to help you out.
5
+
6
+ Catch us in the #github room on freenode if you want to get involved. Or just fork and send a pull request.
7
+
8
+ ===========
9
+ Getting started
10
+ ===========
11
+
12
+ $ gem install github
13
+
14
+ Run it:
15
+
16
+ $ github <command> <args>
17
+
18
+
19
+ =============
20
+ Pulling Changes
21
+ =============
22
+
23
+ Let's say you just forked `github-gem` on GitHub from defunkt.
24
+
25
+ $ git clone git://github.com/YOU/github-gem.git
26
+ $ cd github-gem
27
+ $ github pull defunkt
28
+
29
+ This will setup a remote and branch for defunkt's repository at master.
30
+ In this case, a 'defunkt/master' branch.
31
+
32
+ If defunkt makes some changes you want, simply `github pull defunkt`. This will
33
+ leave you in the 'defunkt/master' branch after pulling changes from defunkt's
34
+ remote. After confirming that defunkt's changes were what you wanted, run `git
35
+ checkout master` and then `git merge defunkt/master` to merge defunkt's changes
36
+ into your own master branch. In summary:
37
+
38
+ $ github pull defunkt
39
+ $ git checkout master
40
+ $ git merge defunkt/master
41
+
42
+ If you've already reviewed defunkt's changes and just want to merge them into your
43
+ master branch, use the `merge` flag:
44
+
45
+ $ github pull --merge defunkt
46
+
47
+ ==========
48
+ Contributors
49
+ ==========
50
+
51
+ - defunkt
52
+ - maddox
53
+ - halorgium
54
+ - kballard
data/bin/github ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/github'
4
+
5
+ GitHub.activate ARGV
@@ -0,0 +1,95 @@
1
+ GitHub.describe :home => "Open this repo's master branch in a web browser."
2
+ GitHub.register :home do |user|
3
+ if helper.project
4
+ helper.open helper.homepage_for(user || helper.owner, 'master')
5
+ end
6
+ end
7
+
8
+ GitHub.describe :browse => "Open this repo in a web browser."
9
+ GitHub.register :browse do |user, branch|
10
+ if helper.project
11
+ # if one arg given, treat it as a branch name
12
+ # unless it maches user/branch, then split it
13
+ # if two args given, treat as user branch
14
+ # if no args given, use defaults
15
+ user, branch = user.split("/", 2) if branch.nil? unless user.nil?
16
+ branch = user and user = nil if branch.nil?
17
+ user ||= helper.branch_user
18
+ branch ||= helper.branch_name
19
+ helper.open helper.homepage_for(user, branch)
20
+ end
21
+ end
22
+
23
+ GitHub.describe :network => "Open the network page for this repo in a web browser"
24
+ GitHub.register :network do |user|
25
+ if helper.project
26
+ user ||= helper.owner
27
+ helper.open helper.network_page_for(user)
28
+ end
29
+ end
30
+
31
+ GitHub.describe :info => "Info about this project."
32
+ GitHub.register :info do
33
+ puts "== Info for #{helper.project}"
34
+ puts "You are #{helper.owner}"
35
+ puts "Currently tracking:"
36
+ helper.tracking.each do |(name,user_or_url)|
37
+ puts " - #{user_or_url} (as #{name})"
38
+ end
39
+ end
40
+
41
+ GitHub.describe :track => "Track another user's repository."
42
+ GitHub.flags :track, :private => "Use git@github.com: instead of git://github.com/"
43
+ GitHub.register :track do |user|
44
+ die "Specify a user to track" if user.nil?
45
+ die "Already tracking #{user}" if helper.tracking?(user)
46
+
47
+ if options[:private]
48
+ git "remote add #{user} #{helper.private_url_for(user)}"
49
+ else
50
+ git "remote add #{user} #{helper.public_url_for(user)}"
51
+ end
52
+ end
53
+
54
+ GitHub.describe :pull => "Pull from a remote."
55
+ GitHub.flags :pull, :merge => "Automatically merge remote's changes into your master."
56
+ GitHub.register :pull do |user, branch|
57
+ die "Specify a user to pull from" if user.nil?
58
+ user, branch = user.split("/", 2) if branch.nil?
59
+ branch ||= 'master'
60
+ GitHub.invoke(:track, user) unless helper.tracking?(user)
61
+
62
+ if options[:merge]
63
+ git_exec "pull #{user} #{branch}"
64
+ else
65
+ puts "Switching to #{user}/#{branch}"
66
+ git "checkout #{user}/#{branch}" if git("checkout -b #{user}/#{branch}").error?
67
+ git_exec "pull #{user} #{branch}"
68
+ end
69
+ end
70
+
71
+ GitHub.describe :clone => "Clone a repo."
72
+ GitHub.flags :clone, :ssh => "Clone using the git@github.com style url"
73
+ GitHub.register :clone do |user, repo, dir|
74
+ die "Specify a user to pull from" if user.nil?
75
+ user, repo = user.split('/') unless repo
76
+ die "Specify a repo to pull from" if repo.nil?
77
+
78
+ if options[:ssh]
79
+ git_exec "clone git@github.com:#{user}/#{repo}.git" + (dir ? " #{dir}" : "")
80
+ else
81
+ git_exec "clone git://github.com/#{user}/#{repo}.git" + (dir ? " #{dir}" : "")
82
+ end
83
+ end
84
+
85
+ GitHub.describe :'pull-request' => "Generate the text for a pull request"
86
+ GitHub.register :'pull-request' do |user, branch|
87
+ if helper.project
88
+ die "Specify a user for the pull request" if user.nil?
89
+ user, branch = user.split('/', 2) if branch.nil?
90
+ branch ||= 'master'
91
+ GitHub.invoke(:track, user) unless helper.tracking?(user)
92
+
93
+ git_exec "request-pull #{user}/#{branch} origin"
94
+ end
95
+ end
@@ -0,0 +1,102 @@
1
+ GitHub.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
+ GitHub.helper :user_and_repo_for do |remote|
9
+ user_and_repo_from(url_for(remote))
10
+ end
11
+
12
+ GitHub.helper :user_for do |remote|
13
+ user_and_repo_for(remote).try.first
14
+ end
15
+
16
+ GitHub.helper :repo_for do |remote|
17
+ user_and_repo_for(remote).try.last
18
+ end
19
+
20
+ GitHub.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
+ GitHub.helper :url_for do |remote|
34
+ `git config --get remote.#{remote}.url`.chomp
35
+ end
36
+
37
+ GitHub.helper :remotes do
38
+ regexp = '^remote\.(.+)\.url$'
39
+ `git config --get-regexp '#{regexp}'`.split(/\n/).inject({}) do |memo, line|
40
+ name_string, url = line.split(/ /, 2)
41
+ m, name = *name_string.match(/#{regexp}/)
42
+ memo[name.to_sym] = url
43
+ memo
44
+ end
45
+ end
46
+
47
+ GitHub.helper :tracking do
48
+ remotes.inject({}) do |memo, (name, url)|
49
+ if ur = user_and_repo_from(url)
50
+ memo[name] = ur.first
51
+ else
52
+ memo[name] = url
53
+ end
54
+ memo
55
+ end
56
+ end
57
+
58
+ GitHub.helper :tracking? do |user|
59
+ tracking.values.include?(user)
60
+ end
61
+
62
+ GitHub.helper :owner do
63
+ user_for(:origin)
64
+ end
65
+
66
+ GitHub.helper :user_and_branch do
67
+ raw_branch = `git rev-parse --symbolic-full-name HEAD`.chomp.sub(/^refs\/heads\//, '')
68
+ user, branch = raw_branch.split(/\//, 2)
69
+ if branch
70
+ [user, branch]
71
+ else
72
+ [owner, user]
73
+ end
74
+ end
75
+
76
+ GitHub.helper :branch_user do
77
+ user_and_branch.first
78
+ end
79
+
80
+ GitHub.helper :branch_name do
81
+ user_and_branch.last
82
+ end
83
+
84
+ GitHub.helper :public_url_for do |user|
85
+ "git://github.com/#{user}/#{project}.git"
86
+ end
87
+
88
+ GitHub.helper :private_url_for do |user|
89
+ "git@github.com:#{user}/#{project}.git"
90
+ end
91
+
92
+ GitHub.helper :homepage_for do |user, branch|
93
+ "https://github.com/#{user}/#{project}/tree/#{branch}"
94
+ end
95
+
96
+ GitHub.helper :network_page_for do |user|
97
+ "https://github.com/#{user}/#{project}/network"
98
+ end
99
+
100
+ GitHub.helper :open do |url|
101
+ Launchy::Browser.new.visit url
102
+ end
data/lib/extensions.rb ADDED
@@ -0,0 +1,28 @@
1
+ # define #try
2
+ class Object
3
+ def try
4
+ self
5
+ end
6
+ end
7
+
8
+ class NilClass
9
+ klass = Class.new
10
+ klass.class_eval do
11
+ instance_methods.each { |meth| undef_method meth.to_sym unless meth =~ /^__(id|send)__$/ }
12
+ def method_missing(*args)
13
+ nil
14
+ end
15
+ end
16
+ NilProxy = klass.new
17
+ def try
18
+ NilProxy
19
+ end
20
+ end
21
+
22
+ # define #tap
23
+ class Object
24
+ def tap(&block)
25
+ block.call(self)
26
+ self
27
+ end
28
+ end
data/lib/github.rb ADDED
@@ -0,0 +1,120 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ require 'extensions'
3
+ require 'github/command'
4
+ require 'github/helper'
5
+ require 'rubygems'
6
+ require 'launchy'
7
+
8
+ ##
9
+ # Starting simple.
10
+ #
11
+ # $ github <command> <args>
12
+ #
13
+ # GitHub.register <command> do |*args|
14
+ # whatever
15
+ # end
16
+ #
17
+ # We'll probably want to use the `choice` gem for concise, tasty DSL
18
+ # arg parsing action.
19
+ #
20
+
21
+ module GitHub
22
+ extend self
23
+
24
+ BasePath = File.expand_path(File.dirname(__FILE__) + '/..')
25
+
26
+ def register(command, &block)
27
+ debug "Registered `#{command}`"
28
+ commands[command.to_s] = Command.new(block)
29
+ end
30
+
31
+ def describe(hash)
32
+ descriptions.update hash
33
+ end
34
+
35
+ def flags(command, hash)
36
+ flag_descriptions[command].update hash
37
+ end
38
+
39
+ def helper(command, &block)
40
+ debug "Helper'd `#{command}`"
41
+ Helper.send :define_method, command, &block
42
+ end
43
+
44
+ def activate(args)
45
+ @options = parse_options(args)
46
+ load 'helpers.rb'
47
+ load 'commands.rb'
48
+ invoke(args.shift, *args)
49
+ end
50
+
51
+ def invoke(command, *args)
52
+ block = commands[command.to_s] || commands['default']
53
+ debug "Invoking `#{command}`"
54
+ block.call(*args)
55
+ end
56
+
57
+ def commands
58
+ @commands ||= {}
59
+ end
60
+
61
+ def descriptions
62
+ @descriptions ||= {}
63
+ end
64
+
65
+ def flag_descriptions
66
+ @flagdescs ||= Hash.new { |h, k| h[k] = {} }
67
+ end
68
+
69
+ def options
70
+ @options
71
+ end
72
+
73
+ def parse_options(args)
74
+ idx = 0
75
+ args.clone.inject({}) do |memo, arg|
76
+ case arg
77
+ when /^--(.+?)=(.*)/
78
+ args.delete_at(idx)
79
+ memo.merge($1.to_sym => $2)
80
+ when /^--(.+)/
81
+ args.delete_at(idx)
82
+ memo.merge($1.to_sym => true)
83
+ when "--"
84
+ args.delete_at(idx)
85
+ return memo
86
+ else
87
+ idx += 1
88
+ memo
89
+ end
90
+ end
91
+ end
92
+
93
+ def load(file)
94
+ file[0] == ?/ ? super : super(BasePath + "/commands/#{file}")
95
+ end
96
+
97
+ def debug(*messages)
98
+ puts *messages.map { |m| "== #{m}" } if debug?
99
+ end
100
+
101
+ def debug?
102
+ !!@debug
103
+ end
104
+ end
105
+
106
+ GitHub.register :default do
107
+ puts "Usage: github command <space separated arguments>", ''
108
+ puts "Available commands:", ''
109
+ longest = GitHub.descriptions.map { |d,| d.to_s.size }.max
110
+ GitHub.descriptions.each do |command, desc|
111
+ cmdstr = "%-#{longest}s" % command
112
+ puts " #{cmdstr} => #{desc}"
113
+ flongest = GitHub.flag_descriptions[command].map { |d,| "--#{d}".size }.max
114
+ GitHub.flag_descriptions[command].each do |flag, fdesc|
115
+ flagstr = "#{" " * longest} %-#{flongest}s" % "--#{flag}"
116
+ puts " #{flagstr}: #{fdesc}"
117
+ end
118
+ end
119
+ puts
120
+ end