git-process-lib 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +123 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +57 -0
- data/LICENSE +193 -0
- data/README.md +342 -0
- data/Rakefile +32 -0
- data/bin/git-new-fb +39 -0
- data/bin/git-pull-request +63 -0
- data/bin/git-sync +38 -0
- data/bin/git-to-master +44 -0
- data/docs/git-new-fb.1.adoc +83 -0
- data/docs/git-process.1.adoc +227 -0
- data/docs/git-pull-request.1.adoc +166 -0
- data/docs/git-sync.1.adoc +120 -0
- data/docs/git-to-master.1.adoc +172 -0
- data/git-new-fb.gemspec +20 -0
- data/git-process-lib.gemspec +25 -0
- data/git-process.gemspec +22 -0
- data/git-pull-request.gemspec +20 -0
- data/git-sync.gemspec +20 -0
- data/git-to-master.gemspec +20 -0
- data/lib/git-process/abstract_error_builder.rb +53 -0
- data/lib/git-process/changed_file_helper.rb +115 -0
- data/lib/git-process/git_abstract_merge_error_builder.rb +130 -0
- data/lib/git-process/git_branch.rb +105 -0
- data/lib/git-process/git_branches.rb +81 -0
- data/lib/git-process/git_config.rb +135 -0
- data/lib/git-process/git_lib.rb +646 -0
- data/lib/git-process/git_logger.rb +84 -0
- data/lib/git-process/git_merge_error.rb +28 -0
- data/lib/git-process/git_process.rb +159 -0
- data/lib/git-process/git_process_error.rb +18 -0
- data/lib/git-process/git_process_options.rb +101 -0
- data/lib/git-process/git_rebase_error.rb +30 -0
- data/lib/git-process/git_remote.rb +222 -0
- data/lib/git-process/git_status.rb +108 -0
- data/lib/git-process/github_configuration.rb +298 -0
- data/lib/git-process/github_pull_request.rb +165 -0
- data/lib/git-process/new_fb.rb +49 -0
- data/lib/git-process/parked_changes_error.rb +41 -0
- data/lib/git-process/pull_request.rb +136 -0
- data/lib/git-process/pull_request_error.rb +25 -0
- data/lib/git-process/rebase_to_master.rb +148 -0
- data/lib/git-process/sync_process.rb +55 -0
- data/lib/git-process/syncer.rb +157 -0
- data/lib/git-process/uncommitted_changes_error.rb +23 -0
- data/lib/git-process/version.rb +22 -0
- data/local-build.rb +24 -0
- data/spec/FileHelpers.rb +19 -0
- data/spec/GitRepoHelper.rb +123 -0
- data/spec/changed_file_helper_spec.rb +127 -0
- data/spec/git_abstract_merge_error_builder_spec.rb +64 -0
- data/spec/git_branch_spec.rb +123 -0
- data/spec/git_config_spec.rb +45 -0
- data/spec/git_lib_spec.rb +176 -0
- data/spec/git_logger_spec.rb +66 -0
- data/spec/git_process_spec.rb +208 -0
- data/spec/git_remote_spec.rb +227 -0
- data/spec/git_status_spec.rb +122 -0
- data/spec/github_configuration_spec.rb +152 -0
- data/spec/github_pull_request_spec.rb +117 -0
- data/spec/github_test_helper.rb +49 -0
- data/spec/new_fb_spec.rb +126 -0
- data/spec/pull_request_helper.rb +94 -0
- data/spec/pull_request_spec.rb +137 -0
- data/spec/rebase_to_master_spec.rb +362 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/sync_spec.rb +1474 -0
- metadata +249 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
2
|
+
# you may not use this file except in compliance with the License.
|
3
|
+
# You may obtain a copy of the License at
|
4
|
+
#
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10
|
+
# See the License for the specific language governing permissions and
|
11
|
+
# limitations under the License.
|
12
|
+
|
13
|
+
require 'git-process/git_process'
|
14
|
+
|
15
|
+
module GitProc
|
16
|
+
|
17
|
+
class NewFeatureBranch < Process
|
18
|
+
|
19
|
+
def initialize(dir, opts)
|
20
|
+
@branch_name = opts[:branch_name]
|
21
|
+
@local_only = opts[:local]
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def runner
|
27
|
+
mybranches = gitlib.branches()
|
28
|
+
on_parking = (mybranches.parking == mybranches.current)
|
29
|
+
|
30
|
+
base_branch = if on_parking and not mybranches[config.integration_branch].contains_all_of(mybranches.parking.name)
|
31
|
+
'_parking_'
|
32
|
+
else
|
33
|
+
config.integration_branch
|
34
|
+
end
|
35
|
+
|
36
|
+
gitlib.fetch if gitlib.has_a_remote? and not @local_only
|
37
|
+
|
38
|
+
logger.info { "Creating #{@branch_name} off of #{base_branch}" }
|
39
|
+
new_branch = gitlib.checkout(@branch_name, :new_branch => base_branch)
|
40
|
+
|
41
|
+
branches = gitlib.branches()
|
42
|
+
branches[@branch_name].upstream(config.integration_branch)
|
43
|
+
branches.parking.delete! if on_parking
|
44
|
+
new_branch
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
2
|
+
# you may not use this file except in compliance with the License.
|
3
|
+
# You may obtain a copy of the License at
|
4
|
+
#
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10
|
+
# See the License for the specific language governing permissions and
|
11
|
+
# limitations under the License.
|
12
|
+
|
13
|
+
require 'git-process/git_process_error'
|
14
|
+
|
15
|
+
module GitProc
|
16
|
+
|
17
|
+
class ParkedChangesError < GitProcessError
|
18
|
+
include GitProc::AbstractErrorBuilder
|
19
|
+
|
20
|
+
attr_reader :error_message, :lib
|
21
|
+
|
22
|
+
|
23
|
+
def initialize(lib)
|
24
|
+
@lib = lib
|
25
|
+
msg = build_message
|
26
|
+
super(msg)
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def human_message
|
31
|
+
"You made your changes on the the '_parking_' branch instead of a feature branch.\n"+'Please rename the branch to be a feature branch.'
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def build_commands
|
36
|
+
['git branch -m _parking_ my_feature_branch']
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
2
|
+
# you may not use this file except in compliance with the License.
|
3
|
+
# You may obtain a copy of the License at
|
4
|
+
#
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10
|
+
# See the License for the specific language governing permissions and
|
11
|
+
# limitations under the License.
|
12
|
+
|
13
|
+
require 'git-process/git_process'
|
14
|
+
require 'git-process/github_pull_request'
|
15
|
+
require 'git-process/pull_request_error'
|
16
|
+
require 'git-process/syncer'
|
17
|
+
require 'highline/import'
|
18
|
+
|
19
|
+
|
20
|
+
module GitProc
|
21
|
+
|
22
|
+
class PullRequest < Process
|
23
|
+
|
24
|
+
def initialize(dir, opts)
|
25
|
+
super
|
26
|
+
@base_branch = opts[:base_branch]
|
27
|
+
@head_branch = opts[:head_branch]
|
28
|
+
@_repo_name = opts[:repo_name]
|
29
|
+
@_remote_name = opts[:server]
|
30
|
+
@pr_number = opts[:prNumber]
|
31
|
+
@title = opts[:title]
|
32
|
+
@description = opts[:description]
|
33
|
+
@user = opts[:user]
|
34
|
+
@password = opts[:password]
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def runner
|
39
|
+
if @pr_number.nil? or @pr_number.empty?
|
40
|
+
pr = create_pull_request
|
41
|
+
logger.info { "Created pull request at #{pr.html_url}" }
|
42
|
+
else
|
43
|
+
checkout_pull_request
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
def create_pull_request_client(remote_name, repo_name)
|
49
|
+
PullRequest.create_pull_request_client(self, remote_name, repo_name, @user, @password)
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def create_pull_request
|
54
|
+
current_branch = gitlib.branches.current.name
|
55
|
+
base_branch = @base_branch || config.master_branch
|
56
|
+
head_branch = @head_branch || current_branch
|
57
|
+
title = @title || current_branch
|
58
|
+
description = @description || ''
|
59
|
+
|
60
|
+
PullRequest.create_pull_request(gitlib, remote.name, remote.name, remote.repo_name, current_branch, base_branch, head_branch, title, description, logger, @user, @password)
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def checkout_pull_request
|
65
|
+
PullRequest.checkout_pull_request(gitlib, @pr_number, remote.name, remote.repo_name, @user, @password, logger)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
class << self
|
70
|
+
|
71
|
+
def create_pull_request_client(lib, remote_name, repo_name, username, password)
|
72
|
+
GitHub::PullRequest.new(lib, remote_name, repo_name, {:user => username, :password => password})
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def create_pull_request(lib, server_name, remote_name, repo_name, current_branch, base_branch, head_branch, title, description, logger, username, password)
|
77
|
+
if base_branch == head_branch
|
78
|
+
raise PullRequestError.new("Can not create a pull request where the base branch and head branch are the same: #{base_branch}")
|
79
|
+
end
|
80
|
+
|
81
|
+
lib.push(server_name, current_branch, current_branch, :force => false)
|
82
|
+
pr = create_pull_request_client(lib, remote_name, repo_name, username, password)
|
83
|
+
pr.create(base_branch, head_branch, title, description)
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def checkout_pull_request(lib, pr_number, remote_name, repo_name, username, password, logger)
|
88
|
+
logger.info { 'Getting #pr_number' }
|
89
|
+
|
90
|
+
lib.fetch(remote_name)
|
91
|
+
|
92
|
+
pr = create_pull_request_client(lib, remote_name, repo_name, username, password)
|
93
|
+
json = pr.pull_request(pr_number)
|
94
|
+
head_branch_name = json.head.ref
|
95
|
+
base_branch_name = json.base.ref
|
96
|
+
|
97
|
+
remote_head_server_name = match_remote_to_pr_remote(lib, json.head.repo.ssh_url)
|
98
|
+
remote_base_server_name = match_remote_to_pr_remote(lib, json.base.repo.ssh_url)
|
99
|
+
lib.checkout(head_branch_name, :new_branch => "#{remote_head_server_name}/#{head_branch_name}")
|
100
|
+
lib.branch(head_branch_name, :upstream => "#{remote_base_server_name}/#{base_branch_name}")
|
101
|
+
#logger.info(json.to_hash)
|
102
|
+
|
103
|
+
lib.fetch(remote_base_server_name) if remote_head_server_name != remote_base_server_name
|
104
|
+
Syncer.rebase_sync(lib, true)
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def match_remote_to_pr_remote(lib, pr_remote)
|
109
|
+
pr_url = lib.remote.expanded_url(nil, pr_remote)
|
110
|
+
servers = lib.remote.remote_names
|
111
|
+
server_urls = servers.collect { |s| {:server_name => s, :url => lib.remote.expanded_url(s)} }
|
112
|
+
|
113
|
+
pair = server_urls.find do |su|
|
114
|
+
url = su[:url]
|
115
|
+
uri = URI.parse(url)
|
116
|
+
host = uri.host
|
117
|
+
path = uri.path
|
118
|
+
|
119
|
+
pr_uri = URI.parse(lib.remote.expanded_url(nil, pr_url))
|
120
|
+
pr_host = pr_uri.host
|
121
|
+
pr_path = pr_uri.path
|
122
|
+
|
123
|
+
pr_host == host and pr_path == path
|
124
|
+
end
|
125
|
+
|
126
|
+
if pair.nil?
|
127
|
+
raise GitHubService::NoRemoteRepository.new("Could not match pull request url (#{pr_url}) to any of the registered remote urls: #{server_urls.map {|s| '{' + s[:server_name] + ': ' + s[:url] + '}'}.join(', ')}")
|
128
|
+
end
|
129
|
+
|
130
|
+
pair[:server_name]
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
2
|
+
# you may not use this file except in compliance with the License.
|
3
|
+
# You may obtain a copy of the License at
|
4
|
+
#
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10
|
+
# See the License for the specific language governing permissions and
|
11
|
+
# limitations under the License.
|
12
|
+
|
13
|
+
require 'git-process/git_process_error'
|
14
|
+
|
15
|
+
module GitProc
|
16
|
+
|
17
|
+
class PullRequestError < GitProcessError
|
18
|
+
|
19
|
+
def initialize(msg)
|
20
|
+
super(msg)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
2
|
+
# you may not use this file except in compliance with the License.
|
3
|
+
# You may obtain a copy of the License at
|
4
|
+
#
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10
|
+
# See the License for the specific language governing permissions and
|
11
|
+
# limitations under the License.
|
12
|
+
|
13
|
+
require 'git-process/git_process'
|
14
|
+
require 'git-process/git_rebase_error'
|
15
|
+
require 'git-process/git_process_error'
|
16
|
+
require 'git-process/parked_changes_error'
|
17
|
+
require 'git-process/uncommitted_changes_error'
|
18
|
+
require 'git-process/github_pull_request'
|
19
|
+
require 'git-process/pull_request'
|
20
|
+
require 'git-process/syncer'
|
21
|
+
|
22
|
+
|
23
|
+
module GitProc
|
24
|
+
|
25
|
+
class RebaseToMaster < Process
|
26
|
+
|
27
|
+
def initialize(dir, opts)
|
28
|
+
@keep = opts[:keep]
|
29
|
+
@pr_number = opts[:prNumber]
|
30
|
+
@user = opts[:user]
|
31
|
+
@password = opts[:password]
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def verify_preconditions
|
37
|
+
super
|
38
|
+
|
39
|
+
raise UncommittedChangesError.new unless gitlib.status.clean?
|
40
|
+
raise ParkedChangesError.new(gitlib) if is_parked?
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def runner
|
45
|
+
if remote.exists?
|
46
|
+
gitlib.fetch(remote.name)
|
47
|
+
|
48
|
+
unless @pr_number.nil? or @pr_number.empty?
|
49
|
+
checkout_pull_request
|
50
|
+
end
|
51
|
+
|
52
|
+
Syncer.rebase_sync(gitlib, true)
|
53
|
+
current = gitlib.branches.current.name
|
54
|
+
gitlib.push(remote.name, current, config.master_branch)
|
55
|
+
|
56
|
+
unless @keep
|
57
|
+
close_pull_request
|
58
|
+
remove_feature_branch
|
59
|
+
gitlib.delete_sync_control_file!(current) if gitlib.sync_control_file_exists?(current)
|
60
|
+
end
|
61
|
+
else
|
62
|
+
Syncer.rebase_sync(gitlib, true)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def checkout_pull_request
|
68
|
+
PullRequest.checkout_pull_request(gitlib, @pr_number, remote.name, remote.repo_name, @user, @password, logger)
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def remove_feature_branch
|
73
|
+
mybranches = gitlib.branches
|
74
|
+
|
75
|
+
remote_master = mybranches[remote.master_branch_name]
|
76
|
+
current_branch = mybranches.current
|
77
|
+
logger.debug { "Removing feature branch (#{current_branch})" }
|
78
|
+
|
79
|
+
unless remote_master.contains_all_of(current_branch.name)
|
80
|
+
raise GitProcessError.new("Branch '#{current_branch.name}' has not been merged into '#{remote.master_branch_name}'")
|
81
|
+
end
|
82
|
+
|
83
|
+
parking_branch = mybranches['_parking_']
|
84
|
+
if parking_branch
|
85
|
+
if parking_branch.is_ahead_of(remote_master.name) and
|
86
|
+
!current_branch.contains_all_of(parking_branch.name)
|
87
|
+
|
88
|
+
parking_branch.rename('_parking_OLD_')
|
89
|
+
|
90
|
+
logger.warn { bad_parking_branch_msg }
|
91
|
+
else
|
92
|
+
parking_branch.delete!
|
93
|
+
end
|
94
|
+
end
|
95
|
+
remote_master.checkout_to_new('_parking_', :no_track => true)
|
96
|
+
|
97
|
+
current_branch.delete!(true)
|
98
|
+
if mybranches["#{remote.name}/#{current_branch.name}"]
|
99
|
+
gitlib.push(remote.name, nil, nil, :delete => current_branch.name)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
def close_pull_request
|
105
|
+
pr = GitHub::PullRequest.new(gitlib, remote.name, remote.repo_name)
|
106
|
+
|
107
|
+
# Assume that if we haven't done something that would create the
|
108
|
+
# GitHub auth token, then this likely isn't a GitHub-based repo.
|
109
|
+
# (Or at least the user isn't using pull requests)
|
110
|
+
if pr.configuration.get_config_auth_token
|
111
|
+
begin
|
112
|
+
if @pr_number
|
113
|
+
pr.close(@pr_number)
|
114
|
+
else
|
115
|
+
mybranches = gitlib.branches()
|
116
|
+
pull = pr.find_pull_request(config.master_branch, mybranches.current.name)
|
117
|
+
if pull
|
118
|
+
pr.close(pull[:number])
|
119
|
+
else
|
120
|
+
logger.debug { "There is no pull request for #{mybranches.current.name} against #{config.master_branch}" }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
rescue GitHubService::NoRemoteRepository => exp
|
124
|
+
logger.debug exp.to_s
|
125
|
+
end
|
126
|
+
else
|
127
|
+
logger.debug 'There is no GitHub auth token defined, so not trying to close a pull request.'
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
|
135
|
+
def bad_parking_branch_msg
|
136
|
+
hl = HighLine.new
|
137
|
+
hl.color(
|
138
|
+
"\n***********************************************************************************************\n\n"+
|
139
|
+
"There is an old '_parking_' branch with unacounted changes in it.\n"+
|
140
|
+
"It has been renamed to '_parking_OLD_'.\n"+
|
141
|
+
"Please rename the branch to what the changes are about (`git branch -m _parking_OLD_ my_fb_name`),\n"+
|
142
|
+
" or remove it altogher (`git branch -D _parking_OLD_`).\n\n"+
|
143
|
+
"***********************************************************************************************\n", :red, :bold)
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
2
|
+
# you may not use this file except in compliance with the License.
|
3
|
+
# You may obtain a copy of the License at
|
4
|
+
#
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10
|
+
# See the License for the specific language governing permissions and
|
11
|
+
# limitations under the License.
|
12
|
+
|
13
|
+
require 'git-process/git_process'
|
14
|
+
require 'git-process/parked_changes_error'
|
15
|
+
require 'git-process/syncer'
|
16
|
+
require 'git-process/changed_file_helper'
|
17
|
+
|
18
|
+
|
19
|
+
module GitProc
|
20
|
+
|
21
|
+
class Sync < Process
|
22
|
+
|
23
|
+
def initialize(base, opts)
|
24
|
+
super
|
25
|
+
|
26
|
+
@opts = opts
|
27
|
+
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
#noinspection RubyControlFlowConversionInspection
|
33
|
+
def verify_preconditions
|
34
|
+
super
|
35
|
+
|
36
|
+
if not gitlib.status.clean?
|
37
|
+
change_file_helper.offer_to_help_uncommitted_changes
|
38
|
+
end
|
39
|
+
|
40
|
+
raise ParkedChangesError.new(self) if gitlib.is_parked?
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def cleanup
|
45
|
+
gitlib.stash_pop if @stash_pushed
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def runner
|
50
|
+
GitProc::Syncer.do_sync(gitlib, @opts)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|