Class: MGit::Workspace

Inherits:
Object
  • Object
show all
Extended by:
PathHelper, WorkspaceHelper
Defined in:
lib/m-git/workspace.rb,
lib/m-git/workspace/path_helper.rb,
lib/m-git/workspace/workspace_helper.rb

Defined Under Namespace

Modules: PathHelper, WorkspaceHelper Classes: RepoFilterConfig

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Methods included from PathHelper

cache_manifest_path, config_file, hooks_dir, local_manifest_path, manifest_path, snapshot_dir, source_config_dir, source_git_dir

Methods included from WorkspaceHelper

invalid_move?, pop, pop_git_entity, push, push_git_entity, replace, sync_workspace

Class Attribute Details

.configObject (readonly)

Returns the value of attribute config.



18
19
20
# File 'lib/m-git/workspace.rb', line 18

def config
  @config
end

.rootObject (readonly)

Returns the value of attribute root.



16
17
18
# File 'lib/m-git/workspace.rb', line 16

def root
  @root
end

Instance Attribute Details

#仓库过滤器,过滤执行的仓库Object



9
# File 'lib/m-git/workspace.rb', line 9

RepoFilterConfig = Struct.new(:auto_exec, :include_lock, :select_repos, :exclude_repos)

Class Method Details

.all_repos(except_config: false) ⇒ Object

获取所有仓库



192
193
194
195
196
197
198
199
200
# File 'lib/m-git/workspace.rb', line 192

def all_repos(except_config:false)
  setup_all_repos if @all_repos.nil?

  if except_config
    @all_repos.select { |e| !e.config.is_config_repo }
  else
    @all_repos
  end
end

.check_branch_consistencyObject

校验分支统一性



363
364
365
366
367
368
369
370
371
372
# File 'lib/m-git/workspace.rb', line 363

def check_branch_consistency
  if has_diff_branch?(all_repos)
    if filter_config.auto_exec || Output.continue_with_user_remind?("当前所有仓库并不处于同一分支(可通过\"mgit branch --compact\"查看),是否继续?")
      return
    else
      Output.puts_cancel_message
      exit
    end
  end
end

.concurrent_enumerate(array) ⇒ Object

并发遍历



470
471
472
473
474
475
476
477
478
479
480
# File 'lib/m-git/workspace.rb', line 470

def concurrent_enumerate(array)
  begin
    max_concurrent_count = MGitConfig.query_with_key(root, :maxconcurrentcount)
  rescue Error => e
    Foundation.help!(e.msg)
  end

  array.peach(max_concurrent_count) { |item|
    yield(item) if block_given?
  }
end

.concurrent_enumerate_with_progress_bar(light_repos, message, &exec_handler) ⇒ Object



273
274
275
276
# File 'lib/m-git/workspace.rb', line 273

def concurrent_enumerate_with_progress_bar(light_repos, message, &exec_handler)
  Output.puts_processing_block(light_repos.map { |e| e.name }, message)
  concurrent_enumerate_with_progress_bar_pure(light_repos, &exec_handler)
end

.concurrent_enumerate_with_progress_bar_pure(light_repos, &exec_handler) ⇒ Object



278
279
280
281
282
283
284
285
286
287
288
# File 'lib/m-git/workspace.rb', line 278

def concurrent_enumerate_with_progress_bar_pure(light_repos, &exec_handler)
  task_count = 0
  Output.update_progress(light_repos.length, task_count)
  concurrent_enumerate(light_repos) { |light_repo|
    exec_handler.call(light_repo) if exec_handler
    Lock.mutex_puts {
      task_count += 1
      Output.update_progress(light_repos.length, task_count)
    }
  }
end

.exec_light_reposArray<LightRepo>

抽取本次需要执行指令的仓库对应的LightRepo

Returns:

  • (Array<LightRepo>)

    本次需要执行指令的LightRepo数组



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/m-git/workspace.rb', line 221

def exec_light_repos
  if @exec_light_repos.nil?
    mrepo_opt = filter_config.select_repos
    exclude_mrepo_opt = filter_config.exclude_repos

    selected_repos = mrepo_opt.value if !mrepo_opt.nil?
    excluded_repos = exclude_mrepo_opt.value if !exclude_mrepo_opt.nil?

    # 校验参数是否正确
    check_repo_names = []
    check_repo_names.concat(selected_repos) if selected_repos
    check_repo_names.concat(excluded_repos) if excluded_repos
    unless check_repo_names.empty?
      light_repo_names = config.light_repos.map(&:name)
      extra_names = check_repo_names - light_repo_names
      Foundation.help!("指定的仓库名称#{extra_names}不存在,请检查命令指定的参数") unless extra_names.empty?
    end

    @exec_light_repos = config.repo_list(selection:selected_repos, exclusion:excluded_repos)
  end
  @exec_light_repos
end

.execute_common_cmd_with_repos(abs_cmd, repos) ⇒ Object

shell指令透传执行(串行)



520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
# File 'lib/m-git/workspace.rb', line 520

def execute_common_cmd_with_repos(abs_cmd, repos)
  success_repos = {}
  error_repos = {}
  task_count = 0
  Output.update_progress(repos.length, task_count)
  repos.each { |repo|
    # 允许针对仓库对指令进行加工
    abs_cmd = yield(repo) if block_given?
    new_abs_cmd = "cd \"#{repo.path}\" && #{abs_cmd}"
    success, output = repo.execute(new_abs_cmd)
    if success
      success_repos[repo.name] = output
    else
      error_repos[repo.name] = output
    end
    task_count += 1
    Output.update_progress(repos.length, task_count)
  }
  show_error(error_repos)
  return success_repos, error_repos
end

.execute_common_cmd_with_repos_concurrent(abs_cmd, repos) ⇒ Object

shell指令透传执行(并发)



543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
# File 'lib/m-git/workspace.rb', line 543

def execute_common_cmd_with_repos_concurrent(abs_cmd, repos)
  mutex = Mutex.new
  success_repos = {}
  error_repos = {}
  task_count = 0
  Output.update_progress(repos.length, task_count)
  concurrent_enumerate(repos) { |repo|
    # 允许针对仓库对指令进行加工
    abs_cmd = yield(repo) if block_given?
    new_abs_cmd = "cd \"#{repo.path}\" && #{abs_cmd}"
    success, output = repo.execute(new_abs_cmd)

    mutex.lock
    if success
      success_repos[repo.name] = output
    else
      error_repos[repo.name] = output
    end
    task_count += 1
    Output.update_progress(repos.length, task_count)
    mutex.unlock
  }
  show_error(error_repos)
  return success_repos, error_repos
end

.execute_git_cmd_with_repos(cmd, git_opts, repos) ⇒ Object

git指令透传执行



494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/m-git/workspace.rb', line 494

def execute_git_cmd_with_repos(cmd, git_opts, repos)
  mutex = Mutex.new
  success_repos = {}
  error_repos = {}
  task_count = 0
  Output.update_progress(repos.length, task_count)
  concurrent_enumerate(repos) { |repo|
    # 允许针对仓库对指令进行加工
    cmd, git_opts = yield(repo) if block_given?
    success, output = repo.execute_git_cmd(cmd, git_opts)

    mutex.lock
    if success
      success_repos[repo.name] = output
    else
      error_repos[repo.name] = output
    end
    task_count += 1
    Output.update_progress(repos.length, task_count)
    mutex.unlock
  }
  show_error(error_repos)
  return success_repos, error_repos
end

.filter_configObject



20
21
22
# File 'lib/m-git/workspace.rb', line 20

def filter_config
  @filter_config ||= RepoFilterConfig.new
end

.generate_config_repoRepo

生成配置仓库的Repo对象

Returns:

  • (Repo)

    配置仓库的Repo对象



262
263
264
265
266
267
268
269
270
# File 'lib/m-git/workspace.rb', line 262

def generate_config_repo
  config_light_repo = exec_light_repos.find { |light_repo| light_repo.is_config_repo == true }
  if !config_light_repo.nil?
    repo, _ = Repo.generate_softly(root, config_light_repo)
    return repo
  else
    return nil
  end
end

.guide_to_checkout_branch(new_repos, exist_repos, append_message: nil) ⇒ Array<Repo>

引导切换新仓库的分支

Parameters:

  • missing_repos (Array<Repo>)

    缺失仓库

  • exist_repos (Array<Repo>)

    已有仓库

Returns:

  • (Array<Repo>)

    切换成功的仓库



402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
# File 'lib/m-git/workspace.rb', line 402

def guide_to_checkout_branch(new_repos, exist_repos, append_message:nil)
  return [] if new_repos.length == 0 || exist_repos.length == 0

  # 寻找最多仓库所在分支作为推荐
  branch_count = {}
  exist_repos.each { |repo|
    branch = repo.status_checker.current_branch(strict_mode:false, use_cache:true)
    if !branch.nil?
      branch_count[branch] = 0 if branch_count[branch].nil?
      branch_count[branch] += 1
    end
  }
  # 若已有仓库都游离,无法推荐切换,则直接返回
  return [] if branch_count.length == 0
  max_branch = branch_count.max_by { |k,v| v }.first

  branch_group = {}
  new_repos.each { |repo|
    branch = repo.status_checker.current_branch(strict_mode:false, use_cache:true)
    if branch.nil?
      branch = 'HEAD游离'
    elsif branch == max_branch
      next
    end
    branch_group[branch] = [] if branch_group[branch].nil?
    branch_group[branch].push(repo.name)
  }

  # 如果新仓库都在当前推荐分支则不操作
  if branch_group.length == 0
    return new_repos
    # 指定了auto则跳过提示,直接开始同步
  elsif branch_group.length > 0 && (filter_config.auto_exec || Output.continue_with_combined_interact_repos?(branch_group.to_a, "检测到已有的仓库大部分(或全部)处于分支:#{max_branch}\n    是否将以上仓库切换到该分支#{"(#{append_message})" if !append_message.nil?}", title:'新仓库所在分支'))

    do_repos = []
    remind_repos = []

    new_repos.each { |repo|
      if repo.status_checker.local_branch_exist?(max_branch) || repo.status_checker.remote_branch_exist?(max_branch)
        do_repos.push(repo)
      else
        remind_repos.push(repo)
      end
    }

    Output.puts_fail_block(remind_repos.map { |e| e.name }, "以上仓库无对应分支,已跳过,请自行处理。") if remind_repos.length > 0

    if do_repos.length > 0
      Output.puts_processing_message("开始切换分支...")
      _, error_repos = execute_git_cmd_with_repos('', '', do_repos) { |repo|
        opts = "#{remind_repos.include?(repo) ? '-b ' : ''}#{max_branch}"
        ["checkout", opts]
      }

      if error_repos.length > 0
        return do_repos.select { |repo| !error_repos.keys.include?(repo.name)}
      else
        Output.puts_success_message("分支切换成功!\n")
        return do_repos
      end

    end
  end

  return []
end

.has_diff_branch?(repos) ⇒ Boolean

检查是否存在不一致的分支

Returns:

  • (Boolean)

    是否存在不一致分支



378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
# File 'lib/m-git/workspace.rb', line 378

def has_diff_branch?(repos)
  return false if repos.length == 0

  branch = nil
  repos.each { |repo|
    current_branch = repo.status_checker.current_branch(strict_mode:false)
    # current_branch为空值意味着HEAD游离
    if current_branch.nil? || (!branch.nil? && branch != current_branch)
      return true
    elsif branch.nil?
      branch = current_branch
    end
  }
  return false
end

.is_all_exec_sub_repos?(subrepos) ⇒ Boolean

检查是否是全部定义的子仓库

Parameters:

  • subrepos (LightRepo)

    仓库轻量对象集合

Returns:

  • (Boolean)

    是否是所有子仓库



626
627
628
629
630
631
# File 'lib/m-git/workspace.rb', line 626

def is_all_exec_sub_repos?(subrepos)
  if subrepos.is_a?(Array)
    subrepo_names = subrepos.map { |e| e.name }
    return is_all_exec_sub_repos_by_name?(subrepo_names)
  end
end

.is_all_exec_sub_repos_by_name?(subrepo_names) ⇒ Boolean

检查是否是全部定义的子仓库

Parameters:

  • subrepos (String)

    仓库名字数组

Returns:

  • (Boolean)

    是否是所有子仓库



639
640
641
642
643
644
# File 'lib/m-git/workspace.rb', line 639

def is_all_exec_sub_repos_by_name?(subrepo_names)
  if subrepo_names.is_a?(Array)
    all_subrepo_name = config.repo_list.select { |e| !e.lock && !e.is_config_repo }.map { |e| e.name }
    return subrepo_names == all_subrepo_name
  end
end

.locked_reposObject



202
203
204
205
# File 'lib/m-git/workspace.rb', line 202

def locked_repos
  setup_all_repos if @locked_repos.nil?
  @locked_repos
end

.multi_repo_root_pathObject


校验mgit根目录



246
247
248
249
250
251
252
253
254
255
256
# File 'lib/m-git/workspace.rb', line 246

def multi_repo_root_path
  dir = Dir.pwd
  while File.dirname(dir) != dir do
    Dir.foreach(dir) do |filename|
      next unless File.directory?(File.join(dir, filename))
      return dir if filename == '.mgit'
    end
    dir = File.dirname(dir)
  end
  nil
end

.pre_fetchObject

获取当前分支远程仓库信息



588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
# File 'lib/m-git/workspace.rb', line 588

def pre_fetch
  Output.puts_processing_message("获取远程仓库信息...")
  mutex = Mutex.new
  error_repos = {}
  task_count = 0
  Output.update_progress(all_repos.length, task_count)
  concurrent_enumerate(all_repos) { |repo|
    Timer.start(repo.name, use_lock:true)
    git_cmd = repo.git_cmd('fetch', '')
    Utils.execute_shell_cmd(git_cmd) { |stdout, stderr, status|
      error_msg = GitMessageParser.new(repo.config.url).parse_fetch_msg(stderr)

      mutex.lock
      if !status.success? || !error_msg.nil?
        error_repos[repo.name] = error_msg.nil? ? stderr : error_msg
      end
      task_count += 1
      Output.update_progress(all_repos.length, task_count)
      mutex.unlock
      Timer.stop(repo.name, use_lock:true)

      # 标记状态更新
      repo.status_checker.refresh
    }
  }
  if error_repos.length > 0
    show_error(error_repos, action:"远程查询")
  else
    Output.puts_success_message("获取成功!\n") if error_repos.length == 0
  end
end

.serial_enumerate_with_progress(array) ⇒ Object

带进度条串行执行



483
484
485
486
487
488
489
490
491
# File 'lib/m-git/workspace.rb', line 483

def serial_enumerate_with_progress(array)
  task_count = 0
  Output.update_progress(array.length, task_count)
  array.each { |repo|
    yield(repo) if block_given?
    task_count += 1
    Output.update_progress(array.length, task_count)
  }
end

.setup_all_repos(strict_mode: true) ⇒ Object

配置实体仓库对象(完成后可通过all_repos方法或@all_repos属性获取所有可执行仓库对象)

Parameters:

  • strict_mode (Boolean) (defaults to: true)

    default: true 严格模式下,出错直接终止,非严格模式下,出错则抛出异常



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/m-git/workspace.rb', line 147

def setup_all_repos(strict_mode: true)
  repos = []
  locked_repos = []
  missing_light_repos = []

  need_sync_repos = []
  exec_light_repos.each do |light_repo|
    need_sync_repos << light_repo unless Repo.check_git_dest(root, light_repo)
  end

  if need_sync_repos.length > 0
    sync_new_repos(need_sync_repos)
  end

  exec_light_repos.each { |light_repo|

    if strict_mode
      repo = Repo.generate_strictly(root, light_repo)
    else
      repo, _ = Repo.generate_softly(root, light_repo)
    end

    if !repo.nil?
      # 同步被锁仓库,不加入到本次执行中
      if repo.config.lock
        locked_repos.push(repo)
      else
        repos.push(repo)
      end
    else
      missing_light_repos.push(light_repo)
    end
  }

  # 同步锁定仓库
  sync_locked_repos(locked_repos)

  @locked_repos = locked_repos
  @all_repos = repos
  @all_repos += locked_repos if filter_config.include_lock

  missing_light_repos
end

.setup_config(strict_mode: true) ⇒ Object

解析配置文件(完成后可使用@config获取配置对象)

Parameters:

  • strict_mode (Boolean) (defaults to: true)

    default: true 严格模式下,出错直接终止,非严格模式下,出错则抛出异常



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/m-git/workspace.rb', line 40

def setup_config(strict_mode:true)
  # 记录旧config哈希
  hash_sha1 = config.hash_sha1 if !config.nil?

  # 调用manifest_hook
  HooksManager.execute_manifest_hook(strict_mode:strict_mode)

  # 解析config
  @config = Manifest.parse(source_config_dir)

  # 是否更新了配置表
  did_update = hash_sha1.nil? || hash_sha1 != config.hash_sha1

  # --- 同步工作区 ---
  begin
    # 从配置中读取
    should_sync_workspace = MGitConfig.query_with_key(root, :syncworkspace)
    if should_sync_workspace
      # 同步工作区仓库(缓存或弹出)
      Utils.sync_workspace(root, config)
    else
      # 若禁止同步的话,则将缓存弹出(若有的话)
      config.light_repos.each { |light_repo|
        if !Dir.exist?(light_repo.abs_dest(root))
          pop(root, light_repo)
        end
      }
    end
  rescue Error => _
    Output.puts_fail_message("MGit配置读取失败,跳过工作区同步!")
  end

  # ------------------

  # --- 同步.git实体 ---
  begin
    # 从配置中读取
    manage_git = MGitConfig.query_with_key(root, :managegit)
    if !manage_git
      Utils.pop_git_entity(root, config)
    else
      # 当前逻辑是如果配置了托管.git的话,此时不压入.git,而是只把新下载仓库的.git压入
      # 后续如果有需要的话可以打开下面这个注释,这样在每次执行mgit指令时都会根据配置压入.git,起到同步的作用
      # Workspace.push_git_entity(@root, @config)
    end
  rescue Error => _
    Output.puts_fail_message("MGit配置读取失败,跳过.git同步!")
  end
  # ------------------
  did_update
end

.setup_multi_repo_root(initial_root = nil) ⇒ Object

配置mgit根目录



25
26
27
28
29
30
31
32
33
34
# File 'lib/m-git/workspace.rb', line 25

def setup_multi_repo_root(initial_root=nil)
  if initial_root
    @root = initial_root
    return
  end
  @root = multi_repo_root_path
  if @root.nil?
    Foundation.help!('该目录不是多仓库目录!!!')
  end
end

.show_error(error_repos, action: nil) ⇒ Object

显示错误信息



570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
# File 'lib/m-git/workspace.rb', line 570

def show_error(error_repos, action:nil)
  if error_repos.keys.length > 0
    # 压缩错误信息
    error_detail = {}
    error_repos.each { |repo_name, error|
      error = '指令执行失败,但无任何输出,请自行检查。' if error == ''
      error_detail[error] = [] if error_detail[error].nil?
      error_detail[error].push(repo_name)
    }

    # 显示错误
    error_detail.each { |error, repos|
      Output.puts_fail_block(repos, "以上仓库执行#{action}失败,原因:\n#{error}")
    }
  end
end

.sync_locked_repos(repos) ⇒ Object

同步锁定仓库



343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/m-git/workspace.rb', line 343

def sync_locked_repos(repos)
  return if repos.length == 0

  Output.puts_processing_message("正在锁定#{repos.length}个仓库...")
  mutex = Mutex.new
  error_repos = {}
  concurrent_enumerate(repos) { |repo|
    error_message = Repo::SyncHelper.sync_exist_repo(repo, repo.config)
    if !error_message.nil?
      mutex.lock
      error_repos[repo.name] = error_message
      mutex.unlock
    end
  }
  if error_repos.length > 0
    show_error(error_repos, action:'锁定')
  end
end

.sync_new_repos(repos) ⇒ Object



290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/m-git/workspace.rb', line 290

def sync_new_repos(repos)
  return if repos.length == 0

  error_repos = {}

  Utils.show_clone_info(root, repos)
  concurrent_enumerate_with_progress_bar_pure(repos) { |light_repo|
    error_message, _ = Repo::SyncHelper.sync_new_repo(light_repo, root)
    if !error_message.nil?
      Lock.mutex_exec { error_repos[light_repo.name] = error_message }
    end
  }

  # 执行下载后hook
  repos_need_to_guide = []
  concurrent_enumerate(repos) { |light_repo|
    if error_repos[light_repo.name].nil? && # 下载未出错
        !light_repo.lock && # 不是锁定仓库
        !HooksManager.execute_post_download_hook(light_repo.name, light_repo.abs_dest(root)) # hook没有修改HEAD
      repos_need_to_guide.push(light_repo)
    end
  }

  # 引导分支切换
  if repos_need_to_guide.length > 0
    existing_repos = []
    missing_repos = []
    config.repo_list.each { |light_repo|
      repo, _ = Repo.generate_softly(root, light_repo)
      if !repo.nil?
        if repos_need_to_guide.include?(light_repo)
          missing_repos.push(repo)
        else
          existing_repos.push(repo)
        end
      end
    }

    repo_combo = missing_repos + existing_repos
    if has_diff_branch?(repo_combo)
      # 提示切换新下载仓库
      guide_to_checkout_branch(missing_repos, existing_repos)
      # 切换完成后如果所出分支不一致,给出提示
      Output.puts_remind_message("注意,当前所有仓库并不处于统一分支,可通过\"mgit branch --compact\"查看。") if has_diff_branch?(repo_combo)
    end
  end

  if error_repos.length > 0
    show_error(error_repos, action:'锁定')
  end
end

.update_all_repos(update_repos_names) ⇒ Object

提供一组light repo,更新repo对象



208
209
210
211
212
213
214
215
# File 'lib/m-git/workspace.rb', line 208

def update_all_repos(update_repos_names)
  if update_repos_names.is_a?(Array)
    update_light_repos = config.repo_list(selection:update_repos_names)
    @exec_light_repos = update_light_repos
    @all_repos = nil
    setup_all_repos
  end
end

.update_config(strict_mode: true) {|missing_repos| ... } ⇒ Object

更新配置解析结果,并同步缺失仓库

Yields:

  • (missing_repos)


93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/m-git/workspace.rb', line 93

def update_config(strict_mode:true)
  Output.puts_processing_message("检查多仓库配置信息...")
  if setup_config(strict_mode:strict_mode)
    Output.puts_success_message("配置信息已更新!\n")
  else
    # 配置表未更新,直接返回
    Output.puts_success_message("配置信息已为最新!\n")
    return
  end

  origin_all_repo_names = all_repos.map { |e| e.name }
  @all_repos, @exec_light_repos = nil, nil
  missing_repos = []
  missing_light_repos = setup_all_repos(strict_mode:false)
  if missing_light_repos.length > 0

    Utils.show_clone_info(root, missing_light_repos)
    mutex = Mutex.new
    error_repos = {}
    task_count = 0
    Output.update_progress(missing_light_repos.length, task_count)
    concurrent_enumerate(missing_light_repos) { |light_repo|
      error_message, _ = Repo::SyncHelper.sync_new_repo(light_repo, root)
      mutex.lock
      if error_message.nil?
        missing_repos.push(Repo.generate_strictly(root, light_repo))
      else
        error_repos[light_repo.name] = error_message
      end
      task_count += 1
      Output.update_progress(missing_light_repos.length, task_count)
      mutex.unlock
    }
    if error_repos.length > 0
      show_error(error_repos, action:'下载操作')
      #(注意,如果不希望被下载仓库的.git实体被mgit管理,请执行\"mgit sync -n -o\",该方式将不会把.git实体放置到.mgit/souce-git中,更适合开发中途接入mgit的用户)
      Foundation.help!("请检查原因并执行\"mgit sync -n\"重新下载。")
    else
      Output.puts_success_message("下载成功!\n")
    end
  end

  # 加入将当前分支上本地已有的新仓库
  current_branch_exist_new_repos = all_repos.select { |repo| !origin_all_repo_names.include?(repo.name) }
  missing_repos += current_branch_exist_new_repos

  # 新仓库当前分支可能并不是所需分支,可以再进一步操作
  yield(missing_repos) if block_given? && missing_repos.length > 0
end