m-git 2.5.4
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/LICENSE +21 -0
- data/README.md +85 -0
- data/lib/m-git.rb +66 -0
- data/lib/m-git/argv.rb +170 -0
- data/lib/m-git/argv/opt.rb +38 -0
- data/lib/m-git/argv/opt_list.rb +71 -0
- data/lib/m-git/argv/parser.rb +66 -0
- data/lib/m-git/base_command.rb +271 -0
- data/lib/m-git/command/add.rb +41 -0
- data/lib/m-git/command/branch.rb +90 -0
- data/lib/m-git/command/checkout.rb +106 -0
- data/lib/m-git/command/clean.rb +64 -0
- data/lib/m-git/command/commit.rb +84 -0
- data/lib/m-git/command/config.rb +202 -0
- data/lib/m-git/command/delete.rb +99 -0
- data/lib/m-git/command/fetch.rb +32 -0
- data/lib/m-git/command/forall.rb +81 -0
- data/lib/m-git/command/info.rb +74 -0
- data/lib/m-git/command/init.rb +324 -0
- data/lib/m-git/command/log.rb +73 -0
- data/lib/m-git/command/merge.rb +381 -0
- data/lib/m-git/command/pull.rb +364 -0
- data/lib/m-git/command/push.rb +311 -0
- data/lib/m-git/command/rebase.rb +348 -0
- data/lib/m-git/command/reset.rb +31 -0
- data/lib/m-git/command/self.rb +223 -0
- data/lib/m-git/command/stash.rb +189 -0
- data/lib/m-git/command/status.rb +135 -0
- data/lib/m-git/command/sync.rb +327 -0
- data/lib/m-git/command/tag.rb +67 -0
- data/lib/m-git/command_manager.rb +24 -0
- data/lib/m-git/error.rb +20 -0
- data/lib/m-git/foundation.rb +25 -0
- data/lib/m-git/foundation/constants.rb +107 -0
- data/lib/m-git/foundation/dir.rb +25 -0
- data/lib/m-git/foundation/duration_recorder.rb +92 -0
- data/lib/m-git/foundation/git_message_parser.rb +50 -0
- data/lib/m-git/foundation/lock.rb +32 -0
- data/lib/m-git/foundation/loger.rb +129 -0
- data/lib/m-git/foundation/mgit_config.rb +222 -0
- data/lib/m-git/foundation/operation_progress_manager.rb +139 -0
- data/lib/m-git/foundation/timer.rb +74 -0
- data/lib/m-git/foundation/utils.rb +361 -0
- data/lib/m-git/hooks_manager.rb +96 -0
- data/lib/m-git/manifest.rb +181 -0
- data/lib/m-git/manifest/cache_manager.rb +44 -0
- data/lib/m-git/manifest/internal.rb +182 -0
- data/lib/m-git/manifest/light_repo.rb +108 -0
- data/lib/m-git/manifest/light_repo_generator.rb +87 -0
- data/lib/m-git/manifest/linter.rb +153 -0
- data/lib/m-git/open_api.rb +427 -0
- data/lib/m-git/open_api/script_download_info.rb +37 -0
- data/lib/m-git/output/output.rb +461 -0
- data/lib/m-git/plugin_manager.rb +112 -0
- data/lib/m-git/repo.rb +133 -0
- data/lib/m-git/repo/status.rb +481 -0
- data/lib/m-git/repo/sync_helper.rb +254 -0
- data/lib/m-git/template.rb +9 -0
- data/lib/m-git/template/local_manifest.rb +27 -0
- data/lib/m-git/template/manifest_hook.rb +28 -0
- data/lib/m-git/template/post_download_hook.rb +29 -0
- data/lib/m-git/template/post_hook.rb +31 -0
- data/lib/m-git/template/pre_exec_hook.rb +31 -0
- data/lib/m-git/template/pre_hook.rb +29 -0
- data/lib/m-git/template/pre_push_hook.rb +32 -0
- data/lib/m-git/version.rb +6 -0
- data/lib/m-git/workspace.rb +648 -0
- data/lib/m-git/workspace/path_helper.rb +56 -0
- data/lib/m-git/workspace/workspace_helper.rb +159 -0
- data/m-git +1 -0
- data/mgit +19 -0
- metadata +218 -0
@@ -0,0 +1,99 @@
|
|
1
|
+
#coding=utf-8
|
2
|
+
|
3
|
+
module MGit
|
4
|
+
|
5
|
+
# @!scope [command] delete 删除某个仓库的`所有`文件,包括工作区、暂存区和版本库
|
6
|
+
#
|
7
|
+
# eg: mgit delete subA
|
8
|
+
#
|
9
|
+
class Delete < BaseCommand
|
10
|
+
|
11
|
+
# --- 覆写前后hook,不需要预设操作 ---
|
12
|
+
def pre_exec
|
13
|
+
MGit::DurationRecorder.start
|
14
|
+
Workspace.setup_multi_repo_root
|
15
|
+
# 配置log
|
16
|
+
MGit::Loger.config(Workspace.root)
|
17
|
+
MGit::Loger.info("~~~ #{@argv.absolute_cmd} ~~~")
|
18
|
+
Workspace.setup_config
|
19
|
+
end
|
20
|
+
|
21
|
+
def post_exec
|
22
|
+
# 打点结束
|
23
|
+
duration = MGit::DurationRecorder.end
|
24
|
+
MGit::Loger.info("~~~ #{@argv.absolute_cmd}, 耗时:#{duration} s ~~~")
|
25
|
+
end
|
26
|
+
# --------------------------------
|
27
|
+
|
28
|
+
def execute(argv)
|
29
|
+
delete_repo_names = parse_repo_name(argv)
|
30
|
+
include_central = Workspace.config.light_repos.find do |e|
|
31
|
+
delete_repo_names.include?(e.name.downcase) && e.is_config_repo
|
32
|
+
end
|
33
|
+
Foundation.help!("禁止删除配置仓库=> #{include_central.name}") if include_central
|
34
|
+
|
35
|
+
Output.puts_start_cmd
|
36
|
+
delete_light_repos = Workspace.config.light_repos.select { |e| delete_repo_names.include?(e.name.downcase) }
|
37
|
+
extra_repo_names = delete_repo_names - delete_light_repos.map { |e| e.name.downcase}
|
38
|
+
if delete_light_repos.length > 0
|
39
|
+
error_repos = {}
|
40
|
+
delete_light_repos.each { |light_repo|
|
41
|
+
begin
|
42
|
+
git_dir = light_repo.git_store_dir(Workspace.root)
|
43
|
+
repo_dir = light_repo.abs_dest(Workspace.root)
|
44
|
+
if !Dir.exist?(git_dir) && !Dir.exist?(repo_dir)
|
45
|
+
Output.puts_remind_message("#{light_repo.name}本地不存在,已跳过。")
|
46
|
+
end
|
47
|
+
|
48
|
+
# 删除git实体
|
49
|
+
if Dir.exist?(git_dir)
|
50
|
+
Output.puts_processing_message("删除仓库#{light_repo.name}的.git实体...")
|
51
|
+
FileUtils.remove_dir(git_dir, true)
|
52
|
+
end
|
53
|
+
|
54
|
+
# 删除工作区文件
|
55
|
+
if Dir.exist?(repo_dir)
|
56
|
+
Output.puts_processing_message("删除仓库#{light_repo.name}工作区文件...")
|
57
|
+
FileUtils.remove_dir(repo_dir, true)
|
58
|
+
end
|
59
|
+
rescue => e
|
60
|
+
error_repos[light_repo.name] = e.message
|
61
|
+
end
|
62
|
+
}
|
63
|
+
if error_repos.length > 0
|
64
|
+
Workspace.show_error(error_repos)
|
65
|
+
else
|
66
|
+
Output.puts_succeed_cmd(argv.absolute_cmd)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
if extra_repo_names.length > 0
|
71
|
+
Output.puts_fail_block(extra_repo_names, "以上仓库配置表中未定义,请重试!")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def parse_repo_name(argv)
|
76
|
+
return if argv.git_opts.nil?
|
77
|
+
|
78
|
+
repos = argv.git_opts.split(' ')
|
79
|
+
extra_opts = repos.select { |e| argv.is_option?(e) }
|
80
|
+
Foundation.help!("输入非法参数:#{extra_opts.join(',')}。请通过\"mgit #{argv.cmd} --help\"查看用法。") if extra_opts.length > 0
|
81
|
+
Foundation.help!("未输入查询仓库名!请使用这种形式查询:mgit info repo1 repo2 ...") if repos.length == 0
|
82
|
+
repos.map { |e| e.downcase }
|
83
|
+
end
|
84
|
+
|
85
|
+
def enable_short_basic_option
|
86
|
+
true
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.description
|
90
|
+
"删除指定单个或多个仓库(包含被管理的.git文件和工程文件以及跟该.git关联的所有缓存)。"
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.usage
|
94
|
+
"mgit delete <repo1> <repo2>... [-h]"
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#coding=utf-8
|
2
|
+
|
3
|
+
module MGit
|
4
|
+
|
5
|
+
# @!scope [command] fetch
|
6
|
+
# follow git fetch
|
7
|
+
# eg: mgit fetch
|
8
|
+
#
|
9
|
+
class Fetch < BaseCommand
|
10
|
+
def execute(argv)
|
11
|
+
Output.puts_start_cmd
|
12
|
+
_, error_repos = Workspace.execute_git_cmd_with_repos(argv.cmd, argv.git_opts, all_repos)
|
13
|
+
if error_repos.length == 0
|
14
|
+
Output.puts_succeed_cmd(argv.absolute_cmd)
|
15
|
+
Timer.show_time_consuming_repos
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def enable_repo_selection
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.description
|
24
|
+
"与远程仓库同步分支引用和数据对象。"
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.usage
|
28
|
+
"mgit fetch [<git-fetch-option>] [(--mrepo|--el-mrepo) <repo>...] [--help]"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#coding=utf-8
|
2
|
+
|
3
|
+
module MGit
|
4
|
+
|
5
|
+
# @!scope [command] forall 对管理的仓库依次执行shell命令
|
6
|
+
#
|
7
|
+
# eg: mgit forall -c 'git status'
|
8
|
+
#
|
9
|
+
class Forall < BaseCommand
|
10
|
+
|
11
|
+
OPT_LIST = {
|
12
|
+
:command => '--command',
|
13
|
+
:command_s => '-c',
|
14
|
+
:concurrent => '--concurrent',
|
15
|
+
:concurrent_s => '-n',
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
def options
|
19
|
+
return [
|
20
|
+
ARGV::Opt.new(OPT_LIST[:command],
|
21
|
+
short_key:OPT_LIST[:command_s],
|
22
|
+
info:'必须参数,指定需要执行的shell命令,如:"mgit forall -c \'git status -s\'"(注意要带引号)。',
|
23
|
+
type: :string),
|
24
|
+
ARGV::Opt.new(OPT_LIST[:concurrent],
|
25
|
+
short_key:OPT_LIST[:concurrent_s],
|
26
|
+
info:'可选参数,若指定,则shell命令以多线程方式执行。',
|
27
|
+
type: :boolean)
|
28
|
+
].concat(super)
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate(argv)
|
32
|
+
Foundation.help!("输入非法参数:#{argv.git_opts}。请通过\"mgit #{argv.cmd} --help\"查看用法。") if argv.git_opts.length > 0
|
33
|
+
Foundation.help!("请输入必须参数--command,示例:mgit forall -c 'git status'") if argv.opt(OPT_LIST[:command]).nil?
|
34
|
+
end
|
35
|
+
|
36
|
+
def execute(argv)
|
37
|
+
# 校验分支统一
|
38
|
+
Workspace.check_branch_consistency
|
39
|
+
|
40
|
+
Output.puts_start_cmd
|
41
|
+
for_all_cmd = argv.opt(OPT_LIST[:command]).value
|
42
|
+
|
43
|
+
use_concurrent = !argv.opt(OPT_LIST[:concurrent]).nil?
|
44
|
+
if use_concurrent
|
45
|
+
succeed_repos, error_repos = Workspace.execute_common_cmd_with_repos_concurrent(for_all_cmd, all_repos)
|
46
|
+
else
|
47
|
+
succeed_repos, error_repos = Workspace.execute_common_cmd_with_repos(for_all_cmd, all_repos)
|
48
|
+
end
|
49
|
+
|
50
|
+
no_output_repos = []
|
51
|
+
succeed_repos.each { |repo_name, output|
|
52
|
+
if output.length > 0
|
53
|
+
puts Output.generate_title_block(repo_name) { output } + "\n"
|
54
|
+
else
|
55
|
+
no_output_repos.push(repo_name)
|
56
|
+
end
|
57
|
+
}
|
58
|
+
|
59
|
+
Output.puts_remind_block(no_output_repos, "以上仓库无输出!") if no_output_repos.length > 0
|
60
|
+
Output.puts_succeed_cmd(argv.absolute_cmd) if error_repos.length == 0
|
61
|
+
end
|
62
|
+
|
63
|
+
def enable_repo_selection
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
def enable_short_basic_option
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.description
|
72
|
+
"对多仓库批量执行指令。"
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.usage
|
76
|
+
"mgit forall -c '<instruction>' [(-m|-e) <repo>...] [-n] [-h]"
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
#coding=utf-8
|
2
|
+
|
3
|
+
module MGit
|
4
|
+
|
5
|
+
# @!scope 指定仓库的信息
|
6
|
+
#
|
7
|
+
class Info < BaseCommand
|
8
|
+
|
9
|
+
def execute(argv)
|
10
|
+
Output.puts_start_cmd
|
11
|
+
|
12
|
+
query_repo_names = parse_repo_name(argv)
|
13
|
+
quere_repos = (all_repos + locked_repos).select { |e| query_repo_names.include?(e.name.downcase) }
|
14
|
+
if quere_repos.length > 0
|
15
|
+
quere_repos.each { |repo|
|
16
|
+
puts Output.generate_title_block(repo.name) {
|
17
|
+
info = []
|
18
|
+
info.push(['仓库位置'.bold, ["#{repo.path}"]])
|
19
|
+
info.push(['占用磁盘大小'.bold, ["#{calculate_size(repo)}"]])
|
20
|
+
info.push(['创建时间'.bold, ["#{File.ctime(repo.path)}"]])
|
21
|
+
|
22
|
+
current_branch = repo.status_checker.current_branch(strict_mode:false)
|
23
|
+
branch_message = repo.status_checker.branch_message
|
24
|
+
info.push(['当前分支'.bold, ["#{current_branch.nil? ? '无' : current_branch}"]])
|
25
|
+
info.push(['分支状态'.bold, ["#{branch_message}"]])
|
26
|
+
|
27
|
+
info.push(['文件改动'.bold, ["#{repo.status_checker.status != Repo::Status::GIT_REPO_STATUS[:clean] ? '有本地改动,请用status指令查看细节' : '本地无修改'}"]])
|
28
|
+
info.push(['Stash状态'.bold, ["#{check_stash(repo)}"]])
|
29
|
+
Output.generate_table_combination(info) + "\n\n"
|
30
|
+
}
|
31
|
+
}
|
32
|
+
Output.puts_succeed_cmd(argv.absolute_cmd)
|
33
|
+
else
|
34
|
+
Output.puts_fail_message("未找到与输入仓库名匹配的仓库,请重试!")
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse_repo_name(argv)
|
40
|
+
return nil if argv.git_opts.nil?
|
41
|
+
repos = argv.git_opts.split(' ')
|
42
|
+
extra_opts = repos.select { |e| argv.is_option?(e) }
|
43
|
+
Foundation.help!("输入非法参数:#{extra_opts.join(',')}。请通过\"mgit #{argv.cmd} --help\"查看用法。") if extra_opts.length > 0
|
44
|
+
Foundation.help!("未输入查询仓库名!请使用这种形式查询:mgit info repo1 repo2 ...") if repos.length == 0
|
45
|
+
repos.map { |e| e.downcase }
|
46
|
+
end
|
47
|
+
|
48
|
+
def calculate_size(repo)
|
49
|
+
success, output = repo.execute("du -sh #{repo.path} | awk '{print $1}'")
|
50
|
+
return '计算失败'.red unless success
|
51
|
+
output.chomp
|
52
|
+
end
|
53
|
+
|
54
|
+
def check_stash(repo)
|
55
|
+
success, output = repo.execute("git -C \"#{repo.path}\" stash list")
|
56
|
+
return "查询失败".red unless success
|
57
|
+
output.length > 0 ? '有内容' : '无内容'
|
58
|
+
end
|
59
|
+
|
60
|
+
def enable_short_basic_option
|
61
|
+
false
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.description
|
65
|
+
"输出指定仓库的信息。"
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.usage
|
69
|
+
"mgit info <repo>... [-h]"
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,324 @@
|
|
1
|
+
#coding=utf-8
|
2
|
+
|
3
|
+
module MGit
|
4
|
+
|
5
|
+
# @!scope 初始化多仓库命令
|
6
|
+
#
|
7
|
+
class Init < BaseCommand
|
8
|
+
|
9
|
+
OPT_LIST = {
|
10
|
+
:git_source => '--git-source',
|
11
|
+
:git_source_s => '-g',
|
12
|
+
:config_source => '--config-source',
|
13
|
+
:config_source_s => '-f',
|
14
|
+
:branch => '--branch',
|
15
|
+
:branch_s => '-b',
|
16
|
+
:local_config => '--local-config',
|
17
|
+
:local_config_s => '-l',
|
18
|
+
:all => '--all',
|
19
|
+
:all_s => '-a'
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
def options
|
23
|
+
return [
|
24
|
+
ARGV::Opt.new(OPT_LIST[:git_source],
|
25
|
+
short_key:OPT_LIST[:git_source_s],
|
26
|
+
info:'通过包含多仓库配置表的git仓库来初始化,传入远程仓库地址,如:"mgit init -g https://someone@bitbucket.org/someone"。不可与"-f","--config-source"同时指定。',
|
27
|
+
type: :string),
|
28
|
+
ARGV::Opt.new(OPT_LIST[:config_source],
|
29
|
+
short_key:OPT_LIST[:config_source_s],
|
30
|
+
info:'通过本地的多仓库配置表来初始化,传入本地配置文件路径,如:"mgit init -f <local_config_path>/manifest.json"。不可与"-g","--git-source"同时指定。',
|
31
|
+
type: :string),
|
32
|
+
ARGV::Opt.new(OPT_LIST[:branch],
|
33
|
+
short_key:OPT_LIST[:branch_s],
|
34
|
+
info:'指定配置仓库克隆分支。',
|
35
|
+
type: :string),
|
36
|
+
ARGV::Opt.new(OPT_LIST[:local_config],
|
37
|
+
short_key:OPT_LIST[:local_config_s],
|
38
|
+
info:'指定是否自动生成本地配置文件模版。指定后会在主仓库下生成名为local_manifest.json的本地配置文件,其内容只包含主仓库信息,并指定其余仓库不纳入mgit管理。初始化时指定该参数将只下载主仓库,若同时指定了"-a"或"--all",则其余仓库也会被下载。',
|
39
|
+
type: :boolean),
|
40
|
+
ARGV::Opt.new(OPT_LIST[:all],
|
41
|
+
short_key:OPT_LIST[:all_s],
|
42
|
+
info:'指定后会下载所有在配置表中配置了远程地址的仓库,无论该仓库是否被纳入mgit管理(无论是否指定"mgit_excluded:true")。',
|
43
|
+
type: :boolean)
|
44
|
+
].concat(super)
|
45
|
+
end
|
46
|
+
|
47
|
+
def validate(argv)
|
48
|
+
Foundation.help!("输入非法参数:#{argv.git_opts}") if argv.git_opts.length > 0
|
49
|
+
|
50
|
+
git_source_opt = argv.opt(OPT_LIST[:git_source])
|
51
|
+
file_source_opt = argv.opt(OPT_LIST[:config_source])
|
52
|
+
if !git_source_opt.nil? && !file_source_opt.nil?
|
53
|
+
Foundation.help!("不能同时指定参数\"#{OPT_LIST[:git_source]}\"和\"#{OPT_LIST[:config_source]}!\"")
|
54
|
+
elsif git_source_opt.nil? && file_source_opt.nil?
|
55
|
+
Foundation.help!("缺失参数\"#{OPT_LIST[:git_source]}\"或\"#{OPT_LIST[:config_source]}!\"")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# --- 覆写前后hook,不需要预设操作 ---
|
60
|
+
def pre_exec
|
61
|
+
Output.puts_processing_message("开始初始化多仓库...")
|
62
|
+
# 开始计时
|
63
|
+
MGit::DurationRecorder.start
|
64
|
+
|
65
|
+
initial_multi_repo_root
|
66
|
+
|
67
|
+
# 配置log
|
68
|
+
MGit::Loger.config(Workspace.root)
|
69
|
+
MGit::Loger.info("~~~ #{@argv.absolute_cmd} ~~~")
|
70
|
+
end
|
71
|
+
|
72
|
+
def post_exec
|
73
|
+
Output.puts_success_message("多仓库初始化成功!")
|
74
|
+
# 打点结束
|
75
|
+
duration = MGit::DurationRecorder.end
|
76
|
+
MGit::Loger.info("~~~ #{@argv.absolute_cmd}, 耗时:#{duration} s ~~~")
|
77
|
+
end
|
78
|
+
|
79
|
+
# --------------------------------
|
80
|
+
|
81
|
+
def execute(argv)
|
82
|
+
init_dir
|
83
|
+
|
84
|
+
begin
|
85
|
+
git_url_opt = argv.opt(OPT_LIST[:git_source])
|
86
|
+
local_url_opt = argv.opt(OPT_LIST[:config_source])
|
87
|
+
clone_all = argv.opt_list.did_set_opt?(OPT_LIST[:all])
|
88
|
+
if !git_url_opt.nil?
|
89
|
+
git_url = git_url_opt.value
|
90
|
+
branch = argv.opt(OPT_LIST[:branch]).value if argv.opt_list.did_set_opt?(OPT_LIST[:branch])
|
91
|
+
use_local = argv.opt_list.did_set_opt?(OPT_LIST[:local_config])
|
92
|
+
clone_with_git_url(git_url, branch, use_local, clone_all)
|
93
|
+
elsif !local_url_opt.nil?
|
94
|
+
clone_with_local_config(local_url_opt.value, clone_all)
|
95
|
+
end
|
96
|
+
rescue Interrupt => e
|
97
|
+
terminate!(e.message)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def init_dir
|
102
|
+
Constants::PROJECT_DIR.each { |key, relative_dir|
|
103
|
+
abs_dir = File.join(Workspace.root, relative_dir)
|
104
|
+
FileUtils.mkdir_p(abs_dir)
|
105
|
+
if key == :hooks
|
106
|
+
setup_hooks(File.join(abs_dir, Constants::HOOK_NAME[:pre_hook]),
|
107
|
+
File.join(abs_dir, Constants::HOOK_NAME[:post_hook]),
|
108
|
+
File.join(abs_dir, Constants::HOOK_NAME[:manifest_hook]),
|
109
|
+
File.join(abs_dir, Constants::HOOK_NAME[:post_download_hook]))
|
110
|
+
end
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
def write_content(path, content)
|
115
|
+
file = File.new(path, 'w')
|
116
|
+
if !file.nil?
|
117
|
+
file.write(content)
|
118
|
+
file.close
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def setup_hooks(pre_hook_path, post_hook_path, manifest_hook_path, post_download_hook)
|
123
|
+
write_content(pre_hook_path, Template::PRE_CUSTOMIZED_HOOK_TEMPLATE)
|
124
|
+
write_content(post_hook_path, Template::POST_CUSTOMIZED_HOOK_TEMPLATE)
|
125
|
+
write_content(manifest_hook_path, Template::MANIFEST_HOOK_TEMPLATE)
|
126
|
+
write_content(post_download_hook, Template::POST_DOWNLOAD_HOOK_TEMPLATE)
|
127
|
+
end
|
128
|
+
|
129
|
+
def initial_multi_repo_root
|
130
|
+
|
131
|
+
if exist_root = Workspace.multi_repo_root_path
|
132
|
+
Foundation.help!("当前已在多仓库目录下,请勿重复初始化!\n`#{exist_root}`")
|
133
|
+
end
|
134
|
+
|
135
|
+
@origin_root = Dir.pwd
|
136
|
+
tmp_root = Utils.generate_init_cache_path(@origin_root)
|
137
|
+
FileUtils.mkdir_p(tmp_root)
|
138
|
+
Workspace.setup_multi_repo_root(tmp_root)
|
139
|
+
end
|
140
|
+
|
141
|
+
def setup_local_config(path, config_repo, use_local)
|
142
|
+
if use_local
|
143
|
+
content = Template.local_config_template(config_repo)
|
144
|
+
else
|
145
|
+
content = Template.default_template
|
146
|
+
end
|
147
|
+
|
148
|
+
write_content(path, content)
|
149
|
+
Utils.link(path, File.join(Workspace.root, Constants::PROJECT_DIR[:source_config], Constants::CONFIG_FILE_NAME[:local_manifest]))
|
150
|
+
end
|
151
|
+
|
152
|
+
def clone_with_git_url(git_url, branch, use_local, clone_all)
|
153
|
+
# 先将主仓库clone到mgit root目录下
|
154
|
+
central_repo_temp_path = File.join(Workspace.root, Constants::CENTRAL_REPO)
|
155
|
+
Output.puts_processing_message("正在克隆主仓库...")
|
156
|
+
Utils.execute_shell_cmd("git clone -b #{branch.nil? ? 'master' : branch} -- #{git_url} #{central_repo_temp_path}") { |stdout, stderr, status|
|
157
|
+
if status.success?
|
158
|
+
# 获取主仓库中的配置文件
|
159
|
+
begin
|
160
|
+
config = Manifest.parse(central_repo_temp_path, strict_mode:false)
|
161
|
+
rescue Error => e
|
162
|
+
terminate!(e.msg)
|
163
|
+
end
|
164
|
+
central_light_repo = config.config_repo
|
165
|
+
terminate!("配置文件中未找到主仓库配置,请添加后重试!") if central_light_repo.nil?
|
166
|
+
terminate!("配置文件中主仓库url与传入的url不一致, 请处理后重试!") if git_url != central_light_repo.url
|
167
|
+
|
168
|
+
central_repo_dest_path = central_light_repo.abs_dest(Workspace.root)
|
169
|
+
|
170
|
+
# 如果不是子目录则删除已有
|
171
|
+
if Dir.exist?(central_repo_dest_path) && !central_repo_temp_path.include?(central_repo_dest_path)
|
172
|
+
FileUtils.remove_dir(central_repo_dest_path, true)
|
173
|
+
end
|
174
|
+
FileUtils.mkdir_p(central_repo_dest_path)
|
175
|
+
mv_cmd = "mv #{central_repo_temp_path + '/{*,.[^.]*}'} #{central_repo_dest_path}"
|
176
|
+
|
177
|
+
`#{mv_cmd}`
|
178
|
+
FileUtils.rm_rf(central_repo_temp_path)
|
179
|
+
|
180
|
+
# 链接.git实体
|
181
|
+
Utils.link_git(central_repo_dest_path, central_light_repo.git_store_dir(Workspace.root))
|
182
|
+
Output.puts_success_message("主仓库克隆完成!")
|
183
|
+
|
184
|
+
# 链接本地配置文件
|
185
|
+
setup_local_config(File.join(central_repo_dest_path, Constants::CONFIG_FILE_NAME[:local_manifest]), central_light_repo.name, use_local)
|
186
|
+
|
187
|
+
# 由于更新了名字和可能的位置移动,重新解析配置文件
|
188
|
+
begin
|
189
|
+
config = Manifest.parse(central_repo_dest_path, strict_mode:false)
|
190
|
+
rescue Error => e
|
191
|
+
terminate!(e.msg)
|
192
|
+
end
|
193
|
+
|
194
|
+
# clone其余仓库
|
195
|
+
clone_sub_repos(config.repo_list(exclusion:[config.config_repo.name], all:clone_all), branch)
|
196
|
+
finish_init(config)
|
197
|
+
else
|
198
|
+
terminate!("主仓库克隆失败,初始化停止,请重试:\n#{stderr}")
|
199
|
+
end
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
def clone_with_local_config(config_path, clone_all)
|
204
|
+
terminate!("指定路径\"#{config_path}\"文件不存在!") if !File.exist?(config_path)
|
205
|
+
|
206
|
+
begin
|
207
|
+
config = Manifest.parse(Utils.expand_path(config_path), strict_mode:false)
|
208
|
+
rescue Error => e
|
209
|
+
terminate!(e.msg)
|
210
|
+
end
|
211
|
+
|
212
|
+
if !config.config_repo.nil?
|
213
|
+
clone_list = config.repo_list(exclusion: [config.config_repo.name], all:clone_all)
|
214
|
+
else
|
215
|
+
clone_list = config.repo_list(all:clone_all)
|
216
|
+
end
|
217
|
+
|
218
|
+
clone_sub_repos(clone_list, 'master')
|
219
|
+
finish_init(config)
|
220
|
+
end
|
221
|
+
|
222
|
+
def clone_sub_repos(repo_list, default_branch)
|
223
|
+
if repo_list.length != 0
|
224
|
+
Output.puts_processing_message("正在克隆子仓库...")
|
225
|
+
try_repos = repo_list
|
226
|
+
retry_repos = []
|
227
|
+
error_repos = {}
|
228
|
+
|
229
|
+
mutex = Mutex.new
|
230
|
+
mutex_progress = Mutex.new
|
231
|
+
|
232
|
+
task_count = 0
|
233
|
+
total_try_count = 4
|
234
|
+
total_retry_count = total_try_count - 1
|
235
|
+
# 保证失败仓库有3次重新下载的机会
|
236
|
+
total_try_count.times { |try_count|
|
237
|
+
Workspace.concurrent_enumerate(try_repos) { |light_repo|
|
238
|
+
# 如果mainfest中指定了分支,就替换default branch
|
239
|
+
branch = light_repo.branch ? light_repo.branch : default_branch
|
240
|
+
Utils.execute_shell_cmd(light_repo.clone_url(Workspace.root, clone_branch:branch)) { |stdout, stderr, status|
|
241
|
+
repo_dir = light_repo.abs_dest(Workspace.root)
|
242
|
+
if status.success?
|
243
|
+
Utils.link_git(repo_dir, light_repo.git_store_dir(Workspace.root))
|
244
|
+
|
245
|
+
mutex_progress.lock
|
246
|
+
task_count += 1
|
247
|
+
Output.puts_success_message("(#{task_count}/#{try_repos.length}) \"#{light_repo.name}\"克隆完成!")
|
248
|
+
mutex_progress.unlock
|
249
|
+
else
|
250
|
+
Output.puts_remind_message("\"#{light_repo.name}\"克隆失败,已加入重试队列。") if try_count < total_retry_count
|
251
|
+
mutex.lock
|
252
|
+
retry_repos.push(light_repo)
|
253
|
+
error_repos[light_repo.name] = stderr
|
254
|
+
FileUtils.remove_dir(repo_dir, true) if Dir.exist?(repo_dir)
|
255
|
+
mutex.unlock
|
256
|
+
end
|
257
|
+
}
|
258
|
+
}
|
259
|
+
|
260
|
+
if retry_repos.length == 0 || try_count >= total_retry_count
|
261
|
+
break
|
262
|
+
else
|
263
|
+
Output.puts_processing_block(error_repos.keys, "以上仓库克隆失败,开始第#{try_count + 1}次重试(最多#{total_retry_count}次)...")
|
264
|
+
try_repos = retry_repos
|
265
|
+
retry_repos = []
|
266
|
+
error_repos = {}
|
267
|
+
task_count = 0
|
268
|
+
end
|
269
|
+
}
|
270
|
+
|
271
|
+
if error_repos.length > 0
|
272
|
+
Workspace.show_error(error_repos)
|
273
|
+
terminate!("初始化停止,请重试!")
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def link_config(config_path, config_cache_path, root)
|
279
|
+
Utils.execute_under_dir("#{File.join(root, Constants::PROJECT_DIR[:source_config])}") {
|
280
|
+
# 在.mgit/source-config文件夹下创建原始配置文件的软连接
|
281
|
+
config_link_path = File.join(Dir.pwd, Constants::CONFIG_FILE_NAME[:manifest])
|
282
|
+
Utils.link(config_path, config_link_path) if config_path != config_link_path
|
283
|
+
|
284
|
+
# 将配置缓存移动到.mgit/source-config文件夹下
|
285
|
+
FileUtils.mv(config_cache_path, Dir.pwd) if File.dirname(config_cache_path) != Dir.pwd
|
286
|
+
}
|
287
|
+
end
|
288
|
+
|
289
|
+
def terminate!(msg)
|
290
|
+
Output.puts_fail_message(msg)
|
291
|
+
Output.puts_processing_message("删除缓存...")
|
292
|
+
FileUtils.remove_dir(Workspace.root, true) if Dir.exist?(Workspace.root)
|
293
|
+
Output.puts_success_message("删除完成!")
|
294
|
+
exit
|
295
|
+
end
|
296
|
+
|
297
|
+
def move_project_to_root
|
298
|
+
Dir.foreach(Workspace.root) { |item|
|
299
|
+
if item != '.' && item != '..' && item != '.DS_Store'
|
300
|
+
FileUtils.mv(File.join(Workspace.root, item), @origin_root)
|
301
|
+
end
|
302
|
+
}
|
303
|
+
FileUtils.remove_dir(Workspace.root, true) if Dir.exist?(Workspace.root)
|
304
|
+
end
|
305
|
+
|
306
|
+
def finish_init(config)
|
307
|
+
move_project_to_root
|
308
|
+
config_path, config_cache_path = config.path.sub(Workspace.root, @origin_root), config.cache_path.sub(Workspace.root, @origin_root)
|
309
|
+
link_config(config_path, config_cache_path, @origin_root)
|
310
|
+
end
|
311
|
+
|
312
|
+
def enable_short_basic_option
|
313
|
+
true
|
314
|
+
end
|
315
|
+
|
316
|
+
def self.description
|
317
|
+
"初始化多仓库目录。"
|
318
|
+
end
|
319
|
+
|
320
|
+
def self.usage
|
321
|
+
"mgit init (-f <path> | -g <url> [-b <branch>] [-l]) [-a]"
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|