##// END OF EJS Templates
scm: git: backout r8840 (#8857, #9472)...
Toshi MARUYAMA -
r9021:374c2aa70500
parent child
Show More
@@ -1,237 +1,232
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 # Copyright (C) 2007 Patrick Aljord patcito@ŋmail.com
3 # Copyright (C) 2007 Patrick Aljord patcito@ŋmail.com
4 #
4 #
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
8 # of the License, or (at your option) any later version.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
18
19 require 'redmine/scm/adapters/git_adapter'
19 require 'redmine/scm/adapters/git_adapter'
20
20
21 class Repository::Git < Repository
21 class Repository::Git < Repository
22 attr_protected :root_url
22 attr_protected :root_url
23 validates_presence_of :url
23 validates_presence_of :url
24
24
25 def self.human_attribute_name(attribute_key_name, *args)
25 def self.human_attribute_name(attribute_key_name, *args)
26 attr_name = attribute_key_name.to_s
26 attr_name = attribute_key_name.to_s
27 if attr_name == "url"
27 if attr_name == "url"
28 attr_name = "path_to_repository"
28 attr_name = "path_to_repository"
29 end
29 end
30 super(attr_name, *args)
30 super(attr_name, *args)
31 end
31 end
32
32
33 def self.scm_adapter_class
33 def self.scm_adapter_class
34 Redmine::Scm::Adapters::GitAdapter
34 Redmine::Scm::Adapters::GitAdapter
35 end
35 end
36
36
37 def self.scm_name
37 def self.scm_name
38 'Git'
38 'Git'
39 end
39 end
40
40
41 def report_last_commit
41 def report_last_commit
42 extra_report_last_commit
42 extra_report_last_commit
43 end
43 end
44
44
45 def extra_report_last_commit
45 def extra_report_last_commit
46 return false if extra_info.nil?
46 return false if extra_info.nil?
47 v = extra_info["extra_report_last_commit"]
47 v = extra_info["extra_report_last_commit"]
48 return false if v.nil?
48 return false if v.nil?
49 v.to_s != '0'
49 v.to_s != '0'
50 end
50 end
51
51
52 def supports_directory_revisions?
52 def supports_directory_revisions?
53 true
53 true
54 end
54 end
55
55
56 def supports_revision_graph?
56 def supports_revision_graph?
57 true
57 true
58 end
58 end
59
59
60 def repo_log_encoding
60 def repo_log_encoding
61 'UTF-8'
61 'UTF-8'
62 end
62 end
63
63
64 # Returns the identifier for the given git changeset
64 # Returns the identifier for the given git changeset
65 def self.changeset_identifier(changeset)
65 def self.changeset_identifier(changeset)
66 changeset.scmid
66 changeset.scmid
67 end
67 end
68
68
69 # Returns the readable identifier for the given git changeset
69 # Returns the readable identifier for the given git changeset
70 def self.format_changeset_identifier(changeset)
70 def self.format_changeset_identifier(changeset)
71 changeset.revision[0, 8]
71 changeset.revision[0, 8]
72 end
72 end
73
73
74 def branches
74 def branches
75 scm.branches
75 scm.branches
76 end
76 end
77
77
78 def tags
78 def tags
79 scm.tags
79 scm.tags
80 end
80 end
81
81
82 def default_branch
82 def default_branch
83 scm.default_branch
83 scm.default_branch
84 rescue Exception => e
84 rescue Exception => e
85 logger.error "git: error during get default branch: #{e.message}"
85 logger.error "git: error during get default branch: #{e.message}"
86 nil
86 nil
87 end
87 end
88
88
89 def find_changeset_by_name(name)
89 def find_changeset_by_name(name)
90 return nil if name.nil? || name.empty?
90 return nil if name.nil? || name.empty?
91 e = changesets.find(:first, :conditions => ['revision = ?', name.to_s])
91 e = changesets.find(:first, :conditions => ['revision = ?', name.to_s])
92 return e if e
92 return e if e
93 changesets.find(:first, :conditions => ['scmid LIKE ?', "#{name}%"])
93 changesets.find(:first, :conditions => ['scmid LIKE ?', "#{name}%"])
94 end
94 end
95
95
96 def entries(path=nil, identifier=nil)
96 def entries(path=nil, identifier=nil)
97 scm.entries(path,
97 scm.entries(path,
98 identifier,
98 identifier,
99 options = {:report_last_commit => extra_report_last_commit})
99 options = {:report_last_commit => extra_report_last_commit})
100 end
100 end
101
101
102 # With SCMs that have a sequential commit numbering,
102 # With SCMs that have a sequential commit numbering,
103 # such as Subversion and Mercurial,
103 # such as Subversion and Mercurial,
104 # Redmine is able to be clever and only fetch changesets
104 # Redmine is able to be clever and only fetch changesets
105 # going forward from the most recent one it knows about.
105 # going forward from the most recent one it knows about.
106 #
106 #
107 # However, Git does not have a sequential commit numbering.
107 # However, Git does not have a sequential commit numbering.
108 #
108 #
109 # In order to fetch only new adding revisions,
109 # In order to fetch only new adding revisions,
110 # Redmine needs to save "heads".
110 # Redmine needs to save "heads".
111 #
111 #
112 # In Git and Mercurial, revisions are not in date order.
112 # In Git and Mercurial, revisions are not in date order.
113 # Redmine Mercurial fixed issues.
113 # Redmine Mercurial fixed issues.
114 # * Redmine Takes Too Long On Large Mercurial Repository
114 # * Redmine Takes Too Long On Large Mercurial Repository
115 # http://www.redmine.org/issues/3449
115 # http://www.redmine.org/issues/3449
116 # * Sorting for changesets might go wrong on Mercurial repos
116 # * Sorting for changesets might go wrong on Mercurial repos
117 # http://www.redmine.org/issues/3567
117 # http://www.redmine.org/issues/3567
118 #
118 #
119 # Database revision column is text, so Redmine can not sort by revision.
119 # Database revision column is text, so Redmine can not sort by revision.
120 # Mercurial has revision number, and revision number guarantees revision order.
120 # Mercurial has revision number, and revision number guarantees revision order.
121 # Redmine Mercurial model stored revisions ordered by database id to database.
121 # Redmine Mercurial model stored revisions ordered by database id to database.
122 # So, Redmine Mercurial model can use correct ordering revisions.
122 # So, Redmine Mercurial model can use correct ordering revisions.
123 #
123 #
124 # Redmine Mercurial adapter uses "hg log -r 0:tip --limit 10"
124 # Redmine Mercurial adapter uses "hg log -r 0:tip --limit 10"
125 # to get limited revisions from old to new.
125 # to get limited revisions from old to new.
126 # But, Git 1.7.3.4 does not support --reverse with -n or --skip.
126 # But, Git 1.7.3.4 does not support --reverse with -n or --skip.
127 #
127 #
128 # The repository can still be fully reloaded by calling #clear_changesets
128 # The repository can still be fully reloaded by calling #clear_changesets
129 # before fetching changesets (eg. for offline resync)
129 # before fetching changesets (eg. for offline resync)
130 def fetch_changesets
130 def fetch_changesets
131 scm_brs = branches
131 scm_brs = branches
132 return if scm_brs.nil? || scm_brs.empty?
132 return if scm_brs.nil? || scm_brs.empty?
133
133
134 h1 = extra_info || {}
134 h1 = extra_info || {}
135 h = h1.dup
135 h = h1.dup
136 repo_heads = scm_brs.map{ |br| br.scmid }
136 repo_heads = scm_brs.map{ |br| br.scmid }
137 h["heads"] ||= []
137 h["heads"] ||= []
138 prev_db_heads = h["heads"].dup
138 prev_db_heads = h["heads"].dup
139 if prev_db_heads.empty?
139 if prev_db_heads.empty?
140 prev_db_heads += heads_from_branches_hash
140 prev_db_heads += heads_from_branches_hash
141 end
141 end
142 return if prev_db_heads.sort == repo_heads.sort
142 return if prev_db_heads.sort == repo_heads.sort
143
143
144 h["db_consistent"] ||= {}
144 h["db_consistent"] ||= {}
145 if changesets.count == 0
145 if changesets.count == 0
146 h["db_consistent"]["ordering"] = 1
146 h["db_consistent"]["ordering"] = 1
147 merge_extra_info(h)
147 merge_extra_info(h)
148 self.save
148 self.save
149 elsif ! h["db_consistent"].has_key?("ordering")
149 elsif ! h["db_consistent"].has_key?("ordering")
150 h["db_consistent"]["ordering"] = 0
150 h["db_consistent"]["ordering"] = 0
151 merge_extra_info(h)
151 merge_extra_info(h)
152 self.save
152 self.save
153 end
153 end
154
154
155 save_revisions(prev_db_heads, repo_heads)
155 save_revisions(prev_db_heads, repo_heads)
156 end
156 end
157
157
158 def save_revisions(prev_db_heads, repo_heads)
158 def save_revisions(prev_db_heads, repo_heads)
159 h = {}
159 h = {}
160 opts = {}
160 opts = {}
161 opts[:reverse] = true
161 opts[:reverse] = true
162 opts[:excludes] = prev_db_heads
162 opts[:excludes] = prev_db_heads
163 opts[:includes] = repo_heads
163 opts[:includes] = repo_heads
164 begin
164 begin
165 cnt = 0
166 scm.revisions('', nil, nil, opts) do |rev|
165 scm.revisions('', nil, nil, opts) do |rev|
167 cnt += 1
168 db_rev = find_changeset_by_name(rev.scmid)
166 db_rev = find_changeset_by_name(rev.scmid)
169 if db_rev.nil?
167 transaction do
170 transaction do
168 if db_rev.nil?
171 db_saved_rev = save_revision(rev)
169 db_saved_rev = save_revision(rev)
172 parents = {}
170 parents = {}
173 parents[db_saved_rev] = rev.parents unless rev.parents.nil?
171 parents[db_saved_rev] = rev.parents unless rev.parents.nil?
174 parents.each do |ch, chparents|
172 parents.each do |ch, chparents|
175 ch.parents = chparents.collect{|rp| find_changeset_by_name(rp)}.compact
173 ch.parents = chparents.collect{|rp| find_changeset_by_name(rp)}.compact
176 end
174 end
177 end
175 end
178 end
179 if cnt > 100
180 cnt = 0
181 h["heads"] = prev_db_heads.dup
176 h["heads"] = prev_db_heads.dup
182 h["heads"] << rev.scmid
177 h["heads"] << rev.scmid
183 merge_extra_info(h)
178 merge_extra_info(h)
184 self.save
179 self.save
185 end
180 end
186 end
181 end
187 h["heads"] = repo_heads.dup
182 h["heads"] = repo_heads.dup
188 merge_extra_info(h)
183 merge_extra_info(h)
189 self.save
184 self.save
190 rescue Redmine::Scm::Adapters::CommandFailed => e
185 rescue Redmine::Scm::Adapters::CommandFailed => e
191 logger.error("save revisions error: #{e.message}")
186 logger.error("save revisions error: #{e.message}")
192 end
187 end
193 end
188 end
194 private :save_revisions
189 private :save_revisions
195
190
196 def save_revision(rev)
191 def save_revision(rev)
197 changeset = Changeset.new(
192 changeset = Changeset.new(
198 :repository => self,
193 :repository => self,
199 :revision => rev.identifier,
194 :revision => rev.identifier,
200 :scmid => rev.scmid,
195 :scmid => rev.scmid,
201 :committer => rev.author,
196 :committer => rev.author,
202 :committed_on => rev.time,
197 :committed_on => rev.time,
203 :comments => rev.message
198 :comments => rev.message
204 )
199 )
205 if changeset.save
200 if changeset.save
206 rev.paths.each do |file|
201 rev.paths.each do |file|
207 Change.create(
202 Change.create(
208 :changeset => changeset,
203 :changeset => changeset,
209 :action => file[:action],
204 :action => file[:action],
210 :path => file[:path])
205 :path => file[:path])
211 end
206 end
212 end
207 end
213 changeset
208 changeset
214 end
209 end
215 private :save_revision
210 private :save_revision
216
211
217 def heads_from_branches_hash
212 def heads_from_branches_hash
218 h1 = extra_info || {}
213 h1 = extra_info || {}
219 h = h1.dup
214 h = h1.dup
220 h["branches"] ||= {}
215 h["branches"] ||= {}
221 h['branches'].map{|br, hs| hs['last_scmid']}
216 h['branches'].map{|br, hs| hs['last_scmid']}
222 end
217 end
223
218
224 def latest_changesets(path,rev,limit=10)
219 def latest_changesets(path,rev,limit=10)
225 revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
220 revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
226 return [] if revisions.nil? || revisions.empty?
221 return [] if revisions.nil? || revisions.empty?
227
222
228 changesets.find(
223 changesets.find(
229 :all,
224 :all,
230 :conditions => [
225 :conditions => [
231 "scmid IN (?)",
226 "scmid IN (?)",
232 revisions.map!{|c| c.scmid}
227 revisions.map!{|c| c.scmid}
233 ],
228 ],
234 :order => 'committed_on DESC'
229 :order => 'committed_on DESC'
235 )
230 )
236 end
231 end
237 end
232 end
General Comments 0
You need to be logged in to leave comments. Login now