joelmoss-grit 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/API.txt +101 -0
  2. data/History.txt +49 -0
  3. data/LICENSE +22 -0
  4. data/README.md +210 -0
  5. data/VERSION.yml +4 -0
  6. data/examples/ex_add_commit.rb +13 -0
  7. data/examples/ex_index.rb +14 -0
  8. data/lib/grit.rb +68 -0
  9. data/lib/grit/actor.rb +36 -0
  10. data/lib/grit/blame.rb +61 -0
  11. data/lib/grit/blob.rb +126 -0
  12. data/lib/grit/commit.rb +242 -0
  13. data/lib/grit/commit_stats.rb +128 -0
  14. data/lib/grit/config.rb +44 -0
  15. data/lib/grit/diff.rb +70 -0
  16. data/lib/grit/errors.rb +7 -0
  17. data/lib/grit/git-ruby.rb +186 -0
  18. data/lib/grit/git-ruby/commit_db.rb +52 -0
  19. data/lib/grit/git-ruby/file_index.rb +193 -0
  20. data/lib/grit/git-ruby/git_object.rb +350 -0
  21. data/lib/grit/git-ruby/internal/file_window.rb +58 -0
  22. data/lib/grit/git-ruby/internal/loose.rb +137 -0
  23. data/lib/grit/git-ruby/internal/pack.rb +382 -0
  24. data/lib/grit/git-ruby/internal/raw_object.rb +37 -0
  25. data/lib/grit/git-ruby/object.rb +325 -0
  26. data/lib/grit/git-ruby/repository.rb +736 -0
  27. data/lib/grit/git.rb +148 -0
  28. data/lib/grit/index.rb +122 -0
  29. data/lib/grit/lazy.rb +33 -0
  30. data/lib/grit/merge.rb +45 -0
  31. data/lib/grit/ref.rb +99 -0
  32. data/lib/grit/repo.rb +565 -0
  33. data/lib/grit/ruby1.9.rb +7 -0
  34. data/lib/grit/status.rb +151 -0
  35. data/lib/grit/submodule.rb +88 -0
  36. data/lib/grit/tag.rb +66 -0
  37. data/lib/grit/tree.rb +123 -0
  38. data/lib/open3_detach.rb +46 -0
  39. data/test/bench/benchmarks.rb +126 -0
  40. data/test/helper.rb +18 -0
  41. data/test/profile.rb +21 -0
  42. data/test/suite.rb +6 -0
  43. data/test/test_actor.rb +35 -0
  44. data/test/test_blame.rb +32 -0
  45. data/test/test_blame_tree.rb +33 -0
  46. data/test/test_blob.rb +83 -0
  47. data/test/test_commit.rb +207 -0
  48. data/test/test_commit_stats.rb +33 -0
  49. data/test/test_commit_write.rb +20 -0
  50. data/test/test_config.rb +58 -0
  51. data/test/test_diff.rb +18 -0
  52. data/test/test_file_index.rb +56 -0
  53. data/test/test_git.rb +84 -0
  54. data/test/test_grit.rb +32 -0
  55. data/test/test_head.rb +47 -0
  56. data/test/test_index_status.rb +40 -0
  57. data/test/test_merge.rb +17 -0
  58. data/test/test_raw.rb +16 -0
  59. data/test/test_real.rb +19 -0
  60. data/test/test_reality.rb +17 -0
  61. data/test/test_remote.rb +14 -0
  62. data/test/test_repo.rb +347 -0
  63. data/test/test_rubygit.rb +188 -0
  64. data/test/test_rubygit_alt.rb +40 -0
  65. data/test/test_rubygit_index.rb +76 -0
  66. data/test/test_rubygit_iv2.rb +28 -0
  67. data/test/test_submodule.rb +69 -0
  68. data/test/test_tag.rb +67 -0
  69. data/test/test_tree.rb +101 -0
  70. metadata +141 -0
data/lib/grit/git.rb ADDED
@@ -0,0 +1,148 @@
1
+ module Grit
2
+
3
+ class Git
4
+ class GitTimeout < RuntimeError
5
+ attr_reader :command, :bytes_read
6
+
7
+ def initialize(command = nil, bytes_read = nil)
8
+ @command = command
9
+ @bytes_read = bytes_read
10
+ end
11
+ end
12
+
13
+ undef_method :clone
14
+
15
+ include GitRuby
16
+
17
+ class << self
18
+ attr_accessor :git_binary, :git_timeout, :git_max_size
19
+ end
20
+
21
+ self.git_binary = "/usr/bin/env git"
22
+ self.git_timeout = 10
23
+ self.git_max_size = 5242880 # 5.megabytes
24
+
25
+ def self.with_timeout(timeout = 10.seconds)
26
+ old_timeout = Grit::Git.git_timeout
27
+ Grit::Git.git_timeout = timeout
28
+ yield
29
+ Grit::Git.git_timeout = old_timeout
30
+ end
31
+
32
+ attr_accessor :git_dir, :work_tree, :bytes_read
33
+ attr_reader :last_error, :last_response
34
+
35
+ def initialize(git_dir, work_tree=nil)
36
+ self.git_dir = git_dir
37
+ self.work_tree = work_tree
38
+ self.bytes_read = 0
39
+ end
40
+
41
+ def shell_escape(str)
42
+ str.to_s.gsub("'", "\\\\'").gsub(";", '\\;')
43
+ end
44
+ alias_method :e, :shell_escape
45
+
46
+ # Run the given git command with the specified arguments and return
47
+ # the result as a String
48
+ # +cmd+ is the command
49
+ # +options+ is a hash of Ruby style options
50
+ # +args+ is the list of arguments (to be joined by spaces)
51
+ #
52
+ # Examples
53
+ # git.rev_list({:max_count => 10, :header => true}, "master")
54
+ #
55
+ # Returns String
56
+ def method_missing(cmd, options = {}, *args)
57
+ run('', cmd, '', options, args)
58
+ end
59
+
60
+ def git_options
61
+ { :git_dir => self.git_dir, :work_tree => self.work_tree }.reject { |k, v| v.nil? }
62
+ end
63
+
64
+ def run(prefix, cmd, postfix, options, args)
65
+ timeout = options.delete(:timeout) rescue nil
66
+ timeout = true if timeout.nil?
67
+
68
+ git_opt_args = transform_options(git_options)
69
+ opt_args = transform_options(options)
70
+ ext_args = args.reject { |a| a.nil? || a.empty? }.map { |a| (a == '--' || a[0].chr == '|') ? a : "'#{e(a)}'" }
71
+
72
+ call = "#{prefix}#{Git.git_binary} #{git_opt_args.join(' ')} #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{e(postfix)}"
73
+ Grit.log(call) if Grit.debug
74
+ response, err = timeout ? sh(call) : wild_sh(call)
75
+ Grit.log(response) if Grit.debug
76
+ Grit.log(err) if Grit.debug
77
+ @last_error, @last_response = err, response
78
+ response
79
+ end
80
+
81
+ def sh(command)
82
+ ret, err = '', ''
83
+ Open3.popen3(command) do |_, stdout, stderr|
84
+ Timeout.timeout(self.class.git_timeout) do
85
+ while tmp = stdout.read(1024)
86
+ ret += tmp
87
+ if (@bytes_read += tmp.size) > self.class.git_max_size
88
+ bytes = @bytes_read
89
+ @bytes_read = 0
90
+ raise GitTimeout.new(command, bytes)
91
+ end
92
+ end
93
+ end
94
+
95
+ while tmp = stderr.read(1024)
96
+ err += tmp
97
+ end
98
+ end
99
+ [ret, err]
100
+ rescue Timeout::Error, Grit::Git::GitTimeout
101
+ bytes = @bytes_read
102
+ @bytes_read = 0
103
+ raise GitTimeout.new(command, bytes)
104
+ end
105
+
106
+ def wild_sh(command)
107
+ ret, err = '', ''
108
+ Open3.popen3(command) do |_, stdout, stderr|
109
+ while tmp = stdout.read(1024)
110
+ ret += tmp
111
+ end
112
+
113
+ while tmp = stderr.read(1024)
114
+ err += tmp
115
+ end
116
+ end
117
+ [ret, err]
118
+ end
119
+
120
+ # Transform Ruby style options into git command line options
121
+ # +options+ is a hash of Ruby style options
122
+ #
123
+ # Returns String[]
124
+ # e.g. ["--max-count=10", "--header"]
125
+ def transform_options(options)
126
+ args = []
127
+ options.keys.each do |opt|
128
+ if opt.to_s.size == 1
129
+ if options[opt] == true
130
+ args << "-#{opt}"
131
+ else
132
+ val = options.delete(opt)
133
+ args << "-#{opt.to_s} '#{e(val)}'"
134
+ end
135
+ else
136
+ if options[opt] == true
137
+ args << "--#{opt.to_s.gsub(/_/, '-')}"
138
+ else
139
+ val = options.delete(opt)
140
+ args << "--#{opt.to_s.gsub(/_/, '-')}='#{e(val)}'"
141
+ end
142
+ end
143
+ end
144
+ args
145
+ end
146
+ end # Git
147
+
148
+ end # Grit
data/lib/grit/index.rb ADDED
@@ -0,0 +1,122 @@
1
+ module Grit
2
+
3
+ class Index
4
+ attr_accessor :repo, :tree, :current_tree
5
+
6
+ def initialize(repo)
7
+ self.repo = repo
8
+ self.tree = {}
9
+ self.current_tree = nil
10
+ end
11
+
12
+ # Add a file to the index
13
+ # +path+ is the path (including filename)
14
+ # +data+ is the binary contents of the file
15
+ #
16
+ # Returns nothing
17
+ def add(file_path, data)
18
+ path = file_path.split('/')
19
+ filename = path.pop
20
+
21
+ current = self.tree
22
+
23
+ path.each do |dir|
24
+ current[dir] ||= {}
25
+ node = current[dir]
26
+ current = node
27
+ end
28
+
29
+ current[filename] = data
30
+ end
31
+
32
+ # Sets the current tree
33
+ # +tree+ the branch/tag/sha... to use - a string
34
+ #
35
+ # Returns index (self)
36
+ def read_tree(tree)
37
+ self.current_tree = self.repo.tree(tree)
38
+ end
39
+
40
+ # Commit the contents of the index
41
+ # +message+ is the commit message [nil]
42
+ # +parents+ is one or more commits to attach this commit to to form a new head [nil]
43
+ # +actor+ is the details of the user making the commit [nil]
44
+ # +last_tree+ is a tree to compare with - to avoid making empty commits [nil]
45
+ # +head+ is the branch to write this head to [master]
46
+ #
47
+ # Returns a String of the SHA1 of the commit
48
+ def commit(message, parents = nil, actor = nil, last_tree = nil, head = 'master')
49
+ tree_sha1 = write_tree(self.tree, self.current_tree)
50
+ return false if tree_sha1 == last_tree # don't write identical commits
51
+
52
+ contents = []
53
+ contents << ['tree', tree_sha1].join(' ')
54
+ parents.each do |p|
55
+ contents << ['parent', p].join(' ') if p
56
+ end if parents
57
+
58
+ if actor
59
+ name = actor.name
60
+ email = actor.email
61
+ else
62
+ config = Config.new(self.repo)
63
+ name = config['user.name']
64
+ email = config['user.email']
65
+ end
66
+
67
+ author_string = "#{name} <#{email}> #{Time.now.to_i} -0700" # !! TODO : gotta fix this
68
+ contents << ['author', author_string].join(' ')
69
+ contents << ['committer', author_string].join(' ')
70
+ contents << ''
71
+ contents << message
72
+
73
+ commit_sha1 = self.repo.git.ruby_git.put_raw_object(contents.join("\n"), 'commit')
74
+
75
+ self.repo.update_ref(head, commit_sha1)
76
+ end
77
+
78
+ # Recursively write a tree to the index
79
+ # +tree+ is the tree
80
+ #
81
+ # Returns the SHA1 String of the tree
82
+ def write_tree(tree, now_tree = nil)
83
+ tree_contents = {}
84
+
85
+ # fill in original tree
86
+ now_tree.contents.each do |obj|
87
+ sha = [obj.id].pack("H*")
88
+ k = obj.name
89
+ k += '/' if (obj.class == Grit::Tree)
90
+ tree_contents[k] = "%s %s\0%s" % [obj.mode.to_s, obj.name, sha]
91
+ end if now_tree
92
+
93
+ # overwrite with new tree contents
94
+ tree.each do |k, v|
95
+ case v
96
+ when String
97
+ sha = write_blob(v)
98
+ sha = [sha].pack("H*")
99
+ str = "%s %s\0%s" % ['100644', k, sha]
100
+ tree_contents[k] = str
101
+ when Hash
102
+ ctree = now_tree/k if now_tree
103
+ sha = write_tree(v, ctree)
104
+ sha = [sha].pack("H*")
105
+ str = "%s %s\0%s" % ['040000', k, sha]
106
+ tree_contents[k + '/'] = str
107
+ end
108
+ end
109
+ tr = tree_contents.sort.map { |k, v| v }.join('')
110
+ self.repo.git.ruby_git.put_raw_object(tr, 'tree')
111
+ end
112
+
113
+ # Write the blob to the index
114
+ # +data+ is the data to write
115
+ #
116
+ # Returns the SHA1 String of the blob
117
+ def write_blob(data)
118
+ self.repo.git.ruby_git.put_raw_object(data, 'blob')
119
+ end
120
+ end # Index
121
+
122
+ end # Grit
data/lib/grit/lazy.rb ADDED
@@ -0,0 +1,33 @@
1
+ ##
2
+ # Allows attributes to be declared as lazy, meaning that they won't be
3
+ # computed until they are asked for.
4
+ #
5
+ # Works by delegating each lazy_reader to a cached lazy_source method.
6
+ #
7
+ # class Person
8
+ # lazy_reader :eyes
9
+ #
10
+ # def lazy_source
11
+ # OpenStruct.new(:eyes => 2)
12
+ # end
13
+ # end
14
+ #
15
+ # >> Person.new.eyes
16
+ # => 2
17
+ #
18
+ module Lazy
19
+ def lazy_reader(*args)
20
+ args.each do |arg|
21
+ ivar = "@#{arg}"
22
+ define_method(arg) do
23
+ if instance_variable_defined?(ivar)
24
+ val = instance_variable_get(ivar)
25
+ return val if val
26
+ end
27
+ instance_variable_set(ivar, (@lazy_source ||= lazy_source).send(arg))
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ Object.extend Lazy unless Object.ancestors.include? Lazy
data/lib/grit/merge.rb ADDED
@@ -0,0 +1,45 @@
1
+ module Grit
2
+
3
+ class Merge
4
+
5
+ STATUS_BOTH = 'both'
6
+ STATUS_OURS = 'ours'
7
+ STATUS_THEIRS = 'theirs'
8
+
9
+ attr_reader :conflicts, :text, :sections
10
+
11
+ def initialize(str)
12
+ status = STATUS_BOTH
13
+
14
+ section = 1
15
+ @conflicts = 0
16
+ @text = {}
17
+
18
+ lines = str.split("\n")
19
+ lines.each do |line|
20
+ if /^<<<<<<< (.*?)/.match(line)
21
+ status = STATUS_OURS
22
+ @conflicts += 1
23
+ section += 1
24
+ elsif line == '======='
25
+ status = STATUS_THEIRS
26
+ elsif /^>>>>>>> (.*?)/.match(line)
27
+ status = STATUS_BOTH
28
+ section += 1
29
+ else
30
+ @text[section] ||= {}
31
+ @text[section][status] ||= []
32
+ @text[section][status] << line
33
+ end
34
+ end
35
+ @text = @text.values
36
+ @sections = @text.size
37
+ end
38
+
39
+ # Pretty object inspection
40
+ def inspect
41
+ %Q{#<Grit::Merge}
42
+ end
43
+ end # Merge
44
+
45
+ end # Grit
data/lib/grit/ref.rb ADDED
@@ -0,0 +1,99 @@
1
+ module Grit
2
+
3
+ class Ref
4
+
5
+ class << self
6
+
7
+ # Find all Refs
8
+ # +repo+ is the Repo
9
+ # +options+ is a Hash of options
10
+ #
11
+ # Returns Grit::Ref[] (baked)
12
+ def find_all(repo, options = {})
13
+ refs = []
14
+ already = {}
15
+ Dir.chdir(repo.path) do
16
+ files = Dir.glob(prefix + '/**/*')
17
+ files.each do |ref|
18
+ next if !File.file?(ref)
19
+ id = File.read(ref).chomp
20
+ name = ref.sub("#{prefix}/", '')
21
+ commit = Commit.create(repo, :id => id)
22
+ if !already[name]
23
+ refs << self.new(name, commit)
24
+ already[name] = true
25
+ end
26
+ end
27
+
28
+ if File.file?('packed-refs')
29
+ File.readlines('packed-refs').each do |line|
30
+ if m = /^(\w{40}) (.*?)$/.match(line)
31
+ next if !Regexp.new('^' + prefix).match(m[2])
32
+ name = m[2].sub("#{prefix}/", '')
33
+ commit = Commit.create(repo, :id => m[1])
34
+ if !already[name]
35
+ refs << self.new(name, commit)
36
+ already[name] = true
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ refs
44
+ end
45
+
46
+ protected
47
+
48
+ def prefix
49
+ "refs/#{name.to_s.gsub(/^.*::/, '').downcase}s"
50
+ end
51
+
52
+ end
53
+
54
+ attr_reader :name
55
+ attr_reader :commit
56
+
57
+ # Instantiate a new Head
58
+ # +name+ is the name of the head
59
+ # +commit+ is the Commit that the head points to
60
+ #
61
+ # Returns Grit::Head (baked)
62
+ def initialize(name, commit)
63
+ @name = name
64
+ @commit = commit
65
+ end
66
+
67
+ # Pretty object inspection
68
+ def inspect
69
+ %Q{#<#{self.class.name} "#{@name}">}
70
+ end
71
+ end # Ref
72
+
73
+ # A Head is a named reference to a Commit. Every Head instance contains a name
74
+ # and a Commit object.
75
+ #
76
+ # r = Grit::Repo.new("/path/to/repo")
77
+ # h = r.heads.first
78
+ # h.name # => "master"
79
+ # h.commit # => #<Grit::Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455">
80
+ # h.commit.id # => "1c09f116cbc2cb4100fb6935bb162daa4723f455"
81
+ class Head < Ref
82
+
83
+ # Get the HEAD revision of the repo.
84
+ # +repo+ is the Repo
85
+ # +options+ is a Hash of options
86
+ #
87
+ # Returns Grit::Head (baked)
88
+ def self.current(repo, options = {})
89
+ head = File.open(File.join(repo.path, 'HEAD')).read.chomp
90
+ if /ref: refs\/heads\/(.*)/.match(head)
91
+ self.new($1, repo.git.rev_parse(options, 'HEAD'))
92
+ end
93
+ end
94
+
95
+ end # Head
96
+
97
+ class Remote < Ref; end
98
+
99
+ end # Grit