braid 0.7.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|