git-process 1.0.11 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +37 -9
- data/Gemfile +2 -2
- data/Gemfile.lock +17 -17
- data/README.md +14 -7
- data/bin/git-new-fb +10 -2
- data/bin/git-pull-request +30 -6
- data/bin/git-sync +5 -2
- data/bin/git-to-master +62 -11
- data/git-process.gemspec +15 -15
- data/lib/git-process/abstract_error_builder.rb +0 -3
- data/lib/git-process/changed_file_helper.rb +30 -24
- data/lib/git-process/git_abstract_merge_error_builder.rb +31 -11
- data/lib/git-process/git_branch.rb +5 -0
- data/lib/git-process/git_config.rb +153 -0
- data/lib/git-process/git_lib.rb +212 -164
- data/lib/git-process/git_logger.rb +84 -0
- data/lib/git-process/git_merge_error.rb +3 -14
- data/lib/git-process/git_process.rb +44 -73
- data/lib/git-process/git_process_options.rb +6 -6
- data/lib/git-process/git_rebase_error.rb +4 -13
- data/lib/git-process/git_remote.rb +254 -0
- data/lib/git-process/github_configuration.rb +298 -0
- data/lib/git-process/github_pull_request.rb +65 -27
- data/lib/git-process/new_fb.rb +14 -4
- data/lib/git-process/parked_changes_error.rb +1 -1
- data/lib/git-process/pull_request.rb +100 -13
- data/lib/git-process/pull_request_error.rb +25 -0
- data/lib/git-process/rebase_to_master.rb +47 -27
- data/lib/git-process/sync.rb +48 -33
- data/lib/git-process/uncommitted_changes_error.rb +1 -1
- data/lib/git-process/version.rb +2 -2
- data/spec/GitRepoHelper.rb +48 -25
- data/spec/changed_file_helper_spec.rb +39 -58
- data/spec/git_abstract_merge_error_builder_spec.rb +42 -33
- data/spec/git_branch_spec.rb +30 -30
- data/spec/git_config_spec.rb +45 -0
- data/spec/git_lib_spec.rb +103 -122
- data/spec/git_logger_spec.rb +66 -0
- data/spec/git_process_spec.rb +81 -81
- data/spec/git_remote_spec.rb +188 -0
- data/spec/git_status_spec.rb +36 -36
- data/spec/github_configuration_spec.rb +152 -0
- data/spec/github_pull_request_spec.rb +39 -35
- data/spec/github_test_helper.rb +49 -0
- data/spec/new_fb_spec.rb +65 -24
- data/spec/pull_request_helper.rb +94 -0
- data/spec/pull_request_spec.rb +128 -0
- data/spec/rebase_to_master_spec.rb +241 -145
- data/spec/spec_helper.rb +20 -0
- data/spec/sync_spec.rb +115 -109
- metadata +34 -20
- data/lib/git-process/github_client.rb +0 -83
- data/lib/git-process/github_service.rb +0 -174
- data/spec/github_service_spec.rb +0 -211
@@ -28,7 +28,7 @@ module GitProc
|
|
28
28
|
|
29
29
|
|
30
30
|
def human_message
|
31
|
-
"You made your changes on the the '_parking_' branch instead of a feature branch.\n"+
|
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
32
|
end
|
33
33
|
|
34
34
|
|
@@ -12,36 +12,123 @@
|
|
12
12
|
|
13
13
|
require 'git-process/git_process'
|
14
14
|
require 'git-process/github_pull_request'
|
15
|
+
require 'git-process/pull_request_error'
|
15
16
|
require 'highline/import'
|
16
17
|
|
17
18
|
|
18
19
|
module GitProc
|
19
20
|
|
20
21
|
class PullRequest < Process
|
21
|
-
include GitLib
|
22
|
-
|
23
22
|
|
24
23
|
def initialize(dir, opts)
|
25
24
|
super
|
26
|
-
|
25
|
+
@base_branch = opts[:base_branch]
|
26
|
+
@head_branch = opts[:head_branch]
|
27
|
+
@_repo_name = opts[:repo_name]
|
28
|
+
@_remote_name = opts[:server]
|
29
|
+
@pr_number = opts[:prNumber]
|
27
30
|
@title = opts[:title]
|
28
|
-
@
|
29
|
-
@head_branch = opts[:head_branch] || current_branch
|
30
|
-
@repo_name = opts[:repo_name] || repo_name()
|
31
|
-
@title = opts[:title] || current_branch
|
32
|
-
@description = opts[:description] || ''
|
31
|
+
@description = opts[:description]
|
33
32
|
@user = opts[:user]
|
34
33
|
@password = opts[:password]
|
35
34
|
end
|
36
35
|
|
37
36
|
|
38
37
|
def runner
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
if @pr_number.nil? or @pr_number.empty?
|
39
|
+
pr = create_pull_request
|
40
|
+
logger.info { "Created pull request at #{pr.html_url}" }
|
41
|
+
else
|
42
|
+
checkout_pull_request
|
43
|
+
end
|
43
44
|
end
|
44
45
|
|
45
|
-
end
|
46
46
|
|
47
|
+
def create_pull_request_client(remote_name, repo_name)
|
48
|
+
PullRequest.create_pull_request_client(self, remote_name, repo_name, @user, @password)
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def create_pull_request
|
53
|
+
current_branch = gitlib.branches.current.name
|
54
|
+
base_branch = @base_branch || config.master_branch
|
55
|
+
head_branch = @head_branch || current_branch
|
56
|
+
title = @title || current_branch
|
57
|
+
description = @description || ''
|
58
|
+
|
59
|
+
PullRequest.create_pull_request(gitlib, remote.name, remote.name, remote.repo_name, current_branch, base_branch, head_branch, title, description, logger, @user, @password)
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
def checkout_pull_request
|
64
|
+
PullRequest.checkout_pull_request(gitlib, @pr_number, remote.name, remote.repo_name, @user, @password, logger)
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
class << self
|
69
|
+
|
70
|
+
def create_pull_request_client(lib, remote_name, repo_name, username, password)
|
71
|
+
GitHub::PullRequest.new(lib, remote_name, repo_name, {:user => username, :password => password})
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def create_pull_request(lib, server_name, remote_name, repo_name, current_branch, base_branch, head_branch, title, description, logger, username, password)
|
76
|
+
if base_branch == head_branch
|
77
|
+
raise PullRequestError.new("Can not create a pull request where the base branch and head branch are the same: #{base_branch}")
|
78
|
+
end
|
79
|
+
|
80
|
+
lib.push(server_name, current_branch, current_branch, :force => false)
|
81
|
+
pr = create_pull_request_client(lib, remote_name, repo_name, username, password)
|
82
|
+
pr.create(base_branch, head_branch, title, description)
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def checkout_pull_request(lib, pr_number, remote_name, repo_name, username, password, logger)
|
87
|
+
logger.info { 'Getting #pr_number' }
|
88
|
+
|
89
|
+
lib.fetch(remote_name)
|
90
|
+
|
91
|
+
pr = create_pull_request_client(lib, remote_name, repo_name, username, password)
|
92
|
+
json = pr.pull_request(pr_number)
|
93
|
+
head_branch_name = json.head.ref
|
94
|
+
base_branch_name = json.base.ref
|
95
|
+
|
96
|
+
remote_head_server_name = match_remote_to_pr_remote(lib, json.head.repo.ssh_url)
|
97
|
+
remote_base_server_name = match_remote_to_pr_remote(lib, json.base.repo.ssh_url)
|
98
|
+
lib.checkout(head_branch_name, :new_branch => "#{remote_head_server_name}/#{head_branch_name}")
|
99
|
+
lib.branch(head_branch_name, :upstream => "#{remote_base_server_name}/#{base_branch_name}")
|
100
|
+
#logger.info(json.to_hash)
|
101
|
+
|
102
|
+
lib.fetch(remote_base_server_name) if remote_head_server_name != remote_base_server_name
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def match_remote_to_pr_remote(lib, pr_remote)
|
107
|
+
pr_url = lib.remote.expanded_url(nil, pr_remote)
|
108
|
+
servers = lib.remote.remote_names
|
109
|
+
server_urls = servers.collect { |s| {:server_name => s, :url => lib.remote.expanded_url(s)} }
|
110
|
+
|
111
|
+
pair = server_urls.find do |su|
|
112
|
+
url = su[:url]
|
113
|
+
uri = URI.parse(url)
|
114
|
+
host = uri.host
|
115
|
+
path = uri.path
|
116
|
+
|
117
|
+
pr_uri = URI.parse(lib.remote.expanded_url(nil, pr_url))
|
118
|
+
pr_host = pr_uri.host
|
119
|
+
pr_path = pr_uri.path
|
120
|
+
|
121
|
+
pr_host == host and pr_path == path
|
122
|
+
end
|
123
|
+
|
124
|
+
if pair.nil?
|
125
|
+
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(', ')}")
|
126
|
+
end
|
127
|
+
|
128
|
+
pair[:server_name]
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
47
134
|
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
|
@@ -16,6 +16,7 @@ require 'git-process/git_process_error'
|
|
16
16
|
require 'git-process/parked_changes_error'
|
17
17
|
require 'git-process/uncommitted_changes_error'
|
18
18
|
require 'git-process/github_pull_request'
|
19
|
+
require 'git-process/pull_request'
|
19
20
|
|
20
21
|
|
21
22
|
module GitProc
|
@@ -25,6 +26,9 @@ module GitProc
|
|
25
26
|
def initialize(dir, opts)
|
26
27
|
@keep = opts[:keep]
|
27
28
|
@interactive = opts[:interactive]
|
29
|
+
@pr_number = opts[:prNumber]
|
30
|
+
@user = opts[:user]
|
31
|
+
@password = opts[:password]
|
28
32
|
super
|
29
33
|
end
|
30
34
|
|
@@ -32,37 +36,48 @@ module GitProc
|
|
32
36
|
def verify_preconditions
|
33
37
|
super
|
34
38
|
|
35
|
-
raise UncommittedChangesError.new unless status.clean?
|
36
|
-
raise ParkedChangesError.new(
|
39
|
+
raise UncommittedChangesError.new unless gitlib.status.clean?
|
40
|
+
raise ParkedChangesError.new(gitlib) if is_parked?
|
37
41
|
end
|
38
42
|
|
39
43
|
|
40
44
|
def runner
|
41
|
-
if
|
42
|
-
fetch(
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
+
proc_rebase(config.integration_branch)
|
53
|
+
proc_rebase(config.integration_branch, :interactive => true) if @interactive
|
54
|
+
current = gitlib.branches.current.name
|
55
|
+
gitlib.push(remote.name, current, config.master_branch)
|
46
56
|
|
47
57
|
unless @keep
|
48
58
|
close_pull_request
|
49
59
|
remove_feature_branch
|
50
60
|
end
|
51
61
|
else
|
52
|
-
proc_rebase(integration_branch)
|
62
|
+
proc_rebase(config.integration_branch)
|
53
63
|
end
|
54
64
|
end
|
55
65
|
|
56
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
|
+
|
57
72
|
def remove_feature_branch
|
58
|
-
mybranches = branches
|
73
|
+
mybranches = gitlib.branches
|
59
74
|
|
60
|
-
remote_master = mybranches[
|
75
|
+
remote_master = mybranches[remote.master_branch_name]
|
61
76
|
current_branch = mybranches.current
|
62
77
|
logger.debug { "Removing feature branch (#{current_branch})" }
|
63
78
|
|
64
79
|
unless remote_master.contains_all_of(current_branch.name)
|
65
|
-
raise GitProcessError.new("Branch '#{current_branch.name}' has not been merged into '#{
|
80
|
+
raise GitProcessError.new("Branch '#{current_branch.name}' has not been merged into '#{remote.master_branch_name}'")
|
66
81
|
end
|
67
82
|
|
68
83
|
parking_branch = mybranches['_parking_']
|
@@ -80,32 +95,36 @@ module GitProc
|
|
80
95
|
remote_master.checkout_to_new('_parking_', :no_track => true)
|
81
96
|
|
82
97
|
current_branch.delete!(true)
|
83
|
-
if mybranches["#{
|
84
|
-
push(
|
98
|
+
if mybranches["#{remote.name}/#{current_branch.name}"]
|
99
|
+
gitlib.push(remote.name, nil, nil, :delete => current_branch.name)
|
85
100
|
end
|
86
101
|
end
|
87
102
|
|
88
103
|
|
89
104
|
def close_pull_request
|
90
|
-
pr = GitHub::PullRequest.new(
|
105
|
+
pr = GitHub::PullRequest.new(gitlib, remote.name, remote.repo_name)
|
91
106
|
|
92
107
|
# Assume that if we haven't done something that would create the
|
93
108
|
# GitHub auth token, then this likely isn't a GitHub-based repo.
|
94
109
|
# (Or at least the user isn't using pull requests)
|
95
|
-
if pr.
|
110
|
+
if pr.configuration.get_config_auth_token
|
96
111
|
begin
|
97
|
-
|
98
|
-
|
99
|
-
if pull
|
100
|
-
pr.close(pull[:number])
|
112
|
+
if @pr_number
|
113
|
+
pr.close(@pr_number)
|
101
114
|
else
|
102
|
-
|
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
|
103
122
|
end
|
104
123
|
rescue GitHubService::NoRemoteRepository => exp
|
105
124
|
logger.debug exp.to_s
|
106
125
|
end
|
107
126
|
else
|
108
|
-
logger.debug
|
127
|
+
logger.debug 'There is no GitHub auth token defined, so not trying to close a pull request.'
|
109
128
|
end
|
110
129
|
end
|
111
130
|
|
@@ -115,12 +134,13 @@ module GitProc
|
|
115
134
|
|
116
135
|
def bad_parking_branch_msg
|
117
136
|
hl = HighLine.new
|
118
|
-
hl.color(
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
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)
|
124
144
|
end
|
125
145
|
|
126
146
|
end
|
data/lib/git-process/sync.rb
CHANGED
@@ -20,20 +20,22 @@ require 'git-process/changed_file_helper'
|
|
20
20
|
module GitProc
|
21
21
|
|
22
22
|
class Sync < Process
|
23
|
-
include ChangeFileHelper
|
24
23
|
|
25
|
-
|
26
|
-
def initialize(dir, opts)
|
24
|
+
def initialize(base, opts)
|
27
25
|
if !opts[:merge].nil? and opts[:merge] == opts[:rebase]
|
28
26
|
raise ArgumentError.new(":merge = #{opts[:merge]} and :rebase = #{opts[:rebase]}")
|
29
27
|
end
|
30
28
|
|
31
|
-
raise ArgumentError.new(
|
29
|
+
raise ArgumentError.new(':rebase is not set') if opts[:rebase].nil?
|
32
30
|
|
33
31
|
@do_rebase = opts[:rebase]
|
34
32
|
@force = opts[:force]
|
35
33
|
@local = opts[:local]
|
34
|
+
|
36
35
|
super
|
36
|
+
|
37
|
+
@change_file_helper = ChangeFileHelper.new(gitlib)
|
38
|
+
self
|
37
39
|
end
|
38
40
|
|
39
41
|
|
@@ -41,8 +43,8 @@ module GitProc
|
|
41
43
|
def verify_preconditions
|
42
44
|
super
|
43
45
|
|
44
|
-
if not status.clean?
|
45
|
-
offer_to_help_uncommitted_changes
|
46
|
+
if not gitlib.status.clean?
|
47
|
+
@change_file_helper.offer_to_help_uncommitted_changes
|
46
48
|
end
|
47
49
|
|
48
50
|
raise ParkedChangesError.new(self) if is_parked?
|
@@ -50,37 +52,35 @@ module GitProc
|
|
50
52
|
|
51
53
|
|
52
54
|
def cleanup
|
53
|
-
stash_pop if @stash_pushed
|
55
|
+
gitlib.stash_pop if @stash_pushed
|
54
56
|
end
|
55
57
|
|
56
58
|
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
new_sha = rev_parse(@remote_branch) rescue ''
|
59
|
+
def remote_branch_sha
|
60
|
+
gitlib.rev_parse(@remote_branch) rescue ''
|
61
|
+
end
|
61
62
|
|
62
|
-
|
63
|
+
|
64
|
+
def current_branch
|
65
|
+
@current_branch ||= gitlib.branches.current
|
63
66
|
end
|
64
67
|
|
65
68
|
|
66
69
|
def runner
|
67
|
-
@
|
68
|
-
@remote_branch ||= "#{server_name}/#@current_branch"
|
70
|
+
@remote_branch ||= "#{remote.name}/#{current_branch}"
|
69
71
|
|
70
72
|
# if the remote branch has changed, merge those changes in before
|
71
73
|
# doing anything with the integration branch
|
72
|
-
if remote_has_changed
|
74
|
+
if remote_has_changed?
|
73
75
|
logger.info('There have been changes on this remote branch, so will merge them in.')
|
74
76
|
proc_merge(@remote_branch)
|
75
77
|
end
|
76
78
|
|
77
|
-
|
78
|
-
|
79
|
-
if @do_rebase
|
79
|
+
if do_rebase?
|
80
80
|
@force = true
|
81
|
-
proc_rebase(integration_branch)
|
81
|
+
proc_rebase(config.integration_branch)
|
82
82
|
else
|
83
|
-
proc_merge(integration_branch)
|
83
|
+
proc_merge(config.integration_branch)
|
84
84
|
end
|
85
85
|
|
86
86
|
push_to_server
|
@@ -89,28 +89,43 @@ module GitProc
|
|
89
89
|
|
90
90
|
private
|
91
91
|
|
92
|
+
|
93
|
+
def remote_has_changed?
|
94
|
+
old_sha = remote_branch_sha
|
95
|
+
fetch_remote_changes
|
96
|
+
new_sha = remote_branch_sha
|
97
|
+
|
98
|
+
old_sha != new_sha
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
def do_rebase?
|
103
|
+
@do_rebase ||= config['gitProcess.defaultRebaseSync'].to_boolean
|
104
|
+
end
|
105
|
+
|
106
|
+
|
92
107
|
def push_to_server
|
93
108
|
if @local
|
94
|
-
logger.debug(
|
95
|
-
elsif not has_a_remote?
|
96
|
-
logger.debug(
|
97
|
-
elsif @current_branch == master_branch
|
98
|
-
logger.warn(
|
109
|
+
logger.debug('Not pushing to the server because the user selected local-only.')
|
110
|
+
elsif not gitlib.has_a_remote?
|
111
|
+
logger.debug('Not pushing to the server because there is no remote.')
|
112
|
+
elsif @current_branch == config.master_branch
|
113
|
+
logger.warn('Not pushing to the server because the current branch is the mainline branch.')
|
99
114
|
else
|
100
|
-
|
115
|
+
handle_remote_changed
|
101
116
|
|
102
|
-
|
103
|
-
|
104
|
-
push(server_name, @current_branch, @current_branch, :force => @force)
|
117
|
+
gitlib.push(remote.name, @current_branch, @current_branch, :force => @force)
|
105
118
|
end
|
106
119
|
end
|
107
120
|
|
108
121
|
|
109
|
-
def handle_remote_changed
|
110
|
-
|
111
|
-
|
122
|
+
def handle_remote_changed
|
123
|
+
old_sha = remote_branch_sha
|
124
|
+
fetch_remote_changes
|
125
|
+
new_sha = remote_branch_sha
|
126
|
+
|
112
127
|
unless old_sha == new_sha
|
113
|
-
logger.warn("'
|
128
|
+
logger.warn("'#{@current_branch}' changed on '#{config.server_name}'"+
|
114
129
|
" [#{old_sha[0..5]}->#{new_sha[0..5]}]; trying sync again.")
|
115
130
|
runner # try again
|
116
131
|
end
|