git-multirepo 1.0.0.beta34 → 1.0.0.beta37

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +2 -2
  3. data/.gitignore +38 -38
  4. data/.multirepo.meta +2 -2
  5. data/.rspec +2 -2
  6. data/Gemfile +4 -4
  7. data/Gemfile.lock +38 -38
  8. data/LICENSE +22 -22
  9. data/README.md +143 -143
  10. data/Rakefile +2 -2
  11. data/bin/multi +10 -10
  12. data/docs/bug-repros/91565510-repro.sh +20 -20
  13. data/docs/git-multirepo-node-dependency-depth-algorithm.pdf +0 -0
  14. data/docs/graphs/dependencies-on-multiple-levels.graffle +0 -0
  15. data/docs/graphs/dependencies-on-one-level.graffle +0 -0
  16. data/git-multirepo.gemspec +29 -29
  17. data/lib/commands.rb +13 -12
  18. data/lib/git-multirepo.rb +1 -1
  19. data/lib/info.rb +4 -4
  20. data/lib/multirepo/commands/add-command.rb +53 -51
  21. data/lib/multirepo/commands/branch-command.rb +65 -60
  22. data/lib/multirepo/commands/checkout-command.rb +119 -140
  23. data/lib/multirepo/commands/clean-command.rb +31 -31
  24. data/lib/multirepo/commands/clone-command.rb +70 -70
  25. data/lib/multirepo/commands/command.rb +71 -71
  26. data/lib/multirepo/commands/fetch-command.rb +30 -30
  27. data/lib/multirepo/commands/init-command.rb +119 -119
  28. data/lib/multirepo/commands/install-command.rb +103 -103
  29. data/lib/multirepo/commands/merge-command.rb +126 -0
  30. data/lib/multirepo/commands/open-command.rb +26 -26
  31. data/lib/multirepo/commands/remove-command.rb +50 -49
  32. data/lib/multirepo/commands/uninit-command.rb +20 -20
  33. data/lib/multirepo/commands/update-command.rb +59 -59
  34. data/lib/multirepo/config.rb +15 -15
  35. data/lib/multirepo/files/config-entry.rb +34 -34
  36. data/lib/multirepo/files/config-file.rb +45 -34
  37. data/lib/multirepo/files/lock-entry.rb +24 -25
  38. data/lib/multirepo/files/lock-file.rb +39 -28
  39. data/lib/multirepo/files/meta-file.rb +41 -33
  40. data/lib/multirepo/files/tracking-file.rb +8 -8
  41. data/lib/multirepo/files/tracking-files.rb +41 -43
  42. data/lib/multirepo/git/branch.rb +27 -27
  43. data/lib/multirepo/git/change.rb +10 -10
  44. data/lib/multirepo/git/commit.rb +17 -17
  45. data/lib/multirepo/git/git.rb +39 -39
  46. data/lib/multirepo/git/remote.rb +16 -16
  47. data/lib/multirepo/git/repo.rb +77 -77
  48. data/lib/multirepo/hooks/post-commit-hook.rb +22 -22
  49. data/lib/multirepo/hooks/pre-commit-hook.rb +29 -29
  50. data/lib/multirepo/logic/node.rb +41 -0
  51. data/lib/multirepo/logic/performer.rb +59 -0
  52. data/lib/multirepo/logic/revision-selector.rb +27 -0
  53. data/lib/multirepo/multirepo-exception.rb +5 -5
  54. data/lib/multirepo/utility/console.rb +51 -51
  55. data/lib/multirepo/utility/runner.rb +34 -34
  56. data/lib/multirepo/utility/utils.rb +65 -66
  57. data/resources/.gitconfig +2 -2
  58. data/resources/post-commit +5 -5
  59. data/resources/pre-commit +5 -5
  60. data/spec/integration/init_spec.rb +18 -18
  61. data/spec/spec_helper.rb +89 -89
  62. metadata +10 -3
data/Rakefile CHANGED
@@ -1,2 +1,2 @@
1
- require "bundler/gem_tasks"
2
-
1
+ require "bundler/gem_tasks"
2
+
data/bin/multi CHANGED
@@ -1,11 +1,11 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "claide"
4
- require "commands"
5
-
6
- trap("INT") do
7
- puts "\rAbort, abort!!" # \r hides the interrupt control characters
8
- exit
9
- end
10
-
1
+ #!/usr/bin/env ruby
2
+
3
+ require "claide"
4
+ require "commands"
5
+
6
+ trap("INT") do
7
+ puts "\rAbort, abort!!" # \r hides the interrupt control characters
8
+ exit
9
+ end
10
+
11
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,29 +1,29 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require '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 = %q{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", "~> 0.8", ">= 0.8.0"
27
- spec.add_runtime_dependency "colored", "~> 1.2"
28
- spec.add_runtime_dependency "os", "~> 0.9.6"
29
- end
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require '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 = %q{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", "~> 0.8", ">= 0.8.0"
27
+ spec.add_runtime_dependency "colored", "~> 1.2"
28
+ spec.add_runtime_dependency "os", "~> 0.9.6"
29
+ end
data/lib/commands.rb CHANGED
@@ -1,13 +1,14 @@
1
- require_relative "multirepo/commands/command"
2
- require_relative "multirepo/commands/add-command"
3
- require_relative "multirepo/commands/branch-command"
4
- require_relative "multirepo/commands/checkout-command"
5
- require_relative "multirepo/commands/clean-command"
6
- require_relative "multirepo/commands/clone-command"
7
- require_relative "multirepo/commands/fetch-command"
8
- require_relative "multirepo/commands/init-command"
9
- require_relative "multirepo/commands/install-command"
10
- require_relative "multirepo/commands/open-command"
11
- require_relative "multirepo/commands/remove-command"
12
- require_relative "multirepo/commands/uninit-command"
1
+ require_relative "multirepo/commands/command"
2
+ require_relative "multirepo/commands/add-command"
3
+ require_relative "multirepo/commands/branch-command"
4
+ require_relative "multirepo/commands/checkout-command"
5
+ require_relative "multirepo/commands/clean-command"
6
+ require_relative "multirepo/commands/clone-command"
7
+ require_relative "multirepo/commands/fetch-command"
8
+ require_relative "multirepo/commands/init-command"
9
+ require_relative "multirepo/commands/install-command"
10
+ require_relative "multirepo/commands/merge-command"
11
+ require_relative "multirepo/commands/open-command"
12
+ require_relative "multirepo/commands/remove-command"
13
+ require_relative "multirepo/commands/uninit-command"
13
14
  require_relative "multirepo/commands/update-command"
data/lib/git-multirepo.rb CHANGED
@@ -1,2 +1,2 @@
1
- require "multirepo/hooks/pre-commit-hook.rb"
1
+ require "multirepo/hooks/pre-commit-hook.rb"
2
2
  require "multirepo/hooks/post-commit-hook.rb"
data/lib/info.rb CHANGED
@@ -1,5 +1,5 @@
1
- module MultiRepo
2
- NAME = "git-multirepo"
3
- VERSION = "1.0.0.beta34"
4
- DESCRIPTION = "Track multiple Git repositories side-by-side."
1
+ module MultiRepo
2
+ NAME = "git-multirepo"
3
+ VERSION = "1.0.0.beta37"
4
+ DESCRIPTION = "Track multiple Git repositories side-by-side."
5
5
  end
@@ -1,52 +1,54 @@
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 = 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
- super
25
- ensure_in_work_tree
26
- ensure_multirepo_enabled
27
- ensure_repo_valid
28
-
29
- entry = ConfigEntry.new(Repo.new(@path))
30
- if ConfigFile.entry_exists?(entry)
31
- Console.log_info("There is already an entry for '#{@path}' in the .multirepo file")
32
- else
33
- ConfigFile.add_entry(entry)
34
- Console.log_step("Added '#{@path}' to the .multirepo file")
35
- end
36
- rescue MultiRepoException => e
37
- Console.log_error(e.message)
38
- end
39
-
40
- def ensure_repo_valid
41
- raise MultiRepoException, "The provided path is not a direct sibling of the main repository" unless validate_is_sibling_repo(@path)
42
- raise MultiRepoException, "There is no folder at path '#{@path}'" unless Dir.exists?(@path)
43
- raise MultiRepoException, "'#{@path}' is not a repository" unless Repo.new(@path).exists?
44
- end
45
-
46
- def validate_is_sibling_repo(path)
47
- parent_dir = File.expand_path("..")
48
- path = File.expand_path("..", path)
49
- return parent_dir == path
50
- end
51
- 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 = 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
+ super
25
+ ensure_in_work_tree
26
+ ensure_multirepo_enabled
27
+ ensure_repo_valid
28
+
29
+ config_file = ConfigFile.new(".")
30
+ entry = ConfigEntry.new(Repo.new(@path))
31
+
32
+ if config_file.entry_exists?(entry)
33
+ Console.log_info("There is already an entry for '#{@path}' in the .multirepo file")
34
+ else
35
+ config_file.add_entry(entry)
36
+ Console.log_step("Added '#{@path}' to the .multirepo file")
37
+ end
38
+ rescue MultiRepoException => e
39
+ Console.log_error(e.message)
40
+ end
41
+
42
+ def ensure_repo_valid
43
+ raise MultiRepoException, "The provided path is not a direct sibling of the main repository" unless validate_is_sibling_repo(@path)
44
+ raise MultiRepoException, "There is no folder at path '#{@path}'" unless Dir.exists?(@path)
45
+ raise MultiRepoException, "'#{@path}' is not a repository" unless Repo.new(@path).exists?
46
+ end
47
+
48
+ def validate_is_sibling_repo(path)
49
+ parent_dir = File.expand_path("..")
50
+ path = File.expand_path("..", path)
51
+ return parent_dir == path
52
+ end
53
+ end
52
54
  end
@@ -1,61 +1,66 @@
1
- require "multirepo/utility/console"
2
- require "multirepo/files/config-file"
3
- require "multirepo/files/tracking-files"
4
-
5
- module MultiRepo
6
- class BranchCommand < Command
7
- self.command = "branch"
8
- self.summary = "Create and/or checkout a new branch for all repos."
9
-
10
- def self.options
11
- [
12
- ['<branch name>', 'The name of the branch to create and checkout.'],
13
- ['[--force]', 'Force creating the branch even if the repos contain uncommmitted changes.'],
14
- ['[--no-track]', 'Do not configure as a remote-tracking branch on creation.']
15
- ].concat(super)
16
- end
17
-
18
- def initialize(argv)
19
- @branch_name = argv.shift_argument
20
- @force = argv.flag?("force")
21
- @remote_tracking = argv.flag?("track", true)
22
- super
23
- end
24
-
25
- def validate!
26
- super
27
- help! "You must specify a branch name" unless @branch_name
28
- end
29
-
30
- def run
31
- super
32
- ensure_in_work_tree
33
- ensure_multirepo_enabled
34
-
35
- Console.log_step("Branching...")
36
-
37
- main_repo = Repo.new(".")
38
- repos = ConfigFile.load_entries.map{ |entry| entry.repo }.push(main_repo)
39
-
40
- if !Utils.ensure_working_copies_clean(repos) && !@force
41
- raise MultiRepoException, "Can't branch because not all repos are clean"
42
- end
43
-
44
- repos.each do |repo|
45
- Console.log_substep("Branching and checking out #{repo.path} #{@branch_name} ...")
46
-
47
- branch = repo.branch(@branch_name)
48
- branch.create(@remote_tracking) unless branch.exists?
49
- branch.checkout
50
- end
51
-
52
- Console.log_substep("Updating and committing tracking files")
53
- TrackingFiles.update
54
- TrackingFiles.commit("[multirepo] Post-branch tracking files update")
55
-
56
- Console.log_step("Done!")
57
- rescue MultiRepoException => e
58
- Console.log_error(e.message)
59
- end
60
- end
1
+ require "multirepo/utility/console"
2
+ require "multirepo/files/config-file"
3
+ require "multirepo/files/tracking-files"
4
+
5
+ module MultiRepo
6
+ class BranchCommand < Command
7
+ self.command = "branch"
8
+ self.summary = "Create and/or checkout a new branch for all repos."
9
+
10
+ def self.options
11
+ [
12
+ ['<branch name>', 'The name of the branch to create and checkout.'],
13
+ ['[--force]', 'Force creating the branch even if the repos contain uncommmitted changes.'],
14
+ ['[--no-track]', 'Do not configure as a remote-tracking branch on creation.']
15
+ ].concat(super)
16
+ end
17
+
18
+ def initialize(argv)
19
+ @branch_name = argv.shift_argument
20
+ @force = argv.flag?("force")
21
+ @remote_tracking = argv.flag?("track", true)
22
+ super
23
+ end
24
+
25
+ def validate!
26
+ super
27
+ help! "You must specify a branch name" unless @branch_name
28
+ end
29
+
30
+ def run
31
+ super
32
+ ensure_in_work_tree
33
+ ensure_multirepo_enabled
34
+
35
+ Console.log_step("Branching...")
36
+
37
+ main_repo = Repo.new(".")
38
+ config_file = ConfigFile.new(".")
39
+ repos = config_file.load_entries.map{ |entry| entry.repo }.push(main_repo)
40
+
41
+ if !Utils.ensure_working_copies_clean(repos) && !@force
42
+ raise MultiRepoException, "Can't branch because not all repos are clean"
43
+ end
44
+
45
+ repos.each do |repo|
46
+ if @remote_tracking
47
+ Console.log_substep("Branching, checking out and pushing '#{repo.path}' #{@branch_name} ...")
48
+ else
49
+ Console.log_substep("Branching and checking out '#{repo.path}' #{@branch_name} (not pushed) ...")
50
+ end
51
+
52
+ branch = repo.branch(@branch_name)
53
+ branch.create(@remote_tracking) unless branch.exists?
54
+ branch.checkout
55
+ end
56
+
57
+ Console.log_substep("Updating and committing tracking files")
58
+ TrackingFiles.update
59
+ TrackingFiles.commit("[multirepo] Post-branch tracking files update")
60
+
61
+ Console.log_step("Done!")
62
+ rescue MultiRepoException => e
63
+ Console.log_error(e.message)
64
+ end
65
+ end
61
66
  end
@@ -1,141 +1,120 @@
1
- require "multirepo/utility/console"
2
-
3
- module MultiRepo
4
- class CheckoutCommand < Command
5
- self.command = "checkout"
6
- self.summary = "Checks out the specified commit or branch of the main repo and checks out matching versions of all dependencies."
7
-
8
- class CheckoutMode
9
- AS_LOCK = 0
10
- LATEST = 1
11
- EXACT = 2
12
- end
13
-
14
- def self.options
15
- [
16
- ['<ref>', 'The main repo tag, branch or commit id to checkout.'],
17
- ['[--latest]', 'Checkout the HEAD of each dependency branch (as recorded in the lock file) instead of the exact required commits.'],
18
- ['[--exact]', 'Checkout the exact specified ref for each repo, regardless of what\'s stored in the lock file.']
19
- ].concat(super)
20
- end
21
-
22
- def initialize(argv)
23
- @ref = argv.shift_argument
24
- @checkout_latest = argv.flag?("latest")
25
- @checkout_exact = argv.flag?("exact")
26
- super
27
- end
28
-
29
- def validate!
30
- super
31
- help! "You must specify a branch or commit id to checkout" unless @ref
32
- help! "You can't provide more than one operation modifier (--latest, --exact, etc.)" if @checkout_latest && @checkout_exact
33
- end
34
-
35
- def run
36
- super
37
- ensure_in_work_tree
38
- ensure_multirepo_enabled
39
-
40
- Console.log_step("Checking out #{@ref} and its dependencies...")
41
-
42
- # Find out the checkout mode based on command-line options
43
- mode = if @checkout_latest then
44
- CheckoutMode::LATEST
45
- elsif @checkout_exact then
46
- CheckoutMode::EXACT
47
- else
48
- CheckoutMode::AS_LOCK
49
- end
50
-
51
- main_repo = Repo.new(".")
52
- initial_revision = main_repo.current_branch || main_repo.head_hash
53
-
54
- unless proceed_if_merge_commit?(main_repo, @ref, mode)
55
- raise MultiRepoException, "Aborting checkout"
56
- end
57
-
58
- main_repo_checkout_step(main_repo, initial_revision, @ref)
59
- ensure_dependencies_clean_step(main_repo, initial_revision)
60
- dependencies_checkout_step(mode, @ref)
61
-
62
- Console.log_step("Done!")
63
- rescue MultiRepoException => e
64
- Console.log_error(e.message)
65
- end
66
-
67
- def main_repo_checkout_step(main_repo, initial_revision, ref)
68
- # Make sure the main repo is clean before attempting a checkout
69
- unless main_repo.is_clean?
70
- raise MultiRepoException, "Can't checkout #{ref} because the main repo contains uncommitted changes"
71
- end
72
-
73
- # Checkout the specified ref
74
- unless main_repo.checkout(ref)
75
- raise MultiRepoException, "Couldn't perform checkout of main repo #{ref}!"
76
- end
77
-
78
- Console.log_substep("Checked out main repo #{ref}")
79
-
80
- # After checkout, make sure we're working with a multirepo-enabled ref
81
- unless Utils.is_multirepo_tracked(".")
82
- main_repo.checkout(initial_revision)
83
- raise MultiRepoException, "This revision is not tracked by multirepo. Checkout reverted."
84
- end
85
- end
86
-
87
- def ensure_dependencies_clean_step(main_repo, initial_revision)
88
- unless Utils.ensure_dependencies_clean(ConfigFile.load_entries)
89
- main_repo.checkout(initial_revision)
90
- raise MultiRepoException, "Checkout reverted."
91
- end
92
- end
93
-
94
- def dependencies_checkout_step(mode, ref = nil)
95
- config_entries = ConfigFile.load_entries # Post-main-repo checkout config entries might be different than pre-checkout
96
- LockFile.load_entries.each { |lock_entry| perform_dependency_checkout(config_entries, lock_entry, ref, mode) }
97
- end
98
-
99
- def proceed_if_merge_commit?(main_repo, ref, mode)
100
- return true unless main_repo.commit(ref).is_merge?
101
-
102
- case mode
103
- when CheckoutMode::AS_LOCK
104
- Console.log_error("The specified ref is a merge commit and an \"as-lock\" checkout was requested.")
105
- Console.log_error("The resulting checkout would most probably not result in a valid project state.")
106
- return false
107
- when CheckoutMode::LATEST
108
- Console.log_warning("The specified ref is a merge commit and a \"latest\" checkout was requested.")
109
- Console.log_warning("The work branches recorded in the branch from which the merge was performed will be checked out.")
110
- end
111
-
112
- return true
113
- end
114
-
115
- def perform_dependency_checkout(config_entries, lock_entry, ref, mode)
116
- # Find the config entry that matches the given lock entry
117
- config_entry = config_entries.select{ |config_entry| config_entry.id == lock_entry.id }.first
118
-
119
- # Make sure the repo exists on disk, and clone it if it doesn't
120
- # (in case the checked-out revision had an additional dependency)
121
- unless config_entry.repo.exists?
122
- Console.log_substep("Cloning missing dependency '#{config_entry.path}' from #{config_entry.url}")
123
- config_entry.repo.clone(config_entry.url)
124
- end
125
-
126
- # Find out the proper revision to checkout based on the checkout mode
127
- revision = case mode
128
- when CheckoutMode::AS_LOCK; lock_entry.head
129
- when CheckoutMode::LATEST; lock_entry.branch
130
- when CheckoutMode::EXACT; ref
131
- end
132
-
133
- # Checkout!
134
- if config_entry.repo.checkout(revision)
135
- Console.log_substep("Checked out #{lock_entry.name} #{revision}")
136
- else
137
- raise MultiRepoException, "Couldn't check out the appropriate version of dependency #{lock_entry.name}"
138
- end
139
- end
140
- end
1
+ require "multirepo/utility/console"
2
+ require "multirepo/logic/revision-selector"
3
+ require "multirepo/logic/performer"
4
+
5
+ module MultiRepo
6
+ class CheckoutCommand < Command
7
+ self.command = "checkout"
8
+ self.summary = "Checks out the specified commit or branch of the main repo and checks out matching versions of all dependencies."
9
+
10
+ def self.options
11
+ [
12
+ ['<ref>', 'The main repo tag, branch or commit id to checkout.'],
13
+ ['[--latest]', 'Checkout the HEAD of each dependency branch (as recorded in the lock file) instead of the exact required commits.'],
14
+ ['[--exact]', 'Checkout the exact specified ref for each repo, regardless of what\'s stored in the lock file.']
15
+ ].concat(super)
16
+ end
17
+
18
+ def initialize(argv)
19
+ @ref = argv.shift_argument
20
+ @checkout_latest = argv.flag?("latest")
21
+ @checkout_exact = argv.flag?("exact")
22
+ super
23
+ end
24
+
25
+ def validate!
26
+ super
27
+ help! "You must specify a branch or commit id to checkout" unless @ref
28
+ help! "You can't provide more than one operation modifier (--latest, --exact, etc.)" if @checkout_latest && @checkout_exact
29
+ end
30
+
31
+ def run
32
+ super
33
+ ensure_in_work_tree
34
+
35
+ Console.log_step("Checking out #{@ref} and its dependencies...")
36
+
37
+ # Find out the checkout mode based on command-line options
38
+ mode = RevisionSelector.mode_for_args(@checkout_latest, @checkout_exact)
39
+
40
+ main_repo = Repo.new(".")
41
+
42
+ unless proceed_if_merge_commit?(main_repo, @ref, mode)
43
+ raise MultiRepoException, "Aborting checkout"
44
+ end
45
+
46
+ checkout_core(main_repo, mode)
47
+
48
+ Console.log_step("Done!")
49
+ rescue MultiRepoException => e
50
+ Console.log_error(e.message)
51
+ end
52
+
53
+ def checkout_core(main_repo, mode)
54
+ initial_revision = main_repo.current_branch || main_repo.head_hash
55
+ begin
56
+ # Checkout first because the current ref might not be multirepo-enabled
57
+ checkout_main_repo_step(main_repo)
58
+ # Only then can we check for dependencies and make sure they are clean
59
+ ensure_dependencies_clean_step(main_repo)
60
+ rescue MultiRepoException => e
61
+ Console.log_error("Reverting main repo checkout")
62
+ main_repo.checkout(initial_revision)
63
+ raise e
64
+ end
65
+ dependencies_checkout_step(mode, @ref)
66
+ end
67
+
68
+ def checkout_main_repo_step(main_repo)
69
+ Performer.perform_main_repo_checkout(main_repo, @ref)
70
+ end
71
+
72
+ def ensure_dependencies_clean_step(main_repo)
73
+ unless Utils.ensure_dependencies_clean(ConfigFile.new(".").load_entries)
74
+ raise MultiRepoException, "Dependencies are not clean!"
75
+ end
76
+ end
77
+
78
+ def dependencies_checkout_step(mode, ref = nil)
79
+ Performer.perform_on_dependencies do |config_entry, lock_entry|
80
+ # Find out the required dependency revision based on the checkout mode
81
+ revision = RevisionSelector.revision_for_mode(mode, ref, lock_entry)
82
+ perform_dependency_checkout(config_entry, revision)
83
+ end
84
+ end
85
+
86
+ def proceed_if_merge_commit?(main_repo, ref, mode)
87
+ return true unless main_repo.commit(ref).is_merge?
88
+
89
+ case mode
90
+ when RevisionSelectionMode::AS_LOCK
91
+ Console.log_error("The specified ref is a merge commit and an \"as-lock\" checkout was requested.")
92
+ Console.log_error("The resulting checkout would most probably not result in a valid project state.")
93
+ return false
94
+ when RevisionSelectionMode::LATEST
95
+ Console.log_warning("The specified ref is a merge commit and a \"latest\" checkout was requested.")
96
+ Console.log_warning("The work branches recorded in the branch from which the merge was performed will be checked out.")
97
+ end
98
+
99
+ return true
100
+ end
101
+
102
+ def perform_dependency_checkout(config_entry, revision)
103
+ dependency_name = config_entry.repo.basename
104
+
105
+ # Make sure the repo exists on disk, and clone it if it doesn't
106
+ # (in case the checked-out revision had an additional dependency)
107
+ unless config_entry.repo.exists?
108
+ Console.log_substep("Cloning missing dependency '#{config_entry.path}' from #{config_entry.url}")
109
+ config_entry.repo.clone(config_entry.url)
110
+ end
111
+
112
+ # Checkout!
113
+ if config_entry.repo.checkout(revision)
114
+ Console.log_substep("Checked out #{dependency_name} #{revision}")
115
+ else
116
+ raise MultiRepoException, "Couldn't check out the appropriate version of dependency #{dependency_name}"
117
+ end
118
+ end
119
+ end
141
120
  end