git-multirepo 1.0.0.beta40 → 1.0.0.beta42

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +2 -2
  3. data/.gitbugtraq +3 -3
  4. data/.gitignore +38 -38
  5. data/.multirepo.meta +2 -2
  6. data/.rspec +2 -2
  7. data/Gemfile +4 -4
  8. data/Gemfile.lock +42 -42
  9. data/LICENSE +22 -22
  10. data/README.md +143 -146
  11. data/Rakefile +2 -2
  12. data/bin/multi +10 -10
  13. data/docs/bug-repros/91565510-repro.sh +20 -20
  14. data/docs/git-multirepo-cheatsheet.docx +0 -0
  15. data/git-multirepo.gemspec +31 -31
  16. data/lib/commands.rb +13 -15
  17. data/lib/git-multirepo.rb +2 -2
  18. data/lib/info.rb +4 -4
  19. data/lib/multirepo/commands/add-command.rb +50 -53
  20. data/lib/multirepo/commands/branch-command.rb +81 -82
  21. data/lib/multirepo/commands/checkout-command.rb +119 -122
  22. data/lib/multirepo/commands/clone-command.rb +67 -70
  23. data/lib/multirepo/commands/command.rb +89 -75
  24. data/lib/multirepo/commands/do-command.rb +100 -75
  25. data/lib/multirepo/commands/graph-command.rb +42 -45
  26. data/lib/multirepo/commands/init-command.rb +119 -119
  27. data/lib/multirepo/commands/install-command.rb +106 -103
  28. data/lib/multirepo/commands/merge-command.rb +225 -167
  29. data/lib/multirepo/commands/open-command.rb +55 -57
  30. data/lib/multirepo/commands/remove-command.rb +47 -50
  31. data/lib/multirepo/commands/uninit-command.rb +17 -20
  32. data/lib/multirepo/commands/update-command.rb +55 -60
  33. data/lib/multirepo/config.rb +15 -15
  34. data/lib/multirepo/files/config-entry.rb +38 -38
  35. data/lib/multirepo/files/config-file.rb +45 -45
  36. data/lib/multirepo/files/lock-entry.rb +28 -24
  37. data/lib/multirepo/files/lock-file.rb +55 -38
  38. data/lib/multirepo/files/meta-file.rb +40 -40
  39. data/lib/multirepo/files/tracking-file.rb +8 -8
  40. data/lib/multirepo/files/tracking-files.rb +46 -46
  41. data/lib/multirepo/git/branch.rb +31 -30
  42. data/lib/multirepo/git/change.rb +10 -10
  43. data/lib/multirepo/git/commit.rb +6 -17
  44. data/lib/multirepo/git/git-runner.rb +46 -46
  45. data/lib/multirepo/git/git.rb +10 -0
  46. data/lib/multirepo/git/ref.rb +38 -0
  47. data/lib/multirepo/git/remote.rb +16 -16
  48. data/lib/multirepo/git/repo.rb +122 -77
  49. data/lib/multirepo/hooks/post-commit-hook.rb +22 -22
  50. data/lib/multirepo/hooks/pre-commit-hook.rb +34 -31
  51. data/lib/multirepo/logic/dependency.rb +6 -0
  52. data/lib/multirepo/logic/merge-descriptor.rb +94 -12
  53. data/lib/multirepo/logic/node.rb +71 -44
  54. data/lib/multirepo/logic/performer.rb +56 -62
  55. data/lib/multirepo/logic/revision-selector.rb +34 -34
  56. data/lib/multirepo/multirepo-exception.rb +5 -5
  57. data/lib/multirepo/utility/console.rb +51 -51
  58. data/lib/multirepo/utility/runner.rb +34 -34
  59. data/lib/multirepo/utility/utils.rb +94 -81
  60. data/resources/.gitconfig +2 -2
  61. data/resources/post-commit +5 -5
  62. data/resources/pre-commit +5 -5
  63. data/spec/integration/init_spec.rb +18 -18
  64. data/spec/spec_helper.rb +89 -89
  65. metadata +6 -5
  66. data/lib/multirepo/commands/clean-command.rb +0 -32
  67. data/lib/multirepo/commands/fetch-command.rb +0 -31
@@ -1,76 +1,101 @@
1
- require "multirepo/utility/console"
2
- require "multirepo/git/git-runner"
3
- require "multirepo/logic/performer"
4
-
5
- module MultiRepo
6
- class DoCommand < Command
7
- self.command = "do"
8
- self.summary = "Perform an arbitrary Git operation in the main repository, dependency repositories or all repositories."
9
-
10
- def self.options
11
- [
12
- ['"<operation>"', 'The git command to perform, between quotes, omitting the executable name (ex: "reset --hard HEAD")'],
13
- ['[--main]', 'Perform the operation in the main repository only.'],
14
- ['[--deps]', 'Perform the operation in dependencies only.'],
15
- ['[--all]', 'Perform the operation in the main repository and all dependencies.']
16
- ].concat(super)
17
- end
18
-
19
- def initialize(argv)
20
- @operation = argv.shift_argument
21
- @main_only = argv.flag?("main")
22
- @deps_only = argv.flag?("deps")
23
- argv.flag?("all") # Eat the default flag!
24
- super
25
- end
26
-
27
- def validate!
28
- super
29
- unless validate_only_one_flag(@main_only, @deps_only, @all)
30
- help! "You can't provide more than one operation modifier (--deps, --main, etc.)"
31
- end
32
- end
33
-
34
- def run
35
- super
36
- ensure_in_work_tree
37
- ensure_multirepo_enabled
38
-
39
- if @main_only
40
- message = "Perform operation on the #{'main repo'.bold}:\n git #{@operation}"
41
- op = lambda { perform_operation_on_main(@operation) }
42
- elsif @deps_only
43
- message = "Perform operation on #{'dependencies'.bold}:\n git #{@operation}"
44
- op = lambda { perform_operation_on_dependencies(@operation) }
45
- else
46
- message = "Perform operation on #{'dependencies'.bold} and the #{'main repo'.bold}:\n git #{@operation}"
47
- op = lambda {
48
- perform_operation_on_dependencies(@operation) # Ordered dependencies first
49
- perform_operation_on_main(@operation) # Main last
50
- }
51
- end
52
-
53
- raise MultiRepoException, "Operation cancelled" unless Console.ask_yes_no(message)
54
-
55
- op.call
56
- rescue MultiRepoException => e
57
- Console.log_error(e.message)
58
- end
59
-
60
- def perform_operation_on_main(operation)
61
- perform_operation(".", operation)
62
- end
63
-
64
- def perform_operation_on_dependencies(operation)
65
- Performer.perform_on_dependencies do |config_entry, lock_entry|
66
- perform_operation(config_entry.repo.path, operation)
67
- end
68
- end
69
-
70
- def perform_operation(path, operation)
71
- Console.log_step("Performing operation on '#{path}'")
72
- Console.log_info("git #{operation}")
73
- GitRunner.run_in_working_dir(path, operation, Runner::Verbosity::OUTPUT_ALWAYS)
74
- end
75
- end
1
+ require_relative "command"
2
+ require "multirepo/utility/utils"
3
+ require "multirepo/utility/console"
4
+ require "multirepo/files/config-file"
5
+ require "multirepo/git/repo"
6
+ require "multirepo/git/git-runner"
7
+ require "multirepo/logic/performer"
8
+
9
+ module MultiRepo
10
+ class DoCommand < Command
11
+ self.command = "do"
12
+ self.summary = "Perform an arbitrary Git operation in the main repository, dependency repositories or all repositories."
13
+
14
+ def self.options
15
+ [
16
+ ['"<operation>"', 'The git command to perform, between quotes, omitting the executable name (ex: "reset --hard HEAD")'],
17
+ ['[--main]', 'Perform the operation in the main repository only.'],
18
+ ['[--deps]', 'Perform the operation in dependencies only.'],
19
+ ['[--all]', 'Perform the operation in the main repository and all dependencies.']
20
+ ].concat(super)
21
+ end
22
+
23
+ def initialize(argv)
24
+ @operation = argv.shift_argument.sub(/^git /, "")
25
+ @all = argv.flag?("all")
26
+ @main_only = argv.flag?("main")
27
+ @deps_only = argv.flag?("deps")
28
+ super
29
+ end
30
+
31
+ def validate!
32
+ super
33
+ help! "You must provide a git operation to perform" unless @operation
34
+ unless validate_only_one_flag(@all, @main_only, @deps_only)
35
+ help! "You can't provide more than one operation modifier (--deps, --main, etc.)"
36
+ end
37
+ end
38
+
39
+ def run
40
+ ensure_in_work_tree
41
+ ensure_multirepo_enabled
42
+
43
+ success = true
44
+ if @main_only
45
+ confirm_main_repo_operation
46
+ success &= perform_operation_on_main(@operation)
47
+ elsif @deps_only
48
+ confirm_dependencies_operation
49
+ success &= perform_operation_on_dependencies(@operation)
50
+ else
51
+ confirm_main_repo_operation
52
+ confirm_dependencies_operation
53
+ success &= perform_operation_on_dependencies(@operation) # Ordered dependencies first
54
+ success &= perform_operation_on_main(@operation) # Main last
55
+ end
56
+
57
+ Console.log_warning("Some operations finished with non-zero exit status. Please review the above.") unless success
58
+ end
59
+
60
+ def perform_operation_on_main(operation)
61
+ perform_operation(".", operation)
62
+ end
63
+
64
+ def perform_operation_on_dependencies(operation)
65
+ success = true
66
+ Performer.dependencies.each do |dependency|
67
+ success &= perform_operation(dependency.config_entry.repo.path, operation)
68
+ end
69
+ return success
70
+ end
71
+
72
+ def perform_operation(path, operation)
73
+ Console.log_step("Performing operation on '#{path}'")
74
+ GitRunner.run_in_working_dir(path, operation, Runner::Verbosity::OUTPUT_ALWAYS)
75
+ GitRunner.last_command_succeeded
76
+ end
77
+
78
+ def confirm_main_repo_operation
79
+ unless main_repo_clean?
80
+ Console.log_warning("Main repo contains uncommitted changes")
81
+ raise MultiRepoException, "Aborted" unless Console.ask_yes_no("Proceed anyway?")
82
+ end
83
+ end
84
+
85
+ def confirm_dependencies_operation
86
+ unless dependencies_clean?
87
+ Console.log_warning("Some dependencies contain uncommitted changes")
88
+ raise MultiRepoException, "Aborted" unless Console.ask_yes_no("Proceed anyway?")
89
+ end
90
+ end
91
+
92
+ def main_repo_clean?
93
+ Repo.new(".").clean?
94
+ end
95
+
96
+ def dependencies_clean?
97
+ config_entries = ConfigFile.new(".").load_entries
98
+ return Utils.dependencies_clean?(config_entries)
99
+ end
100
+ end
76
101
  end
@@ -1,46 +1,43 @@
1
- require "os"
2
- require "graphviz"
3
-
4
- require "multirepo/utility/utils"
5
- require "multirepo/utility/console"
6
- require "multirepo/logic/node"
7
-
8
- module MultiRepo
9
- class GraphCommand < Command
10
- self.command = "graph"
11
- self.summary = "Graphs the dependency tree from the current repository."
12
-
13
- def run
14
- super
15
- ensure_in_work_tree
16
- ensure_multirepo_enabled
17
-
18
- root = Node.new(".")
19
- graph = GraphViz.new(:G, :type => :digraph)
20
- build_graph_recursive(graph, root)
21
-
22
- path = File.expand_path("~/Desktop/#{root.name}-graph.png")
23
-
24
- begin
25
- graph.output(:png => path)
26
- Utils.open_in_default_app(path)
27
- rescue StandardError => e
28
- Console.log_error(e.message)
29
- raise MultiRepoException, "Could not generate graph image because an error occurred during graph generation"
30
- end
31
-
32
- Console.log_step("Generated graph image #{path}")
33
- rescue MultiRepoException => e
34
- Console.log_error(e.message)
35
- end
36
-
37
- def build_graph_recursive(graph, node)
38
- parent_graph_node = graph.add_nodes(node.name)
39
- node.children.each do |child_node|
40
- child_graph_node = graph.add_node(child_node.name)
41
- graph.add_edge(parent_graph_node, child_graph_node)
42
- build_graph_recursive(graph, child_node)
43
- end
44
- end
45
- end
1
+ require "os"
2
+ require "graphviz"
3
+
4
+ require "multirepo/utility/utils"
5
+ require "multirepo/utility/console"
6
+ require "multirepo/logic/node"
7
+
8
+ module MultiRepo
9
+ class GraphCommand < Command
10
+ self.command = "graph"
11
+ self.summary = "Graphs the dependency tree from the current repository."
12
+
13
+ def run
14
+ ensure_in_work_tree
15
+ ensure_multirepo_enabled
16
+
17
+ root = Node.new(".")
18
+ graph = GraphViz.new(:G, :type => :digraph)
19
+ build_graph_recursive(graph, root)
20
+
21
+ path = File.expand_path("~/Desktop/#{root.name}-graph.png")
22
+
23
+ begin
24
+ graph.output(:png => path)
25
+ Utils.open_in_default_app(path)
26
+ rescue StandardError => e
27
+ Console.log_error(e.message)
28
+ raise MultiRepoException, "Could not generate graph image because an error occurred during graph generation"
29
+ end
30
+
31
+ Console.log_step("Generated graph image #{path}")
32
+ end
33
+
34
+ def build_graph_recursive(graph, node)
35
+ parent_graph_node = graph.add_nodes(node.name)
36
+ node.children.each do |child_node|
37
+ child_graph_node = graph.add_node(child_node.name)
38
+ graph.add_edge(parent_graph_node, child_graph_node)
39
+ build_graph_recursive(graph, child_node)
40
+ end
41
+ end
42
+ end
46
43
  end
@@ -1,120 +1,120 @@
1
- require "multirepo/utility/console"
2
- require "multirepo/utility/utils"
3
- require "multirepo/files/config-file"
4
- require "multirepo/files/lock-file"
5
- require "multirepo/files/tracking-files"
6
- require "multirepo/commands/command"
7
-
8
- module MultiRepo
9
- class InitCommand < Command
10
- self.command = "init"
11
- self.summary = "Initialize the current repository as a multirepo project."
12
-
13
- def self.options
14
- [['[--extras]', 'Keep the current .multirepo config file as-is and initialize everything else.']].concat(super)
15
- end
16
-
17
- def initialize(argv)
18
- @only_extras = argv.flag?("extras")
19
- super
20
- end
21
-
22
- def run
23
- super
24
- ensure_in_work_tree
25
-
26
- if @only_extras
27
- ensure_multirepo_enabled
28
- Console.log_step("Initializing extras...")
29
- initialize_extras_step
30
- else
31
- Console.log_step("Initializing multirepo...")
32
- full_initialize_step
33
- end
34
-
35
- Console.log_step("Done!")
36
- rescue MultiRepoException => e
37
- Console.log_error(e.message)
38
- end
39
-
40
- def full_initialize_step
41
- if ConfigFile.new(".").exists?
42
- reinitialize = Console.ask_yes_no(".multirepo file already exists. Reinitialize?")
43
- raise MultiRepoException, "Initialization aborted" unless reinitialize
44
- end
45
-
46
- unless add_sibling_repos_step
47
- raise MultiRepoException, "There are no sibling repositories to track as dependencies. Initialization aborted."
48
- end
49
-
50
- initialize_extras_step
51
- end
52
-
53
- def add_sibling_repos_step
54
- sibling_repos = Utils.sibling_repos
55
- return false unless sibling_repos.any?
56
-
57
- Console.log_substep("Creating new multirepo config...")
58
-
59
- valid_repos = find_valid_repos(sibling_repos)
60
- entries = create_entries(valid_repos)
61
-
62
- raise MultiRepoException, "No sibling repositories were added as dependencies; aborting." unless entries.any?
63
-
64
- ConfigFile.new(".").save_entries(entries)
65
- return true
66
- end
67
-
68
- def initialize_extras_step
69
- install_hooks_step
70
- update_gitattributes_step
71
- update_gitconfig_step
72
- end
73
-
74
- def install_hooks_step
75
- install_hooks(".")
76
- Console.log_substep("Installed git hooks")
77
- end
78
-
79
- def update_gitattributes_step
80
- TrackingFiles.new(".").files.each do |f|
81
- filename = f.filename
82
- regex_escaped_filename = Regexp.quote(filename)
83
- Utils.append_if_missing("./.gitattributes", /^#{regex_escaped_filename} .*/, "#{filename} merge=ours")
84
- end
85
- Console.log_substep("Updated .gitattributes file")
86
- end
87
-
88
- def update_gitconfig_step
89
- update_gitconfig(".")
90
- Console.log_substep("Updated .git/config file")
91
- end
92
-
93
- def find_valid_repos(repos)
94
- repos.select do |repo|
95
- next true if repo.head_born?
96
- Console.log_warning("Ignoring repo '#{repo.path}' because its HEAD is unborn. You must perform at least one commit.")
97
- end
98
- end
99
-
100
- def create_entries(repos)
101
- entries = []
102
- repos.each do |repo|
103
- origin_url = repo.remote('origin').url
104
- current_branch = repo.current_branch
105
-
106
- if Console.ask_yes_no("Do you want to add '#{repo.path}' as a dependency?\n [origin: #{origin_url || "NONE"}, branch: #{current_branch}]")
107
- raise MultiRepoException, "Repo 'origin' remote url is not set; aborting." unless origin_url
108
- entries.push(ConfigEntry.new(repo))
109
- Console.log_substep("Added the repository '#{repo.path}' to the .multirepo file")
110
- end
111
- end
112
- return entries
113
- end
114
-
115
- def check_repo_exists
116
- if !Dir.exists?(@repo.path) then raise MultiRepoException, "There is no folder at path '#{@repo.path}'" end
117
- if !@repo.exists? then raise MultiRepoException, "'#{@repo.path}' is not a repository" end
118
- end
119
- end
1
+ require "multirepo/utility/console"
2
+ require "multirepo/utility/utils"
3
+ require "multirepo/files/config-file"
4
+ require "multirepo/files/lock-file"
5
+ require "multirepo/files/tracking-files"
6
+ require "multirepo/commands/command"
7
+
8
+ module MultiRepo
9
+ class InitCommand < Command
10
+ self.command = "init"
11
+ self.summary = "Initialize the current repository as a multirepo project."
12
+
13
+ def self.options
14
+ [['[--extras]', 'Keep the current .multirepo config file as-is and initialize everything else.']].concat(super)
15
+ end
16
+
17
+ def initialize(argv)
18
+ @only_extras = argv.flag?("extras")
19
+ super
20
+ end
21
+
22
+ def run
23
+ ensure_in_work_tree
24
+
25
+ if @only_extras
26
+ ensure_multirepo_enabled
27
+ Console.log_step("Initializing extras...")
28
+ initialize_extras_step
29
+ else
30
+ Console.log_step("Initializing multirepo...")
31
+ full_initialize_step
32
+ end
33
+
34
+ Console.log_step("Done!")
35
+ end
36
+
37
+ def full_initialize_step
38
+ if ConfigFile.new(".").exists?
39
+ reinitialize = Console.ask_yes_no(".multirepo file already exists. Reinitialize?")
40
+ raise MultiRepoException, "Initialization aborted" unless reinitialize
41
+ end
42
+
43
+ unless add_sibling_repos_step
44
+ raise MultiRepoException, "There are no sibling repositories to track as dependencies. Initialization aborted."
45
+ end
46
+
47
+ initialize_extras_step
48
+ end
49
+
50
+ def add_sibling_repos_step
51
+ sibling_repos = Utils.sibling_repos
52
+ return false unless sibling_repos.any?
53
+
54
+ Console.log_substep("Creating new multirepo config...")
55
+
56
+ valid_repos = find_valid_repos(sibling_repos)
57
+ entries = create_entries(valid_repos)
58
+
59
+ raise MultiRepoException, "No sibling repositories were added as dependencies; init aborted" unless entries.any?
60
+
61
+ ConfigFile.new(".").save_entries(entries)
62
+ return true
63
+ end
64
+
65
+ def initialize_extras_step
66
+ install_hooks_step
67
+ update_gitattributes_step
68
+ update_gitconfig_step
69
+ end
70
+
71
+ def install_hooks_step
72
+ install_hooks(".")
73
+ Console.log_substep("Installed git hooks")
74
+ end
75
+
76
+ def update_gitattributes_step
77
+ TrackingFiles.new(".").files.each do |f|
78
+ filename = f.filename
79
+ regex_escaped_filename = Regexp.quote(filename)
80
+ Utils.append_if_missing("./.gitattributes", /^#{regex_escaped_filename} .*/, "#{filename} merge=ours")
81
+ end
82
+ Console.log_substep("Updated .gitattributes file")
83
+ end
84
+
85
+ def update_gitconfig_step
86
+ update_gitconfig(".")
87
+ Console.log_substep("Updated .git/config file")
88
+ end
89
+
90
+ def find_valid_repos(repos)
91
+ repos.select do |repo|
92
+ next true if repo.head_born?
93
+ Console.log_warning("Ignoring repo '#{repo.path}' because its HEAD is unborn. You must perform at least one commit.")
94
+ end
95
+ end
96
+
97
+ def create_entries(repos)
98
+ entries = []
99
+ repos.each do |repo|
100
+ origin_url = repo.remote('origin').url
101
+ current_branch_name = repo.current_branch.name
102
+
103
+ if Console.ask_yes_no("Do you want to add '#{repo.path}' as a dependency?\n [origin: #{origin_url || "NONE"}, branch: #{current_branch_name}]")
104
+ unless origin_url
105
+ Console.log_warning("Repo 'origin' remote url is not set; skipping")
106
+ next
107
+ end
108
+ entries.push(ConfigEntry.new(repo))
109
+ Console.log_substep("Added the repository '#{repo.path}' to the .multirepo file")
110
+ end
111
+ end
112
+ return entries
113
+ end
114
+
115
+ def check_repo_exists
116
+ raise MultiRepoException, "There is no folder at path '#{@repo.path}'" unless Dir.exists?(@repo.path)
117
+ raise MultiRepoException, "'#{@repo.path}' is not a repository" unless @repo.exists?
118
+ end
119
+ end
120
120
  end