class MGit::Pull
@!scope 类似 git pull
Constants
- OPT_LIST
Public Class Methods
description()
click to toggle source
# File lib/m-git/command/pull.rb, line 354 def self.description return "从仓库或本地分支获取数据并合并。" end
usage()
click to toggle source
# File lib/m-git/command/pull.rb, line 358 def self.usage return "mgit pull [<git-pull-option>] [(--mrepo|--el-mrepo) <repo>...] [--auto-exec] [--no-check] [--include-lock] [--help]\nmgit pull --continue\nmgit pull --abort" end
Public Instance Methods
__progress_type()
click to toggle source
# File lib/m-git/command/pull.rb, line 19 def __progress_type OperationProgressManager::PROGRESS_TYPE[:pull] end
do_abort(argv)
click to toggle source
# File lib/m-git/command/pull.rb, line 295 def do_abort(argv) if mgit_try_to_abort? Output.puts_start_cmd OperationProgressManager.remove_progress(Workspace.root, __progress_type) do_repos = all_repos.select { |repo| repo.status_checker.is_in_merge_progress? } if do_repos.length > 0 append_message = ",另有#{all_repos.length - do_repos.length}个仓库无须操作" if do_repos.length < all_repos.length Output.puts_processing_block(do_repos.map { |e| e.name }, "开始操作以上仓库#{append_message}...") _, error_repos = Workspace.execute_git_cmd_with_repos('merge', '--abort', do_repos) Output.puts_succeed_cmd(argv.absolute_cmd) if error_repos.length == 0 else Output.puts_success_message("没有仓库需要操作!") end return true else return false end end
enable_abort_operation()
click to toggle source
# File lib/m-git/command/pull.rb, line 350 def enable_abort_operation return true end
enable_auto_execution()
click to toggle source
# File lib/m-git/command/pull.rb, line 338 def enable_auto_execution return true end
enable_continue_operation()
click to toggle source
# File lib/m-git/command/pull.rb, line 346 def enable_continue_operation return true end
enable_repo_selection()
click to toggle source
# File lib/m-git/command/pull.rb, line 334 def enable_repo_selection return true end
execute(argv)
click to toggle source
# File lib/m-git/command/pull.rb, line 23 def execute(argv) if argv.opt(OPT_LIST[:no_check]) simple_pull(argv) return end verbose_pull(argv) end
include_lock_by_default()
click to toggle source
# File lib/m-git/command/pull.rb, line 342 def include_lock_by_default return true end
options()
click to toggle source
Calls superclass method
MGit::BaseCommand#options
# File lib/m-git/command/pull.rb, line 13 def options [ ARGV::Opt.new(OPT_LIST[:no_check], info:'指定该参数意味着执行前跳过仓库的状态检查,直接对指定或所有仓库执行pull操作,有一定风险,请慎重执行。', type: :boolean), ].concat(super) end
pull_config_repo(cmd, opts, repo)
click to toggle source
# File lib/m-git/command/pull.rb, line 222 def pull_config_repo(cmd, opts, repo) if !repo.nil? branch_status = repo.status_checker.branch_status if branch_status == Repo::Status::GIT_BRANCH_STATUS[:detached] remind_config_repo_fail("主仓库\"#{repo.name}\"HEAD游离,当前不在任何分支上,无法执行!") elsif branch_status == Repo::Status::GIT_BRANCH_STATUS[:no_tracking] remind_config_repo_fail("主仓库\"#{repo.name}\"未跟踪对应远程分支,无法执行!(需要执行'mgit branch -u origin/<branch>')") elsif branch_status == Repo::Status::GIT_BRANCH_STATUS[:no_remote] remind_config_repo_fail("主仓库\"#{repo.name}\"远程分支不存在,无法执行!") # elsif repo.status_checker.status == Repo::Status::GIT_REPO_STATUS[:dirty] # remind_config_repo_fail("主仓库\"#{repo.name}\"有改动,无法执行!") else Output.puts_processing_message("开始操作主仓库...") # 现场信息 exec_subrepos = all_repos(except_config:true) is_all = Workspace.is_all_exec_sub_repos?(exec_subrepos) context = OperationProgressContext.new(__progress_type) context.cmd = cmd context.opts = opts context.repos = is_all ? nil : exec_subrepos.map { |e| e.name } # nil表示操作所有子仓库 context.branch = repo.status_checker.current_branch(use_cache:true) # 如果不带任何参数,则将pull分解为fetch+merge执行, fetch已经执行,此处执行merge。带参数则透传。 if opts.length == 0 branch = repo.status_checker.current_branch(strict_mode:false, use_cache:true) tracking_branch = repo.status_checker.tracking_branch(branch, use_cache:true) msg = "-m \"【Merge】【0.0.0】【#{branch}】合并远程分支'#{tracking_branch}'。\"" if branch_status == Repo::Status::GIT_BRANCH_STATUS[:diverged] cmd, opts = "merge", "#{tracking_branch} #{msg}" end success, output = repo.execute_git_cmd(cmd, opts) if success Output.puts_success_message("主仓库操作成功!\n") else Output.puts_fail_message("主仓库操作失败!\n") config_error = output end # 刷新配置表 begin Workspace.update_config(strict_mode:false) { |missing_repos| if missing_repos.length > 0 success_missing_repos = Workspace.guide_to_checkout_branch(missing_repos, all_repos, append_message:"拒绝该操作本次执行将忽略以上仓库") all_repos.concat(success_missing_repos) # success_missing_repos包含新下载的和当前分支已有的新仓库,其中已有仓库包含在@all_repos内,需要去重 all_repos.uniq! { |repo| repo.name } end } refresh_context(context) rescue Error => e if e.type == MGIT_ERROR_TYPE[:config_generate_error] OperationProgressManager.trap_into_progress(Workspace.root, context) show_progress_error("配置表生成失败", "#{e.msg}") end end return config_error end end end
refresh_context(context)
click to toggle source
# File lib/m-git/command/pull.rb, line 316 def refresh_context(context) exec_subrepos = all_repos(except_config:true) is_all = Workspace.is_all_exec_sub_repos?(exec_subrepos) context.repos = is_all ? nil : exec_subrepos.map { |e| e.name } # nil表示操作所有子仓库 end
remind_config_repo_fail(msg)
click to toggle source
# File lib/m-git/command/pull.rb, line 285 def remind_config_repo_fail(msg) Output.puts_fail_message(msg) if Workspace.filter_config.auto_exec || Output.continue_with_user_remind?("是否继续操作其余仓库?") return else Output.puts_cancel_message exit end end
show_progress_error(summary, detail)
click to toggle source
# File lib/m-git/command/pull.rb, line 322 def show_progress_error(summary, detail) error = "#{summary} 已进入操作中间态。 原因: #{detail} 可选: - 使用\"mgit pull --continue\"继续拉取。 - 使用\"mgit pull --abort\"取消拉取。" Foundation.help!(error, title:'暂停') end
simple_pull(argv)
click to toggle source
# File lib/m-git/command/pull.rb, line 202 def simple_pull(argv) Output.puts_start_cmd # 优先pull配置仓库 config_repo = generate_config_repo config_error = pull_config_repo(argv.cmd, argv.git_opts, config_repo) Output.puts_processing_message("开始pull子仓库...") _, error_repos = Workspace.execute_git_cmd_with_repos(argv.cmd, argv.git_opts, all_repos) if config_error.nil? && error_repos.length == 0 Output.puts_succeed_cmd(argv.absolute_cmd) elsif !config_error.nil? Output.puts_fail_block([config_repo.name], "主仓库操作失败:#{config_error}") end # 情况中间态 OperationProgressManager.remove_progress(Workspace.root, __progress_type) end
verbose_pull(argv)
click to toggle source
# File lib/m-git/command/pull.rb, line 32 def verbose_pull(argv) return if do_abort(argv) Output.puts_start_cmd # 获取远程仓库当前分支信息 Workspace.pre_fetch config_repo = generate_config_repo if mgit_try_to_continue? # 不处于中间态禁止执行 Foundation.help!("当前并不处于操作中间态,无法进行continue操作!") if !OperationProgressManager.is_in_progress?(Workspace.root, __progress_type) # 读取指令缓存失败禁止执行 context, _ = OperationProgressManager.load_context(Workspace.root, __progress_type) Foundation.help!("缓存指令读取失败,continue无法继续进行,请重新执行完整指令。") if context.nil? || !context.validate? # 分支不匹配禁止执行 Foundation.help!("当前主仓库所在分支跟上次操作时所在分支(#{context.branch})不一致,请切换后重试。") if config_repo.status_checker.current_branch(use_cache:true) != context.branch if !context.repos.nil? Output.puts_processing_message("加载上次即将操作的子仓库...") Workspace.update_all_repos(context.repos) end Output.puts_success_message("已跳过主仓库。") else # 处于中间态则提示 if OperationProgressManager.is_in_progress?(Workspace.root, __progress_type) if Output.continue_with_user_remind?("当前处于操作中间态,建议取消操作并执行\"mgit pull --continue\"继续操作子仓库。\n 继续执行将清除中间态并重新操作所有仓库,是否取消?") Output.puts_cancel_message return end end # 优先pull配置仓库 config_error = pull_config_repo(argv.cmd, argv.git_opts, config_repo) if config_error Output.puts_fail_block([config_repo.name], "主仓库操作失败:#{config_error}") return end end do_repos = [] diverged_repos = [] no_remote_repos = [] no_tracking_repos = [] dirty_repos = [] detached_repos = [] remote_inconsist_repos = [] do_nothing_repos = [] Output.puts_processing_message("检查各仓库状态...") Workspace.serial_enumerate_with_progress(all_repos) { |repo| next if !config_repo.nil? && repo.name == config_repo.name Timer.start(repo.name) status = repo.status_checker.status branch_status = repo.status_checker.branch_status if branch_status == Repo::Status::GIT_BRANCH_STATUS[:up_to_date] || branch_status == Repo::Status::GIT_BRANCH_STATUS[:ahead] # 领先和最新的仓库均不操作 do_nothing_repos.push(repo) else url_consist = repo.url_consist? is_dirty = status == Repo::Status::GIT_REPO_STATUS[:dirty] dirty_repos.push(repo) if is_dirty remote_inconsist_repos.push(repo) if !url_consist # 仅有分叉或落后,且工作区干净的仓库直接加入到操作集 if branch_status == Repo::Status::GIT_BRANCH_STATUS[:diverged] && !is_dirty && url_consist do_repos.push(repo) diverged_repos.push(repo.name) elsif branch_status == Repo::Status::GIT_BRANCH_STATUS[:behind] && !is_dirty && url_consist do_repos.push(repo) elsif branch_status == Repo::Status::GIT_BRANCH_STATUS[:no_remote] no_remote_repos.push(repo) elsif branch_status == Repo::Status::GIT_BRANCH_STATUS[:no_tracking] no_tracking_repos.push(repo) elsif branch_status == Repo::Status::GIT_BRANCH_STATUS[:detached] detached_repos.push(repo) end end Timer.stop(repo.name) } Output.puts_success_message("检查完成!\n") if Workspace.filter_config.auto_exec do_repos += dirty_repos do_repos.uniq! { |repo| repo.name } elsif no_remote_repos.length > 0 || dirty_repos.length > 0 || detached_repos.length > 0 || no_tracking_repos.length > 0 || remote_inconsist_repos.length > 0 remind_repos = [] remind_repos.push(['远程分支不存在', no_remote_repos.map { |e| e.name }]) if no_remote_repos.length > 0 remind_repos.push(['未追踪远程分支(建议:mgit branch -u origin/<branch>)', no_tracking_repos.map { |e| e.name }]) if no_tracking_repos.length > 0 remind_repos.push(['有本地改动', dirty_repos.map { |e| e.name }]) if dirty_repos.length > 0 remind_repos.push(['HEAD游离,当前不在任何分支上', detached_repos.map { |e| e.name }]) if detached_repos.length > 0 remind_repos.push(['实际url与配置不一致', remote_inconsist_repos.map { |e| e.name }]) if remote_inconsist_repos.length > 0 Output.interact_with_multi_selection_combined_repos(remind_repos, "以上仓库状态异常", ['a: 跳过并继续', 'b: 强制执行', 'c: 终止']) { |input| if input == 'b' do_repos += dirty_repos do_repos += detached_repos do_repos += no_remote_repos do_repos += no_tracking_repos do_repos += remote_inconsist_repos do_repos.uniq! { |repo| repo.name } elsif input == 'c' || input != 'a' Output.puts_cancel_message return end } end if do_repos.length != 0 error_repos = [] # 如果不带任何参数,则将pull分解为fetch+merge执行, fetch已经执行,此处执行merge。带参数则透传。 if argv.git_opts.length == 0 # 排除HEAD游离,无远程分支,未追踪远程分支的仓库,这三种仓库是无法强制执行git pull的(但是可以执行如:git pull origin master,因此透传不做此校验) skip_repos = do_repos.select { |repo| branch_status = repo.status_checker.branch_status branch_status == Repo::Status::GIT_BRANCH_STATUS[:no_remote] || branch_status == Repo::Status::GIT_BRANCH_STATUS[:no_tracking] || branch_status == Repo::Status::GIT_BRANCH_STATUS[:detached] } if skip_repos.length > 0 Output.puts_remind_block(skip_repos.map { |e| e.name }, "以上仓库无法强制执行,已跳过。") do_repos -= skip_repos if do_repos.length == 0 Output.puts_success_message("仓库均为最新,无须执行!") return end end count_msg = ",另有#{do_nothing_repos.length}个仓库无须执行" if do_nothing_repos.length > 0 Output.puts_remind_block(do_repos.map { |repo| repo.name }, "开始为以上仓库合并远程分支#{count_msg}...") _, error_repos = Workspace.execute_git_cmd_with_repos('', '', do_repos) { |repo| msg = nil branch = repo.status_checker.current_branch(strict_mode:false, use_cache:true) tracking_branch = repo.status_checker.tracking_branch(branch, use_cache:true) # 如果产生分叉,为生成的新节点提供log msg = "-m \"【Merge】【0.0.0】【#{branch}】合并远程分支'#{tracking_branch}'。\"" if diverged_repos.include?(repo.name) ["merge", "#{tracking_branch} #{msg}"] } else count_msg = ",另有#{do_nothing_repos.length}个仓库无须执行" if do_nothing_repos.length > 0 Output.puts_remind_block(do_repos.map { |repo| repo.name }, "开始pull以上仓库#{count_msg}...") _, error_repos = Workspace.execute_git_cmd_with_repos(argv.cmd, argv.git_opts, do_repos) end if config_error.nil? && error_repos.length == 0 Output.puts_succeed_cmd(argv.absolute_cmd) Timer.show_time_consuming_repos end else Output.puts_success_message("仓库均为最新,无须执行!") end # 清除中间态 OperationProgressManager.remove_progress(Workspace.root, __progress_type) end