##// END OF EJS Templates
scm: git: use --encoding=UTF-8 in "git log" (#3396)....
Toshi MARUYAMA -
r4844:eb1271bb444c
parent child
Show More
@@ -1,119 +1,123
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 # Copyright (C) 2007 Patrick Aljord patcito@ŋmail.com
3 # Copyright (C) 2007 Patrick Aljord patcito@ŋmail.com
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'redmine/scm/adapters/git_adapter'
18 require 'redmine/scm/adapters/git_adapter'
19
19
20 class Repository::Git < Repository
20 class Repository::Git < Repository
21 attr_protected :root_url
21 attr_protected :root_url
22 validates_presence_of :url
22 validates_presence_of :url
23
23
24 def self.scm_adapter_class
24 def self.scm_adapter_class
25 Redmine::Scm::Adapters::GitAdapter
25 Redmine::Scm::Adapters::GitAdapter
26 end
26 end
27
27
28 def self.scm_name
28 def self.scm_name
29 'Git'
29 'Git'
30 end
30 end
31
31
32 def repo_log_encoding
33 'UTF-8'
34 end
35
32 # Returns the identifier for the given git changeset
36 # Returns the identifier for the given git changeset
33 def self.changeset_identifier(changeset)
37 def self.changeset_identifier(changeset)
34 changeset.scmid
38 changeset.scmid
35 end
39 end
36
40
37 # Returns the readable identifier for the given git changeset
41 # Returns the readable identifier for the given git changeset
38 def self.format_changeset_identifier(changeset)
42 def self.format_changeset_identifier(changeset)
39 changeset.revision[0, 8]
43 changeset.revision[0, 8]
40 end
44 end
41
45
42 def branches
46 def branches
43 scm.branches
47 scm.branches
44 end
48 end
45
49
46 def tags
50 def tags
47 scm.tags
51 scm.tags
48 end
52 end
49
53
50 def find_changeset_by_name(name)
54 def find_changeset_by_name(name)
51 return nil if name.nil? || name.empty?
55 return nil if name.nil? || name.empty?
52 e = changesets.find(:first, :conditions => ['revision = ?', name.to_s])
56 e = changesets.find(:first, :conditions => ['revision = ?', name.to_s])
53 return e if e
57 return e if e
54 changesets.find(:first, :conditions => ['scmid LIKE ?', "#{name}%"])
58 changesets.find(:first, :conditions => ['scmid LIKE ?', "#{name}%"])
55 end
59 end
56
60
57 # With SCM's that have a sequential commit numbering, redmine is able to be
61 # With SCM's that have a sequential commit numbering, redmine is able to be
58 # clever and only fetch changesets going forward from the most recent one
62 # clever and only fetch changesets going forward from the most recent one
59 # it knows about. However, with git, you never know if people have merged
63 # it knows about. However, with git, you never know if people have merged
60 # commits into the middle of the repository history, so we should parse
64 # commits into the middle of the repository history, so we should parse
61 # the entire log. Since it's way too slow for large repositories, we only
65 # the entire log. Since it's way too slow for large repositories, we only
62 # parse 1 week before the last known commit.
66 # parse 1 week before the last known commit.
63 # The repository can still be fully reloaded by calling #clear_changesets
67 # The repository can still be fully reloaded by calling #clear_changesets
64 # before fetching changesets (eg. for offline resync)
68 # before fetching changesets (eg. for offline resync)
65 def fetch_changesets
69 def fetch_changesets
66 c = changesets.find(:first, :order => 'committed_on DESC')
70 c = changesets.find(:first, :order => 'committed_on DESC')
67 since = (c ? c.committed_on - 7.days : nil)
71 since = (c ? c.committed_on - 7.days : nil)
68
72
69 revisions = scm.revisions('', nil, nil, :all => true, :since => since)
73 revisions = scm.revisions('', nil, nil, :all => true, :since => since)
70 return if revisions.nil? || revisions.empty?
74 return if revisions.nil? || revisions.empty?
71
75
72 recent_changesets = changesets.find(:all, :conditions => ['committed_on >= ?', since])
76 recent_changesets = changesets.find(:all, :conditions => ['committed_on >= ?', since])
73
77
74 # Clean out revisions that are no longer in git
78 # Clean out revisions that are no longer in git
75 recent_changesets.each {|c| c.destroy unless revisions.detect {|r| r.scmid.to_s == c.scmid.to_s }}
79 recent_changesets.each {|c| c.destroy unless revisions.detect {|r| r.scmid.to_s == c.scmid.to_s }}
76
80
77 # Subtract revisions that redmine already knows about
81 # Subtract revisions that redmine already knows about
78 recent_revisions = recent_changesets.map{|c| c.scmid}
82 recent_revisions = recent_changesets.map{|c| c.scmid}
79 revisions.reject!{|r| recent_revisions.include?(r.scmid)}
83 revisions.reject!{|r| recent_revisions.include?(r.scmid)}
80
84
81 # Save the remaining ones to the database
85 # Save the remaining ones to the database
82 unless revisions.nil?
86 unless revisions.nil?
83 revisions.each do |rev|
87 revisions.each do |rev|
84 transaction do
88 transaction do
85 changeset = Changeset.new(
89 changeset = Changeset.new(
86 :repository => self,
90 :repository => self,
87 :revision => rev.identifier,
91 :revision => rev.identifier,
88 :scmid => rev.scmid,
92 :scmid => rev.scmid,
89 :committer => rev.author,
93 :committer => rev.author,
90 :committed_on => rev.time,
94 :committed_on => rev.time,
91 :comments => rev.message)
95 :comments => rev.message)
92
96
93 if changeset.save
97 if changeset.save
94 rev.paths.each do |file|
98 rev.paths.each do |file|
95 Change.create(
99 Change.create(
96 :changeset => changeset,
100 :changeset => changeset,
97 :action => file[:action],
101 :action => file[:action],
98 :path => file[:path])
102 :path => file[:path])
99 end
103 end
100 end
104 end
101 end
105 end
102 end
106 end
103 end
107 end
104 end
108 end
105
109
106 def latest_changesets(path,rev,limit=10)
110 def latest_changesets(path,rev,limit=10)
107 revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
111 revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
108 return [] if revisions.nil? || revisions.empty?
112 return [] if revisions.nil? || revisions.empty?
109
113
110 changesets.find(
114 changesets.find(
111 :all,
115 :all,
112 :conditions => [
116 :conditions => [
113 "scmid IN (?)",
117 "scmid IN (?)",
114 revisions.map!{|c| c.scmid}
118 revisions.map!{|c| c.scmid}
115 ],
119 ],
116 :order => 'committed_on DESC'
120 :order => 'committed_on DESC'
117 )
121 )
118 end
122 end
119 end
123 end
@@ -1,334 +1,334
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'redmine/scm/adapters/abstract_adapter'
18 require 'redmine/scm/adapters/abstract_adapter'
19
19
20 module Redmine
20 module Redmine
21 module Scm
21 module Scm
22 module Adapters
22 module Adapters
23 class GitAdapter < AbstractAdapter
23 class GitAdapter < AbstractAdapter
24
24
25 SCM_GIT_REPORT_LAST_COMMIT = true
25 SCM_GIT_REPORT_LAST_COMMIT = true
26
26
27 # Git executable name
27 # Git executable name
28 GIT_BIN = Redmine::Configuration['scm_git_command'] || "git"
28 GIT_BIN = Redmine::Configuration['scm_git_command'] || "git"
29
29
30 # raised if scm command exited with error, e.g. unknown revision.
30 # raised if scm command exited with error, e.g. unknown revision.
31 class ScmCommandAborted < CommandFailed; end
31 class ScmCommandAborted < CommandFailed; end
32
32
33 class << self
33 class << self
34 def client_command
34 def client_command
35 @@bin ||= GIT_BIN
35 @@bin ||= GIT_BIN
36 end
36 end
37
37
38 def sq_bin
38 def sq_bin
39 @@sq_bin ||= shell_quote(GIT_BIN)
39 @@sq_bin ||= shell_quote(GIT_BIN)
40 end
40 end
41
41
42 def client_version
42 def client_version
43 @@client_version ||= (scm_command_version || [])
43 @@client_version ||= (scm_command_version || [])
44 end
44 end
45
45
46 def client_available
46 def client_available
47 !client_version.empty?
47 !client_version.empty?
48 end
48 end
49
49
50 def scm_command_version
50 def scm_command_version
51 scm_version = scm_version_from_command_line
51 scm_version = scm_version_from_command_line
52 if scm_version.respond_to?(:force_encoding)
52 if scm_version.respond_to?(:force_encoding)
53 scm_version.force_encoding('ASCII-8BIT')
53 scm_version.force_encoding('ASCII-8BIT')
54 end
54 end
55 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)})
55 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)})
56 m[2].scan(%r{\d+}).collect(&:to_i)
56 m[2].scan(%r{\d+}).collect(&:to_i)
57 end
57 end
58 end
58 end
59
59
60 def scm_version_from_command_line
60 def scm_version_from_command_line
61 shellout("#{sq_bin} --version --no-color") { |io| io.read }.to_s
61 shellout("#{sq_bin} --version --no-color") { |io| io.read }.to_s
62 end
62 end
63 end
63 end
64
64
65 def initialize(url, root_url=nil, login=nil, password=nil, path_encoding=nil)
65 def initialize(url, root_url=nil, login=nil, password=nil, path_encoding=nil)
66 super
66 super
67 @flag_report_last_commit = SCM_GIT_REPORT_LAST_COMMIT
67 @flag_report_last_commit = SCM_GIT_REPORT_LAST_COMMIT
68 end
68 end
69
69
70 def info
70 def info
71 begin
71 begin
72 Info.new(:root_url => url, :lastrev => lastrev('',nil))
72 Info.new(:root_url => url, :lastrev => lastrev('',nil))
73 rescue
73 rescue
74 nil
74 nil
75 end
75 end
76 end
76 end
77
77
78 def branches
78 def branches
79 return @branches if @branches
79 return @branches if @branches
80 @branches = []
80 @branches = []
81 cmd = "#{self.class.sq_bin} --git-dir #{target('')} branch --no-color"
81 cmd = "#{self.class.sq_bin} --git-dir #{target('')} branch --no-color"
82 shellout(cmd) do |io|
82 shellout(cmd) do |io|
83 io.each_line do |line|
83 io.each_line do |line|
84 @branches << line.match('\s*\*?\s*(.*)$')[1]
84 @branches << line.match('\s*\*?\s*(.*)$')[1]
85 end
85 end
86 end
86 end
87 @branches.sort!
87 @branches.sort!
88 end
88 end
89
89
90 def tags
90 def tags
91 return @tags if @tags
91 return @tags if @tags
92 cmd = "#{self.class.sq_bin} --git-dir #{target('')} tag"
92 cmd = "#{self.class.sq_bin} --git-dir #{target('')} tag"
93 shellout(cmd) do |io|
93 shellout(cmd) do |io|
94 @tags = io.readlines.sort!.map{|t| t.strip}
94 @tags = io.readlines.sort!.map{|t| t.strip}
95 end
95 end
96 end
96 end
97
97
98 def default_branch
98 def default_branch
99 branches.include?('master') ? 'master' : branches.first
99 branches.include?('master') ? 'master' : branches.first
100 end
100 end
101
101
102 def entries(path=nil, identifier=nil)
102 def entries(path=nil, identifier=nil)
103 path ||= ''
103 path ||= ''
104 entries = Entries.new
104 entries = Entries.new
105 cmd = "#{self.class.sq_bin} --git-dir #{target('')} ls-tree -l "
105 cmd = "#{self.class.sq_bin} --git-dir #{target('')} ls-tree -l "
106 cmd << shell_quote("HEAD:" + path) if identifier.nil?
106 cmd << shell_quote("HEAD:" + path) if identifier.nil?
107 cmd << shell_quote(identifier + ":" + path) if identifier
107 cmd << shell_quote(identifier + ":" + path) if identifier
108 shellout(cmd) do |io|
108 shellout(cmd) do |io|
109 io.each_line do |line|
109 io.each_line do |line|
110 e = line.chomp.to_s
110 e = line.chomp.to_s
111 if e =~ /^\d+\s+(\w+)\s+([0-9a-f]{40})\s+([0-9-]+)\t(.+)$/
111 if e =~ /^\d+\s+(\w+)\s+([0-9a-f]{40})\s+([0-9-]+)\t(.+)$/
112 type = $1
112 type = $1
113 sha = $2
113 sha = $2
114 size = $3
114 size = $3
115 name = $4
115 name = $4
116 full_path = path.empty? ? name : "#{path}/#{name}"
116 full_path = path.empty? ? name : "#{path}/#{name}"
117 entries << Entry.new({:name => name,
117 entries << Entry.new({:name => name,
118 :path => full_path,
118 :path => full_path,
119 :kind => (type == "tree") ? 'dir' : 'file',
119 :kind => (type == "tree") ? 'dir' : 'file',
120 :size => (type == "tree") ? nil : size,
120 :size => (type == "tree") ? nil : size,
121 :lastrev => @flag_report_last_commit ? lastrev(full_path,identifier) : Revision.new
121 :lastrev => @flag_report_last_commit ? lastrev(full_path,identifier) : Revision.new
122 }) unless entries.detect{|entry| entry.name == name}
122 }) unless entries.detect{|entry| entry.name == name}
123 end
123 end
124 end
124 end
125 end
125 end
126 return nil if $? && $?.exitstatus != 0
126 return nil if $? && $?.exitstatus != 0
127 entries.sort_by_name
127 entries.sort_by_name
128 end
128 end
129
129
130 def lastrev(path, rev)
130 def lastrev(path, rev)
131 return nil if path.nil?
131 return nil if path.nil?
132 cmd_args = %w|log --no-color --date=iso --pretty=fuller --no-merges -n 1|
132 cmd_args = %w|log --no-color --encoding=UTF-8 --date=iso --pretty=fuller --no-merges -n 1|
133 cmd_args << rev if rev
133 cmd_args << rev if rev
134 cmd_args << "--" << path unless path.empty?
134 cmd_args << "--" << path unless path.empty?
135 lines = []
135 lines = []
136 scm_cmd(*cmd_args) { |io| lines = io.readlines }
136 scm_cmd(*cmd_args) { |io| lines = io.readlines }
137 begin
137 begin
138 id = lines[0].split[1]
138 id = lines[0].split[1]
139 author = lines[1].match('Author:\s+(.*)$')[1]
139 author = lines[1].match('Author:\s+(.*)$')[1]
140 time = Time.parse(lines[4].match('CommitDate:\s+(.*)$')[1])
140 time = Time.parse(lines[4].match('CommitDate:\s+(.*)$')[1])
141
141
142 Revision.new({
142 Revision.new({
143 :identifier => id,
143 :identifier => id,
144 :scmid => id,
144 :scmid => id,
145 :author => author,
145 :author => author,
146 :time => time,
146 :time => time,
147 :message => nil,
147 :message => nil,
148 :paths => nil
148 :paths => nil
149 })
149 })
150 rescue NoMethodError => e
150 rescue NoMethodError => e
151 logger.error("The revision '#{path}' has a wrong format")
151 logger.error("The revision '#{path}' has a wrong format")
152 return nil
152 return nil
153 end
153 end
154 rescue ScmCommandAborted
154 rescue ScmCommandAborted
155 nil
155 nil
156 end
156 end
157
157
158 def revisions(path, identifier_from, identifier_to, options={})
158 def revisions(path, identifier_from, identifier_to, options={})
159 revisions = Revisions.new
159 revisions = Revisions.new
160 cmd_args = %w|log --no-color --raw --date=iso --pretty=fuller|
160 cmd_args = %w|log --no-color --encoding=UTF-8 --raw --date=iso --pretty=fuller|
161 cmd_args << "--reverse" if options[:reverse]
161 cmd_args << "--reverse" if options[:reverse]
162 cmd_args << "--all" if options[:all]
162 cmd_args << "--all" if options[:all]
163 cmd_args << "-n" << "#{options[:limit].to_i}" if options[:limit]
163 cmd_args << "-n" << "#{options[:limit].to_i}" if options[:limit]
164 from_to = ""
164 from_to = ""
165 from_to << "#{identifier_from}.." if identifier_from
165 from_to << "#{identifier_from}.." if identifier_from
166 from_to << "#{identifier_to}" if identifier_to
166 from_to << "#{identifier_to}" if identifier_to
167 cmd_args << from_to if !from_to.empty?
167 cmd_args << from_to if !from_to.empty?
168 cmd_args << "--since=#{options[:since].strftime("%Y-%m-%d %H:%M:%S")}" if options[:since]
168 cmd_args << "--since=#{options[:since].strftime("%Y-%m-%d %H:%M:%S")}" if options[:since]
169 cmd_args << "--" << path if path && !path.empty?
169 cmd_args << "--" << path if path && !path.empty?
170
170
171 scm_cmd *cmd_args do |io|
171 scm_cmd *cmd_args do |io|
172 files=[]
172 files=[]
173 changeset = {}
173 changeset = {}
174 parsing_descr = 0 #0: not parsing desc or files, 1: parsing desc, 2: parsing files
174 parsing_descr = 0 #0: not parsing desc or files, 1: parsing desc, 2: parsing files
175 revno = 1
175 revno = 1
176
176
177 io.each_line do |line|
177 io.each_line do |line|
178 if line =~ /^commit ([0-9a-f]{40})$/
178 if line =~ /^commit ([0-9a-f]{40})$/
179 key = "commit"
179 key = "commit"
180 value = $1
180 value = $1
181 if (parsing_descr == 1 || parsing_descr == 2)
181 if (parsing_descr == 1 || parsing_descr == 2)
182 parsing_descr = 0
182 parsing_descr = 0
183 revision = Revision.new({
183 revision = Revision.new({
184 :identifier => changeset[:commit],
184 :identifier => changeset[:commit],
185 :scmid => changeset[:commit],
185 :scmid => changeset[:commit],
186 :author => changeset[:author],
186 :author => changeset[:author],
187 :time => Time.parse(changeset[:date]),
187 :time => Time.parse(changeset[:date]),
188 :message => changeset[:description],
188 :message => changeset[:description],
189 :paths => files
189 :paths => files
190 })
190 })
191 if block_given?
191 if block_given?
192 yield revision
192 yield revision
193 else
193 else
194 revisions << revision
194 revisions << revision
195 end
195 end
196 changeset = {}
196 changeset = {}
197 files = []
197 files = []
198 revno = revno + 1
198 revno = revno + 1
199 end
199 end
200 changeset[:commit] = $1
200 changeset[:commit] = $1
201 elsif (parsing_descr == 0) && line =~ /^(\w+):\s*(.*)$/
201 elsif (parsing_descr == 0) && line =~ /^(\w+):\s*(.*)$/
202 key = $1
202 key = $1
203 value = $2
203 value = $2
204 if key == "Author"
204 if key == "Author"
205 changeset[:author] = value
205 changeset[:author] = value
206 elsif key == "CommitDate"
206 elsif key == "CommitDate"
207 changeset[:date] = value
207 changeset[:date] = value
208 end
208 end
209 elsif (parsing_descr == 0) && line.chomp.to_s == ""
209 elsif (parsing_descr == 0) && line.chomp.to_s == ""
210 parsing_descr = 1
210 parsing_descr = 1
211 changeset[:description] = ""
211 changeset[:description] = ""
212 elsif (parsing_descr == 1 || parsing_descr == 2) \
212 elsif (parsing_descr == 1 || parsing_descr == 2) \
213 && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\t(.+)$/
213 && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\t(.+)$/
214 parsing_descr = 2
214 parsing_descr = 2
215 fileaction = $1
215 fileaction = $1
216 filepath = $2
216 filepath = $2
217 files << {:action => fileaction, :path => filepath}
217 files << {:action => fileaction, :path => filepath}
218 elsif (parsing_descr == 1 || parsing_descr == 2) \
218 elsif (parsing_descr == 1 || parsing_descr == 2) \
219 && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\d+\s+(\S+)\t(.+)$/
219 && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\d+\s+(\S+)\t(.+)$/
220 parsing_descr = 2
220 parsing_descr = 2
221 fileaction = $1
221 fileaction = $1
222 filepath = $3
222 filepath = $3
223 files << {:action => fileaction, :path => filepath}
223 files << {:action => fileaction, :path => filepath}
224 elsif (parsing_descr == 1) && line.chomp.to_s == ""
224 elsif (parsing_descr == 1) && line.chomp.to_s == ""
225 parsing_descr = 2
225 parsing_descr = 2
226 elsif (parsing_descr == 1)
226 elsif (parsing_descr == 1)
227 changeset[:description] << line[4..-1]
227 changeset[:description] << line[4..-1]
228 end
228 end
229 end
229 end
230
230
231 if changeset[:commit]
231 if changeset[:commit]
232 revision = Revision.new({
232 revision = Revision.new({
233 :identifier => changeset[:commit],
233 :identifier => changeset[:commit],
234 :scmid => changeset[:commit],
234 :scmid => changeset[:commit],
235 :author => changeset[:author],
235 :author => changeset[:author],
236 :time => Time.parse(changeset[:date]),
236 :time => Time.parse(changeset[:date]),
237 :message => changeset[:description],
237 :message => changeset[:description],
238 :paths => files
238 :paths => files
239 })
239 })
240
240
241 if block_given?
241 if block_given?
242 yield revision
242 yield revision
243 else
243 else
244 revisions << revision
244 revisions << revision
245 end
245 end
246 end
246 end
247 end
247 end
248 revisions
248 revisions
249 rescue ScmCommandAborted
249 rescue ScmCommandAborted
250 revisions
250 revisions
251 end
251 end
252
252
253 def diff(path, identifier_from, identifier_to=nil)
253 def diff(path, identifier_from, identifier_to=nil)
254 path ||= ''
254 path ||= ''
255
255
256 if identifier_to
256 if identifier_to
257 cmd = "#{self.class.sq_bin} --git-dir #{target('')} diff --no-color #{shell_quote identifier_to} #{shell_quote identifier_from}"
257 cmd = "#{self.class.sq_bin} --git-dir #{target('')} diff --no-color #{shell_quote identifier_to} #{shell_quote identifier_from}"
258 else
258 else
259 cmd = "#{self.class.sq_bin} --git-dir #{target('')} show --no-color #{shell_quote identifier_from}"
259 cmd = "#{self.class.sq_bin} --git-dir #{target('')} show --no-color #{shell_quote identifier_from}"
260 end
260 end
261
261
262 cmd << " -- #{shell_quote path}" unless path.empty?
262 cmd << " -- #{shell_quote path}" unless path.empty?
263 diff = []
263 diff = []
264 shellout(cmd) do |io|
264 shellout(cmd) do |io|
265 io.each_line do |line|
265 io.each_line do |line|
266 diff << line
266 diff << line
267 end
267 end
268 end
268 end
269 return nil if $? && $?.exitstatus != 0
269 return nil if $? && $?.exitstatus != 0
270 diff
270 diff
271 end
271 end
272
272
273 def annotate(path, identifier=nil)
273 def annotate(path, identifier=nil)
274 identifier = 'HEAD' if identifier.blank?
274 identifier = 'HEAD' if identifier.blank?
275 cmd = "#{self.class.sq_bin} --git-dir #{target('')} blame -p #{shell_quote identifier} -- #{shell_quote path}"
275 cmd = "#{self.class.sq_bin} --git-dir #{target('')} blame -p #{shell_quote identifier} -- #{shell_quote path}"
276 blame = Annotate.new
276 blame = Annotate.new
277 content = nil
277 content = nil
278 shellout(cmd) { |io| io.binmode; content = io.read }
278 shellout(cmd) { |io| io.binmode; content = io.read }
279 return nil if $? && $?.exitstatus != 0
279 return nil if $? && $?.exitstatus != 0
280 # git annotates binary files
280 # git annotates binary files
281 return nil if content.is_binary_data?
281 return nil if content.is_binary_data?
282 identifier = ''
282 identifier = ''
283 # git shows commit author on the first occurrence only
283 # git shows commit author on the first occurrence only
284 authors_by_commit = {}
284 authors_by_commit = {}
285 content.split("\n").each do |line|
285 content.split("\n").each do |line|
286 if line =~ /^([0-9a-f]{39,40})\s.*/
286 if line =~ /^([0-9a-f]{39,40})\s.*/
287 identifier = $1
287 identifier = $1
288 elsif line =~ /^author (.+)/
288 elsif line =~ /^author (.+)/
289 authors_by_commit[identifier] = $1.strip
289 authors_by_commit[identifier] = $1.strip
290 elsif line =~ /^\t(.*)/
290 elsif line =~ /^\t(.*)/
291 blame.add_line($1, Revision.new(:identifier => identifier, :author => authors_by_commit[identifier]))
291 blame.add_line($1, Revision.new(:identifier => identifier, :author => authors_by_commit[identifier]))
292 identifier = ''
292 identifier = ''
293 author = ''
293 author = ''
294 end
294 end
295 end
295 end
296 blame
296 blame
297 end
297 end
298
298
299 def cat(path, identifier=nil)
299 def cat(path, identifier=nil)
300 if identifier.nil?
300 if identifier.nil?
301 identifier = 'HEAD'
301 identifier = 'HEAD'
302 end
302 end
303 cmd = "#{self.class.sq_bin} --git-dir #{target('')} show --no-color #{shell_quote(identifier + ':' + path)}"
303 cmd = "#{self.class.sq_bin} --git-dir #{target('')} show --no-color #{shell_quote(identifier + ':' + path)}"
304 cat = nil
304 cat = nil
305 shellout(cmd) do |io|
305 shellout(cmd) do |io|
306 io.binmode
306 io.binmode
307 cat = io.read
307 cat = io.read
308 end
308 end
309 return nil if $? && $?.exitstatus != 0
309 return nil if $? && $?.exitstatus != 0
310 cat
310 cat
311 end
311 end
312
312
313 class Revision < Redmine::Scm::Adapters::Revision
313 class Revision < Redmine::Scm::Adapters::Revision
314 # Returns the readable identifier
314 # Returns the readable identifier
315 def format_identifier
315 def format_identifier
316 identifier[0,8]
316 identifier[0,8]
317 end
317 end
318 end
318 end
319
319
320 def scm_cmd(*args, &block)
320 def scm_cmd(*args, &block)
321 repo_path = root_url || url
321 repo_path = root_url || url
322 full_args = [GIT_BIN, '--git-dir', repo_path]
322 full_args = [GIT_BIN, '--git-dir', repo_path]
323 full_args += args
323 full_args += args
324 ret = shellout(full_args.map { |e| shell_quote e.to_s }.join(' '), &block)
324 ret = shellout(full_args.map { |e| shell_quote e.to_s }.join(' '), &block)
325 if $? && $?.exitstatus != 0
325 if $? && $?.exitstatus != 0
326 raise ScmCommandAborted, "git exited with non-zero status: #{$?.exitstatus}"
326 raise ScmCommandAborted, "git exited with non-zero status: #{$?.exitstatus}"
327 end
327 end
328 ret
328 ret
329 end
329 end
330 private :scm_cmd
330 private :scm_cmd
331 end
331 end
332 end
332 end
333 end
333 end
334 end
334 end
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,120 +1,120
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 # This file includes UTF-8 "Felix Schäfer".
3 # This file includes UTF-8 "Felix Schäfer".
4 # We need to consider Ruby 1.9 compatibility.
4 # We need to consider Ruby 1.9 compatibility.
5
5
6 require File.expand_path('../../../../../../test_helper', __FILE__)
6 require File.expand_path('../../../../../../test_helper', __FILE__)
7 begin
7 begin
8 require 'mocha'
8 require 'mocha'
9
9
10 class GitAdapterTest < ActiveSupport::TestCase
10 class GitAdapterTest < ActiveSupport::TestCase
11 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository'
11 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository'
12
12
13 FELIX_UTF8 = "Felix Schäfer"
13 FELIX_UTF8 = "Felix Schäfer"
14 FELIX_HEX = "Felix Sch\xC3\xA4fer"
14 FELIX_HEX = "Felix Sch\xC3\xA4fer"
15
15
16 if File.directory?(REPOSITORY_PATH)
16 if File.directory?(REPOSITORY_PATH)
17 def setup
17 def setup
18 @adapter = Redmine::Scm::Adapters::GitAdapter.new(REPOSITORY_PATH)
18 @adapter = Redmine::Scm::Adapters::GitAdapter.new(REPOSITORY_PATH)
19 end
19 end
20
20
21 def test_scm_version
21 def test_scm_version
22 to_test = { "git version 1.7.3.4\n" => [1,7,3,4],
22 to_test = { "git version 1.7.3.4\n" => [1,7,3,4],
23 "1.6.1\n1.7\n1.8" => [1,6,1],
23 "1.6.1\n1.7\n1.8" => [1,6,1],
24 "1.6.2\r\n1.8.1\r\n1.9.1" => [1,6,2]}
24 "1.6.2\r\n1.8.1\r\n1.9.1" => [1,6,2]}
25 to_test.each do |s, v|
25 to_test.each do |s, v|
26 test_scm_version_for(s, v)
26 test_scm_version_for(s, v)
27 end
27 end
28 end
28 end
29
29
30 def test_branches
30 def test_branches
31 assert_equal @adapter.branches, ['master', 'test_branch']
31 assert_equal @adapter.branches, ['master', 'test-latin-1', 'test_branch']
32 end
32 end
33
33
34 def test_getting_all_revisions
34 def test_getting_all_revisions
35 assert_equal 15, @adapter.revisions('',nil,nil,:all => true).length
35 assert_equal 16, @adapter.revisions('',nil,nil,:all => true).length
36 end
36 end
37
37
38 def test_getting_certain_revisions
38 def test_getting_certain_revisions
39 assert_equal 1, @adapter.revisions('','899a15d^','899a15d').length
39 assert_equal 1, @adapter.revisions('','899a15d^','899a15d').length
40 end
40 end
41
41
42 def test_getting_revisions_with_spaces_in_filename
42 def test_getting_revisions_with_spaces_in_filename
43 assert_equal 1, @adapter.revisions("filemane with spaces.txt",
43 assert_equal 1, @adapter.revisions("filemane with spaces.txt",
44 nil, nil, :all => true).length
44 nil, nil, :all => true).length
45 end
45 end
46
46
47 def test_getting_revisions_with_leading_and_trailing_spaces_in_filename
47 def test_getting_revisions_with_leading_and_trailing_spaces_in_filename
48 assert_equal " filename with a leading space.txt ",
48 assert_equal " filename with a leading space.txt ",
49 @adapter.revisions(" filename with a leading space.txt ",
49 @adapter.revisions(" filename with a leading space.txt ",
50 nil, nil, :all => true)[0].paths[0][:path]
50 nil, nil, :all => true)[0].paths[0][:path]
51 end
51 end
52
52
53 def test_getting_entries_with_leading_and_trailing_spaces_in_filename
53 def test_getting_entries_with_leading_and_trailing_spaces_in_filename
54 assert_equal " filename with a leading space.txt ",
54 assert_equal " filename with a leading space.txt ",
55 @adapter.entries('',
55 @adapter.entries('',
56 '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c')[3].name
56 '83ca5fd546063a3c7dc2e568ba3355661a9e2b2c')[3].name
57 end
57 end
58
58
59 def test_annotate
59 def test_annotate
60 annotate = @adapter.annotate('sources/watchers_controller.rb')
60 annotate = @adapter.annotate('sources/watchers_controller.rb')
61 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
61 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
62 assert_equal 41, annotate.lines.size
62 assert_equal 41, annotate.lines.size
63 assert_equal "# This program is free software; you can redistribute it and/or", annotate.lines[4].strip
63 assert_equal "# This program is free software; you can redistribute it and/or", annotate.lines[4].strip
64 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
64 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
65 annotate.revisions[4].identifier
65 annotate.revisions[4].identifier
66 assert_equal "jsmith", annotate.revisions[4].author
66 assert_equal "jsmith", annotate.revisions[4].author
67 end
67 end
68
68
69 def test_annotate_moved_file
69 def test_annotate_moved_file
70 annotate = @adapter.annotate('renamed_test.txt')
70 annotate = @adapter.annotate('renamed_test.txt')
71 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
71 assert_kind_of Redmine::Scm::Adapters::Annotate, annotate
72 assert_equal 2, annotate.lines.size
72 assert_equal 2, annotate.lines.size
73 end
73 end
74
74
75 def test_last_rev
75 def test_last_rev
76 last_rev = @adapter.lastrev("README",
76 last_rev = @adapter.lastrev("README",
77 "4f26664364207fa8b1af9f8722647ab2d4ac5d43")
77 "4f26664364207fa8b1af9f8722647ab2d4ac5d43")
78 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.scmid
78 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.scmid
79 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.identifier
79 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", last_rev.identifier
80 assert_equal "Adam Soltys <asoltys@gmail.com>", last_rev.author
80 assert_equal "Adam Soltys <asoltys@gmail.com>", last_rev.author
81 assert_equal "2009-06-24 05:27:38".to_time, last_rev.time
81 assert_equal "2009-06-24 05:27:38".to_time, last_rev.time
82 end
82 end
83
83
84 def test_last_rev_with_spaces_in_filename
84 def test_last_rev_with_spaces_in_filename
85 last_rev = @adapter.lastrev("filemane with spaces.txt",
85 last_rev = @adapter.lastrev("filemane with spaces.txt",
86 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b")
86 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b")
87 str_felix_utf8 = FELIX_UTF8
87 str_felix_utf8 = FELIX_UTF8
88 str_felix_hex = FELIX_HEX
88 str_felix_hex = FELIX_HEX
89 last_rev_author = last_rev.author
89 last_rev_author = last_rev.author
90 if last_rev_author.respond_to?(:force_encoding)
90 if last_rev_author.respond_to?(:force_encoding)
91 last_rev_author.force_encoding('UTF-8')
91 last_rev_author.force_encoding('UTF-8')
92 end
92 end
93 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.scmid
93 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.scmid
94 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.identifier
94 assert_equal "ed5bb786bbda2dee66a2d50faf51429dbc043a7b", last_rev.identifier
95 assert_equal "#{str_felix_utf8} <felix@fachschaften.org>",
95 assert_equal "#{str_felix_utf8} <felix@fachschaften.org>",
96 last_rev.author
96 last_rev.author
97 assert_equal "#{str_felix_hex} <felix@fachschaften.org>",
97 assert_equal "#{str_felix_hex} <felix@fachschaften.org>",
98 last_rev.author
98 last_rev.author
99 assert_equal "2010-09-18 19:59:46".to_time, last_rev.time
99 assert_equal "2010-09-18 19:59:46".to_time, last_rev.time
100 end
100 end
101
101
102 private
102 private
103
103
104 def test_scm_version_for(scm_command_version, version)
104 def test_scm_version_for(scm_command_version, version)
105 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
105 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
106 assert_equal version, @adapter.class.scm_command_version
106 assert_equal version, @adapter.class.scm_command_version
107 end
107 end
108
108
109 else
109 else
110 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
110 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
111 def test_fake; assert true end
111 def test_fake; assert true end
112 end
112 end
113 end
113 end
114
114
115 rescue LoadError
115 rescue LoadError
116 class GitMochaFake < ActiveSupport::TestCase
116 class GitMochaFake < ActiveSupport::TestCase
117 def test_fake; assert(false, "Requires mocha to run those tests") end
117 def test_fake; assert(false, "Requires mocha to run those tests") end
118 end
118 end
119 end
119 end
120
120
@@ -1,125 +1,125
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class RepositoryGitTest < ActiveSupport::TestCase
20 class RepositoryGitTest < ActiveSupport::TestCase
21 fixtures :projects, :repositories, :enabled_modules, :users, :roles
21 fixtures :projects, :repositories, :enabled_modules, :users, :roles
22
22
23 # No '..' in the repository path
23 # No '..' in the repository path
24 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository'
24 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository'
25 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
25 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
26
26
27 FELIX_HEX = "Felix Sch\xC3\xA4fer"
27 FELIX_HEX = "Felix Sch\xC3\xA4fer"
28
28
29 def setup
29 def setup
30 Setting.commit_logs_encoding = 'UTF-8'
30 Setting.commit_logs_encoding = 'UTF-8'
31 @project = Project.find(3)
31 @project = Project.find(3)
32 @repository = Repository::Git.create(:project => @project, :url => REPOSITORY_PATH)
32 @repository = Repository::Git.create(:project => @project, :url => REPOSITORY_PATH)
33 assert @repository
33 assert @repository
34 end
34 end
35
35
36 if File.directory?(REPOSITORY_PATH)
36 if File.directory?(REPOSITORY_PATH)
37 def test_fetch_changesets_from_scratch
37 def test_fetch_changesets_from_scratch
38 @repository.fetch_changesets
38 @repository.fetch_changesets
39 @repository.reload
39 @repository.reload
40
40
41 assert_equal 15, @repository.changesets.count
41 assert_equal 16, @repository.changesets.count
42 assert_equal 24, @repository.changes.count
42 assert_equal 25, @repository.changes.count
43
43
44 commit = @repository.changesets.find(:first, :order => 'committed_on ASC')
44 commit = @repository.changesets.find(:first, :order => 'committed_on ASC')
45 assert_equal "Initial import.\nThe repository contains 3 files.", commit.comments
45 assert_equal "Initial import.\nThe repository contains 3 files.", commit.comments
46 assert_equal "jsmith <jsmith@foo.bar>", commit.committer
46 assert_equal "jsmith <jsmith@foo.bar>", commit.committer
47 assert_equal User.find_by_login('jsmith'), commit.user
47 assert_equal User.find_by_login('jsmith'), commit.user
48 # TODO: add a commit with commit time <> author time to the test repository
48 # TODO: add a commit with commit time <> author time to the test repository
49 assert_equal "2007-12-14 09:22:52".to_time, commit.committed_on
49 assert_equal "2007-12-14 09:22:52".to_time, commit.committed_on
50 assert_equal "2007-12-14".to_date, commit.commit_date
50 assert_equal "2007-12-14".to_date, commit.commit_date
51 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", commit.revision
51 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", commit.revision
52 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", commit.scmid
52 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", commit.scmid
53 assert_equal 3, commit.changes.count
53 assert_equal 3, commit.changes.count
54 change = commit.changes.sort_by(&:path).first
54 change = commit.changes.sort_by(&:path).first
55 assert_equal "README", change.path
55 assert_equal "README", change.path
56 assert_equal "A", change.action
56 assert_equal "A", change.action
57 end
57 end
58
58
59 def test_fetch_changesets_incremental
59 def test_fetch_changesets_incremental
60 @repository.fetch_changesets
60 @repository.fetch_changesets
61 # Remove the 3 latest changesets
61 # Remove the 3 latest changesets
62 @repository.changesets.find(:all, :order => 'committed_on DESC', :limit => 3).each(&:destroy)
62 @repository.changesets.find(:all, :order => 'committed_on DESC', :limit => 3).each(&:destroy)
63 @repository.reload
63 @repository.reload
64 assert_equal 12, @repository.changesets.count
64 assert_equal 13, @repository.changesets.count
65
65
66 @repository.fetch_changesets
66 @repository.fetch_changesets
67 assert_equal 15, @repository.changesets.count
67 assert_equal 16, @repository.changesets.count
68 end
68 end
69
69
70 def test_find_changeset_by_name
70 def test_find_changeset_by_name
71 @repository.fetch_changesets
71 @repository.fetch_changesets
72 @repository.reload
72 @repository.reload
73 ['7234cb2750b63f47bff735edc50a1c0a433c2518', '7234cb2750b'].each do |r|
73 ['7234cb2750b63f47bff735edc50a1c0a433c2518', '7234cb2750b'].each do |r|
74 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518',
74 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518',
75 @repository.find_changeset_by_name(r).revision
75 @repository.find_changeset_by_name(r).revision
76 end
76 end
77 end
77 end
78
78
79 def test_find_changeset_by_empty_name
79 def test_find_changeset_by_empty_name
80 @repository.fetch_changesets
80 @repository.fetch_changesets
81 @repository.reload
81 @repository.reload
82 ['', ' ', nil].each do |r|
82 ['', ' ', nil].each do |r|
83 assert_nil @repository.find_changeset_by_name(r)
83 assert_nil @repository.find_changeset_by_name(r)
84 end
84 end
85 end
85 end
86
86
87 def test_identifier
87 def test_identifier
88 @repository.fetch_changesets
88 @repository.fetch_changesets
89 @repository.reload
89 @repository.reload
90 c = @repository.changesets.find_by_revision('7234cb2750b63f47bff735edc50a1c0a433c2518')
90 c = @repository.changesets.find_by_revision('7234cb2750b63f47bff735edc50a1c0a433c2518')
91 assert_equal c.scmid, c.identifier
91 assert_equal c.scmid, c.identifier
92 end
92 end
93
93
94 def test_format_identifier
94 def test_format_identifier
95 @repository.fetch_changesets
95 @repository.fetch_changesets
96 @repository.reload
96 @repository.reload
97 c = @repository.changesets.find_by_revision('7234cb2750b63f47bff735edc50a1c0a433c2518')
97 c = @repository.changesets.find_by_revision('7234cb2750b63f47bff735edc50a1c0a433c2518')
98 assert_equal '7234cb27', c.format_identifier
98 assert_equal '7234cb27', c.format_identifier
99 end
99 end
100
100
101 def test_activities
101 def test_activities
102 c = Changeset.new(:repository => @repository,
102 c = Changeset.new(:repository => @repository,
103 :committed_on => Time.now,
103 :committed_on => Time.now,
104 :revision => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
104 :revision => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
105 :scmid => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
105 :scmid => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
106 :comments => 'test')
106 :comments => 'test')
107 assert c.event_title.include?('abc7234c:')
107 assert c.event_title.include?('abc7234c:')
108 assert_equal 'abc7234cb2750b63f47bff735edc50a1c0a433c2', c.event_url[:rev]
108 assert_equal 'abc7234cb2750b63f47bff735edc50a1c0a433c2', c.event_url[:rev]
109 end
109 end
110
110
111 def test_log_utf8
111 def test_log_utf8
112 @repository.fetch_changesets
112 @repository.fetch_changesets
113 @repository.reload
113 @repository.reload
114 str_felix_hex = FELIX_HEX
114 str_felix_hex = FELIX_HEX
115 if str_felix_hex.respond_to?(:force_encoding)
115 if str_felix_hex.respond_to?(:force_encoding)
116 str_felix_hex.force_encoding('UTF-8')
116 str_felix_hex.force_encoding('UTF-8')
117 end
117 end
118 c = @repository.changesets.find_by_revision('ed5bb786bbda2dee66a2d50faf51429dbc043a7b')
118 c = @repository.changesets.find_by_revision('ed5bb786bbda2dee66a2d50faf51429dbc043a7b')
119 assert_equal "#{str_felix_hex} <felix@fachschaften.org>", c.committer
119 assert_equal "#{str_felix_hex} <felix@fachschaften.org>", c.committer
120 end
120 end
121 else
121 else
122 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
122 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
123 def test_fake; assert true end
123 def test_fake; assert true end
124 end
124 end
125 end
125 end
General Comments 0
You need to be logged in to leave comments. Login now