git-multirepo 1.0.0.beta70 → 1.0.0.beta71

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 (72) hide show
  1. checksums.yaml +5 -5
  2. data/.gitattributes +4 -4
  3. data/.gitbugtraq +3 -3
  4. data/.gitignore +38 -38
  5. data/.rspec +2 -2
  6. data/.rubocop.yml +79 -79
  7. data/CHANGELOG.md +116 -112
  8. data/Gemfile +4 -4
  9. data/Gemfile.lock +47 -47
  10. data/LICENSE +22 -22
  11. data/README.md +178 -178
  12. data/Rakefile +1 -1
  13. data/bin/multi +11 -11
  14. data/docs/bug-repros/91565510-repro.sh +20 -20
  15. data/git-multirepo.gemspec +31 -31
  16. data/lib/git-multirepo.rb +3 -3
  17. data/lib/multirepo/commands/add-command.rb +55 -55
  18. data/lib/multirepo/commands/branch-command.rb +88 -88
  19. data/lib/multirepo/commands/checkout-command.rb +127 -127
  20. data/lib/multirepo/commands/clone-command.rb +68 -68
  21. data/lib/multirepo/commands/command.rb +87 -87
  22. data/lib/multirepo/commands/commands.rb +14 -14
  23. data/lib/multirepo/commands/do-command.rb +101 -101
  24. data/lib/multirepo/commands/init-command.rb +121 -121
  25. data/lib/multirepo/commands/inspect-command.rb +48 -48
  26. data/lib/multirepo/commands/install-command.rb +170 -170
  27. data/lib/multirepo/commands/merge-command.rb +249 -249
  28. data/lib/multirepo/commands/open-command.rb +55 -55
  29. data/lib/multirepo/commands/remove-command.rb +48 -48
  30. data/lib/multirepo/commands/uninit-command.rb +18 -18
  31. data/lib/multirepo/commands/update-command.rb +112 -112
  32. data/lib/multirepo/config.rb +19 -19
  33. data/lib/multirepo/files/config-entry.rb +39 -39
  34. data/lib/multirepo/files/config-file.rb +52 -52
  35. data/lib/multirepo/files/lock-entry.rb +29 -29
  36. data/lib/multirepo/files/lock-file.rb +62 -62
  37. data/lib/multirepo/files/meta-file.rb +51 -51
  38. data/lib/multirepo/files/tracking-file.rb +9 -9
  39. data/lib/multirepo/files/tracking-files.rb +64 -64
  40. data/lib/multirepo/git/branch.rb +32 -32
  41. data/lib/multirepo/git/change.rb +11 -11
  42. data/lib/multirepo/git/commit.rb +7 -7
  43. data/lib/multirepo/git/git-runner.rb +56 -56
  44. data/lib/multirepo/git/git.rb +10 -10
  45. data/lib/multirepo/git/ref.rb +38 -38
  46. data/lib/multirepo/git/remote.rb +17 -17
  47. data/lib/multirepo/git/repo.rb +131 -131
  48. data/lib/multirepo/hooks/post-commit-hook.rb +23 -23
  49. data/lib/multirepo/hooks/pre-commit-hook.rb +35 -35
  50. data/lib/multirepo/info.rb +5 -5
  51. data/lib/multirepo/logic/dependency.rb +6 -6
  52. data/lib/multirepo/logic/merge-descriptor.rb +95 -95
  53. data/lib/multirepo/logic/node.rb +75 -75
  54. data/lib/multirepo/logic/performer.rb +62 -62
  55. data/lib/multirepo/logic/repo-selection.rb +25 -25
  56. data/lib/multirepo/logic/revision-selection.rb +15 -15
  57. data/lib/multirepo/logic/revision-selector.rb +23 -23
  58. data/lib/multirepo/logic/version-comparer.rb +10 -10
  59. data/lib/multirepo/multirepo-exception.rb +6 -6
  60. data/lib/multirepo/output/extra-output.rb +12 -12
  61. data/lib/multirepo/output/teamcity-extra-output.rb +11 -11
  62. data/lib/multirepo/utility/console.rb +52 -52
  63. data/lib/multirepo/utility/popen-runner.rb +27 -27
  64. data/lib/multirepo/utility/system-runner.rb +14 -14
  65. data/lib/multirepo/utility/utils.rb +107 -107
  66. data/lib/multirepo/utility/verbosity.rb +6 -6
  67. data/resources/.gitconfig +2 -2
  68. data/resources/post-commit +0 -0
  69. data/resources/pre-commit +0 -0
  70. data/spec/integration/init_spec.rb +19 -19
  71. data/spec/spec_helper.rb +89 -89
  72. metadata +3 -3
data/Rakefile CHANGED
@@ -1 +1 @@
1
- require "bundler/gem_tasks"
1
+ require "bundler/gem_tasks"
data/bin/multi CHANGED
@@ -1,11 +1,11 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "claide"
4
- require "multirepo/commands/commands"
5
-
6
- trap("INT") do
7
- puts "\rAbort, abort!!" # \r hides the interrupt control characters
8
- exit
9
- end
10
-
11
- MultiRepo::Command.run(ARGV)
1
+ #!/usr/bin/env ruby
2
+
3
+ require "claide"
4
+ require "multirepo/commands/commands"
5
+
6
+ trap("INT") do
7
+ puts "\rAbort, abort!!" # \r hides the interrupt control characters
8
+ exit
9
+ end
10
+
11
+ MultiRepo::Command.run(ARGV)
@@ -1,21 +1,21 @@
1
- echo "----> Setup a new test repo"
2
- dir_name="PreCommitHookAddTest"
3
- rm -rf $dir_name; mkdir $dir_name; cd $dir_name
4
- git init; git commit --allow-empty -m "Initial commit"
5
-
6
- echo "----> Add a pre-commit hook that stages a file that doesn't currently exist in the repo"
7
- echo "touch auto-added; git add auto-added" > .git/hooks/pre-commit
8
- chmod +x .git/hooks/pre-commit
9
-
10
- echo "----> Try committing a new file using the '-o' flag"
11
- touch manually-added; git add manually-added
12
- git commit -o -m "Commit that ran the pre-commit hook and should contain file 'auto-added'" -- manually-added
13
-
14
- echo "----> Results (expected: working copy clean; actual: auto-added is reported as both DELETED and UNTRACKED. HEAD and working copy are the same, staging area contains ‘incorrect' state)"
15
- git status
16
-
17
- echo "----> Stage the file after the fact"
18
- git add auto-added
19
-
20
- echo "----> Notice that the working copy is now clean"
1
+ echo "----> Setup a new test repo"
2
+ dir_name="PreCommitHookAddTest"
3
+ rm -rf $dir_name; mkdir $dir_name; cd $dir_name
4
+ git init; git commit --allow-empty -m "Initial commit"
5
+
6
+ echo "----> Add a pre-commit hook that stages a file that doesn't currently exist in the repo"
7
+ echo "touch auto-added; git add auto-added" > .git/hooks/pre-commit
8
+ chmod +x .git/hooks/pre-commit
9
+
10
+ echo "----> Try committing a new file using the '-o' flag"
11
+ touch manually-added; git add manually-added
12
+ git commit -o -m "Commit that ran the pre-commit hook and should contain file 'auto-added'" -- manually-added
13
+
14
+ echo "----> Results (expected: working copy clean; actual: auto-added is reported as both DELETED and UNTRACKED. HEAD and working copy are the same, staging area contains ‘incorrect' state)"
15
+ git status
16
+
17
+ echo "----> Stage the file after the fact"
18
+ git add auto-added
19
+
20
+ echo "----> Notice that the working copy is now clean"
21
21
  git status
@@ -1,31 +1,31 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "multirepo/info"
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = MultiRepo::NAME
8
- spec.version = MultiRepo::VERSION
9
- spec.authors = ["Michaël Fortin"]
10
- spec.email = ["fortinmike@irradiated.net"]
11
- spec.summary = "Track multiple Git repositories side-by-side"
12
- spec.description = MultiRepo::DESCRIPTION
13
- spec.homepage = "https://github.com/fortinmike/git-multirepo"
14
- spec.license = "MIT"
15
-
16
- spec.required_ruby_version = '~> 2.0'
17
- spec.files = `git ls-files -z`.split("\x0")
18
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
- spec.require_paths = ["lib"]
21
-
22
- spec.add_development_dependency "bundler", "~> 1.7"
23
- spec.add_development_dependency "rake", "~> 10.0"
24
- spec.add_development_dependency "rspec", "~> 3.1.0"
25
-
26
- spec.add_runtime_dependency "claide", "~> 1.0.1"
27
- spec.add_runtime_dependency "colored", "~> 1.2"
28
- spec.add_runtime_dependency "os", "~> 0.9.6"
29
- spec.add_runtime_dependency "terminal-table", "~> 1.7.3"
30
- spec.add_runtime_dependency "naturally", "~> 2.1"
31
- end
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "multirepo/info"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = MultiRepo::NAME
8
+ spec.version = MultiRepo::VERSION
9
+ spec.authors = ["Michaël Fortin"]
10
+ spec.email = ["fortinmike@irradiated.net"]
11
+ spec.summary = "Track multiple Git repositories side-by-side"
12
+ spec.description = MultiRepo::DESCRIPTION
13
+ spec.homepage = "https://github.com/fortinmike/git-multirepo"
14
+ spec.license = "MIT"
15
+
16
+ spec.required_ruby_version = '~> 2.0'
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.7"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec", "~> 3.1.0"
25
+
26
+ spec.add_runtime_dependency "claide", "~> 1.0.1"
27
+ spec.add_runtime_dependency "colored", "~> 1.2"
28
+ spec.add_runtime_dependency "os", "~> 0.9.6"
29
+ spec.add_runtime_dependency "terminal-table", "~> 1.7.3"
30
+ spec.add_runtime_dependency "naturally", "~> 2.1"
31
+ end
data/lib/git-multirepo.rb CHANGED
@@ -1,3 +1,3 @@
1
- require "multirepo/utility/console"
2
- require "multirepo/hooks/pre-commit-hook"
3
- require "multirepo/hooks/post-commit-hook"
1
+ require "multirepo/utility/console"
2
+ require "multirepo/hooks/pre-commit-hook"
3
+ require "multirepo/hooks/post-commit-hook"
@@ -1,55 +1,55 @@
1
- require "multirepo/utility/console"
2
- require "multirepo/files/config-file"
3
-
4
- module MultiRepo
5
- class AddCommand < Command
6
- self.command = "add"
7
- self.summary = "Track an additional dependency with multirepo."
8
-
9
- def self.options
10
- [['<path>', 'The relative path to the new dependency (e.g. ../MyNewDependency)']].concat(super)
11
- end
12
-
13
- def initialize(argv)
14
- @path = with_trailing_slash(argv.shift_argument)
15
- super
16
- end
17
-
18
- def validate!
19
- super
20
- help! "You must specify a repository to add as a dependency" unless @path
21
- end
22
-
23
- def run
24
- ensure_in_work_tree
25
- ensure_multirepo_enabled
26
- ensure_repo_valid
27
-
28
- config_file = ConfigFile.new(".")
29
- entry = ConfigEntry.new(Repo.new(@path))
30
-
31
- if config_file.entry_exists?(entry)
32
- Console.log_info("There is already an entry for '#{@path}' in the .multirepo file")
33
- else
34
- config_file.add_entry(entry)
35
- Console.log_step("Added '#{@path}' to the .multirepo file")
36
- end
37
- end
38
-
39
- def ensure_repo_valid
40
- fail MultiRepoException, "The provided path is not a direct sibling of the main repository" unless validate_is_sibling_repo(@path)
41
- fail MultiRepoException, "There is no folder at path '#{@path}'" unless Dir.exist?(@path)
42
- fail MultiRepoException, "'#{@path}' is not a repository" unless Repo.new(@path).exists?
43
- end
44
-
45
- def validate_is_sibling_repo(path)
46
- parent_dir = File.expand_path("..")
47
- path = File.expand_path("..", path)
48
- return parent_dir == path
49
- end
50
-
51
- def with_trailing_slash(path)
52
- if path.end_with?("/") then path else path + "/" end
53
- end
54
- end
55
- end
1
+ require "multirepo/utility/console"
2
+ require "multirepo/files/config-file"
3
+
4
+ module MultiRepo
5
+ class AddCommand < Command
6
+ self.command = "add"
7
+ self.summary = "Track an additional dependency with multirepo."
8
+
9
+ def self.options
10
+ [['<path>', 'The relative path to the new dependency (e.g. ../MyNewDependency)']].concat(super)
11
+ end
12
+
13
+ def initialize(argv)
14
+ @path = with_trailing_slash(argv.shift_argument)
15
+ super
16
+ end
17
+
18
+ def validate!
19
+ super
20
+ help! "You must specify a repository to add as a dependency" unless @path
21
+ end
22
+
23
+ def run
24
+ ensure_in_work_tree
25
+ ensure_multirepo_enabled
26
+ ensure_repo_valid
27
+
28
+ config_file = ConfigFile.new(".")
29
+ entry = ConfigEntry.new(Repo.new(@path))
30
+
31
+ if config_file.entry_exists?(entry)
32
+ Console.log_info("There is already an entry for '#{@path}' in the .multirepo file")
33
+ else
34
+ config_file.add_entry(entry)
35
+ Console.log_step("Added '#{@path}' to the .multirepo file")
36
+ end
37
+ end
38
+
39
+ def ensure_repo_valid
40
+ fail MultiRepoException, "The provided path is not a direct sibling of the main repository" unless validate_is_sibling_repo(@path)
41
+ fail MultiRepoException, "There is no folder at path '#{@path}'" unless Dir.exist?(@path)
42
+ fail MultiRepoException, "'#{@path}' is not a repository" unless Repo.new(@path).exists?
43
+ end
44
+
45
+ def validate_is_sibling_repo(path)
46
+ parent_dir = File.expand_path("..")
47
+ path = File.expand_path("..", path)
48
+ return parent_dir == path
49
+ end
50
+
51
+ def with_trailing_slash(path)
52
+ if path.end_with?("/") then path else path + "/" end
53
+ end
54
+ end
55
+ end
@@ -1,88 +1,88 @@
1
- require "multirepo/utility/console"
2
- require "multirepo/git/git"
3
- require "multirepo/files/config-file"
4
- require "multirepo/files/tracking-files"
5
- require "multirepo/logic/performer"
6
-
7
- module MultiRepo
8
- class BranchCommand < Command
9
- self.command = "branch"
10
- self.summary = "Create and/or checkout a new branch for all repos."
11
-
12
- def self.options
13
- [
14
- ['<branch name>', 'The name of the branch to create and checkout.'],
15
- ['[--force]', 'Force creating the branch even if there are uncommmitted changes.'],
16
- ['[--push]', 'Push the branch on creation.']
17
- ].concat(super)
18
- end
19
-
20
- def initialize(argv)
21
- @branch_name = argv.shift_argument
22
- @force = argv.flag?("force")
23
- @push = argv.flag?("push", false)
24
- super
25
- end
26
-
27
- def validate!
28
- super
29
- help! "You must specify a branch name" unless @branch_name
30
- help! "Please provide a valid branch name" unless Git.valid_branch_name?(@branch_name)
31
- end
32
-
33
- def run
34
- ensure_in_work_tree
35
- ensure_multirepo_enabled
36
-
37
- Console.log_step("Branching...")
38
-
39
- main_repo = Repo.new(".")
40
-
41
- unless @force
42
- # Ensure the main repo is clean
43
- fail MultiRepoException, "Main repo is not clean; multi branch aborted" unless main_repo.clean?
44
-
45
- # Ensure dependencies are clean
46
- config_entries = ConfigFile.new(".").load_entries
47
- unless Utils.dependencies_clean?(config_entries)
48
- fail MultiRepoException, "Dependencies are not clean; multi branch aborted"
49
- end
50
- end
51
-
52
- # Branch dependencies
53
- Performer.depth_ordered_dependencies.each do |dependency|
54
- perform_branch(dependency.config_entry.repo)
55
- end
56
-
57
- # Branch the main repo
58
- perform_branch(main_repo)
59
-
60
- Console.log_step("Done!")
61
- end
62
-
63
- def perform_branch(repo)
64
- Console.log_substep("Branching '#{repo.path}' ...")
65
- Console.log_info("Creating and checking out branch #{@branch_name} ...")
66
-
67
- branch = repo.branch(@branch_name)
68
- branch.create unless branch.exists?
69
- branch.checkout
70
-
71
- if Utils.multirepo_enabled?(repo.path)
72
- Console.log_info("Updating and committing tracking files")
73
- tracking_files = TrackingFiles.new(repo.path)
74
- tracking_files.update
75
- tracking_files.commit("[multirepo] Post-branch tracking files update")
76
- end
77
-
78
- return unless @push
79
-
80
- if @force
81
- Console.log_warning("Skipping #{@branch_name} branch push because we're force-branching")
82
- else
83
- Console.log_info("Pushing #{@branch_name} to origin/#{@branch_name}")
84
- repo.branch(@branch_name).push
85
- end
86
- end
87
- end
88
- end
1
+ require "multirepo/utility/console"
2
+ require "multirepo/git/git"
3
+ require "multirepo/files/config-file"
4
+ require "multirepo/files/tracking-files"
5
+ require "multirepo/logic/performer"
6
+
7
+ module MultiRepo
8
+ class BranchCommand < Command
9
+ self.command = "branch"
10
+ self.summary = "Create and/or checkout a new branch for all repos."
11
+
12
+ def self.options
13
+ [
14
+ ['<branch name>', 'The name of the branch to create and checkout.'],
15
+ ['[--force]', 'Force creating the branch even if there are uncommmitted changes.'],
16
+ ['[--push]', 'Push the branch on creation.']
17
+ ].concat(super)
18
+ end
19
+
20
+ def initialize(argv)
21
+ @branch_name = argv.shift_argument
22
+ @force = argv.flag?("force")
23
+ @push = argv.flag?("push", false)
24
+ super
25
+ end
26
+
27
+ def validate!
28
+ super
29
+ help! "You must specify a branch name" unless @branch_name
30
+ help! "Please provide a valid branch name" unless Git.valid_branch_name?(@branch_name)
31
+ end
32
+
33
+ def run
34
+ ensure_in_work_tree
35
+ ensure_multirepo_enabled
36
+
37
+ Console.log_step("Branching...")
38
+
39
+ main_repo = Repo.new(".")
40
+
41
+ unless @force
42
+ # Ensure the main repo is clean
43
+ fail MultiRepoException, "Main repo is not clean; multi branch aborted" unless main_repo.clean?
44
+
45
+ # Ensure dependencies are clean
46
+ config_entries = ConfigFile.new(".").load_entries
47
+ unless Utils.dependencies_clean?(config_entries)
48
+ fail MultiRepoException, "Dependencies are not clean; multi branch aborted"
49
+ end
50
+ end
51
+
52
+ # Branch dependencies
53
+ Performer.depth_ordered_dependencies.each do |dependency|
54
+ perform_branch(dependency.config_entry.repo)
55
+ end
56
+
57
+ # Branch the main repo
58
+ perform_branch(main_repo)
59
+
60
+ Console.log_step("Done!")
61
+ end
62
+
63
+ def perform_branch(repo)
64
+ Console.log_substep("Branching '#{repo.path}' ...")
65
+ Console.log_info("Creating and checking out branch #{@branch_name} ...")
66
+
67
+ branch = repo.branch(@branch_name)
68
+ branch.create unless branch.exists?
69
+ branch.checkout
70
+
71
+ if Utils.multirepo_enabled?(repo.path)
72
+ Console.log_info("Updating and committing tracking files")
73
+ tracking_files = TrackingFiles.new(repo.path)
74
+ tracking_files.update
75
+ tracking_files.commit("[multirepo] Post-branch tracking files update")
76
+ end
77
+
78
+ return unless @push
79
+
80
+ if @force
81
+ Console.log_warning("Skipping #{@branch_name} branch push because we're force-branching")
82
+ else
83
+ Console.log_info("Pushing #{@branch_name} to origin/#{@branch_name}")
84
+ repo.branch(@branch_name).push
85
+ end
86
+ end
87
+ end
88
+ end
@@ -1,127 +1,127 @@
1
- require "multirepo/utility/console"
2
- require "multirepo/utility/utils"
3
- require "multirepo/output/extra-output"
4
- require "multirepo/logic/revision-selector"
5
- require "multirepo/logic/performer"
6
-
7
- module MultiRepo
8
- class CheckoutCommand < Command
9
- self.command = "checkout"
10
- self.summary = "Checks out the specified commit or branch of the main repo and checks out matching versions of all dependencies."
11
-
12
- def self.options
13
- [
14
- ['<refname>', 'The main repo tag, branch or commit id to checkout.'],
15
- ['[--latest]', 'Checkout the HEAD of each dependency branch (as recorded in the lock file) instead of the exact required commits.'],
16
- ['[--exact]', 'Checkout the exact specified ref for each repo, regardless of what\'s stored in the lock file.'],
17
- ['[--force]', 'Force checkout even if there are uncommmitted changes.']
18
- ].concat(super)
19
- end
20
-
21
- def initialize(argv)
22
- @ref_name = argv.shift_argument
23
- @checkout_latest = argv.flag?("latest")
24
- @checkout_exact = argv.flag?("exact")
25
- @force = argv.flag?("force")
26
- super
27
- end
28
-
29
- def validate!
30
- super
31
- help! "You must specify a branch or commit id to checkout" unless @ref_name
32
- unless Utils.only_one_true?(@checkout_latest, @checkout_exact)
33
- help! "You can't provide more than one operation modifier (--latest, --exact, etc.)"
34
- end
35
- end
36
-
37
- def run
38
- ensure_in_work_tree
39
-
40
- # Find out the checkout mode based on command-line options
41
- mode = RevisionSelector.mode_for_args(@checkout_latest, @checkout_exact)
42
-
43
- strategy_name = RevisionSelection.name_for_mode(mode)
44
- Console.log_step("Checking out #{@ref_name} and its dependencies using the '#{strategy_name}' strategy...")
45
-
46
- main_repo = Repo.new(".")
47
-
48
- unless proceed_if_merge_commit?(main_repo, @ref_name, mode)
49
- fail MultiRepoException, "Aborting checkout"
50
- end
51
-
52
- checkout_core(main_repo, mode)
53
-
54
- Console.log_step("Done!")
55
- end
56
-
57
- def checkout_core(main_repo, mode)
58
- initial_revision = main_repo.current_revision
59
- begin
60
- # Checkout first because the current ref might not be multirepo-enabled
61
- checkout_main_repo_step(main_repo)
62
- # Only then can we check for dependencies and make sure they are clean
63
- ensure_dependencies_clean_step if !@force
64
- rescue MultiRepoException => e
65
- Console.log_warning("Restoring working copy to #{initial_revision}")
66
- main_repo.checkout(initial_revision)
67
- raise e
68
- end
69
- dependencies_checkout_step(mode, @ref_name)
70
- end
71
-
72
- def checkout_main_repo_step(main_repo)
73
- Performer.perform_main_repo_checkout(main_repo, @ref_name, @force)
74
- end
75
-
76
- def ensure_dependencies_clean_step
77
- unless Utils.dependencies_clean?(ConfigFile.new(".").load_entries)
78
- fail MultiRepoException, "Dependencies are not clean!"
79
- end
80
- end
81
-
82
- def dependencies_checkout_step(mode, ref_name = nil)
83
- Performer.depth_ordered_dependencies.each do |dependency|
84
- # Find out the required dependency revision based on the checkout mode
85
- revision = RevisionSelector.revision_for_mode(mode, ref_name, dependency.lock_entry)
86
- perform_dependency_checkout(dependency.config_entry, revision)
87
- end
88
- end
89
-
90
- def proceed_if_merge_commit?(main_repo, ref_name, mode)
91
- return true unless main_repo.ref(ref_name).merge_commit?
92
-
93
- case mode
94
- when RevisionSelection::AS_LOCK
95
- Console.log_error("The specified ref is a merge commit and an \"as-lock\" checkout was requested.")
96
- Console.log_error("The resulting checkout would most probably not result in a valid project state.")
97
- return false
98
- when RevisionSelection::LATEST
99
- Console.log_warning("The specified ref is a merge commit and a \"latest\" checkout was requested.")
100
- Console.log_warning("The work branches recorded in the branch from which the merge was performed will be checked out.")
101
- end
102
-
103
- return true
104
- end
105
-
106
- def perform_dependency_checkout(config_entry, revision)
107
- dependency_name = config_entry.repo.basename
108
-
109
- # Make sure the repo exists on disk, and clone it if it doesn't
110
- # (in case the checked-out revision had an additional dependency)
111
- unless config_entry.repo.exists?
112
- Console.log_substep("Cloning missing dependency '#{config_entry.path}' from #{config_entry.url}")
113
- ExtraOutput.progress("Cloning missing dependency #{config_entry.path}")
114
- config_entry.repo.clone(config_entry.url)
115
- end
116
-
117
- # Checkout!
118
- ExtraOutput.progress("Checking out #{dependency_name} #{revision}")
119
- if config_entry.repo.checkout(revision)
120
- Console.log_substep("Checked out #{dependency_name} '#{revision}'")
121
- else
122
- ExtraOutput.error("Couldn|'t check out dependency #{dependency_name}")
123
- fail MultiRepoException, "Couldn't check out the appropriate version of dependency #{dependency_name}"
124
- end
125
- end
126
- end
127
- end
1
+ require "multirepo/utility/console"
2
+ require "multirepo/utility/utils"
3
+ require "multirepo/output/extra-output"
4
+ require "multirepo/logic/revision-selector"
5
+ require "multirepo/logic/performer"
6
+
7
+ module MultiRepo
8
+ class CheckoutCommand < Command
9
+ self.command = "checkout"
10
+ self.summary = "Checks out the specified commit or branch of the main repo and checks out matching versions of all dependencies."
11
+
12
+ def self.options
13
+ [
14
+ ['<refname>', 'The main repo tag, branch or commit id to checkout.'],
15
+ ['[--latest]', 'Checkout the HEAD of each dependency branch (as recorded in the lock file) instead of the exact required commits.'],
16
+ ['[--exact]', 'Checkout the exact specified ref for each repo, regardless of what\'s stored in the lock file.'],
17
+ ['[--force]', 'Force checkout even if there are uncommmitted changes.']
18
+ ].concat(super)
19
+ end
20
+
21
+ def initialize(argv)
22
+ @ref_name = argv.shift_argument
23
+ @checkout_latest = argv.flag?("latest")
24
+ @checkout_exact = argv.flag?("exact")
25
+ @force = argv.flag?("force")
26
+ super
27
+ end
28
+
29
+ def validate!
30
+ super
31
+ help! "You must specify a branch or commit id to checkout" unless @ref_name
32
+ unless Utils.only_one_true?(@checkout_latest, @checkout_exact)
33
+ help! "You can't provide more than one operation modifier (--latest, --exact, etc.)"
34
+ end
35
+ end
36
+
37
+ def run
38
+ ensure_in_work_tree
39
+
40
+ # Find out the checkout mode based on command-line options
41
+ mode = RevisionSelector.mode_for_args(@checkout_latest, @checkout_exact)
42
+
43
+ strategy_name = RevisionSelection.name_for_mode(mode)
44
+ Console.log_step("Checking out #{@ref_name} and its dependencies using the '#{strategy_name}' strategy...")
45
+
46
+ main_repo = Repo.new(".")
47
+
48
+ unless proceed_if_merge_commit?(main_repo, @ref_name, mode)
49
+ fail MultiRepoException, "Aborting checkout"
50
+ end
51
+
52
+ checkout_core(main_repo, mode)
53
+
54
+ Console.log_step("Done!")
55
+ end
56
+
57
+ def checkout_core(main_repo, mode)
58
+ initial_revision = main_repo.current_revision
59
+ begin
60
+ # Checkout first because the current ref might not be multirepo-enabled
61
+ checkout_main_repo_step(main_repo)
62
+ # Only then can we check for dependencies and make sure they are clean
63
+ ensure_dependencies_clean_step if !@force
64
+ rescue MultiRepoException => e
65
+ Console.log_warning("Restoring working copy to #{initial_revision}")
66
+ main_repo.checkout(initial_revision)
67
+ raise e
68
+ end
69
+ dependencies_checkout_step(mode, @ref_name)
70
+ end
71
+
72
+ def checkout_main_repo_step(main_repo)
73
+ Performer.perform_main_repo_checkout(main_repo, @ref_name, @force)
74
+ end
75
+
76
+ def ensure_dependencies_clean_step
77
+ unless Utils.dependencies_clean?(ConfigFile.new(".").load_entries)
78
+ fail MultiRepoException, "Dependencies are not clean!"
79
+ end
80
+ end
81
+
82
+ def dependencies_checkout_step(mode, ref_name = nil)
83
+ Performer.depth_ordered_dependencies.each do |dependency|
84
+ # Find out the required dependency revision based on the checkout mode
85
+ revision = RevisionSelector.revision_for_mode(mode, ref_name, dependency.lock_entry)
86
+ perform_dependency_checkout(dependency.config_entry, revision)
87
+ end
88
+ end
89
+
90
+ def proceed_if_merge_commit?(main_repo, ref_name, mode)
91
+ return true unless main_repo.ref(ref_name).merge_commit?
92
+
93
+ case mode
94
+ when RevisionSelection::AS_LOCK
95
+ Console.log_error("The specified ref is a merge commit and an \"as-lock\" checkout was requested.")
96
+ Console.log_error("The resulting checkout would most probably not result in a valid project state.")
97
+ return false
98
+ when RevisionSelection::LATEST
99
+ Console.log_warning("The specified ref is a merge commit and a \"latest\" checkout was requested.")
100
+ Console.log_warning("The work branches recorded in the branch from which the merge was performed will be checked out.")
101
+ end
102
+
103
+ return true
104
+ end
105
+
106
+ def perform_dependency_checkout(config_entry, revision)
107
+ dependency_name = config_entry.repo.basename
108
+
109
+ # Make sure the repo exists on disk, and clone it if it doesn't
110
+ # (in case the checked-out revision had an additional dependency)
111
+ unless config_entry.repo.exists?
112
+ Console.log_substep("Cloning missing dependency '#{config_entry.path}' from #{config_entry.url}")
113
+ ExtraOutput.progress("Cloning missing dependency #{config_entry.path}")
114
+ config_entry.repo.clone(config_entry.url)
115
+ end
116
+
117
+ # Checkout!
118
+ ExtraOutput.progress("Checking out #{dependency_name} #{revision}")
119
+ if config_entry.repo.checkout(revision)
120
+ Console.log_substep("Checked out #{dependency_name} '#{revision}'")
121
+ else
122
+ ExtraOutput.error("Couldn|'t check out dependency #{dependency_name}")
123
+ fail MultiRepoException, "Couldn't check out the appropriate version of dependency #{dependency_name}"
124
+ end
125
+ end
126
+ end
127
+ end