git-multirepo 1.0.0.beta65 → 1.0.0.beta66
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitattributes +4 -4
- data/.gitbugtraq +3 -3
- data/.gitignore +38 -38
- data/.rspec +2 -2
- data/.rubocop.yml +79 -79
- data/CHANGELOG.md +95 -91
- data/Gemfile +4 -4
- data/Gemfile.lock +47 -47
- data/LICENSE +22 -22
- data/README.md +178 -178
- data/Rakefile +1 -1
- data/bin/multi +11 -11
- data/docs/bug-repros/91565510-repro.sh +20 -20
- data/git-multirepo.gemspec +31 -31
- data/lib/git-multirepo.rb +3 -3
- data/lib/multirepo/commands/add-command.rb +51 -51
- data/lib/multirepo/commands/branch-command.rb +88 -88
- data/lib/multirepo/commands/checkout-command.rb +127 -127
- data/lib/multirepo/commands/clone-command.rb +68 -68
- data/lib/multirepo/commands/command.rb +87 -87
- data/lib/multirepo/commands/commands.rb +14 -14
- data/lib/multirepo/commands/do-command.rb +101 -101
- data/lib/multirepo/commands/init-command.rb +121 -121
- data/lib/multirepo/commands/inspect-command.rb +48 -48
- data/lib/multirepo/commands/install-command.rb +170 -170
- data/lib/multirepo/commands/merge-command.rb +251 -249
- data/lib/multirepo/commands/open-command.rb +55 -55
- data/lib/multirepo/commands/remove-command.rb +48 -48
- data/lib/multirepo/commands/uninit-command.rb +18 -18
- data/lib/multirepo/commands/update-command.rb +112 -112
- data/lib/multirepo/config.rb +19 -19
- data/lib/multirepo/files/config-entry.rb +39 -39
- data/lib/multirepo/files/config-file.rb +52 -52
- data/lib/multirepo/files/lock-entry.rb +29 -29
- data/lib/multirepo/files/lock-file.rb +62 -62
- data/lib/multirepo/files/meta-file.rb +51 -51
- data/lib/multirepo/files/tracking-file.rb +9 -9
- data/lib/multirepo/files/tracking-files.rb +64 -64
- data/lib/multirepo/git/branch.rb +32 -32
- data/lib/multirepo/git/change.rb +11 -11
- data/lib/multirepo/git/commit.rb +7 -7
- data/lib/multirepo/git/git-runner.rb +56 -56
- data/lib/multirepo/git/git.rb +10 -10
- data/lib/multirepo/git/ref.rb +38 -38
- data/lib/multirepo/git/remote.rb +17 -17
- data/lib/multirepo/git/repo.rb +129 -129
- data/lib/multirepo/hooks/post-commit-hook.rb +23 -23
- data/lib/multirepo/hooks/pre-commit-hook.rb +35 -35
- data/lib/multirepo/info.rb +5 -5
- data/lib/multirepo/logic/dependency.rb +6 -6
- data/lib/multirepo/logic/merge-descriptor.rb +95 -95
- data/lib/multirepo/logic/node.rb +72 -72
- data/lib/multirepo/logic/performer.rb +55 -55
- data/lib/multirepo/logic/repo-selection.rb +25 -25
- data/lib/multirepo/logic/revision-selection.rb +15 -15
- data/lib/multirepo/logic/revision-selector.rb +23 -23
- data/lib/multirepo/logic/version-comparer.rb +10 -10
- data/lib/multirepo/multirepo-exception.rb +6 -6
- data/lib/multirepo/output/extra-output.rb +12 -12
- data/lib/multirepo/output/teamcity-extra-output.rb +11 -11
- data/lib/multirepo/utility/console.rb +52 -52
- data/lib/multirepo/utility/popen-runner.rb +27 -27
- data/lib/multirepo/utility/system-runner.rb +14 -14
- data/lib/multirepo/utility/utils.rb +99 -99
- data/lib/multirepo/utility/verbosity.rb +6 -6
- data/resources/.gitconfig +2 -2
- data/resources/post-commit +0 -0
- data/resources/pre-commit +0 -0
- data/spec/integration/init_spec.rb +19 -19
- data/spec/spec_helper.rb +89 -89
- metadata +3 -3
data/README.md
CHANGED
@@ -1,179 +1,179 @@
|
|
1
|
-
# git-multirepo
|
2
|
-
|
3
|
-
[![Gem Version](https://badge.fury.io/rb/git-multirepo.svg)](http://badge.fury.io/rb/git-multirepo)
|
4
|
-
[![Code Climate](https://codeclimate.com/github/fortinmike/git-multirepo/badges/gpa.svg)](https://codeclimate.com/github/fortinmike/git-multirepo)
|
5
|
-
[![License](http://img.shields.io/:license-mit-blue.svg)](https://github.com/fortinmike/git-multirepo/blob/master/LICENSE)
|
6
|
-
|
7
|
-
Track multiple Git repositories side-by-side.
|
8
|
-
|
9
|
-
An alternative approach to manage constantly evolving dependencies.
|
10
|
-
|
11
|
-
Check out [the project's to-do list](https://www.pivotaltracker.com/n/projects/1256156) to get an idea of upcoming enhancements and read the [change log](https://github.com/fortinmike/git-multirepo/blob/master/CHANGELOG.md) for detailed information on git-multirepo releases.
|
12
|
-
|
13
|
-
You can download a handy cheat sheet [here](https://github.com/fortinmike/git-multirepo/raw/master/docs/git-multirepo-cheatsheet.docx).
|
14
|
-
|
15
|
-
## Installation
|
16
|
-
|
17
|
-
git-multirepo is distributed as a Ruby Gem. It can thus be installed as follows (provided Ruby is installed on your machine):
|
18
|
-
|
19
|
-
$ gem install git-multirepo --pre
|
20
|
-
|
21
|
-
The `--pre` flag is necessary to install beta releases.
|
22
|
-
|
23
|
-
## Motivation
|
24
|
-
|
25
|
-
By now the
|
26
|
-
[pitfalls](http://somethingsinistral.net/blog/git-submodules-are-probably-not-the-answer/)
|
27
|
-
of git submodules are
|
28
|
-
[pretty](https://codingkilledthecat.wordpress.com/2012/04/28/why-your-company-shouldnt-use-git-submodules/)
|
29
|
-
[well](http://slopjong.de/2013/06/04/git-why-submodules-are-evil/)
|
30
|
-
[known](http://stackoverflow.com/questions/12075809/git-submodules-workflow-issues).
|
31
|
-
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.
|
32
|
-
|
33
|
-
Git subtrees are the recommended alternative but have pitfalls of their own:
|
34
|
-
|
35
|
-
- They require verbose and error-prone command-line operations. This can become quite tedious when managing more than one or two dependencies.
|
36
|
-
- They change the rules of the game when it comes to merges.
|
37
|
-
- Each developer has to configure the appropriate remotes and subtrees on his machine if he or she wants to contribute back.
|
38
|
-
- Developers must not forget to push changes to dependencies back to the appropriate remotes.
|
39
|
-
- Few git GUIs have any kind of subtree integration, so you're stuck with the command-line.
|
40
|
-
|
41
|
-
Etc.
|
42
|
-
|
43
|
-
## Overview
|
44
|
-
|
45
|
-
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.
|
46
|
-
|
47
|
-
A git-multirepo setup looks like this:
|
48
|
-
|
49
|
-
```
|
50
|
-
MyAwesomeProject
|
51
|
-
|-- AwesomeApp
|
52
|
-
|-- Dependency1
|
53
|
-
|-- Dependency2
|
54
|
-
```
|
55
|
-
|
56
|
-
In essence:
|
57
|
-
|
58
|
-
1. You tell git-multirepo what your dependencies are.
|
59
|
-
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).
|
60
|
-
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.
|
61
|
-
4. Setting up the project on a new machine is only a single `multi clone` away.
|
62
|
-
|
63
|
-
## Example
|
64
|
-
|
65
|
-
Say you want to track an existing project with git-multirepo:
|
66
|
-
|
67
|
-
1. Organize repos on disk in the following manner:
|
68
|
-
|
69
|
-
```
|
70
|
-
MyAwesomeProject
|
71
|
-
|-- AwesomeApp
|
72
|
-
|-- Dependency1
|
73
|
-
|-- Dependency2
|
74
|
-
```
|
75
|
-
|
76
|
-
2. `cd` into the *AwesomeApp* directory (aka the "main repo").
|
77
|
-
3. Run `multi init`.
|
78
|
-
4. You will get prompted to add *Dependency1* and *Dependency2* to multirepo; do so.
|
79
|
-
5. git-multirepo reads all required information from dependency repos and initializes itself, storing its metadata in the main repo, under version control.
|
80
|
-
|
81
|
-
From now on, each time you commit the main repo git-multirepo tracks — using local git hooks — which revision of each dependency is required for that main repo revision, and where to get them. The hooks also ensure that you won't commit the main repo before its dependencies so that you always get a valid state stored under version control.
|
82
|
-
|
83
|
-
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`.
|
84
|
-
|
85
|
-
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.
|
86
|
-
|
87
|
-
If you want to setup your project on another machine, simply run `multi clone` with the appropriate parameters. This will clone the project and each of its dependencies, then checkout the appropriate work branches.
|
88
|
-
|
89
|
-
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, including local git hooks.
|
90
|
-
|
91
|
-
## Advantages
|
92
|
-
|
93
|
-
- Makes setting up a project on a new machine a breeze.
|
94
|
-
- Really effective when working on multiple projects that share a common set of constantly evolving dependencies.
|
95
|
-
- 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.
|
96
|
-
- 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.
|
97
|
-
- It supports sub-dependencies (e.g. dependencies that have dependencies of their own) so that you can bring back any subset of your project in a valid state at will.
|
98
|
-
- Much more approachable to novice developers than submodules or subtrees.
|
99
|
-
- Once setup, there is little need for git-multirepo commands (although some of them are great timesavers), so you are free to use whatever tools you like to work with your git repos.
|
100
|
-
- Low possibility of human error (such as forgetting to contribute changes to dependencies back to the appropriate remotes, forgetting to commit dependencies in the proper order, etc.)
|
101
|
-
- 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.
|
102
|
-
|
103
|
-
| How It Handles... | git-multirepo | git submodules | git subtrees |
|
104
|
-
|----------------------------------|:----------------:|:--------------:|:------------:|
|
105
|
-
| Working Copy | beside main repo | in main repo | in main repo |
|
106
|
-
| Constantly Evolving Dependencies | easy | hard | passable |
|
107
|
-
| Merging Changes to Dependencies | easy | hard | passable |
|
108
|
-
| Contributing Upstream | easy | easy | passable |
|
109
|
-
| Continuous Integration | medium | medium | easy |
|
110
|
-
| Branch-Based Workflows | easy* | hard | easy |
|
111
|
-
|
112
|
-
(*) The `multi branch` and `multi merge` commands faciliate branching and merging the main repo and its dependencies as a whole.
|
113
|
-
|
114
|
-
## Limitations
|
115
|
-
|
116
|
-
- This tool should be considered beta at the moment (but it's getting pretty stable).
|
117
|
-
- The tracked project and its dependencies must live beside each other on disk.
|
118
|
-
- You must install the tool on your CI server (`gem install git-multirepo`) and perform a `multi install --ci` to checkout dependencies prior to building.
|
119
|
-
|
120
|
-
## Subdependencies
|
121
|
-
|
122
|
-
Dependencies can be initialized and have their own dependencies. However, git-multirepo currently supports only *direct* dependencies (which is a minor inconvenience in practice). This means that every git-multirepo-enabled repository must have its direct and indirect dependencies listed in its `.multirepo file`. Take for example the following directory listing.
|
123
|
-
|
124
|
-
```
|
125
|
-
MyAwesomeProject
|
126
|
-
|-- AwesomeApp
|
127
|
-
|-- AppDependency1
|
128
|
-
|-- AppDependency1-Dependency
|
129
|
-
|-- AppDependency2
|
130
|
-
```
|
131
|
-
|
132
|
-
To properly track those repositories with git-multirepo, `AppDependency1` would be initialized with `AppDependency1-Dependency` as a dependency. Then, `AwesomeApp` would be initialized with `AppDependency1`, `AppDependency1-Dependency` and `AppDependency2` as dependencies, even though `MyAwesomeApp` does not directly depend on `AppDependency1-Dependency`. `AppDependency1-Dependency` and `AppDependency2` do not need to be initialized because they have no dependencies of their own.
|
133
|
-
|
134
|
-
After repositories are initialized this way, git-multirepo handles the rest and enforces dependency tree-based commit order, merges and more. From then, it is also possible to perform a `multi checkout` not only in the `AwesomeApp` directory but also in the `AppDependency1` directory. This means you can easily get a subset of your dependencies into the state they were in at some point in the past.
|
135
|
-
|
136
|
-
## Continuous Integration
|
137
|
-
|
138
|
-
git-multirepo supports continuous integration in a couple of ways:
|
139
|
-
|
140
|
-
- The `install` command has a special `--ci` flag, which:
|
141
|
-
- Installs exact revisions of dependencies in-place
|
142
|
-
- Skips local hooks installation
|
143
|
-
- Logs additional information that's useful in a CI context
|
144
|
-
- The `inspect` command offers plumbing-style output that can be used to inspect repositories to conditionally perform multirepo operations on them afterwards.
|
145
|
-
- A special `--extra-output` option causes git-multirepo to output specially-formatted progress and error messages for CI purposes, such as TeamCity service messages (when using `--extra-output=teamcity`).
|
146
|
-
|
147
|
-
## Summary of Commands
|
148
|
-
|
149
|
-
Here is a quick rundown of commands available to you in git-multirepo:
|
150
|
-
|
151
|
-
| Command | Description |
|
152
|
-
|---------|-------------|
|
153
|
-
| init | Initialize the current repository as a multirepo project. |
|
154
|
-
| add | Track an additional dependency with multirepo. |
|
155
|
-
| branch | Create and/or checkout a new branch for all repos. |
|
156
|
-
| checkout | Checks out the specified commit or branch of the main repo and checks out matching versions of all dependencies. |
|
157
|
-
| clone | Clones the specified repository in a subfolder, then installs it. |
|
158
|
-
| do | Perform an arbitrary Git operation in the main repository, dependency repositories or all repositories. |
|
159
|
-
| inspect | Outputs various information about multirepo-enabled repos. For use in scripting and CI scenarios. |
|
160
|
-
| install | Clones and checks out dependencies as defined in the version-controlled multirepo metadata files and installs git-multirepo's local git hooks. Idempotent for a given main repo checkout. |
|
161
|
-
| merge | Performs a git merge on all dependencies and the main repo, in the proper order. |
|
162
|
-
| open | Opens repositories in the OS's file explorer. |
|
163
|
-
| remove | Removes the specified dependency from multirepo. |
|
164
|
-
| update | Force-updates the multirepo tracking files. |
|
165
|
-
| uninit | Removes all traces of multirepo in the current multirepo repository. |
|
166
|
-
|
167
|
-
To read more about each command, use the `--help` flag (e.g. `$ multi clone --help`).
|
168
|
-
|
169
|
-
## Metadata
|
170
|
-
|
171
|
-
git-multirepo stores all of its metadata in three files:
|
172
|
-
|
173
|
-
| File | Format | Updated | Contents |
|
174
|
-
|------|--------|---------|----------|
|
175
|
-
| .multirepo | YAML | at *initialization*, on *add* on *remove* | A collection of your project's dependencies. For each dependency, stores its **local path** relative to the main repo and the **remote URL** your project depends upon.
|
176
|
-
| .multirepo.lock | YAML | before each commit | For each dependency, stores the **commit id** 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. |
|
177
|
-
| .multirepo.meta | YAML | before each commit | Various git-multirepo metadata, such as the **git-multirepo version** that the last commit was performed with. |
|
178
|
-
|
1
|
+
# git-multirepo
|
2
|
+
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/git-multirepo.svg)](http://badge.fury.io/rb/git-multirepo)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/fortinmike/git-multirepo/badges/gpa.svg)](https://codeclimate.com/github/fortinmike/git-multirepo)
|
5
|
+
[![License](http://img.shields.io/:license-mit-blue.svg)](https://github.com/fortinmike/git-multirepo/blob/master/LICENSE)
|
6
|
+
|
7
|
+
Track multiple Git repositories side-by-side.
|
8
|
+
|
9
|
+
An alternative approach to manage constantly evolving dependencies.
|
10
|
+
|
11
|
+
Check out [the project's to-do list](https://www.pivotaltracker.com/n/projects/1256156) to get an idea of upcoming enhancements and read the [change log](https://github.com/fortinmike/git-multirepo/blob/master/CHANGELOG.md) for detailed information on git-multirepo releases.
|
12
|
+
|
13
|
+
You can download a handy cheat sheet [here](https://github.com/fortinmike/git-multirepo/raw/master/docs/git-multirepo-cheatsheet.docx).
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
git-multirepo is distributed as a Ruby Gem. It can thus be installed as follows (provided Ruby is installed on your machine):
|
18
|
+
|
19
|
+
$ gem install git-multirepo --pre
|
20
|
+
|
21
|
+
The `--pre` flag is necessary to install beta releases.
|
22
|
+
|
23
|
+
## Motivation
|
24
|
+
|
25
|
+
By now the
|
26
|
+
[pitfalls](http://somethingsinistral.net/blog/git-submodules-are-probably-not-the-answer/)
|
27
|
+
of git submodules are
|
28
|
+
[pretty](https://codingkilledthecat.wordpress.com/2012/04/28/why-your-company-shouldnt-use-git-submodules/)
|
29
|
+
[well](http://slopjong.de/2013/06/04/git-why-submodules-are-evil/)
|
30
|
+
[known](http://stackoverflow.com/questions/12075809/git-submodules-workflow-issues).
|
31
|
+
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.
|
32
|
+
|
33
|
+
Git subtrees are the recommended alternative but have pitfalls of their own:
|
34
|
+
|
35
|
+
- They require verbose and error-prone command-line operations. This can become quite tedious when managing more than one or two dependencies.
|
36
|
+
- They change the rules of the game when it comes to merges.
|
37
|
+
- Each developer has to configure the appropriate remotes and subtrees on his machine if he or she wants to contribute back.
|
38
|
+
- Developers must not forget to push changes to dependencies back to the appropriate remotes.
|
39
|
+
- Few git GUIs have any kind of subtree integration, so you're stuck with the command-line.
|
40
|
+
|
41
|
+
Etc.
|
42
|
+
|
43
|
+
## Overview
|
44
|
+
|
45
|
+
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.
|
46
|
+
|
47
|
+
A git-multirepo setup looks like this:
|
48
|
+
|
49
|
+
```
|
50
|
+
MyAwesomeProject
|
51
|
+
|-- AwesomeApp
|
52
|
+
|-- Dependency1
|
53
|
+
|-- Dependency2
|
54
|
+
```
|
55
|
+
|
56
|
+
In essence:
|
57
|
+
|
58
|
+
1. You tell git-multirepo what your dependencies are.
|
59
|
+
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).
|
60
|
+
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.
|
61
|
+
4. Setting up the project on a new machine is only a single `multi clone` away.
|
62
|
+
|
63
|
+
## Example
|
64
|
+
|
65
|
+
Say you want to track an existing project with git-multirepo:
|
66
|
+
|
67
|
+
1. Organize repos on disk in the following manner:
|
68
|
+
|
69
|
+
```
|
70
|
+
MyAwesomeProject
|
71
|
+
|-- AwesomeApp
|
72
|
+
|-- Dependency1
|
73
|
+
|-- Dependency2
|
74
|
+
```
|
75
|
+
|
76
|
+
2. `cd` into the *AwesomeApp* directory (aka the "main repo").
|
77
|
+
3. Run `multi init`.
|
78
|
+
4. You will get prompted to add *Dependency1* and *Dependency2* to multirepo; do so.
|
79
|
+
5. git-multirepo reads all required information from dependency repos and initializes itself, storing its metadata in the main repo, under version control.
|
80
|
+
|
81
|
+
From now on, each time you commit the main repo git-multirepo tracks — using local git hooks — which revision of each dependency is required for that main repo revision, and where to get them. The hooks also ensure that you won't commit the main repo before its dependencies so that you always get a valid state stored under version control.
|
82
|
+
|
83
|
+
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`.
|
84
|
+
|
85
|
+
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.
|
86
|
+
|
87
|
+
If you want to setup your project on another machine, simply run `multi clone` with the appropriate parameters. This will clone the project and each of its dependencies, then checkout the appropriate work branches.
|
88
|
+
|
89
|
+
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, including local git hooks.
|
90
|
+
|
91
|
+
## Advantages
|
92
|
+
|
93
|
+
- Makes setting up a project on a new machine a breeze.
|
94
|
+
- Really effective when working on multiple projects that share a common set of constantly evolving dependencies.
|
95
|
+
- 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.
|
96
|
+
- 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.
|
97
|
+
- It supports sub-dependencies (e.g. dependencies that have dependencies of their own) so that you can bring back any subset of your project in a valid state at will.
|
98
|
+
- Much more approachable to novice developers than submodules or subtrees.
|
99
|
+
- Once setup, there is little need for git-multirepo commands (although some of them are great timesavers), so you are free to use whatever tools you like to work with your git repos.
|
100
|
+
- Low possibility of human error (such as forgetting to contribute changes to dependencies back to the appropriate remotes, forgetting to commit dependencies in the proper order, etc.)
|
101
|
+
- 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.
|
102
|
+
|
103
|
+
| How It Handles... | git-multirepo | git submodules | git subtrees |
|
104
|
+
|----------------------------------|:----------------:|:--------------:|:------------:|
|
105
|
+
| Working Copy | beside main repo | in main repo | in main repo |
|
106
|
+
| Constantly Evolving Dependencies | easy | hard | passable |
|
107
|
+
| Merging Changes to Dependencies | easy | hard | passable |
|
108
|
+
| Contributing Upstream | easy | easy | passable |
|
109
|
+
| Continuous Integration | medium | medium | easy |
|
110
|
+
| Branch-Based Workflows | easy* | hard | easy |
|
111
|
+
|
112
|
+
(*) The `multi branch` and `multi merge` commands faciliate branching and merging the main repo and its dependencies as a whole.
|
113
|
+
|
114
|
+
## Limitations
|
115
|
+
|
116
|
+
- This tool should be considered beta at the moment (but it's getting pretty stable).
|
117
|
+
- The tracked project and its dependencies must live beside each other on disk.
|
118
|
+
- You must install the tool on your CI server (`gem install git-multirepo`) and perform a `multi install --ci` to checkout dependencies prior to building.
|
119
|
+
|
120
|
+
## Subdependencies
|
121
|
+
|
122
|
+
Dependencies can be initialized and have their own dependencies. However, git-multirepo currently supports only *direct* dependencies (which is a minor inconvenience in practice). This means that every git-multirepo-enabled repository must have its direct and indirect dependencies listed in its `.multirepo file`. Take for example the following directory listing.
|
123
|
+
|
124
|
+
```
|
125
|
+
MyAwesomeProject
|
126
|
+
|-- AwesomeApp
|
127
|
+
|-- AppDependency1
|
128
|
+
|-- AppDependency1-Dependency
|
129
|
+
|-- AppDependency2
|
130
|
+
```
|
131
|
+
|
132
|
+
To properly track those repositories with git-multirepo, `AppDependency1` would be initialized with `AppDependency1-Dependency` as a dependency. Then, `AwesomeApp` would be initialized with `AppDependency1`, `AppDependency1-Dependency` and `AppDependency2` as dependencies, even though `MyAwesomeApp` does not directly depend on `AppDependency1-Dependency`. `AppDependency1-Dependency` and `AppDependency2` do not need to be initialized because they have no dependencies of their own.
|
133
|
+
|
134
|
+
After repositories are initialized this way, git-multirepo handles the rest and enforces dependency tree-based commit order, merges and more. From then, it is also possible to perform a `multi checkout` not only in the `AwesomeApp` directory but also in the `AppDependency1` directory. This means you can easily get a subset of your dependencies into the state they were in at some point in the past.
|
135
|
+
|
136
|
+
## Continuous Integration
|
137
|
+
|
138
|
+
git-multirepo supports continuous integration in a couple of ways:
|
139
|
+
|
140
|
+
- The `install` command has a special `--ci` flag, which:
|
141
|
+
- Installs exact revisions of dependencies in-place
|
142
|
+
- Skips local hooks installation
|
143
|
+
- Logs additional information that's useful in a CI context
|
144
|
+
- The `inspect` command offers plumbing-style output that can be used to inspect repositories to conditionally perform multirepo operations on them afterwards.
|
145
|
+
- A special `--extra-output` option causes git-multirepo to output specially-formatted progress and error messages for CI purposes, such as TeamCity service messages (when using `--extra-output=teamcity`).
|
146
|
+
|
147
|
+
## Summary of Commands
|
148
|
+
|
149
|
+
Here is a quick rundown of commands available to you in git-multirepo:
|
150
|
+
|
151
|
+
| Command | Description |
|
152
|
+
|---------|-------------|
|
153
|
+
| init | Initialize the current repository as a multirepo project. |
|
154
|
+
| add | Track an additional dependency with multirepo. |
|
155
|
+
| branch | Create and/or checkout a new branch for all repos. |
|
156
|
+
| checkout | Checks out the specified commit or branch of the main repo and checks out matching versions of all dependencies. |
|
157
|
+
| clone | Clones the specified repository in a subfolder, then installs it. |
|
158
|
+
| do | Perform an arbitrary Git operation in the main repository, dependency repositories or all repositories. |
|
159
|
+
| inspect | Outputs various information about multirepo-enabled repos. For use in scripting and CI scenarios. |
|
160
|
+
| install | Clones and checks out dependencies as defined in the version-controlled multirepo metadata files and installs git-multirepo's local git hooks. Idempotent for a given main repo checkout. |
|
161
|
+
| merge | Performs a git merge on all dependencies and the main repo, in the proper order. |
|
162
|
+
| open | Opens repositories in the OS's file explorer. |
|
163
|
+
| remove | Removes the specified dependency from multirepo. |
|
164
|
+
| update | Force-updates the multirepo tracking files. |
|
165
|
+
| uninit | Removes all traces of multirepo in the current multirepo repository. |
|
166
|
+
|
167
|
+
To read more about each command, use the `--help` flag (e.g. `$ multi clone --help`).
|
168
|
+
|
169
|
+
## Metadata
|
170
|
+
|
171
|
+
git-multirepo stores all of its metadata in three files:
|
172
|
+
|
173
|
+
| File | Format | Updated | Contents |
|
174
|
+
|------|--------|---------|----------|
|
175
|
+
| .multirepo | YAML | at *initialization*, on *add* on *remove* | A collection of your project's dependencies. For each dependency, stores its **local path** relative to the main repo and the **remote URL** your project depends upon.
|
176
|
+
| .multirepo.lock | YAML | before each commit | For each dependency, stores the **commit id** 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. |
|
177
|
+
| .multirepo.meta | YAML | before each commit | Various git-multirepo metadata, such as the **git-multirepo version** that the last commit was performed with. |
|
178
|
+
|
179
179
|
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
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require "bundler/gem_tasks"
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/multi
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "claide"
|
4
|
-
require "multirepo/commands/commands"
|
5
|
-
|
6
|
-
trap("INT") do
|
7
|
-
puts "\rAbort, abort!!" # \r hides the interrupt control characters
|
8
|
-
exit
|
9
|
-
end
|
10
|
-
|
11
|
-
MultiRepo::Command.run(ARGV)
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "claide"
|
4
|
+
require "multirepo/commands/commands"
|
5
|
+
|
6
|
+
trap("INT") do
|
7
|
+
puts "\rAbort, abort!!" # \r hides the interrupt control characters
|
8
|
+
exit
|
9
|
+
end
|
10
|
+
|
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
|
data/git-multirepo.gemspec
CHANGED
@@ -1,31 +1,31 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require "multirepo/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 = "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", "~> 1.0.1"
|
27
|
-
spec.add_runtime_dependency "colored", "~> 1.2"
|
28
|
-
spec.add_runtime_dependency "os", "~> 0.9.6"
|
29
|
-
spec.add_runtime_dependency "terminal-table", "~> 1.7.3"
|
30
|
-
spec.add_runtime_dependency "naturally", "~> 2.1"
|
31
|
-
end
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "multirepo/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 = "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", "~> 1.0.1"
|
27
|
+
spec.add_runtime_dependency "colored", "~> 1.2"
|
28
|
+
spec.add_runtime_dependency "os", "~> 0.9.6"
|
29
|
+
spec.add_runtime_dependency "terminal-table", "~> 1.7.3"
|
30
|
+
spec.add_runtime_dependency "naturally", "~> 2.1"
|
31
|
+
end
|
data/lib/git-multirepo.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
require "multirepo/utility/console"
|
2
|
-
require "multirepo/hooks/pre-commit-hook"
|
3
|
-
require "multirepo/hooks/post-commit-hook"
|
1
|
+
require "multirepo/utility/console"
|
2
|
+
require "multirepo/hooks/pre-commit-hook"
|
3
|
+
require "multirepo/hooks/post-commit-hook"
|
@@ -1,51 +1,51 @@
|
|
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
|
-
ensure_in_work_tree
|
25
|
-
ensure_multirepo_enabled
|
26
|
-
ensure_repo_valid
|
27
|
-
|
28
|
-
config_file = ConfigFile.new(".")
|
29
|
-
entry = ConfigEntry.new(Repo.new(@path))
|
30
|
-
|
31
|
-
if config_file.entry_exists?(entry)
|
32
|
-
Console.log_info("There is already an entry for '#{@path}' in the .multirepo file")
|
33
|
-
else
|
34
|
-
config_file.add_entry(entry)
|
35
|
-
Console.log_step("Added '#{@path}' to the .multirepo file")
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def ensure_repo_valid
|
40
|
-
fail MultiRepoException, "The provided path is not a direct sibling of the main repository" unless validate_is_sibling_repo(@path)
|
41
|
-
fail MultiRepoException, "There is no folder at path '#{@path}'" unless Dir.exist?(@path)
|
42
|
-
fail MultiRepoException, "'#{@path}' is not a repository" unless Repo.new(@path).exists?
|
43
|
-
end
|
44
|
-
|
45
|
-
def validate_is_sibling_repo(path)
|
46
|
-
parent_dir = File.expand_path("..")
|
47
|
-
path = File.expand_path("..", path)
|
48
|
-
return parent_dir == path
|
49
|
-
end
|
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
|
+
ensure_in_work_tree
|
25
|
+
ensure_multirepo_enabled
|
26
|
+
ensure_repo_valid
|
27
|
+
|
28
|
+
config_file = ConfigFile.new(".")
|
29
|
+
entry = ConfigEntry.new(Repo.new(@path))
|
30
|
+
|
31
|
+
if config_file.entry_exists?(entry)
|
32
|
+
Console.log_info("There is already an entry for '#{@path}' in the .multirepo file")
|
33
|
+
else
|
34
|
+
config_file.add_entry(entry)
|
35
|
+
Console.log_step("Added '#{@path}' to the .multirepo file")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def ensure_repo_valid
|
40
|
+
fail MultiRepoException, "The provided path is not a direct sibling of the main repository" unless validate_is_sibling_repo(@path)
|
41
|
+
fail MultiRepoException, "There is no folder at path '#{@path}'" unless Dir.exist?(@path)
|
42
|
+
fail MultiRepoException, "'#{@path}' is not a repository" unless Repo.new(@path).exists?
|
43
|
+
end
|
44
|
+
|
45
|
+
def validate_is_sibling_repo(path)
|
46
|
+
parent_dir = File.expand_path("..")
|
47
|
+
path = File.expand_path("..", path)
|
48
|
+
return parent_dir == path
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|