gistore 1.0.0.rc4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +30 -0
  3. data/COPYING +340 -0
  4. data/README.md +98 -0
  5. data/exe/gistore +15 -0
  6. data/lib/gistore.rb +20 -0
  7. data/lib/gistore/cmd/add.rb +15 -0
  8. data/lib/gistore/cmd/checkout.rb +49 -0
  9. data/lib/gistore/cmd/commit.rb +171 -0
  10. data/lib/gistore/cmd/config.rb +23 -0
  11. data/lib/gistore/cmd/export-to-backups.rb +79 -0
  12. data/lib/gistore/cmd/gc.rb +15 -0
  13. data/lib/gistore/cmd/git-version.rb +14 -0
  14. data/lib/gistore/cmd/init.rb +36 -0
  15. data/lib/gistore/cmd/restore-from-backups.rb +91 -0
  16. data/lib/gistore/cmd/rm.rb +15 -0
  17. data/lib/gistore/cmd/safe-commands.rb +53 -0
  18. data/lib/gistore/cmd/status.rb +40 -0
  19. data/lib/gistore/cmd/task.rb +85 -0
  20. data/lib/gistore/cmd/version.rb +27 -0
  21. data/lib/gistore/config.rb +13 -0
  22. data/lib/gistore/config/gistore.yml +1 -0
  23. data/lib/gistore/error.rb +6 -0
  24. data/lib/gistore/repo.rb +683 -0
  25. data/lib/gistore/runner.rb +43 -0
  26. data/lib/gistore/templates/description +1 -0
  27. data/lib/gistore/templates/hooks/applypatch-msg.sample +15 -0
  28. data/lib/gistore/templates/hooks/commit-msg.sample +24 -0
  29. data/lib/gistore/templates/hooks/post-update.sample +8 -0
  30. data/lib/gistore/templates/hooks/pre-applypatch.sample +14 -0
  31. data/lib/gistore/templates/hooks/pre-commit.sample +49 -0
  32. data/lib/gistore/templates/hooks/pre-push.sample +54 -0
  33. data/lib/gistore/templates/hooks/pre-rebase.sample +169 -0
  34. data/lib/gistore/templates/hooks/prepare-commit-msg.sample +36 -0
  35. data/lib/gistore/templates/hooks/update.sample +128 -0
  36. data/lib/gistore/templates/info/exclude +6 -0
  37. data/lib/gistore/utils.rb +382 -0
  38. data/lib/gistore/version.rb +4 -0
  39. data/t/Makefile +80 -0
  40. data/t/README +745 -0
  41. data/t/aggregate-results.sh +46 -0
  42. data/t/lib-worktree.sh +76 -0
  43. data/t/t0000-init.sh +75 -0
  44. data/t/t0010-config.sh +75 -0
  45. data/t/t0020-version.sh +32 -0
  46. data/t/t1000-add-remove.sh +89 -0
  47. data/t/t1010-status.sh +87 -0
  48. data/t/t1020-commit.sh +134 -0
  49. data/t/t1030-commit-and-rotate.sh +266 -0
  50. data/t/t2000-task-and-commit-all.sh +132 -0
  51. data/t/t3000-checkout.sh +115 -0
  52. data/t/t3010-export-and-restore.sh +141 -0
  53. data/t/test-binary-1.png +0 -0
  54. data/t/test-binary-2.png +0 -0
  55. data/t/test-lib-functions.sh +722 -0
  56. data/t/test-lib.sh +684 -0
  57. metadata +161 -0
@@ -0,0 +1,6 @@
1
+ # git ls-files --others --exclude-from=.git/info/exclude
2
+ # Lines that start with '#' are comments.
3
+ # For a project mostly in C, the following would be a good set of
4
+ # exclude patterns (uncomment them if you want to use them):
5
+ # *.[oa]
6
+ # *~
@@ -0,0 +1,382 @@
1
+ require 'pathname'
2
+ require 'gistore/error'
3
+
4
+ if RUBY_VERSION < '1.9'
5
+ require 'open4'
6
+ else
7
+ require 'open3'
8
+ end
9
+
10
+ module Gistore
11
+
12
+ class <<self
13
+ def git_cmd
14
+ @git_cmd ||= begin
15
+ git_path = which "git"
16
+ unless git_path
17
+ abort "Please install git first."
18
+ else
19
+ git_path.to_s
20
+ end
21
+ end
22
+ end
23
+
24
+ def git_version
25
+ @git_version ||= begin
26
+ shellout(git_cmd, "--version",
27
+ :without_locale => true) do |io|
28
+ if io.read.strip =~ /^git version (.*)/
29
+ $1.split('.').map(&:to_i)
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ def git_version_compare(v1, v2=nil)
36
+ if v2
37
+ current_version = v1.is_a?(Array) ? v1.dup : v1.split('.').map(&:to_i)
38
+ check_version = v2.is_a?(Array) ? v2.dup : v2.split('.').map(&:to_i)
39
+ else
40
+ current_version = git_version.dup
41
+ check_version = v1.is_a?(Array) ? v1.dup : v1.split('.').map(&:to_i)
42
+ end
43
+ current_version.each do |current|
44
+ check = check_version.shift.to_i
45
+ result = current <=> check
46
+ return result if result != 0
47
+ end
48
+ check_version.shift ? -1 : 0
49
+ end
50
+
51
+ def which cmd, path=ENV['PATH']
52
+ dir = path.split(File::PATH_SEPARATOR).find {|p| File.executable? File.join(p, cmd)}
53
+ Pathname.new(File.join(dir, cmd)) unless dir.nil?
54
+ end
55
+
56
+ def shellout(*args, &block)
57
+ if Hash === args.last
58
+ options = args.pop.dup
59
+ else
60
+ options = {}
61
+ end
62
+ options[:stdout_only] = true
63
+ args << options
64
+ self.popen3(*args, &block)
65
+ end
66
+
67
+ def shellpipe(*args, &block)
68
+ if Hash === args.last
69
+ options = args.pop.dup
70
+ else
71
+ options = {}
72
+ end
73
+ args << options unless options.empty?
74
+ self.popen3(*args, &block)
75
+ end
76
+
77
+ def system(*args, &block)
78
+ fork do
79
+ block.call if block_given?
80
+ args.map!{|arg| arg.to_s}
81
+ exec(*args) rescue nil
82
+ # never gets here unless raise some error (exec failed)
83
+ exit! 1
84
+ end
85
+ Process.wait
86
+ $?.success?
87
+ end
88
+
89
+ # Same like system but with exceptions
90
+ def safe_system(*args, &block)
91
+ unless Gistore.system(*args, &block)
92
+ args = args.map{ |arg| arg.to_s.gsub " ", "\\ " } * " "
93
+ raise CommandReturnError, "Failure while executing: #{args}"
94
+ end
95
+ end
96
+
97
+ # prints no output
98
+ def quiet_system(*args)
99
+ Gistore.system(*args) do
100
+ # Redirect output streams to `/dev/null` instead of closing as some programs
101
+ # will fail to execute if they can't write to an open stream.
102
+ $stdout.reopen('/dev/null')
103
+ $stderr.reopen('/dev/null')
104
+ end
105
+ end
106
+
107
+ if RUBY_VERSION < '1.9'
108
+
109
+ def popen3(*cmd, &block)
110
+ if Hash === cmd.last
111
+ options = cmd.pop.dup
112
+ else
113
+ options = {}
114
+ end
115
+ result = nil
116
+ pid, stdin, stdout, stderr = nil
117
+ begin
118
+ pid, stdin, stdout, stderr = Open4.popen4(*cmd)
119
+ if options[:stdout_only]
120
+ stdin.close
121
+ result = block.call(stdout) if block_given?
122
+ else
123
+ result = block.call(stdin, stdout, stderr) if block_given?
124
+ end
125
+ ignored, status = Process::waitpid2 pid
126
+ if options[:check_return] and status and status.exitstatus != 0
127
+ raise CommandReturnError.new("Command failed (return #{status.exitstatus}).")
128
+ end
129
+ rescue Exception => e
130
+ msg = strip_credential(e.message)
131
+ # The command failed, log it and re-raise
132
+ logmsg = "Command failed: #{msg}"
133
+ logmsg << "\n"
134
+ logmsg << " >> #{strip_credential(cmd)}"
135
+ if e.is_a? CommandReturnError
136
+ raise CommandReturnError.new(logmsg)
137
+ else
138
+ raise CommandExceptionError.new(logmsg)
139
+ end
140
+ ensure
141
+ [stdin, stdout, stderr].each {|io| io.close unless io.closed?}
142
+ end
143
+ result
144
+ end
145
+
146
+ else
147
+
148
+ def popen3(*cmd, &block)
149
+ if Hash === cmd.last
150
+ options = cmd.pop.dup
151
+ else
152
+ options = {}
153
+ end
154
+
155
+ result = nil
156
+ stdin, stdout, stderr, wait_thr = nil
157
+ begin
158
+ if options[:merge_stderr]
159
+ stdin, stdout, wait_thr = Open3.popen2e(*cmd)
160
+ else
161
+ stdin, stdout, stderr, wait_thr = Open3.popen3(*cmd)
162
+ end
163
+ if options[:stdout_only]
164
+ stdin.close
165
+ result = block.call(stdout) if block_given?
166
+ elsif options[:merge_stderr]
167
+ result = block.call(stdin, stdout) if block_given?
168
+ else
169
+ result = block.call(stdin, stdout, stderr) if block_given?
170
+ end
171
+ wait_thr.join
172
+ if (options[:check_return] and
173
+ wait_thr and wait_thr.value and
174
+ wait_thr.value.exitstatus != 0)
175
+ raise CommandReturnError.new("Command failed (return #{wait_thr.value.exitstatus}).")
176
+ end
177
+ rescue Exception => e
178
+ msg = strip_credential(e.message)
179
+ # The command failed, log it and re-raise
180
+ logmsg = "Command failed: #{msg}"
181
+ logmsg << "\n"
182
+ logmsg << " >> #{strip_credential(cmd)}"
183
+ if e.is_a? CommandReturnError
184
+ raise CommandReturnError.new(logmsg)
185
+ else
186
+ raise CommandExceptionError.new(logmsg)
187
+ end
188
+ ensure
189
+ [stdin, stdout, stderr].each {|io| io.close if io and not io.closed?}
190
+ end
191
+ result
192
+ end
193
+ end
194
+
195
+ def strip_credential(message)
196
+ message
197
+ end
198
+
199
+ def get_gistore_tasks(options={})
200
+ cmds = [git_cmd, "config"]
201
+ unless ENV["GISTORE_TEST_GIT_CONFIG"]
202
+ if options[:system]
203
+ cmds << "--system"
204
+ elsif options[:global]
205
+ cmds << "--global"
206
+ end
207
+ end
208
+ cmds << "--get-regexp"
209
+ cmds << "gistore.task."
210
+ cmds << {:with_git_config => true} if ENV["GISTORE_TEST_GIT_CONFIG"]
211
+ tasks = {}
212
+ begin
213
+ Gistore::shellout(*cmds) do |stdout|
214
+ stdout.readlines.each do |line|
215
+ if line =~ /^gistore.task.([\S]+) (.*)$/
216
+ tasks[Regexp.last_match(1)] = Regexp.last_match(2)
217
+ end
218
+ end
219
+ end
220
+ tasks
221
+ rescue
222
+ {}
223
+ end
224
+ end
225
+
226
+ def is_git_repo?(name)
227
+ File.directory?("#{name}/objects") &&
228
+ File.directory?("#{name}/refs") &&
229
+ File.exist?("#{name}/config")
230
+ end
231
+ end
232
+
233
+ def git_cmd; self.class.git_cmd; end
234
+
235
+ def git_version; self.class.git_version; end
236
+
237
+ def git_version_compare(version)
238
+ self.class.git_version_compare(version)
239
+ end
240
+
241
+ class Tty
242
+ class << self
243
+ def options
244
+ @options ||= {}
245
+ end
246
+ def blue; bold 34; end
247
+ def white; bold 39; end
248
+ def red; underline 31; end
249
+ def yellow; underline 33 ; end
250
+ def reset; escape 0; end
251
+ def em; underline 39; end
252
+ def green; color 92 end
253
+ def gray; bold 30 end
254
+
255
+ def width
256
+ @width = begin
257
+ w = %x{stty size 2>/dev/null}.chomp.split.last.to_i.nonzero?
258
+ w ||= %x{tput cols 2>/dev/null}.to_i
259
+ w < 1 ? 80 : w
260
+ end
261
+ end
262
+
263
+ def truncate(str)
264
+ str.to_s[0, width - 4]
265
+ end
266
+
267
+ def die(message)
268
+ error message
269
+ exit 1
270
+ end
271
+
272
+ def error(message)
273
+ lines = message.to_s.split("\n")
274
+ if STDERR.tty?
275
+ STDERR.puts "#{Tty.red}Error#{Tty.reset}: #{lines.shift}"
276
+ else
277
+ STDERR.puts "Error: #{lines.shift}"
278
+ end
279
+ STDERR.puts lines unless lines.empty?
280
+ end
281
+
282
+ def warning(message)
283
+ if STDERR.tty?
284
+ STDERR.puts "#{Tty.red}Warning#{Tty.reset}: #{message}"
285
+ else
286
+ STDERR.puts "Warning: #{message}"
287
+ end
288
+ end
289
+
290
+ def info(message)
291
+ unless quiet?
292
+ if STDERR.tty?
293
+ STDERR.puts "#{Tty.blue}Info#{Tty.reset}: #{message}"
294
+ else
295
+ STDERR.puts "Info: #{message}"
296
+ end
297
+ end
298
+ end
299
+
300
+ def debug(message)
301
+ if verbose?
302
+ if STDERR.tty?
303
+ STDERR.puts "#{Tty.yellow}Debug#{Tty.reset}: #{message}"
304
+ else
305
+ STDERR.puts "Debug: #{message}"
306
+ end
307
+ end
308
+ end
309
+
310
+ private
311
+
312
+ def verbose?
313
+ options[:verbose]
314
+ end
315
+
316
+ def quiet?
317
+ options[:quiet]
318
+ end
319
+
320
+ def color n
321
+ escape "0;#{n}"
322
+ end
323
+ def bold n
324
+ escape "1;#{n}"
325
+ end
326
+ def underline n
327
+ escape "4;#{n}"
328
+ end
329
+ def escape n
330
+ "\033[#{n}m" if $stdout.tty?
331
+ end
332
+
333
+ public
334
+
335
+ def show_columns(args)
336
+ if Hash === args.last
337
+ options = args.last.dup
338
+ else
339
+ options = {}
340
+ end
341
+ options[:padding] ||= 4
342
+ options[:indent] ||= 4
343
+ output = ''
344
+
345
+ if $stdout.tty?
346
+ # determine the best width to display for different console sizes
347
+ console_width = width
348
+ longest = args.sort_by { |arg| arg.length }.last.length rescue 0
349
+ optimal_col_width = ((console_width - options[:indent] + options[:padding]).to_f /
350
+ (longest + options[:padding]).to_f).floor rescue 0
351
+ cols = optimal_col_width > 1 ? optimal_col_width : 1
352
+
353
+ Gistore::shellpipe("/usr/bin/pr",
354
+ "-#{cols}",
355
+ "-o#{options[:indent]}",
356
+ "-t",
357
+ "-w#{console_width}") do |stdin, stdout, stderr|
358
+ stdin.puts args
359
+ stdin.close
360
+ output << stdout.read
361
+ end
362
+ output.rstrip!
363
+ output << "\n" unless output.empty?
364
+ else
365
+ output << args.map{|e| " " * options[:indent] + e.to_s} * "\n"
366
+ end
367
+ output
368
+ end
369
+ end
370
+ end
371
+ end
372
+
373
+ def git_cmd
374
+ Gistore.git_cmd
375
+ end
376
+
377
+ class String
378
+ def charat(n)
379
+ result = self.send "[]", n
380
+ RUBY_VERSION < "1.9" ? result.chr : result
381
+ end
382
+ end
@@ -0,0 +1,4 @@
1
+ module Gistore
2
+ VERSION = "1.0.0.rc4"
3
+ REPO_VERSION = 1
4
+ end
data/t/Makefile ADDED
@@ -0,0 +1,80 @@
1
+ # Run tests
2
+ #
3
+ # Copyright (c) 2005 Junio C Hamano
4
+ #
5
+
6
+ #GIT_TEST_OPTS = --verbose --debug
7
+ SHELL_PATH ?= $(SHELL)
8
+ PERL_PATH ?= /usr/bin/perl
9
+ TAR ?= $(TAR)
10
+ RM ?= rm -f
11
+ PROVE ?= prove
12
+ DEFAULT_TEST_TARGET ?= test
13
+ TEST_LINT ?= test-lint-duplicates test-lint-executable
14
+
15
+ ifdef TEST_OUTPUT_DIRECTORY
16
+ TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results
17
+ else
18
+ TEST_RESULTS_DIRECTORY = test-results
19
+ endif
20
+
21
+ # Shell quote;
22
+ SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
23
+ PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
24
+ TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY))
25
+
26
+ T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))
27
+
28
+ all: $(DEFAULT_TEST_TARGET)
29
+
30
+ test: pre-clean $(TEST_LINT)
31
+ $(MAKE) aggregate-results-and-cleanup
32
+
33
+ prove: pre-clean $(TEST_LINT)
34
+ @echo "*** prove ***"; $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
35
+ $(MAKE) clean-except-prove-cache
36
+
37
+ $(T):
38
+ @echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
39
+
40
+ pre-clean:
41
+ $(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
42
+
43
+ clean-except-prove-cache:
44
+ $(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'
45
+ $(RM) -r valgrind/bin
46
+
47
+ clean: clean-except-prove-cache
48
+ $(RM) .prove
49
+
50
+ test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax
51
+
52
+ test-lint-duplicates:
53
+ @dups=`echo $(T) | tr ' ' '\n' | sed 's/-.*//' | sort | uniq -d` && \
54
+ test -z "$$dups" || { \
55
+ echo >&2 "duplicate test numbers:" $$dups; exit 1; }
56
+
57
+ test-lint-executable:
58
+ @bad=`for i in $(T); do test -x "$$i" || echo $$i; done` && \
59
+ test -z "$$bad" || { \
60
+ echo >&2 "non-executable tests:" $$bad; exit 1; }
61
+
62
+ test-lint-shell-syntax:
63
+ @'$(PERL_PATH_SQ)' check-non-portable-shell.pl $(T)
64
+
65
+ aggregate-results-and-cleanup: $(T)
66
+ $(MAKE) aggregate-results
67
+ $(MAKE) clean
68
+
69
+ aggregate-results:
70
+ for f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \
71
+ echo "$$f"; \
72
+ done | '$(SHELL_PATH_SQ)' ./aggregate-results.sh
73
+
74
+ valgrind:
75
+ $(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
76
+
77
+ perf:
78
+ $(MAKE) -C perf/ all
79
+
80
+ .PHONY: pre-clean $(T) aggregate-results clean valgrind perf