gistore 1.0.0.rc4

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.
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