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