git-topic 0.1.6.4 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ git-topic
2
+ workflow
3
+ wip
4
+ namespace
5
+ Rebase
6
+ TODOs
7
+ autocomplete
8
+ autocompletion
9
+ dir
Binary file
data/README.rdoc CHANGED
@@ -1,11 +1,21 @@
1
- = git-topic
1
+ = git-topic
2
2
 
3
3
  git-topic is a git command to help with a particular kind of workflow, in which
4
4
  all work is written by an author, and reviewed by someone else.
5
5
 
6
- == Workflow in brief
7
6
 
8
- === Doing Work
7
+ = Requirements
8
+
9
+ * git-topic has only been tested on debian flavours of linux. Other unix-like
10
+ environments should work.
11
+ * git >= 1.7.1 (older versions may work, except for comment and comments
12
+ commands)
13
+ * ruby >= 1.9.2-rc1 (older versions may work)
14
+
15
+
16
+ = Workflow
17
+ == Workflow in brief
18
+ === Doing Work
9
19
 
10
20
  1. Create a topic branch in the wip namespace.
11
21
  2. Do some work.
@@ -17,7 +27,7 @@ all work is written by an author, and reviewed by someone else.
17
27
  2. Do some work, resolve the reviewer's issues.
18
28
  3. go to step 3, above.
19
29
 
20
- === Reviewing
30
+ === Reviewing
21
31
 
22
32
  1. Create a local branch from a review branch somebody else pushed.
23
33
  2. Review their work.
@@ -27,9 +37,8 @@ all work is written by an author, and reviewed by someone else.
27
37
  the review namespace.
28
38
 
29
39
 
30
- == Workflow without git-topic
31
-
32
- === Working on a topic (e.g. feature, or bugfix) foo
40
+ == Workflow without git-topic
41
+ === Working on a topic (e.g. feature, or bugfix) foo
33
42
 
34
43
  # Create a new branch and switch to it.
35
44
  git checkout -b wip/hal/foo
@@ -60,7 +69,7 @@ all work is written by an author, and reviewed by someone else.
60
69
 
61
70
 
62
71
 
63
- === Reviewing a topic (no proposed changes)
72
+ === Reviewing a topic (no proposed changes)
64
73
 
65
74
  # update origin
66
75
  git fetch origin
@@ -86,19 +95,19 @@ all work is written by an author, and reviewed by someone else.
86
95
 
87
96
 
88
97
 
89
- === Reviewing a topc (proposed changes)
98
+ === Reviewing a topic (proposed changes)
90
99
 
91
100
  As above, but if, after examining the code you feel some changes are
92
101
  appropriate, you can either:
93
102
 
94
- ==== do them yourself and have the other person review and merge
103
+ ==== do them yourself and have the other person review and merge
95
104
 
96
105
  # do some changes, then destroy the old review branch and make a new one
97
106
  # with the cleaned up code. Now the owner is changed from djh to hal, so
98
107
  # djh must review and merge into master
99
108
  git push origin review/djh/bar:review/hal/bar :review/djh/bar
100
109
 
101
- ==== do some (or no) changes, but with some TODOs for the original author.
110
+ ==== do some (or no) changes, but with some TODOs for the original author.
102
111
 
103
112
  # do your changes on review/djh/bar
104
113
 
@@ -110,9 +119,8 @@ appropriate, you can either:
110
119
  # review/djh/bar
111
120
 
112
121
 
113
- == Workflow with git-topic
114
-
115
- === Doing Work
122
+ == Workflow with git-topic
123
+ === Doing Work
116
124
 
117
125
  # Work on a (possibly new, possibly rejected) topic
118
126
  git topic work-on <topic>
@@ -121,7 +129,7 @@ appropriate, you can either:
121
129
  git topic done [<topic>]
122
130
 
123
131
 
124
- === Reviewing
132
+ === Reviewing
125
133
 
126
134
  # see if we have any topics to review (or rejected topics to fix up)
127
135
  git topic status
@@ -131,10 +139,18 @@ appropriate, you can either:
131
139
  # happy with the review, get it into master
132
140
  git topic accept
133
141
 
134
- # unhappy with the review, push it to rejected
142
+ # unhappy with the review
143
+ # edit files to add file-specific comments (see git-topic comment --help for
144
+ # details).
145
+
146
+ # save your file specific comments, and launch an editor to enter general
147
+ # comments about the topic.
148
+ git topic comment
149
+
150
+ # push the topic to rejected.
135
151
  git topic reject
136
152
 
137
- == Again, but with aliases
153
+ == Again, but with aliases
138
154
 
139
155
  # first install aliases
140
156
  # add --local if you don't want to update your global aliases
@@ -144,6 +160,10 @@ appropriate, you can either:
144
160
  # git w <topic>
145
161
  git work-on <topic>
146
162
 
163
+ # see reviewer's comments
164
+ git comments
165
+
166
+ # finished, submit work
147
167
  git done
148
168
 
149
169
 
@@ -157,22 +177,32 @@ appropriate, you can either:
157
177
 
158
178
  git accept
159
179
  # or
180
+ git comment
160
181
  git reject
161
182
 
162
- == Misc
183
+ == Bash autocompletion
184
+
185
+ See git-topic --completion-help for details. In short, you have to do some
186
+ manual work, because loading completions as a gem is too slow (see Misc, below,
187
+ and ruby issue 3465[1]).
188
+
189
+ 1. Make sure you source share/completion.bash __before__ sourcing git's standard
190
+ completion.
191
+
192
+ 2. Copy bin/git-topic-completion to your gem env's default bin dir, overriding
193
+ the generated git-topic-completion. Otherwise, autocompletion will be too
194
+ slow to be useful.
195
+
196
+ == Misc
163
197
 
164
198
  At present the binary is stupidly slow. Actually, the binary is not slow, but
165
199
  all rubygems binaries are slow. See ruby issue 3465[1]. One way around this is
166
- to modify yor PATH so you invoke the binary directly (instead of the rubygem
200
+ to modify your PATH so you invoke the binary directly (instead of the rubygem
167
201
  wrapper). Alternatively, put up with a 300ms load time until you have a ruby
168
202
  with the issue fixed.
169
203
 
170
- == Obvious Features Missing
171
-
172
- 1. shell autocomplete
173
-
174
204
 
175
- == Note on Patches/Pull Requests
205
+ == Note on Patches/Pull Requests
176
206
 
177
207
  1. Fork the project.
178
208
  2. Make your feature addition or bug fix.
@@ -183,10 +213,10 @@ with the issue fixed.
183
213
  can ignore when I pull)
184
214
  5. Send me a pull request. Bonus points for topic branches.
185
215
 
186
- == Copyright
216
+ == Copyright
187
217
 
188
- Copyright (c) 2010 David J. Hamilton. See LICENSE for details.
218
+ Copyright © 2010 David J. Hamilton. See LICENSE for details.
189
219
 
190
- == References
220
+ == References
191
221
 
192
222
  1. http://redmine.ruby-lang.org/issues/show/3465
data/Rakefile CHANGED
@@ -33,11 +33,21 @@ begin
33
33
  git work-on <topic>
34
34
 
35
35
  see README.rdoc for more (any) details.
36
+
37
+
38
+ To make use of bash autocompletion, you must do the following:
39
+
40
+ 1. Make sure you source share/completion.bash before you source git's completion.
41
+ 2. Optionally, copy git-topic-completion to your gem's bin directory.
42
+ This is to sidestep ruby issue 3465 which makes loading gems far too
43
+ slow for autocompletion.
36
44
  }
37
45
  gem.email = "git-topic@hjdivad.com"
38
46
  gem.homepage = "http://github.com/hjdivad/git-topic"
39
47
  gem.authors = ["David J. Hamilton"]
40
48
 
49
+ gem.files.exclude 'git-topic'
50
+
41
51
  if File.exists? 'Gemfile'
42
52
  require 'bundler'
43
53
  bundler = Bundler.load
data/VERSION.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
2
  :major: 0
3
- :minor: 1
4
- :patch: 6
5
- :build: "4"
3
+ :minor: 2
4
+ :patch: 1
5
+ :build:
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/ruby -w
2
+
3
+
4
+ module GitTopic; end
5
+
6
+ module GitTopic::Completion
7
+ class << self
8
+
9
+ def user
10
+ ENV['USER']
11
+ end
12
+
13
+ def branches
14
+ `git branch -a --no-color`.split( "\n" ).map do |l|
15
+ l.gsub( /->.*$/, '' )[ 2..-1 ]
16
+ end
17
+ end
18
+
19
+ def others_review_branches
20
+ branches.map do |b|
21
+ b =~ %r{^remotes/origin/review/(.*)} && $1
22
+ end.reject do |b|
23
+ b =~ %r{^#{user}/}
24
+ end.compact
25
+ end
26
+
27
+ def my_reject_review_and_local_wip_branches
28
+ branches.map do |b|
29
+ b =~ %r{^(wip)/#{user}/(.*)} ||
30
+ b =~ %r{^remotes/origin/(rejected)/#{user}/(.*)} ||
31
+ b =~ %r{^remotes/origin/(review)/#{user}/(.*)}
32
+ suggestion = [$1,$2].compact.join("/")
33
+ suggestion unless suggestion.strip.empty?
34
+ end.compact
35
+ end
36
+
37
+ def complete
38
+ suggestions =
39
+ case ARGV.shift
40
+ # TODO 2: if we let accept/reject take args, simply return
41
+ # others_review_branches here
42
+ when "accept"
43
+ when "reject"
44
+ when "done"
45
+ # nothing
46
+ when "work-on"
47
+ my_reject_review_and_local_wip_branches
48
+ when "review"
49
+ others_review_branches
50
+ end || []
51
+
52
+ suggestions.each{ |s| puts s }
53
+ end
54
+
55
+ end
56
+ end
57
+
58
+ GitTopic::Completion.complete
data/lib/core_ext.rb CHANGED
@@ -7,22 +7,84 @@ require 'active_support/core_ext/string/inflections'
7
7
 
8
8
 
9
9
  class String
10
- def cleanup
11
- indent = (index /^([ \t]+)/; $1) || ''
12
- regex = /^#{Regexp::escape( indent )}/
13
- strip.gsub regex, ''
14
- end
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
15
26
 
16
- def oneline
17
- strip.gsub( /\n\s+/, '' )
27
+ def wrap( width, heading_indentation=nil, heading=nil )
28
+ # TODO 2: if indentation is nil, compute indentation
29
+ heading_indentation ||= 0
30
+ heading ||= ""
31
+
32
+ indentation = heading.size + heading_indentation
33
+ indent = ' ' * indentation
34
+ sbuf = "#{indent}"
35
+ line_w = indentation
36
+
37
+ original_indentation = nil
38
+
39
+ new_line = lambda do
40
+ sbuf << "\n#{indent}"
41
+ line_w = indentation
18
42
  end
19
43
 
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
44
+ start_of_line = lambda { line_w == indentation }
45
+
46
+ split( "\n" ).each do |line|
47
+ if original_indentation.nil?
48
+ line =~ /^(\s*)/
49
+ original_indentation = $1.size
50
+ end
51
+
52
+ if line =~ /^\s{#{original_indentation}}\s/
53
+ sbuf << "\n#{indent}" unless start_of_line.call
54
+ sbuf << line.slice( original_indentation, line.size )
55
+ new_line.call
56
+ next
57
+ end
58
+
59
+ leading_space =
60
+ start_of_line.call ? '' : ' '
61
+ line.gsub!( %r/^\s{,#{original_indentation}}/, leading_space )
62
+ line.split( /(\s+)/ ).each do |word|
63
+ next if start_of_line.call && word.strip.empty?
64
+
65
+ if line_w + word.size <= width
66
+ sbuf << word
67
+ line_w += word.size
68
+ elsif start_of_line.call
69
+ sbuf << word[0...(size - indentation)]
70
+ new_line.call
71
+ word.slice! (size - indentation), word.size
72
+ else
73
+ new_line.call
74
+ redo
75
+ end
76
+ end
24
77
  end
25
- alias_method_chain :pluralize, :count
78
+
79
+ sbuf[ 0...heading.size ] = heading
80
+
81
+ sbuf
82
+ end
83
+
84
+ def wrap!( width, heading_indentation=nil, heading=nil )
85
+ self.replace wrap( width, heading_indentation, heading )
86
+ end
87
+
26
88
  end
27
89
 
28
90
 
data/lib/git_topic.rb CHANGED
@@ -7,13 +7,18 @@ require 'active_support/core_ext/hash/keys'
7
7
  require 'core_ext'
8
8
  require 'git_topic/git'
9
9
  require 'git_topic/naming'
10
+ require 'git_topic/comment'
10
11
 
11
12
 
12
13
  module GitTopic
13
14
  include GitTopic::Git
14
15
  include GitTopic::Naming
16
+ include GitTopic::Comment
15
17
 
16
- GlobalOptKeys = [ :verbose, :help, :verbose_given, :version ]
18
+ GlobalOptKeys = [
19
+ :verbose, :help, :verbose_given, :version, :completion_help,
20
+ :completion_help_given
21
+ ]
17
22
 
18
23
 
19
24
  class << self
@@ -25,7 +30,7 @@ module GitTopic
25
30
  # setup a remote branch, if necessary
26
31
  wb = wip_branch( topic )
27
32
  git(
28
- "push origin HEAD:#{wb}"
33
+ "push origin HEAD:refs/heads/#{wb}"
29
34
  ) unless remote_branches.include? "origin/#{wb}"
30
35
  # switch to the new branch
31
36
  git [ switch_to_branch( wb, "origin/#{wb}" )]
@@ -35,11 +40,22 @@ module GitTopic
35
40
  if remote_branches.include? "origin/#{rej_branch}"
36
41
  git [
37
42
  "reset --hard origin/#{rej_branch}",
38
- "push origin :#{rej_branch} HEAD:#{wb}",
43
+ "push origin :refs/heads/#{rej_branch} HEAD:refs/heads/#{wb}",
39
44
  ]
40
45
  end
46
+
47
+ # Reset upstream, if specified
48
+ if opts[:upstream]
49
+ git "reset --hard #{opts[:upstream]}"
50
+ end
51
+
52
+ report "Switching branches to work on #{topic}."
53
+ if existing_comments?
54
+ report "You have reviewer comments on this topic."
55
+ end
41
56
  end
42
57
 
58
+
43
59
  # Done with the given topic. If none is specified, then topic is assumed to
44
60
  # be the current branch (if it's a topic branch).
45
61
  def done( topic=nil, opts={} )
@@ -61,12 +77,15 @@ module GitTopic
61
77
  wb = wip_branch( topic )
62
78
  rb = review_branch( topic )
63
79
  git [
64
- "push origin #{wb}:#{rb} :#{wb}",
80
+ "push origin refs/heads/#{wb}:refs/heads/#{rb} :refs/heads/#{wb}",
65
81
  ("checkout master" if strip_namespace( topic ) == current_topic),
66
82
  "branch -D #{wip_branch( topic )}"
67
83
  ].compact
84
+
85
+ report "Completed topic #{topic}. It has been pushed for review."
68
86
  end
69
87
 
88
+
70
89
  # Produce status like
71
90
  #
72
91
  # # There are 2 topics you can review.
@@ -118,12 +137,13 @@ module GitTopic
118
137
  end
119
138
  end
120
139
 
140
+
121
141
  # Switch to a review branch to check somebody else's code.
122
- def review( spec=nil, opts={} )
142
+ def review( ref=nil, opts={} )
123
143
  rb = remote_branches_organized
124
144
  review_branches = rb[:review]
125
145
 
126
- if spec.nil?
146
+ if ref.nil?
127
147
  # select the oldest (by HEAD) topic, if any exist
128
148
  if review_branches.empty?
129
149
  puts "nothing to review."
@@ -132,12 +152,12 @@ module GitTopic
132
152
  user, topic = oldest_review_user_topic
133
153
  end
134
154
  else
135
- p = topic_parts( spec )
155
+ p = topic_parts( ref )
136
156
  user, topic = p[:user], p[:topic]
137
157
  end
138
158
 
139
159
  if remote_topic_branch = find_remote_review_branch( topic )
140
- # Get the actual user/topic, e.g. to get the user if spec only specifies
160
+ # Get the actual user/topic, e.g. to get the user if ref only specifies
141
161
  # the topic.
142
162
  real_user, real_topic = user_topic_name( remote_topic_branch )
143
163
  git [
@@ -145,13 +165,17 @@ module GitTopic
145
165
  review_branch( real_topic, real_user ),
146
166
  remote_topic_branch )]
147
167
  else
148
- raise "No review topic found matching ‘#{spec}’"
168
+ raise "No review topic found matching ‘#{ref}’"
149
169
  end
170
+
171
+ report "Reviewing topic #{user}/#{topic}."
150
172
  end
151
173
 
174
+
152
175
  # Accept the branch currently being reviewed.
153
176
  def accept( topic=nil, opts={} )
154
177
  raise "Must be on a review branch." unless on_review_branch?
178
+ raise "Working tree must be clean" unless working_tree_clean?
155
179
 
156
180
  # switch to master
157
181
  # merge review branch, assuming FF
@@ -175,14 +199,95 @@ module GitTopic
175
199
 
176
200
  rem_review_branch = find_remote_review_branch( topic ).gsub( %r{^origin/}, '' )
177
201
  git [
178
- "push origin master :#{rem_review_branch}",
202
+ "push origin master :refs/heads/#{rem_review_branch}",
179
203
  "branch -d #{local_review_branch}"
180
204
  ]
205
+
206
+ report "Accepted topic #{user}/#{topic}."
207
+ end
208
+
209
+
210
+ def comment( opts={} )
211
+ diff_legal =
212
+ git( "diff --diff-filter=ACDRTUXB --quiet" ) &&
213
+ git( "diff --cached --diff-filter=ACDRTUXB --quiet" ) &&
214
+ $?.success?
215
+
216
+ raise "
217
+ Diffs are not comments. Files have been added, deleted or had their
218
+ modes changed. See
219
+ git diff --diff-filter=ACDRTUXB
220
+ for a list of changes preventing git-topic comment from saving your
221
+ comments.
222
+ ".cleanup unless diff_legal
223
+
224
+
225
+ diff_empty = git( "diff --diff-filter=M --quiet" ) && $?.success?
226
+
227
+ case current_namespace
228
+ when "wip"
229
+ if existing_comments?
230
+ raise "
231
+ diff → comments not allowed when replying. Please make sure your
232
+ working tree is completely clean and then invoke git-topic comment
233
+ again.
234
+ ".oneline unless diff_empty
235
+
236
+ notes_from_reply_to_comments
237
+ else
238
+ puts "No comments to reply to. See git-topic comment --help for usage."
239
+ return
240
+ end
241
+ when "review"
242
+ if existing_comments?
243
+ if opts[ :force_update ]
244
+ notes_from_initial_comments( "edit" )
245
+ else
246
+ raise "
247
+ diff → comments not allowed when replying. Please make sure your
248
+ working tree is completely clean and then invoke git-topic comment
249
+ again.
250
+ ".oneline unless diff_empty
251
+
252
+ notes_from_reply_to_comments
253
+ end
254
+ else
255
+ notes_from_initial_comments
256
+ end
257
+ else
258
+ raise "Inappropriate namespace for comments: [#{namespace}]"
259
+ end
260
+
261
+ report "Your comments have been saved."
262
+ end
263
+
264
+ def comments( opts={} )
265
+ unless existing_comments?
266
+ puts "There are no comments on this branch."
267
+ return
268
+ end
269
+
270
+ git "log origin/master.. --show-notes=#{notes_ref} --no-standard-notes"
181
271
  end
182
272
 
273
+
183
274
  # Reject the branch currently being reviewed.
184
- def reject( topic=nil, opts={} )
275
+ def reject( topic_or_opts=nil, opts={} )
276
+ if topic_or_opts.is_a? Hash
277
+ topic = nil
278
+ opts = topic_or_opts
279
+ else
280
+ topic = topic_or_opts
281
+ end
282
+
185
283
  raise "Must be on a review branch." unless on_review_branch?
284
+ unless working_tree_clean?
285
+ if opts[:save_comments]
286
+ comment
287
+ else
288
+ raise "Working tree must be clean without --save-comments."
289
+ end
290
+ end
186
291
 
187
292
  # switch to master
188
293
  # push to rejected, destroy remote
@@ -191,13 +296,21 @@ module GitTopic
191
296
 
192
297
  rem_review_branch = find_remote_review_branch( topic ).gsub( %r{^origin/}, '' )
193
298
  rem_rej_branch = remote_rejected_branch( topic, user )
299
+
300
+ refspecs = [
301
+ "refs/heads/#{current_branch}:refs/heads/#{rem_rej_branch}",
302
+ ":refs/heads/#{rem_review_branch}",
303
+ ].join( " " )
194
304
  git [
195
305
  "checkout master",
196
- "push origin #{current_branch}:#{rem_rej_branch} :#{rem_review_branch}",
306
+ "push origin #{refspecs}",
197
307
  "branch -D #{current_branch}"
198
308
  ]
309
+
310
+ report "Rejected topic #{user}/#{topic}"
199
311
  end
200
312
 
313
+
201
314
  def install_aliases( opts={} )
202
315
  opts.assert_valid_keys :local, :local_given, *GlobalOptKeys
203
316
 
@@ -209,11 +322,31 @@ module GitTopic
209
322
  "config #{flags} alias.review 'topic review'",
210
323
  "config #{flags} alias.accept 'topic accept'",
211
324
  "config #{flags} alias.reject 'topic reject'",
325
+ "config #{flags} alias.comment 'topic comment'",
326
+ "config #{flags} alias.comments 'topic comments'",
212
327
 
213
328
  "config #{flags} alias.w 'topic work-on'",
214
329
  "config #{flags} alias.r 'topic review'",
215
330
  "config #{flags} alias.st 'topic status --prepended'",
216
331
  ]
332
+
333
+ report "Aliases installed Successfully.",
334
+ "
335
+ Error installing aliases. re-run with --verbose flag for
336
+ details.
337
+ ".oneline
338
+ end
339
+
340
+
341
+ protected
342
+
343
+ def report( success_msg, error_msg=nil )
344
+ if $?.success?
345
+ puts success_msg
346
+ else
347
+ error_msg ||= "Error running command. re-run with --verbose for details"
348
+ raise error_msg
349
+ end
217
350
  end
218
351
 
219
352
  end