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.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +85 -0
  4. data/lib/m-git.rb +66 -0
  5. data/lib/m-git/argv.rb +170 -0
  6. data/lib/m-git/argv/opt.rb +38 -0
  7. data/lib/m-git/argv/opt_list.rb +71 -0
  8. data/lib/m-git/argv/parser.rb +66 -0
  9. data/lib/m-git/base_command.rb +271 -0
  10. data/lib/m-git/command/add.rb +41 -0
  11. data/lib/m-git/command/branch.rb +90 -0
  12. data/lib/m-git/command/checkout.rb +106 -0
  13. data/lib/m-git/command/clean.rb +64 -0
  14. data/lib/m-git/command/commit.rb +84 -0
  15. data/lib/m-git/command/config.rb +202 -0
  16. data/lib/m-git/command/delete.rb +99 -0
  17. data/lib/m-git/command/fetch.rb +32 -0
  18. data/lib/m-git/command/forall.rb +81 -0
  19. data/lib/m-git/command/info.rb +74 -0
  20. data/lib/m-git/command/init.rb +324 -0
  21. data/lib/m-git/command/log.rb +73 -0
  22. data/lib/m-git/command/merge.rb +381 -0
  23. data/lib/m-git/command/pull.rb +364 -0
  24. data/lib/m-git/command/push.rb +311 -0
  25. data/lib/m-git/command/rebase.rb +348 -0
  26. data/lib/m-git/command/reset.rb +31 -0
  27. data/lib/m-git/command/self.rb +223 -0
  28. data/lib/m-git/command/stash.rb +189 -0
  29. data/lib/m-git/command/status.rb +135 -0
  30. data/lib/m-git/command/sync.rb +327 -0
  31. data/lib/m-git/command/tag.rb +67 -0
  32. data/lib/m-git/command_manager.rb +24 -0
  33. data/lib/m-git/error.rb +20 -0
  34. data/lib/m-git/foundation.rb +25 -0
  35. data/lib/m-git/foundation/constants.rb +107 -0
  36. data/lib/m-git/foundation/dir.rb +25 -0
  37. data/lib/m-git/foundation/duration_recorder.rb +92 -0
  38. data/lib/m-git/foundation/git_message_parser.rb +50 -0
  39. data/lib/m-git/foundation/lock.rb +32 -0
  40. data/lib/m-git/foundation/loger.rb +129 -0
  41. data/lib/m-git/foundation/mgit_config.rb +222 -0
  42. data/lib/m-git/foundation/operation_progress_manager.rb +139 -0
  43. data/lib/m-git/foundation/timer.rb +74 -0
  44. data/lib/m-git/foundation/utils.rb +361 -0
  45. data/lib/m-git/hooks_manager.rb +96 -0
  46. data/lib/m-git/manifest.rb +181 -0
  47. data/lib/m-git/manifest/cache_manager.rb +44 -0
  48. data/lib/m-git/manifest/internal.rb +182 -0
  49. data/lib/m-git/manifest/light_repo.rb +108 -0
  50. data/lib/m-git/manifest/light_repo_generator.rb +87 -0
  51. data/lib/m-git/manifest/linter.rb +153 -0
  52. data/lib/m-git/open_api.rb +427 -0
  53. data/lib/m-git/open_api/script_download_info.rb +37 -0
  54. data/lib/m-git/output/output.rb +461 -0
  55. data/lib/m-git/plugin_manager.rb +112 -0
  56. data/lib/m-git/repo.rb +133 -0
  57. data/lib/m-git/repo/status.rb +481 -0
  58. data/lib/m-git/repo/sync_helper.rb +254 -0
  59. data/lib/m-git/template.rb +9 -0
  60. data/lib/m-git/template/local_manifest.rb +27 -0
  61. data/lib/m-git/template/manifest_hook.rb +28 -0
  62. data/lib/m-git/template/post_download_hook.rb +29 -0
  63. data/lib/m-git/template/post_hook.rb +31 -0
  64. data/lib/m-git/template/pre_exec_hook.rb +31 -0
  65. data/lib/m-git/template/pre_hook.rb +29 -0
  66. data/lib/m-git/template/pre_push_hook.rb +32 -0
  67. data/lib/m-git/version.rb +6 -0
  68. data/lib/m-git/workspace.rb +648 -0
  69. data/lib/m-git/workspace/path_helper.rb +56 -0
  70. data/lib/m-git/workspace/workspace_helper.rb +159 -0
  71. data/m-git +1 -0
  72. data/mgit +19 -0
  73. 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