m-git 2.5.4

Sign up to get free protection for your applications and to get access to all the features.
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