git-multirepo 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +38 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +37 -0
  6. data/LICENSE +22 -0
  7. data/README.md +132 -0
  8. data/Rakefile +2 -0
  9. data/bin/multi +6 -0
  10. data/git-multirepo.gemspec +29 -0
  11. data/lib/commands.rb +11 -0
  12. data/lib/git-multirepo.rb +1 -0
  13. data/lib/info.rb +5 -0
  14. data/lib/multirepo/commands/add.rb +41 -0
  15. data/lib/multirepo/commands/checkout.rb +59 -0
  16. data/lib/multirepo/commands/command.rb +41 -0
  17. data/lib/multirepo/commands/edit.rb +22 -0
  18. data/lib/multirepo/commands/fetch.rb +24 -0
  19. data/lib/multirepo/commands/init.rb +54 -0
  20. data/lib/multirepo/commands/install.rb +61 -0
  21. data/lib/multirepo/commands/open.rb +26 -0
  22. data/lib/multirepo/commands/remove.rb +42 -0
  23. data/lib/multirepo/commands/uninit.rb +21 -0
  24. data/lib/multirepo/commands/update.rb +19 -0
  25. data/lib/multirepo/config.rb +10 -0
  26. data/lib/multirepo/files/config-entry.rb +38 -0
  27. data/lib/multirepo/files/config-file.rb +38 -0
  28. data/lib/multirepo/files/lock-entry.rb +26 -0
  29. data/lib/multirepo/files/lock-file.rb +35 -0
  30. data/lib/multirepo/git/branch.rb +17 -0
  31. data/lib/multirepo/git/change.rb +11 -0
  32. data/lib/multirepo/git/git.rb +33 -0
  33. data/lib/multirepo/git/remote.rb +16 -0
  34. data/lib/multirepo/git/repo.rb +67 -0
  35. data/lib/multirepo/hooks/pre-commit-hook.rb +23 -0
  36. data/lib/multirepo/multirepo-exception.rb +6 -0
  37. data/lib/multirepo/utility/console.rb +52 -0
  38. data/lib/multirepo/utility/runner.rb +21 -0
  39. data/lib/multirepo/utility/utils.rb +37 -0
  40. data/resources/pre-commit +6 -0
  41. data/spec/integration/init-spec.rb +23 -0
  42. data/spec/spec_helper.rb +89 -0
  43. metadata +178 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d610a188dbbbaca809a697879cf909120e321965
4
+ data.tar.gz: 36431ca075fedca8e43cb00eb2c895c253920a9e
5
+ SHA512:
6
+ metadata.gz: 2e2f18b479298e1eeb135882b07fd6dad6dcc5338746f3096c0821a58d07a150c91121eb1285ad4b8eb5bf88bd4a56ba487fcbd8d99dca6ccabbda280da02e3d
7
+ data.tar.gz: 13f716a3bd918525167642d6dd87d6bfe413669fb66f3a0b208ac01f6fc93171478fa696af23a8601732b54e53f583126c55dd6858c11f71029f995f809a41ea
data/.gitignore ADDED
@@ -0,0 +1,38 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ # Gemfile.lock
30
+ # .ruby-version
31
+ # .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
35
+
36
+ # Project-specific
37
+ /.multirepo
38
+ /.multirepo.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in git-multirepo.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,37 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ git-multirepo (0.0.1)
5
+ claide (~> 0.8, >= 0.8.0)
6
+ colored (~> 1.2)
7
+ os (~> 0.9.6)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ claide (0.8.0)
13
+ colored (1.2)
14
+ diff-lcs (1.2.5)
15
+ os (0.9.6)
16
+ rake (10.4.2)
17
+ rspec (3.1.0)
18
+ rspec-core (~> 3.1.0)
19
+ rspec-expectations (~> 3.1.0)
20
+ rspec-mocks (~> 3.1.0)
21
+ rspec-core (3.1.7)
22
+ rspec-support (~> 3.1.0)
23
+ rspec-expectations (3.1.2)
24
+ diff-lcs (>= 1.2.0, < 2.0)
25
+ rspec-support (~> 3.1.0)
26
+ rspec-mocks (3.1.3)
27
+ rspec-support (~> 3.1.0)
28
+ rspec-support (3.1.2)
29
+
30
+ PLATFORMS
31
+ ruby
32
+
33
+ DEPENDENCIES
34
+ bundler (~> 1.7)
35
+ git-multirepo!
36
+ rake (~> 10.0)
37
+ rspec (~> 3.1.0)
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Michaël Fortin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # git-multirepo
2
+
3
+ Track multiple Git repositories side-by-side.
4
+
5
+ An alternative approach to manage constantly evolving dependencies.
6
+
7
+ Check out [the project's to-do list](https://www.pivotaltracker.com/n/projects/1256156) to get an idea of upcoming enhancements.
8
+
9
+ ## Motivation
10
+
11
+ By now the
12
+ [pitfalls](http://somethingsinistral.net/blog/git-submodules-are-probably-not-the-answer/)
13
+ of git submodules are
14
+ [pretty](https://codingkilledthecat.wordpress.com/2012/04/28/why-your-company-shouldnt-use-git-submodules/)
15
+ [well](http://slopjong.de/2013/06/04/git-why-submodules-are-evil/)
16
+ [known](http://stackoverflow.com/questions/12075809/git-submodules-workflow-issues).
17
+ They work when your dependencies are linearly-evolving third-party libraries that you seldom update but they fall apart when it comes to managing your own, constantly evolving dependencies.
18
+
19
+ Git subtrees are the recommended alternative but have pitfalls of their own:
20
+
21
+ - They require verbose and error-prone command-line operations. This can become quite tedious when managing more than one or two dependencies.
22
+ - They change the rules of the game when it comes to merges.
23
+ - Each developer has to configure the appropriate remotes and subtrees on his machine if he or she wants to contribute back.
24
+ - Developers must not forget to push changes to dependencies back to the appropriate remotes.
25
+ - Few git GUIs have any kind of subtree integration, so you're stuck with the command-line.
26
+
27
+ Etc.
28
+
29
+ ## Overview
30
+
31
+ Using git-multirepo, you can manage your main project and its dependencies as completely independent repositories while still maintaining the ability to checkout a previous revision in a single step and have the project build properly.
32
+
33
+ A git-multirepo setup looks like this:
34
+
35
+ ```
36
+ MyAwesomeProject
37
+ |-- AwesomeApp
38
+ |-- Dependency1
39
+ |-- Dependency2
40
+ ```
41
+
42
+ In essence:
43
+
44
+ 1. You tell git-multirepo what your dependencies are.
45
+ 2. Each time you commit the main repo, git-multirepo tracks what revision of each dependency is required by the project (don't worry, it ensures that you don't forget to commit changes to dependencies beforehand; more on that later).
46
+ 3. If you ever want to go back to a previous version of your project, git-multirepo handles checking out the main repo and appropriate revisions of all of its dependencies in a single, seamless operation.
47
+ 4. Setting up the project on a new machine is only a single `git clone` and `multi install` away.
48
+
49
+ ## Example
50
+
51
+ Say you want to track an existing project with git-multirepo:
52
+
53
+ 1. Organize repos on disk in the following manner:
54
+
55
+ ```
56
+ MyAwesomeProject
57
+ |-- AwesomeApp
58
+ |-- Dependency1
59
+ |-- Dependency2
60
+ ```
61
+
62
+ 2. `cd` into the *AwesomeApp* directory (aka the "main repo").
63
+ 3. Run `multi init`.
64
+ 4. You will get prompted to add *Dependency1* and *Dependency2* to multirepo; do so.
65
+ 5. git-multirepo reads all required information from dependency repos and initializes itself, storing its metadata files in the main repo, under version control.
66
+
67
+ From now on, each time you commit the main repo git-multirepo tracks &mdash; using a pre-commit hook &mdash; which revision of each dependency is required for that main repo revision, and where to get them. The pre-commit hook also ensures that you won't commit the main repo before its dependencies so that you always get a valid state stored under version control.
68
+
69
+ If you want to add another dependency later on, you can run `multi add ../NewDependency` and you can do the opposite with `multi remove ../SomeOtherDependency`.
70
+
71
+ If you want to checkout a previous revision (say `e690d`), you use the checkout command: `multi checkout e690d`. This will checkout the main repo's `e690d` revision and all of its dependencies with the proper revisions in detached HEAD state.
72
+
73
+ If you want to setup your project on another machine, simply clone the main repo in a container directory (see above) and run `multi install`. This will clone each dependency and checkout the appropriate branches.
74
+
75
+ If you want to stop using git-multirepo, run `multi uninit`. This will remove all traces of git-multirepo from your repository and working copy.
76
+
77
+ ## Advantages
78
+
79
+ - Makes setting up the project on a new machine a breeze.
80
+ - Works really well for multiple projects that share a common set of constantly evolving dependencies.
81
+ - Each dependency's repository is totally independent from the main repository, which simplifies a lot of things (merges, contributing upstream, etc.) and works well with git GUIs.
82
+ - While the repositories are independent, git-multirepo makes sure to track everything that's required to bring back a previous version of your project in a valid state.
83
+ - Much more approachable to novice developers than submodules or subtrees.
84
+ - Once setup, there is little need for git-multirepo commands, so you are free to use whatever tools you like to work with your git repos.
85
+ - Low possibility of human error (such as forgetting to contribute dependency changes back to the appropriate remotes, forgetting to commit dependencies before committing the main project, etc.)
86
+ - You're not stuck with git-multirepo. It stores its metadata as [YAML](http://www.yaml.org) in the main repo. You can clone and checkout appropriate revisions of your dependencies by hand without git-multirepo if you need to. The information is there, in human-readable form.
87
+
88
+ | How It Handles... | git-multirepo | git submodules | git subtrees |
89
+ |----------------------------------|:----------------:|:--------------:|:------------:|
90
+ | Working Copy | beside main repo | in main repo | in main repo |
91
+ | Constantly Evolving Dependencies | easy | hard | passable |
92
+ | Merging Changes to Dependencies | easy | hard | passable |
93
+ | Contributing Upstream | easy | easy | passable |
94
+ | Continuous Integration | medium | medium | easy |
95
+ | Complex Branch-Based Workflows | hard* | hard | easy |
96
+
97
+ (*) This should get better in future versions of git-multirepo.
98
+
99
+ ## Limitations
100
+
101
+ - git-multirepo should be considered beta at the moment. All of the core features work as described, though. Suggestions and contributions are welcome.
102
+ - The project and its dependencies must live beside each other on disk (for now).
103
+ - There are currently no features to facilitate branch-heavy workflows.
104
+ - You must (ideally) install the tool on your CI server: `gem install git-multirepo`
105
+
106
+ ## Summary of Commands
107
+
108
+ Here is a quick rundown of commands available to you in git-multirepo:
109
+
110
+ | Command | Description |
111
+ |---------|-------------|
112
+ | init | Initialize the current repository as a multirepo project. |
113
+ | add | Track an additional dependency with multirepo. |
114
+ | checkout | Checks out the specified commit or branch of the main repo and checks out matching versions of all dependencies. |
115
+ | edit | Opens the .multirepo file in the default text editor. |
116
+ | fetch | Performs a git fetch on all dependencies. |
117
+ | install | Clones and checks out dependencies as defined in the .multirepo file, and installs git-multirepo's local pre-commit hook. |
118
+ | open | Opens all dependencies in the current OS's file explorer. |
119
+ | remove | Removes the specified dependency from multirepo. |
120
+ | update | Force-updates the multirepo lock file. |
121
+ | uninit | Removes all traces of multirepo in the current multirepo repository. |
122
+
123
+ ## Metadata
124
+
125
+ git-multirepo stores all of its metadata in two files:
126
+
127
+ | File | Format | Contents |
128
+ |------|--------|----------|
129
+ | .multirepo | YAML | A collection of your project's dependencies. For each dependency, stores its **local path** relative to the main repo, the **remote URL** and the **branch** your project depends upon.
130
+ | .multirepo.lock | YAML | For each dependency, stores the **commit hash** and **branch** on which the dependency was when the main repo was committed. The dependency's **name** is also included but only serves as a reference to make inspecting the lock file easier. |
131
+
132
+ The information contained in .multirepo and .multirepo.lock allow one-step cloning of a project and all its dependencies, and checking out any prior revision of the main project with appropriate revisions of all of its dependencies, respectively.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/multi ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "claide"
4
+ require "commands"
5
+
6
+ MultiRepo::Command.run(ARGV)
@@ -0,0 +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 = "http://www.irradiated.net"
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 ADDED
@@ -0,0 +1,11 @@
1
+ require_relative "multirepo/commands/command"
2
+ require_relative "multirepo/commands/add"
3
+ require_relative "multirepo/commands/checkout"
4
+ require_relative "multirepo/commands/edit"
5
+ require_relative "multirepo/commands/fetch"
6
+ require_relative "multirepo/commands/init"
7
+ require_relative "multirepo/commands/install"
8
+ require_relative "multirepo/commands/open"
9
+ require_relative "multirepo/commands/remove"
10
+ require_relative "multirepo/commands/uninit"
11
+ require_relative "multirepo/commands/update"
@@ -0,0 +1 @@
1
+ require "multirepo/hooks/pre-commit-hook.rb"
data/lib/info.rb ADDED
@@ -0,0 +1,5 @@
1
+ module MultiRepo
2
+ NAME = "git-multirepo"
3
+ VERSION = "1.0.0.beta1"
4
+ DESCRIPTION = "Track multiple Git repositories side-by-side."
5
+ end
@@ -0,0 +1,41 @@
1
+ require "multirepo/utility/console"
2
+ require "multirepo/files/config-file"
3
+
4
+ module MultiRepo
5
+ class Add < Command
6
+ self.command = "add"
7
+ self.summary = "Track an additional dependency with multirepo."
8
+
9
+ def initialize(argv)
10
+ @path = argv.shift_argument
11
+ super
12
+ end
13
+
14
+ def validate!
15
+ super
16
+ help! "You must specify a repository to add as a dependency" unless @path
17
+ end
18
+
19
+ def run
20
+ super
21
+ ensure_multirepo_initialized
22
+ ensure_repo_exists
23
+
24
+ entry = ConfigEntry.new(Repo.new(@path))
25
+ if ConfigFile.entry_exists?(entry)
26
+ Console.log_info("There is already an entry for '#{@path}' in the .multirepo file")
27
+ else
28
+ ConfigFile.add_entry(entry)
29
+ ConfigFile.stage
30
+ Console.log_step("Added '#{@path}' to the .multirepo file")
31
+ end
32
+ rescue MultiRepoException => e
33
+ Console.log_error(e.message)
34
+ end
35
+
36
+ def ensure_repo_exists
37
+ raise MultiRepoException, "There is no folder at path '#{@path}'" unless Dir.exists?(@path)
38
+ raise MultiRepoException, "'#{@path}' is not a repository" unless Repo.new(@path).exists?
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,59 @@
1
+ require "multirepo/utility/console"
2
+
3
+ module MultiRepo
4
+ class Checkout < 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
+ def initialize(argv)
9
+ @ref = argv.shift_argument
10
+ @checkout_latest = argv.flag?("latest")
11
+ super
12
+ end
13
+
14
+ def run
15
+ super
16
+ ensure_multirepo_initialized
17
+
18
+ main_repo = Repo.new(".")
19
+ initial_revision = main_repo.current_branch || main_repo.head_hash
20
+
21
+ Console.log_step("Checking out #{@ref} and its dependencies...")
22
+
23
+ unless main_repo.is_clean?
24
+ raise "Can't checkout #{@ref} because the main repo contains uncommitted changes"
25
+ end
26
+
27
+ unless main_repo.checkout(@ref)
28
+ raise MultiRepoException, "Couldn't perform checkout of main repo #{@ref}!"
29
+ end
30
+
31
+ Console.log_substep("Checked out main repo #{@ref}")
32
+
33
+ unless LockFile.exists?
34
+ main_repo.checkout(initial_revision)
35
+ raise MultiRepoException, "The specified revision was not managed by multirepo. Checkout reverted."
36
+ end
37
+
38
+ if Utils.warn_of_uncommitted_changes(ConfigFile.load)
39
+ main_repo.checkout(initial_revision)
40
+ raise "'#{e.path}' contains uncommitted changes. Checkout reverted."
41
+ end
42
+
43
+ config_entries = ConfigFile.load # Load the post-checkout config entries, which might be different than pre-checkout
44
+ LockFile.load.each do |lock_entry|
45
+ config_entry = config_entries.select{ |config_entry| config_entry.id == lock_entry.id }.first
46
+ revision = @checkout_latest ? lock_entry.branch : lock_entry.head
47
+ if config_entry.repo.checkout(revision)
48
+ Console.log_substep("Checked out #{lock_entry.name} #{revision}")
49
+ else
50
+ raise MultiRepoException, "Couldn't check out the appropriate version of dependency #{lock_entry.name}"
51
+ end
52
+ end
53
+
54
+ Console.log_step("Done!")
55
+ rescue MultiRepoException => e
56
+ Console.log_error(e.message)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,41 @@
1
+ require "claide"
2
+
3
+ require "info"
4
+ require "multirepo/multirepo-exception"
5
+ require "multirepo/config"
6
+
7
+ module MultiRepo
8
+ class Command < CLAide::Command
9
+ self.abstract_command = true
10
+ self.command = "multi"
11
+ self.version = VERSION
12
+ self.description = DESCRIPTION
13
+
14
+ def initialize(argv)
15
+ Config.instance.verbose = argv.flag?("verbose") ? true : false
16
+ super
17
+ end
18
+
19
+ def run
20
+ validate_in_work_tree
21
+ end
22
+
23
+ def validate_in_work_tree
24
+ raise MultiRepoException, "Not a git repository" unless Git.is_inside_git_repo(".")
25
+ end
26
+
27
+ def install_pre_commit_hook
28
+ Utils.install_pre_commit_hook
29
+ Console.log_substep("Installed pre-commit hook")
30
+ end
31
+
32
+ def update_lock_file
33
+ LockFile.update
34
+ Console.log_substep("Updated and staged lock file with current HEAD revisions for all dependencies")
35
+ end
36
+
37
+ def ensure_multirepo_initialized
38
+ raise MultiRepoException, "multirepo is not initialized in this repository." unless ConfigFile.exists?
39
+ end
40
+ end
41
+ end