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,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