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,106 @@
1
+ #coding=utf-8
2
+
3
+ module MGit
4
+
5
+ # @!scope [command] checkout
6
+ # follow git checkout
7
+ # eg: mgit checkout master
8
+ #
9
+ class Checkout < BaseCommand
10
+
11
+ def execute(argv)
12
+ Output.puts_start_cmd
13
+
14
+ # 优先checkout配置仓库
15
+ config_repo = generate_config_repo
16
+ checkout_config_repo(argv.cmd, argv.git_opts, config_repo)
17
+
18
+ do_repos = []
19
+ dirty_repos = []
20
+
21
+ Output.puts_processing_message("检查各仓库状态...")
22
+ Workspace.serial_enumerate_with_progress(all_repos) { |repo|
23
+ if !config_repo.nil? && repo.name == config_repo.name
24
+ next
25
+ elsif repo.status_checker.status != Repo::Status::GIT_REPO_STATUS[:dirty]
26
+ do_repos.push(repo)
27
+ else
28
+ dirty_repos.push(repo)
29
+ end
30
+ }
31
+ Output.puts_success_message("检查完成!\n")
32
+
33
+ if dirty_repos.length > 0
34
+ remind_repos = []
35
+ remind_repos.push(['有本地改动', dirty_repos.map { |e| e.name }]) if dirty_repos.length > 0
36
+ Output.interact_with_multi_selection_combined_repos(remind_repos, "以上仓库状态异常", ['a: 跳过并继续', 'b: 强制执行', 'c: 终止']) { |input|
37
+ if input == 'b'
38
+ do_repos += dirty_repos
39
+ do_repos.uniq! { |repo| repo.name }
40
+ elsif input == 'c' || input != 'a'
41
+ Output.puts_cancel_message
42
+ return
43
+ end
44
+ }
45
+ end
46
+
47
+ if do_repos.length == 0
48
+ if config_repo.nil?
49
+ Output.puts_nothing_to_do_cmd
50
+ else
51
+ Output.puts_succeed_cmd(argv.absolute_cmd)
52
+ end
53
+ else
54
+ Output.puts_processing_message("开始checkout子仓库...")
55
+ _, error_repos = Workspace.execute_git_cmd_with_repos(argv.cmd, argv.git_opts, do_repos)
56
+ Output.puts_succeed_cmd(argv.absolute_cmd) if error_repos.length == 0
57
+ end
58
+ end
59
+
60
+ def checkout_config_repo(cmd, opts, repo)
61
+ return if repo.nil?
62
+ if repo.status_checker.status == Repo::Status::GIT_REPO_STATUS[:dirty]
63
+ remind_config_repo_fail("主仓库\"#{repo.name}\"有改动,无法执行!")
64
+ else
65
+ Output.puts_processing_message("开始checkout主仓库...")
66
+ success, output = repo.execute_git_cmd(cmd, opts)
67
+ if !success
68
+ remind_config_repo_fail("主仓库\"#{repo.name}\"执行\"#{cmd}\"失败:\n#{output}")
69
+ else
70
+ Output.puts_success_message("主仓库checkout成功!\n")
71
+ end
72
+
73
+ # 刷新配置表
74
+ Workspace.update_config { |missing_repos|
75
+ if missing_repos.length > 0
76
+ all_repos.concat(missing_repos)
77
+ # missing_repos包含新下载的和当前分支已有的新仓库,其中已有仓库包含在@all_repos内,需要去重
78
+ all_repos.uniq! { |repo| repo.name }
79
+ end
80
+ }
81
+
82
+ end
83
+ end
84
+
85
+ def remind_config_repo_fail(msg)
86
+ Output.puts_fail_message(msg)
87
+ return if Output.continue_with_user_remind?("是否继续操作其余仓库?")
88
+ Output.puts_cancel_message
89
+ exit
90
+ end
91
+
92
+ def enable_repo_selection
93
+ true
94
+ end
95
+
96
+ def self.description
97
+ "切换分支或恢复工作区改动。"
98
+ end
99
+
100
+ def self.usage
101
+ "mgit checkout [<git-checkout-option>] [(--mrepo|--el-mrepo) <repo>...] [--help]"
102
+ end
103
+
104
+ end
105
+
106
+ end
@@ -0,0 +1,64 @@
1
+ #coding=utf-8
2
+
3
+ module MGit
4
+
5
+ # @!scope [command] clean 清除所有仓库中工作区的变更
6
+ # follow git combinatorial command
7
+ # eg: git add . && git reset --hard
8
+ #
9
+ class Clean < BaseCommand
10
+
11
+ def execute(argv)
12
+
13
+ Output.puts_start_cmd
14
+
15
+ # 清除中间态
16
+ OperationProgressManager::PROGRESS_TYPE.each { |type, type_value|
17
+ if OperationProgressManager.is_in_progress?(Workspace.root, type_value)
18
+ Output.puts_processing_message("清除#{type.to_s}中间态...")
19
+ OperationProgressManager.remove_progress(Workspace.root, type_value)
20
+ Output.puts_success_message("清除成功!")
21
+ end
22
+ }
23
+
24
+ do_repos = []
25
+ all_repos.each { |repo|
26
+ do_repos.push(repo) if repo.status_checker.status != Repo::Status::GIT_REPO_STATUS[:clean]
27
+ }
28
+
29
+ if do_repos.length > 0
30
+ Workspace.check_branch_consistency
31
+ Output.puts_processing_message("正在将改动加入暂存区...")
32
+ _, error_repos1 = Workspace.execute_git_cmd_with_repos('add', '.', do_repos)
33
+ Output.puts_processing_message("正在重置...")
34
+ _, error_repos2 = Workspace.execute_git_cmd_with_repos('reset', '--hard', do_repos)
35
+ Output.puts_succeed_cmd(argv.absolute_cmd) if error_repos1.length + error_repos2.length == 0
36
+ else
37
+ Output.puts_success_message("所有仓库均无改动,无须执行。")
38
+ end
39
+
40
+ end
41
+
42
+ def validate(argv)
43
+ Foundation.help!("输入非法参数:#{argv.git_opts}。请通过\"mgit #{argv.cmd} --help\"查看用法。") if argv.git_opts.length > 0
44
+ end
45
+
46
+ def enable_repo_selection
47
+ true
48
+ end
49
+
50
+ def enable_short_basic_option
51
+ true
52
+ end
53
+
54
+ def self.description
55
+ "强制清空暂存区和工作区,相当于对指定或所有仓库执行\"git add . && git reset --hard\"操作"
56
+ end
57
+
58
+ def self.usage
59
+ "mgit clean [(-m|-e) <repo>...] [-h]"
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,84 @@
1
+ #coding=utf-8
2
+
3
+ module MGit
4
+
5
+ # @!scope [command] commit
6
+ # follow git commit
7
+ # eg: mgit commit -m 'Just for fun'
8
+ #
9
+ class Commit < BaseCommand
10
+
11
+ private def validate(argv)
12
+ super
13
+ # 禁用--amend
14
+ if argv.git_opts.include?('--amend')
15
+ Output.puts_fail_message("MGit不支持\"--amend\"操作,请重试。")
16
+ Output.puts_cancel_message
17
+ exit
18
+ end
19
+ end
20
+
21
+ def execute(argv)
22
+ Workspace.check_branch_consistency
23
+
24
+ Output.puts_start_cmd
25
+
26
+ do_repos = []
27
+ remind_repos = []
28
+ do_nothing_repos = []
29
+
30
+ Output.puts_processing_message("检查各仓库状态...")
31
+ Workspace.serial_enumerate_with_progress(all_repos) { |repo|
32
+ if repo.status_checker.status == Repo::Status::GIT_REPO_STATUS[:dirty]
33
+ index_dirty_mask = Repo::Status::GIT_REPO_STATUS_DIRTY_ZONE[:index]
34
+ # 仅在暂存区有未提交的改动时执行
35
+ if repo.status_checker.dirty_zone & index_dirty_mask == index_dirty_mask
36
+ do_repos.push(repo)
37
+ else
38
+ remind_repos.push(repo.name)
39
+ end
40
+ else
41
+ do_nothing_repos.push(repo.name)
42
+ end
43
+ }
44
+ Output.puts_success_message("检查完成!\n")
45
+
46
+ if remind_repos.length > 0 && !Output.continue_with_interact_repos?(remind_repos, "以上仓库暂存区无可提交内容,仅存在工作区改动或未跟踪文件,若需要提交这些改动请先add到暂存区。是否跳过并继续?")
47
+ Output.puts_cancel_message
48
+ return
49
+ end
50
+
51
+ if do_repos.length != 0
52
+ if argv.git_opts.include?('-m ') || !Output.continue_with_user_remind?("未添加\"-m\"参数,请使用如[ mgit commit -m \"my log\" ]的形式提交。是否取消执行并重新输入(若确实有意执行该指令请忽略本提示)?")
53
+
54
+ # commit前调用hook
55
+ HooksManager.execute_mgit_pre_exec_hook(argv.cmd, argv.pure_opts, do_repos.map { |e| e.config })
56
+
57
+ msg = ",另有#{do_nothing_repos.length}个仓库暂存区无待提交内容,无须执行" if do_nothing_repos.length > 0
58
+ Output.puts_remind_block(do_repos.map { |repo| repo.name }, "开始commit以上仓库#{msg}...")
59
+ _, error_repos = Workspace.execute_git_cmd_with_repos(argv.cmd, argv.git_opts, do_repos)
60
+ Output.puts_succeed_cmd(argv.absolute_cmd) if error_repos.length == 0
61
+ else
62
+ Output.puts_cancel_message
63
+ return
64
+ end
65
+ else
66
+ Output.puts_remind_message("所有仓库均无改动,无须执行!")
67
+ end
68
+ end
69
+
70
+ def enable_repo_selection
71
+ true
72
+ end
73
+
74
+ def self.description
75
+ "将修改记录到版本库。"
76
+ end
77
+
78
+ def self.usage
79
+ "mgit commit [<git-commit-option>] [(--mrepo|--el-mrepo) <repo>...] [--help]"
80
+ end
81
+
82
+ end
83
+
84
+ end
@@ -0,0 +1,202 @@
1
+ #coding=utf-8
2
+
3
+ module MGit
4
+
5
+ # @!scope [command] config 配置 .mgit/config.yml 文件信息
6
+ #
7
+ # eg: mgit config -s key 'value'
8
+ #
9
+ class Config < BaseCommand
10
+
11
+ OPT_LIST = {
12
+ :create_local => '--create-local',
13
+ :create_local_s => '-c',
14
+ :update_manifest => '--update-manifest',
15
+ :update_manifest_s => '-m',
16
+ :update_local => '--update-local',
17
+ :update_local_s => '-u',
18
+ :list => '--list',
19
+ :list_s => '-l',
20
+ :set => '--set',
21
+ :set_s => '-s'
22
+ }.freeze
23
+
24
+ def options
25
+ return [
26
+ ARGV::Opt.new(OPT_LIST[:update_manifest],
27
+ short_key:OPT_LIST[:update_manifest_s],
28
+ info:"该指令用于更新mgit所使用的配置文件,如:\"mgit config -m <new_path>/manifest.json\"。",
29
+ type: :boolean),
30
+ ARGV::Opt.new(OPT_LIST[:update_local],
31
+ short_key:OPT_LIST[:update_local_s],
32
+ info:"该指令用于更新mgit所使用的本地配置文件,如:\"mgit config -u <new_path>/local_manifest.json\"。",
33
+ type: :boolean),
34
+ ARGV::Opt.new(OPT_LIST[:create_local],
35
+ short_key:OPT_LIST[:create_local_s],
36
+ info:"在指定目录下创建本地配置文件,若目录不存在则自动创建。如执行:\"mgit config -c /a/b/c\",则生成本地配置文件:\"/a/b/c/local_manifest.json\"。如果未传入值,如:\"mgit config -c\",那么若配置仓库存在的话,会在配置仓库中创建本地配置文件。",
37
+ type: :string),
38
+ ARGV::Opt.new(OPT_LIST[:list],
39
+ short_key:OPT_LIST[:list_s],
40
+ info:"列出当前MGit所有配置,无参数,如:\"mgit config -l\"。",
41
+ type: :boolean),
42
+ ARGV::Opt.new(OPT_LIST[:set],
43
+ short_key:OPT_LIST[:set_s],
44
+ info:"对MGit进行配置,遵守格式:\"mgit config -s <key> <value>\",如:\"mgit config -s maxconcurrentcount 5\"。")
45
+ ].concat(super)
46
+ end
47
+
48
+ def validate(argv)
49
+ Foundation.help!("输入非法参数:#{argv.git_opts}。请通过\"mgit #{argv.cmd} --help\"查看用法。") if argv.git_opts.length > 0
50
+
51
+ if set_kv = argv.opt(OPT_LIST[:set])
52
+ Foundation.help!("参数#{OPT_LIST[:set]}格式错误,只需传入key和value两个值!") if set_kv.value.count != 2
53
+ end
54
+ end
55
+
56
+ # --- 覆写前后hook,不需要预设操作 ---
57
+ def pre_exec
58
+ # 开始计时
59
+ MGit::DurationRecorder.start
60
+ Workspace.setup_multi_repo_root
61
+ # 配置log
62
+ MGit::Loger.config(Workspace.root)
63
+ MGit::Loger.info("~~~ #{@argv.absolute_cmd} ~~~")
64
+ end
65
+
66
+ def post_exec
67
+ # 打点结束
68
+ duration = MGit::DurationRecorder.end
69
+ MGit::Loger.info("~~~ #{@argv.absolute_cmd}, 耗时:#{duration} s ~~~")
70
+ end
71
+ # --------------------------------
72
+
73
+ def execute(argv)
74
+ argv.enumerate_valid_opts { |opt|
75
+ if opt.key == OPT_LIST[:update_manifest]
76
+ update_mgit_config(opt.value)
77
+ return
78
+ elsif opt.key == OPT_LIST[:update_local]
79
+ update_local_config(opt.value)
80
+ return
81
+ elsif opt.key == OPT_LIST[:create_local]
82
+ dir = opt.value
83
+ if opt.value.is_a?(TrueClass)
84
+ Workspace.setup_config
85
+ if Workspace.config.config_repo.nil?
86
+ Foundation.help!("未找到配置仓库,请为参数\"--create-local\"或\"-c\"指定一个具体文件夹目录并重试!")
87
+ else
88
+ dir = Workspace.config.config_repo.abs_dest(Workspace.root)
89
+ end
90
+ end
91
+ create_local_config(dir)
92
+ return
93
+ elsif opt.key == OPT_LIST[:list]
94
+ dump_config
95
+ elsif opt.key == OPT_LIST[:set]
96
+ set_config(opt.value)
97
+ end
98
+ }
99
+ end
100
+
101
+ # 更新配置表软链接
102
+ def update_mgit_config(config_path)
103
+ config = Manifest.parse(Utils.expand_path(config_path))
104
+ Utils.execute_under_dir("#{File.join(Workspace.root, Constants::PROJECT_DIR[:source_config])}") {
105
+ mgit_managed_config_link_path = File.join(Dir.pwd, Constants::CONFIG_FILE_NAME[:manifest])
106
+ mgit_managed_config_cache_path = File.join(Dir.pwd, Constants::CONFIG_FILE_NAME[:manifest_cache])
107
+
108
+ # 在.mgit/source-config文件夹下创建原始配置文件的软连接
109
+ if config.path != mgit_managed_config_link_path
110
+ Utils.link(config.path, mgit_managed_config_link_path)
111
+ end
112
+
113
+ # 将配置缓存移动到.mgit/source-config文件夹下
114
+ if config.cache_path != mgit_managed_config_cache_path
115
+ FileUtils.rm_f(mgit_managed_config_cache_path) if File.exist?(mgit_managed_config_cache_path)
116
+ FileUtils.mv(config.cache_path, Dir.pwd)
117
+ end
118
+
119
+ Output.puts_success_message("配置文件更新完毕!")
120
+ }
121
+ end
122
+
123
+ # 更新本地配置表软链接
124
+ def update_local_config(config_path)
125
+ config_path = Utils.expand_path(config_path)
126
+ Utils.execute_under_dir("#{File.join(Workspace.root, Constants::PROJECT_DIR[:source_config])}") {
127
+ mgit_managed_local_config_link_path = File.join(Dir.pwd, Constants::CONFIG_FILE_NAME[:local_manifest])
128
+ # 在.mgit/source-config文件夹下创建原始本地配置文件的软连接
129
+ if config_path != mgit_managed_local_config_link_path
130
+ Utils.link(config_path, mgit_managed_local_config_link_path)
131
+ end
132
+
133
+ Output.puts_success_message("本地配置文件更新完毕!")
134
+ }
135
+ end
136
+
137
+ # 新建本地配置表软链接
138
+ def create_local_config(dir)
139
+ path = Utils.expand_path(File.join(dir, Constants::CONFIG_FILE_NAME[:local_manifest]))
140
+ if File.exist?(path) && !Output.continue_with_user_remind?("本地配置文件\"#{path}\"已经存在,是否覆盖?")
141
+ Output.puts_cancel_message
142
+ return
143
+ end
144
+
145
+ FileUtils.mkdir_p(dir)
146
+ file = File.new(path, 'w')
147
+ if !file.nil?
148
+ file.write(Template.default_template)
149
+ file.close
150
+ end
151
+
152
+ Utils.link(path, File.join(Workspace.root, Constants::PROJECT_DIR[:source_config], Constants::CONFIG_FILE_NAME[:local_manifest]))
153
+ Output.puts_success_message("本地配置文件生成完毕:#{path}")
154
+ end
155
+
156
+ # 列出所有配置
157
+ def dump_config
158
+ begin
159
+ MGitConfig.dump_config(Workspace.root)
160
+ rescue Error => e
161
+ Foundation.help!(e.msg)
162
+ end
163
+ end
164
+
165
+ # 设置配置
166
+ def set_config(key_value_arr)
167
+ key = key_value_arr.first
168
+ value = key_value_arr.last
169
+ begin
170
+ MGitConfig.update(Workspace.root) { |config|
171
+ if MGitConfig::CONFIG_KEY.keys.include?(key.to_sym)
172
+ valid_value = MGitConfig.to_suitable_value_for_key(Workspace.root, key, value)
173
+ if !valid_value.nil?
174
+ config[key] = valid_value
175
+ else
176
+ type = MGitConfig::CONFIG_KEY[key.to_sym][:type]
177
+ Foundation.help!("#{value}不匹配类型:#{type},请重试。")
178
+ end
179
+ else
180
+ Foundation.help!("非法key值:#{key}。使用mgit config -l查看所有可配置字段。")
181
+ end
182
+ }
183
+ Output.puts_success_message("配置成功!")
184
+ rescue Error => e
185
+ Foundation.help!(e.msg)
186
+ end
187
+ end
188
+
189
+ # 允许使用短命令
190
+ def enable_short_basic_option
191
+ true
192
+ end
193
+
194
+ def self.description
195
+ "用于更新多仓库配置信息。"
196
+ end
197
+
198
+ def self.usage
199
+ "mgit config [-s <config_key> <config_value>] [-l]\nmgit config [(-m|-u) <path_to_manifest> | -c <dir_contains_local>]\nmgit config [-h]"
200
+ end
201
+ end
202
+ end