gtlab 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/Gitl.yml +42 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/gitl +8 -0
- data/gitl.gemspec +39 -0
- data/lib/Command.rb +70 -0
- data/lib/commands/branch.rb +2 -0
- data/lib/commands/create.rb +67 -0
- data/lib/commands/create_tag.rb +104 -0
- data/lib/commands/delete_tag.rb +98 -0
- data/lib/commands/diff.rb +2 -0
- data/lib/commands/forall.rb +2 -0
- data/lib/commands/init.rb +36 -0
- data/lib/commands/push.rb +2 -0
- data/lib/commands/review.rb +262 -0
- data/lib/commands/start.rb +116 -0
- data/lib/commands/status.rb +2 -0
- data/lib/commands/sync.rb +45 -0
- data/lib/config/gitl_config.rb +71 -0
- data/lib/config/work_space_config.rb +25 -0
- data/lib/git_ext.rb +120 -0
- data/lib/gitl.rb +8 -0
- data/lib/gitl/version.rb +3 -0
- data/lib/gitlab_ext.rb +26 -0
- data/lib/sub_command.rb +139 -0
- metadata +175 -0
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'sub_command'
|
2
|
+
|
3
|
+
module Gitl
|
4
|
+
class Start < SubCommand
|
5
|
+
|
6
|
+
self.summary = '创建对应工作分支,并同步到gitlab.'
|
7
|
+
|
8
|
+
self.description = <<-DESC
|
9
|
+
创建对应工作分支,并同步到gitlab.
|
10
|
+
DESC
|
11
|
+
|
12
|
+
self.arguments = [
|
13
|
+
CLAide::Argument.new('working_branch', true, false),
|
14
|
+
CLAide::Argument.new('remote_branch', true, false),
|
15
|
+
]
|
16
|
+
|
17
|
+
def self.options
|
18
|
+
[
|
19
|
+
["--force", "忽略工作分支是否存在,强制执行"],
|
20
|
+
].concat(super)
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(argv)
|
24
|
+
@working_branch = argv.shift_argument
|
25
|
+
@remote_branch = argv.shift_argument
|
26
|
+
@force = argv.flag?('force')
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate!
|
31
|
+
super
|
32
|
+
if @working_branch.nil?
|
33
|
+
help! 'working_branch is required.'
|
34
|
+
end
|
35
|
+
if @remote_branch.nil?
|
36
|
+
help! 'remote_branch is required.'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def run
|
41
|
+
remote = 'origin'
|
42
|
+
workspace_config = WorkSpaceConfig.new(@remote_branch, @working_branch)
|
43
|
+
|
44
|
+
self.gitl_config.projects.each do |project|
|
45
|
+
project_path = File.expand_path(project.name, './')
|
46
|
+
if File.exist?(project_path)
|
47
|
+
g = Git.open(project_path)
|
48
|
+
else
|
49
|
+
g = Git.clone(project.git, project.name, :path => './')
|
50
|
+
end
|
51
|
+
|
52
|
+
if self.verbose?
|
53
|
+
# g.setLogger(Logger.new(STDOUT))
|
54
|
+
end
|
55
|
+
|
56
|
+
check_uncommit(g, project.name)
|
57
|
+
|
58
|
+
# 更新本地代码
|
59
|
+
g.fetch(remote, :p => true, :t => true)
|
60
|
+
# g.pull_opts(remote, g.current_branch, :p => true)
|
61
|
+
|
62
|
+
if !g.is_remote_branch?(@remote_branch)
|
63
|
+
raise Error.new("remote branch '#{@remote_branch}' does not exist for project '#{project.name}'.")
|
64
|
+
end
|
65
|
+
|
66
|
+
if g.is_remote_branch?(@working_branch) && !@force
|
67
|
+
raise Error.new("branch '#{@working_branch}' exist in remote '#{remote}' for project '#{project.name}'.")
|
68
|
+
end
|
69
|
+
|
70
|
+
if g.is_local_branch?(@working_branch) && !@force
|
71
|
+
raise Error.new("branch '#{@working_branch}' exist in local for project '#{project.name}'.")
|
72
|
+
end
|
73
|
+
|
74
|
+
# g.remote(remote).branch(@remote_branch).checkout()
|
75
|
+
# g.branch(@remote_branch).checkout
|
76
|
+
|
77
|
+
# git_cmd = "git remote set-branches #{remote} '#{@remote_branch}'"
|
78
|
+
# puts `#{git_cmd}`.chomp
|
79
|
+
#
|
80
|
+
# git_cmd = "git fetch --depth 1 #{remote} '#{@remote_branch}'"
|
81
|
+
# puts `#{git_cmd}`.chomp
|
82
|
+
#
|
83
|
+
# $ git remote set-branches origin 'remote_branch_name'
|
84
|
+
# $ git fetch --depth 1 origin remote_branch_name
|
85
|
+
# $ git checkout remote_branch_name
|
86
|
+
|
87
|
+
g.checkout(@remote_branch)
|
88
|
+
|
89
|
+
g.pull(remote, @remote_branch)
|
90
|
+
|
91
|
+
if g.is_local_branch?(@working_branch)
|
92
|
+
g.checkout(@working_branch)
|
93
|
+
g.pull(remote, @working_branch)
|
94
|
+
else
|
95
|
+
puts "create branch '#{@working_branch}' for project '#{project.name}'.".green
|
96
|
+
# 创建本地工作分支
|
97
|
+
g.checkout(@working_branch, :new_branch => true)
|
98
|
+
end
|
99
|
+
|
100
|
+
# 跟踪远程分支
|
101
|
+
g.track(remote, @remote_branch)
|
102
|
+
|
103
|
+
puts "push branch '#{@working_branch}' to remote for project '#{project.name}'.".green
|
104
|
+
# push到origin
|
105
|
+
g.push(remote, @working_branch)
|
106
|
+
|
107
|
+
puts
|
108
|
+
end
|
109
|
+
|
110
|
+
#保存新的workspace配置
|
111
|
+
self.save_workspace_config(workspace_config)
|
112
|
+
|
113
|
+
puts "create work branch '#{@working_branch}' from #{@remote_branch} and push to '#{remote}' success.".green
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'sub_command'
|
2
|
+
|
3
|
+
|
4
|
+
module Gitl
|
5
|
+
|
6
|
+
class Sync < SubCommand
|
7
|
+
|
8
|
+
self.summary = '更新工作分支代码'
|
9
|
+
|
10
|
+
self.description = <<-DESC
|
11
|
+
根据yml配置,更新代码.
|
12
|
+
DESC
|
13
|
+
|
14
|
+
def run_in_workspace
|
15
|
+
|
16
|
+
remote = 'origin'
|
17
|
+
workspace_config = self.workspace_config
|
18
|
+
|
19
|
+
info "current work branch '#{workspace_config.workspace_branch}', remote branch '#{workspace_config.remote_branch}'."
|
20
|
+
|
21
|
+
self.gitl_config.projects.each do |project|
|
22
|
+
project_path = File.expand_path(project.name, './')
|
23
|
+
|
24
|
+
if File.exist?(project_path)
|
25
|
+
info "sync project '#{project.name}'..."
|
26
|
+
g = Git.open(project_path)
|
27
|
+
if workspace_config.workspace_branch != g.current_branch
|
28
|
+
error "current branch is not work branch(#{workspace_config.workspace_branch})."
|
29
|
+
exit(1)
|
30
|
+
end
|
31
|
+
g.fetch(remote, :p => true, :t => true)
|
32
|
+
g.pull("origin", workspace_config.workspace_branch)
|
33
|
+
g.pull("origin", workspace_config.remote_branch)
|
34
|
+
puts
|
35
|
+
|
36
|
+
else
|
37
|
+
error "please run 'gitl init' first."
|
38
|
+
break
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Gitl
|
2
|
+
class GitlConfig
|
3
|
+
|
4
|
+
attr_reader :projects
|
5
|
+
attr_reader :gitlab
|
6
|
+
attr_reader :config_path
|
7
|
+
|
8
|
+
def initialize(config_path, node)
|
9
|
+
@config_path = config_path
|
10
|
+
|
11
|
+
gitlab = node['gitlab']
|
12
|
+
@gitlab = GitlabConfig.new(gitlab)
|
13
|
+
|
14
|
+
@projects = []
|
15
|
+
projects = node['projects']
|
16
|
+
projects.each do |project|
|
17
|
+
projectConfig = ProjectConfig.new(project)
|
18
|
+
@projects << projectConfig
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.load_file(config_path)
|
23
|
+
node = YAML.load_file(config_path)
|
24
|
+
GitlConfig.new(config_path, node)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.load_yml(yml)
|
28
|
+
node = YAML.load(yml)
|
29
|
+
GitlConfig.new(nil, node)
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_dictionary
|
33
|
+
projects = self.projects.map do |project|
|
34
|
+
project.to_dictionary
|
35
|
+
end
|
36
|
+
{"projects"=>projects, "gitlab"=>self.gitlab.to_dictionary}
|
37
|
+
end
|
38
|
+
|
39
|
+
class ProjectConfig
|
40
|
+
attr_reader :name
|
41
|
+
attr_reader :git
|
42
|
+
|
43
|
+
def initialize(node)
|
44
|
+
@name = node['name']
|
45
|
+
@git = node['git']
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_dictionary
|
49
|
+
{"name"=>self.name, "git"=>self.git}
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
class GitlabConfig
|
55
|
+
attr_reader :endpoint
|
56
|
+
attr_accessor :private_token
|
57
|
+
|
58
|
+
def initialize(node)
|
59
|
+
@endpoint = node['endpoint']
|
60
|
+
@private_token = node['private_token']
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_dictionary
|
64
|
+
{"endpoint"=>self.endpoint, "private_token"=>private_token}
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
module Gitl
|
3
|
+
class WorkSpaceConfig
|
4
|
+
attr_reader :remote_branch
|
5
|
+
attr_reader :workspace_branch
|
6
|
+
|
7
|
+
def initialize(remote_branch, workspace_branch)
|
8
|
+
@remote_branch = remote_branch
|
9
|
+
@workspace_branch = workspace_branch
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.load_file(yaml_filename)
|
13
|
+
node = YAML.load_file(yaml_filename)
|
14
|
+
remote_branch = node['remote_branch']
|
15
|
+
workspace_branch = node['workspace_branch']
|
16
|
+
return WorkSpaceConfig.new(remote_branch, workspace_branch)
|
17
|
+
end
|
18
|
+
|
19
|
+
def save(path)
|
20
|
+
File.open(path, 'w') do |file|
|
21
|
+
Psych.dump({'remote_branch' => @remote_branch, 'workspace_branch' => @workspace_branch}, file)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/git_ext.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'git'
|
2
|
+
|
3
|
+
|
4
|
+
module Git
|
5
|
+
def self.clone_without_env(repository, name, opts = {})
|
6
|
+
opts = Git::Lib.new.clone_without_env(repository, name, opts)
|
7
|
+
Base.new(opts)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Patches
|
12
|
+
module Git
|
13
|
+
|
14
|
+
module Base
|
15
|
+
|
16
|
+
def track(remote, branch)
|
17
|
+
self.lib.track(remote, branch)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
module Lib
|
22
|
+
def initialize(*args)
|
23
|
+
super
|
24
|
+
# @logger = Logger.new(STDOUT)
|
25
|
+
end
|
26
|
+
|
27
|
+
def run_command(git_cmd, &block)
|
28
|
+
git_cmd = git_cmd.gsub(/2>&1$/, '')
|
29
|
+
return IO.popen(git_cmd, &block) if block_given?
|
30
|
+
|
31
|
+
`#{git_cmd}`.chomp
|
32
|
+
end
|
33
|
+
|
34
|
+
def track(remote, branch)
|
35
|
+
arr_opts = []
|
36
|
+
arr_opts << '-u'
|
37
|
+
arr_opts << "#{remote}/#{branch}"
|
38
|
+
command('branch', arr_opts)
|
39
|
+
end
|
40
|
+
|
41
|
+
def clone_without_env(repository, name, opts = {})
|
42
|
+
@path = opts[:path] || '.'
|
43
|
+
clone_dir = opts[:path] ? File.join(@path, name) : name
|
44
|
+
|
45
|
+
arr_opts = []
|
46
|
+
arr_opts << '--bare' if opts[:bare]
|
47
|
+
arr_opts << '--branch' << opts[:branch] if opts[:branch]
|
48
|
+
arr_opts << '--depth' << opts[:depth].to_i if opts[:depth] && opts[:depth].to_i > 0
|
49
|
+
arr_opts << '--config' << opts[:gitl_config] if opts[:gitl_config]
|
50
|
+
arr_opts << '--origin' << opts[:remote] || opts[:origin] if opts[:remote] || opts[:origin]
|
51
|
+
arr_opts << '--recursive' if opts[:recursive]
|
52
|
+
arr_opts << "--mirror" if opts[:mirror]
|
53
|
+
|
54
|
+
arr_opts << '--'
|
55
|
+
|
56
|
+
arr_opts << repository
|
57
|
+
arr_opts << clone_dir
|
58
|
+
|
59
|
+
command_without_env('clone', arr_opts)
|
60
|
+
|
61
|
+
(opts[:bare] or opts[:mirror]) ? {:repository => clone_dir} : {:working_directory => clone_dir}
|
62
|
+
end
|
63
|
+
|
64
|
+
def command_without_env(cmd, opts = [], chdir = true, redirect = '', &block)
|
65
|
+
global_opts = []
|
66
|
+
global_opts << "--git-dir=#{@git_dir}" if !@git_dir.nil?
|
67
|
+
global_opts << "--work-tree=#{@git_work_dir}" if !@git_work_dir.nil?
|
68
|
+
|
69
|
+
opts = [opts].flatten.map {|s| escape(s) }.join(' ')
|
70
|
+
|
71
|
+
global_opts = global_opts.flatten.map {|s| escape(s) }.join(' ')
|
72
|
+
|
73
|
+
git_cmd = "#{::Git::Base.config.binary_path} #{global_opts} #{cmd} #{opts} #{redirect} 2>&1"
|
74
|
+
|
75
|
+
output = nil
|
76
|
+
|
77
|
+
command_thread = nil;
|
78
|
+
|
79
|
+
exitstatus = nil
|
80
|
+
|
81
|
+
command_thread = Thread.new do
|
82
|
+
output = run_command(git_cmd, &block)
|
83
|
+
exitstatus = $?.exitstatus
|
84
|
+
end
|
85
|
+
command_thread.join
|
86
|
+
|
87
|
+
if @logger
|
88
|
+
@logger.info(git_cmd)
|
89
|
+
@logger.debug(output)
|
90
|
+
end
|
91
|
+
|
92
|
+
if exitstatus > 1 || (exitstatus == 1 && output != '')
|
93
|
+
raise Git::GitExecuteError.new(git_cmd + ':' + output.to_s)
|
94
|
+
end
|
95
|
+
|
96
|
+
return output
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
module Status
|
102
|
+
def fetch_untracked
|
103
|
+
ignore = @base.lib.ignored_files
|
104
|
+
|
105
|
+
Dir.chdir(@base.dir.path) do
|
106
|
+
Dir.glob('**/*', File::FNM_DOTMATCH) do |file|
|
107
|
+
next if @files[file] || File.directory?(file) ||
|
108
|
+
ignore.include?(file) || file =~ %r{^.git\/.+} || file =~ %r{^(.*\/)?.gitkeep$}
|
109
|
+
|
110
|
+
@files[file] = { path: file, untracked: true }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
Git::Status.prepend(Patches::Git::Status)
|
119
|
+
Git::Lib.prepend(Patches::Git::Lib)
|
120
|
+
Git::Base.prepend(Patches::Git::Base)
|
data/lib/gitl.rb
ADDED
data/lib/gitl/version.rb
ADDED
data/lib/gitlab_ext.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Patches
|
4
|
+
# Defines methods related to projects.
|
5
|
+
# @see https://docs.gitlab.com/ce/api/projects.html
|
6
|
+
module Projects
|
7
|
+
|
8
|
+
# Gets a list of project users.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# Gitlab.project_usesrs(42)
|
12
|
+
# Gitlab.project_usesrs('gitlab')
|
13
|
+
#
|
14
|
+
# @param [Integer, String] project The ID or path of a project.
|
15
|
+
# @param [Hash] options A customizable set of options.
|
16
|
+
# @option options [Integer] :page The page number.
|
17
|
+
# @option options [Integer] :per_page The number of results per page.
|
18
|
+
# @return [Array<Gitlab::ObjectifiedHash>]
|
19
|
+
def project_usesrs(project, options = {})
|
20
|
+
get("/projects/#{url_encode project}/users", query: options)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
Gitlab::Client.prepend(Patches::Projects)
|
data/lib/sub_command.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'command'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'config/gitl_config'
|
4
|
+
require 'colored2'
|
5
|
+
require 'gitlab'
|
6
|
+
require 'git_ext'
|
7
|
+
require 'yaml'
|
8
|
+
|
9
|
+
module Gitl
|
10
|
+
class SubCommand < Command
|
11
|
+
|
12
|
+
self.ignore_in_command_lookup = true
|
13
|
+
attr_reader :gitl_config
|
14
|
+
|
15
|
+
def self.options
|
16
|
+
[
|
17
|
+
['--config=[Gitl.yml]', 'gitl配置, 默认为Gitl.yml'],
|
18
|
+
].concat(super)
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(argv)
|
22
|
+
@yml = argv.option('config')
|
23
|
+
if @yml.nil?
|
24
|
+
@yml = 'Gitl.yml'
|
25
|
+
end
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate!
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
def run
|
34
|
+
workspace_path = "./"
|
35
|
+
find_workspace = false;
|
36
|
+
begin
|
37
|
+
result = nil
|
38
|
+
Dir.chdir(workspace_path) do
|
39
|
+
result = Dir.glob('.gitl', File::FNM_DOTMATCH)
|
40
|
+
end
|
41
|
+
if result.length > 0
|
42
|
+
find_workspace = true
|
43
|
+
break
|
44
|
+
else
|
45
|
+
workspace_path = File.expand_path("../", workspace_path)
|
46
|
+
end
|
47
|
+
end while workspace_path.length > 0 && workspace_path != "/"
|
48
|
+
|
49
|
+
if find_workspace
|
50
|
+
Dir.chdir(workspace_path) do
|
51
|
+
self.run_in_workspace()
|
52
|
+
end
|
53
|
+
else
|
54
|
+
raise Error.new("Current path is not gitl workspace.")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def run_in_workspace
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
def gitl_config
|
63
|
+
if File.exist?(@yml)
|
64
|
+
@gitl_config = GitlConfig.load_file(@yml)
|
65
|
+
else
|
66
|
+
help! 'config is required.'
|
67
|
+
end
|
68
|
+
@gitl_config
|
69
|
+
end
|
70
|
+
|
71
|
+
def workspace_config
|
72
|
+
if @workspace_config.nil?
|
73
|
+
filename = '.gitl'
|
74
|
+
# workspace_config_path = File.expand_path(filename, File.dirname(self.gitl_config.config_path))
|
75
|
+
workspace_config_path = filename
|
76
|
+
if !File.exist?(workspace_config_path)
|
77
|
+
help! "workspace config not found. please run 'gitl start' first."
|
78
|
+
end
|
79
|
+
@workspace_config = WorkSpaceConfig.load_file(workspace_config_path)
|
80
|
+
end
|
81
|
+
@workspace_config
|
82
|
+
end
|
83
|
+
|
84
|
+
def save_workspace_config(workspace_config)
|
85
|
+
filename = '.gitl'
|
86
|
+
# workspace_config_path = File.expand_path(filename, File.dirname(self.gitl_config.config_path))
|
87
|
+
workspace_config_path = filename
|
88
|
+
workspace_config.save(workspace_config_path)
|
89
|
+
@workspace_config = workspace_config
|
90
|
+
end
|
91
|
+
|
92
|
+
def check_uncommit(g, project_name)
|
93
|
+
changed = g.status.changed
|
94
|
+
added = g.status.added
|
95
|
+
deleted = g.status.deleted
|
96
|
+
untracked = g.status.untracked
|
97
|
+
|
98
|
+
if !changed.empty?
|
99
|
+
alert = true
|
100
|
+
puts "modified files:".red
|
101
|
+
changed.each do |file, status|
|
102
|
+
puts (" M: " << file).red
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
if !added.empty?
|
107
|
+
alert = true
|
108
|
+
puts "added files:".red
|
109
|
+
added.each do |file, status|
|
110
|
+
puts (" A: " << file).red
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
if !deleted.empty?
|
115
|
+
alert = true
|
116
|
+
puts "deleted files:".red
|
117
|
+
deleted.each do |file, status|
|
118
|
+
puts (" D: " << file).red
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
if !untracked.empty?
|
123
|
+
alert = true
|
124
|
+
puts "untracked files:".red
|
125
|
+
untracked.each do |file, status|
|
126
|
+
puts (" " << file).red
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
if alert
|
131
|
+
puts "exist uncommit files in current branch '#{g.current_branch}' for '#{project_name}'. ignore it? y/n "
|
132
|
+
flag = STDIN.gets.chomp
|
133
|
+
unless flag.downcase == "y"
|
134
|
+
exit
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|