git-scripts 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -0
  3. data/bin/feature +81 -32
  4. data/bin/hotfix +40 -4
  5. data/completion/_feature +2 -1
  6. data/completion/bash_completion.sh +2 -2
  7. data/lib/github.rb +32 -6
  8. data/lib/helpers.rb +17 -6
  9. data/lib/plugins.rb +25 -0
  10. data/man/feature-finish-issue.1 +2 -2
  11. data/man/feature-finish-issue.1.ronn +1 -1
  12. data/man/feature-finish.1 +1 -1
  13. data/man/feature-switch.1 +12 -2
  14. data/man/feature-switch.1.ronn +5 -2
  15. data/man/feature-url.1 +22 -0
  16. data/man/feature-url.1.ronn +23 -0
  17. data/man/feature.1 +9 -1
  18. data/man/feature.1.ronn +4 -0
  19. data/man/hotfix-finish-issue.1 +2 -2
  20. data/man/hotfix-finish-issue.1.ronn +1 -1
  21. data/man/hotfix-finish.1 +1 -1
  22. data/man/hotfix-switch.1 +17 -1
  23. data/man/hotfix-switch.1.ronn +10 -2
  24. data/man/hotfix-url.1 +22 -0
  25. data/man/hotfix-url.1.ronn +23 -0
  26. data/man/hotfix.1 +9 -1
  27. data/man/hotfix.1.ronn +4 -0
  28. metadata +45 -78
  29. data/man/feature-clean.1.html +0 -120
  30. data/man/feature-clean.1.markdown +0 -61
  31. data/man/feature-finish-issue.1.html +0 -113
  32. data/man/feature-finish-issue.1.markdown +0 -56
  33. data/man/feature-finish.1.html +0 -113
  34. data/man/feature-finish.1.markdown +0 -56
  35. data/man/feature-github-test.1.html +0 -110
  36. data/man/feature-github-test.1.markdown +0 -53
  37. data/man/feature-list.1.html +0 -119
  38. data/man/feature-list.1.markdown +0 -60
  39. data/man/feature-merge.1.html +0 -116
  40. data/man/feature-merge.1.markdown +0 -59
  41. data/man/feature-prune.1.html +0 -116
  42. data/man/feature-prune.1.markdown +0 -60
  43. data/man/feature-start.1.html +0 -110
  44. data/man/feature-start.1.markdown +0 -53
  45. data/man/feature-stashes.1.html +0 -122
  46. data/man/feature-stashes.1.markdown +0 -63
  47. data/man/feature-status.1.html +0 -113
  48. data/man/feature-status.1.markdown +0 -56
  49. data/man/feature-switch.1.html +0 -120
  50. data/man/feature-switch.1.markdown +0 -61
  51. data/man/feature.1.html +0 -129
  52. data/man/feature.1.markdown +0 -80
  53. data/man/hotfix-finish-issue.1.html +0 -113
  54. data/man/hotfix-finish-issue.1.markdown +0 -56
  55. data/man/hotfix-finish.1.html +0 -112
  56. data/man/hotfix-finish.1.markdown +0 -55
  57. data/man/hotfix-list.1.html +0 -119
  58. data/man/hotfix-list.1.markdown +0 -60
  59. data/man/hotfix-merge.1.html +0 -116
  60. data/man/hotfix-merge.1.markdown +0 -59
  61. data/man/hotfix-start.1.html +0 -110
  62. data/man/hotfix-start.1.markdown +0 -53
  63. data/man/hotfix-switch.1.html +0 -112
  64. data/man/hotfix-switch.1.markdown +0 -55
  65. data/man/hotfix.1.html +0 -123
  66. data/man/hotfix.1.markdown +0 -68
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9a2d6d6518dbc7ecc8f65e1d05f2d726198bbd74
4
- data.tar.gz: 676e53c8cc96c98d13745280205c74108d7aa281
3
+ metadata.gz: af70a37bea46ed2bb2c96555972ca8a4be70a0a2
4
+ data.tar.gz: c9398a71ddcfb1b623c12fbf08db5ffabb465fa1
5
5
  SHA512:
6
- metadata.gz: 4d8177e27268f8eb876a17936a196364c7bb633c2ded649e4a715fa22fd6a8d8f6895e7dbe8d57122f03bab8d1ff75291bb46744b0ede02568fa1766c80efeab
7
- data.tar.gz: 05cb8fd81e03130a108f45be0d1802cbc5d1bf2ef5faf2250958f2714a87c69a8f3f8c05838cbef976fc727c6cd2b5d9615394cd99f6ad4ace0bc6b9ade6c5d9
6
+ metadata.gz: b3a9bc18372e11269d784c3f60ddced3853e286ee4bf13ae4586f08ce17831098f5f00b6039f8b94294871c89fcab912991f462cd76126bd4da1e2532f59feb8
7
+ data.tar.gz: fb056a372806a58a188d6e6418486d71903f3c3d9372dbcf30109c9741f32ee8ce08b9e45c085b38f8c94c88fff0ce374360c3c302197d67c5d11a6289830c19
data/README.md CHANGED
@@ -124,3 +124,11 @@ Merges the hotfix branch back into `stable` with `--no-ff`. Also does a
124
124
  merge back into `master`.
125
125
 
126
126
  [gitflow]: http://nvie.com/posts/a-successful-git-branching-model/
127
+
128
+ ## Plugins
129
+
130
+ Any files matching `plugins/*.rb` will be loaded as plugins. The plugin
131
+ architecture is *very* simple. There are a few hooks scattered over the
132
+ code-base of the form: `Plugins.invoke :before_start, :feature`. If a plugin
133
+ has a method of the same name `before_start` it will be called, passing along
134
+ any arguments passed to `Plugins.invoke`.
@@ -3,6 +3,7 @@ require_relative '../lib/signal_handlers.rb'
3
3
  require_relative '../lib/github.rb'
4
4
  require_relative '../lib/git.rb'
5
5
  require_relative '../lib/helpers.rb'
6
+ require_relative '../lib/plugins.rb'
6
7
 
7
8
  command=ARGV.first
8
9
 
@@ -16,21 +17,22 @@ end
16
17
 
17
18
  case command
18
19
  when 'github-test'
19
- octokit = Github::api
20
20
  # Should succeed if authentication is setup.
21
- octokit.pulls(Github::get_github_repo)
21
+ Github.pulls
22
22
  puts "[Successfully Authenticated]"
23
23
 
24
24
  when 'start'
25
25
  require_argument(:feature, :start)
26
26
  feature = ARGV[1]
27
27
 
28
- exit unless confirm("Create feature branch named: '#{feature}' ?")
29
28
  development_branch = Git::get_branch('development')
30
29
 
30
+ Plugins.invoke :before_start, :feature, feature
31
+
31
32
  Git::run_safe([
32
33
  "git checkout #{development_branch}",
33
- "git pull --rebase",
34
+ "git fetch",
35
+ "git rebase --preserve-merges origin/#{development_branch}",
34
36
  "git branch \"#{feature}\" #{development_branch}",
35
37
  "git checkout \"#{feature}\"",
36
38
  ])
@@ -83,12 +85,19 @@ when 'status'
83
85
 
84
86
  when 'finish'
85
87
  feature = ARGV[1] || Git::current_branch
88
+
89
+ if is_hotfix_branch feature
90
+ abort "This is a hotfix branch. Please use hotfix-finish."
91
+ end
92
+
86
93
  # Push commits to origin
87
94
  Git::run_safe(["git push origin #{feature}:#{feature}"])
88
95
 
89
96
  exit 1 unless confirm("Create a pull-request for feature branch named: '#{feature}' ?")
90
97
  octokit = Github::api
91
98
 
99
+ Plugins.invoke :before_finish, :feature, feature
100
+
92
101
  description = Github::get_pull_request_description(feature)
93
102
  puts "Pull-request description:"
94
103
  puts description[:title]
@@ -103,6 +112,7 @@ when 'finish'
103
112
  description[:body]
104
113
  )
105
114
 
115
+ Plugins.invoke :after_finish, :feature, feature, response
106
116
  puts "Successfully created pull-request ##{response[:number]}"
107
117
  puts " " + response[:html_url]
108
118
 
@@ -110,6 +120,11 @@ when 'finish-issue'
110
120
  require_argument(:feature, :'finish-issue')
111
121
  issue = ARGV[1]
112
122
  feature = Git::current_branch
123
+
124
+ if is_hotfix_branch feature
125
+ abort "This is a hotfix branch. Please use hotfix-finish-issue."
126
+ end
127
+
113
128
  # Push commits to origin
114
129
  Git::run_safe(["git push origin #{feature}:#{feature}"])
115
130
 
@@ -166,6 +181,9 @@ when 'merge'
166
181
  feature = ARGV[1] || Git::current_branch
167
182
  end
168
183
 
184
+ if is_hotfix_branch feature
185
+ exit 1 unless confirm("It looks like this is a hotfix branch. Are you sure you want to merge it into only #{dev_branch}?")
186
+ end
169
187
 
170
188
  pull_info = Github::get_pull_request_info_from_api(feature, dev_branch)
171
189
 
@@ -174,6 +192,8 @@ when 'merge'
174
192
  puts highlight(warning)
175
193
  end
176
194
 
195
+ Plugins.invoke :before_merge, :feature, feature
196
+
177
197
  exit 1 unless confirm("Merge feature branch named: '#{feature}' ?")
178
198
 
179
199
  update = Git::submodules_update("get")
@@ -195,6 +215,8 @@ when 'merge'
195
215
  "git branch -d \"#{feature}\""
196
216
  ])
197
217
 
218
+ Plugins.invoke :after_merge, :feature, feature
219
+
198
220
  puts
199
221
  puts "Successfully merged feature-branch: #{feature} into #{dev_branch}"
200
222
  puts "If you are satisfied with the result, do this:\n" + <<CMDS
@@ -203,7 +225,7 @@ CMDS
203
225
 
204
226
  when 'switch'
205
227
 
206
- require_argument(:feature, :switch, min=2, max=4)
228
+ require_argument(:feature, :switch, min=2, max=5)
207
229
 
208
230
  feature = ARGV[1]
209
231
 
@@ -213,6 +235,20 @@ when 'switch'
213
235
 
214
236
  Git::switch_branch(feature)
215
237
 
238
+ optional_pull
239
+
240
+ when 'url'
241
+ require_argument(:feature, :url, min=1, max=2)
242
+ feature = ARGV[1] || Git::current_branch
243
+
244
+ url = Github::get_url(feature)
245
+ if url
246
+ puts url
247
+ else
248
+ abort "There is no pull request available for #{feature}.\n" +
249
+ 'You can make one by using `feature finish`.'
250
+ end
251
+
216
252
  when 'prune'
217
253
  require_argument(:feature, :prune, min=3, max=3)
218
254
  location = ARGV[1]
@@ -227,52 +263,65 @@ when 'prune'
227
263
  abort("Valid options: preview, clean.")
228
264
  end
229
265
 
230
- currentBranch = Git::current_branch()
266
+ current_branch = Git::current_branch()
231
267
  development_branch = Git::get_branch('development')
232
268
  stable_branch = Git::get_branch('stable')
233
269
 
234
- def branches_cmd(location, currentBranch)
235
- branch = location == 'origin' ? "origin/#{development_branch}" : development_branch
236
- return "git branch --merged #{branch} |
237
- grep -v #{development_branch} |
238
- grep -v #{stable_branch} |
239
- grep -v #{currentBranch}"
270
+ # Returns a shell command that will output a list of branches that can be
271
+ # safely pruned (are merged into the development_branch).
272
+ # Note: if location != 'local' this will return remote branches
273
+ branches_cmd = lambda do
274
+ if location == 'local'
275
+ branch = development_branch
276
+ options = ''
277
+ prefix=''
278
+ else
279
+ branch = "#{location}/#{development_branch}"
280
+ options = '--remotes'
281
+ prefix='origin/'
282
+ end
283
+ return "git branch #{options} --merged #{esc branch} |
284
+ sed 's/^* //g' | sed 's/^\s*//' |
285
+ grep --invert-match --line-regexp \
286
+ --regexp=#{esc (prefix + development_branch)} \
287
+ --regexp=#{esc (prefix + stable_branch)} \
288
+ --regexp=#{esc (prefix + current_branch)}"
240
289
  end
241
290
 
242
- def preview(location, currentBranch)
291
+ preview = lambda do
243
292
  puts "Would delete the following..."
244
- commands = branches_cmd(location, currentBranch)
245
293
 
246
- if location == "local"
247
- system(commands)
248
- elsif location == "origin"
249
- message = `git remote prune -n origin && #{commands}`
250
- puts message.gsub(" * [would prune] origin/",'')
294
+ system(branches_cmd.call)
295
+
296
+ if location != "local"
297
+ system("git remote prune --dry-run #{esc location}")
251
298
  end
252
299
  end
253
300
 
254
- def delete(location, currentBranch)
301
+ delete = lambda do |preview = true|
255
302
  puts "Deleting..."
256
- commands = branches_cmd(location, currentBranch)
257
303
 
258
304
  if location == "local"
259
- system("#{commands} | xargs git branch -d")
260
- elsif location == "origin"
261
- system("git remote prune origin &&
262
- git branch -r --merged origin/#{development_branch} |
263
- grep -v #{development_branch} |
264
- grep -v #{stable_branch} |
265
- grep -v #{currentBranch} |
266
- sed -n 's| origin/|:|p' |
267
- xargs git push origin")
305
+ action_command = "xargs git branch -d"
306
+ else
307
+ sed_str = "s|^#{location}/|:|p"
308
+ action_command = "sed -n #{esc sed_str} | xargs git push origin &&
309
+ git remote prune #{esc location}"
268
310
  end
311
+
312
+ if `#{branches_cmd.call}`.empty?
313
+ puts "No branches need to be pruned"
314
+ return
315
+ end
316
+
317
+ system("#{branches_cmd.call} | #{action_command}")
269
318
  end
270
319
 
271
320
  if option == 'preview'
272
- preview(location, currentBranch)
321
+ preview.call
273
322
  elsif option == 'clean'
274
323
  exit unless confirm("Are you sure you want to prune branches?")
275
- delete(location, currentBranch)
324
+ delete.call
276
325
  end
277
326
 
278
327
  when 'clean'
data/bin/hotfix CHANGED
@@ -3,6 +3,7 @@ require_relative '../lib/signal_handlers.rb'
3
3
  require_relative '../lib/github.rb'
4
4
  require_relative '../lib/git.rb'
5
5
  require_relative '../lib/helpers.rb'
6
+ require_relative '../lib/plugins.rb'
6
7
 
7
8
  command=ARGV.first
8
9
 
@@ -20,11 +21,12 @@ when 'start'
20
21
  hotfix = hotfix_branch(ARGV[1])
21
22
  stable_branch = Git::get_branch('stable')
22
23
 
23
- exit unless confirm("Create hotfix branch named: '#{hotfix}' ?")
24
+ Plugins.invoke :before_start, :hotfix, hotfix
24
25
 
25
26
  Git::run_safe([
26
27
  "git checkout #{stable_branch}",
27
- "git pull --rebase",
28
+ "git fetch",
29
+ "git rebase --preserve-merges origin/#{stable_branch}",
28
30
  "git branch \"#{hotfix}\" #{stable_branch}",
29
31
  "git checkout \"#{hotfix}\""
30
32
  ])
@@ -38,21 +40,42 @@ when 'start'
38
40
  "git config branch.#{hotfix}.rebase true"
39
41
  ])
40
42
 
43
+ when 'url'
44
+ require_argument(:hotfix, :url, min=1, max=2)
45
+ hotfix = ARGV[1] || Git::current_branch
46
+
47
+ url = Github::get_url(hotfix)
48
+ if url
49
+ puts url
50
+ else
51
+ abort "There is no pull request available for #{hotfix}.\n" +
52
+ 'You can make one by using `hotfix finish`.'
53
+ end
54
+
55
+
41
56
  when 'switch'
42
- require_argument(:hotfix, :switch, min=2, max=3)
57
+ require_argument(:hotfix, :switch, min=2, max=5)
43
58
  hotfix = current_hotfix_branch
44
59
 
45
60
  Git::switch_branch(hotfix)
46
61
 
62
+ optional_pull
63
+
47
64
  when 'finish'
48
65
  hotfix = current_hotfix_branch
49
66
 
67
+ if !is_hotfix_branch hotfix
68
+ abort "This is a feature branch. Please use feature-finish."
69
+ end
70
+
50
71
  # Push commits to origin
51
72
  Git::run_safe(["git push origin #{hotfix}:#{hotfix}"])
52
73
 
53
74
  exit 1 unless confirm("Create a pull-request for hotfix branch named: '#{hotfix}' ?")
54
75
  octokit = Github::api
55
76
 
77
+ Plugins.invoke :before_finish, :hotfix, hotfix
78
+
56
79
  description = Github::get_pull_request_description(hotfix)
57
80
  puts "Pull-request description:"
58
81
  puts description[:title]
@@ -67,6 +90,7 @@ when 'finish'
67
90
  description[:body]
68
91
  )
69
92
 
93
+ Plugins.invoke :after_finish, :hotfix, hotfix, response
70
94
  puts "Successfully created pull-request ##{response[:number]}"
71
95
  puts " " + response[:html_url]
72
96
 
@@ -75,6 +99,10 @@ when 'finish-issue'
75
99
  issue = ARGV[1]
76
100
  hotfix = Git::current_branch
77
101
 
102
+ if !is_hotfix_branch hotfix
103
+ abort "This is a feature branch. Please use feature-finish-issue."
104
+ end
105
+
78
106
  # Push commits to origin
79
107
  Git::run_safe(["git push origin #{hotfix}:#{hotfix}"])
80
108
 
@@ -127,6 +155,10 @@ when 'merge'
127
155
  stable_branch = Git::get_branch('stable')
128
156
  hotfix = current_hotfix_branch
129
157
 
158
+ if !is_hotfix_branch hotfix
159
+ exit 1 unless confirm("It looks like this is a feature branch. Are you sure you want to merge it into #{stable_branch}?")
160
+ end
161
+
130
162
  Git::run_safe(["git fetch"])
131
163
 
132
164
  pull_info = Github::get_pull_request_info_from_api(hotfix, stable_branch)
@@ -136,6 +168,8 @@ when 'merge'
136
168
  puts highlight(warning)
137
169
  end
138
170
 
171
+ Plugins.invoke :before_merge, :hotfix, hotfix
172
+
139
173
  exit 1 unless confirm("Merge hotfix named: '#{hotfix}' ?")
140
174
 
141
175
  commit_message = Git::get_description_from_user(pull_info[:description])
@@ -157,7 +191,7 @@ when 'merge'
157
191
  # Merge into master.
158
192
  "git checkout #{dev_branch}",
159
193
  # Pull the latest changes and rebase the unpushed master commits if any.
160
- "git rebase origin/#{dev_branch}",
194
+ "git rebase --preserve-merges origin/#{dev_branch}",
161
195
  # Merge the hotfix branch into master.
162
196
  "git merge --no-ff --no-edit -m #{commit_message_dev.shellescape} \"#{hotfix}\"",
163
197
  # Init any submodules in the master branch. Note: no need to change.
@@ -170,6 +204,8 @@ when 'merge'
170
204
  "git checkout #{stable_branch}"
171
205
  ])
172
206
 
207
+ Plugins.invoke :after_merge, :hotfix, hotfix
208
+
173
209
  puts "Successfully merged hotfix branch: #{hotfix} into #{stable_branch} and #{dev_branch}"
174
210
  puts "If you are satisfied with the result, do this:\n" + <<CMDS
175
211
  git push
@@ -11,7 +11,7 @@ _feature() {
11
11
 
12
12
  case $state in
13
13
  commands)
14
- _arguments '1:Commands:(list start switch finish finish-issue merge pull prune status stashes clean github-test)'
14
+ _arguments '1:Commands:(list start switch finish finish-issue merge pull prune status stashes clean github-test url)'
15
15
  ;;
16
16
  params)
17
17
  if [[ "$words[2]" == "prune" ]]; then
@@ -19,6 +19,7 @@ _feature() {
19
19
  fi
20
20
  if [[ "$words[2]" == "switch" ||
21
21
  "$words[2]" == "merge" ||
22
+ "$words[2]" == "url" ||
22
23
  "$words[2]" == "finish" ]]; then
23
24
  local -a featureBranches args
24
25
  featureBranches="$(git branch -a | tr -d ' *' | grep -v 'hotfix-' | sed 's|remotes/origin/||')"
@@ -10,7 +10,7 @@ _git-scripts()
10
10
  case "$cmd" in
11
11
  feature)
12
12
  if [ "$line" = "$cmd $cur" ]; then
13
- words="switch start finish finish-issue stashes list merge pull status clean prune"
13
+ words="switch start finish finish-issue stashes list merge pull status clean prune url"
14
14
  else
15
15
  # get branch names minus hotfixes
16
16
  words="$(git branch -a | tr -d ' *' | grep -v 'hotfix-' | sed 's|remotes/origin/||')"
@@ -18,7 +18,7 @@ _git-scripts()
18
18
  ;;
19
19
  hotfix)
20
20
  if [ "$line" = "$cmd $cur" ]; then
21
- words="switch start finish finish-issue merge list clean"
21
+ words="switch start finish finish-issue merge list clean url"
22
22
  else
23
23
  # get hotfix branch names
24
24
  words="$(git branch -a | tr -d ' *' | grep 'hotfix-' | sed -e 's|remotes/origin/||' -e 's|hotfix-||')"
@@ -34,6 +34,8 @@ module Github
34
34
  # }
35
35
  ##
36
36
  def self.api(authorization_info = {})
37
+ # Let Octokit handle pagination automagically for us.
38
+ Octokit.auto_traversal = true
37
39
  # Defaults
38
40
  authorization_info = {
39
41
  :scopes => ['repo'],
@@ -120,6 +122,13 @@ Body of pull-request
120
122
  return self::open_title_body_editor(initial_message)
121
123
  end
122
124
 
125
+ ##
126
+ # Returns the most recent github commit status for a given commit
127
+ ##
128
+ def self.get_most_recent_commit_status(repo, sha)
129
+ api.statuses(repo, sha).sort_by {|status| status['id'] }.last
130
+ end
131
+
123
132
  ##
124
133
  # Prompts the user (using $EDITOR) to confirm the title and body
125
134
  # in the provided message.
@@ -133,6 +142,8 @@ Body of pull-request
133
142
  msg.write(message)
134
143
  msg.close
135
144
 
145
+ Plugins.invoke :pre_message_edit, msg.path
146
+
136
147
  editor = Git::editor
137
148
  if (editor == 'vim')
138
149
  opts = "'+set ft=gitcommit' '+set textwidth=72'" +
@@ -162,17 +173,19 @@ Body of pull-request
162
173
  }
163
174
  end
164
175
 
176
+ # Returns a URL based off the branch name.
177
+ def self.get_url(branch_name)
178
+ pull = self.pull_for_branch(branch_name)
179
+ return pull && pull[:html_url]
180
+ end
181
+
165
182
  def self.get_pull_request_info_from_api(branch_name, into_branch)
166
- octokit = Github::api
167
- # Should succeed if authentication is set up.
168
- repo = Github::get_github_repo
169
- pulls = octokit.pulls(repo)
170
- pull = pulls.find {|pull| branch_name == pull[:head][:ref] }
183
+ pull = self.pull_for_branch(branch_name)
171
184
 
172
185
  if pull
173
186
  # This will grab the latest commit and retrieve the state from it.
174
187
  sha = pull[:head][:sha]
175
- state = octokit.statuses(repo, sha).shift
188
+ state = self.get_most_recent_commit_status(get_github_repo, sha)
176
189
  state = state ? state[:state] : 'none'
177
190
 
178
191
  desc = <<-MSG
@@ -189,6 +202,19 @@ Merge #{branch_name} (##{pull[:number]}) into #{into_branch}
189
202
  end
190
203
  end
191
204
 
205
+ @@pulls = nil
206
+ def self.pulls
207
+ if !@@pulls
208
+ repo = get_github_repo
209
+ @@pulls = api.pulls(repo)
210
+ end
211
+ return @@pulls
212
+ end
213
+
214
+ def self.pull_for_branch(branch_name)
215
+ pull = self.pulls.find {|pull| branch_name == pull[:head][:ref] }
216
+ end
217
+
192
218
  def self.get_commit_status_warning(status)
193
219
  warning = 'Merge with caution.'
194
220
  case status