git-topic 0.1.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.
- data/.gitignore +4 -0
- data/.gvimrc +23 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/.vimproject +26 -0
- data/.vimrc +1 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +63 -0
- data/History.txt +6 -0
- data/LICENSE +20 -0
- data/README.rdoc +192 -0
- data/Rakefile +62 -0
- data/VERSION +1 -0
- data/autotest/discover.rb +1 -0
- data/bin/git-topic +143 -0
- data/git-topic.gemspec +127 -0
- data/lib/git-topic.rb +375 -0
- data/lib/util.rb +34 -0
- data/spec/git-topic_spec.rb +378 -0
- data/spec/spec_helper.rb +56 -0
- data/spec/template/origin/HEAD +1 -0
- data/spec/template/origin/config +7 -0
- data/spec/template/origin/description +1 -0
- data/spec/template/origin/hooks/applypatch-msg.sample +15 -0
- data/spec/template/origin/hooks/commit-msg.sample +24 -0
- data/spec/template/origin/hooks/post-commit.sample +8 -0
- data/spec/template/origin/hooks/post-receive.sample +15 -0
- data/spec/template/origin/hooks/post-update.sample +8 -0
- data/spec/template/origin/hooks/pre-applypatch.sample +14 -0
- data/spec/template/origin/hooks/pre-commit.sample +46 -0
- data/spec/template/origin/hooks/pre-rebase.sample +169 -0
- data/spec/template/origin/hooks/prepare-commit-msg.sample +36 -0
- data/spec/template/origin/hooks/update.sample +128 -0
- data/spec/template/origin/info/exclude +6 -0
- data/spec/template/origin/objects/0a/da6d051b94cd0df50f5a0b7229aec26f0d2cdf +0 -0
- data/spec/template/origin/objects/0c/e06c616769768f09f5e629cfcc68eabe3dee81 +0 -0
- data/spec/template/origin/objects/20/049991cdafdce826f5a3c01e10ffa84d6997ec +0 -0
- data/spec/template/origin/objects/33/1d827fd47fb234af54e3a4bbf8c6705e9116cc +3 -0
- data/spec/template/origin/objects/41/51899b742fd6b1c873b177b9d13451682089bc +0 -0
- data/spec/template/origin/objects/44/ffd9c9c8b52b201659e3ad318cdad6ec836b46 +0 -0
- data/spec/template/origin/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 +0 -0
- data/spec/template/origin/objects/55/eeb01bdf874d1a35870bcf24a970c475c63344 +0 -0
- data/spec/template/origin/objects/8d/09f9b8d80ce282218125cb0cbf53cccf022203 +0 -0
- data/spec/template/origin/objects/b4/8e68d5cac189af36abe48e893d11c24b7b2a19 +0 -0
- data/spec/template/origin/objects/c0/838ed2ee8f2e83c8bda859fc5e332b92f0a5a3 +1 -0
- data/spec/template/origin/objects/cd/f7b9dbc4911a0d1404db54cde2ed448f6a6afd +0 -0
- data/spec/template/origin/objects/d2/6b33daea1ed9823a189992bba38fbc913483c1 +0 -0
- data/spec/template/origin/objects/fe/4e254557e19f338f40ccfdc00a7517771db880 +0 -0
- data/spec/template/origin/refs/heads/master +1 -0
- data/spec/template/origin/refs/heads/rejected/davidjh/krakens +1 -0
- data/spec/template/origin/refs/heads/review/davidjh/pirates +1 -0
- data/spec/template/origin/refs/heads/review/user24601/ninja-basic +1 -0
- data/spec/template/origin/refs/heads/review/user24601/zombie-basic +1 -0
- metadata +158 -0
data/git-topic.gemspec
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{git-topic}
|
8
|
+
s.version = "0.1.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["David J. Hamilton"]
|
12
|
+
s.date = %q{2010-07-08}
|
13
|
+
s.default_executable = %q{git-topic}
|
14
|
+
s.description = %q{
|
15
|
+
gem command around reviewed topic branches. Supports workflow of the form:
|
16
|
+
|
17
|
+
# alexander:
|
18
|
+
git work-on <topic>
|
19
|
+
git done
|
20
|
+
|
21
|
+
# bismarck:
|
22
|
+
git status # notice a review branch
|
23
|
+
git review <topic>
|
24
|
+
# happy, merge into master, push and cleanup
|
25
|
+
git accept
|
26
|
+
|
27
|
+
git review <topic2>
|
28
|
+
# unhappy
|
29
|
+
git reject
|
30
|
+
|
31
|
+
# alexander:
|
32
|
+
git status # notice rejected topic
|
33
|
+
git work-on <topic>
|
34
|
+
|
35
|
+
see README.rdoc for more (any) details.
|
36
|
+
}
|
37
|
+
s.email = %q{git-topic@hjdivad.com}
|
38
|
+
s.executables = ["git-topic"]
|
39
|
+
s.extra_rdoc_files = [
|
40
|
+
"LICENSE",
|
41
|
+
"README.rdoc"
|
42
|
+
]
|
43
|
+
s.files = [
|
44
|
+
".gitignore",
|
45
|
+
".gvimrc",
|
46
|
+
".rspec",
|
47
|
+
".rvmrc",
|
48
|
+
".vimproject",
|
49
|
+
".vimrc",
|
50
|
+
"Gemfile",
|
51
|
+
"Gemfile.lock",
|
52
|
+
"History.txt",
|
53
|
+
"LICENSE",
|
54
|
+
"README.rdoc",
|
55
|
+
"Rakefile",
|
56
|
+
"VERSION",
|
57
|
+
"autotest/discover.rb",
|
58
|
+
"bin/git-topic",
|
59
|
+
"git-topic.gemspec",
|
60
|
+
"lib/git-topic.rb",
|
61
|
+
"lib/util.rb",
|
62
|
+
"spec/git-topic_spec.rb",
|
63
|
+
"spec/spec_helper.rb",
|
64
|
+
"spec/template/origin/HEAD",
|
65
|
+
"spec/template/origin/config",
|
66
|
+
"spec/template/origin/description",
|
67
|
+
"spec/template/origin/hooks/applypatch-msg.sample",
|
68
|
+
"spec/template/origin/hooks/commit-msg.sample",
|
69
|
+
"spec/template/origin/hooks/post-commit.sample",
|
70
|
+
"spec/template/origin/hooks/post-receive.sample",
|
71
|
+
"spec/template/origin/hooks/post-update.sample",
|
72
|
+
"spec/template/origin/hooks/pre-applypatch.sample",
|
73
|
+
"spec/template/origin/hooks/pre-commit.sample",
|
74
|
+
"spec/template/origin/hooks/pre-rebase.sample",
|
75
|
+
"spec/template/origin/hooks/prepare-commit-msg.sample",
|
76
|
+
"spec/template/origin/hooks/update.sample",
|
77
|
+
"spec/template/origin/info/exclude",
|
78
|
+
"spec/template/origin/objects/0a/da6d051b94cd0df50f5a0b7229aec26f0d2cdf",
|
79
|
+
"spec/template/origin/objects/0c/e06c616769768f09f5e629cfcc68eabe3dee81",
|
80
|
+
"spec/template/origin/objects/20/049991cdafdce826f5a3c01e10ffa84d6997ec",
|
81
|
+
"spec/template/origin/objects/33/1d827fd47fb234af54e3a4bbf8c6705e9116cc",
|
82
|
+
"spec/template/origin/objects/41/51899b742fd6b1c873b177b9d13451682089bc",
|
83
|
+
"spec/template/origin/objects/44/ffd9c9c8b52b201659e3ad318cdad6ec836b46",
|
84
|
+
"spec/template/origin/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904",
|
85
|
+
"spec/template/origin/objects/55/eeb01bdf874d1a35870bcf24a970c475c63344",
|
86
|
+
"spec/template/origin/objects/8d/09f9b8d80ce282218125cb0cbf53cccf022203",
|
87
|
+
"spec/template/origin/objects/b4/8e68d5cac189af36abe48e893d11c24b7b2a19",
|
88
|
+
"spec/template/origin/objects/c0/838ed2ee8f2e83c8bda859fc5e332b92f0a5a3",
|
89
|
+
"spec/template/origin/objects/cd/f7b9dbc4911a0d1404db54cde2ed448f6a6afd",
|
90
|
+
"spec/template/origin/objects/d2/6b33daea1ed9823a189992bba38fbc913483c1",
|
91
|
+
"spec/template/origin/objects/fe/4e254557e19f338f40ccfdc00a7517771db880",
|
92
|
+
"spec/template/origin/refs/heads/master",
|
93
|
+
"spec/template/origin/refs/heads/rejected/davidjh/krakens",
|
94
|
+
"spec/template/origin/refs/heads/review/davidjh/pirates",
|
95
|
+
"spec/template/origin/refs/heads/review/user24601/ninja-basic",
|
96
|
+
"spec/template/origin/refs/heads/review/user24601/zombie-basic"
|
97
|
+
]
|
98
|
+
s.homepage = %q{http://github.com/hjdivad/git-topic}
|
99
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
100
|
+
s.require_paths = ["lib"]
|
101
|
+
s.rubygems_version = %q{1.3.7}
|
102
|
+
s.summary = %q{git command around reviewed topic branches}
|
103
|
+
s.test_files = [
|
104
|
+
"spec/spec_helper.rb",
|
105
|
+
"spec/git-topic_spec.rb"
|
106
|
+
]
|
107
|
+
|
108
|
+
if s.respond_to? :specification_version then
|
109
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
110
|
+
s.specification_version = 3
|
111
|
+
|
112
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
113
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
114
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
115
|
+
s.add_development_dependency(%q<cucumber>, [">= 0"])
|
116
|
+
else
|
117
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
118
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
119
|
+
s.add_dependency(%q<cucumber>, [">= 0"])
|
120
|
+
end
|
121
|
+
else
|
122
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
123
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
124
|
+
s.add_dependency(%q<cucumber>, [">= 0"])
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
data/lib/git-topic.rb
ADDED
@@ -0,0 +1,375 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'active_support'
|
5
|
+
require 'active_support/core_ext/hash/keys'
|
6
|
+
|
7
|
+
require 'util'
|
8
|
+
|
9
|
+
|
10
|
+
module GitTopic
|
11
|
+
GlobalOptKeys = [ :verbose, :help, :verbose_given ]
|
12
|
+
|
13
|
+
class << self
|
14
|
+
|
15
|
+
# Switch to a branch for the given topic.
|
16
|
+
def work_on( topic, opts={} )
|
17
|
+
raise "Topic must be specified" if topic.nil?
|
18
|
+
|
19
|
+
# setup a remote branch, if necessary
|
20
|
+
wb = wip_branch( topic )
|
21
|
+
git(
|
22
|
+
"push origin HEAD:#{wb}"
|
23
|
+
) unless remote_branches.include? "origin/#{wb}"
|
24
|
+
# switch to the new branch
|
25
|
+
git [ switch_to_branch( wb, "origin/#{wb}" )]
|
26
|
+
|
27
|
+
# Check for rejected branch
|
28
|
+
rej_branch = rejected_branch( topic )
|
29
|
+
if remote_branches.include? "origin/#{rej_branch}"
|
30
|
+
git [
|
31
|
+
"reset --hard origin/#{rej_branch}",
|
32
|
+
"push origin :#{rej_branch} HEAD:#{wb}",
|
33
|
+
]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Done with the given topic. If none is specified, then topic is assumed to
|
38
|
+
# be the current branch (if it's a topic branch).
|
39
|
+
def done( topic=nil, opts={} )
|
40
|
+
raise(
|
41
|
+
"Branch must be a topic branch"
|
42
|
+
) unless current_branch =~ %r{^wip/}
|
43
|
+
raise(
|
44
|
+
"Working tree must be clean"
|
45
|
+
) unless working_tree_clean?
|
46
|
+
|
47
|
+
topic = current_topic if topic.nil?
|
48
|
+
|
49
|
+
wb = wip_branch( topic )
|
50
|
+
rb = review_branch( topic )
|
51
|
+
git [
|
52
|
+
"push origin #{wb}:#{rb} :#{wb}",
|
53
|
+
"checkout master",
|
54
|
+
"branch -D #{wip_branch( topic )}"
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Produce status like
|
59
|
+
#
|
60
|
+
# # There are 2 topics you can review.
|
61
|
+
# #
|
62
|
+
# # from davidjh:
|
63
|
+
# # zombies
|
64
|
+
# # pirates
|
65
|
+
# # from king-julian:
|
66
|
+
# # fish
|
67
|
+
# # whales
|
68
|
+
# #
|
69
|
+
# # 2 of your topics were rejected.
|
70
|
+
# # dragons
|
71
|
+
# # liches
|
72
|
+
def status( opts={} )
|
73
|
+
opts.assert_valid_keys :prepended, :prepended_given, *GlobalOptKeys
|
74
|
+
|
75
|
+
sb = ''
|
76
|
+
rb = remote_branches_organized
|
77
|
+
review_ut = rb[:review]
|
78
|
+
rejected_ut = rb[:rejected]
|
79
|
+
|
80
|
+
unless review_ut.empty?
|
81
|
+
prep = review_ut.size == 1 ? "is 1" : "are #{review_ut.size}"
|
82
|
+
sb << "# There #{prep} #{'topic'.pluralize( review_ut.size )} you can review.\n\n"
|
83
|
+
|
84
|
+
sb << review_ut.map do |user, topics|
|
85
|
+
sb2 = " from #{user}:\n"
|
86
|
+
sb2 << topics.map{|t| " #{t}"}.join( "\n" )
|
87
|
+
sb2
|
88
|
+
end.join( "\n" )
|
89
|
+
end
|
90
|
+
|
91
|
+
rejected_topics = rejected_ut[ user ] || []
|
92
|
+
unless rejected_topics.empty?
|
93
|
+
sb << "\n" unless review_ut.empty?
|
94
|
+
verb = rejected_topics.size == 1 ? 'is' : 'are'
|
95
|
+
sb << "\n#{rejected_topics.size} of your topics #{verb} rejected.\n "
|
96
|
+
sb << rejected_topics.join( "\n " )
|
97
|
+
end
|
98
|
+
|
99
|
+
sb.gsub! "\n", "\n# "
|
100
|
+
sb << "\n" unless sb.empty?
|
101
|
+
print sb
|
102
|
+
|
103
|
+
if opts[ :prepended ]
|
104
|
+
print "#\n" unless sb.empty?
|
105
|
+
git "status", :show => true
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Switch to a review branch to check somebody else's code.
|
110
|
+
def review( spec=nil, opts={} )
|
111
|
+
rb = remote_branches_organized
|
112
|
+
review_branches = rb[:review]
|
113
|
+
|
114
|
+
if spec.nil?
|
115
|
+
# select the oldest (by HEAD) topic, if any exist
|
116
|
+
if review_branches.empty?
|
117
|
+
puts "nothing to review."
|
118
|
+
return
|
119
|
+
end
|
120
|
+
|
121
|
+
user, topic = oldest_review_user_topic
|
122
|
+
else
|
123
|
+
user, topic = spec.split( '/' )
|
124
|
+
end
|
125
|
+
|
126
|
+
if remote_topic_branch = find_remote_review_branch( topic )
|
127
|
+
git [
|
128
|
+
switch_to_branch(
|
129
|
+
review_branch( topic, user ),
|
130
|
+
remote_topic_branch )]
|
131
|
+
else
|
132
|
+
raise "No review topic found matching ‘#{spec}’"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Accept the branch currently being reviewed.
|
137
|
+
def accept( topic=nil, opts={} )
|
138
|
+
raise "Must be on a review branch." unless on_review_branch?
|
139
|
+
|
140
|
+
# switch to master
|
141
|
+
# merge review branch, assuming FF
|
142
|
+
# push master, destroy remote
|
143
|
+
# destroy local
|
144
|
+
user, topic = user_topic_name( current_branch )
|
145
|
+
|
146
|
+
local_review_branch = current_branch
|
147
|
+
ff_merge = git [
|
148
|
+
"checkout master",
|
149
|
+
"merge --ff-only #{local_review_branch}",
|
150
|
+
]
|
151
|
+
|
152
|
+
unless ff_merge
|
153
|
+
git "checkout #{local_review_branch}"
|
154
|
+
raise "
|
155
|
+
review branch is not up to date: merge not a fast-forward. Either
|
156
|
+
rebase or reject this branch.
|
157
|
+
".cleanup
|
158
|
+
end
|
159
|
+
|
160
|
+
rem_review_branch = find_remote_review_branch( topic ).gsub( %r{^origin/}, '' )
|
161
|
+
git [
|
162
|
+
"push origin :#{rem_review_branch}",
|
163
|
+
"branch -d #{local_review_branch}"
|
164
|
+
]
|
165
|
+
end
|
166
|
+
|
167
|
+
# Reject the branch currently being reviewed.
|
168
|
+
def reject( topic=nil, opts={} )
|
169
|
+
raise "Must be on a review branch." unless on_review_branch?
|
170
|
+
|
171
|
+
# switch to master
|
172
|
+
# push to rejected, destroy remote
|
173
|
+
# destroy local
|
174
|
+
user, topic = user_topic_name( current_branch )
|
175
|
+
|
176
|
+
rem_review_branch = find_remote_review_branch( topic ).gsub( %r{^origin/}, '' )
|
177
|
+
rem_rej_branch = remote_rejected_branch( topic, user )
|
178
|
+
git [
|
179
|
+
"checkout master",
|
180
|
+
"push origin #{current_branch}:#{rem_rej_branch} :#{rem_review_branch}",
|
181
|
+
"branch -D #{current_branch}"
|
182
|
+
]
|
183
|
+
end
|
184
|
+
|
185
|
+
def install_aliases( opts={} )
|
186
|
+
opts.assert_valid_keys :local, :local_given, *GlobalOptKeys
|
187
|
+
|
188
|
+
flags = "--global" unless opts[:local]
|
189
|
+
|
190
|
+
git [
|
191
|
+
"config #{flags} alias.work-on 'topic work-on'",
|
192
|
+
"config #{flags} alias.done 'topic done'",
|
193
|
+
"config #{flags} alias.review 'topic review'",
|
194
|
+
"config #{flags} alias.accept 'topic accept'",
|
195
|
+
"config #{flags} alias.reject 'topic reject'",
|
196
|
+
|
197
|
+
"config #{flags} alias.w 'topic work-on'",
|
198
|
+
"config #{flags} alias.r 'topic review'",
|
199
|
+
"config #{flags} alias.st 'topic status --prepended'",
|
200
|
+
]
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
private
|
205
|
+
|
206
|
+
|
207
|
+
def backup_branch( topic )
|
208
|
+
"backup/#{user}/#{topic}"
|
209
|
+
end
|
210
|
+
|
211
|
+
def wip_branch( topic )
|
212
|
+
"wip/#{user}/#{topic}"
|
213
|
+
end
|
214
|
+
|
215
|
+
def rejected_branch( topic )
|
216
|
+
"rejected/#{user}/#{topic}"
|
217
|
+
end
|
218
|
+
|
219
|
+
def review_branch( topic, user=user )
|
220
|
+
"review/#{user}/#{topic}"
|
221
|
+
end
|
222
|
+
|
223
|
+
def remote_rejected_branch( topic, user=user )
|
224
|
+
"rejected/#{user}/#{topic}"
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
def find_remote_review_branch( topic )
|
229
|
+
others_review_branches.find{|b| b.index topic}
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
def user_topic_name( branch )
|
234
|
+
if branch =~ %r{^origin}
|
235
|
+
branch =~ %r{^\S*?/\S*?/(\S*?)/(\S*)}
|
236
|
+
[$1, $2]
|
237
|
+
else
|
238
|
+
branch =~ %r{^\S*?/(\S*?)/(\S*)}
|
239
|
+
[$1, $2]
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
|
244
|
+
def user
|
245
|
+
@@user ||= (ENV['USER'] || `whoami`)
|
246
|
+
end
|
247
|
+
|
248
|
+
def current_topic
|
249
|
+
current_branch =~ %r{wip/\S*?/(\S*)}
|
250
|
+
$1
|
251
|
+
end
|
252
|
+
|
253
|
+
def current_branch
|
254
|
+
@@current_branch ||= capture_git( "branch --no-color" ).split( "\n" ).find do |b|
|
255
|
+
b =~ %r{^\*}
|
256
|
+
end[ 2..-1 ]
|
257
|
+
end
|
258
|
+
|
259
|
+
def branches
|
260
|
+
@@branches ||= capture_git( "branch --no-color" ).split( "\n" ).map{|b| b[2..-1]}
|
261
|
+
end
|
262
|
+
|
263
|
+
def remote_branches
|
264
|
+
@@remote_branches ||= capture_git( "branch -r --no-color" ).split( "\n" ).map{|b| b[2..-1]}
|
265
|
+
end
|
266
|
+
|
267
|
+
def others_review_branches
|
268
|
+
remote_branches.select do
|
269
|
+
|b| b =~ %r{/review/}
|
270
|
+
end.reject do |b|
|
271
|
+
b =~ %r{/#{user}/}
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def remote_branches_organized
|
276
|
+
@@remote_branches_organized ||= (
|
277
|
+
rb = remote_branches.dup
|
278
|
+
# Convert a bunch of remote branch names, like
|
279
|
+
# origin/HEAD -> origin/masterr
|
280
|
+
# origin/master
|
281
|
+
# origin/review/user1/topic1
|
282
|
+
# origin/something-else
|
283
|
+
# origin/rejected/user2/topic2
|
284
|
+
#
|
285
|
+
# Into a hash with keys 'review' and 'rejected' pointing to hashes of
|
286
|
+
# user-topic(s) pairs.
|
287
|
+
rb.map!{|s| s.gsub( /->.*/, '')}
|
288
|
+
rb.map!{|s| s.strip.split( '/' )}
|
289
|
+
namespace_ut = rb.group_by{|remote, namespace, user, topic| namespace if topic}
|
290
|
+
namespace_ut.reject!{|k,v| not %w(rejected review).include? k}
|
291
|
+
|
292
|
+
namespace_ut.each do |k,v|
|
293
|
+
v.each{|a| a.shift( 2 )}
|
294
|
+
v = namespace_ut[k] = v.group_by{|user, topic| user if topic}
|
295
|
+
v.each{|kk,vv| vv.each(&:shift); vv.flatten!}
|
296
|
+
end
|
297
|
+
|
298
|
+
namespace_ut.symbolize_keys!
|
299
|
+
namespace_ut[:review] ||= {}
|
300
|
+
namespace_ut[:rejected] ||= {}
|
301
|
+
|
302
|
+
namespace_ut[:review].reject!{|k,v| k == user}
|
303
|
+
namespace_ut
|
304
|
+
)
|
305
|
+
end
|
306
|
+
|
307
|
+
def oldest_review_branch
|
308
|
+
return nil if others_review_branches.empty?
|
309
|
+
|
310
|
+
commits_by_age = capture_git([
|
311
|
+
"log --date-order --reverse --pretty=format:%d",
|
312
|
+
"^origin/master #{others_review_branches.join( ' ' )}",
|
313
|
+
].join( " " )).split( "\n" )
|
314
|
+
|
315
|
+
commits_by_age.find do |ref|
|
316
|
+
# no ‘,’, i.e. only one ref matches the commit
|
317
|
+
ref.index( ',' ).nil?
|
318
|
+
end.strip[ 1..-2 ] # chomp the leading and trailing parenthesis
|
319
|
+
end
|
320
|
+
|
321
|
+
def oldest_review_user_topic
|
322
|
+
user_topic_name( oldest_review_branch )
|
323
|
+
end
|
324
|
+
|
325
|
+
def on_review_branch?
|
326
|
+
current_branch =~ %r{^review/}
|
327
|
+
end
|
328
|
+
|
329
|
+
def working_tree_clean?
|
330
|
+
git [ "diff --quiet", "diff --quiet --cached" ]
|
331
|
+
$?.success?
|
332
|
+
end
|
333
|
+
|
334
|
+
def working_tree_dirty?
|
335
|
+
not working_tree_clean?
|
336
|
+
end
|
337
|
+
|
338
|
+
|
339
|
+
def display_git_output?
|
340
|
+
@@display_git_output ||= false
|
341
|
+
end
|
342
|
+
|
343
|
+
def display_git_output!
|
344
|
+
@@display_git_output = true
|
345
|
+
end
|
346
|
+
|
347
|
+
|
348
|
+
def switch_to_branch( branch, tracking=nil )
|
349
|
+
if branches.include?( branch )
|
350
|
+
"checkout #{branch}"
|
351
|
+
else
|
352
|
+
"checkout -b #{branch} #{tracking}"
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
def cmd_redirect_suffix( opts )
|
357
|
+
if !opts[:show] && !display_git_output?
|
358
|
+
"> /dev/null 2> /dev/null"
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
def git( cmds=[], opts={} )
|
363
|
+
cmds = [cmds] if cmds.is_a? String
|
364
|
+
redir = cmd_redirect_suffix( opts )
|
365
|
+
system cmds.map{|c| "git #{c} #{redir}"}.join( " && " )
|
366
|
+
end
|
367
|
+
|
368
|
+
def capture_git( cmds=[] )
|
369
|
+
cmds = [cmds] if cmds.is_a? String
|
370
|
+
redir = "2> /dev/null" unless display_git_output?
|
371
|
+
`#{cmds.map{|c| "git #{c} #{redir}"}.join( " && " )}`
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
data/lib/util.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/inflector'
|
5
|
+
require 'active_support/core_ext/module/aliasing'
|
6
|
+
require 'active_support/core_ext/string/inflections'
|
7
|
+
|
8
|
+
|
9
|
+
class String
|
10
|
+
def cleanup
|
11
|
+
indent = (index /^([ \t]+)/; $1) || ''
|
12
|
+
regex = /^#{Regexp::escape( indent )}/
|
13
|
+
strip.gsub regex, ''
|
14
|
+
end
|
15
|
+
|
16
|
+
def oneline
|
17
|
+
strip.gsub( /\n\s+/, '' )
|
18
|
+
end
|
19
|
+
|
20
|
+
# Annoyingly, the useful version of pluralize in texthelpers isn't in the
|
21
|
+
# string core extensions.
|
22
|
+
def pluralize_with_count( count )
|
23
|
+
count > 1 ? pluralize_without_count : singularize
|
24
|
+
end
|
25
|
+
alias_method_chain :pluralize, :count
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
class Object
|
30
|
+
# Deep duplicate via remarshaling. Not always applicable.
|
31
|
+
def ddup
|
32
|
+
Marshal.load( Marshal.dump( self ))
|
33
|
+
end
|
34
|
+
end
|