git-process 0.9.1.pre3
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 +16 -0
- data/.rspec +3 -0
- data/.travis.yml +4 -0
- data/Gemfile +22 -0
- data/Gemfile.lock +56 -0
- data/LICENSE +22 -0
- data/README.md +80 -0
- data/Rakefile +16 -0
- data/bin/git-new-fb +21 -0
- data/bin/git-pull-request +21 -0
- data/bin/git-sync +21 -0
- data/bin/git-to-master +21 -0
- data/git-process.gemspec +21 -0
- data/lib/git-process/abstract-error-builder.rb +46 -0
- data/lib/git-process/git-abstract-merge-error-builder.rb +115 -0
- data/lib/git-process/git-branch.rb +86 -0
- data/lib/git-process/git-branches.rb +53 -0
- data/lib/git-process/git-lib.rb +413 -0
- data/lib/git-process/git-merge-error.rb +31 -0
- data/lib/git-process/git-new-fb-options.rb +34 -0
- data/lib/git-process/git-process-error.rb +10 -0
- data/lib/git-process/git-process-options.rb +82 -0
- data/lib/git-process/git-process.rb +194 -0
- data/lib/git-process/git-pull-request-options.rb +42 -0
- data/lib/git-process/git-rebase-error.rb +31 -0
- data/lib/git-process/git-status.rb +72 -0
- data/lib/git-process/git-sync-options.rb +34 -0
- data/lib/git-process/git-to-master-options.rb +18 -0
- data/lib/git-process/github-client.rb +73 -0
- data/lib/git-process/github-service.rb +156 -0
- data/lib/git-process/parked-changes-error.rb +32 -0
- data/lib/git-process/pull-request.rb +38 -0
- data/lib/git-process/uncommitted-changes-error.rb +15 -0
- data/lib/git-process/version.rb +12 -0
- data/spec/FileHelpers.rb +18 -0
- data/spec/GitRepoHelper.rb +86 -0
- data/spec/git-abstract-merge-error-builder_spec.rb +113 -0
- data/spec/git-lib_spec.rb +118 -0
- data/spec/git-process_spec.rb +328 -0
- data/spec/git-status_spec.rb +101 -0
- data/spec/github-service_spec.rb +209 -0
- data/spec/pull-request_spec.rb +57 -0
- data/spec/spec_helper.rb +1 -0
- metadata +133 -0
@@ -0,0 +1,194 @@
|
|
1
|
+
require 'git-lib'
|
2
|
+
require 'uncommitted-changes-error'
|
3
|
+
require 'git-rebase-error'
|
4
|
+
require 'git-merge-error'
|
5
|
+
require 'parked-changes-error'
|
6
|
+
require 'pull-request'
|
7
|
+
require 'shellwords'
|
8
|
+
require 'highline/import'
|
9
|
+
|
10
|
+
|
11
|
+
module Git
|
12
|
+
|
13
|
+
class Process
|
14
|
+
attr_reader :lib
|
15
|
+
|
16
|
+
@@server_name = 'origin'
|
17
|
+
@@master_branch = 'master'
|
18
|
+
|
19
|
+
def initialize(dir = nil, gitlib = nil, options = {})
|
20
|
+
@lib = gitlib || Git::GitLib.new(dir, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def Process.remote_master_branch
|
25
|
+
"#{@@server_name}/#{@@master_branch}"
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def Process.server_name
|
30
|
+
@@server_name
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def Process.master_branch
|
35
|
+
@@master_branch
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def rebase_to_master
|
40
|
+
raise UncommittedChangesError.new unless lib.status.clean?
|
41
|
+
raise ParkedChangesError.new(lib) if is_parked?
|
42
|
+
|
43
|
+
if lib.has_a_remote?
|
44
|
+
lib.fetch
|
45
|
+
rebase(Process::remote_master_branch)
|
46
|
+
lib.push(Process::server_name, lib.branches.current, Process::master_branch)
|
47
|
+
remove_feature_branch
|
48
|
+
else
|
49
|
+
rebase("master")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def sync_with_server(rebase, force)
|
55
|
+
raise UncommittedChangesError.new unless lib.status.clean?
|
56
|
+
raise ParkedChangesError.new(lib) if is_parked?
|
57
|
+
|
58
|
+
current_branch = lib.branches.current
|
59
|
+
remote_branch = "#{Process::server_name}/#{current_branch}"
|
60
|
+
|
61
|
+
lib.fetch
|
62
|
+
|
63
|
+
if rebase
|
64
|
+
rebase(Process::remote_master_branch)
|
65
|
+
else
|
66
|
+
merge(Process::remote_master_branch)
|
67
|
+
end
|
68
|
+
|
69
|
+
old_sha = lib.command('rev-parse', remote_branch) rescue ''
|
70
|
+
|
71
|
+
unless current_branch == Process::master_branch
|
72
|
+
lib.fetch
|
73
|
+
new_sha = lib.command('rev-parse', remote_branch) rescue ''
|
74
|
+
unless old_sha == new_sha
|
75
|
+
logger.warn("'#{current_branch}' changed on '#{Process::server_name}'"+
|
76
|
+
" [#{old_sha[0..5]}->#{new_sha[0..5]}]; trying sync again.")
|
77
|
+
sync_with_server(rebase, force)
|
78
|
+
end
|
79
|
+
lib.push(Process::server_name, current_branch, current_branch, :force => rebase || force)
|
80
|
+
else
|
81
|
+
logger.warn("Not pushing to the server because the current branch is the master branch.")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def new_feature_branch(branch_name)
|
87
|
+
branches = lib.branches
|
88
|
+
on_parking = (branches.parking == branches.current)
|
89
|
+
|
90
|
+
if on_parking
|
91
|
+
new_branch = lib.checkout(branch_name, :new_branch => '_parking_')
|
92
|
+
branches.parking.delete
|
93
|
+
new_branch
|
94
|
+
else
|
95
|
+
lib.checkout(branch_name, :new_branch => 'origin/master')
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def bad_parking_branch_msg
|
101
|
+
hl = HighLine.new
|
102
|
+
hl.color("\n***********************************************************************************************\n\n"+
|
103
|
+
"There is an old '_parking_' branch with unacounted changes in it.\n"+
|
104
|
+
"It has been renamed to '_parking_OLD_'.\n"+
|
105
|
+
"Please rename the branch to what the changes are about (`git branch -m _parking_OLD_ my_fb_name`),\n"+
|
106
|
+
" or remove it altogher (`git branch -D _parking_OLD_`).\n\n"+
|
107
|
+
"***********************************************************************************************\n", :red, :bold)
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
def remove_feature_branch
|
112
|
+
branches = lib.branches
|
113
|
+
|
114
|
+
remote_master = branches[Process::remote_master_branch]
|
115
|
+
current_branch = branches.current
|
116
|
+
|
117
|
+
unless remote_master.contains_all_of(current_branch.name)
|
118
|
+
raise GitProcessError.new("Branch '#{current_branch.name}' has not been merged into '#{Process::remote_master_branch}'")
|
119
|
+
end
|
120
|
+
|
121
|
+
parking_branch = branches['_parking_']
|
122
|
+
if parking_branch
|
123
|
+
if (parking_branch.is_ahead_of(remote_master.name) and
|
124
|
+
!current_branch.contains_all_of(parking_branch.name))
|
125
|
+
|
126
|
+
parking_branch.rename('_parking_OLD_')
|
127
|
+
|
128
|
+
logger.warn {bad_parking_branch_msg}
|
129
|
+
else
|
130
|
+
parking_branch.delete
|
131
|
+
end
|
132
|
+
end
|
133
|
+
remote_master.checkout_to_new('_parking_', :no_track => true)
|
134
|
+
|
135
|
+
current_branch.delete(true)
|
136
|
+
if branches["#{Process.server_name}/#{current_branch.name}"]
|
137
|
+
lib.push(Process.server_name, nil, nil, :delete => current_branch.name)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
def is_parked?
|
143
|
+
branches = lib.branches
|
144
|
+
branches.parking == branches.current
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
def rebase(base)
|
149
|
+
begin
|
150
|
+
lib.rebase(base)
|
151
|
+
rescue Git::GitExecuteError => rebase_error
|
152
|
+
raise RebaseError.new(rebase_error.message, lib)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
def merge(base)
|
158
|
+
begin
|
159
|
+
lib.merge(base)
|
160
|
+
rescue Git::GitExecuteError => merge_error
|
161
|
+
raise MergeError.new(merge_error.message, lib)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
def pull_request(repo_name, base, head, title, body, opts = {})
|
167
|
+
repo_name ||= lib.repo_name
|
168
|
+
base ||= @@master_branch
|
169
|
+
head ||= lib.branches.current
|
170
|
+
title ||= ask_for_pull_title
|
171
|
+
body ||= ask_for_pull_body
|
172
|
+
GitHub::PullRequest.new(lib, repo_name, opts).pull_request(base, head, title, body)
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
def ask_for_pull_title
|
177
|
+
ask("What <%= color('title', [:bold]) %> do you want to give the pull request? ") do |q|
|
178
|
+
q.validate = /^\w+.*/
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
def ask_for_pull_body
|
184
|
+
ask("What <%= color('description', [:bold]) %> do you want to give the pull request? ")
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
def logger
|
189
|
+
@lib.logger
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'git-process-options'
|
2
|
+
|
3
|
+
module Git
|
4
|
+
|
5
|
+
class Process
|
6
|
+
|
7
|
+
class PullRequestOptions
|
8
|
+
include GitProcessOptions
|
9
|
+
|
10
|
+
attr_reader :user, :password, :description, :title, :filename
|
11
|
+
|
12
|
+
def initialize(filename, argv)
|
13
|
+
@filename = filename
|
14
|
+
parse(filename, argv)
|
15
|
+
end
|
16
|
+
|
17
|
+
def extend_opts(opts)
|
18
|
+
opts.banner = "Usage: #{filename} [ options ] [pull_request_title]"
|
19
|
+
|
20
|
+
opts.on("-u", "--user name", String, "GitHub account username") do |user|
|
21
|
+
@user = user
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.on("-p", "--password pw", String, "GitHub account password") do |password|
|
25
|
+
@password = password
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on(nil, "--desc description", String, "Description of the changes.") do |desc|
|
29
|
+
@description = desc
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def extend_args(argv)
|
35
|
+
@title = argv.pop unless argv.empty?
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'git-process-error'
|
2
|
+
require 'git-abstract-merge-error-builder'
|
3
|
+
|
4
|
+
module Git
|
5
|
+
|
6
|
+
class Process
|
7
|
+
|
8
|
+
class RebaseError < GitProcessError
|
9
|
+
include Git::AbstractMergeErrorBuilder
|
10
|
+
|
11
|
+
attr_reader :error_message, :lib
|
12
|
+
|
13
|
+
def initialize(rebase_error_message, lib)
|
14
|
+
@lib = lib
|
15
|
+
@error_message = rebase_error_message
|
16
|
+
|
17
|
+
msg = build_message
|
18
|
+
|
19
|
+
super(msg)
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def continue_command
|
24
|
+
'git rebase --continue'
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Git
|
2
|
+
|
3
|
+
#
|
4
|
+
# The status of the Git repository.
|
5
|
+
#
|
6
|
+
# @!attribute [r] unmerged
|
7
|
+
# @return [Enumerable] a sorted list of unmerged files
|
8
|
+
# @!attribute [r] modified
|
9
|
+
# @return [Enumerable] a sorted list of modified files
|
10
|
+
# @!attribute [r] deleted
|
11
|
+
# @return [Enumerable] a sorted list of deleted files
|
12
|
+
# @!attribute [r] added
|
13
|
+
# @return [Enumerable] a sorted list of files that have been added
|
14
|
+
# @!attribute [r] unknown
|
15
|
+
# @return [Enumerable] a sorted list of unknown files
|
16
|
+
class GitStatus
|
17
|
+
attr_reader :unmerged, :modified, :deleted, :added, :unknown
|
18
|
+
|
19
|
+
def initialize(lib)
|
20
|
+
unmerged = []
|
21
|
+
modified = []
|
22
|
+
deleted = []
|
23
|
+
added = []
|
24
|
+
unknown = []
|
25
|
+
|
26
|
+
stats = lib.porcelain_status.split("\n")
|
27
|
+
|
28
|
+
stats.each do |s|
|
29
|
+
stat = s[0..1]
|
30
|
+
file = s[3..-1]
|
31
|
+
#puts "stat #{stat} - #{file}"
|
32
|
+
case stat
|
33
|
+
when 'U ', ' U'
|
34
|
+
unmerged << file
|
35
|
+
when 'UU'
|
36
|
+
unmerged << file
|
37
|
+
modified << file
|
38
|
+
when 'M ', ' M'
|
39
|
+
modified << file
|
40
|
+
when 'D ', ' D'
|
41
|
+
deleted << file
|
42
|
+
when 'DU', 'UD'
|
43
|
+
deleted << file
|
44
|
+
unmerged << file
|
45
|
+
when 'A ', ' A'
|
46
|
+
added << file
|
47
|
+
when 'AA'
|
48
|
+
added << file
|
49
|
+
unmerged << file
|
50
|
+
when '??'
|
51
|
+
unknown << file
|
52
|
+
else
|
53
|
+
raise "Do not know what to do with status #{stat} - #{file}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
@unmerged = unmerged.sort.uniq.freeze
|
58
|
+
@modified = modified.sort.uniq.freeze
|
59
|
+
@deleted = deleted.sort.uniq.freeze
|
60
|
+
@added = added.sort.uniq.freeze
|
61
|
+
@unknown = unknown.sort.uniq.freeze
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
# @return [Boolean] are there any changes in the index or working directory?
|
66
|
+
def clean?
|
67
|
+
@unmerged.empty? and @modified.empty? and @deleted.empty? and @added.empty? and @unknown.empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'git-process-options'
|
3
|
+
|
4
|
+
module Git
|
5
|
+
|
6
|
+
class Process
|
7
|
+
|
8
|
+
class SyncOptions
|
9
|
+
include GitProcessOptions
|
10
|
+
|
11
|
+
attr_reader :rebase, :force
|
12
|
+
|
13
|
+
|
14
|
+
def initialize(filename, argv)
|
15
|
+
@rebase = false
|
16
|
+
@force = false
|
17
|
+
parse(filename, argv)
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def extend_opts(opts)
|
22
|
+
opts.on("-r", "--rebase", "Rebase instead of merge") do |v|
|
23
|
+
@rebase = true
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on("-f", "--force", "Force the push") do |v|
|
27
|
+
@force = true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'octokit'
|
2
|
+
|
3
|
+
module Octokit
|
4
|
+
|
5
|
+
module Connection
|
6
|
+
|
7
|
+
#
|
8
|
+
# Unfortunately, there's no way to change the URL except by completely replacing
|
9
|
+
# this method.
|
10
|
+
#
|
11
|
+
def connection(authenticate=true, raw=false, version=3, force_urlencoded=false)
|
12
|
+
if site
|
13
|
+
url = site
|
14
|
+
else
|
15
|
+
case version
|
16
|
+
when 2
|
17
|
+
url = "https://github.com"
|
18
|
+
when 3
|
19
|
+
url = "https://api.github.com"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
options = {
|
24
|
+
:proxy => proxy,
|
25
|
+
:ssl => { :verify => false },
|
26
|
+
:url => url,
|
27
|
+
}
|
28
|
+
|
29
|
+
options.merge!(:params => {:access_token => oauth_token}) if oauthed? && !authenticated?
|
30
|
+
|
31
|
+
connection = Faraday.new(options) do |builder|
|
32
|
+
if version >= 3 && !force_urlencoded
|
33
|
+
builder.request :json
|
34
|
+
else
|
35
|
+
builder.request :url_encoded
|
36
|
+
end
|
37
|
+
builder.use Faraday::Response::RaiseOctokitError
|
38
|
+
unless raw
|
39
|
+
builder.use FaradayMiddleware::Mashify
|
40
|
+
builder.use FaradayMiddleware::ParseJson
|
41
|
+
end
|
42
|
+
builder.adapter(adapter)
|
43
|
+
end
|
44
|
+
connection.basic_auth authentication[:login], authentication[:password] if authenticate and authenticated?
|
45
|
+
connection
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
class GitHubClient < Octokit::Client
|
53
|
+
|
54
|
+
def site
|
55
|
+
@site
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
def site=(new_site)
|
60
|
+
@site = new_site
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
alias :old_request :request
|
65
|
+
|
66
|
+
def request(method, path, options, version, authenticate, raw, force_urlencoded)
|
67
|
+
if /api.github.com/ !~ site
|
68
|
+
path = "/api/v3#{path}"
|
69
|
+
end
|
70
|
+
old_request(method, path, options, version, authenticate, raw, force_urlencoded)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'highline/import'
|
2
|
+
require 'github-client'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
|
6
|
+
module GitHubService
|
7
|
+
|
8
|
+
def client
|
9
|
+
unless @client
|
10
|
+
auth_token
|
11
|
+
logger.debug { "Creating GitHub client for user #{user} using token '#{auth_token}'" }
|
12
|
+
@client = GitHubClient.new(:login => user, :oauth_token=> auth_token)
|
13
|
+
@client.site = site
|
14
|
+
end
|
15
|
+
@client
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def site(opts = {})
|
20
|
+
@site ||= compute_site(opts)
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def compute_site(opts = {})
|
25
|
+
origin_url = lib.config('remote.origin.url')
|
26
|
+
|
27
|
+
raise GitHubService::NoRemoteRepository.new("There is no value set for 'remote.origin.url'") if origin_url.empty?
|
28
|
+
|
29
|
+
if /^git\@/ =~ origin_url
|
30
|
+
host = origin_url.sub(/^git\@(.*?):.*$/, '\1')
|
31
|
+
site = host_to_site(host, false)
|
32
|
+
else
|
33
|
+
uri = URI.parse(origin_url)
|
34
|
+
host = uri.host
|
35
|
+
scheme = uri.scheme
|
36
|
+
|
37
|
+
raise URI::InvalidURIError.new("Need a scheme in URI: '#{origin_url}'") unless scheme
|
38
|
+
|
39
|
+
unless host
|
40
|
+
# assume that the 'scheme' is the named configuration in ~/.ssh/config
|
41
|
+
host = hostname_from_ssh_config(scheme, opts[:ssh_config_file] ||= "#{ENV['HOME']}/.ssh/config")
|
42
|
+
end
|
43
|
+
|
44
|
+
site = host_to_site(host, scheme == 'https')
|
45
|
+
end
|
46
|
+
site
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def hostname_from_ssh_config(host_alias, config_file)
|
51
|
+
config_lines = File.new(config_file).readlines
|
52
|
+
|
53
|
+
in_host_section = false
|
54
|
+
host_name = nil
|
55
|
+
|
56
|
+
sections = config_lines.each do |line|
|
57
|
+
line.chop!
|
58
|
+
if /^\s*Host\s+#{host_alias}\s*$/ =~ line
|
59
|
+
in_host_section = true
|
60
|
+
next
|
61
|
+
end
|
62
|
+
if in_host_section and (/^\s*HostName\s+\S+\s*$/ =~ line)
|
63
|
+
host_name = line.sub(/^\s*HostName\s+(\S+)\s*$/, '\1')
|
64
|
+
break
|
65
|
+
end
|
66
|
+
end
|
67
|
+
host_name
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
def host_to_site(host, ssl)
|
72
|
+
if /github.com$/ =~ host
|
73
|
+
'https://api.github.com'
|
74
|
+
else
|
75
|
+
"http#{ssl ? 's' : ''}://#{host}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
private :host_to_site, :compute_site
|
81
|
+
|
82
|
+
|
83
|
+
def pw_client
|
84
|
+
unless @pw_client
|
85
|
+
logger.debug { "Creating GitHub client for user #{user} using password #{password}" }
|
86
|
+
@pw_client = GitHubClient.new(:login => user, :password => password)
|
87
|
+
@pw_client.site = site
|
88
|
+
end
|
89
|
+
@pw_client
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
def user
|
94
|
+
unless @user
|
95
|
+
user = lib.config('github.user')
|
96
|
+
if user.empty?
|
97
|
+
user = ask("Your <%= color('GitHub', [:bold, :blue]) %> username: ") do |q|
|
98
|
+
q.validate = /^\w\w+$/
|
99
|
+
end
|
100
|
+
lib.config('github.user', user)
|
101
|
+
end
|
102
|
+
@user = user
|
103
|
+
end
|
104
|
+
@user
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def password
|
109
|
+
unless @password
|
110
|
+
@password = ask("Your <%= color('GitHub', [:bold, :blue]) %> password: ") do |q|
|
111
|
+
q.validate = /^\w\w+$/
|
112
|
+
q.echo = 'x'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
@password
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
def auth_token
|
120
|
+
@auth_token ||= config_auth_token || create_authorization
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
def create_authorization
|
125
|
+
logger.info("Authorizing this to work with your repos.")
|
126
|
+
auth = pw_client.create_authorization(:scopes => ['repo', 'user', 'gist'],
|
127
|
+
:note => 'Git-Process',
|
128
|
+
:note_url => 'http://jdigger.github.com/git-process')
|
129
|
+
config_auth_token = auth['token']
|
130
|
+
lib.config('gitProcess.github.authToken', config_auth_token)
|
131
|
+
config_auth_token
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
def config_auth_token
|
136
|
+
unless @auth_token
|
137
|
+
c_auth_token = lib.config('gitProcess.github.authToken')
|
138
|
+
@auth_token = c_auth_token.empty? ? nil : c_auth_token
|
139
|
+
end
|
140
|
+
@auth_token
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
def logger
|
145
|
+
@lib.logger
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
class GithubServiceError < StandardError
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
class NoRemoteRepository < GithubServiceError
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'git-process-error'
|
2
|
+
|
3
|
+
module Git
|
4
|
+
|
5
|
+
class Process
|
6
|
+
|
7
|
+
class ParkedChangesError < GitProcessError
|
8
|
+
include Git::AbstractErrorBuilder
|
9
|
+
|
10
|
+
attr_reader :error_message, :lib
|
11
|
+
|
12
|
+
def initialize(lib)
|
13
|
+
@lib = lib
|
14
|
+
msg = build_message
|
15
|
+
super(msg)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def human_message
|
20
|
+
"You made your changes on the the '_parking_' branch instead of a feature branch.\n"+"Please rename the branch to be a feature branch."
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def build_commands
|
25
|
+
['git branch -m _parking_ my_feature_branch']
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'github-service'
|
2
|
+
require 'octokit'
|
3
|
+
|
4
|
+
|
5
|
+
module GitHub
|
6
|
+
|
7
|
+
class PullRequest
|
8
|
+
include GitHubService
|
9
|
+
|
10
|
+
attr_reader :lib, :repo
|
11
|
+
|
12
|
+
def initialize(lib, repo, opts = {})
|
13
|
+
@lib = lib
|
14
|
+
@repo = repo
|
15
|
+
@user = opts[:user]
|
16
|
+
@password = opts[:password]
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def pull_requests
|
21
|
+
@pull_requests ||= client.pull_requests(repo)
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def create(base, head, title, body)
|
26
|
+
logger.info { "Creating a pull request asking for '#{head}' to be merged into '#{base}' on #{repo}." }
|
27
|
+
begin
|
28
|
+
client.create_pull_request(repo, base, head, title, body)
|
29
|
+
rescue Octokit::UnprocessableEntity => exp
|
30
|
+
pull = pull_requests.find {|p| p[:head][:ref] == head and p[:base][:ref] == base}
|
31
|
+
logger.warn { "Pull request already exists. See #{pull[:html_url]}" }
|
32
|
+
pull
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'git-process-error'
|
2
|
+
|
3
|
+
module Git
|
4
|
+
|
5
|
+
class Process
|
6
|
+
|
7
|
+
class UncommittedChangesError < GitProcessError
|
8
|
+
def initialize()
|
9
|
+
super("There are uncommitted changes.\nPlease either commit your changes, or use 'git stash' to set them aside.")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|