git-whistles 1.2.1 → 1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +5 -3
- data/bin/git-jira-branch +1 -2
- data/bin/git-jira-pr +2 -154
- data/bin/git-pivotal-pr +2 -167
- data/bin/git-pr +18 -0
- data/git-whistles.gemspec +1 -1
- data/lib/git-whistles/jira.rb +3 -4
- data/lib/git-whistles/pull_request/bare.rb +110 -0
- data/lib/git-whistles/pull_request/jira.rb +77 -0
- data/lib/git-whistles/pull_request/pivotal.rb +78 -0
- data/lib/git-whistles/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '078014219cfdf3c6bae4aa0e471e60226f79063e'
|
4
|
+
data.tar.gz: 9418248182d6a8d514852de02f95c41f107544a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 15cf0189527e58fd684e4259884108514afc7ae24cc4468fe0fc0faf98466beee7d9c060650c4096c494fbb665e2152760978487bf44621467d93f9c1351239c
|
7
|
+
data.tar.gz: 1033a1b9b25b2c9601c0e0dd25f18c311f1f0b25cc46b3ff65e21c1c4c697a0b8cf4f24e81f88d1c6e872c529da34ef600b1c17e7a1afb1560c24c6c40398f9b
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -11,6 +11,8 @@ Install with:
|
|
11
11
|
|
12
12
|
`git chop [branch1, ...]` - Deletes the local and origin copy of a branch. Useful to close feature branches once a feature is completed. It also accepts multiple branches separated by spaces
|
13
13
|
|
14
|
+
`git pr [--from your-branch] [--to target-branch]` - Open your browser at a Github pull-request page for the specified branch (defaults to the current `head`).
|
15
|
+
|
14
16
|
`git explore [-r REF] [-p PATH]` - Opens the remote origin interface on the given reference and path. Reference defaults to current branch and path to root
|
15
17
|
|
16
18
|
`git ff-all-branches [-f] [-p] [-v]` - Fast-forward all local tracking branches to their remote counterpart (where possible). Very useful on big projects.
|
@@ -37,7 +39,7 @@ Install with:
|
|
37
39
|
|
38
40
|
`git stash-and-checkout [branch]` - Stash and checkout another branch.
|
39
41
|
|
40
|
-
`git youtrack-branch <ticket-id>` - Creates a branch name suggestion from the specified Youtrack ticket ID.
|
42
|
+
`git youtrack-branch <ticket-id>` - Creates a branch name suggestion from the specified Youtrack ticket ID.
|
41
43
|
|
42
44
|
### More details on some of the commands
|
43
45
|
|
@@ -45,7 +47,7 @@ Install with:
|
|
45
47
|
|
46
48
|
`git merge-po <ancestor> <left> <right>`
|
47
49
|
|
48
|
-
For those using `gettext` for I18n, a must-have: this custom merge driver
|
50
|
+
For those using `gettext` for I18n, a must-have: this custom merge driver
|
49
51
|
will handle most merge/conflicts issues when a PO file was edited by different
|
50
52
|
committers.
|
51
53
|
|
@@ -61,7 +63,7 @@ Add this to .gitattributes:
|
|
61
63
|
|
62
64
|
*.po merge=pofile
|
63
65
|
*.pot merge=pofile
|
64
|
-
|
66
|
+
|
65
67
|
Or, if you want to make this setting global:
|
66
68
|
|
67
69
|
Create a user global file ~/.gitattributes and fill it with:
|
data/bin/git-jira-branch
CHANGED
data/bin/git-jira-pr
CHANGED
@@ -9,162 +9,10 @@
|
|
9
9
|
# <team>/<branch-title>-<story-id>
|
10
10
|
#
|
11
11
|
require 'rubygems'
|
12
|
-
require 'jira-ruby'
|
13
12
|
require 'optparse'
|
14
13
|
require 'cgi'
|
15
14
|
require 'term/ansicolor'
|
16
15
|
require 'git-whistles/app'
|
17
|
-
require 'git-whistles/jira'
|
16
|
+
require 'git-whistles/pull_request/jira'
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
BROWSERS = %w(xdg-open open firefox iceweasel)
|
22
|
-
SAFE_QUERY_STRING_SIZE = 8000
|
23
|
-
|
24
|
-
def initialize
|
25
|
-
super
|
26
|
-
|
27
|
-
@client = Git::Whistles::Jira.new.get_client
|
28
|
-
end
|
29
|
-
|
30
|
-
def main(args)
|
31
|
-
super
|
32
|
-
parse_args!(args)
|
33
|
-
|
34
|
-
if args.count > 0
|
35
|
-
die "Too many arguments", :usage => true
|
36
|
-
end
|
37
|
-
|
38
|
-
if options.from == options.to
|
39
|
-
die "You cannot issue a pull request to the same branch (#{options.from})."
|
40
|
-
end
|
41
|
-
|
42
|
-
query = { }
|
43
|
-
|
44
|
-
# guess team name
|
45
|
-
if options.from =~ %r{^(\w+-\w+)/.*}
|
46
|
-
team, issue_id = $1.capitalize.split('-')
|
47
|
-
else
|
48
|
-
issue_id = team = nil
|
49
|
-
end
|
50
|
-
|
51
|
-
# guess title.
|
52
|
-
title = options.from.split('/').last.split(/[_-]/).delete_if { |word| word =~ /^\d+$/ }.join(' ').capitalize
|
53
|
-
query[:"pull_request[title]"] = team ? "#{team}: #{title}" : title
|
54
|
-
|
55
|
-
# add Jira infos
|
56
|
-
add_jira_info(query, "#{team}-#{issue_id}".upcase) if issue_id =~ /(\d+)$/
|
57
|
-
|
58
|
-
query_string = query.map { |key,value|
|
59
|
-
"#{CGI.escape key.to_s}=#{CGI.escape value}"
|
60
|
-
}.join('&')
|
61
|
-
url = "https://github.com/#{repo}/compare/#{options.to}...#{options.from}?#{query_string}"
|
62
|
-
|
63
|
-
puts "Preparing a pull request for branch #{options.from}"
|
64
|
-
|
65
|
-
unless launch_browser(url)
|
66
|
-
log.warn "Sorry, I don't know how to launch a web browser on your system. You can open it yourself and paste this URL:\n#{url}"
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
def defaults
|
73
|
-
{
|
74
|
-
:from => run!('git symbolic-ref HEAD').strip.gsub(%r(^refs/heads/), ""),
|
75
|
-
:to => 'master',
|
76
|
-
:remote => 'origin'
|
77
|
-
}
|
78
|
-
end
|
79
|
-
|
80
|
-
def option_parser
|
81
|
-
@option_parser ||= OptionParser.new do |op|
|
82
|
-
op.banner = "Usage: git jira-pr [options]"
|
83
|
-
|
84
|
-
op.on("-f", "--from YOUR_BRANCH", "Branch to issue pull request for [head]") do |v|
|
85
|
-
options.from = v
|
86
|
-
end
|
87
|
-
|
88
|
-
op.on("-to", "--to UPSTREAM_BRANCH", "Branch into which you want your code merged [master]") do |v|
|
89
|
-
options.to = v
|
90
|
-
end
|
91
|
-
|
92
|
-
op.on("-r", "--remote NAME", "The remote you're sending this to [origin]") do |v|
|
93
|
-
options.to = v
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def origin_url
|
99
|
-
@origin_url ||= begin
|
100
|
-
run!("git config --get remote.#{options.remote}.url").strip.tap do |url|
|
101
|
-
url =~ /github\.com/ or die "origin does not have a Github URL !"
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def repo
|
107
|
-
@repo ||= origin_url.sub(/.*github\.com[\/:]/,'').sub(/\.git$/,'')
|
108
|
-
end
|
109
|
-
|
110
|
-
def add_jira_info(query, issue_id)
|
111
|
-
log.info "Finding your Jira Issue¬"
|
112
|
-
|
113
|
-
issue = @client.Issue.find(issue_id)
|
114
|
-
log.info '.'
|
115
|
-
|
116
|
-
if issue.nil?
|
117
|
-
log.warn "Apologies... I could not find issue #{issue_id}."
|
118
|
-
return
|
119
|
-
end
|
120
|
-
|
121
|
-
log.info "Found story #{issue_id} in '#{issue.fields['project']['name']}'"
|
122
|
-
|
123
|
-
title = "#{issue_id}: #{issue.summary}"
|
124
|
-
headline = "Jira story [##{issue_id}](#{@client.options[:site]}/browse/#{issue_id}) in project *#{issue.project.name}*:"
|
125
|
-
|
126
|
-
description = safe_description(issue.description)
|
127
|
-
query.merge! subject: issue.summary, :"pull_request[title]" => title
|
128
|
-
|
129
|
-
if (headline.length + description.length) > SAFE_QUERY_STRING_SIZE
|
130
|
-
log.warn "Oops looks like your story body exceeds maximum allowed caracters to send a github request"
|
131
|
-
log.warn "Please copy the info below to your pull request body:"
|
132
|
-
puts
|
133
|
-
puts headline
|
134
|
-
puts
|
135
|
-
puts
|
136
|
-
puts description
|
137
|
-
puts
|
138
|
-
puts
|
139
|
-
puts "Press any key to continue..."
|
140
|
-
gets
|
141
|
-
query.merge! :"pull_request[body]" => "Please check your command line for the story body"
|
142
|
-
else
|
143
|
-
body = "TODO: describe your changes\n\n===\n\n#{headline}\n\n#{description}"
|
144
|
-
query.merge! :"pull_request[body]" => body
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
def safe_description(description)
|
149
|
-
return '' unless description
|
150
|
-
|
151
|
-
description.split("\n").map do |line|
|
152
|
-
(1..6).each { |i| line.gsub!(/(h#{i}.)/, '#' * i) }
|
153
|
-
line.gsub!(/({{)|(}})/, '`')
|
154
|
-
"> #{line}"
|
155
|
-
end.join("\n")
|
156
|
-
end
|
157
|
-
|
158
|
-
def launch_browser(url)
|
159
|
-
BROWSERS.each do |command|
|
160
|
-
next if run("which #{command}").strip.empty?
|
161
|
-
system(command, url) and return true
|
162
|
-
end
|
163
|
-
false
|
164
|
-
end
|
165
|
-
|
166
|
-
end
|
167
|
-
|
168
|
-
############################################################################
|
169
|
-
|
170
|
-
App.run!
|
18
|
+
Git::Whistles::PullRequest::Jira.run!
|
data/bin/git-pivotal-pr
CHANGED
@@ -9,175 +9,10 @@
|
|
9
9
|
# <team>/<branch-title>-<story-id>
|
10
10
|
#
|
11
11
|
require 'rubygems'
|
12
|
-
require 'pivotal-tracker'
|
13
12
|
require 'optparse'
|
14
13
|
require 'cgi'
|
15
14
|
require 'term/ansicolor'
|
16
15
|
require 'git-whistles/app'
|
16
|
+
require 'git-whistles/pull_request/pivotal'
|
17
17
|
|
18
|
-
|
19
|
-
class App < Git::Whistles::App
|
20
|
-
|
21
|
-
BROWSERS = %w(xdg-open open firefox iceweasel)
|
22
|
-
SAFE_QUERY_STRING_SIZE = 8000
|
23
|
-
|
24
|
-
def initialize
|
25
|
-
super
|
26
|
-
end
|
27
|
-
|
28
|
-
|
29
|
-
def main(args)
|
30
|
-
super
|
31
|
-
parse_args!(args)
|
32
|
-
|
33
|
-
if args.count > 0
|
34
|
-
die "Too many arguments", :usage => true
|
35
|
-
end
|
36
|
-
|
37
|
-
if options.from == options.to
|
38
|
-
die "You cannot issue a pull request to the same branch (#{options.from})."
|
39
|
-
end
|
40
|
-
|
41
|
-
query = { }
|
42
|
-
|
43
|
-
# guess team name
|
44
|
-
if options.from =~ %r{^(\w+)/.*}
|
45
|
-
team = $1.capitalize
|
46
|
-
else
|
47
|
-
team = nil
|
48
|
-
end
|
49
|
-
|
50
|
-
# guess title.
|
51
|
-
title = options.from.split('/').last.split(/[_-]/).delete_if { |word| word =~ /^\d+$/ }.join(' ').capitalize
|
52
|
-
query[:"pull_request[title]"] = team ? "#{team}: #{title}" : title
|
53
|
-
|
54
|
-
# add Pivotal infos
|
55
|
-
add_pivotal_info(query, $1.to_i) if options.from =~ /(\d+)$/
|
56
|
-
|
57
|
-
query_string = query.map { |key,value|
|
58
|
-
"#{CGI.escape key.to_s}=#{CGI.escape value}"
|
59
|
-
}.join('&')
|
60
|
-
url = "https://github.com/#{repo}/compare/#{options.to}...#{options.from}?#{query_string}"
|
61
|
-
|
62
|
-
puts "Preparing a pull request for branch #{options.from}"
|
63
|
-
|
64
|
-
unless launch_browser(url)
|
65
|
-
log.warn "Sorry, I don't know how to launch a web browser on your system. You can open it yourself and paste this URL:\n#{url}"
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
private
|
70
|
-
|
71
|
-
def defaults
|
72
|
-
{
|
73
|
-
:from => run!('git symbolic-ref HEAD').strip.gsub(%r(^refs/heads/), ""),
|
74
|
-
:to => 'master',
|
75
|
-
:remote => 'origin'
|
76
|
-
}
|
77
|
-
end
|
78
|
-
|
79
|
-
def option_parser
|
80
|
-
@option_parser ||= OptionParser.new do |op|
|
81
|
-
op.banner = "Usage: git pivotal-pr [options]"
|
82
|
-
|
83
|
-
op.on("-f", "--from YOUR_BRANCH", "Branch to issue pull request for [head]") do |v|
|
84
|
-
options.from = v
|
85
|
-
end
|
86
|
-
|
87
|
-
op.on("-to", "--to UPSTREAM_BRANCH", "Branch into which you want your code merged [master]") do |v|
|
88
|
-
options.to = v
|
89
|
-
end
|
90
|
-
|
91
|
-
op.on("-r", "--remote NAME", "The remote you're sending this to [origin]") do |v|
|
92
|
-
options.to = v
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
|
98
|
-
def origin_url
|
99
|
-
@origin_url ||= begin
|
100
|
-
run!("git config --get remote.#{options.remote}.url").strip.tap do |url|
|
101
|
-
url =~ /github\.com/ or die "origin does not have a Github URL !"
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
|
107
|
-
def repo
|
108
|
-
@repo ||= origin_url.sub(/.*github\.com[\/:]/,'').sub(/\.git$/,'')
|
109
|
-
end
|
110
|
-
|
111
|
-
|
112
|
-
def add_pivotal_info(query, story_id)
|
113
|
-
token = `git config pivotal-tracker.token`.strip
|
114
|
-
if token.empty?
|
115
|
-
puts Term::ANSIColor.yellow %Q{
|
116
|
-
Your branch appears to have a story ID,
|
117
|
-
but I don't know your Pivotal Tracker token!
|
118
|
-
Please set it with:
|
119
|
-
$ git config [--global] pivotal-tracker.token <token>
|
120
|
-
}
|
121
|
-
die "Aborting."
|
122
|
-
end
|
123
|
-
|
124
|
-
log.info "Finding your project and story¬"
|
125
|
-
|
126
|
-
PivotalTracker::Client.token = token
|
127
|
-
begin
|
128
|
-
story, project = PivotalTracker::Project.all.find do |project|
|
129
|
-
log.info '.¬'
|
130
|
-
story = project.stories.find(story_id) and break story, project
|
131
|
-
end
|
132
|
-
log.info '.'
|
133
|
-
rescue RestClient::Unauthorized
|
134
|
-
log.info '.'
|
135
|
-
die "Your token is not authorized by Pivotal Tracker! Please make sure you have the correct one"
|
136
|
-
end
|
137
|
-
|
138
|
-
if story.nil?
|
139
|
-
log.warn "Apologies... I could not find story #{story_id}."
|
140
|
-
return
|
141
|
-
end
|
142
|
-
|
143
|
-
log.info "Found story #{story_id} in '#{project.name}'"
|
144
|
-
|
145
|
-
title = "#{project.name}: #{story.name} [##{story.id}]"
|
146
|
-
headline = "Pivotal tracker story [##{story_id}](#{story.url}) in project *#{project.name}*:"
|
147
|
-
description = story.description.split("\n").map { |line| "> #{line}" }.join("\n")
|
148
|
-
|
149
|
-
query.merge! subject: story.name, :"pull_request[title]" => title
|
150
|
-
|
151
|
-
if (headline.length + description.length) > SAFE_QUERY_STRING_SIZE
|
152
|
-
log.warn "Oops looks like your story body exceeds maximum allowed caracters to send a github request"
|
153
|
-
log.warn "Please copy the info below to your pull request body:"
|
154
|
-
puts
|
155
|
-
puts headline
|
156
|
-
puts
|
157
|
-
puts
|
158
|
-
puts description
|
159
|
-
puts
|
160
|
-
puts
|
161
|
-
puts "Press any key to continue..."
|
162
|
-
gets
|
163
|
-
query.merge! :"pull_request[body]" => "Please check your command line for the story body"
|
164
|
-
else
|
165
|
-
body = "TODO: describe your changes\n\n===\n\n#{headline}\n\n#{description}"
|
166
|
-
query.merge! :"pull_request[body]" => body
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
def launch_browser(url)
|
171
|
-
BROWSERS.each do |command|
|
172
|
-
next if run("which #{command}").strip.empty?
|
173
|
-
system(command, url) and return true
|
174
|
-
end
|
175
|
-
false
|
176
|
-
end
|
177
|
-
|
178
|
-
|
179
|
-
end
|
180
|
-
|
181
|
-
############################################################################
|
182
|
-
|
183
|
-
App.run!
|
18
|
+
Git::Whistles::PullRequest::Pivotal.run!
|
data/bin/git-pr
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
#
|
4
|
+
# git-pr --
|
5
|
+
#
|
6
|
+
# Open a pull request for the current branch in your default browser
|
7
|
+
#
|
8
|
+
# Assumes the branches are named
|
9
|
+
# <team>/<branch-title>-<story-id>
|
10
|
+
#
|
11
|
+
require 'rubygems'
|
12
|
+
require 'optparse'
|
13
|
+
require 'cgi'
|
14
|
+
require 'term/ansicolor'
|
15
|
+
require 'git-whistles/app'
|
16
|
+
require 'git-whistles/pull_request/bare'
|
17
|
+
|
18
|
+
Git::Whistles::PullRequest::Bare.run!
|
data/git-whistles.gemspec
CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |gem|
|
|
29
29
|
gem.add_dependency "youtrack", "~> 0.0.11"
|
30
30
|
|
31
31
|
gem.files = `git ls-files`.split($\)
|
32
|
-
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
32
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
33
33
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
34
34
|
gem.name = "git-whistles"
|
35
35
|
gem.require_paths = ["lib"]
|
data/lib/git-whistles/jira.rb
CHANGED
@@ -31,7 +31,7 @@ module Git
|
|
31
31
|
Please set it with:
|
32
32
|
$ git config [--global] jira.username <username>
|
33
33
|
}
|
34
|
-
|
34
|
+
raise "Aborting."
|
35
35
|
end
|
36
36
|
|
37
37
|
@password = `git config jira.password`.strip
|
@@ -42,7 +42,7 @@ module Git
|
|
42
42
|
Please set it with:
|
43
43
|
$ git config [--global] jira.password <password>
|
44
44
|
}
|
45
|
-
|
45
|
+
raise "Aborting."
|
46
46
|
end
|
47
47
|
|
48
48
|
@site = `git config jira.site`.strip
|
@@ -53,9 +53,8 @@ module Git
|
|
53
53
|
Please set it with:
|
54
54
|
$ git config [--global] jira.site <https://mydomain.atlassian.net>
|
55
55
|
}
|
56
|
-
|
56
|
+
raise "Aborting."
|
57
57
|
end
|
58
|
-
|
59
58
|
end
|
60
59
|
end
|
61
60
|
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Git
|
2
|
+
module Whistles
|
3
|
+
module PullRequest
|
4
|
+
class Bare < Git::Whistles::App
|
5
|
+
BROWSERS = %w(xdg-open open firefox iceweasel)
|
6
|
+
SAFE_QUERY_STRING_SIZE = 8000
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def main(args)
|
13
|
+
super
|
14
|
+
|
15
|
+
parse_args!(args)
|
16
|
+
|
17
|
+
if args.count > 0
|
18
|
+
die 'Too many arguments', :usage => true
|
19
|
+
end
|
20
|
+
|
21
|
+
if options.from == options.to
|
22
|
+
die "You cannot issue a pull request to the same branch (#{options.from})."
|
23
|
+
end
|
24
|
+
|
25
|
+
query = {}
|
26
|
+
|
27
|
+
# guess team name
|
28
|
+
if options.from =~ %r{^(\w+)/.*}
|
29
|
+
team = $1.capitalize
|
30
|
+
else
|
31
|
+
team = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
# guess title.
|
35
|
+
title = options.from.split('/').last.split(/[_-]/).delete_if { |word| word =~ /^\d+$/ }.join(' ').capitalize
|
36
|
+
query[:"pull_request[title]"] = team ? "#{team}: #{title}" : title
|
37
|
+
|
38
|
+
query.merge!(tracker_related_params(team))
|
39
|
+
|
40
|
+
query_string = query.map { |key,value|
|
41
|
+
"#{CGI.escape key.to_s}=#{CGI.escape value}"
|
42
|
+
}.join('&')
|
43
|
+
url = "https://github.com/#{repo}/compare/#{options.to}...#{options.from}?#{query_string}"
|
44
|
+
|
45
|
+
puts "Preparing a pull request for branch #{options.from}"
|
46
|
+
|
47
|
+
unless launch_browser(url)
|
48
|
+
log.warn "Sorry, I don't know how to launch a web browser on your system. You can open it yourself and paste this URL:\n#{url}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def pr_command # to be overridden by subclass
|
55
|
+
'pr'
|
56
|
+
end
|
57
|
+
|
58
|
+
def tracker_related_params(team)
|
59
|
+
{} # to be overridden by tracker-specific subclass
|
60
|
+
end
|
61
|
+
|
62
|
+
def origin_url
|
63
|
+
@origin_url ||= begin
|
64
|
+
run!("git config --get remote.#{options.remote}.url").strip.tap do |url|
|
65
|
+
url =~ /github\.com/ or die 'origin does not have a Github URL !'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def repo
|
71
|
+
@repo ||= origin_url.sub(/.*github\.com[\/:]/, '').sub(/\.git$/, '')
|
72
|
+
end
|
73
|
+
|
74
|
+
def launch_browser(url)
|
75
|
+
BROWSERS.each do |command|
|
76
|
+
next if run("which #{command}").strip.empty?
|
77
|
+
system(command, url) and return true
|
78
|
+
end
|
79
|
+
false
|
80
|
+
end
|
81
|
+
|
82
|
+
def defaults
|
83
|
+
{
|
84
|
+
from: run!('git symbolic-ref HEAD').strip.gsub(%r(^refs/heads/), ''),
|
85
|
+
to: 'master',
|
86
|
+
remote: 'origin'
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def option_parser
|
91
|
+
@option_parser ||= OptionParser.new do |op|
|
92
|
+
op.banner = "Usage: git #{pr_command} [options]"
|
93
|
+
|
94
|
+
op.on("-f", "--from YOUR_BRANCH", "Branch to issue pull request for [head]") do |v|
|
95
|
+
options.from = v
|
96
|
+
end
|
97
|
+
|
98
|
+
op.on("-to", "--to UPSTREAM_BRANCH", "Branch into which you want your code merged [master]") do |v|
|
99
|
+
options.to = v
|
100
|
+
end
|
101
|
+
|
102
|
+
op.on("-r", "--remote NAME", "The remote you're sending this to [origin]") do |v|
|
103
|
+
options.to = v
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'jira-ruby'
|
2
|
+
require 'git-whistles/jira'
|
3
|
+
require_relative 'bare'
|
4
|
+
|
5
|
+
module Git
|
6
|
+
module Whistles
|
7
|
+
module PullRequest
|
8
|
+
class Jira < Bare
|
9
|
+
private
|
10
|
+
|
11
|
+
def pr_command
|
12
|
+
'jira-pr'
|
13
|
+
end
|
14
|
+
|
15
|
+
def tracker_related_params(team)
|
16
|
+
params = {}
|
17
|
+
|
18
|
+
if options.from =~ %r{^(\w+-\w+)/.*}
|
19
|
+
team, issue_id = $1.capitalize.split('-')
|
20
|
+
else
|
21
|
+
issue_id = team = nil
|
22
|
+
end
|
23
|
+
issue_id = "#{team}-#{issue_id}".upcase if issue_id =~ /(\d+)$/
|
24
|
+
|
25
|
+
log.info 'Finding your Jira Issue¬'
|
26
|
+
|
27
|
+
client = Git::Whistles::Jira.new.get_client rescue die('Aborting.')
|
28
|
+
issue = client.Issue.find(issue_id)
|
29
|
+
log.info '.'
|
30
|
+
|
31
|
+
if issue.nil?
|
32
|
+
log.warn "Apologies… I could not find issue #{issue_id}."
|
33
|
+
die
|
34
|
+
end
|
35
|
+
|
36
|
+
log.info "Found story #{issue_id} in '#{issue.fields['project']['name']}'"
|
37
|
+
|
38
|
+
title = "#{issue_id}: #{issue.summary}"
|
39
|
+
headline = "Jira story [##{issue_id}](#{client.options[:site]}/browse/#{issue_id}) in project *#{issue.project.name}*:"
|
40
|
+
|
41
|
+
description = safe_description(issue.description)
|
42
|
+
params[:subject] = issue.summary
|
43
|
+
params[:'pull_request[title]'] = title
|
44
|
+
|
45
|
+
if (headline.length + description.length) > SAFE_QUERY_STRING_SIZE
|
46
|
+
log.warn 'Oops looks like your story body exceeds maximum allowed caracters to send a github request'
|
47
|
+
log.warn 'Please copy the info below to your pull request body:'
|
48
|
+
puts
|
49
|
+
puts headline
|
50
|
+
puts
|
51
|
+
puts
|
52
|
+
puts description
|
53
|
+
puts
|
54
|
+
puts
|
55
|
+
puts 'Press any key to continue…'
|
56
|
+
gets
|
57
|
+
params.merge! :"pull_request[body]" => 'Please check your command line for the story body'
|
58
|
+
else
|
59
|
+
body = "TODO: describe your changes\n\n===\n\n#{headline}\n\n#{description}"
|
60
|
+
params[:'pull_request[body]'] = body
|
61
|
+
end
|
62
|
+
params
|
63
|
+
end
|
64
|
+
|
65
|
+
def safe_description(description)
|
66
|
+
return '' unless description
|
67
|
+
|
68
|
+
description.split("\n").map do |line|
|
69
|
+
(1..6).each { |i| line.gsub!(/(h#{i}.)/, '#' * i) }
|
70
|
+
line.gsub!(/({{)|(}})/, '`')
|
71
|
+
"> #{line}"
|
72
|
+
end.join("\n")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'pivotal-tracker'
|
2
|
+
require_relative 'bare'
|
3
|
+
|
4
|
+
module Git
|
5
|
+
module Whistles
|
6
|
+
module PullRequest
|
7
|
+
class Pivotal < Bare
|
8
|
+
private
|
9
|
+
|
10
|
+
def pr_command
|
11
|
+
'pivotal-pr'
|
12
|
+
end
|
13
|
+
|
14
|
+
def tracker_related_params(team)
|
15
|
+
params = {}
|
16
|
+
story_id = $1.to_i if options.from =~ /(\d+)$/
|
17
|
+
|
18
|
+
token = `git config pivotal-tracker.token`.strip
|
19
|
+
if token.empty?
|
20
|
+
puts Term::ANSIColor.yellow %Q{
|
21
|
+
Your branch appears to have a story ID,
|
22
|
+
but I don't know your Pivotal Tracker token!
|
23
|
+
Please set it with:
|
24
|
+
$ git config [--global] pivotal-tracker.token <token>
|
25
|
+
}
|
26
|
+
die 'Aborting.'
|
27
|
+
end
|
28
|
+
|
29
|
+
log.info 'Finding your project and story¬'
|
30
|
+
|
31
|
+
PivotalTracker::Client.token = token
|
32
|
+
begin
|
33
|
+
story, project = PivotalTracker::Project.all.find do |project|
|
34
|
+
log.info '.¬'
|
35
|
+
story = project.stories.find(story_id) and break story, project
|
36
|
+
end
|
37
|
+
log.info '.'
|
38
|
+
rescue RestClient::Unauthorized
|
39
|
+
log.info '.'
|
40
|
+
die "Your token is not authorized by Pivotal Tracker! Please make sure you have the correct one"
|
41
|
+
end
|
42
|
+
|
43
|
+
if story.nil?
|
44
|
+
log.warn "Apologies… I could not find story #{story_id}."
|
45
|
+
die
|
46
|
+
end
|
47
|
+
|
48
|
+
log.info "Found story #{story_id} in '#{project.name}'"
|
49
|
+
|
50
|
+
title = "#{project.name}: #{story.name} [##{story.id}]"
|
51
|
+
headline = "Pivotal tracker story [##{story_id}](#{story.url}) in project *#{project.name}*:"
|
52
|
+
description = story.description.split("\n").map { |line| "> #{line}" }.join("\n")
|
53
|
+
params[:subject] = story.name
|
54
|
+
params[:'pull_request[title]'] = title
|
55
|
+
|
56
|
+
if (headline.length + description.length) > SAFE_QUERY_STRING_SIZE
|
57
|
+
log.warn "Oops looks like your story body exceeds maximum allowed caracters to send a github request"
|
58
|
+
log.warn "Please copy the info below to your pull request body:"
|
59
|
+
puts
|
60
|
+
puts headline
|
61
|
+
puts
|
62
|
+
puts
|
63
|
+
puts description
|
64
|
+
puts
|
65
|
+
puts
|
66
|
+
puts 'Press any key to continue…'
|
67
|
+
gets
|
68
|
+
params.merge! :"pull_request[body]" => "Please check your command line for the story body"
|
69
|
+
else
|
70
|
+
body = "TODO: describe your changes\n\n===\n\n#{headline}\n\n#{description}"
|
71
|
+
params[:'pull_request[body]'] = body
|
72
|
+
end
|
73
|
+
params
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/git-whistles/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: git-whistles
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: '1.3'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julien Letessier
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-12-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -181,6 +181,7 @@ executables:
|
|
181
181
|
- git-pivotal-branch
|
182
182
|
- git-pivotal-open
|
183
183
|
- git-pivotal-pr
|
184
|
+
- git-pr
|
184
185
|
- git-select
|
185
186
|
- git-stash-and-checkout
|
186
187
|
- git-whistles
|
@@ -208,6 +209,7 @@ files:
|
|
208
209
|
- bin/git-pivotal-branch
|
209
210
|
- bin/git-pivotal-open
|
210
211
|
- bin/git-pivotal-pr
|
212
|
+
- bin/git-pr
|
211
213
|
- bin/git-select
|
212
214
|
- bin/git-stash-and-checkout
|
213
215
|
- bin/git-whistles
|
@@ -218,6 +220,9 @@ files:
|
|
218
220
|
- lib/git-whistles/helpers.rb
|
219
221
|
- lib/git-whistles/jira.rb
|
220
222
|
- lib/git-whistles/logger.rb
|
223
|
+
- lib/git-whistles/pull_request/bare.rb
|
224
|
+
- lib/git-whistles/pull_request/jira.rb
|
225
|
+
- lib/git-whistles/pull_request/pivotal.rb
|
221
226
|
- lib/git-whistles/version.rb
|
222
227
|
- lib/git-whistles/youtrack/api.rb
|
223
228
|
- lib/git-whistles/youtrack/branch.rb
|