git-multirepo 1.0.0.beta1

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 (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