gifts 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +29 -0
  5. data/Rakefile +9 -0
  6. data/gifts.gemspec +32 -0
  7. data/lib/gifts.rb +22 -0
  8. data/lib/gifts/commit_table.rb +70 -0
  9. data/lib/gifts/database.rb +49 -0
  10. data/lib/gifts/diff_table.rb +55 -0
  11. data/lib/gifts/file_table.rb +51 -0
  12. data/lib/gifts/repo_table.rb +31 -0
  13. data/lib/gifts/table_base.rb +18 -0
  14. data/lib/gifts/term_table.rb +28 -0
  15. data/lib/gifts/user_table.rb +19 -0
  16. data/lib/gifts/version.rb +3 -0
  17. data/spec/.gitkeeper +0 -0
  18. data/vendor/lib/LICENSE-grit +22 -0
  19. data/vendor/lib/LICENSE-grit_ext +22 -0
  20. data/vendor/lib/gifts/grit.rb +73 -0
  21. data/vendor/lib/gifts/grit/actor.rb +52 -0
  22. data/vendor/lib/gifts/grit/blame.rb +70 -0
  23. data/vendor/lib/gifts/grit/blob.rb +126 -0
  24. data/vendor/lib/gifts/grit/commit.rb +324 -0
  25. data/vendor/lib/gifts/grit/commit_stats.rb +128 -0
  26. data/vendor/lib/gifts/grit/config.rb +44 -0
  27. data/vendor/lib/gifts/grit/diff.rb +97 -0
  28. data/vendor/lib/gifts/grit/errors.rb +10 -0
  29. data/vendor/lib/gifts/grit/git-ruby.rb +262 -0
  30. data/vendor/lib/gifts/grit/git-ruby/commit_db.rb +52 -0
  31. data/vendor/lib/gifts/grit/git-ruby/git_object.rb +353 -0
  32. data/vendor/lib/gifts/grit/git-ruby/internal/file_window.rb +58 -0
  33. data/vendor/lib/gifts/grit/git-ruby/internal/loose.rb +137 -0
  34. data/vendor/lib/gifts/grit/git-ruby/internal/pack.rb +398 -0
  35. data/vendor/lib/gifts/grit/git-ruby/internal/raw_object.rb +44 -0
  36. data/vendor/lib/gifts/grit/git-ruby/repository.rb +784 -0
  37. data/vendor/lib/gifts/grit/git.rb +501 -0
  38. data/vendor/lib/gifts/grit/index.rb +222 -0
  39. data/vendor/lib/gifts/grit/lazy.rb +35 -0
  40. data/vendor/lib/gifts/grit/merge.rb +45 -0
  41. data/vendor/lib/gifts/grit/ref.rb +98 -0
  42. data/vendor/lib/gifts/grit/repo.rb +722 -0
  43. data/vendor/lib/gifts/grit/ruby1.9.rb +15 -0
  44. data/vendor/lib/gifts/grit/status.rb +153 -0
  45. data/vendor/lib/gifts/grit/submodule.rb +88 -0
  46. data/vendor/lib/gifts/grit/tag.rb +97 -0
  47. data/vendor/lib/gifts/grit/tree.rb +125 -0
  48. data/vendor/lib/gifts/grit_ext.rb +41 -0
  49. data/vendor/lib/gifts/grit_ext/actor.rb +15 -0
  50. data/vendor/lib/gifts/grit_ext/blob.rb +26 -0
  51. data/vendor/lib/gifts/grit_ext/commit.rb +15 -0
  52. data/vendor/lib/gifts/grit_ext/diff.rb +23 -0
  53. data/vendor/lib/gifts/grit_ext/tag.rb +10 -0
  54. data/vendor/lib/gifts/grit_ext/tree.rb +10 -0
  55. data/vendor/lib/gifts/grit_ext/version.rb +7 -0
  56. metadata +256 -0
@@ -0,0 +1,19 @@
1
+ module Gifts
2
+ class UserTable < TableBase
3
+ def table_name
4
+ "user"
5
+ end
6
+
7
+ def define_schema
8
+ Groonga::Schema.define do |schema|
9
+ schema.create_table(table_name, type: :hash) do |table|
10
+ table.string("name")
11
+ end
12
+ end
13
+ end
14
+
15
+ def add(name)
16
+ db_user = table[name] || table.add(name, name: name)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module Gifts
2
+ VERSION = "0.0.1"
3
+ end
File without changes
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2007-2009 Tom Preston-Werner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Saito
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,73 @@
1
+ # core
2
+ require 'fileutils'
3
+ require 'time'
4
+
5
+ # stdlib
6
+ require 'timeout'
7
+ require 'logger'
8
+ require 'digest/sha1'
9
+
10
+ # third party
11
+
12
+ begin
13
+ require 'mime/types'
14
+ require 'rubygems'
15
+ rescue LoadError
16
+ require 'rubygems'
17
+ begin
18
+ gem "mime-types", ">=0"
19
+ require 'mime/types'
20
+ rescue Gem::LoadError => e
21
+ puts "WARNING: Gem LoadError: #{e.message}"
22
+ end
23
+ end
24
+
25
+ # ruby 1.9 compatibility
26
+ require 'gifts/grit/ruby1.9'
27
+
28
+ # internal requires
29
+ require 'gifts/grit/lazy'
30
+ require 'gifts/grit/errors'
31
+ require 'gifts/grit/git-ruby'
32
+ require 'gifts/grit/git' unless defined? Gifts::Grit::Git
33
+ require 'gifts/grit/ref'
34
+ require 'gifts/grit/tag'
35
+ require 'gifts/grit/commit'
36
+ require 'gifts/grit/commit_stats'
37
+ require 'gifts/grit/tree'
38
+ require 'gifts/grit/blob'
39
+ require 'gifts/grit/actor'
40
+ require 'gifts/grit/diff'
41
+ require 'gifts/grit/config'
42
+ require 'gifts/grit/repo'
43
+ require 'gifts/grit/index'
44
+ require 'gifts/grit/status'
45
+ require 'gifts/grit/submodule'
46
+ require 'gifts/grit/blame'
47
+ require 'gifts/grit/merge'
48
+
49
+ module Gifts::Grit
50
+ VERSION = '2.5.0'
51
+
52
+ class << self
53
+ # Set +debug+ to true to log all git calls and responses
54
+ attr_accessor :debug
55
+ attr_accessor :use_git_ruby
56
+ attr_accessor :no_quote
57
+
58
+ # The standard +logger+ for debugging git calls - this defaults to a plain STDOUT logger
59
+ attr_accessor :logger
60
+ def log(str)
61
+ logger.debug { str }
62
+ end
63
+ end
64
+ self.debug = false
65
+ self.use_git_ruby = true
66
+ self.no_quote = false
67
+
68
+ @logger ||= ::Logger.new(STDOUT)
69
+
70
+ def self.version
71
+ VERSION
72
+ end
73
+ end
@@ -0,0 +1,52 @@
1
+ module Gifts::Grit
2
+
3
+ class Actor
4
+ attr_reader :name
5
+ attr_reader :email
6
+
7
+ def initialize(name, email)
8
+ @name = name
9
+ @email = email
10
+ end
11
+ alias_method :to_s, :name
12
+
13
+ # Create an Actor from a string.
14
+ #
15
+ # str - The String in this format: 'John Doe <jdoe@example.com>'
16
+ #
17
+ # Returns Git::Actor.
18
+ def self.from_string(str)
19
+ case str
20
+ when /<.+>/
21
+ m, name, email = *str.match(/(.*) <(.+?)>/)
22
+ return self.new(name, email)
23
+ else
24
+ return self.new(str, nil)
25
+ end
26
+ end
27
+
28
+ # Outputs an actor string for Git commits.
29
+ #
30
+ # actor = Actor.new('bob', 'bob@email.com')
31
+ # actor.output(time) # => "bob <bob@email.com> UNIX_TIME +0700"
32
+ #
33
+ # time - The Time the commit was authored or committed.
34
+ #
35
+ # Returns a String.
36
+ def output(time)
37
+ offset = time.utc_offset / 60
38
+ "%s <%s> %d %+.2d%.2d" % [
39
+ @name,
40
+ @email || "null",
41
+ time.to_i,
42
+ offset / 60,
43
+ offset.abs % 60]
44
+ end
45
+
46
+ # Pretty object inspection
47
+ def inspect
48
+ %Q{#<Gifts::Grit::Actor "#{@name} <#{@email}>">}
49
+ end
50
+ end # Actor
51
+
52
+ end # Gifts::Grit
@@ -0,0 +1,70 @@
1
+ module Gifts::Grit
2
+
3
+ class Blame
4
+
5
+ attr_reader :lines
6
+
7
+ def initialize(repo, file, commit, lines=nil)
8
+ @repo = repo
9
+ @file = file
10
+ @commit = commit
11
+ if lines.nil?
12
+ @lines = []
13
+ load_blame
14
+ else
15
+ @lines = lines
16
+ end
17
+ end
18
+
19
+ def load_blame
20
+ output = @repo.git.blame({'p' => true}, @commit, '--', @file)
21
+ process_raw_blame(output)
22
+ end
23
+
24
+ def process_raw_blame(output)
25
+ lines, final = [], []
26
+ info, commits = {}, {}
27
+
28
+ # process the output
29
+ output.split("\n").each do |line|
30
+ if line[0, 1] == "\t"
31
+ lines << line[1, line.size]
32
+ elsif m = /^(\w{40}) (\d+) (\d+)/.match(line)
33
+ commit_id, old_lineno, lineno = m[1], m[2].to_i, m[3].to_i
34
+ commits[commit_id] = nil if !commits.key?(commit_id)
35
+ info[lineno] = [commit_id, old_lineno]
36
+ end
37
+ end
38
+
39
+ # load all commits in single call
40
+ @repo.batch(*commits.keys).each do |commit|
41
+ commits[commit.id] = commit
42
+ end
43
+
44
+ # get it together
45
+ info.sort.each do |lineno, (commit_id, old_lineno)|
46
+ commit = commits[commit_id]
47
+ final << BlameLine.new(lineno, old_lineno, commit, lines[lineno - 1])
48
+ end
49
+
50
+ @lines = final
51
+ end
52
+
53
+ # Pretty object inspection
54
+ def inspect
55
+ %Q{#<Gifts::Grit::Blame "#{@file} <#{@commit}>">}
56
+ end
57
+
58
+ class BlameLine
59
+ attr_accessor :lineno, :oldlineno, :commit, :line
60
+ def initialize(lineno, oldlineno, commit, line)
61
+ @lineno = lineno
62
+ @oldlineno = oldlineno
63
+ @commit = commit
64
+ @line = line
65
+ end
66
+ end
67
+
68
+ end # Blame
69
+
70
+ end # Gifts::Grit
@@ -0,0 +1,126 @@
1
+ module Gifts::Grit
2
+
3
+ class Blob
4
+ DEFAULT_MIME_TYPE = "text/plain"
5
+
6
+ attr_reader :id
7
+ attr_reader :mode
8
+ attr_reader :name
9
+
10
+ # Create an unbaked Blob containing just the specified attributes
11
+ # +repo+ is the Repo
12
+ # +atts+ is a Hash of instance variable data
13
+ #
14
+ # Returns Gifts::Grit::Blob (unbaked)
15
+ def self.create(repo, atts)
16
+ self.allocate.create_initialize(repo, atts)
17
+ end
18
+
19
+ # Initializer for Blob.create
20
+ # +repo+ is the Repo
21
+ # +atts+ is a Hash of instance variable data
22
+ #
23
+ # Returns Gifts::Grit::Blob (unbaked)
24
+ def create_initialize(repo, atts)
25
+ @repo = repo
26
+ atts.each do |k, v|
27
+ instance_variable_set("@#{k}".to_sym, v)
28
+ end
29
+ self
30
+ end
31
+
32
+ # The size of this blob in bytes
33
+ #
34
+ # Returns Integer
35
+ def size
36
+ @size ||= @repo.git.cat_file({:s => true}, id).chomp.to_i
37
+ end
38
+
39
+ # The binary contents of this blob.
40
+ #
41
+ # Returns String
42
+ def data
43
+ @data ||= @repo.git.cat_file({:p => true}, id)
44
+ end
45
+
46
+ # The mime type of this file (based on the filename)
47
+ #
48
+ # Returns String
49
+ def mime_type
50
+ guesses = MIME::Types.type_for(self.name) rescue []
51
+ guesses.first ? guesses.first.simplified : DEFAULT_MIME_TYPE
52
+ end
53
+
54
+ # The blame information for the given file at the given commit
55
+ #
56
+ # Returns Array: [Gifts::Grit::Commit, Array: [<line>]]
57
+ def self.blame(repo, commit, file)
58
+ data = repo.git.blame({:p => true}, commit, '--', file)
59
+
60
+ commits = {}
61
+ blames = []
62
+ info = nil
63
+
64
+ data.split("\n").each do |line|
65
+ parts = line.split(/\s+/, 2)
66
+ case parts.first
67
+ when /^[0-9A-Fa-f]{40}$/
68
+ case line
69
+ when /^([0-9A-Fa-f]{40}) (\d+) (\d+) (\d+)$/
70
+ _, id, origin_line, final_line, group_lines = *line.match(/^([0-9A-Fa-f]{40}) (\d+) (\d+) (\d+)$/)
71
+ info = {:id => id}
72
+ blames << [nil, []]
73
+ when /^([0-9A-Fa-f]{40}) (\d+) (\d+)$/
74
+ _, id, origin_line, final_line = *line.match(/^([0-9A-Fa-f]{40}) (\d+) (\d+)$/)
75
+ info = {:id => id}
76
+ end
77
+ when /^(author|committer)/
78
+ case parts.first
79
+ when /^(.+)-mail$/
80
+ info["#{$1}_email".intern] = parts.last
81
+ when /^(.+)-time$/
82
+ info["#{$1}_date".intern] = Time.at(parts.last.to_i)
83
+ when /^(author|committer)$/
84
+ info[$1.intern] = parts.last
85
+ end
86
+ when /^filename/
87
+ info[:filename] = parts.last
88
+ when /^summary/
89
+ info[:summary] = parts.last
90
+ when ''
91
+ c = commits[info[:id]]
92
+ unless c
93
+ c = Commit.create(repo, :id => info[:id],
94
+ :author => Actor.from_string(info[:author] + ' ' + info[:author_email]),
95
+ :authored_date => info[:author_date],
96
+ :committer => Actor.from_string(info[:committer] + ' ' + info[:committer_email]),
97
+ :committed_date => info[:committer_date],
98
+ :message => info[:summary])
99
+ commits[info[:id]] = c
100
+ end
101
+ _, text = *line.match(/^\t(.*)$/)
102
+ blames.last[0] = c
103
+ blames.last[1] << text
104
+ info = nil
105
+ end
106
+ end
107
+
108
+ blames
109
+ end
110
+
111
+ def basename
112
+ File.basename(name)
113
+ end
114
+
115
+ # Pretty object inspection
116
+ def inspect
117
+ %Q{#<Gifts::Grit::Blob "#{@id}">}
118
+ end
119
+
120
+ # Compares blobs by name
121
+ def <=>(other)
122
+ name <=> other.name
123
+ end
124
+ end # Blob
125
+
126
+ end # Gifts::Grit
@@ -0,0 +1,324 @@
1
+ module Gifts::Grit
2
+
3
+ class Commit
4
+ extend Lazy
5
+
6
+ attr_reader :id
7
+ attr_reader :repo
8
+ lazy_reader :parents
9
+ lazy_reader :tree
10
+ lazy_reader :author
11
+ lazy_reader :authored_date
12
+ lazy_reader :committer
13
+ lazy_reader :committed_date
14
+ lazy_reader :message
15
+ lazy_reader :short_message
16
+
17
+ # Parses output from the `git-cat-file --batch'.
18
+ #
19
+ # repo - Gifts::Grit::Repo instance.
20
+ # sha - String SHA of the Commit.
21
+ # size - Fixnum size of the object.
22
+ # object - Parsed String output from `git cat-file --batch`.
23
+ #
24
+ # Returns an Array of Gifts::Grit::Commit objects.
25
+ def self.parse_batch(repo, sha, size, object)
26
+ info, message = object.split("\n\n", 2)
27
+
28
+ lines = info.split("\n")
29
+ tree = lines.shift.split(' ', 2).last
30
+ parents = []
31
+ parents << lines.shift[7..-1] while lines.first[0, 6] == 'parent'
32
+ author, authored_date = Gifts::Grit::Commit.actor(lines.shift)
33
+ committer, committed_date = Gifts::Grit::Commit.actor(lines.shift)
34
+
35
+ Gifts::Grit::Commit.new(
36
+ repo, sha, parents, tree,
37
+ author, authored_date,
38
+ committer, committed_date,
39
+ message.to_s.split("\n"))
40
+ end
41
+
42
+ # Instantiate a new Commit
43
+ # +id+ is the id of the commit
44
+ # +parents+ is an array of commit ids (will be converted into Commit instances)
45
+ # +tree+ is the correspdonding tree id (will be converted into a Tree object)
46
+ # +author+ is the author string
47
+ # +authored_date+ is the authored Time
48
+ # +committer+ is the committer string
49
+ # +committed_date+ is the committed Time
50
+ # +message+ is an array of commit message lines
51
+ #
52
+ # Returns Gifts::Grit::Commit (baked)
53
+ def initialize(repo, id, parents, tree, author, authored_date, committer, committed_date, message)
54
+ @repo = repo
55
+ @id = id
56
+ @parents = parents.map { |p| Commit.create(repo, :id => p) }
57
+ @tree = Tree.create(repo, :id => tree)
58
+ @author = author
59
+ @authored_date = authored_date
60
+ @committer = committer
61
+ @committed_date = committed_date
62
+ @message = message.join("\n")
63
+ @short_message = message.find { |x| !x.strip.empty? } || ''
64
+ end
65
+
66
+ def id_abbrev
67
+ @id_abbrev ||= @repo.git.rev_parse({}, self.id).chomp[0, 7]
68
+ end
69
+
70
+ # Create an unbaked Commit containing just the specified attributes
71
+ # +repo+ is the Repo
72
+ # +atts+ is a Hash of instance variable data
73
+ #
74
+ # Returns Gifts::Grit::Commit (unbaked)
75
+ def self.create(repo, atts)
76
+ self.allocate.create_initialize(repo, atts)
77
+ end
78
+
79
+ # Initializer for Commit.create
80
+ # +repo+ is the Repo
81
+ # +atts+ is a Hash of instance variable data
82
+ #
83
+ # Returns Gifts::Grit::Commit (unbaked)
84
+ def create_initialize(repo, atts)
85
+ @repo = repo
86
+ atts.each do |k, v|
87
+ instance_variable_set("@#{k}", v)
88
+ end
89
+ self
90
+ end
91
+
92
+ def lazy_source
93
+ self.class.find_all(@repo, @id, {:max_count => 1}).first
94
+ end
95
+
96
+ # Count the number of commits reachable from this ref
97
+ # +repo+ is the Repo
98
+ # +ref+ is the ref from which to begin (SHA1 or name)
99
+ #
100
+ # Returns Integer
101
+ def self.count(repo, ref)
102
+ repo.git.rev_list({}, ref).size / 41
103
+ end
104
+
105
+ # Find all commits matching the given criteria.
106
+ # +repo+ is the Repo
107
+ # +ref+ is the ref from which to begin (SHA1 or name) or nil for --all
108
+ # +options+ is a Hash of optional arguments to git
109
+ # :max_count is the maximum number of commits to fetch
110
+ # :skip is the number of commits to skip
111
+ #
112
+ # Returns Gifts::Grit::Commit[] (baked)
113
+ def self.find_all(repo, ref, options = {})
114
+ allowed_options = [:max_count, :skip, :since]
115
+
116
+ default_options = {:pretty => "raw"}
117
+ actual_options = default_options.merge(options)
118
+
119
+ if ref
120
+ output = repo.git.rev_list(actual_options, ref)
121
+ else
122
+ output = repo.git.rev_list(actual_options.merge(:all => true))
123
+ end
124
+
125
+ self.list_from_string(repo, output)
126
+ rescue Gifts::Grit::GitRuby::Repository::NoSuchShaFound
127
+ []
128
+ end
129
+
130
+ # Parse out commit information into an array of baked Commit objects
131
+ # +repo+ is the Repo
132
+ # +text+ is the text output from the git command (raw format)
133
+ #
134
+ # Returns Gifts::Grit::Commit[] (baked)
135
+ #
136
+ # really should re-write this to be more accepting of non-standard commit messages
137
+ # - it broke when 'encoding' was introduced - not sure what else might show up
138
+ #
139
+ def self.list_from_string(repo, text)
140
+ text_gpgless = text.gsub(/gpgsig -----BEGIN PGP SIGNATURE-----[\n\r](.*[\n\r])*? -----END PGP SIGNATURE-----[\n\r]/, "")
141
+ lines = text_gpgless.split("\n")
142
+
143
+ commits = []
144
+
145
+ while !lines.empty?
146
+ # GITLAB patch
147
+ # Skip all garbage unless we get real commit
148
+ while !lines.empty? && lines.first !~ /^commit [a-zA-Z0-9]*$/
149
+ lines.shift
150
+ end
151
+
152
+ id = lines.shift.split.last
153
+ tree = lines.shift.split.last
154
+
155
+ parents = []
156
+ parents << lines.shift.split.last while lines.first =~ /^parent/
157
+
158
+ author_line = lines.shift
159
+ author_line << lines.shift if lines[0] !~ /^committer /
160
+ author, authored_date = self.actor(author_line)
161
+
162
+ committer_line = lines.shift
163
+ committer_line << lines.shift if lines[0] && lines[0] != '' && lines[0] !~ /^encoding/
164
+ committer, committed_date = self.actor(committer_line)
165
+
166
+ # not doing anything with this yet, but it's sometimes there
167
+ encoding = lines.shift.split.last if lines.first =~ /^encoding/
168
+
169
+ # GITLAB patch
170
+ # Skip Signature and other raw data
171
+ lines.shift while lines.first =~ /^ /
172
+
173
+ lines.shift
174
+
175
+ message_lines = []
176
+ message_lines << lines.shift[4..-1] while lines.first =~ /^ {4}/
177
+
178
+ lines.shift while lines.first && lines.first.empty?
179
+
180
+ commits << Commit.new(repo, id, parents, tree, author, authored_date, committer, committed_date, message_lines)
181
+ end
182
+
183
+ commits
184
+ end
185
+
186
+ # Show diffs between two trees.
187
+ #
188
+ # repo - The current Gifts::Grit::Repo instance.
189
+ # a - A String named commit.
190
+ # b - An optional String named commit. Passing an array assumes you
191
+ # wish to omit the second named commit and limit the diff to the
192
+ # given paths.
193
+ # paths - An optional Array of paths to limit the diff.
194
+ # options - An optional Hash of options. Merged into {:full_index => true}.
195
+ #
196
+ # Returns Gifts::Grit::Diff[] (baked)
197
+ def self.diff(repo, a, b = nil, paths = [], options = {})
198
+ if b.is_a?(Array)
199
+ paths = b
200
+ b = nil
201
+ end
202
+ paths.unshift("--") unless paths.empty?
203
+ paths.unshift(b) unless b.nil?
204
+ paths.unshift(a)
205
+ options = {full_index: true, no_color: true, no_ext_diff: true, M: true}.update(options)
206
+ text = repo.git.native(:diff, options, *paths)
207
+ Diff.list_from_string(repo, text, a)
208
+ end
209
+
210
+ def show
211
+ if parents.size > 1
212
+ diff = @repo.git.native(:diff, {:full_index => true}, "#{parents[0].id}...#{parents[1].id}")
213
+ else
214
+ diff = @repo.git.show({:full_index => true, :pretty => 'raw'}, @id)
215
+ end
216
+
217
+ if diff =~ /diff --git "?a/
218
+ diff = diff.sub(/.+?(diff --git "?a)/m, '\1')
219
+ else
220
+ diff = ''
221
+ end
222
+ Diff.list_from_string(@repo, diff, @id)
223
+ end
224
+
225
+ # Shows diffs between the commit's parent and the commit.
226
+ #
227
+ # options - An optional Hash of options, passed to Gifts::Grit::Commit.diff.
228
+ #
229
+ # Returns Gifts::Grit::Diff[] (baked)
230
+ def diffs(options = {})
231
+ if parents.empty?
232
+ show
233
+ else
234
+ self.class.diff(@repo, parents.first.id, @id, [], options)
235
+ end
236
+ end
237
+
238
+ def stats
239
+ stats = @repo.commit_stats(self.sha, 1)[0][-1]
240
+ end
241
+
242
+ # Convert this Commit to a String which is just the SHA1 id
243
+ def to_s
244
+ @id
245
+ end
246
+
247
+ def sha
248
+ @id
249
+ end
250
+
251
+ def date
252
+ @committed_date
253
+ end
254
+
255
+ def to_patch
256
+ @repo.git.format_patch({'1' => true, :stdout => true}, to_s)
257
+ end
258
+
259
+ def notes
260
+ ret = {}
261
+ notes = Note.find_all(@repo)
262
+ notes.each do |note|
263
+ if n = note.commit.tree/(self.id)
264
+ ret[note.name] = n.data
265
+ end
266
+ end
267
+ ret
268
+ end
269
+
270
+ # Calculates the commit's Patch ID. The Patch ID is essentially the SHA1
271
+ # of the diff that the commit is introducing.
272
+ #
273
+ # Returns the 40 character hex String if a patch-id could be calculated
274
+ # or nil otherwise.
275
+ def patch_id
276
+ show = @repo.git.show({}, @id)
277
+ patch_line = @repo.git.native(:patch_id, :input => show)
278
+ if patch_line =~ /^([0-9a-f]{40}) [0-9a-f]{40}\n$/
279
+ $1
280
+ else
281
+ nil
282
+ end
283
+ end
284
+
285
+ # Pretty object inspection
286
+ def inspect
287
+ %Q{#<Gifts::Grit::Commit "#{@id}">}
288
+ end
289
+
290
+ # private
291
+
292
+ # Parse out the actor (author or committer) info
293
+ #
294
+ # Returns [String (actor name and email), Time (acted at time)]
295
+ def self.actor(line)
296
+ m, actor, epoch = *line.match(/^.+? (.*) (\d+) .*$/)
297
+ [Actor.from_string(actor), Time.at(epoch.to_i)]
298
+ end
299
+
300
+ def author_string
301
+ "%s <%s> %s %+05d" % [author.name, author.email, authored_date.to_i, 800]
302
+ end
303
+
304
+ def to_hash
305
+ {
306
+ 'id' => id,
307
+ 'parents' => parents.map { |p| { 'id' => p.id } },
308
+ 'tree' => tree.id,
309
+ 'message' => message,
310
+ 'author' => {
311
+ 'name' => author.name,
312
+ 'email' => author.email
313
+ },
314
+ 'committer' => {
315
+ 'name' => committer.name,
316
+ 'email' => committer.email
317
+ },
318
+ 'authored_date' => authored_date.xmlschema,
319
+ 'committed_date' => committed_date.xmlschema,
320
+ }
321
+ end
322
+ end # Commit
323
+
324
+ end # Gifts::Grit