boof-grit 1.1.2

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 (70) hide show
  1. data/API.txt +101 -0
  2. data/History.txt +53 -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 +246 -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 +740 -0
  27. data/lib/grit/git.rb +141 -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 +177 -0
  32. data/lib/grit/repo.rb +452 -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 +94 -0
  54. data/test/test_grit.rb +32 -0
  55. data/test/test_head.rb +72 -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 +381 -0
  63. data/test/test_rubygit.rb +192 -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 +103 -0
  69. data/test/test_tree.rb +101 -0
  70. metadata +143 -0
data/lib/grit/git.rb ADDED
@@ -0,0 +1,141 @@
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
+
34
+ def initialize(git_dir, work_tree = nil)
35
+ self.git_dir, self.work_tree = git_dir, work_tree
36
+ self.bytes_read = 0
37
+ end
38
+
39
+ def shell_escape(str)
40
+ str.to_s.gsub("'", "\\\\'").gsub(";", '\\;')
41
+ end
42
+ alias_method :e, :shell_escape
43
+
44
+ # Run the given git command with the specified arguments and return
45
+ # the result as a String
46
+ # +cmd+ is the command
47
+ # +options+ is a hash of Ruby style options
48
+ # +args+ is the list of arguments (to be joined by spaces)
49
+ #
50
+ # Examples
51
+ # git.rev_list({:max_count => 10, :header => true}, "master")
52
+ #
53
+ # Returns String
54
+ def method_missing(cmd, options = {}, *args)
55
+ run('', cmd, '', options, args)
56
+ end
57
+
58
+ def run(prefix, cmd, postfix, options, args)
59
+ timeout = options.delete(:timeout) rescue nil
60
+ timeout = true if timeout.nil?
61
+
62
+ opt_args = transform_options(options)
63
+ ext_args = args.reject { |a| a.empty? }.map { |a| (a == '--' || a[0].chr == '|') ? a : "'#{e(a)}'" }
64
+
65
+ work_tree_option = " --work-tree='#{self.work_tree}'" if self.work_tree
66
+ call = "#{prefix}#{Git.git_binary}#{work_tree_option} --git-dir='#{self.git_dir}' #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{e(postfix)}"
67
+ Grit.log(call) if Grit.debug
68
+ response, err = timeout ? sh(call) : wild_sh(call)
69
+ Grit.log(response) if Grit.debug
70
+ Grit.log(err) if Grit.debug
71
+ response
72
+ end
73
+
74
+ def sh(command)
75
+ ret, err = '', ''
76
+ Open3.popen3(command) do |_, stdout, stderr|
77
+ Timeout.timeout(self.class.git_timeout) do
78
+ while tmp = stdout.read(1024)
79
+ ret += tmp
80
+ if (@bytes_read += tmp.size) > self.class.git_max_size
81
+ bytes = @bytes_read
82
+ @bytes_read = 0
83
+ raise GitTimeout.new(command, bytes)
84
+ end
85
+ end
86
+ end
87
+
88
+ while tmp = stderr.read(1024)
89
+ err += tmp
90
+ end
91
+ end
92
+ [ret, err]
93
+ rescue Timeout::Error, Grit::Git::GitTimeout
94
+ bytes = @bytes_read
95
+ @bytes_read = 0
96
+ raise GitTimeout.new(command, bytes)
97
+ end
98
+
99
+ def wild_sh(command)
100
+ ret, err = '', ''
101
+ Open3.popen3(command) do |_, stdout, stderr|
102
+ while tmp = stdout.read(1024)
103
+ ret += tmp
104
+ end
105
+
106
+ while tmp = stderr.read(1024)
107
+ err += tmp
108
+ end
109
+ end
110
+ [ret, err]
111
+ end
112
+
113
+ # Transform Ruby style options into git command line options
114
+ # +options+ is a hash of Ruby style options
115
+ #
116
+ # Returns String[]
117
+ # e.g. ["--max-count=10", "--header"]
118
+ def transform_options(options)
119
+ args = []
120
+ options.keys.each do |opt|
121
+ if opt.to_s.size == 1
122
+ if options[opt] == true
123
+ args << "-#{opt}"
124
+ else
125
+ val = options.delete(opt)
126
+ args << "-#{opt.to_s} '#{e(val)}'"
127
+ end
128
+ else
129
+ if options[opt] == true
130
+ args << "--#{opt.to_s.gsub(/_/, '-')}"
131
+ else
132
+ val = options.delete(opt)
133
+ args << "--#{opt.to_s.gsub(/_/, '-')}='#{e(val)}'"
134
+ end
135
+ end
136
+ end
137
+ args
138
+ end
139
+ end # Git
140
+
141
+ 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,177 @@
1
+ module Grit
2
+
3
+ class Ref
4
+
5
+ class << self
6
+ # Creates a new Ref object with <tt>name</tt> in the repository if it
7
+ # does not exist already. This method can only be called on Tag, Head or
8
+ # Remote.
9
+ #
10
+ # The <tt>startpoint</tt> defaults to HEAD.
11
+ #
12
+ # Returns kind of Ref (baked).
13
+ def create(repo, ref_name, startpoint = nil, type = nil)
14
+ type = extract_type type
15
+ startpoint = startpoint_from_object repo, startpoint
16
+
17
+ path = File.join repo.path, %W[ refs #{ type }s #{ ref_name } ]
18
+ unless File.exists? path
19
+ dir = File.dirname path
20
+ FileUtils.mkdir_p dir unless File.exist? dir
21
+ open(path, 'w') { |f| f << startpoint }
22
+ end
23
+
24
+ new ref_name, Commit.create(repo, :id => startpoint)
25
+ end
26
+
27
+ # Find all Refs
28
+ # +repo+ is the Repo
29
+ # +options+ is a Hash of options
30
+ #
31
+ # Returns Grit::Ref[] (baked)
32
+ def find_all(repo, options = {})
33
+ refs = []
34
+ already = {}
35
+ Dir.chdir(repo.path) do
36
+ files = Dir.glob(prefix + '/**/*')
37
+ files.each do |ref|
38
+ next if !File.file?(ref)
39
+ id = File.read(ref).chomp
40
+ name = ref.sub("#{prefix}/", '')
41
+ commit = Commit.create(repo, :id => id)
42
+ if !already[name]
43
+ refs << self.new(name, commit)
44
+ already[name] = true
45
+ end
46
+ end
47
+
48
+ if File.file?('packed-refs')
49
+ File.readlines('packed-refs').each do |line|
50
+ if m = /^(\w{40}) (.*?)$/.match(line)
51
+ next if !Regexp.new('^' + prefix).match(m[2])
52
+ name = m[2].sub("#{prefix}/", '')
53
+ commit = Commit.create(repo, :id => m[1])
54
+ if !already[name]
55
+ refs << self.new(name, commit)
56
+ already[name] = true
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ refs
64
+ end
65
+
66
+ protected
67
+ def extract_type(type)
68
+ type ||= name.split('::').last.downcase
69
+
70
+ %w[ head remote tag ].include? type or
71
+ raise ArgumentError, "expected head, remote or tags but was #{type}"
72
+
73
+ type
74
+ end
75
+ def startpoint_from_object(repo, object)
76
+ case object
77
+ when String
78
+ ref = repo.refs.find {|r| r.name == object }
79
+ ref ? ref.commit.id : object
80
+ when Grit::Ref; object.commit.id
81
+ when Grit::Commit; object.id
82
+ else
83
+ repo.git.rev_parse nil, 'HEAD'
84
+ end
85
+ end
86
+ def prefix
87
+ "refs/#{name.to_s.gsub(/^.*::/, '').downcase}s"
88
+ end
89
+
90
+ end
91
+
92
+ attr_reader :name
93
+ attr_reader :commit
94
+
95
+ # Instantiate a new Head
96
+ # +name+ is the name of the head
97
+ # +commit+ is the Commit that the head points to
98
+ #
99
+ # Returns Grit::Head (baked)
100
+ def initialize(name, commit)
101
+ @name, @commit = name, commit
102
+ @repo = @commit.instance_variable_get :@repo
103
+ @git = @repo.git
104
+ end
105
+
106
+ # Checkout the current Ref unless the current repo is bare. In this case
107
+ # a RuntimeError is raised.
108
+ def checkout
109
+ # TODO: should this change the HEAD?
110
+ raise RuntimeError, 'bare repository' if @repo.bare
111
+ invoke :checkout, @name
112
+ end
113
+
114
+ # Pretty object inspection
115
+ def inspect
116
+ %Q{#<#{self.class.name} "#{@name}">}
117
+ end
118
+
119
+ def ==(other)
120
+ self.class === other and name == other.name
121
+ end
122
+
123
+ protected
124
+ def invoke(cmd, *args)
125
+ @git.send cmd, {}, *args
126
+ end
127
+ end # Ref
128
+
129
+ # A Head is a named reference to a Commit. Every Head instance contains a
130
+ # name and a Commit object.
131
+ #
132
+ # r = Grit::Repo.new("/path/to/repo")
133
+ # h = r.heads.first
134
+ # h.name # => "master"
135
+ # h.commit # => #<Grit::Commit "1c09...">
136
+ # h.commit.id # => "1c09f116cbc2cb4100fb6935bb162daa4723f455"
137
+ class Head < Ref
138
+
139
+ # Get the HEAD revision of the repo.
140
+ # +repo+ is the Repo
141
+ # +options+ is a Hash of options
142
+ #
143
+ # Returns Grit::Head (baked)
144
+ def self.current(repo, options = {})
145
+ head = File.read File.join(repo.path, 'HEAD')
146
+ head.chomp!
147
+
148
+ if match = /ref: refs\/heads\/(.*)/.match(head)
149
+ commit = Commit.create repo,
150
+ :id => repo.git.rev_parse(options, 'HEAD')
151
+
152
+ new match[1], commit
153
+ end
154
+ end
155
+
156
+ # Checkout the this branch, do stuff and ensure that the old branch gets
157
+ # checked out afterwards.
158
+ # If message is set the index is commited. In this case the commit (baked)
159
+ # is returned.
160
+ def in_branch(message = nil)
161
+ old_ref = @repo.head
162
+ checkout
163
+ yield @repo
164
+
165
+ if message
166
+ @repo.commit_index message
167
+ @repo.head.commit
168
+ end
169
+ ensure
170
+ old_ref.checkout
171
+ end
172
+
173
+ end # Head
174
+
175
+ class Remote < Ref; end
176
+
177
+ end # Grit