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