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.
Files changed (54) hide show
  1. data/CHANGELOG.md +37 -9
  2. data/Gemfile +2 -2
  3. data/Gemfile.lock +17 -17
  4. data/README.md +14 -7
  5. data/bin/git-new-fb +10 -2
  6. data/bin/git-pull-request +30 -6
  7. data/bin/git-sync +5 -2
  8. data/bin/git-to-master +62 -11
  9. data/git-process.gemspec +15 -15
  10. data/lib/git-process/abstract_error_builder.rb +0 -3
  11. data/lib/git-process/changed_file_helper.rb +30 -24
  12. data/lib/git-process/git_abstract_merge_error_builder.rb +31 -11
  13. data/lib/git-process/git_branch.rb +5 -0
  14. data/lib/git-process/git_config.rb +153 -0
  15. data/lib/git-process/git_lib.rb +212 -164
  16. data/lib/git-process/git_logger.rb +84 -0
  17. data/lib/git-process/git_merge_error.rb +3 -14
  18. data/lib/git-process/git_process.rb +44 -73
  19. data/lib/git-process/git_process_options.rb +6 -6
  20. data/lib/git-process/git_rebase_error.rb +4 -13
  21. data/lib/git-process/git_remote.rb +254 -0
  22. data/lib/git-process/github_configuration.rb +298 -0
  23. data/lib/git-process/github_pull_request.rb +65 -27
  24. data/lib/git-process/new_fb.rb +14 -4
  25. data/lib/git-process/parked_changes_error.rb +1 -1
  26. data/lib/git-process/pull_request.rb +100 -13
  27. data/lib/git-process/pull_request_error.rb +25 -0
  28. data/lib/git-process/rebase_to_master.rb +47 -27
  29. data/lib/git-process/sync.rb +48 -33
  30. data/lib/git-process/uncommitted_changes_error.rb +1 -1
  31. data/lib/git-process/version.rb +2 -2
  32. data/spec/GitRepoHelper.rb +48 -25
  33. data/spec/changed_file_helper_spec.rb +39 -58
  34. data/spec/git_abstract_merge_error_builder_spec.rb +42 -33
  35. data/spec/git_branch_spec.rb +30 -30
  36. data/spec/git_config_spec.rb +45 -0
  37. data/spec/git_lib_spec.rb +103 -122
  38. data/spec/git_logger_spec.rb +66 -0
  39. data/spec/git_process_spec.rb +81 -81
  40. data/spec/git_remote_spec.rb +188 -0
  41. data/spec/git_status_spec.rb +36 -36
  42. data/spec/github_configuration_spec.rb +152 -0
  43. data/spec/github_pull_request_spec.rb +39 -35
  44. data/spec/github_test_helper.rb +49 -0
  45. data/spec/new_fb_spec.rb +65 -24
  46. data/spec/pull_request_helper.rb +94 -0
  47. data/spec/pull_request_spec.rb +128 -0
  48. data/spec/rebase_to_master_spec.rb +241 -145
  49. data/spec/spec_helper.rb +20 -0
  50. data/spec/sync_spec.rb +115 -109
  51. metadata +34 -20
  52. data/lib/git-process/github_client.rb +0 -83
  53. data/lib/git-process/github_service.rb +0 -174
  54. data/spec/github_service_spec.rb +0 -211
@@ -0,0 +1,84 @@
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 'logger'
14
+
15
+ module GitProc
16
+
17
+ #
18
+ # Provides a Logger for Git commands
19
+ #
20
+ class GitLogger
21
+
22
+ DEBUG = Logger::DEBUG
23
+ INFO = Logger::INFO
24
+ WARN = Logger::WARN
25
+ ERROR = Logger::ERROR
26
+
27
+
28
+ def initialize(log_level = nil, out = STDOUT)
29
+ if out.nil?
30
+ @logger = ::Logger.new(RUBY_PLATFORM =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
31
+ else
32
+ @logger = ::Logger.new(out)
33
+ end
34
+ @logger.level = log_level.nil? ? GitLogger::WARN : log_level
35
+ @logger.datetime_format = '%Y-%m-%d %H:%M:%S'
36
+ @logger.formatter = proc do |_, _, _, msg|
37
+ "#{msg}\n"
38
+ end
39
+ end
40
+
41
+
42
+ def level
43
+ @logger.level
44
+ end
45
+
46
+
47
+ def debug(msg = nil, &block)
48
+ if msg.nil?
49
+ @logger.debug(&block)
50
+ else
51
+ @logger.debug(msg)
52
+ end
53
+ end
54
+
55
+
56
+ def info(msg = nil, &block)
57
+ if msg.nil?
58
+ @logger.info(&block)
59
+ else
60
+ @logger.info(msg)
61
+ end
62
+ end
63
+
64
+
65
+ def warn(msg = nil, &block)
66
+ if msg.nil?
67
+ @logger.send(:warn, &block)
68
+ else
69
+ @logger.send(:warn, msg)
70
+ end
71
+ end
72
+
73
+
74
+ def error(msg = nil, &block)
75
+ if msg.nil?
76
+ @logger.error(&block)
77
+ else
78
+ @logger.error(msg)
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+ end
@@ -15,25 +15,14 @@ require 'git-process/git_abstract_merge_error_builder'
15
15
  module GitProc
16
16
 
17
17
  class MergeError < GitProcessError
18
- include AbstractMergeErrorBuilder
19
18
 
20
- attr_reader :error_message, :lib
21
-
22
-
23
- def initialize(merge_error_message, lib)
24
- @lib = lib
25
- @error_message = merge_error_message
26
-
27
- msg = build_message
19
+ def initialize(merge_error_message, gitlib)
20
+ @error_builder = GitProc::AbstractMergeErrorBuilder.new(gitlib, merge_error_message, 'git commit')
21
+ msg = error_builder.build_message
28
22
 
29
23
  super(msg)
30
24
  end
31
25
 
32
-
33
- def continue_command
34
- 'git commit'
35
- end
36
-
37
26
  end
38
27
 
39
28
  end
@@ -19,13 +19,15 @@ require 'highline/import'
19
19
  module GitProc
20
20
 
21
21
  class Process
22
- include GitLib
23
22
 
23
+ # @param [GitLib] gitlib
24
+ def initialize(gitlib, opts = {})
25
+ @gitlib = gitlib
26
+ end
24
27
 
25
- def initialize(dir, opts = {})
26
- @log_level = Process.log_level(opts)
27
28
 
28
- set_workdir(dir)
29
+ def gitlib
30
+ @gitlib
29
31
  end
30
32
 
31
33
 
@@ -48,51 +50,30 @@ module GitProc
48
50
  end
49
51
 
50
52
 
51
- def set_workdir(dir)
52
- unless dir.nil?
53
- @workdir = find_workdir(dir)
54
- if @workdir.nil?
55
- @workdir = dir
56
- logger.info { "Initializing new repository at #{workdir}" }
57
- command(:init)
58
- else
59
- logger.debug { "Opening existing repository at #{workdir}" }
60
- end
61
- else
62
- logger.debug "Process dir is nil"
63
- end
64
- end
65
-
66
-
67
53
  def workdir
68
- @workdir
54
+ gitlib.workdir
69
55
  end
70
56
 
71
57
 
72
- def log_level
73
- @log_level
58
+ def logger
59
+ gitlib.logger
74
60
  end
75
61
 
76
62
 
77
- def log_level=(ll)
78
- @log_level = ll
63
+ def config
64
+ gitlib.config
79
65
  end
80
66
 
81
67
 
82
- def remote_master_branch
83
- "#{server_name}/#{master_branch}"
84
- end
85
-
86
-
87
- def integration_branch
88
- has_a_remote? ? remote_master_branch : master_branch
68
+ def remote
69
+ gitlib.remote
89
70
  end
90
71
 
91
72
 
92
73
  def verify_preconditions
93
- if should_remove_master
74
+ if should_remove_master?
94
75
  if ask_about_removing_master
95
- branches[master_branch].delete!
76
+ delete_master_branch!
96
77
  end
97
78
  end
98
79
  end
@@ -103,46 +84,47 @@ module GitProc
103
84
  end
104
85
 
105
86
 
106
- def should_remove_master
107
- my_branches = branches()
108
- has_a_remote? and
109
- my_branches.include?(master_branch) and
110
- my_branches.current.name != master_branch and
111
- !keep_local_integration_branch? and
112
- my_branches[integration_branch].contains_all_of(master_branch)
87
+ def fetch_remote_changes(remote_name = nil)
88
+ if remote.exists?
89
+ gitlib.fetch(remote_name || remote.name)
90
+ else
91
+ logger.debug 'Can not fetch latest changes because there is no remote defined'
92
+ end
113
93
  end
114
94
 
115
95
 
116
- def keep_local_integration_branch?
117
- keep_local_integration_branch_config_value.to_boolean
96
+ def is_parked?
97
+ mybranches = gitlib.branches
98
+ mybranches.parking == mybranches.current
118
99
  end
119
100
 
120
101
 
121
- def Process.log_level(opts)
122
- if opts[:log_level]
123
- opts[:log_level]
124
- elsif opts[:quiet]
125
- Logger::ERROR
126
- elsif opts[:verbose]
127
- Logger::DEBUG
128
- else
129
- Logger::INFO
130
- end
102
+ private
103
+
104
+
105
+ def delete_master_branch!
106
+ gitlib.branches[config.master_branch].delete!
131
107
  end
132
108
 
133
109
 
134
- def is_parked?
135
- mybranches = branches
136
- mybranches.parking == mybranches.current
110
+ def should_remove_master?
111
+ my_branches = gitlib.branches()
112
+ gitlib.has_a_remote? and
113
+ my_branches.include?(config.master_branch) and
114
+ my_branches.current.name != config.master_branch and
115
+ !keep_local_integration_branch? and
116
+ my_branches[config.integration_branch].contains_all_of(config.master_branch)
137
117
  end
138
118
 
139
119
 
140
- private
120
+ def keep_local_integration_branch?
121
+ keep_local_integration_branch_config_value.to_boolean
122
+ end
141
123
 
142
124
 
143
125
  #noinspection RubyInstanceMethodNamingConvention
144
126
  def keep_local_integration_branch_config_value
145
- config('gitProcess.keepLocalIntegrationBranch')
127
+ gitlib.config['gitProcess.keepLocalIntegrationBranch']
146
128
  end
147
129
 
148
130
 
@@ -163,31 +145,20 @@ module GitProc
163
145
  end
164
146
 
165
147
 
166
- def find_workdir(dir)
167
- if dir == File::SEPARATOR
168
- nil
169
- elsif File.directory?(File.join(dir, '.git'))
170
- dir
171
- else
172
- find_workdir(File.expand_path("#{dir}/.."))
173
- end
174
- end
175
-
176
-
177
148
  def proc_rebase(base, opts = {})
178
149
  begin
179
- rebase(base, opts)
150
+ gitlib.rebase(base, opts)
180
151
  rescue GitExecuteError => rebase_error
181
- raise RebaseError.new(rebase_error.message, self)
152
+ raise RebaseError.new(rebase_error.message, gitlib)
182
153
  end
183
154
  end
184
155
 
185
156
 
186
157
  def proc_merge(base)
187
158
  begin
188
- merge(base)
159
+ gitlib.merge(base)
189
160
  rescue GitExecuteError => merge_error
190
- raise MergeError.new(merge_error.message, self)
161
+ raise MergeError.new(merge_error.message, gitlib)
191
162
  end
192
163
  end
193
164
 
@@ -53,18 +53,18 @@ module GitProc
53
53
 
54
54
 
55
55
  def standard_opts(parser)
56
- parser.opt :info, "Informational messages; show the major things this is doing", :default => true, :short => :none
57
- parser.opt :quiet, "Quiet messages; only show errors", :short => :q
58
- parser.opt :verbose, "Verbose messages; show lots of details on what this is doing", :short => :v
56
+ parser.opt :info, 'Informational messages; show the major things this is doing', :default => true, :short => :none
57
+ parser.opt :quiet, 'Quiet messages; only show errors', :short => :q
58
+ parser.opt :verbose, 'Verbose messages; show lots of details on what this is doing', :short => :v
59
59
  parser.opt :version, "Print version (#{GitProc::Version::STRING}) and exit", :short => :none
60
- parser.opt :help, "Show this message", :short => :h
60
+ parser.opt :help, 'Show this message', :short => :h
61
61
 
62
62
  parser.conflicts :verbose, :info, :quiet
63
63
  end
64
64
 
65
65
 
66
66
  def summary
67
- "Default summary"
67
+ 'Default summary'
68
68
  end
69
69
 
70
70
 
@@ -74,7 +74,7 @@ module GitProc
74
74
 
75
75
 
76
76
  def description
77
- "Default description"
77
+ 'Default description'
78
78
  end
79
79
 
80
80
 
@@ -15,25 +15,16 @@ require 'git-process/git_abstract_merge_error_builder'
15
15
  module GitProc
16
16
 
17
17
  class RebaseError < GitProcessError
18
- include GitProc::AbstractMergeErrorBuilder
18
+ attr_reader :error_builder
19
19
 
20
- attr_reader :error_message, :lib
21
20
 
22
-
23
- def initialize(rebase_error_message, lib)
24
- @lib = lib
25
- @error_message = rebase_error_message
26
-
27
- msg = build_message
21
+ def initialize(rebase_error_message, gitlib)
22
+ @error_builder = GitProc::AbstractMergeErrorBuilder.new(gitlib, rebase_error_message, 'git rebase --continue')
23
+ msg = error_builder.build_message
28
24
 
29
25
  super(msg)
30
26
  end
31
27
 
32
-
33
- def continue_command
34
- 'git rebase --continue'
35
- end
36
-
37
28
  end
38
29
 
39
30
  end
@@ -0,0 +1,254 @@
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_config'
14
+ #require 'git-process/git_branches'
15
+ #require 'git-process/git_status'
16
+ #require 'git-process/git_process_error'
17
+
18
+
19
+ class String
20
+
21
+ def to_boolean
22
+ return false if self == false || self.nil? || self =~ (/(false|f|no|n|0)$/i)
23
+ return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
24
+ raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
25
+ end
26
+
27
+ end
28
+
29
+
30
+ class NilClass
31
+ def to_boolean
32
+ false
33
+ end
34
+ end
35
+
36
+
37
+ module GitProc
38
+
39
+ #
40
+ # Git Remote configuration
41
+ #
42
+ class GitRemote
43
+
44
+ # @param [GitProc::GitConfig] gitconfig
45
+ def initialize(gitconfig)
46
+ @gitconfig = gitconfig
47
+ end
48
+
49
+
50
+ # @return [#info, #warn, #debug, #error]
51
+ def logger
52
+ @logger ||= @gitconfig.logger
53
+ end
54
+
55
+
56
+ # @return [GitProc::GitConfig]
57
+ def config
58
+ @gitconfig
59
+ end
60
+
61
+
62
+ # @deprecated
63
+ # TODO: Remove
64
+ def server_name
65
+ @server_name ||= self.remote_name
66
+ end
67
+
68
+
69
+ # @return [Boolean] does this have a remote defined?
70
+ def exists?
71
+ if @has_remote.nil?
72
+ @has_remote = (config.gitlib.command(:remote) != '')
73
+ end
74
+ logger.debug { "Does a remote exist? #{@has_remote}" }
75
+ @has_remote
76
+ end
77
+
78
+
79
+ def repo_name
80
+ unless @repo_name
81
+ url = config["remote.#{name}.url"]
82
+ raise GitProcessError.new("There is no #{name} url set up.") if url.nil? or url.empty?
83
+ @repo_name = url.sub(/^.*:(.*?)(.git)?$/, '\1')
84
+ end
85
+ @repo_name
86
+ end
87
+
88
+
89
+ def name
90
+ unless @remote_name
91
+ @remote_name = config['gitProcess.remoteName']
92
+ if @remote_name.nil? or @remote_name.empty?
93
+ remotes = self.remote_names
94
+ if remotes.empty?
95
+ @remote_name = nil
96
+ else
97
+ @remote_name = remotes[0]
98
+ raise '!@remote_name.is_a? String' unless @remote_name.is_a? String
99
+ end
100
+ end
101
+ logger.debug { "Using remote name of '#{@remote_name}'" }
102
+ end
103
+ @remote_name
104
+ end
105
+
106
+
107
+ def master_branch_name
108
+ "#{self.name}/#{config.master_branch}"
109
+ end
110
+
111
+
112
+ def remote_names
113
+ remote_str = config.gitlib.command(:remote, [:show])
114
+ if remote_str.nil? or remote_str.empty?
115
+ []
116
+ else
117
+ remote_str.split(/\n/)
118
+ end
119
+ end
120
+
121
+
122
+ #
123
+ # Expands the git configuration server name to a url.
124
+ #
125
+ # Takes into account further expanding an SSH uri that uses SSH aliasing in .ssh/config
126
+ #
127
+ # @param [String] server_name the git configuration server name; defaults to 'origin'
128
+ #
129
+ # @option opts [String] :ssh_config_file the SSH config file to use; defaults to ~/.ssh/config
130
+ #
131
+ # @return the fully expanded URL; never nil
132
+ #
133
+ # @raise [GitHubService::NoRemoteRepository] there is not a URL set for the server name
134
+ # @raise [URI::InvalidURIError] the retrieved URL does not have a schema
135
+ # @raise [GitHubService::NoRemoteRepository] if could not figure out a host for the retrieved URL
136
+ # @raise [::ArgumentError] if a server name is not provided
137
+ def expanded_url(server_name = 'origin', raw_url = nil, opts = {})
138
+ if raw_url.nil?
139
+ raise ArgumentError.new('Need server_name') unless server_name
140
+
141
+ conf_key = "remote.#{server_name}.url"
142
+ url = config[conf_key]
143
+
144
+ raise GitHubService::NoRemoteRepository.new("There is no value set for '#{conf_key}'") if url.nil? or url.empty?
145
+ else
146
+ raise GitHubService::NoRemoteRepository.new("There is no value set for '#{raw_url}'") if raw_url.nil? or raw_url.empty?
147
+ url = raw_url
148
+ end
149
+
150
+ if /^\S+@/ =~ url
151
+ url.sub(/^(\S+@\S+?):(.*)$/, "ssh://\\1/\\2")
152
+ else
153
+ uri = URI.parse(url)
154
+ host = uri.host
155
+ scheme = uri.scheme
156
+
157
+ raise URI::InvalidURIError.new("Need a scheme in URI: '#{url}'") unless scheme
158
+
159
+ if scheme == 'file'
160
+ url
161
+ elsif host.nil?
162
+ # assume that the 'scheme' is the named configuration in ~/.ssh/config
163
+ rv = GitRemote.hostname_and_user_from_ssh_config(scheme, opts[:ssh_config_file] ||= "#{ENV['HOME']}/.ssh/config")
164
+
165
+ raise GitHubService::NoRemoteRepository.new("Could not determine a host from #{url}") if rv.nil?
166
+
167
+ host = rv[0]
168
+ user = rv[1]
169
+ url.sub(/^\S+:(\S+)$/, "ssh://#{user}@#{host}/\\1")
170
+ else
171
+ url
172
+ end
173
+ end
174
+ end
175
+
176
+
177
+ # @return [boolean]
178
+ def rerere_enabled?
179
+ re = config['rerere.enabled']
180
+ !re.nil? && re.to_boolean
181
+ end
182
+
183
+
184
+ # @return [void]
185
+ def rerere_enabled(re, global = true)
186
+ if global
187
+ config.set_global('rerere.enabled', re)
188
+ else
189
+ config['rerere.enabled'] = re
190
+ end
191
+ end
192
+
193
+
194
+ # @return [boolean]
195
+ def rerere_autoupdate?
196
+ re = config['rerere.autoupdate']
197
+ !re.nil? && re.to_boolean
198
+ end
199
+
200
+
201
+ # @return [void]
202
+ def rerere_autoupdate(re, global = true)
203
+ if global
204
+ config.set_global('rerere.autoupdate', re)
205
+ else
206
+ config['rerere.autoupdate'] = re
207
+ end
208
+ end
209
+
210
+
211
+ # @return [void]
212
+ def add_remote(remote_name, url)
213
+ config.gitlib.command(:remote, ['add', remote_name, url])
214
+ end
215
+
216
+
217
+ alias :add :add_remote
218
+
219
+
220
+ #noinspection RubyClassMethodNamingConvention
221
+ def self.hostname_and_user_from_ssh_config(host_alias, config_file)
222
+ if File.exists?(config_file)
223
+ config_lines = File.new(config_file).readlines
224
+
225
+ in_host_section = false
226
+ host_name = nil
227
+ user_name = nil
228
+
229
+ config_lines.each do |line|
230
+ line.chop!
231
+ if /^\s*Host\s+#{host_alias}\s*$/ =~ line
232
+ in_host_section = true
233
+ next
234
+ end
235
+
236
+ if in_host_section and (/^\s*HostName\s+\S+\s*$/ =~ line)
237
+ host_name = line.sub(/^\s*HostName\s+(\S+)\s*$/, '\1')
238
+ break unless user_name.nil?
239
+ elsif in_host_section and (/^\s*User\s+\S+\s*$/ =~ line)
240
+ user_name = line.sub(/^\s*User\s+(\S+)\s*$/, '\1')
241
+ break unless host_name.nil?
242
+ elsif in_host_section and (/^\s*Host\s+.*$/ =~ line)
243
+ break
244
+ end
245
+ end
246
+ host_name.nil? ? nil : [host_name, user_name]
247
+ else
248
+ nil
249
+ end
250
+ end
251
+
252
+ end
253
+
254
+ end