##// END OF EJS Templates
scm: model: git: set revision graph support true (#5501)...
Toshi MARUYAMA -
r7597:57afa111a993
parent child
Show More
@@ -1,201 +1,205
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)
25 def self.human_attribute_name(attribute_key_name)
26 attr_name = attribute_key_name
26 attr_name = attribute_key_name
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)
30 super(attr_name)
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?
57 true
58 end
59
56 def repo_log_encoding
60 def repo_log_encoding
57 'UTF-8'
61 'UTF-8'
58 end
62 end
59
63
60 # Returns the identifier for the given git changeset
64 # Returns the identifier for the given git changeset
61 def self.changeset_identifier(changeset)
65 def self.changeset_identifier(changeset)
62 changeset.scmid
66 changeset.scmid
63 end
67 end
64
68
65 # Returns the readable identifier for the given git changeset
69 # Returns the readable identifier for the given git changeset
66 def self.format_changeset_identifier(changeset)
70 def self.format_changeset_identifier(changeset)
67 changeset.revision[0, 8]
71 changeset.revision[0, 8]
68 end
72 end
69
73
70 def branches
74 def branches
71 scm.branches
75 scm.branches
72 end
76 end
73
77
74 def tags
78 def tags
75 scm.tags
79 scm.tags
76 end
80 end
77
81
78 def default_branch
82 def default_branch
79 scm.default_branch
83 scm.default_branch
80 rescue Exception => e
84 rescue Exception => e
81 logger.error "git: error during get default branch: #{e.message}"
85 logger.error "git: error during get default branch: #{e.message}"
82 nil
86 nil
83 end
87 end
84
88
85 def find_changeset_by_name(name)
89 def find_changeset_by_name(name)
86 return nil if name.nil? || name.empty?
90 return nil if name.nil? || name.empty?
87 e = changesets.find(:first, :conditions => ['revision = ?', name.to_s])
91 e = changesets.find(:first, :conditions => ['revision = ?', name.to_s])
88 return e if e
92 return e if e
89 changesets.find(:first, :conditions => ['scmid LIKE ?', "#{name}%"])
93 changesets.find(:first, :conditions => ['scmid LIKE ?', "#{name}%"])
90 end
94 end
91
95
92 def entries(path=nil, identifier=nil)
96 def entries(path=nil, identifier=nil)
93 scm.entries(path,
97 scm.entries(path,
94 identifier,
98 identifier,
95 options = {:report_last_commit => extra_report_last_commit})
99 options = {:report_last_commit => extra_report_last_commit})
96 end
100 end
97
101
98 # With SCMs that have a sequential commit numbering,
102 # With SCMs that have a sequential commit numbering,
99 # such as Subversion and Mercurial,
103 # such as Subversion and Mercurial,
100 # Redmine is able to be clever and only fetch changesets
104 # Redmine is able to be clever and only fetch changesets
101 # going forward from the most recent one it knows about.
105 # going forward from the most recent one it knows about.
102 #
106 #
103 # However, Git does not have a sequential commit numbering.
107 # However, Git does not have a sequential commit numbering.
104 #
108 #
105 # In order to fetch only new adding revisions,
109 # In order to fetch only new adding revisions,
106 # Redmine needs to parse revisions per branch.
110 # Redmine needs to parse revisions per branch.
107 # Branch "last_scmid" is for this requirement.
111 # Branch "last_scmid" is for this requirement.
108 #
112 #
109 # In Git and Mercurial, revisions are not in date order.
113 # In Git and Mercurial, revisions are not in date order.
110 # Redmine Mercurial fixed issues.
114 # Redmine Mercurial fixed issues.
111 # * Redmine Takes Too Long On Large Mercurial Repository
115 # * Redmine Takes Too Long On Large Mercurial Repository
112 # http://www.redmine.org/issues/3449
116 # http://www.redmine.org/issues/3449
113 # * Sorting for changesets might go wrong on Mercurial repos
117 # * Sorting for changesets might go wrong on Mercurial repos
114 # http://www.redmine.org/issues/3567
118 # http://www.redmine.org/issues/3567
115 #
119 #
116 # Database revision column is text, so Redmine can not sort by revision.
120 # Database revision column is text, so Redmine can not sort by revision.
117 # Mercurial has revision number, and revision number guarantees revision order.
121 # Mercurial has revision number, and revision number guarantees revision order.
118 # Redmine Mercurial model stored revisions ordered by database id to database.
122 # Redmine Mercurial model stored revisions ordered by database id to database.
119 # So, Redmine Mercurial model can use correct ordering revisions.
123 # So, Redmine Mercurial model can use correct ordering revisions.
120 #
124 #
121 # Redmine Mercurial adapter uses "hg log -r 0:tip --limit 10"
125 # Redmine Mercurial adapter uses "hg log -r 0:tip --limit 10"
122 # to get limited revisions from old to new.
126 # to get limited revisions from old to new.
123 # But, Git 1.7.3.4 does not support --reverse with -n or --skip.
127 # But, Git 1.7.3.4 does not support --reverse with -n or --skip.
124 #
128 #
125 # The repository can still be fully reloaded by calling #clear_changesets
129 # The repository can still be fully reloaded by calling #clear_changesets
126 # before fetching changesets (eg. for offline resync)
130 # before fetching changesets (eg. for offline resync)
127 def fetch_changesets
131 def fetch_changesets
128 scm_brs = branches
132 scm_brs = branches
129 return if scm_brs.nil? || scm_brs.empty?
133 return if scm_brs.nil? || scm_brs.empty?
130 h1 = extra_info || {}
134 h1 = extra_info || {}
131 h = h1.dup
135 h = h1.dup
132 h["branches"] ||= {}
136 h["branches"] ||= {}
133 h["db_consistent"] ||= {}
137 h["db_consistent"] ||= {}
134 if changesets.count == 0
138 if changesets.count == 0
135 h["db_consistent"]["ordering"] = 1
139 h["db_consistent"]["ordering"] = 1
136 merge_extra_info(h)
140 merge_extra_info(h)
137 self.save
141 self.save
138 elsif ! h["db_consistent"].has_key?("ordering")
142 elsif ! h["db_consistent"].has_key?("ordering")
139 h["db_consistent"]["ordering"] = 0
143 h["db_consistent"]["ordering"] = 0
140 merge_extra_info(h)
144 merge_extra_info(h)
141 self.save
145 self.save
142 end
146 end
143 scm_brs.each do |br1|
147 scm_brs.each do |br1|
144 br = br1.to_s
148 br = br1.to_s
145 from_scmid = nil
149 from_scmid = nil
146 from_scmid = h["branches"][br]["last_scmid"] if h["branches"][br]
150 from_scmid = h["branches"][br]["last_scmid"] if h["branches"][br]
147 h["branches"][br] ||= {}
151 h["branches"][br] ||= {}
148 scm.revisions('', from_scmid, br, {:reverse => true}) do |rev|
152 scm.revisions('', from_scmid, br, {:reverse => true}) do |rev|
149 db_rev = find_changeset_by_name(rev.revision)
153 db_rev = find_changeset_by_name(rev.revision)
150 transaction do
154 transaction do
151 if db_rev.nil?
155 if db_rev.nil?
152 db_saved_rev = save_revision(rev)
156 db_saved_rev = save_revision(rev)
153 parents = {}
157 parents = {}
154 parents[db_saved_rev] = rev.parents unless rev.parents.nil?
158 parents[db_saved_rev] = rev.parents unless rev.parents.nil?
155 parents.each do |ch, chparents|
159 parents.each do |ch, chparents|
156 ch.parents = chparents.collect{|rp| find_changeset_by_name(rp)}.compact
160 ch.parents = chparents.collect{|rp| find_changeset_by_name(rp)}.compact
157 end
161 end
158 end
162 end
159 h["branches"][br]["last_scmid"] = rev.scmid
163 h["branches"][br]["last_scmid"] = rev.scmid
160 merge_extra_info(h)
164 merge_extra_info(h)
161 self.save
165 self.save
162 end
166 end
163 end
167 end
164 end
168 end
165 end
169 end
166
170
167 def save_revision(rev)
171 def save_revision(rev)
168 changeset = Changeset.new(
172 changeset = Changeset.new(
169 :repository => self,
173 :repository => self,
170 :revision => rev.identifier,
174 :revision => rev.identifier,
171 :scmid => rev.scmid,
175 :scmid => rev.scmid,
172 :committer => rev.author,
176 :committer => rev.author,
173 :committed_on => rev.time,
177 :committed_on => rev.time,
174 :comments => rev.message
178 :comments => rev.message
175 )
179 )
176 if changeset.save
180 if changeset.save
177 rev.paths.each do |file|
181 rev.paths.each do |file|
178 Change.create(
182 Change.create(
179 :changeset => changeset,
183 :changeset => changeset,
180 :action => file[:action],
184 :action => file[:action],
181 :path => file[:path])
185 :path => file[:path])
182 end
186 end
183 end
187 end
184 changeset
188 changeset
185 end
189 end
186 private :save_revision
190 private :save_revision
187
191
188 def latest_changesets(path,rev,limit=10)
192 def latest_changesets(path,rev,limit=10)
189 revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
193 revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
190 return [] if revisions.nil? || revisions.empty?
194 return [] if revisions.nil? || revisions.empty?
191
195
192 changesets.find(
196 changesets.find(
193 :all,
197 :all,
194 :conditions => [
198 :conditions => [
195 "scmid IN (?)",
199 "scmid IN (?)",
196 revisions.map!{|c| c.scmid}
200 revisions.map!{|c| c.scmid}
197 ],
201 ],
198 :order => 'committed_on DESC'
202 :order => 'committed_on DESC'
199 )
203 )
200 end
204 end
201 end
205 end
General Comments 0
You need to be logged in to leave comments. Login now