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,189 @@
|
|
1
|
+
#coding=utf-8
|
2
|
+
|
3
|
+
module MGit
|
4
|
+
|
5
|
+
# @!scope 类似 git stash,但是强制标记名称
|
6
|
+
#
|
7
|
+
class Stash < BaseCommand
|
8
|
+
|
9
|
+
OPT_LIST = {
|
10
|
+
:push => '--push',
|
11
|
+
:pop => '--pop',
|
12
|
+
:apply => '--apply',
|
13
|
+
:list => '--list',
|
14
|
+
:clear => '--clear',
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
def options
|
18
|
+
return [
|
19
|
+
ARGV::Opt.new(OPT_LIST[:push], info:'添加储藏:mgit stash --push "stash_name"。', type: :string),
|
20
|
+
ARGV::Opt.new(OPT_LIST[:pop], info:'恢复储藏:mgit stash --pop "stash_name"。', type: :string),
|
21
|
+
ARGV::Opt.new(OPT_LIST[:apply], info:'恢复储藏:mgit stash --apply "stash_name"。', type: :string),
|
22
|
+
ARGV::Opt.new(OPT_LIST[:list], info:'显示储藏列表。', type: :boolean),
|
23
|
+
ARGV::Opt.new(OPT_LIST[:clear], info:'清空所有储藏。', type: :boolean)
|
24
|
+
].concat(super)
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate(argv)
|
28
|
+
missing_msg = "缺失必要参数" if argv.raw_opts.length == 0
|
29
|
+
illegal_msg = "输入非法参数:#{argv.git_opts}" if argv.git_opts.length > 0
|
30
|
+
conjunction = ",同时" if !missing_msg.nil? && !illegal_msg.nil?
|
31
|
+
Foundation.help!("#{missing_msg}#{conjunction}#{illegal_msg},请通过\"mgit #{argv.cmd} --help\"查看用法。") if !missing_msg.nil? || !illegal_msg.nil?
|
32
|
+
end
|
33
|
+
|
34
|
+
# 注意:git stash相关命令不支持指定“working tree”和“git dir”
|
35
|
+
# 如:git --git-dir=/path/to/.git --work-tree=/path/to/working-tree stash list,若当前不在working tree目录下,则将无法执行。
|
36
|
+
def execute(argv)
|
37
|
+
argv.enumerate_valid_opts { |opt|
|
38
|
+
if opt.key == OPT_LIST[:push]
|
39
|
+
do_stash_push(argv, opt.value)
|
40
|
+
break
|
41
|
+
elsif opt.key == OPT_LIST[:pop] || opt.key == OPT_LIST[:apply]
|
42
|
+
action = opt.key.gsub('--', '')
|
43
|
+
do_stash_pop_apply(argv, opt.value, action)
|
44
|
+
break
|
45
|
+
elsif opt.key == OPT_LIST[:list]
|
46
|
+
do_stash_list(argv)
|
47
|
+
elsif opt.key == OPT_LIST[:clear]
|
48
|
+
do_clear(argv)
|
49
|
+
end
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def do_clear(argv)
|
54
|
+
if Output.continue_with_user_remind?("该操作会丢失所有的stash,确定要执行吗?")
|
55
|
+
Output.puts_start_cmd
|
56
|
+
abs_cmd = "git stash clear"
|
57
|
+
_, error_repos = Workspace.execute_common_cmd_with_repos(abs_cmd, all_repos)
|
58
|
+
Output.puts_succeed_cmd(argv.absolute_cmd) if error_repos.length == 0
|
59
|
+
else
|
60
|
+
Output.puts_cancel_message
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def do_stash_list(argv)
|
65
|
+
Output.puts_start_cmd
|
66
|
+
|
67
|
+
error_repos = {}
|
68
|
+
all_repos.each { |repo|
|
69
|
+
cmd = "git -C \"#{repo.path}\" stash list"
|
70
|
+
success, output = repo.execute(cmd)
|
71
|
+
if success
|
72
|
+
puts Output.generate_title_block(repo.name) {
|
73
|
+
output
|
74
|
+
} + "\n" if output.length > 0
|
75
|
+
else
|
76
|
+
error_repos[repo.name] = output
|
77
|
+
end
|
78
|
+
}
|
79
|
+
if error_repos.length > 0
|
80
|
+
Workspace.show_error(error_repos)
|
81
|
+
else
|
82
|
+
Output.puts_succeed_cmd(argv.absolute_cmd)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def do_stash_pop_apply(argv, stash_name, action)
|
87
|
+
do_repos = []
|
88
|
+
mutex = Mutex.new
|
89
|
+
Output.puts_processing_message("检查仓库状态...")
|
90
|
+
all_repos.each { |repo|
|
91
|
+
stash_list = repo_stash_list_msg(repo)
|
92
|
+
next if stash_list.nil?
|
93
|
+
|
94
|
+
stash_id = stash_include_name(stash_list, stash_name)
|
95
|
+
next if stash_id.nil?
|
96
|
+
mutex.lock
|
97
|
+
do_repos.push(repo)
|
98
|
+
mutex.unlock
|
99
|
+
}
|
100
|
+
Output.puts_success_message("检查完成!\n")
|
101
|
+
|
102
|
+
Output.puts_start_cmd
|
103
|
+
if do_repos.length == 0
|
104
|
+
Output.puts_nothing_to_do_cmd
|
105
|
+
else
|
106
|
+
_, error_repos = Workspace.execute_common_cmd_with_repos('', do_repos) { |repo|
|
107
|
+
stash_list = repo_stash_list_msg(repo)
|
108
|
+
stash_id = stash_include_name(stash_list, stash_name)
|
109
|
+
"git stash #{action} #{stash_id}"
|
110
|
+
}
|
111
|
+
Output.puts_succeed_cmd(argv.absolute_cmd) if error_repos.length == 0
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def do_stash_push(argv, stash_name)
|
116
|
+
do_repos = []
|
117
|
+
remind_repos = []
|
118
|
+
mutex = Mutex.new
|
119
|
+
Output.puts_processing_message("检查仓库状态...")
|
120
|
+
all_repos.each { |repo|
|
121
|
+
next if repo.status_checker.status == Repo::Status::GIT_REPO_STATUS[:clean]
|
122
|
+
next if repo.status_checker.dirty_zone == Repo::Status::GIT_REPO_STATUS_DIRTY_ZONE[:special]
|
123
|
+
|
124
|
+
stash_list = repo_stash_list_msg(repo)
|
125
|
+
stash_id = stash_include_name(stash_list, stash_name)
|
126
|
+
mutex.lock
|
127
|
+
if stash_id.nil?
|
128
|
+
do_repos.push(repo)
|
129
|
+
else
|
130
|
+
remind_repos.push(repo.name)
|
131
|
+
end
|
132
|
+
mutex.unlock
|
133
|
+
}
|
134
|
+
Output.puts_success_message("检查完成!\n")
|
135
|
+
|
136
|
+
if remind_repos.length > 0
|
137
|
+
Output.puts_remind_block(remind_repos, "以上仓库当前分支已经存在stash名称:#{stash_name},请换一个名称或者使用\"mgit stash --list\"查看详情。")
|
138
|
+
Output.puts_fail_cmd(argv.absolute_cmd)
|
139
|
+
elsif do_repos.empty?
|
140
|
+
Output.puts_remind_message("所有仓库均是clean状态或者文件未跟踪,无需执行")
|
141
|
+
else
|
142
|
+
Output.puts_start_cmd
|
143
|
+
abs_cmd = "git stash save -u #{stash_name}"
|
144
|
+
_, error_repos = Workspace.execute_common_cmd_with_repos(abs_cmd, do_repos)
|
145
|
+
Output.puts_succeed_cmd(argv.absolute_cmd) if error_repos.length == 0
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# 获取当前的 stash list 字符串,nil,标识当前没有stash
|
150
|
+
def repo_stash_list_msg(repo)
|
151
|
+
success, output = repo.execute("git -C \"#{repo.path}\" stash list")
|
152
|
+
return output if success && output.length > 0
|
153
|
+
end
|
154
|
+
|
155
|
+
# 查询stash_list 是否包含某一个保存的stash
|
156
|
+
# 不做分支判断,因为在保存的stashlist中,分支只保留了/之后的内容
|
157
|
+
#
|
158
|
+
def stash_include_name(stash_list, stash_name)
|
159
|
+
return if stash_list.nil?
|
160
|
+
|
161
|
+
stash_list_array = stash_list.split("\n")
|
162
|
+
|
163
|
+
find_stash_id = nil
|
164
|
+
stash_list_array.each do |line|
|
165
|
+
regex = /(stash@{\d+}):.*:\s(.*)$/
|
166
|
+
next unless line.match(regex)
|
167
|
+
match_stash_name = $2
|
168
|
+
next unless match_stash_name == stash_name
|
169
|
+
find_stash_id = $1
|
170
|
+
break
|
171
|
+
end
|
172
|
+
find_stash_id
|
173
|
+
end
|
174
|
+
|
175
|
+
def enable_repo_selection
|
176
|
+
true
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.description
|
180
|
+
"使用git stash将当前工作区改动暂时存放起来。"
|
181
|
+
end
|
182
|
+
|
183
|
+
def self.usage
|
184
|
+
"mgit stash [<option> <value>...] [(--mrepo|--el-mrepo) <repo>...] [--help]"
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
#coding=utf-8
|
2
|
+
|
3
|
+
module MGit
|
4
|
+
|
5
|
+
# @!scope 类似 git status
|
6
|
+
#
|
7
|
+
class Status < BaseCommand
|
8
|
+
def execute(argv)
|
9
|
+
Output.puts_processing_message("正在检查各仓库状态...")
|
10
|
+
|
11
|
+
status_info = {}
|
12
|
+
mutex = Mutex.new
|
13
|
+
mutex_branch = Mutex.new
|
14
|
+
mutex_modification = Mutex.new
|
15
|
+
mutex_other = Mutex.new
|
16
|
+
|
17
|
+
branch_notice = []
|
18
|
+
modification_notice = []
|
19
|
+
other_notice = []
|
20
|
+
in_progress_notice = []
|
21
|
+
|
22
|
+
task_count = 0
|
23
|
+
repo_combo = all_repos + locked_repos
|
24
|
+
Output.update_progress(repo_combo.length, task_count)
|
25
|
+
Workspace.concurrent_enumerate(repo_combo) { |repo|
|
26
|
+
status_msg = ''
|
27
|
+
info = []
|
28
|
+
|
29
|
+
# 非锁定仓库进行常规分支检查
|
30
|
+
if !locked_repos.include?(repo) &&
|
31
|
+
repo.status_checker.branch_status != Repo::Status::GIT_BRANCH_STATUS[:up_to_date] &&
|
32
|
+
repo.status_checker.branch_status != Repo::Status::GIT_BRANCH_STATUS[:no_remote]
|
33
|
+
info.push(['分支', [repo.status_checker.branch_message]])
|
34
|
+
|
35
|
+
mutex_branch.lock
|
36
|
+
branch_notice.push(repo.name)
|
37
|
+
mutex_branch.unlock
|
38
|
+
end
|
39
|
+
|
40
|
+
# 检查工作区状态
|
41
|
+
if repo.status_checker.status != Repo::Status::GIT_REPO_STATUS[:clean]
|
42
|
+
info += repo.status_checker.message
|
43
|
+
mutex_modification.lock
|
44
|
+
modification_notice.push(repo.name)
|
45
|
+
mutex_modification.unlock
|
46
|
+
end
|
47
|
+
|
48
|
+
# 检查url是否一致
|
49
|
+
if !repo.url_consist?
|
50
|
+
info.push(['其他', ['仓库实际url与当前配置不一致']])
|
51
|
+
mutex_other.lock
|
52
|
+
other_notice.push(repo.name)
|
53
|
+
mutex_other.unlock
|
54
|
+
end
|
55
|
+
|
56
|
+
# 生成表格
|
57
|
+
status_msg = Output.generate_table_combination(info) + "\n\n" if info.length > 0
|
58
|
+
|
59
|
+
# 压缩状态信息
|
60
|
+
mutex.lock
|
61
|
+
if status_msg.length > 0
|
62
|
+
status_info[status_msg] = {'repo_names' => [], 'info' => info} if status_info[status_msg].nil?
|
63
|
+
status_info[status_msg]['repo_names'].push(repo.name)
|
64
|
+
end
|
65
|
+
task_count += 1
|
66
|
+
Output.update_progress(repo_combo.length, task_count)
|
67
|
+
mutex.unlock
|
68
|
+
}
|
69
|
+
|
70
|
+
status_info.each_with_index { |(status_msg, item), index|
|
71
|
+
info = item['info']
|
72
|
+
repo_names = item['repo_names']
|
73
|
+
Output.puts_remind_block(repo_names, "以上仓库状态:")
|
74
|
+
MGit::Loger.info(info)
|
75
|
+
status_msg += "\n" if index != status_info.length - 1
|
76
|
+
puts status_msg
|
77
|
+
}
|
78
|
+
|
79
|
+
OperationProgressManager::PROGRESS_TYPE.each { |type, type_str|
|
80
|
+
if OperationProgressManager.is_in_progress?(Workspace.root, type_str)
|
81
|
+
in_progress_notice.push(type.to_s)
|
82
|
+
end
|
83
|
+
}
|
84
|
+
|
85
|
+
summary = []
|
86
|
+
if branch_notice.length > 0
|
87
|
+
summary.push(["分支提醒(#{branch_notice.length})",branch_notice])
|
88
|
+
end
|
89
|
+
|
90
|
+
if modification_notice.length > 0
|
91
|
+
summary.push(["改动提醒(#{modification_notice.length})",modification_notice])
|
92
|
+
end
|
93
|
+
|
94
|
+
if other_notice.length > 0
|
95
|
+
summary.push(["其他警告(#{other_notice.length})", other_notice])
|
96
|
+
end
|
97
|
+
|
98
|
+
if in_progress_notice.length > 0
|
99
|
+
summary.push(["处于中间态的操作",in_progress_notice])
|
100
|
+
end
|
101
|
+
|
102
|
+
if summary.length > 0
|
103
|
+
puts "\n"
|
104
|
+
puts Output.generate_table_combination(summary, title: "状态小结", separator: "|")
|
105
|
+
MGit::Loger.info('状态小结')
|
106
|
+
MGit::Loger.info(summary)
|
107
|
+
end
|
108
|
+
|
109
|
+
Output.puts_success_message("所查询仓库均无改动!") if status_info.keys.length == 0
|
110
|
+
|
111
|
+
Output.puts_succeed_cmd(argv.absolute_cmd)
|
112
|
+
end
|
113
|
+
|
114
|
+
def validate(argv)
|
115
|
+
Foundation.help!("输入非法参数:#{argv.git_opts}。请通过\"mgit #{argv.cmd} --help\"查看用法。") if argv.git_opts.length > 0
|
116
|
+
end
|
117
|
+
|
118
|
+
def enable_repo_selection
|
119
|
+
true
|
120
|
+
end
|
121
|
+
|
122
|
+
def enable_short_basic_option
|
123
|
+
true
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.description
|
127
|
+
"输出所有仓库的状态。包括:\"分支\",\"暂存区\",\"工作区\",\"特殊(未跟踪和被忽略)\",\"冲突\"。"
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.usage
|
131
|
+
"mgit status [(-m|-e) <repo>...] [-h]"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
@@ -0,0 +1,327 @@
|
|
1
|
+
#coding=utf-8
|
2
|
+
|
3
|
+
module MGit
|
4
|
+
|
5
|
+
# @!scope 同步所有管理的仓库到工作区,可能从远端拉取,可能从本地dump,所以sync后的工作区不一定是分支的最新节点
|
6
|
+
# 可通过 mgit sync --pull 进行同步后pull到最新节点
|
7
|
+
#
|
8
|
+
class Sync < BaseCommand
|
9
|
+
|
10
|
+
OPT_LIST = {
|
11
|
+
:new_repo => '--new-repo',
|
12
|
+
:new_repo_s => '-n',
|
13
|
+
:all => '--all',
|
14
|
+
:all_s => '-a',
|
15
|
+
:clone => '--clone',
|
16
|
+
:clone_s => '-c',
|
17
|
+
:pull => '--pull',
|
18
|
+
:pull_s => '-p',
|
19
|
+
:url => '--url',
|
20
|
+
:url_s => '-u',
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
# --- 覆写前后hook,不需要预设操作 ---
|
24
|
+
def pre_exec
|
25
|
+
MGit::DurationRecorder.start
|
26
|
+
Workspace.setup_multi_repo_root
|
27
|
+
# 配置log
|
28
|
+
MGit::Loger.config(Workspace.root)
|
29
|
+
MGit::Loger.info("~~~ #{@argv.absolute_cmd} ~~~")
|
30
|
+
Workspace.setup_config
|
31
|
+
end
|
32
|
+
|
33
|
+
def post_exec
|
34
|
+
# 打点结束
|
35
|
+
duration = MGit::DurationRecorder.end
|
36
|
+
MGit::Loger.info("~~~ #{@argv.absolute_cmd}, 耗时:#{duration} s ~~~")
|
37
|
+
end
|
38
|
+
# --------------------------------
|
39
|
+
|
40
|
+
def options
|
41
|
+
return [
|
42
|
+
ARGV::Opt.new(OPT_LIST[:new_repo],
|
43
|
+
short_key:OPT_LIST[:new_repo_s],
|
44
|
+
info:"下载配置表中指定被mgit管理,但本地不存在的仓库,已有仓库不做任何处理,使用:mgit sync -n。",
|
45
|
+
type: :boolean),
|
46
|
+
ARGV::Opt.new(OPT_LIST[:all],
|
47
|
+
short_key:OPT_LIST[:all_s],
|
48
|
+
info:'对所有(包含不被mgit管理的)仓库操作:1.如果本地缺失则下载。2.如果本地存在且被锁定则同步到锁定状态。注意,如果需要下载代码,需要配置仓库URL,否则跳过,使用:mgit sync -a。',
|
49
|
+
type: :boolean),
|
50
|
+
ARGV::Opt.new(OPT_LIST[:clone], short_key:OPT_LIST[:clone_s], info:'下载一组仓库(包含不被mgit管理的仓库),如: mgit sync -c repo1 repo2。'),
|
51
|
+
ARGV::Opt.new(OPT_LIST[:pull],
|
52
|
+
short_key:OPT_LIST[:pull_s],
|
53
|
+
info:'同步本地仓库后执行pull操作更新,配合其他指令使用,如: mgit sync -a -p。',
|
54
|
+
type: :boolean),
|
55
|
+
ARGV::Opt.new(OPT_LIST[:url],
|
56
|
+
short_key:OPT_LIST[:url_s],
|
57
|
+
info:'校验并同步URL与配置不一致的仓库,如: mgit sync -u。',
|
58
|
+
type: :boolean)
|
59
|
+
].concat(super)
|
60
|
+
end
|
61
|
+
|
62
|
+
def validate(argv)
|
63
|
+
opt_all = argv.opt(OPT_LIST[:all])
|
64
|
+
opt_clone = argv.opt(OPT_LIST[:clone])
|
65
|
+
opt_new = argv.opt(OPT_LIST[:new_repo])
|
66
|
+
invalid = [opt_all, opt_clone, opt_new].select { |e| !e.nil? }.length > 1
|
67
|
+
Foundation.help!("请勿同时指定[#{OPT_LIST[:all]}|#{OPT_LIST[:all_s]}],[#{OPT_LIST[:clone]}|#{OPT_LIST[:clone_s]}]和[#{OPT_LIST[:new_repo]}|#{OPT_LIST[:new_repo_s]}]。") if invalid
|
68
|
+
end
|
69
|
+
|
70
|
+
def execute(argv)
|
71
|
+
Output.puts_start_cmd
|
72
|
+
|
73
|
+
if argv.opt_list.did_set_opt?(OPT_LIST[:all])
|
74
|
+
setup_all_sync_reops(argv)
|
75
|
+
elsif argv.opt_list.did_set_opt?(OPT_LIST[:new_repo])
|
76
|
+
setup_new_reops
|
77
|
+
elsif argv.opt_list.did_set_opt?(OPT_LIST[:url])
|
78
|
+
setup_config_url_repos
|
79
|
+
elsif argv.opt_list.did_set_opt?(OPT_LIST[:clone])
|
80
|
+
return if !setup_download_reops(argv.opt(OPT_LIST[:clone]).value)
|
81
|
+
elsif argv.git_opts.length > 0
|
82
|
+
return if !setup_specified_repos(argv)
|
83
|
+
else
|
84
|
+
setup_normal_reops(argv)
|
85
|
+
end
|
86
|
+
|
87
|
+
if (@sync_repos.length + @update_repos.length + @download_repos.length) == 0
|
88
|
+
Output.puts_success_message("没有仓库需要同步!")
|
89
|
+
return
|
90
|
+
end
|
91
|
+
|
92
|
+
error_repos = {}
|
93
|
+
if @sync_repos.length > 0
|
94
|
+
Workspace.concurrent_enumerate_with_progress_bar(@sync_repos, "正在同步(锁定)以上仓库...") { |light_repo|
|
95
|
+
repo = Repo.generate_strictly(Workspace.root, light_repo)
|
96
|
+
error_message = Repo::SyncHelper.sync_exist_repo(repo, light_repo)
|
97
|
+
if !error_message.nil?
|
98
|
+
Lock.mutex_exec { error_repos[light_repo.name] = error_message }
|
99
|
+
end
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
if @update_repos.length > 0
|
104
|
+
Workspace.concurrent_enumerate_with_progress_bar(@update_repos, "正在更新以上仓库...") { |light_repo|
|
105
|
+
repo = Repo.generate_strictly(Workspace.root, light_repo)
|
106
|
+
success, output = repo.execute_git_cmd('pull', '')
|
107
|
+
if !success
|
108
|
+
Lock.mutex_exec { error_repos[light_repo.name] = output }
|
109
|
+
end
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
if @download_repos.length > 0
|
114
|
+
Workspace.sync_new_repos(@download_repos)
|
115
|
+
end
|
116
|
+
|
117
|
+
if error_repos.length > 0
|
118
|
+
Workspace.show_error(error_repos)
|
119
|
+
else
|
120
|
+
Output.puts_succeed_cmd(argv.absolute_cmd)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def prepare_repo_category
|
125
|
+
@sync_repos = []
|
126
|
+
@update_repos = []
|
127
|
+
@download_repos = []
|
128
|
+
end
|
129
|
+
|
130
|
+
# M/NM: 被/不被mgit管理
|
131
|
+
# E/NE: 仓库本地存在/不存在
|
132
|
+
# L/NL: 仓库被/不被锁定
|
133
|
+
# U/NU: 仓库有/没有远程地址
|
134
|
+
|
135
|
+
# mgit sync repo1 repo2 ... [--pull]
|
136
|
+
# 针对一组指定仓库进行:下载仓库:NE|U,同步仓库:E|L,更新仓库: E|U(可选)
|
137
|
+
def setup_specified_repos(argv)
|
138
|
+
prepare_repo_category
|
139
|
+
should_pull = argv.opt_list.did_set_opt?(OPT_LIST[:pull])
|
140
|
+
valid_repos, error_repo_names = check_valid_repos(parse_repo_name(argv))
|
141
|
+
valid_repos.each { |light_repo|
|
142
|
+
repo_exist = Dir.exist?(light_repo.abs_dest(Workspace.root))
|
143
|
+
if !repo_exist
|
144
|
+
if !light_repo.url.nil?
|
145
|
+
@download_repos.push(light_repo)
|
146
|
+
else
|
147
|
+
error_repo_names.push(light_repo.name)
|
148
|
+
end
|
149
|
+
else
|
150
|
+
if light_repo.lock
|
151
|
+
@sync_repos.push(light_repo)
|
152
|
+
end
|
153
|
+
|
154
|
+
if should_pull
|
155
|
+
if !light_repo.url.nil?
|
156
|
+
@update_repos.push(light_repo)
|
157
|
+
else
|
158
|
+
error_repo_names.push(light_repo.name)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
}
|
163
|
+
|
164
|
+
should_continue = true
|
165
|
+
error_repos = []
|
166
|
+
error_repos.push(['配置表中未定义(或未指定"remote-path")', error_repo_names]) if error_repo_names.length > 0
|
167
|
+
Output.interact_with_multi_selection_combined_repos(error_repos, "以上仓库状态异常", ['a: 跳过并继续', 'b: 终止']) { |input|
|
168
|
+
if input == 'b'
|
169
|
+
Output.puts_cancel_message
|
170
|
+
should_continue = false
|
171
|
+
end
|
172
|
+
} if error_repos.length > 0
|
173
|
+
return should_continue
|
174
|
+
end
|
175
|
+
|
176
|
+
# mgit sync [--pull]
|
177
|
+
# 下载仓库:M|NE|U, 同步仓库:M|E|L, 更新仓库:M|E|U(可选)
|
178
|
+
def setup_normal_reops(argv)
|
179
|
+
prepare_repo_category
|
180
|
+
should_pull = argv.opt_list.did_set_opt?(OPT_LIST[:pull])
|
181
|
+
Workspace.config.light_repos.each { |light_repo|
|
182
|
+
if !light_repo.mgit_excluded
|
183
|
+
repo_exist = Dir.exist?(light_repo.abs_dest(Workspace.root))
|
184
|
+
if !repo_exist && !light_repo.url.nil?
|
185
|
+
@download_repos.push(light_repo)
|
186
|
+
elsif repo_exist && light_repo.lock
|
187
|
+
@sync_repos.push(light_repo)
|
188
|
+
end
|
189
|
+
|
190
|
+
if repo_exist && should_pull && !light_repo.url.nil?
|
191
|
+
@update_repos.push(light_repo)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
}
|
195
|
+
end
|
196
|
+
|
197
|
+
# mgit sync --new
|
198
|
+
# 下载仓库:M|NE|U
|
199
|
+
def setup_new_reops
|
200
|
+
prepare_repo_category
|
201
|
+
Workspace.config.light_repos.each { |light_repo|
|
202
|
+
if !light_repo.mgit_excluded
|
203
|
+
repo_exist = Dir.exist?(light_repo.abs_dest(Workspace.root))
|
204
|
+
@download_repos.push(light_repo) if !repo_exist && !light_repo.url.nil?
|
205
|
+
end
|
206
|
+
}
|
207
|
+
end
|
208
|
+
|
209
|
+
# mgit sync --all [--pull]
|
210
|
+
# 下载仓库:NE|U,同步仓库:E|L,更新仓库: E|NL|U(可选)
|
211
|
+
def setup_all_sync_reops(argv)
|
212
|
+
prepare_repo_category
|
213
|
+
Workspace.config.light_repos.each { |light_repo|
|
214
|
+
repo_exist = Dir.exist?(light_repo.abs_dest(Workspace.root))
|
215
|
+
if !repo_exist && !light_repo.url.nil?
|
216
|
+
@download_repos.push(light_repo)
|
217
|
+
elsif repo_exist && light_repo.lock
|
218
|
+
@sync_repos.push(light_repo)
|
219
|
+
elsif repo_exist && !light_repo.url.nil? && argv.opt_list.did_set_opt?(OPT_LIST[:pull])
|
220
|
+
@update_repos.push(light_repo)
|
221
|
+
end
|
222
|
+
}
|
223
|
+
end
|
224
|
+
|
225
|
+
# mgit sync --clone repo1 repo2 ...
|
226
|
+
# 下载仓库:NE|U(指定)
|
227
|
+
def setup_download_reops(repo_names)
|
228
|
+
prepare_repo_category
|
229
|
+
existing_repos = []
|
230
|
+
valid_repos, error_repo_names = check_valid_repos(repo_names)
|
231
|
+
|
232
|
+
valid_repos.each { |light_repo|
|
233
|
+
repo_exist = Dir.exist?(light_repo.abs_dest(Workspace.root))
|
234
|
+
if !repo_exist
|
235
|
+
if !light_repo.url.nil?
|
236
|
+
@download_repos.push(light_repo)
|
237
|
+
else
|
238
|
+
error_repo_names.push(light_repo.name)
|
239
|
+
end
|
240
|
+
else
|
241
|
+
error_repo_names.push(light_repo.name) if light_repo.url.nil?
|
242
|
+
existing_repos.push(light_repo.name)
|
243
|
+
end
|
244
|
+
}
|
245
|
+
|
246
|
+
should_continue = true
|
247
|
+
error_repos = []
|
248
|
+
error_repos.push(['配置表中未定义(或未指定"remote-path")', error_repo_names]) if error_repo_names.length > 0
|
249
|
+
error_repos.push(['本地已经存在', existing_repos]) if existing_repos.length > 0
|
250
|
+
Output.interact_with_multi_selection_combined_repos(error_repos, "以上仓库状态异常", ['a: 跳过并继续', 'b: 终止']) { |input|
|
251
|
+
if input == 'b'
|
252
|
+
Output.puts_cancel_message
|
253
|
+
should_continue = false
|
254
|
+
end
|
255
|
+
} if error_repos.length > 0
|
256
|
+
return should_continue
|
257
|
+
end
|
258
|
+
|
259
|
+
# mgit sync -u
|
260
|
+
# 同步仓库:E | url不一致
|
261
|
+
def setup_config_url_repos()
|
262
|
+
prepare_repo_category
|
263
|
+
|
264
|
+
warning_repos = []
|
265
|
+
missing_repos = []
|
266
|
+
Workspace.config.light_repos.each { |light_repo|
|
267
|
+
if !light_repo.mgit_excluded
|
268
|
+
repo, _ = Repo.generate_softly(Workspace.root, light_repo)
|
269
|
+
if !repo.nil?
|
270
|
+
original_url = repo.status_checker.default_url
|
271
|
+
target_url = light_repo.url
|
272
|
+
warning_repos.push(light_repo) if !Utils.url_consist?(original_url, target_url)
|
273
|
+
else
|
274
|
+
missing_repos.push(light_repo)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
}
|
278
|
+
|
279
|
+
Output.puts_remind_block(missing_repos.map { |repo| repo.name }, "以上仓库本地缺失,无法校验URL,请执行\"mgit sync -n\"重新下载后重试!") if missing_repos.length > 0
|
280
|
+
|
281
|
+
if warning_repos.length > 0
|
282
|
+
if Output.continue_with_interact_repos?(warning_repos.map { |repo| repo.name }, "以上仓库的当前URL(origin)和配置表指定URL不一致,建议\n 1. 执行\"mgit delete repo1 repo2...\"删除仓库.\n 2. 执行\"mgit sync -n\"重新下载。\n 继续强制设置可能导致仓库出错,是否继续?")
|
283
|
+
@sync_repos = warning_repos
|
284
|
+
else
|
285
|
+
Output.puts_cancel_message
|
286
|
+
exit
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def check_valid_repos(repo_names)
|
292
|
+
specified_repos = repo_names.map { |name| name.downcase }
|
293
|
+
all_valid_repos = Workspace.config.light_repos.map { |light_repo| light_repo.name.downcase }
|
294
|
+
error_repo_names = specified_repos - all_valid_repos
|
295
|
+
valid_repo_names = specified_repos - error_repo_names
|
296
|
+
valid_repos = Workspace.config.light_repos.select { |light_repo|
|
297
|
+
valid_repo_names.include?(light_repo.name.downcase)
|
298
|
+
}
|
299
|
+
[valid_repos, error_repo_names]
|
300
|
+
end
|
301
|
+
|
302
|
+
|
303
|
+
def parse_repo_name(argv)
|
304
|
+
return if argv.git_opts.nil?
|
305
|
+
|
306
|
+
repos = argv.git_opts.split(' ')
|
307
|
+
extra_opts = repos.select { |e| argv.is_option?(e) }
|
308
|
+
Foundation.help!("输入非法参数:#{extra_opts.join(',')}。请通过\"mgit #{argv.cmd} --help\"查看用法。") if extra_opts.length > 0
|
309
|
+
Foundation.help!("未输入查询仓库名!请使用这种形式查询:mgit info repo1 repo2 ...") if repos.length == 0
|
310
|
+
repos.map { |e| e.downcase }
|
311
|
+
end
|
312
|
+
|
313
|
+
def enable_short_basic_option
|
314
|
+
true
|
315
|
+
end
|
316
|
+
|
317
|
+
def self.description
|
318
|
+
"根据配置表(从远端或本地)同步仓库到工作区,包括被锁定仓库,已经在工作的不作处理(默认不执行pull)。"
|
319
|
+
end
|
320
|
+
|
321
|
+
def self.usage
|
322
|
+
"mgit sync [-a|-n|-c] [<repo>...] [-p] [-o] [-h]"
|
323
|
+
end
|
324
|
+
|
325
|
+
end
|
326
|
+
|
327
|
+
end
|