git-process 0.9.1.pre3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|