git-forks 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/LICENSE +20 -0
  2. data/bin/git-forks +8 -0
  3. data/lib/git-forks.rb +281 -0
  4. metadata +80 -0
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Scott Chacon
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.
data/bin/git-forks ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+
4
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
5
+
6
+ require 'git-forks'
7
+
8
+ GitForks.start(ARGV)
data/lib/git-forks.rb ADDED
@@ -0,0 +1,281 @@
1
+ # Octokit is used to access GitHub's API.
2
+ require 'octokit'
3
+ # Time is used to parse time strings from git back into Time objects.
4
+ require 'time'
5
+ # JSON is used to cache GitHub API response data.
6
+ require 'json'
7
+
8
+ class GitForks
9
+
10
+ CACHE_FILE = '.git/forks_cache.json'
11
+
12
+ def initialize(args)
13
+ @command = args.shift
14
+ @user, @repo = repo_info
15
+ @args = args
16
+ @branch_pattern = branch_pattern
17
+ end
18
+
19
+ def self.start(args)
20
+ GitForks.new(args).run
21
+ end
22
+
23
+ def run
24
+ configure
25
+ if @command && self.respond_to?(@command)
26
+ # If the cache file doesn't exist, make sure we run update
27
+ # before any other command. git-forks will otherwise crash
28
+ # with an exception.
29
+ update unless File.exists?(CACHE_FILE) || @command == 'update'
30
+
31
+ self.send @command
32
+ elsif %w(-h --help).include?(@command)
33
+ usage
34
+ else
35
+ help
36
+ end
37
+ end
38
+
39
+ #-----------------------------------------------------------------------------
40
+ # Commands
41
+ #-----------------------------------------------------------------------------
42
+
43
+ # Get the latest GitHub data.
44
+ def update
45
+ cache('forks', fetch_fork_info)
46
+ update_branches
47
+ end
48
+
49
+ # Fetch and cache all branches for each fork.
50
+ def update_branches(pattern=nil)
51
+ pattern ||= @branch_pattern
52
+ forks = get_cached_data('forks')
53
+
54
+ forks.each do |fork|
55
+ fork_user = fork['owner']['login']
56
+ puts "Checking for new branches matching '#{pattern}' in '#{fork_user}/#{@repo}'"
57
+
58
+ branches = fetch_fork_branches(fork_user, pattern)
59
+
60
+ fork['branches'] = branches
61
+ end
62
+
63
+ cache('forks', forks)
64
+ end
65
+
66
+ # List all forks.
67
+ #
68
+ # Example::
69
+ #
70
+ # --------------------------------------------------------------------------------
71
+ # Forks of 'doubleotoo/foo/master'
72
+ #
73
+ # Owner Branches Updated
74
+ # ------ -------- -------
75
+ # justintoo 2 01-May-12
76
+ # rose-compiler 3 27-Apr-12
77
+ #
78
+ def list
79
+ forks = get_cached_data('forks')
80
+ forks.reverse! if @args.shift == '--reverse'
81
+
82
+ output = forks.collect do |f|
83
+ line = ""
84
+ line << l(f['owner']['login'], 25)
85
+ line << l(f['branches'].size, 12)
86
+ line << strftime(clean(f['updated_at']))
87
+ end
88
+
89
+ if output.compact.empty?
90
+ puts "No forks of '#{@user}/#{@repo}'"
91
+ else
92
+ puts '-' * 80
93
+ puts "Forks of '#{@user}/#{@repo}'"
94
+ puts
95
+ puts l('Owner', 25) + l('Branches', 12) + 'Updated'
96
+ puts l('------', 25) + l('--------', 12) + '-------'
97
+ puts output.compact
98
+ puts
99
+ end
100
+ end
101
+
102
+ # Show details of one fork.
103
+ #
104
+ # Example::
105
+ #
106
+ # -------------------------------------------------------------------------------
107
+ # Owner : justintoo
108
+ # Created : 01-May-12
109
+ # Updated : 01-May-12
110
+ # Branches : 2
111
+ # 444a867d338cafc0c82d058b458b4fe268fa14d6 master
112
+ # 14178fe5b204c38650de8ddaf5d9fb80aa834e74 foo
113
+ #
114
+ def show
115
+ owner = @args.shift
116
+ option = @args.shift
117
+ if f = fork(owner)
118
+ puts '-' * 80
119
+ puts "Owner : #{f['owner']['login']}"
120
+ puts "Created : #{strftime(f['created_at'])}"
121
+ puts "Updated : #{strftime(f['updated_at'])}"
122
+ puts "Branches : #{f['branches'].size}"
123
+ f['branches'].each do |b|
124
+ puts " #{b['commit']['sha']} #{b['name']}"
125
+ end
126
+ puts
127
+ else
128
+ puts "No such fork: #{owner}/#{@repo}"
129
+ end
130
+ end
131
+
132
+ # Show a quick reference of available commands.
133
+ def usage
134
+ puts 'Usage: git forks <command>'
135
+ puts 'Get GitHub project forks information.'
136
+ puts
137
+ puts 'Available commands:'
138
+ puts ' update Retrieve fork info from GitHub API v3.'
139
+ puts ' list [--reverse] List all forks.'
140
+ puts ' show <owner> Show details for a single fork.'
141
+ puts
142
+ puts 'Git configurations:'
143
+ puts ' github.forks.branchpattern Only grab branches matching this Ruby Regexp.'
144
+ end
145
+
146
+ #-----------------------------------------------------------------------------
147
+ # Cache
148
+ #-----------------------------------------------------------------------------
149
+
150
+ def cache(group, json)
151
+ save_data({group => json}, CACHE_FILE)
152
+ end
153
+
154
+ # get_cached_data('forks')
155
+ def get_cached_data(group=nil)
156
+ data = JSON.parse(File.read(CACHE_FILE))
157
+ if group
158
+ data[group]
159
+ else
160
+ data
161
+ end
162
+ end
163
+
164
+ def save_data(data, file)
165
+ File.open(file, "w+") do |f|
166
+ f.puts data.to_json
167
+ end
168
+ end
169
+
170
+ # Get a fork by owner name.
171
+ def fork(owner)
172
+ forks = get_cached_data('forks')
173
+ forks.select {|f| f['owner']['login'] == owner }.first
174
+ end
175
+
176
+ #-----------------------------------------------------------------------------
177
+ # GitHub API v3 (using Octokit gem)
178
+ #-----------------------------------------------------------------------------
179
+
180
+ def fetch_fork_info
181
+ forks = Octokit.forks("#{@user}/#{@repo}")
182
+ end
183
+
184
+ def fetch_fork_branches(fork_user, pattern)
185
+ pattern ||= @branch_pattern
186
+ branches = Octokit.branches("#{fork_user}/#{@repo}")
187
+ .select {|b| not b.name.match(pattern).nil? }
188
+ end
189
+
190
+ #-----------------------------------------------------------------------------
191
+ # Display Helper Functions
192
+ #-----------------------------------------------------------------------------
193
+
194
+ def l(info, size)
195
+ clean(info)[0, size].ljust(size)
196
+ end
197
+
198
+ def r(info, size)
199
+ clean(info)[0, size].rjust(size)
200
+ end
201
+
202
+ def clean(info)
203
+ info.to_s.gsub("\n", ' ')
204
+ end
205
+
206
+ def strftime(time_string)
207
+ Time.parse(time_string).strftime('%d-%b-%y')
208
+ end
209
+
210
+ #-----------------------------------------------------------------------------
211
+ private
212
+ #-----------------------------------------------------------------------------
213
+
214
+ def help
215
+ puts "No command: #{@command}"
216
+ puts "Try: update, list, show"
217
+ puts "or call with '-h' for usage information"
218
+ end
219
+
220
+ def configure
221
+ Octokit.configure do |config|
222
+ #config.login = github_login
223
+ end
224
+ end
225
+
226
+ #def github_login
227
+ # git("config --get-all github.user")
228
+ #end
229
+
230
+ def branch_pattern
231
+ patterns = git("config --get-all github.forks.branchpattern")
232
+ if patterns.empty?
233
+ Regexp.new(/.+/) # match anything
234
+ else
235
+ Regexp.new(patterns.gsub("\n", "|"))
236
+ end
237
+ end
238
+
239
+ def repo_info
240
+ c = {}
241
+ config = git('config --list')
242
+ config.split("\n").each do |line|
243
+ k, v = line.split('=')
244
+ c[k] = v
245
+ end
246
+ u = c['remote.origin.url']
247
+
248
+ user, proj = github_user_and_proj(u)
249
+ if !(user and proj)
250
+ short, base = github_insteadof_matching(c, u)
251
+ if short and base
252
+ u = u.sub(short, base)
253
+ user, proj = github_user_and_proj(u)
254
+ end
255
+ end
256
+ [user, proj]
257
+ end
258
+
259
+ def github_insteadof_matching(c, u)
260
+ first = c.collect {|k,v| [v, /url\.(.*github\.com.*)\.insteadof/.match(k)]}.
261
+ find {|v,m| u.index(v) and m != nil}
262
+ if first
263
+ return first[0], first[1][1]
264
+ end
265
+ return nil, nil
266
+ end
267
+
268
+ def github_user_and_proj(u)
269
+ # Trouble getting optional ".git" at end to work, so put that logic below
270
+ m = /github\.com.(.*?)\/(.*)/.match(u)
271
+ if m
272
+ return m[1], m[2].sub(/\.git\Z/, "")
273
+ end
274
+ return nil, nil
275
+ end
276
+
277
+ def git(command)
278
+ `git #{command}`.chomp
279
+ end
280
+
281
+ end # GitForks
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: git-forks
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Justin Too
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-01 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: octokit
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - '='
36
+ - !ruby/object:Gem::Version
37
+ version: 0.6.5
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - '='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.6.5
46
+ description: git-forks gets info about a GitHub project's forks.
47
+ email: doubleotoo@gmail.com
48
+ executables:
49
+ - git-forks
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - LICENSE
54
+ - lib/git-forks.rb
55
+ - bin/git-forks
56
+ homepage: http://github.com/doubleotoo/git-forks
57
+ licenses: []
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 1.8.24
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: gets info about a GitHub project's forks
80
+ test_files: []