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
@@ -1,23 +1,23 @@
1
- require "multirepo/files/config-file"
2
- require "multirepo/files/tracking-files"
3
- require "multirepo/utility/utils"
4
- require "multirepo/utility/console"
5
-
6
- module MultiRepo
7
- class PostCommitHook
8
- def self.run
9
- Config.instance.running_git_hook = true
10
-
11
- Console.log_step("Performing post-commit operations...")
12
-
13
- # Works around bug #91565510 (https://www.pivotaltracker.com/story/show/91565510)
14
- TrackingFiles.stage
15
- Console.log_info("Cleaned-up staging area")
16
-
17
- exit 0 # Success!
18
- rescue StandardError => e
19
- Console.log_error("Post-commit hook failed to execute! #{e.message}")
20
- exit 1 # Something went wrong!
21
- end
22
- end
1
+ require "multirepo/files/config-file"
2
+ require "multirepo/files/tracking-files"
3
+ require "multirepo/utility/utils"
4
+ require "multirepo/utility/console"
5
+
6
+ module MultiRepo
7
+ class PostCommitHook
8
+ def self.run
9
+ Config.instance.running_git_hook = true
10
+
11
+ Console.log_step("Performing post-commit operations...")
12
+
13
+ # Works around bug #91565510 (https://www.pivotaltracker.com/story/show/91565510)
14
+ TrackingFiles.stage
15
+ Console.log_info("Cleaned-up staging area")
16
+
17
+ exit 0 # Success!
18
+ rescue StandardError => e
19
+ Console.log_error("Post-commit hook failed to execute! #{e.message}")
20
+ exit 1 # Something went wrong!
21
+ end
22
+ end
23
23
  end
@@ -1,30 +1,30 @@
1
- require "multirepo/files/config-file"
2
- require "multirepo/files/tracking-files"
3
- require "multirepo/utility/utils"
4
- require "multirepo/utility/console"
5
-
6
- module MultiRepo
7
- class PreCommitHook
8
- def self.run
9
- Config.instance.running_git_hook = true
10
-
11
- Console.log_step("Performing pre-commit operations...")
12
-
13
- dependencies_clean = Utils.ensure_dependencies_clean(ConfigFile.load_entries)
14
-
15
- if !dependencies_clean
16
- Console.log_error("You must commit changes to your dependencies before you can commit this repo")
17
- exit 1
18
- end
19
-
20
- TrackingFiles.update
21
- TrackingFiles.stage
22
- Console.log_info("Updated and staged tracking files")
23
-
24
- exit 0 # Success!
25
- rescue StandardError => e
26
- Console.log_error("Pre-commit hook failed to execute! #{e.message}")
27
- exit 1 # Something went wrong!
28
- end
29
- end
1
+ require "multirepo/files/config-file"
2
+ require "multirepo/files/tracking-files"
3
+ require "multirepo/utility/utils"
4
+ require "multirepo/utility/console"
5
+
6
+ module MultiRepo
7
+ class PreCommitHook
8
+ def self.run
9
+ Config.instance.running_git_hook = true
10
+
11
+ Console.log_step("Performing pre-commit operations...")
12
+
13
+ dependencies_clean = Utils.ensure_dependencies_clean(ConfigFile.new(".").load_entries)
14
+
15
+ if !dependencies_clean
16
+ Console.log_error("You must commit changes to your dependencies before you can commit this repo")
17
+ exit 1
18
+ end
19
+
20
+ TrackingFiles.update
21
+ TrackingFiles.stage
22
+ Console.log_info("Updated and staged tracking files")
23
+
24
+ exit 0 # Success!
25
+ rescue StandardError => e
26
+ Console.log_error("Pre-commit hook failed to execute! #{e.message}")
27
+ exit 1 # Something went wrong!
28
+ end
29
+ end
30
30
  end
@@ -0,0 +1,41 @@
1
+ require "multirepo/files/config-file"
2
+
3
+ module MultiRepo
4
+ class Node
5
+ attr_accessor :path
6
+ attr_accessor :depth
7
+
8
+ def initialize(path, depth = 0)
9
+ @path = path
10
+ @depth = depth
11
+ end
12
+
13
+ def children
14
+ return [] unless Utils.is_multirepo_enabled(@path)
15
+ config_entries = ConfigFile.new(@path).load_entries
16
+ return config_entries.map { |e| Node.new(e.path, @depth + 1) }
17
+ end
18
+
19
+ def ordered_descendants_including_self
20
+ return ordered_descendants.push(self)
21
+ end
22
+
23
+ def ordered_descendants
24
+ descendants = find_descendants_recursive(self)
25
+
26
+ unique_paths = descendants.map{ |d| d.path }.uniq
27
+ unique_nodes = unique_paths.collect do |path|
28
+ nodes_for_path = descendants.select { |d| d.path == path }
29
+ next nodes_for_path.sort{ |n| n.depth }.first
30
+ end
31
+
32
+ return unique_nodes.sort_by{ |d| d.depth }.reverse
33
+ end
34
+
35
+ def find_descendants_recursive(node)
36
+ descendants = node.children
37
+ descendants.each { |d| descendants.push(*find_descendants_recursive(d)) }
38
+ return descendants
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,59 @@
1
+ require "ostruct"
2
+
3
+ require "multirepo/files/config-file"
4
+ require "multirepo/files/lock-file"
5
+
6
+ module MultiRepo
7
+ class Performer
8
+ def self.perform_main_repo_checkout(main_repo, ref)
9
+ # Make sure the main repo is clean before attempting a checkout
10
+ unless main_repo.clean?
11
+ raise MultiRepoException, "Can't checkout #{ref} because the main repo contains uncommitted changes"
12
+ end
13
+
14
+ # Checkout the specified ref
15
+ unless main_repo.checkout(ref)
16
+ raise MultiRepoException, "Couldn't perform checkout of main repo #{ref}!"
17
+ end
18
+
19
+ Console.log_substep("Checked out main repo #{ref}")
20
+
21
+ # After checkout, make sure we're working with a multirepo-enabled ref
22
+ unless Utils.is_multirepo_tracked(".")
23
+ raise MultiRepoException, "Revision #{ref} is not tracked by multirepo!"
24
+ end
25
+ end
26
+
27
+ def self.perform_on_dependencies(&operation)
28
+ config_entries = ConfigFile.new(".").load_entries
29
+ lock_entries = LockFile.new(".").load_entries
30
+
31
+ config_lock_pairs = build_config_lock_pairs(config_entries, lock_entries)
32
+ dependency_ordered_nodes = Node.new(".").ordered_descendants
33
+
34
+ ordered_pairs = dependency_ordered_nodes.map do |node|
35
+ pair = config_lock_pairs.find { |pair| pair.config_entry.path == node.path }
36
+ end
37
+
38
+ ordered_pairs.each { |pair| operation.call(pair.config_entry, pair.lock_entry) }
39
+ end
40
+
41
+ private
42
+
43
+ def self.build_config_lock_pairs(config_entries, lock_entries)
44
+ lock_entries.map do |lock_entry|
45
+ config_entry = config_entry_for_lock_entry(config_entries, lock_entry)
46
+
47
+ pair = OpenStruct.new
48
+ pair.config_entry = config_entry
49
+ pair.lock_entry = lock_entry
50
+
51
+ next pair
52
+ end
53
+ end
54
+
55
+ def self.config_entry_for_lock_entry(config_entries, lock_entry)
56
+ config_entries.find { |config_entry| config_entry.id == lock_entry.id }
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,27 @@
1
+ module MultiRepo
2
+ class RevisionSelectionMode
3
+ AS_LOCK = 0
4
+ LATEST = 1
5
+ EXACT = 2
6
+ end
7
+
8
+ class RevisionSelector
9
+ def self.mode_for_args(checkout_latest, checkout_exact)
10
+ if checkout_latest then
11
+ RevisionSelectionMode::LATEST
12
+ elsif checkout_exact then
13
+ RevisionSelectionMode::EXACT
14
+ else
15
+ RevisionSelectionMode::AS_LOCK
16
+ end
17
+ end
18
+
19
+ def self.revision_for_mode(mode, ref, lock_entry)
20
+ case mode
21
+ when RevisionSelectionMode::AS_LOCK; lock_entry.head
22
+ when RevisionSelectionMode::LATEST; lock_entry.branch
23
+ when RevisionSelectionMode::EXACT; ref
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,6 +1,6 @@
1
- require "claide/informative_error"
2
-
3
- module MultiRepo
4
- class MultiRepoException < StandardError
5
- end
1
+ require "claide/informative_error"
2
+
3
+ module MultiRepo
4
+ class MultiRepoException < StandardError
5
+ end
6
6
  end
@@ -1,52 +1,52 @@
1
- require "colored"
2
-
3
- module MultiRepo
4
- class Console
5
- def self.log_step(message)
6
- print_prefix
7
- puts $stdout.isatty ? message.bold.green : message
8
- end
9
-
10
- def self.log_substep(message)
11
- print_prefix
12
- puts $stdout.isatty ? message.blue : message
13
- end
14
-
15
- def self.log_info(message)
16
- print_prefix
17
- puts $stdout.isatty ? message.white : message
18
- end
19
-
20
- def self.log_warning(message)
21
- print_prefix
22
- puts $stdout.isatty ? message.yellow : message
23
- end
24
-
25
- def self.log_error(message)
26
- print_prefix
27
- puts $stdout.isatty ? message.red : message
28
- end
29
-
30
- def self.ask_yes_no(message)
31
- answered = false
32
- while !answered
33
- print_prefix
34
- print message
35
- print " (y/n) "
36
-
37
- case $stdin.gets.strip.downcase
38
- when "y", "yes"
39
- answered = true
40
- return true
41
- when "n", "no"
42
- answered = true
43
- return false
44
- end
45
- end
46
- end
47
-
48
- def self.print_prefix
49
- print $stdout.isatty ? "> ".white : "[multirepo] "
50
- end
51
- end
1
+ require "colored"
2
+
3
+ module MultiRepo
4
+ class Console
5
+ def self.log_step(message)
6
+ print_prefix
7
+ puts $stdout.isatty ? message.bold.green : message
8
+ end
9
+
10
+ def self.log_substep(message)
11
+ print_prefix
12
+ puts $stdout.isatty ? message.blue : message
13
+ end
14
+
15
+ def self.log_info(message)
16
+ print_prefix
17
+ puts $stdout.isatty ? message.white : message
18
+ end
19
+
20
+ def self.log_warning(message)
21
+ print_prefix
22
+ puts $stdout.isatty ? message.yellow : message
23
+ end
24
+
25
+ def self.log_error(message)
26
+ print_prefix
27
+ puts $stdout.isatty ? message.red : message
28
+ end
29
+
30
+ def self.ask_yes_no(message)
31
+ answered = false
32
+ while !answered
33
+ print_prefix
34
+ print message
35
+ print " (y/n) "
36
+
37
+ case $stdin.gets.strip.downcase
38
+ when "y", "yes"
39
+ answered = true
40
+ return true
41
+ when "n", "no"
42
+ answered = true
43
+ return false
44
+ end
45
+ end
46
+ end
47
+
48
+ def self.print_prefix
49
+ print $stdout.isatty ? "> ".white : "[multirepo] "
50
+ end
51
+ end
52
52
  end
@@ -1,35 +1,35 @@
1
- require "open3"
2
- require "multirepo/utility/console"
3
-
4
- module MultiRepo
5
- class Runner
6
- class Verbosity
7
- NEVER_OUTPUT = 0
8
- ALWAYS_OUTPUT = 1
9
- OUTPUT_ON_ERROR = 2
10
- end
11
-
12
- class << self
13
- attr_accessor :last_command_succeeded
14
- end
15
-
16
- def self.run(cmd, verbosity)
17
- Console.log_info("Command: #{cmd}") if Config.instance.verbose
18
-
19
- lines = []
20
- Open3.popen2e(cmd) do |stdin, stdout_and_stderr, thread|
21
- stdout_and_stderr.each do |line|
22
- Console.log_info("-------> #{line.rstrip}") if verbosity == Verbosity::ALWAYS_OUTPUT || Config.instance.verbose
23
- lines << line
24
- end
25
- @last_command_succeeded = thread.value.success?
26
- end
27
-
28
- output = lines.join("").rstrip
29
-
30
- Console.log_error(output) if !@last_command_succeeded && verbosity == Verbosity::OUTPUT_ON_ERROR
31
-
32
- return output
33
- end
34
- end
1
+ require "open3"
2
+ require "multirepo/utility/console"
3
+
4
+ module MultiRepo
5
+ class Runner
6
+ class Verbosity
7
+ OUTPUT_NEVER = 0
8
+ OUTPUT_ALWAYS = 1
9
+ OUTPUT_ON_ERROR = 2
10
+ end
11
+
12
+ class << self
13
+ attr_accessor :last_command_succeeded
14
+ end
15
+
16
+ def self.run(cmd, verbosity)
17
+ Console.log_info("Command: #{cmd}") if Config.instance.verbose
18
+
19
+ lines = []
20
+ Open3.popen2e(cmd) do |stdin, stdout_and_stderr, thread|
21
+ stdout_and_stderr.each do |line|
22
+ Console.log_info("#{line.rstrip}") if verbosity == Verbosity::OUTPUT_ALWAYS || Config.instance.verbose
23
+ lines << line
24
+ end
25
+ @last_command_succeeded = thread.value.success?
26
+ end
27
+
28
+ output = lines.join("").rstrip
29
+
30
+ Console.log_error(output) if !@last_command_succeeded && verbosity == Verbosity::OUTPUT_ON_ERROR
31
+
32
+ return output
33
+ end
34
+ end
35
35
  end
@@ -1,67 +1,66 @@
1
- require "fileutils"
2
-
3
- module MultiRepo
4
- class Utils
5
- def self.path_for_resource(resource_name)
6
- gem_path = Gem::Specification.find_by_name("git-multirepo").gem_dir
7
- File.join(gem_path, "resources/#{resource_name}")
8
- end
9
-
10
- def self.is_multirepo_enabled(path)
11
- File.exists?(File.join(path, ".multirepo"))
12
- end
13
-
14
- def self.is_multirepo_tracked(path)
15
- is_multirepo_enabled(path) && File.exists?(File.join(path, ".multirepo.lock"))
16
- end
17
-
18
- def self.install_hook(name, path)
19
- destination_path = File.join(path, ".git/hooks")
20
- destination_file = File.join(destination_path, name)
21
- FileUtils.cp(path_for_resource(name), destination_file)
22
- FileUtils.chmod(0755, destination_file) # -rwxr-xr-x
23
- end
24
-
25
- def self.sibling_repos
26
- sibling_directories = Dir['../*/']
27
- sibling_repos = sibling_directories.map{ |d| Repo.new(d) }.select{ |r| r.exists? }
28
- sibling_repos.delete_if{ |r| Pathname.new(r.path).realpath == Pathname.new(".").realpath }
29
- end
30
-
31
- def self.ensure_dependencies_clean(config_entries)
32
- clean = true
33
- config_entries.each do |e|
34
- next unless e.repo.exists?
35
- dependency_clean = e.repo.is_clean?
36
- clean &= dependency_clean
37
- Console.log_info("Dependency '#{e.repo.path}' is clean") if dependency_clean
38
- Console.log_warning("Dependency '#{e.repo.path}' contains uncommitted changes") unless dependency_clean
39
- end
40
- return clean
41
- end
42
-
43
- def self.ensure_working_copies_clean(repos)
44
- clean = true
45
- repos.each do |repo|
46
- dependency_clean = repo.is_clean?
47
- clean &= dependency_clean
48
- Console.log_warning("Repo '#{repo.path}' contains uncommitted changes") unless dependency_clean
49
- end
50
- return clean
51
- end
52
-
53
- def self.convert_to_windows_path(unix_path)
54
- components = Pathname.new(unix_path).each_filename.to_a
55
- components.join(File::ALT_SEPARATOR)
56
- end
57
-
58
- def self.append_if_missing(path, pattern, string_to_append)
59
- unless File.exists?(path)
60
- File.open(path, 'w') { |f| f.puts(string_to_append) }
61
- else
62
- string_located = File.readlines(path).grep(pattern).any?
63
- File.open(path, 'a') { |f| f.puts(string_to_append) } unless string_located
64
- end
65
- end
66
- end
1
+ require "fileutils"
2
+
3
+ module MultiRepo
4
+ class Utils
5
+ def self.path_for_resource(resource_name)
6
+ gem_path = Gem::Specification.find_by_name("git-multirepo").gem_dir
7
+ File.join(gem_path, "resources/#{resource_name}")
8
+ end
9
+
10
+ def self.is_multirepo_enabled(path)
11
+ File.exists?(File.join(path, ".multirepo"))
12
+ end
13
+
14
+ def self.is_multirepo_tracked(path)
15
+ is_multirepo_enabled(path) && File.exists?(File.join(path, ".multirepo.lock"))
16
+ end
17
+
18
+ def self.install_hook(name, path)
19
+ destination_path = File.join(path, ".git/hooks")
20
+ destination_file = File.join(destination_path, name)
21
+ FileUtils.cp(path_for_resource(name), destination_file)
22
+ FileUtils.chmod(0755, destination_file) # -rwxr-xr-x
23
+ end
24
+
25
+ def self.sibling_repos
26
+ sibling_directories = Dir['../*/']
27
+ sibling_repos = sibling_directories.map{ |d| Repo.new(d) }.select{ |r| r.exists? }
28
+ sibling_repos.delete_if{ |r| Pathname.new(r.path).realpath == Pathname.new(".").realpath }
29
+ end
30
+
31
+ def self.ensure_dependencies_clean(config_entries)
32
+ clean = true
33
+ config_entries.each do |e|
34
+ next unless e.repo.exists?
35
+ dependency_clean = e.repo.clean?
36
+ clean &= dependency_clean
37
+ Console.log_warning("Dependency '#{e.repo.path}' contains uncommitted changes") unless dependency_clean
38
+ end
39
+ return clean
40
+ end
41
+
42
+ def self.ensure_working_copies_clean(repos)
43
+ clean = true
44
+ repos.each do |repo|
45
+ dependency_clean = repo.clean?
46
+ clean &= dependency_clean
47
+ Console.log_warning("Repo '#{repo.path}' contains uncommitted changes") unless dependency_clean
48
+ end
49
+ return clean
50
+ end
51
+
52
+ def self.convert_to_windows_path(unix_path)
53
+ components = Pathname.new(unix_path).each_filename.to_a
54
+ components.join(File::ALT_SEPARATOR)
55
+ end
56
+
57
+ def self.append_if_missing(path, pattern, string_to_append)
58
+ unless File.exists?(path)
59
+ File.open(path, 'w') { |f| f.puts(string_to_append) }
60
+ else
61
+ string_located = File.readlines(path).grep(pattern).any?
62
+ File.open(path, 'a') { |f| f.puts(string_to_append) } unless string_located
63
+ end
64
+ end
65
+ end
67
66
  end