git-forks 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.
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: []