braid 0.7.1 → 1.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +4 -1
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/CONTRIBUTING.md +26 -0
- data/Gemfile +1 -2
- data/LICENSE +3 -1
- data/README.md +90 -51
- data/Rakefile +6 -14
- data/bin/braid +9 -37
- data/braid.gemspec +11 -11
- data/lib/braid.rb +13 -12
- data/lib/braid/command.rb +9 -42
- data/lib/braid/commands/add.rb +6 -8
- data/lib/braid/commands/list.rb +11 -6
- data/lib/braid/commands/push.rb +5 -5
- data/lib/braid/commands/remove.rb +1 -3
- data/lib/braid/commands/setup.rb +4 -8
- data/lib/braid/commands/update.rb +12 -14
- data/lib/braid/config.rb +38 -43
- data/lib/braid/mirror.rb +19 -63
- data/lib/braid/operations.rb +51 -90
- data/lib/braid/version.rb +1 -1
- data/{test/config_test.rb → spec/config_spec.rb} +14 -17
- data/{test → spec}/fixtures/shiny/README +0 -0
- data/{test → spec}/fixtures/skit1.1/layouts/layout.liquid +0 -0
- data/{test → spec}/fixtures/skit1.2/layouts/layout.liquid +0 -0
- data/{test → spec}/fixtures/skit1/layouts/layout.liquid +0 -0
- data/{test → spec}/fixtures/skit1/preview.png +0 -0
- data/spec/integration/adding_spec.rb +43 -0
- data/{test/integration/updating_test.rb → spec/integration/updating_spec.rb} +2 -41
- data/spec/integration_helper.rb +47 -0
- data/{test/mirror_test.rb → spec/mirror_spec.rb} +6 -33
- data/{test/operations_test.rb → spec/operations_spec.rb} +2 -2
- data/{test → spec}/test_helper.rb +6 -2
- metadata +86 -123
- data/lib/core_ext.rb +0 -13
- data/test/braid_test.rb +0 -7
- data/test/integration/adding_test.rb +0 -80
- data/test/integration_helper.rb +0 -70
data/lib/braid/mirror.rb
CHANGED
@@ -1,21 +1,15 @@
|
|
1
1
|
module Braid
|
2
2
|
class Mirror
|
3
|
-
|
4
|
-
ATTRIBUTES = %w(url remote type branch squashed revision lock)
|
3
|
+
ATTRIBUTES = %w(url remote branch squashed revision lock)
|
5
4
|
|
6
5
|
class UnknownType < BraidError
|
7
6
|
def message
|
8
7
|
"unknown type: #{super}"
|
9
8
|
end
|
10
9
|
end
|
11
|
-
class CannotGuessType < BraidError
|
12
|
-
def message
|
13
|
-
"cannot guess type: #{super}"
|
14
|
-
end
|
15
|
-
end
|
16
10
|
class PathRequired < BraidError
|
17
11
|
def message
|
18
|
-
|
12
|
+
'path is required'
|
19
13
|
end
|
20
14
|
end
|
21
15
|
|
@@ -31,27 +25,16 @@ module Braid
|
|
31
25
|
def self.new_from_options(url, options = {})
|
32
26
|
url = url.sub(/\/$/, '')
|
33
27
|
|
34
|
-
branch = options[
|
35
|
-
|
36
|
-
if type = options["type"] || extract_type_from_url(url)
|
37
|
-
raise UnknownType, type unless TYPES.include?(type)
|
38
|
-
else
|
39
|
-
raise CannotGuessType, url
|
40
|
-
end
|
28
|
+
branch = options['branch'] || 'master'
|
41
29
|
|
42
|
-
unless path = options[
|
30
|
+
unless path = options['path'] || extract_path_from_url(url)
|
43
31
|
raise PathRequired
|
44
32
|
end
|
45
33
|
|
46
|
-
|
47
|
-
|
48
|
-
end
|
49
|
-
|
50
|
-
remote = "#{branch}/braid/#{path}".gsub("_", '-') # stupid git svn changes all _ to ., weird
|
51
|
-
squashed = !options["full"]
|
52
|
-
branch = nil if type == "svn"
|
34
|
+
remote = "#{branch}/braid/#{path}"
|
35
|
+
squashed = !options['full']
|
53
36
|
|
54
|
-
attributes = {
|
37
|
+
attributes = {'url' => url, 'remote' => remote, 'branch' => branch, 'squashed' => squashed}
|
55
38
|
self.new(path, attributes)
|
56
39
|
end
|
57
40
|
|
@@ -59,11 +42,6 @@ module Braid
|
|
59
42
|
path == comparison.path && attributes == comparison.attributes
|
60
43
|
end
|
61
44
|
|
62
|
-
def type
|
63
|
-
# override Object#type
|
64
|
-
attributes["type"]
|
65
|
-
end
|
66
|
-
|
67
45
|
def locked?
|
68
46
|
!!lock
|
69
47
|
end
|
@@ -79,23 +57,20 @@ module Braid
|
|
79
57
|
if squashed?
|
80
58
|
!!base_revision && git.merge_base(commit, base_revision) == commit
|
81
59
|
else
|
82
|
-
git.merge_base(commit,
|
60
|
+
git.merge_base(commit, 'HEAD') == commit
|
83
61
|
end
|
84
62
|
end
|
85
63
|
|
86
64
|
def diff
|
65
|
+
fetch
|
87
66
|
remote_hash = git.rev_parse("#{base_revision}:")
|
88
67
|
local_hash = git.tree_hash(path)
|
89
|
-
remote_hash != local_hash ? git.diff_tree(remote_hash, local_hash) :
|
68
|
+
remote_hash != local_hash ? git.diff_tree(remote_hash, local_hash) : ''
|
90
69
|
end
|
91
70
|
|
92
71
|
def fetch
|
93
|
-
|
94
|
-
|
95
|
-
git.fetch(remote)
|
96
|
-
else
|
97
|
-
git_svn.fetch(remote)
|
98
|
-
end
|
72
|
+
git_cache.fetch(url) if cached?
|
73
|
+
git.fetch(remote)
|
99
74
|
end
|
100
75
|
|
101
76
|
def cached?
|
@@ -104,11 +79,7 @@ module Braid
|
|
104
79
|
|
105
80
|
def base_revision
|
106
81
|
if revision
|
107
|
-
|
108
|
-
git.rev_parse(revision)
|
109
|
-
else
|
110
|
-
git_svn.commit_hash(remote, revision)
|
111
|
-
end
|
82
|
+
git.rev_parse(revision)
|
112
83
|
else
|
113
84
|
inferred_revision
|
114
85
|
end
|
@@ -119,10 +90,10 @@ module Braid
|
|
119
90
|
end
|
120
91
|
|
121
92
|
def remote
|
122
|
-
if (attributes[
|
123
|
-
attributes[
|
93
|
+
if (attributes['remote'] && attributes['remote'] =~ /^braid\//)
|
94
|
+
attributes['remote'] = "#{branch}/#{attributes['remote']}"
|
124
95
|
else
|
125
|
-
attributes[
|
96
|
+
attributes['remote']
|
126
97
|
end
|
127
98
|
end
|
128
99
|
|
@@ -141,8 +112,8 @@ module Braid
|
|
141
112
|
end
|
142
113
|
|
143
114
|
def inferred_revision
|
144
|
-
local_commits = git.rev_list(
|
145
|
-
remote_hashes = git.rev_list("--pretty=format:\"%T\"", remote).split(
|
115
|
+
local_commits = git.rev_list('HEAD', "-- #{path}").split("\n")
|
116
|
+
remote_hashes = git.rev_list("--pretty=format:\"%T\"", remote).split('commit ').map do |chunk|
|
146
117
|
chunk.split("\n", 2).map { |value| value.strip }
|
147
118
|
end
|
148
119
|
hash = nil
|
@@ -156,28 +127,13 @@ module Braid
|
|
156
127
|
hash
|
157
128
|
end
|
158
129
|
|
159
|
-
def self.extract_type_from_url(url)
|
160
|
-
return nil unless url
|
161
|
-
url.sub!(/\/$/, '')
|
162
|
-
|
163
|
-
# check for git:// and svn:// URLs
|
164
|
-
url_scheme = url.split(":").first
|
165
|
-
return url_scheme if TYPES.include?(url_scheme)
|
166
|
-
|
167
|
-
return "svn" if url[-6..-1] == "/trunk"
|
168
|
-
return "git" if url[-4..-1] == ".git"
|
169
|
-
end
|
170
|
-
|
171
130
|
def self.extract_path_from_url(url)
|
172
131
|
return nil unless url
|
173
132
|
name = File.basename(url)
|
174
133
|
|
175
|
-
if File.extname(name) ==
|
134
|
+
if File.extname(name) == '.git'
|
176
135
|
# strip .git
|
177
136
|
name[0..-5]
|
178
|
-
elsif name == "trunk"
|
179
|
-
# use parent
|
180
|
-
File.basename(File.dirname(url))
|
181
137
|
else
|
182
138
|
name
|
183
139
|
end
|
data/lib/braid/operations.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'singleton'
|
2
2
|
require 'rubygems'
|
3
|
-
require defined?(JRUBY_VERSION) ? 'open3' : 'open4'
|
4
3
|
require 'tempfile'
|
5
4
|
|
6
5
|
module Braid
|
6
|
+
USE_OPEN3 = defined?(JRUBY_VERSION) || Gem.win_platform?
|
7
|
+
require USE_OPEN3 ? 'open3' : 'open4'
|
8
|
+
|
7
9
|
module Operations
|
8
10
|
class ShellExecutionError < BraidError
|
9
11
|
def initialize(err = nil)
|
@@ -32,16 +34,16 @@ module Braid
|
|
32
34
|
end
|
33
35
|
class LocalChangesPresent < BraidError
|
34
36
|
def message
|
35
|
-
|
37
|
+
'local changes are present'
|
36
38
|
end
|
37
39
|
end
|
38
40
|
class MergeError < BraidError
|
39
41
|
def message
|
40
|
-
|
42
|
+
'could not merge'
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
44
|
-
# The command proxy is meant to encapsulate commands such as git,
|
46
|
+
# The command proxy is meant to encapsulate commands such as git, that work with subcommands.
|
45
47
|
class Proxy
|
46
48
|
include Singleton
|
47
49
|
|
@@ -52,12 +54,12 @@ module Braid
|
|
52
54
|
# hax!
|
53
55
|
def version
|
54
56
|
status, out, err = exec!("#{self.class.command} --version")
|
55
|
-
out.sub(/^.* version/,
|
57
|
+
out.sub(/^.* version/, '').strip
|
56
58
|
end
|
57
59
|
|
58
60
|
def require_version(required)
|
59
|
-
required = required.split(
|
60
|
-
actual = version.split(
|
61
|
+
required = required.split('.')
|
62
|
+
actual = version.split('.')
|
61
63
|
|
62
64
|
actual.each_with_index do |actual_piece, idx|
|
63
65
|
required_piece = required[idx]
|
@@ -103,14 +105,21 @@ module Braid
|
|
103
105
|
ENV['LANG'] = 'C'
|
104
106
|
|
105
107
|
out, err = nil
|
108
|
+
status, pid = 0
|
106
109
|
log(cmd)
|
107
110
|
|
108
|
-
if
|
109
|
-
|
111
|
+
if USE_OPEN3
|
112
|
+
status = nil
|
113
|
+
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thread|
|
114
|
+
# Under old jrubies this may sometimes throw an exception
|
115
|
+
stdin.close rescue nil
|
110
116
|
out = stdout.read
|
111
117
|
err = stderr.read
|
118
|
+
# Under earlier jrubies this is not correctly passed so add in check
|
119
|
+
status = wait_thread.value if wait_thread # Process::Status object returned.
|
112
120
|
end
|
113
|
-
|
121
|
+
# Handle earlier jrubies such as 1.6.7.2
|
122
|
+
status = $?.exitstatus if status.nil?
|
114
123
|
else
|
115
124
|
status = Open4.popen4(cmd) do |pid, stdin, stdout, stderr|
|
116
125
|
out = stdout.read
|
@@ -130,7 +139,7 @@ module Braid
|
|
130
139
|
end
|
131
140
|
|
132
141
|
def sh(cmd, message = nil)
|
133
|
-
message ||=
|
142
|
+
message ||= 'could not fetch' if cmd =~ /fetch/
|
134
143
|
log(cmd)
|
135
144
|
`#{cmd}`
|
136
145
|
raise ShellExecutionError, message unless $?.exitstatus == 0
|
@@ -152,11 +161,12 @@ module Braid
|
|
152
161
|
|
153
162
|
class Git < Proxy
|
154
163
|
def commit(message, *args)
|
155
|
-
cmd =
|
164
|
+
cmd = 'git commit --no-verify'
|
156
165
|
if message # allow nil
|
157
166
|
message_file = Tempfile.new("braid_commit")
|
158
167
|
message_file.print("Braid: #{message}")
|
159
168
|
message_file.flush
|
169
|
+
message_file.close
|
160
170
|
cmd << " -F #{message_file.path}"
|
161
171
|
end
|
162
172
|
cmd << " #{args.join(' ')}" unless args.empty?
|
@@ -175,7 +185,7 @@ module Braid
|
|
175
185
|
def fetch(remote = nil, *args)
|
176
186
|
args.unshift "-n #{remote}" if remote
|
177
187
|
# open4 messes with the pipes of index-pack
|
178
|
-
sh("git fetch #{args.join(' ')} 2>&1
|
188
|
+
sh("git fetch #{args.join(' ')} 2>&1 > #{Gem.win_platform? ? 'nul' : '/dev/null'}")
|
179
189
|
end
|
180
190
|
|
181
191
|
def checkout(treeish)
|
@@ -198,42 +208,38 @@ module Braid
|
|
198
208
|
|
199
209
|
# Implies tracking.
|
200
210
|
def remote_add(remote, path, branch)
|
201
|
-
invoke(:remote,
|
211
|
+
invoke(:remote, 'add', "-t #{branch} -m #{branch}", remote, path)
|
202
212
|
true
|
203
213
|
end
|
204
214
|
|
205
215
|
def remote_rm(remote)
|
206
|
-
invoke(:remote,
|
216
|
+
invoke(:remote, 'rm', remote)
|
207
217
|
true
|
208
218
|
end
|
209
219
|
|
210
|
-
# Checks git
|
220
|
+
# Checks git remotes.
|
211
221
|
def remote_url(remote)
|
212
222
|
key = "remote.#{remote}.url"
|
213
|
-
|
214
|
-
invoke(:config, key)
|
215
|
-
rescue ShellExecutionError
|
216
|
-
invoke(:config, "svn-#{key}")
|
217
|
-
end
|
223
|
+
invoke(:config, key)
|
218
224
|
rescue ShellExecutionError
|
219
225
|
nil
|
220
226
|
end
|
221
227
|
|
222
228
|
def reset_hard(target)
|
223
|
-
invoke(:reset,
|
229
|
+
invoke(:reset, '--hard', target)
|
224
230
|
true
|
225
231
|
end
|
226
232
|
|
227
233
|
# Implies no commit.
|
228
234
|
def merge_ours(opt)
|
229
|
-
invoke(:merge,
|
235
|
+
invoke(:merge, '-s ours --no-commit', opt)
|
230
236
|
true
|
231
237
|
end
|
232
238
|
|
233
239
|
# Implies no commit.
|
234
240
|
def merge_subtree(opt)
|
235
241
|
# TODO which options are needed?
|
236
|
-
invoke(:merge,
|
242
|
+
invoke(:merge, '-s subtree --no-commit --no-ff', opt)
|
237
243
|
true
|
238
244
|
rescue ShellExecutionError
|
239
245
|
raise MergeError
|
@@ -246,18 +252,22 @@ module Braid
|
|
246
252
|
raise MergeError
|
247
253
|
end
|
248
254
|
|
255
|
+
def read_ls_files(prefix)
|
256
|
+
invoke('ls-files', prefix)
|
257
|
+
end
|
258
|
+
|
249
259
|
def read_tree_prefix(treeish, prefix)
|
250
260
|
invoke(:read_tree, "--prefix=#{prefix}/ -u", treeish)
|
251
261
|
true
|
252
262
|
end
|
253
263
|
|
254
264
|
def rm_r(path)
|
255
|
-
invoke(:rm,
|
265
|
+
invoke(:rm, '-r', path)
|
256
266
|
true
|
257
267
|
end
|
258
268
|
|
259
|
-
def tree_hash(path, treeish =
|
260
|
-
out = invoke(:ls_tree, treeish,
|
269
|
+
def tree_hash(path, treeish = 'HEAD')
|
270
|
+
out = invoke(:ls_tree, treeish, '-d', path)
|
261
271
|
out.split[2]
|
262
272
|
end
|
263
273
|
|
@@ -269,7 +279,7 @@ module Braid
|
|
269
279
|
end
|
270
280
|
|
271
281
|
def status_clean?
|
272
|
-
status, out, err = exec(
|
282
|
+
status, out, err = exec('git status')
|
273
283
|
!out.split("\n").grep(/nothing to commit/).empty?
|
274
284
|
end
|
275
285
|
|
@@ -278,7 +288,7 @@ module Braid
|
|
278
288
|
end
|
279
289
|
|
280
290
|
def head
|
281
|
-
rev_parse(
|
291
|
+
rev_parse('HEAD')
|
282
292
|
end
|
283
293
|
|
284
294
|
def branch
|
@@ -287,17 +297,22 @@ module Braid
|
|
287
297
|
end
|
288
298
|
|
289
299
|
def apply(diff, *args)
|
290
|
-
err = nil
|
300
|
+
status, err = nil, nil
|
301
|
+
|
302
|
+
command = "git apply --index --whitespace=nowarn #{args.join(' ')} -"
|
291
303
|
|
292
|
-
if
|
293
|
-
Open3.popen3(
|
304
|
+
if USE_OPEN3
|
305
|
+
Open3.popen3(command) do |stdin, stdout, stderr, wait_thread|
|
294
306
|
stdin.puts(diff)
|
295
307
|
stdin.close
|
296
308
|
err = stderr.read
|
309
|
+
# Under earlier jrubies this is not correctly passed so add in check
|
310
|
+
status = wait_thread.value if wait_thread # Process::Status object returned.
|
297
311
|
end
|
298
|
-
|
312
|
+
# Handle earlier jrubies such as 1.6.7.2
|
313
|
+
status = $?.exitstatus if status.nil?
|
299
314
|
else
|
300
|
-
status = Open4.popen4(
|
315
|
+
status = Open4.popen4(command) do |pid, stdin, stdout, stderr|
|
301
316
|
stdin.puts(diff)
|
302
317
|
stdin.close
|
303
318
|
err = stderr.read
|
@@ -320,52 +335,6 @@ module Braid
|
|
320
335
|
end
|
321
336
|
end
|
322
337
|
|
323
|
-
class GitSvn < Proxy
|
324
|
-
def self.command;
|
325
|
-
"git svn";
|
326
|
-
end
|
327
|
-
|
328
|
-
def commit_hash(remote, revision)
|
329
|
-
out = invoke(:log, "--show-commit --oneline", "-r #{revision}", remote)
|
330
|
-
part = out.to_s.split("|")[1]
|
331
|
-
part.strip!
|
332
|
-
raise UnknownRevision, "r#{revision}" unless part
|
333
|
-
git.rev_parse(part)
|
334
|
-
end
|
335
|
-
|
336
|
-
def fetch(remote)
|
337
|
-
sh("git svn fetch #{remote} 2>&1 >/dev/null")
|
338
|
-
end
|
339
|
-
|
340
|
-
def init(remote, path)
|
341
|
-
invoke(:init, "-R", remote, "--id=#{remote}", path)
|
342
|
-
true
|
343
|
-
end
|
344
|
-
|
345
|
-
private
|
346
|
-
|
347
|
-
def command(name)
|
348
|
-
"#{self.class.command} #{name}"
|
349
|
-
end
|
350
|
-
|
351
|
-
def git
|
352
|
-
Git.instance
|
353
|
-
end
|
354
|
-
end
|
355
|
-
|
356
|
-
class Svn < Proxy
|
357
|
-
def clean_revision(revision)
|
358
|
-
revision.to_i if revision
|
359
|
-
end
|
360
|
-
|
361
|
-
def head_revision(path)
|
362
|
-
# not using svn info because it's retarded and doesn't show the actual last changed rev for the url
|
363
|
-
# git svn has no clue on how to get the actual HEAD revision number on it's own
|
364
|
-
status, out, err = exec!("svn log -q --limit 1 #{path}")
|
365
|
-
out.split(/\n/).find { |x| x.match /^r\d+/ }.split(" | ")[0][1..-1].to_i
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
338
|
class GitCache
|
370
339
|
include Singleton
|
371
340
|
|
@@ -383,12 +352,12 @@ module Braid
|
|
383
352
|
end
|
384
353
|
else
|
385
354
|
FileUtils.mkdir_p(local_cache_dir)
|
386
|
-
git.clone(
|
355
|
+
git.clone('--mirror', url, dir)
|
387
356
|
end
|
388
357
|
end
|
389
358
|
|
390
359
|
def path(url)
|
391
|
-
File.join(local_cache_dir, url.gsub(/[\/:@]/,
|
360
|
+
File.join(local_cache_dir, url.gsub(/[\/:@]/, '_'))
|
392
361
|
end
|
393
362
|
|
394
363
|
private
|
@@ -407,14 +376,6 @@ module Braid
|
|
407
376
|
Git.instance
|
408
377
|
end
|
409
378
|
|
410
|
-
def git_svn
|
411
|
-
GitSvn.instance
|
412
|
-
end
|
413
|
-
|
414
|
-
def svn
|
415
|
-
Svn.instance
|
416
|
-
end
|
417
|
-
|
418
379
|
def git_cache
|
419
380
|
GitCache.instance
|
420
381
|
end
|