git-process-lib 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/CHANGELOG.md +123 -0
  2. data/Gemfile +21 -0
  3. data/Gemfile.lock +57 -0
  4. data/LICENSE +193 -0
  5. data/README.md +342 -0
  6. data/Rakefile +32 -0
  7. data/bin/git-new-fb +39 -0
  8. data/bin/git-pull-request +63 -0
  9. data/bin/git-sync +38 -0
  10. data/bin/git-to-master +44 -0
  11. data/docs/git-new-fb.1.adoc +83 -0
  12. data/docs/git-process.1.adoc +227 -0
  13. data/docs/git-pull-request.1.adoc +166 -0
  14. data/docs/git-sync.1.adoc +120 -0
  15. data/docs/git-to-master.1.adoc +172 -0
  16. data/git-new-fb.gemspec +20 -0
  17. data/git-process-lib.gemspec +25 -0
  18. data/git-process.gemspec +22 -0
  19. data/git-pull-request.gemspec +20 -0
  20. data/git-sync.gemspec +20 -0
  21. data/git-to-master.gemspec +20 -0
  22. data/lib/git-process/abstract_error_builder.rb +53 -0
  23. data/lib/git-process/changed_file_helper.rb +115 -0
  24. data/lib/git-process/git_abstract_merge_error_builder.rb +130 -0
  25. data/lib/git-process/git_branch.rb +105 -0
  26. data/lib/git-process/git_branches.rb +81 -0
  27. data/lib/git-process/git_config.rb +135 -0
  28. data/lib/git-process/git_lib.rb +646 -0
  29. data/lib/git-process/git_logger.rb +84 -0
  30. data/lib/git-process/git_merge_error.rb +28 -0
  31. data/lib/git-process/git_process.rb +159 -0
  32. data/lib/git-process/git_process_error.rb +18 -0
  33. data/lib/git-process/git_process_options.rb +101 -0
  34. data/lib/git-process/git_rebase_error.rb +30 -0
  35. data/lib/git-process/git_remote.rb +222 -0
  36. data/lib/git-process/git_status.rb +108 -0
  37. data/lib/git-process/github_configuration.rb +298 -0
  38. data/lib/git-process/github_pull_request.rb +165 -0
  39. data/lib/git-process/new_fb.rb +49 -0
  40. data/lib/git-process/parked_changes_error.rb +41 -0
  41. data/lib/git-process/pull_request.rb +136 -0
  42. data/lib/git-process/pull_request_error.rb +25 -0
  43. data/lib/git-process/rebase_to_master.rb +148 -0
  44. data/lib/git-process/sync_process.rb +55 -0
  45. data/lib/git-process/syncer.rb +157 -0
  46. data/lib/git-process/uncommitted_changes_error.rb +23 -0
  47. data/lib/git-process/version.rb +22 -0
  48. data/local-build.rb +24 -0
  49. data/spec/FileHelpers.rb +19 -0
  50. data/spec/GitRepoHelper.rb +123 -0
  51. data/spec/changed_file_helper_spec.rb +127 -0
  52. data/spec/git_abstract_merge_error_builder_spec.rb +64 -0
  53. data/spec/git_branch_spec.rb +123 -0
  54. data/spec/git_config_spec.rb +45 -0
  55. data/spec/git_lib_spec.rb +176 -0
  56. data/spec/git_logger_spec.rb +66 -0
  57. data/spec/git_process_spec.rb +208 -0
  58. data/spec/git_remote_spec.rb +227 -0
  59. data/spec/git_status_spec.rb +122 -0
  60. data/spec/github_configuration_spec.rb +152 -0
  61. data/spec/github_pull_request_spec.rb +117 -0
  62. data/spec/github_test_helper.rb +49 -0
  63. data/spec/new_fb_spec.rb +126 -0
  64. data/spec/pull_request_helper.rb +94 -0
  65. data/spec/pull_request_spec.rb +137 -0
  66. data/spec/rebase_to_master_spec.rb +362 -0
  67. data/spec/spec_helper.rb +21 -0
  68. data/spec/sync_spec.rb +1474 -0
  69. metadata +249 -0
@@ -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
@@ -0,0 +1,28 @@
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_abstract_merge_error_builder'
14
+
15
+ module GitProc
16
+
17
+ class MergeError < GitProcessError
18
+
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
22
+
23
+ super(msg)
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,159 @@
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_lib'
14
+ require 'git-process/git_rebase_error'
15
+ require 'git-process/git_merge_error'
16
+ require 'highline/import'
17
+
18
+
19
+ module GitProc
20
+
21
+ class Process
22
+
23
+ # @param [GitLib] gitlib
24
+ def initialize(gitlib, opts = {})
25
+ @gitlib = gitlib
26
+ end
27
+
28
+
29
+ def gitlib
30
+ @gitlib
31
+ end
32
+
33
+
34
+ def run
35
+ begin
36
+ verify_preconditions
37
+
38
+ runner
39
+ rescue GitProc::GitProcessError => exp
40
+ puts exp.message
41
+ exit(-1)
42
+ ensure
43
+ cleanup
44
+ end
45
+ end
46
+
47
+
48
+ def runner
49
+ # extension point - does nothing by default
50
+ end
51
+
52
+
53
+ def workdir
54
+ gitlib.workdir
55
+ end
56
+
57
+
58
+ def logger
59
+ gitlib.logger
60
+ end
61
+
62
+
63
+ def config
64
+ gitlib.config
65
+ end
66
+
67
+
68
+ def master_branch
69
+ gitlib.config.master_branch
70
+ end
71
+
72
+
73
+ def remote
74
+ gitlib.remote
75
+ end
76
+
77
+
78
+ def verify_preconditions
79
+ if should_remove_master?
80
+ if ask_about_removing_master
81
+ delete_master_branch!
82
+ end
83
+ end
84
+ end
85
+
86
+
87
+ def cleanup
88
+ # extension point
89
+ end
90
+
91
+
92
+ def fetch_remote_changes(remote_name = nil)
93
+ gitlib.fetch_remote_changes(remote_name)
94
+ end
95
+
96
+
97
+ def is_parked?
98
+ gitlib.is_parked?
99
+ end
100
+
101
+
102
+ private
103
+
104
+
105
+ def delete_master_branch!
106
+ gitlib.branches[config.master_branch].delete!
107
+ end
108
+
109
+
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)
117
+ end
118
+
119
+
120
+ def keep_local_integration_branch?
121
+ keep_local_integration_branch_config_value.to_boolean
122
+ end
123
+
124
+
125
+ #noinspection RubyInstanceMethodNamingConvention
126
+ def keep_local_integration_branch_config_value
127
+ gitlib.config['gitProcess.keepLocalIntegrationBranch']
128
+ end
129
+
130
+
131
+ def ask_about_removing_master
132
+ resp = ask("You should remove your obsolete <%= color('local', [:bold]) %> branch, '#{config.master_branch}'. Should I remove it for you? (Yn) ") do |q|
133
+ q.responses[:not_valid] = 'Please respond with either (y)es or (n)o. Defaults to (y)es.'
134
+ q.case = :down
135
+ q.default = 'Y'
136
+ q.validate = /y|n/i
137
+ end
138
+
139
+ if resp == 'n'
140
+ say("(You can turn off this message using \"git config gitProcess.keepLocalIntegrationBranch true\").")
141
+ false
142
+ else
143
+ true
144
+ end
145
+ end
146
+
147
+
148
+ def proc_rebase(base, opts = {})
149
+ gitlib.proc_rebase(base, opts)
150
+ end
151
+
152
+
153
+ def proc_merge(base, opts = {})
154
+ gitlib.proc_merge(base, opts)
155
+ end
156
+
157
+ end
158
+
159
+ end
@@ -0,0 +1,18 @@
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
+ module GitProc
14
+
15
+ class GitProcessError < RuntimeError
16
+ end
17
+
18
+ end
@@ -0,0 +1,101 @@
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 'optparse'
14
+ require 'trollop'
15
+ require 'git-process/version'
16
+
17
+ module GitProc
18
+
19
+ module GitProcessOptions
20
+
21
+ DEBUG = false
22
+
23
+
24
+ def parse_cli(filename, argv)
25
+ parser = Trollop::Parser.new
26
+ parser.version "#{filename} #{GitProc::Version::STRING}"
27
+
28
+ #parser.banner "#{summary}\n\n"
29
+ command_name = filename.sub(/git-/, '')
30
+ parser.banner "For full documentation, see 'git help #{command_name}'"
31
+ parser.banner ""
32
+
33
+ extend_opts(parser)
34
+ standard_opts(parser)
35
+
36
+ #parser.banner "\n#{description}"
37
+
38
+ opts = Trollop::with_standard_exception_handling parser do
39
+ raise Trollop::HelpNeeded if ARGV.empty? and !empty_argv_ok?
40
+ parser.parse argv
41
+ end
42
+
43
+ opts[:info] = false if opts[:verbose] || opts[:quiet]
44
+ opts[:info] = true if opts[:info_given]
45
+
46
+ post_parse(opts, argv)
47
+
48
+ if DEBUG
49
+ puts "\n\n#{opts.map { |k, v| "#{k}:#{v}" }.join(', ')}"
50
+ puts "\nargs: #{argv.join(', ')}"
51
+ end
52
+
53
+ opts
54
+ end
55
+
56
+
57
+ def standard_opts(parser)
58
+ parser.opt :info, 'Informational messages; show the major things this is doing', :default => true, :short => :none
59
+ parser.opt :quiet, 'Quiet messages; only show errors', :short => :q
60
+ parser.opt :verbose, 'Verbose messages; show lots of details on what this is doing', :short => :v
61
+ parser.opt :version, "Print version (#{GitProc::Version::STRING}) and exit", :short => :none
62
+ parser.opt :help, 'Show this message', :short => :h
63
+
64
+ parser.conflicts :verbose, :info, :quiet
65
+ end
66
+
67
+
68
+ def summary
69
+ 'Default summary'
70
+ end
71
+
72
+
73
+ def usage(filename)
74
+ "#{filename} [options]"
75
+ end
76
+
77
+
78
+ def description
79
+ 'Default description'
80
+ end
81
+
82
+
83
+ def empty_argv_ok?
84
+ true
85
+ end
86
+
87
+
88
+ #noinspection RubyUnusedLocalVariable
89
+ def extend_opts(parser)
90
+ # extension point - does nothing by default
91
+ end
92
+
93
+
94
+ #noinspection RubyUnusedLocalVariable
95
+ def post_parse(opts, argv)
96
+ # extension point - does nothing by default
97
+ end
98
+
99
+ end
100
+
101
+ end
@@ -0,0 +1,30 @@
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_abstract_merge_error_builder'
14
+
15
+ module GitProc
16
+
17
+ class RebaseError < GitProcessError
18
+ attr_reader :error_builder
19
+
20
+
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
24
+
25
+ super(msg)
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,222 @@
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 'addressable/uri'
15
+ #require 'git-process/git_branches'
16
+ #require 'git-process/git_status'
17
+ #require 'git-process/git_process_error'
18
+
19
+
20
+ class String
21
+
22
+ def to_boolean
23
+ return false if self == false || self.nil? || self =~ (/(false|f|no|n|0)$/i)
24
+ return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
25
+ raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
26
+ end
27
+
28
+ end
29
+
30
+
31
+ class NilClass
32
+ def to_boolean
33
+ false
34
+ end
35
+ end
36
+
37
+
38
+ module GitProc
39
+
40
+ #
41
+ # Git Remote configuration
42
+ #
43
+ class GitRemote
44
+
45
+ # @param [GitProc::GitConfig] gitconfig
46
+ def initialize(gitconfig)
47
+ @gitconfig = gitconfig
48
+ end
49
+
50
+
51
+ # @return [#info, #warn, #debug, #error]
52
+ def logger
53
+ @logger ||= @gitconfig.logger
54
+ end
55
+
56
+
57
+ # @return [GitProc::GitConfig]
58
+ def config
59
+ @gitconfig
60
+ end
61
+
62
+
63
+ # @deprecated
64
+ # TODO: Remove
65
+ def server_name
66
+ @server_name ||= self.remote_name
67
+ end
68
+
69
+
70
+ # @return [Boolean] does this have a remote defined?
71
+ def exists?
72
+ if @has_remote.nil?
73
+ @has_remote = (config.gitlib.command(:remote) != '')
74
+ end
75
+ logger.debug { "Does a remote exist? #{@has_remote}" }
76
+ @has_remote
77
+ end
78
+
79
+
80
+ def repo_name
81
+ unless @repo_name
82
+ url = config["remote.#{name}.url"]
83
+ raise GitProcessError.new("There is no #{name} url set up.") if url.nil? or url.empty?
84
+ uri = Addressable::URI.parse(url)
85
+ @repo_name = uri.path.sub(/\.git/, '').sub(/^\//, '')
86
+ end
87
+ @repo_name
88
+ end
89
+
90
+
91
+ def name
92
+ unless @remote_name
93
+ @remote_name = config['gitProcess.remoteName']
94
+ if @remote_name.nil? or @remote_name.empty?
95
+ remotes = self.remote_names
96
+ if remotes.empty?
97
+ @remote_name = nil
98
+ else
99
+ @remote_name = remotes[0]
100
+ raise '!@remote_name.is_a? String' unless @remote_name.is_a? String
101
+ end
102
+ end
103
+ logger.debug { "Using remote name of '#{@remote_name}'" }
104
+ end
105
+ @remote_name
106
+ end
107
+
108
+
109
+ def master_branch_name
110
+ "#{self.name}/#{config.master_branch}"
111
+ end
112
+
113
+
114
+ def remote_names
115
+ remote_str = config.gitlib.command(:remote, [:show])
116
+ if remote_str.nil? or remote_str.empty?
117
+ []
118
+ else
119
+ remote_str.split(/\n/)
120
+ end
121
+ end
122
+
123
+
124
+ #
125
+ # Expands the git configuration server name to a url.
126
+ #
127
+ # Takes into account further expanding an SSH uri that uses SSH aliasing in .ssh/config
128
+ #
129
+ # @param [String] server_name the git configuration server name; defaults to 'origin'
130
+ #
131
+ # @option opts [String] :ssh_config_file the SSH config file to use; defaults to ~/.ssh/config
132
+ #
133
+ # @return the fully expanded URL; never nil
134
+ #
135
+ # @raise [GitHubService::NoRemoteRepository] there is not a URL set for the server name
136
+ # @raise [URI::InvalidURIError] the retrieved URL does not have a schema
137
+ # @raise [GitHubService::NoRemoteRepository] if could not figure out a host for the retrieved URL
138
+ # @raise [::ArgumentError] if a server name is not provided
139
+ def expanded_url(server_name = 'origin', raw_url = nil, opts = {})
140
+ if raw_url.nil?
141
+ raise ArgumentError.new('Need server_name') unless server_name
142
+
143
+ conf_key = "remote.#{server_name}.url"
144
+ url = config[conf_key]
145
+
146
+ raise GitHubService::NoRemoteRepository.new("There is no value set for '#{conf_key}'") if url.nil? or url.empty?
147
+ else
148
+ raise GitHubService::NoRemoteRepository.new("There is no value set for '#{raw_url}'") if raw_url.nil? or raw_url.empty?
149
+ url = raw_url
150
+ end
151
+
152
+ if /^\S+@/ =~ url
153
+ url.sub(/^(\S+@\S+?):(.*)$/, "ssh://\\1/\\2")
154
+ else
155
+ uri = URI.parse(url)
156
+ host = uri.host
157
+ scheme = uri.scheme
158
+
159
+ raise URI::InvalidURIError.new("Need a scheme in URI: '#{url}'") unless scheme
160
+
161
+ if scheme == 'file'
162
+ url
163
+ elsif host.nil?
164
+ # assume that the 'scheme' is the named configuration in ~/.ssh/config
165
+ rv = GitRemote.hostname_and_user_from_ssh_config(scheme, opts[:ssh_config_file] ||= "#{ENV['HOME']}/.ssh/config")
166
+
167
+ raise GitHubService::NoRemoteRepository.new("Could not determine a host from #{url}") if rv.nil?
168
+
169
+ host = rv[0]
170
+ user = rv[1]
171
+ url.sub(/^\S+:(\S+)$/, "ssh://#{user}@#{host}/\\1")
172
+ else
173
+ url
174
+ end
175
+ end
176
+ end
177
+
178
+
179
+ # @return [void]
180
+ def add_remote(remote_name, url)
181
+ config.gitlib.command(:remote, ['add', remote_name, url])
182
+ end
183
+
184
+
185
+ alias :add :add_remote
186
+
187
+
188
+ #noinspection RubyClassMethodNamingConvention
189
+ def self.hostname_and_user_from_ssh_config(host_alias, config_file)
190
+ if File.exists?(config_file)
191
+ config_lines = File.new(config_file).readlines
192
+
193
+ in_host_section = false
194
+ host_name = nil
195
+ user_name = nil
196
+
197
+ config_lines.each do |line|
198
+ line.chop!
199
+ if /^\s*Host\s+#{host_alias}\s*$/ =~ line
200
+ in_host_section = true
201
+ next
202
+ end
203
+
204
+ if in_host_section and (/^\s*HostName\s+\S+\s*$/ =~ line)
205
+ host_name = line.sub(/^\s*HostName\s+(\S+)\s*$/, '\1')
206
+ break unless user_name.nil?
207
+ elsif in_host_section and (/^\s*User\s+\S+\s*$/ =~ line)
208
+ user_name = line.sub(/^\s*User\s+(\S+)\s*$/, '\1')
209
+ break unless host_name.nil?
210
+ elsif in_host_section and (/^\s*Host\s+.*$/ =~ line)
211
+ break
212
+ end
213
+ end
214
+ host_name.nil? ? nil : [host_name, user_name]
215
+ else
216
+ nil
217
+ end
218
+ end
219
+
220
+ end
221
+
222
+ end