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,348 @@
|
|
1
|
+
#coding=utf-8
|
2
|
+
|
3
|
+
module MGit
|
4
|
+
|
5
|
+
# @!scope 类似 git rebase
|
6
|
+
#
|
7
|
+
class Rebase < BaseCommand
|
8
|
+
|
9
|
+
PROGRESS_STAGE = {
|
10
|
+
:new_start => 0,
|
11
|
+
:did_pull_config => 1,
|
12
|
+
:did_refresh_config => 2,
|
13
|
+
:did_pull_sub => 3
|
14
|
+
}.freeze
|
15
|
+
PROGRESS_STAGE_KEY = 'progress_stage'
|
16
|
+
|
17
|
+
PROGRESS_AUTO = 'auto_exec'
|
18
|
+
|
19
|
+
OPT_LIST = {
|
20
|
+
:pull => '--pull'
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
def options
|
24
|
+
[
|
25
|
+
ARGV::Opt.new(OPT_LIST[:pull], info:'可选参数,指定后在合并仓库前会拉取远程分支更新代码,如:"mgit rabase --pull"。', type: :boolean)
|
26
|
+
].concat(super)
|
27
|
+
end
|
28
|
+
|
29
|
+
def execute(argv)
|
30
|
+
return if do_abort(argv)
|
31
|
+
|
32
|
+
check_master_rebase(argv)
|
33
|
+
Workspace.check_branch_consistency
|
34
|
+
|
35
|
+
Output.puts_start_cmd
|
36
|
+
|
37
|
+
config_repo = generate_config_repo
|
38
|
+
|
39
|
+
if mgit_try_to_continue?
|
40
|
+
# 不处于中间态禁止执行
|
41
|
+
Foundation.help!("当前并不处于操作中间态,无法进行continue操作!") if !OperationProgressManager.is_in_progress?(Workspace.root, __progress_type)
|
42
|
+
|
43
|
+
# 读取指令缓存失败禁止执行
|
44
|
+
context, _ = OperationProgressManager.load_context(Workspace.root, __progress_type)
|
45
|
+
Foundation.help!("缓存指令读取失败,continue无法继续进行,请重新执行完整指令。") if context.nil? || !context.validate?
|
46
|
+
|
47
|
+
# 分支不匹配禁止执行
|
48
|
+
Foundation.help!("当前主仓库所在分支跟上次操作时所在分支(#{context.branch})不一致,请切换后重试。") if config_repo.status_checker.current_branch(use_cache:true) != context.branch
|
49
|
+
|
50
|
+
if !context.repos.nil?
|
51
|
+
Output.puts_processing_message("加载上次即将操作的子仓库...")
|
52
|
+
Workspace.update_all_repos(context.repos)
|
53
|
+
end
|
54
|
+
|
55
|
+
cmd = context.cmd
|
56
|
+
opts = context.opts
|
57
|
+
config_error = continue_execute(cmd, opts, config_repo, context.other[PROGRESS_STAGE_KEY], context.other[PROGRESS_AUTO])
|
58
|
+
if config_error
|
59
|
+
Output.puts_fail_block([config_repo.name], "主仓库操作失败:#{config_error}")
|
60
|
+
return
|
61
|
+
end
|
62
|
+
else
|
63
|
+
# 处于中间态则提示
|
64
|
+
if OperationProgressManager.is_in_progress?(Workspace.root, __progress_type)
|
65
|
+
if Output.continue_with_user_remind?("当前处于操作中间态,建议取消操作并执行\"mgit merge --continue\"继续操作未完成仓库。\n 继续执行将清除中间态并重新操作所有仓库,是否取消?")
|
66
|
+
Output.puts_cancel_message
|
67
|
+
return
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
cmd = argv.cmd
|
72
|
+
opts = argv.git_opts
|
73
|
+
|
74
|
+
# 优先操作配置仓库
|
75
|
+
config_error = rebase_config_repo(cmd, opts, config_repo, argv.opt_list.did_set_opt?(OPT_LIST[:pull]))
|
76
|
+
if config_error
|
77
|
+
Output.puts_fail_block([config_repo.name], "主仓库操作失败:#{config_error}")
|
78
|
+
return
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
do_repos = []
|
83
|
+
dirty_repos = []
|
84
|
+
detached_repos = []
|
85
|
+
no_tracking_repos = []
|
86
|
+
|
87
|
+
Output.puts_processing_message("检查各仓库状态...")
|
88
|
+
Workspace.serial_enumerate_with_progress(all_repos) { |repo|
|
89
|
+
next if !config_repo.nil? && repo.name == config_repo.name
|
90
|
+
|
91
|
+
status = repo.status_checker.status
|
92
|
+
branch_status = repo.status_checker.branch_status
|
93
|
+
|
94
|
+
if status == Repo::Status::GIT_REPO_STATUS[:clean] &&
|
95
|
+
branch_status != Repo::Status::GIT_BRANCH_STATUS[:detached] &&
|
96
|
+
branch_status != Repo::Status::GIT_BRANCH_STATUS[:no_tracking]
|
97
|
+
do_repos.push(repo)
|
98
|
+
else
|
99
|
+
if status == Repo::Status::GIT_REPO_STATUS[:dirty]
|
100
|
+
dirty_repos.push(repo)
|
101
|
+
end
|
102
|
+
if branch_status == Repo::Status::GIT_BRANCH_STATUS[:detached]
|
103
|
+
detached_repos.push(repo)
|
104
|
+
elsif branch_status == Repo::Status::GIT_BRANCH_STATUS[:no_tracking]
|
105
|
+
no_tracking_repos.push(repo)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
}
|
109
|
+
Output.puts_success_message("检查完成!\n")
|
110
|
+
|
111
|
+
if dirty_repos.length > 0 || no_tracking_repos.length > 0 || detached_repos.length > 0
|
112
|
+
remind_repos = []
|
113
|
+
remind_repos.push(['有本地改动', dirty_repos.map { |e| e.name }])
|
114
|
+
remind_repos.push(['未追踪远程分支(建议:mgit branch -u origin/<branch>)', no_tracking_repos.map { |e| e.name }]) if no_tracking_repos.length > 0
|
115
|
+
remind_repos.push(['HEAD游离,当前不在任何分支上', detached_repos.map { |e| e.name }]) if detached_repos.length > 0
|
116
|
+
Output.interact_with_multi_selection_combined_repos(remind_repos, "以上仓库状态异常", ['a: 跳过并继续', 'b: 强制执行', 'c: 终止']) { |input|
|
117
|
+
if input == 'b'
|
118
|
+
do_repos += dirty_repos
|
119
|
+
do_repos += detached_repos
|
120
|
+
do_repos += no_tracking_repos
|
121
|
+
do_repos.uniq! { |repo| repo.name }
|
122
|
+
elsif input == 'c' || input != 'a'
|
123
|
+
Output.puts_cancel_message
|
124
|
+
return
|
125
|
+
end
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
if do_repos.length == 0
|
130
|
+
Output.puts_remind_message("没有仓库需要执行rebase指令!") if config_repo.nil?
|
131
|
+
else
|
132
|
+
Output.puts_processing_message("开始rebase子仓库...")
|
133
|
+
_, error_repos = Workspace.execute_git_cmd_with_repos(cmd, opts, do_repos)
|
134
|
+
end
|
135
|
+
|
136
|
+
Output.puts_succeed_cmd("#{cmd} #{opts}") if config_error.nil? || error_repos.empty?
|
137
|
+
|
138
|
+
# 清除中间态
|
139
|
+
OperationProgressManager.remove_progress(Workspace.root, __progress_type)
|
140
|
+
end
|
141
|
+
|
142
|
+
# 合并主仓库
|
143
|
+
#
|
144
|
+
# @param cmd [String] 合并指令
|
145
|
+
#
|
146
|
+
# @param opts [String] 合并参数
|
147
|
+
#
|
148
|
+
# @param repo [Repo] 配置仓库对象
|
149
|
+
#
|
150
|
+
# @param exec_repos [Array<Repo>] 本次操作的所有仓库(含配置仓库)
|
151
|
+
#
|
152
|
+
def rebase_config_repo(cmd, opts, repo, auto_update)
|
153
|
+
return if repo.nil?
|
154
|
+
branch_status = repo.status_checker.branch_status
|
155
|
+
if branch_status == Repo::Status::GIT_BRANCH_STATUS[:detached]
|
156
|
+
remind_config_repo_fail("主仓库\"#{repo.name}\"HEAD游离,当前不在任何分支上,无法执行!")
|
157
|
+
elsif branch_status == Repo::Status::GIT_BRANCH_STATUS[:no_tracking]
|
158
|
+
remind_config_repo_fail("主仓库\"#{repo.name}\"未跟踪对应远程分支,无法执行!(需要执行'mgit branch -u origin/<branch>')")
|
159
|
+
elsif repo.status_checker.status == Repo::Status::GIT_REPO_STATUS[:dirty]
|
160
|
+
remind_config_repo_fail("主仓库\"#{repo.name}\"有改动,无法执行\"#{cmd}\"!")
|
161
|
+
else
|
162
|
+
return continue_execute(cmd, opts, repo, PROGRESS_STAGE[:new_start], auto_update)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def continue_execute(cmd, opts, repo, check_point, auto_update)
|
167
|
+
|
168
|
+
# 现场信息
|
169
|
+
exec_subrepos = all_repos(except_config:true)
|
170
|
+
is_all = Workspace.is_all_exec_sub_repos?(exec_subrepos)
|
171
|
+
context = OperationProgressContext.new(__progress_type)
|
172
|
+
context.cmd = cmd
|
173
|
+
context.opts = opts
|
174
|
+
context.repos = is_all ? nil : exec_subrepos.map { |e| e.name } # nil表示操作所有子仓库
|
175
|
+
context.branch = repo.status_checker.current_branch(use_cache:true)
|
176
|
+
|
177
|
+
# 更新主仓库
|
178
|
+
update_config_repo(repo, context, auto_update) if check_point < PROGRESS_STAGE[:did_pull_config]
|
179
|
+
|
180
|
+
if check_point < PROGRESS_STAGE[:did_refresh_config]
|
181
|
+
# 操作主仓库
|
182
|
+
config_error = exec_config_repo(repo, cmd, opts)
|
183
|
+
return config_error if config_error
|
184
|
+
# 更新配置表
|
185
|
+
refresh_config(repo, context, auto_update)
|
186
|
+
end
|
187
|
+
|
188
|
+
if check_point < PROGRESS_STAGE[:did_pull_sub]
|
189
|
+
# 如果本次操作所有子仓库,则再次获取所有子仓库(因为配置表可能已经更新,子仓库列表也有更新,此处获取的仓库包含:已有的子仓库 + 合并后新下载仓库 + 从缓存弹出的仓库)
|
190
|
+
exec_subrepos = all_repos(except_config:true) if context.repos.nil?
|
191
|
+
# 更新子仓库
|
192
|
+
update_subrepos(exec_subrepos, context, auto_update)
|
193
|
+
end
|
194
|
+
|
195
|
+
config_error
|
196
|
+
end
|
197
|
+
|
198
|
+
def update_config_repo(repo, context, auto)
|
199
|
+
if auto || Output.continue_with_user_remind?("即将合并主仓库,是否先拉取远程代码更新?")
|
200
|
+
Output.puts_processing_message("正在更新主仓库...")
|
201
|
+
success, output = repo.execute_git_cmd('pull', '')
|
202
|
+
if !success
|
203
|
+
context.other = {
|
204
|
+
PROGRESS_STAGE_KEY => PROGRESS_STAGE[:did_pull_config],
|
205
|
+
PROGRESS_AUTO => auto
|
206
|
+
}
|
207
|
+
OperationProgressManager.trap_into_progress(Workspace.root, context)
|
208
|
+
show_progress_error("主仓库更新失败", "#{output}")
|
209
|
+
else
|
210
|
+
Output.puts_success_message("更新成功!\n")
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def exec_config_repo(repo, cmd, opts)
|
216
|
+
error = nil
|
217
|
+
Output.puts_processing_message("开始操作主仓库...")
|
218
|
+
success, output = repo.execute_git_cmd(cmd, opts)
|
219
|
+
if success
|
220
|
+
Output.puts_success_message("操作成功!\n")
|
221
|
+
else
|
222
|
+
Output.puts_fail_message("操作失败!\n")
|
223
|
+
error = output
|
224
|
+
end
|
225
|
+
error
|
226
|
+
end
|
227
|
+
|
228
|
+
# 刷新配置表
|
229
|
+
def refresh_config(repo, context, auto)
|
230
|
+
begin
|
231
|
+
Workspace.update_config(strict_mode:false) { |missing_repos|
|
232
|
+
if missing_repos.length > 0
|
233
|
+
# 这里分支引导仅根据主仓库来进行,如果使用all_repos来作为引导
|
234
|
+
# 基准,可能不准确(因为all_repos可能包含merge分支已有的本地
|
235
|
+
# 仓库,而这些仓库所在分支可能五花八门,数量也可能多于处于正确
|
236
|
+
# 分支的仓库)。
|
237
|
+
success_missing_repos = Workspace.guide_to_checkout_branch(missing_repos, [repo], append_message:"拒绝该操作本次执行将忽略以上仓库")
|
238
|
+
all_repos.concat(success_missing_repos)
|
239
|
+
# success_missing_repos包含新下载的和当前分支已有的新仓库,其中已有仓库包含在@all_repos内,需要去重
|
240
|
+
all_repos.uniq! { |repo| repo.name }
|
241
|
+
end
|
242
|
+
}
|
243
|
+
refresh_context(context)
|
244
|
+
rescue Error => e
|
245
|
+
if e.type == MGIT_ERROR_TYPE[:config_generate_error]
|
246
|
+
context.other = {
|
247
|
+
PROGRESS_STAGE_KEY => PROGRESS_STAGE[:did_refresh_config],
|
248
|
+
PROGRESS_AUTO => auto
|
249
|
+
}
|
250
|
+
|
251
|
+
OperationProgressManager.trap_into_progress(Workspace.root, context)
|
252
|
+
show_progress_error("配置表生成失败", "#{e.msg}")
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def update_subrepos(subrepos, context, auto)
|
258
|
+
if auto || Output.continue_with_user_remind?("即将合并子仓库,是否先拉取远程代码更新?")
|
259
|
+
Output.puts_processing_message("正在更新子仓库...")
|
260
|
+
_, error_repos = Workspace.execute_git_cmd_with_repos('pull', '', subrepos)
|
261
|
+
if error_repos.length > 0
|
262
|
+
context.other = {
|
263
|
+
PROGRESS_STAGE_KEY => PROGRESS_STAGE[:did_pull_sub],
|
264
|
+
PROGRESS_AUTO => auto
|
265
|
+
}
|
266
|
+
OperationProgressManager.trap_into_progress(Workspace.root, context)
|
267
|
+
show_progress_error("子仓库更新失败", "见上述输出")
|
268
|
+
else
|
269
|
+
Output.puts_success_message("更新成功!\n")
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def refresh_context(context)
|
275
|
+
exec_subrepos = all_repos(except_config:true)
|
276
|
+
is_all = Workspace.is_all_exec_sub_repos?(exec_subrepos)
|
277
|
+
context.repos = is_all ? nil : exec_subrepos.map { |e| e.name } # nil表示操作所有子仓库
|
278
|
+
end
|
279
|
+
|
280
|
+
def remind_config_repo_fail(msg)
|
281
|
+
Output.puts_fail_message(msg)
|
282
|
+
return if Output.continue_with_user_remind?("是否继续操作其余仓库?")
|
283
|
+
Output.puts_cancel_message
|
284
|
+
exit
|
285
|
+
end
|
286
|
+
|
287
|
+
def check_master_rebase(argv)
|
288
|
+
opt_arr = argv.git_opts(raw:false)
|
289
|
+
opt_arr.each { |opts|
|
290
|
+
Foundation.help!("当前版本不支持\"-i\"或\"--interactive\"参数,请重试。") if ['-i','--interactive'].include?(opts.first)
|
291
|
+
}
|
292
|
+
end
|
293
|
+
|
294
|
+
def do_abort(argv)
|
295
|
+
return false unless argv.git_opts.include?('--abort')
|
296
|
+
Output.puts_start_cmd
|
297
|
+
OperationProgressManager.remove_progress(Workspace.root, __progress_type)
|
298
|
+
do_repos = all_repos.select { |repo| repo.status_checker.is_in_rebase_progress? }
|
299
|
+
|
300
|
+
if do_repos.length > 0
|
301
|
+
append_message = ",另有#{all_repos.length - do_repos.length}个仓库无须操作" if do_repos.length < all_repos.length
|
302
|
+
Output.puts_processing_block(do_repos.map { |e| e.name }, "开始操作以上仓库#{append_message}...")
|
303
|
+
_, error_repos = Workspace.execute_git_cmd_with_repos(argv.cmd, argv.git_opts, do_repos)
|
304
|
+
Output.puts_succeed_cmd(argv.absolute_cmd) if error_repos.length == 0
|
305
|
+
else
|
306
|
+
Output.puts_success_message("没有仓库需要操作!")
|
307
|
+
end
|
308
|
+
|
309
|
+
true
|
310
|
+
end
|
311
|
+
|
312
|
+
def show_progress_error(summary, detail)
|
313
|
+
error = "#{summary} 已进入操作中间态。
|
314
|
+
|
315
|
+
原因:
|
316
|
+
#{detail}
|
317
|
+
|
318
|
+
可选:
|
319
|
+
- 使用\"mgit rebase --continue\"继续变基。
|
320
|
+
- 使用\"mgit rebase --abort\"取消变基。"
|
321
|
+
Foundation.help!(error, title:'暂停')
|
322
|
+
end
|
323
|
+
|
324
|
+
def enable_repo_selection
|
325
|
+
true
|
326
|
+
end
|
327
|
+
|
328
|
+
def enable_continue_operation
|
329
|
+
true
|
330
|
+
end
|
331
|
+
|
332
|
+
def self.description
|
333
|
+
"重新将提交应用到其他基点,该命令不执行lock的仓库。"
|
334
|
+
end
|
335
|
+
|
336
|
+
def self.usage
|
337
|
+
"mgit rebase [<git-rebase-option>] [(--mrepo|--el-mrepo) <repo>...] [--help]\nmgit rebase --continue\nmgit rebase --abort"
|
338
|
+
end
|
339
|
+
|
340
|
+
private
|
341
|
+
|
342
|
+
def __progress_type
|
343
|
+
OperationProgressManager::PROGRESS_TYPE[:rebase]
|
344
|
+
end
|
345
|
+
|
346
|
+
end
|
347
|
+
|
348
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#coding=utf-8
|
2
|
+
|
3
|
+
module MGit
|
4
|
+
|
5
|
+
# @!scope 类似 git reset
|
6
|
+
#
|
7
|
+
class Reset < BaseCommand
|
8
|
+
|
9
|
+
def execute(argv)
|
10
|
+
Workspace.check_branch_consistency
|
11
|
+
|
12
|
+
Output.puts_start_cmd
|
13
|
+
_, error_repos = Workspace.execute_git_cmd_with_repos(argv.cmd, argv.git_opts, all_repos)
|
14
|
+
Output.puts_succeed_cmd(argv.absolute_cmd) if error_repos.length == 0
|
15
|
+
end
|
16
|
+
|
17
|
+
def enable_repo_selection
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.description
|
22
|
+
"将当前HEAD指针还原到指定状态。"
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.usage
|
26
|
+
"mgit reset [<git-reset-option>] [(--mrepo|--el-mrepo) <repo>...] [--help]"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
#coding=utf-8
|
2
|
+
|
3
|
+
module MGit
|
4
|
+
|
5
|
+
# 该指令用于不带指令的输入:mgit --help,用于执行mgit的一级参数(如"mgit --help"的"--help")
|
6
|
+
#
|
7
|
+
class Self < BaseCommand
|
8
|
+
|
9
|
+
HELP_INTRODUCTION = <<INTRO
|
10
|
+
#{Output.info_title("Description:")}
|
11
|
+
|
12
|
+
mgit是多仓库管理工具,通过将git指令作用到多个仓库,实现批量的版本管理功能
|
13
|
+
|
14
|
+
更多介绍:https://github.com/baidu/mgit
|
15
|
+
|
16
|
+
INTRO
|
17
|
+
|
18
|
+
OPT_LIST = {
|
19
|
+
:all => '--all',
|
20
|
+
:all_s => '-a',
|
21
|
+
:list => '--list',
|
22
|
+
:list_s => '-l',
|
23
|
+
:size => '--size',
|
24
|
+
:size_s => '-s',
|
25
|
+
:version => '--version',
|
26
|
+
:version_s => '-v',
|
27
|
+
:workspace => '--workspace',
|
28
|
+
:workspace_s => '-w'
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
def options
|
32
|
+
return [
|
33
|
+
ARGV::Opt.new(OPT_LIST[:list], short_key:OPT_LIST[:list_s], info:"显示MGit管理的仓库。", type: :boolean),
|
34
|
+
ARGV::Opt.new(OPT_LIST[:size], short_key:OPT_LIST[:size_s], info:"显示MGit管理的仓库的磁盘占用量。", type: :boolean),
|
35
|
+
ARGV::Opt.new(OPT_LIST[:all], short_key:OPT_LIST[:all_s], info:"指定操作所有定义在manifest内的仓库,可配合-l合并使用: \"mgit -al\"。", type: :boolean),
|
36
|
+
ARGV::Opt.new(OPT_LIST[:version], short_key:OPT_LIST[:version_s], info:"显示当前MGit版本。", type: :boolean),
|
37
|
+
ARGV::Opt.new(OPT_LIST[:workspace], short_key:OPT_LIST[:workspace_s], info:"显示当前MGit工程管理根目录(.mgit所在目录)。", type: :boolean)
|
38
|
+
].concat(super)
|
39
|
+
end
|
40
|
+
|
41
|
+
def validate(argv)
|
42
|
+
Foundation.help!("输入非法参数:#{argv.git_opts}。请通过\"mgit --help\"查看用法。") if argv.git_opts.length > 0
|
43
|
+
end
|
44
|
+
|
45
|
+
def prepare
|
46
|
+
Workspace.setup_multi_repo_root
|
47
|
+
Workspace.setup_config
|
48
|
+
end
|
49
|
+
|
50
|
+
# --- 覆写,不需要预设操作 ---
|
51
|
+
def pre_exec
|
52
|
+
end
|
53
|
+
|
54
|
+
def post_exec
|
55
|
+
end
|
56
|
+
|
57
|
+
def usage(argv)
|
58
|
+
show_help(argv)
|
59
|
+
end
|
60
|
+
# -------------------------
|
61
|
+
|
62
|
+
def execute(argv)
|
63
|
+
# 如果存在多余(透传)指令则报错
|
64
|
+
extra_opt_str = argv.git_opts
|
65
|
+
if extra_opt_str.length > 0
|
66
|
+
Output.puts_fail_message("输入无效参数:#{extra_opt_str}\n")
|
67
|
+
show_help(argv)
|
68
|
+
exit
|
69
|
+
end
|
70
|
+
|
71
|
+
argv.enumerate_valid_opts { |opt|
|
72
|
+
if opt.key == OPT_LIST[:list] || opt.key == OPT_LIST[:list_s]
|
73
|
+
show_all_repos(argv)
|
74
|
+
return
|
75
|
+
elsif opt.key == OPT_LIST[:size] || opt.key == OPT_LIST[:size_s]
|
76
|
+
show_repo_size
|
77
|
+
return
|
78
|
+
elsif opt.key == OPT_LIST[:version] || opt.key == OPT_LIST[:version_s]
|
79
|
+
show_version
|
80
|
+
return
|
81
|
+
elsif opt.key == OPT_LIST[:workspace] || opt.key == OPT_LIST[:workspace_s]
|
82
|
+
show_workspace
|
83
|
+
return
|
84
|
+
end
|
85
|
+
}
|
86
|
+
|
87
|
+
# 无任何参数传入时显示帮助
|
88
|
+
show_help(argv)
|
89
|
+
end
|
90
|
+
|
91
|
+
def show_help(argv)
|
92
|
+
head_space = ' '
|
93
|
+
middle_space = ' '
|
94
|
+
|
95
|
+
output = HELP_INTRODUCTION # "#{Output.info_title("Description:")}\n\n#{head_space}mgit是多仓库管理工具,通过将git指令作用到多个仓库,实现批量的版本管理功能。"
|
96
|
+
output += "#{Output.info_title("Usage:")}\n\n#{head_space}$ #{Output.green_message("mgit <mgit_options>")}\n"
|
97
|
+
output += "#{head_space}$ #{Output.green_message("mgit <command> [<command_option>...] [<value>...]")}\n\n"
|
98
|
+
|
99
|
+
# mgit options
|
100
|
+
output += "#{Output.info_title("MGit Option:")}\n\n"
|
101
|
+
divider = ", "
|
102
|
+
longest_opt = argv.opt_list.opts.max_by { |e| "#{Output.blue_message("#{e.key}#{divider + e.short_key if !e.short_key.nil?}")}".length }
|
103
|
+
max_opt_length = "#{longest_opt.short_key + divider + longest_opt.key}".length
|
104
|
+
mgit_option_info = ''
|
105
|
+
argv.opt_list.opts.each { |opt|
|
106
|
+
key = "#{opt.short_key + divider + opt.key}"
|
107
|
+
mgit_option_info += "#{head_space}#{Output.blue_message(key)}#{' ' * (max_opt_length - key.length + middle_space.length)}#{argv.info(opt.key)}\n"
|
108
|
+
}
|
109
|
+
output += mgit_option_info + "\n"
|
110
|
+
|
111
|
+
# subcommand
|
112
|
+
output += "#{Output.info_title("Command:")}\n\n"
|
113
|
+
cmd_header = '+ '
|
114
|
+
cmd_info = ''
|
115
|
+
|
116
|
+
max_cmd_length = Output.blue_message(cmd_header + CommandManager.commands.keys.max_by { |e| e.length }.to_s).length
|
117
|
+
CommandManager.commands.keys.sort.each { |cmd_name|
|
118
|
+
next if cmd_name == self.class.cmd
|
119
|
+
cls_name = CommandManager.commands[cmd_name]
|
120
|
+
cmd_name = Output.green_message(cmd_header + cmd_name.downcase.to_s)
|
121
|
+
cmd_info += "#{head_space}#{cmd_name}#{' ' * (max_cmd_length - cmd_name.length + middle_space.length)}#{cls_name.description}\n"
|
122
|
+
}
|
123
|
+
output += cmd_info + "\n"
|
124
|
+
output += "#{Output.info_title("Command Option:")}\n\n#{head_space}请通过[ mgit <command> --help ]查看。\n\n"
|
125
|
+
output += "#{Output.info_title("Version:")}\n\n#{head_space}#{MGit::VERSION}\n"
|
126
|
+
|
127
|
+
puts output
|
128
|
+
end
|
129
|
+
|
130
|
+
def show_all_repos(argv)
|
131
|
+
prepare
|
132
|
+
|
133
|
+
list_all = argv.opt_list.did_set_opt?(OPT_LIST[:all])
|
134
|
+
existing_repos, missing_repos = prepare_repos(with_excluded:list_all)
|
135
|
+
|
136
|
+
list = {}
|
137
|
+
if existing_repos.length > 0
|
138
|
+
existing_repos.sort_by { |e| e.name }.each { |light_repo|
|
139
|
+
dir = File.join("<ROOT>", File.dirname(light_repo.path)).bold
|
140
|
+
list[dir] = [] if list[dir].nil?
|
141
|
+
list[dir].push(light_repo.name)
|
142
|
+
}
|
143
|
+
end
|
144
|
+
|
145
|
+
if missing_repos.length > 0
|
146
|
+
list['本地缺失'.bold] = missing_repos.sort_by { |e| e.name }.map { |e| e.name }
|
147
|
+
end
|
148
|
+
|
149
|
+
list_array = []
|
150
|
+
list.each { |dir, list|
|
151
|
+
list_array.push([dir.bold, list])
|
152
|
+
}
|
153
|
+
|
154
|
+
puts Output.generate_table_combination(list_array, separator:'|')
|
155
|
+
if list_all
|
156
|
+
message = "共统计#{existing_repos.length + missing_repos.length}个仓库。"
|
157
|
+
else
|
158
|
+
message = "mgit目前共管理#{existing_repos.length + missing_repos.length}个仓库。"
|
159
|
+
end
|
160
|
+
Output.puts_remind_message(message)
|
161
|
+
Output.puts_fail_message("有#{missing_repos.length}个仓库本地缺失!") if missing_repos.length > 0
|
162
|
+
end
|
163
|
+
|
164
|
+
def show_repo_size
|
165
|
+
prepare
|
166
|
+
Workspace.setup_all_repos
|
167
|
+
Output.puts_processing_message("开始计算...")
|
168
|
+
repo_size = {}
|
169
|
+
mutex = Mutex.new
|
170
|
+
task_count = 0
|
171
|
+
Output.update_progress(all_repos.length, task_count)
|
172
|
+
Workspace.concurrent_enumerate(all_repos) { |repo|
|
173
|
+
success, output = repo.execute("du -sh #{repo.path}")
|
174
|
+
mutex.lock
|
175
|
+
if success
|
176
|
+
repo_size[repo.name] = output&.strip
|
177
|
+
end
|
178
|
+
task_count += 1
|
179
|
+
Output.update_progress(all_repos.length, task_count)
|
180
|
+
mutex.unlock
|
181
|
+
}
|
182
|
+
Output.puts_success_message("计算完成。")
|
183
|
+
|
184
|
+
display_size = repo_size.sort_by { |k,v| k}.map { |e| e.last }
|
185
|
+
Output.puts_remind_block(display_size, "共统计#{repo_size.length}个仓库。")
|
186
|
+
end
|
187
|
+
|
188
|
+
def prepare_repos(with_excluded:false)
|
189
|
+
existing_repos = []
|
190
|
+
missing_repos = []
|
191
|
+
Workspace.config.light_repos.each { |light_repo|
|
192
|
+
if with_excluded || !light_repo.mgit_excluded
|
193
|
+
repo_exist = Repo.is_git_repo?(light_repo.abs_dest(Workspace.root))
|
194
|
+
if repo_exist
|
195
|
+
existing_repos.push(light_repo)
|
196
|
+
else
|
197
|
+
missing_repos.push(light_repo)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
}
|
201
|
+
return existing_repos, missing_repos
|
202
|
+
end
|
203
|
+
|
204
|
+
def show_workspace
|
205
|
+
root = Workspace.multi_repo_root_path
|
206
|
+
if root.nil?
|
207
|
+
Output.puts_fail_message("当前不在任何多仓库目录下!")
|
208
|
+
else
|
209
|
+
puts root
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def show_version
|
214
|
+
puts MGit::VERSION
|
215
|
+
end
|
216
|
+
|
217
|
+
def enable_short_basic_option
|
218
|
+
return true
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|