braid 1.0.22 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -2
  3. data/README.md +48 -0
  4. data/_config.yml +1 -0
  5. data/bin/braid +44 -7
  6. data/braid.gemspec +4 -0
  7. data/config_versions.md +58 -0
  8. data/lib/braid.rb +7 -0
  9. data/lib/braid/command.rb +7 -7
  10. data/lib/braid/commands/add.rb +3 -3
  11. data/lib/braid/commands/diff.rb +7 -14
  12. data/lib/braid/commands/push.rb +10 -4
  13. data/lib/braid/commands/setup.rb +5 -1
  14. data/lib/braid/commands/status.rb +5 -1
  15. data/lib/braid/commands/update.rb +6 -15
  16. data/lib/braid/commands/upgrade_config.rb +56 -0
  17. data/lib/braid/config.rb +166 -27
  18. data/lib/braid/mirror.rb +111 -11
  19. data/lib/braid/operations.rb +51 -35
  20. data/lib/braid/version.rb +1 -1
  21. data/spec/config_spec.rb +2 -2
  22. data/spec/fixtures/shiny-conf-1.0.9-lock/.braids.json +10 -0
  23. data/spec/fixtures/shiny-conf-1.0.9-lock/expected.braids.json +9 -0
  24. data/spec/fixtures/shiny-conf-1.0.9-lock/skit1/layouts/layout.liquid +219 -0
  25. data/spec/fixtures/shiny-conf-1.0.9-lock/skit1/preview.png +0 -0
  26. data/spec/fixtures/shiny-conf-breaking-changes/.braids +14 -0
  27. data/spec/fixtures/shiny-conf-breaking-changes/Spoon-Knife/README.md +9 -0
  28. data/spec/fixtures/shiny-conf-breaking-changes/Spoon-Knife/index.html +20 -0
  29. data/spec/fixtures/shiny-conf-breaking-changes/Spoon-Knife/styles.css +17 -0
  30. data/spec/fixtures/shiny-conf-breaking-changes/expected.braids.json +10 -0
  31. data/spec/fixtures/shiny-conf-breaking-changes/skit1/layouts/layout.liquid +219 -0
  32. data/spec/fixtures/shiny-conf-breaking-changes/skit1/preview.png +0 -0
  33. data/spec/fixtures/shiny-conf-future/.braids.json +10 -0
  34. data/spec/fixtures/shiny-conf-future/skit1/layouts/layout.liquid +219 -0
  35. data/spec/fixtures/shiny-conf-future/skit1/preview.png +0 -0
  36. data/spec/fixtures/shiny-conf-json-old-name/.braids +9 -0
  37. data/spec/fixtures/shiny-conf-json-old-name/expected.braids.json +10 -0
  38. data/spec/fixtures/shiny-conf-json-old-name/skit1/layouts/layout.liquid +219 -0
  39. data/spec/fixtures/shiny-conf-json-old-name/skit1/preview.png +0 -0
  40. data/spec/fixtures/shiny-conf-yaml/.braids +8 -0
  41. data/spec/fixtures/shiny-conf-yaml/expected.braids.json +10 -0
  42. data/spec/fixtures/shiny-conf-yaml/skit1/layouts/layout.liquid +219 -0
  43. data/spec/fixtures/shiny-conf-yaml/skit1/preview.png +0 -0
  44. data/spec/fixtures/shiny/skit-layout.liquid.test +2 -0
  45. data/spec/fixtures/shiny/skit1.test +2 -0
  46. data/spec/fixtures/skit1.1x/layouts/layout.liquid +219 -0
  47. data/spec/integration/adding_spec.rb +82 -34
  48. data/spec/integration/config_versioning_spec.rb +222 -0
  49. data/spec/integration/diff_spec.rb +173 -9
  50. data/spec/integration/integration_helper.rb +23 -5
  51. data/spec/integration/push_spec.rb +57 -4
  52. data/spec/integration/remove_spec.rb +27 -5
  53. data/spec/integration/updating_spec.rb +104 -19
  54. metadata +73 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ab9eeee697a55d6b0126b0b7fc83f1ef5bcd72ce
4
- data.tar.gz: 592d7f2a02f89a1afa3accb32c3e8ab910982ba8
3
+ metadata.gz: aac9e65e786b934873c38a987ca055e3decb4fec
4
+ data.tar.gz: 0b653e5049e2961bfef4f4d0fa3ad7b9a35ac91a
5
5
  SHA512:
6
- metadata.gz: e0e1aab9e20c6be6b449c14e9c8c9b768e71b58bc81634e737b83358172a39b62044f92cc0dc8e7fb2eb48045ea4b3dfed67b19ac22faae335619040265a2869
7
- data.tar.gz: 644e0aced9a33f47309f1bf6df77c31e7098b9292e3133a1e44e7f5852b7f2d749f02147425b73f949f9e39d444cc1aa156897613450953906f073ff062d3231
6
+ metadata.gz: afe093d29c687ecedae3f8442cd8b2a309b1d3a8066d9c9f14fdb3b74b1c001040e5aa2f301d14a266fe02071b2728a8791cde094d68007380eaea9f8c4b6a3e
7
+ data.tar.gz: f8340b68dcb924a2af19da115eda4ae1b9eea2e8756159e9ffb6092c5625605c83da6df13cd561db47db760c4c5cec9f984ecce1d511a5d2b271563d57036e2e
data/Gemfile CHANGED
@@ -1,5 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
-
5
- gem 'rake'
data/README.md CHANGED
@@ -144,6 +144,54 @@ Go back to tracking a particular branch.
144
144
 
145
145
  braid diff vendor/rails
146
146
 
147
+ ## Braid version compatibility
148
+
149
+ Since Braid has been regularly changing the configuration format and adding new
150
+ features that some projects may choose to rely on, and somewhat less often
151
+ making breaking changes in how the configuration is handled, problems can arise
152
+ if different developers work on the same project using different versions of
153
+ Braid. Since version 1.1.0, Braid refuses to operate if it detects potentially
154
+ problematic version skew. If this happens, Braid will tell you what you can do.
155
+ If you'd like an overview of what to expect, read on.
156
+
157
+ Roughly speaking, the `.braids.json` configuration file contains a configuration
158
+ version number that corresponds to a range of compatible Braid minor versions
159
+ (`x.y`). "Patch" upgrades to Braid (i.e., `x.y.z` -> `x.y.(z+1)`) will never
160
+ (intentionally!) have configuration compatibility implications and are always
161
+ recommended as they may fix critical bugs.
162
+
163
+ If you use a Braid version too old for your configuration file, Braid will
164
+ direct you to the [configuration version history page](config_versions.md) with
165
+ instructions to upgrade Braid. If you use a Braid version too new, Braid will
166
+ tell you how you can upgrade your configuration file or find a compatible older
167
+ Braid version to use. (As an exception, a newer version of Braid can run
168
+ read-only commands on an older configuration file without upgrading it if there
169
+ are no breaking changes.) If you upgrade your configuration file, then other
170
+ developers on the project may need to upgrade Braid. Braid does not support
171
+ downgrading a configuration file, though you can revert the commit that upgraded
172
+ it if you haven't made any subsequent changes to the configuration.
173
+
174
+ If you work on multiple projects, you may need to install multiple versions of
175
+ Braid and manually run the correct version for each project. Fortunately, the
176
+ RubyGems system makes this reasonably straightforward.
177
+
178
+ Another approach is to standardize the Braid version for a project by listing
179
+ Braid in a `Gemfile` (either checking in `Gemfile.lock` or using a version
180
+ constraint in the `Gemfile`) and run the project's version of Braid via
181
+ [Bundler](http://bundler.io/) with `bundle exec braid`. Even non-Ruby projects
182
+ can do this if it's acceptable to have a `Gemfile` and `Gemfile.lock`. Ruby
183
+ projects that don't want Braid to interact with their other gems can potentially
184
+ put the `Gemfile` in a subdirectory and provide a wrapper script for `bundle`
185
+ that sets the `BUNDLE_GEMFILE` environment variable. We do not yet have enough
186
+ experience with this approach to make a firm recommendation for or against it.
187
+
188
+ This is the best design we could find to prevent surprises and adequately
189
+ support normal development processes while minimizing the additional maintenance
190
+ cost of the version compatibility mechanism. We want to have a scheme in place
191
+ that is robust enough to make it reasonable to encourage serious adoption of
192
+ Braid, yet we don't want to spend extra work adding conveniences until there's
193
+ evidence of sufficient demand for them.
194
+
147
195
  ## Contributing
148
196
 
149
197
  We appreciate any patches, error reports and usage ideas you may have. Please
@@ -0,0 +1 @@
1
+ theme: jekyll-theme-tactile
data/bin/braid CHANGED
@@ -64,7 +64,7 @@ Main {
64
64
  run {
65
65
  check_no_extra_args!
66
66
  Braid.verbose = verbose
67
- Braid::Command.run(:add, url, {'path' => local_path, 'branch' => branch, 'tag' => tag, 'revision' => revision, 'remote_path' => path})
67
+ Braid::Command.run(:Add, url, {'path' => local_path, 'branch' => branch, 'tag' => tag, 'revision' => revision, 'remote_path' => path})
68
68
  }
69
69
  }
70
70
 
@@ -97,7 +97,7 @@ Main {
97
97
  'keep' => keep
98
98
  }
99
99
  Braid.verbose = verbose
100
- Braid::Command.run(:update, local_path, options)
100
+ Braid::Command.run(:Update, local_path, options)
101
101
  }
102
102
  }
103
103
 
@@ -122,7 +122,7 @@ Main {
122
122
  :keep => keep
123
123
  }
124
124
  Braid.verbose = verbose
125
- Braid::Command.run(:remove, local_path, options)
125
+ Braid::Command.run(:Remove, local_path, options)
126
126
  }
127
127
  }
128
128
 
@@ -149,7 +149,7 @@ Main {
149
149
  'git_diff_args' => @argv
150
150
  }
151
151
  Braid.verbose = verbose
152
- Braid::Command.run(:diff, local_path, options)
152
+ Braid::Command.run(:Diff, local_path, options)
153
153
  }
154
154
  }
155
155
 
@@ -167,7 +167,7 @@ Main {
167
167
  'branch' => branch
168
168
  }
169
169
  Braid.verbose = verbose
170
- Braid::Command.run(:push, local_path, options)
170
+ Braid::Command.run(:Push, local_path, options)
171
171
  }
172
172
  }
173
173
 
@@ -182,7 +182,7 @@ Main {
182
182
  check_no_extra_args!
183
183
  Braid.verbose = verbose
184
184
  Braid.force = force
185
- Braid::Command.run(:setup, local_path)
185
+ Braid::Command.run(:Setup, local_path)
186
186
  }
187
187
  }
188
188
 
@@ -203,7 +203,44 @@ Main {
203
203
  run {
204
204
  check_no_extra_args!
205
205
  Braid.verbose = verbose
206
- Braid::Command.run(:status, local_path)
206
+ Braid::Command.run(:Status, local_path)
207
+ }
208
+ }
209
+
210
+ mode('upgrade-config') {
211
+ description <<-DESC
212
+ Upgrade your project's Braid configuration to the latest configuration version.
213
+ Other commands will notify you when you need to do this. This may make older
214
+ versions of Braid unable to read the configuration and may introduce breaking
215
+ changes in how the configuration is handled by Braid. An upgrade that
216
+ introduces breaking changes will not be performed without the
217
+ --allow-breaking-changes option.
218
+ DESC
219
+
220
+ mixin :option_verbose
221
+
222
+ option('dry-run') {
223
+ optional
224
+ desc 'Explain the consequences of the upgrade without performing it.'
225
+ attr :dry_run
226
+ }
227
+
228
+ option('allow-breaking-changes') {
229
+ optional
230
+ desc <<-DESC
231
+ Perform the upgrade even if it involves breaking changes.
232
+ DESC
233
+ attr :allow_breaking_changes
234
+ }
235
+
236
+ run {
237
+ check_no_extra_args!
238
+ options = {
239
+ 'dry_run' => dry_run,
240
+ 'allow_breaking_changes' => allow_breaking_changes
241
+ }
242
+ Braid.verbose = verbose
243
+ Braid::Command.run(:UpgradeConfig, options)
207
244
  }
208
245
  }
209
246
 
@@ -27,7 +27,11 @@ Gem::Specification.new do |s|
27
27
 
28
28
  s.required_ruby_version = '>= 2.2.0'
29
29
  s.add_dependency(%q<main>, ['>= 4.7.3'])
30
+ # XXX: Minimum version?
31
+ s.add_dependency(%q<json>)
30
32
 
31
33
  s.add_development_dependency(%q<rspec>, ['>= 3.4.4'])
32
34
  s.add_development_dependency(%q<mocha>, ['>= 0.9.11'])
35
+ s.add_development_dependency(%q<rake>)
36
+ s.add_development_dependency(%q<bundler>)
33
37
  end
@@ -0,0 +1,58 @@
1
+ # Braid configuration version history
2
+
3
+ The Braid configuration file (`.braids.json`) contains a configuration version
4
+ number that indicates the format of the configuration file and the Braid
5
+ features required by the project. You'll be directed to this page if you use a
6
+ version of Braid that does not support the project's configuration version; see
7
+ [the readme](README.md#braid-version-compatibility) for more information about
8
+ the versioning scheme.
9
+
10
+ To get a compatible version of Braid:
11
+
12
+ 1. First check if the project has its own instructions to install and run Braid,
13
+ and if so, follow them instead.
14
+ 2. Look up the Braid versions corresponding to your current configuration
15
+ version in the table below.
16
+ 3. Run `gem query --remote --all --exact braid` to get a list of all existing
17
+ versions of Braid, and choose one that is compatible with your configuration
18
+ version (you probably want the newest such version); call it `x.y.z`.
19
+ 4. Run `gem install braid --version x.y.z` to install the chosen version of
20
+ Braid.
21
+ 5. Run Braid as `braid _x.y.z_` (that's the chosen version surrounded by literal
22
+ underscores) followed by your desired arguments.
23
+
24
+ <table border="border">
25
+ <tr>
26
+ <th>Config. version</th>
27
+ <th>Braid versions</th>
28
+ <th>Changes since previous</th>
29
+ </tr>
30
+ <tr>
31
+ <td>1</td>
32
+ <td>1.1.x</td>
33
+ <td>(Various)</td>
34
+ </tr>
35
+ <tr>
36
+ <td>"0"</td>
37
+ <td colspan="2">
38
+ (Braid versions earlier than 1.1.0 have varying configuration formats and
39
+ features and do not have a well-defined compatibility scheme. Braid 1.1.0 and
40
+ newer refer to all of these formats as version "0" and are capable of correctly
41
+ upgrading most of them. We recommend upgrading to Braid 1.1.0 or newer if you
42
+ can.)
43
+ </td>
44
+ </tr>
45
+ </table>
46
+
47
+ <style>
48
+ header, section#downloads, .inner > hr {
49
+ display: none;
50
+ }
51
+ .inner {
52
+ padding-top: 35px; /* same as header when it is visible */
53
+ }
54
+ th, td {
55
+ border: 1px solid #6d6d6d;
56
+ padding: 2px;
57
+ }
58
+ </style>
@@ -35,6 +35,12 @@ module Braid
35
35
  value if value != self.class.name
36
36
  end
37
37
  end
38
+
39
+ class InternalError < BraidError
40
+ def message
41
+ "internal error: #{super}"
42
+ end
43
+ end
38
44
  end
39
45
 
40
46
  require 'braid/operations_lite'
@@ -49,3 +55,4 @@ require 'braid/commands/remove'
49
55
  require 'braid/commands/setup'
50
56
  require 'braid/commands/update'
51
57
  require 'braid/commands/status'
58
+ require 'braid/commands/upgrade_config'
@@ -10,7 +10,7 @@ module Braid
10
10
  verify_git_version!
11
11
  check_working_dir!
12
12
 
13
- klass = Commands.const_get(command.to_s.capitalize)
13
+ klass = Commands.const_get(command.to_s)
14
14
  klass.new.run(*args)
15
15
 
16
16
  rescue BraidError => error
@@ -32,7 +32,7 @@ module Braid
32
32
  end
33
33
 
34
34
  def config
35
- @config ||= Config.new
35
+ @config ||= Config.new({'mode' => config_mode})
36
36
  end
37
37
 
38
38
  def verbose?
@@ -45,11 +45,15 @@ module Braid
45
45
 
46
46
  private
47
47
 
48
+ def config_mode
49
+ Config::MODE_MAY_WRITE
50
+ end
51
+
48
52
  def setup_remote(mirror)
49
53
  existing_force = Braid.force
50
54
  begin
51
55
  Braid.force = true
52
- Command.run(:setup, mirror.path)
56
+ Command.run(:Setup, mirror.path)
53
57
  ensure
54
58
  Braid.force = existing_force
55
59
  end
@@ -133,9 +137,5 @@ module Braid
133
137
  new_revision
134
138
  end
135
139
  end
136
-
137
- def determine_target_revision(mirror, new_revision)
138
- git.rev_parse(mirror.versioned_path(new_revision))
139
- end
140
140
  end
141
141
  end
@@ -21,10 +21,10 @@ module Braid
21
21
  setup_remote(mirror)
22
22
  mirror.fetch
23
23
 
24
- new_revision = validate_new_revision(mirror, options['revision'])
25
- target_revision = determine_target_revision(mirror, new_revision)
24
+ new_revision = validate_new_revision(mirror, options['revision'])
25
+ target_item = mirror.upstream_item_for_revision(new_revision)
26
26
 
27
- git.read_tree_prefix_u(target_revision, mirror.path)
27
+ git.add_item_to_index(target_item, mirror.path, true)
28
28
 
29
29
  mirror.revision = new_revision
30
30
  config.update(mirror)
@@ -5,12 +5,12 @@ module Braid
5
5
  path ? diff_one(path, options) : diff_all(options)
6
6
  end
7
7
 
8
- protected
8
+ private
9
9
 
10
10
  def diff_all(options = {})
11
11
  # We don't want "git diff" to invoke the pager once for each mirror.
12
12
  # TODO: Invoke the default pager once for the entire output.
13
- Operations::with_modified_environment({"GIT_PAGER" => ''}) do
13
+ Operations::with_modified_environment({ 'GIT_PAGER' => ''}) do
14
14
  config.mirrors.each do |path|
15
15
  separator
16
16
  msg "Diffing #{path}\n"
@@ -33,23 +33,16 @@ module Braid
33
33
  setup_remote(mirror)
34
34
  mirror.fetch_base_revision_if_missing
35
35
 
36
- # We do not need to spend the time to copy the content outside the
37
- # mirror from HEAD because --relative will exclude it anyway. Rename
38
- # detection seems to apply only to the files included in the diff, so we
39
- # shouldn't have another bug like
40
- # https://github.com/cristibalan/braid/issues/41.
41
- base_tree = git.make_tree_with_subtree(nil, mirror.path,
42
- mirror.versioned_path(mirror.base_revision))
43
- # Git 1.7.2.2 release notes mention a bug when --relative is used
44
- # without a trailing slash, and our minimum git version is 1.6.0, so
45
- # attempt to work around the bug here.
46
- #
47
36
  # XXX: Warn if the user specifies file paths that are outside the
48
37
  # mirror? Currently, they just won't match anything.
49
- git.diff_to_stdout("--relative=#{mirror.path}/", base_tree, options['git_diff_args'])
38
+ git.diff_to_stdout(*mirror.diff_args(*options['git_diff_args']))
50
39
 
51
40
  clear_remote(mirror, options)
52
41
  end
42
+
43
+ def config_mode
44
+ Config::MODE_READ_ONLY
45
+ end
53
46
  end
54
47
  end
55
48
  end
@@ -33,7 +33,7 @@ module Braid
33
33
  clear_remote(mirror, options)
34
34
  return
35
35
  end
36
- local_mirror_tree = git.rev_parse("HEAD:#{mirror.path}")
36
+ local_mirror_item = git.get_tree_item('HEAD', mirror.path)
37
37
 
38
38
  odb_paths = [File.expand_path(git.repo_file_path('objects'))]
39
39
  if File.exist?(mirror.cached_url)
@@ -51,11 +51,13 @@ module Braid
51
51
  end
52
52
  user_name = git.config(%w(--local --get user.name))
53
53
  user_email = git.config(%w(--local --get user.email))
54
+ commit_gpgsign = git.config(%w(--local --get commit.gpgsign))
54
55
  Dir.chdir(clone_dir) do
55
56
  msg 'Cloning mirror with local changes.'
56
57
  git.init
57
58
  git.config(['--local', 'user.name', "\"#{user_name}\""]) if user_name
58
59
  git.config(['--local', 'user.email', "\"#{user_email}\""]) if user_email
60
+ git.config(['--local', 'commit.gpgsign', commit_gpgsign]) if commit_gpgsign
59
61
  # Adding other repositories as alternates is safe (we don't have to
60
62
  # worry about them being moved or deleted during the lifetime of this
61
63
  # temporary repository) and faster than fetching from them. We don't
@@ -68,9 +70,7 @@ module Braid
68
70
  git.fetch(remote_url, mirror.remote_ref)
69
71
  git.checkout(base_revision)
70
72
  git.rm_r(mirror.remote_path || '.')
71
- # Yes, when mirror.remote_path is unset, "git read-tree --prefix=/"
72
- # seems to work. :/
73
- git.read_tree_prefix_u(local_mirror_tree, mirror.remote_path || '')
73
+ git.add_item_to_index(local_mirror_item, mirror.remote_path || '', true)
74
74
  system('git commit -v')
75
75
  msg "Pushing changes to remote branch #{branch}."
76
76
  git.push(remote_url, "HEAD:refs/heads/#{branch}")
@@ -80,5 +80,11 @@ module Braid
80
80
  clear_remote(mirror, options)
81
81
  end
82
82
  end
83
+
84
+ private
85
+
86
+ def config_mode
87
+ Config::MODE_READ_ONLY # Surprisingly enough.
88
+ end
83
89
  end
84
90
  end
@@ -5,7 +5,7 @@ module Braid
5
5
  path ? setup_one(path) : setup_all
6
6
  end
7
7
 
8
- protected
8
+ private
9
9
 
10
10
  def setup_all
11
11
  msg 'Setting up all mirrors.'
@@ -31,6 +31,10 @@ module Braid
31
31
  url = use_local_cache? ? git_cache.path(mirror.url) : mirror.url
32
32
  git.remote_add(mirror.remote, url)
33
33
  end
34
+
35
+ def config_mode
36
+ Config::MODE_READ_ONLY
37
+ end
34
38
  end
35
39
  end
36
40
  end
@@ -5,7 +5,7 @@ module Braid
5
5
  path ? status_one(path, options) : status_all(options)
6
6
  end
7
7
 
8
- protected
8
+ private
9
9
 
10
10
  def status_all(options = {})
11
11
  print "\n"
@@ -41,6 +41,10 @@ module Braid
41
41
  print "\n"
42
42
  clear_remote(mirror, options)
43
43
  end
44
+
45
+ def config_mode
46
+ Config::MODE_READ_ONLY
47
+ end
44
48
  end
45
49
  end
46
50
  end