Defect #8857 » Optimize-Git-operations-for-new-branches.patch
| app/models/repository/git.rb | ||
|---|---|---|
| 79 | 79 |
scm.tags |
| 80 | 80 |
end |
| 81 | 81 | |
| 82 |
def head_revisions |
|
| 83 |
scm.head_revisions |
|
| 84 |
end |
|
| 85 | ||
| 82 | 86 |
def default_branch |
| 83 | 87 |
scm.default_branch |
| 84 | 88 |
rescue Exception => e |
| ... | ... | |
| 129 | 133 |
# The repository can still be fully reloaded by calling #clear_changesets |
| 130 | 134 |
# before fetching changesets (eg. for offline resync) |
| 131 | 135 |
def fetch_changesets |
| 132 |
scm_brs = branches
|
|
| 133 |
return if scm_brs.nil? || scm_brs.empty?
|
|
| 136 |
new_head_scmids = head_revisions
|
|
| 137 |
return if new_head_scmids.nil? || new_head_scmids.empty?
|
|
| 134 | 138 |
h1 = extra_info || {}
|
| 135 | 139 |
h = h1.dup |
| 136 |
h["branches"] ||= {}
|
|
| 137 | 140 |
h["db_consistent"] ||= {}
|
| 138 | 141 |
if changesets.count == 0 |
| 139 | 142 |
h["db_consistent"]["ordering"] = 1 |
| ... | ... | |
| 144 | 147 |
merge_extra_info(h) |
| 145 | 148 |
self.save |
| 146 | 149 |
end |
| 147 |
scm_brs.each do |br1| |
|
| 148 |
br = br1.to_s |
|
| 149 |
from_scmid = nil |
|
| 150 |
from_scmid = h["branches"][br]["last_scmid"] if h["branches"][br] |
|
| 151 |
h["branches"][br] ||= {}
|
|
| 152 |
scm.revisions('', from_scmid, br, {:reverse => true}) do |rev|
|
|
| 150 |
h["head_scmids"] ||= [] |
|
| 151 |
transaction do |
|
| 152 |
scm.revisions('',
|
|
| 153 |
h["head_scmids"], new_head_scmids, |
|
| 154 |
{:reverse => true}) do |rev|
|
|
| 153 | 155 |
db_rev = find_changeset_by_name(rev.revision) |
| 154 |
transaction do |
|
| 155 |
if db_rev.nil? |
|
| 156 |
db_saved_rev = save_revision(rev) |
|
| 157 |
parents = {}
|
|
| 158 |
parents[db_saved_rev] = rev.parents unless rev.parents.nil? |
|
| 159 |
parents.each do |ch, chparents| |
|
| 160 |
ch.parents = chparents.collect{|rp| find_changeset_by_name(rp)}.compact
|
|
| 161 |
end |
|
| 156 |
if db_rev.nil? |
|
| 157 |
db_saved_rev = save_revision(rev) |
|
| 158 |
unless rev.parents.nil? |
|
| 159 |
db_saved_rev.parents = |
|
| 160 |
rev.parents.collect{|rp| find_changeset_by_name(rp)}.compact
|
|
| 162 | 161 |
end |
| 163 |
h["branches"][br]["last_scmid"] = rev.scmid |
|
| 164 |
merge_extra_info(h) |
|
| 165 |
self.save |
|
| 166 | 162 |
end |
| 167 | 163 |
end |
| 164 |
# Update saved head revisions. |
|
| 165 |
h["head_scmids"] = new_head_scmids.dup |
|
| 166 |
merge_extra_info(h) |
|
| 167 |
self.save |
|
| 168 | 168 |
end |
| 169 | 169 |
end |
| 170 | 170 | |
| db/migrate/20111129232941_reformat_git_extra_info.rb | ||
|---|---|---|
| 1 |
class ReformatGitExtraInfo < ActiveRecord::Migration |
|
| 2 |
def self.up |
|
| 3 |
Repository.find(:all, :conditions => ['type = ?', 'Git']).each do |repo| |
|
| 4 |
repo.extra_info['head_scmids'] = |
|
| 5 |
repo.extra_info['branches'].map{|br, h| h['last_scmid']}
|
|
| 6 |
repo.extra_info.delete('branches')
|
|
| 7 |
repo.write_attribute(:extra_info, repo.extra_info) |
|
| 8 |
repo.save |
|
| 9 |
end |
|
| 10 |
end |
|
| 11 | ||
| 12 |
def self.down |
|
| 13 |
raise ActiveRecord::IrreversibleMigration |
|
| 14 |
end |
|
| 15 |
end |
|
| lib/redmine/scm/adapters/git_adapter.rb | ||
|---|---|---|
| 102 | 102 |
nil |
| 103 | 103 |
end |
| 104 | 104 | |
| 105 |
def head_revisions |
|
| 106 |
return @head_revisions if @head_revisions |
|
| 107 |
cmd_args = %w|show-ref --heads| |
|
| 108 |
@head_revisions = scm_cmd(*cmd_args) do |io| |
|
| 109 |
io.map{|line| line.match('(.*?)\s+refs/heads/.*')[1]}.sort!
|
|
| 110 |
end |
|
| 111 |
rescue ScmCommandAborted |
|
| 112 |
nil |
|
| 113 |
end |
|
| 114 | ||
| 105 | 115 |
def default_branch |
| 106 | 116 |
bras = self.branches |
| 107 | 117 |
return nil if bras.nil? |
| ... | ... | |
| 187 | 197 |
nil |
| 188 | 198 |
end |
| 189 | 199 | |
| 190 |
def revisions(path, identifier_from, identifier_to, options={})
|
|
| 200 |
def revisions(path, excludes, includes, options={})
|
|
| 201 |
# Ensure that includes and excludes are lists (possibly empty). |
|
| 202 |
includes = [includes].compact.flatten |
|
| 203 |
excludes = [excludes].compact.flatten |
|
| 204 | ||
| 191 | 205 |
revs = Revisions.new |
| 192 | 206 |
cmd_args = %w|log --no-color --encoding=UTF-8 --raw --date=iso --pretty=fuller --parents| |
| 193 | 207 |
cmd_args << "--reverse" if options[:reverse] |
| 194 | 208 |
cmd_args << "--all" if options[:all] |
| 195 | 209 |
cmd_args << "-n" << "#{options[:limit].to_i}" if options[:limit]
|
| 196 |
from_to = "" |
|
| 197 |
from_to << "#{identifier_from}.." if identifier_from
|
|
| 198 |
from_to << "#{identifier_to}" if identifier_to
|
|
| 199 |
cmd_args << from_to if !from_to.empty? |
|
| 200 | 210 |
cmd_args << "--since=#{options[:since].strftime("%Y-%m-%d %H:%M:%S")}" if options[:since]
|
| 211 |
cmd_args.push(*includes) unless includes.empty? |
|
| 212 |
cmd_args.push("--not", *excludes) unless excludes.empty?
|
|
| 201 | 213 |
cmd_args << "--" << scm_iconv(@path_encoding, 'UTF-8', path) if path && !path.empty? |
| 202 | 214 | |
| 203 | 215 |
scm_cmd *cmd_args do |io| |
| ... | ... | |
| 284 | 296 |
end |
| 285 | 297 |
revs |
| 286 | 298 |
rescue ScmCommandAborted => e |
| 287 |
logger.error("git log #{from_to.to_s} error: #{e.message}")
|
|
| 299 |
logger.error("git log error: #{e.message}")
|
|
| 288 | 300 |
revs |
| 289 | 301 |
end |
| 290 | 302 | |
| test/unit/lib/redmine/scm/adapters/git_adapter_test.rb | ||
|---|---|---|
| 87 | 87 |
], @adapter.tags |
| 88 | 88 |
end |
| 89 | 89 | |
| 90 |
def test_head_revisions |
|
| 91 |
assert_equal [ |
|
| 92 |
"1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127", |
|
| 93 |
"67e7792ce20ccae2e4bb73eed09bb397819c8834", |
|
| 94 |
"83ca5fd546063a3c7dc2e568ba3355661a9e2b2c", |
|
| 95 |
"fba357b886984ee71185ad2065e65fc0417d9b92" |
|
| 96 |
], @adapter.head_revisions |
|
| 97 |
end |
|
| 98 | ||
| 90 | 99 |
def test_getting_all_revisions |
| 91 | 100 |
assert_equal 21, @adapter.revisions('',nil,nil,:all => true).length
|
| 92 | 101 |
end |
| ... | ... | |
| 95 | 104 |
assert_equal 1, @adapter.revisions('','899a15d^','899a15d').length
|
| 96 | 105 |
end |
| 97 | 106 | |
| 107 |
def test_getting_multiple_named_revisions |
|
| 108 |
revs = @adapter.revisions('',nil,['4f26664'])
|
|
| 109 |
assert_equal 13, revs.length |
|
| 110 |
rev_ids = revs.map{|rev| rev.identifier}
|
|
| 111 |
assert rev_ids.include?("4f26664364207fa8b1af9f8722647ab2d4ac5d43"),
|
|
| 112 |
"Expected #{rev_ids.inspect} to include \"4f26664364207fa8b1af9f8722647ab2d4ac5d43\""
|
|
| 113 | ||
| 114 |
revs = @adapter.revisions('',nil,['4f26664', '57ca437'])
|
|
| 115 |
assert_equal 15, revs.length |
|
| 116 |
rev_ids = revs.map{|rev| rev.identifier}
|
|
| 117 |
assert rev_ids.include?("4f26664364207fa8b1af9f8722647ab2d4ac5d43"),
|
|
| 118 |
"Expected #{rev_ids.inspect} to include \"4f26664364207fa8b1af9f8722647ab2d4ac5d43\""
|
|
| 119 |
assert rev_ids.include?("57ca437c0acbbcb749821fdf3726a1367056d364"),
|
|
| 120 |
"Expected #{rev_ids.inspect} to include \"57ca437c0acbbcb749821fdf3726a1367056d364\""
|
|
| 121 |
end |
|
| 122 | ||
| 123 |
def test_excluding_multiple_revisions |
|
| 124 |
revs = @adapter.revisions('',['32ae898'],'4f26664')
|
|
| 125 |
assert_equal 2, revs.length |
|
| 126 |
rev_ids = revs.map{|rev| rev.identifier}
|
|
| 127 |
assert rev_ids.include?("4f26664364207fa8b1af9f8722647ab2d4ac5d43"),
|
|
| 128 |
"Expected #{rev_ids.inspect} to include \"4f26664364207fa8b1af9f8722647ab2d4ac5d43\""
|
|
| 129 |
assert rev_ids.include?("deff712f05a90d96edbd70facc47d944be5897e3"),
|
|
| 130 |
"Expected #{rev_ids.inspect} to include \"deff712f05a90d96edbd70facc47d944be5897e3\""
|
|
| 131 | ||
| 132 |
revs = @adapter.revisions('',['713f494', '4a07fe3'],'4f26664')
|
|
| 133 |
assert_equal 5, revs.length |
|
| 134 |
rev_ids = revs.map{|rev| rev.identifier}
|
|
| 135 |
assert rev_ids.include?("4f26664364207fa8b1af9f8722647ab2d4ac5d43"),
|
|
| 136 |
"Expected #{rev_ids.inspect} to include \"4f26664364207fa8b1af9f8722647ab2d4ac5d43\""
|
|
| 137 |
assert rev_ids.include?("deff712f05a90d96edbd70facc47d944be5897e3"),
|
|
| 138 |
"Expected #{rev_ids.inspect} to include \"deff712f05a90d96edbd70facc47d944be5897e3\""
|
|
| 139 |
assert rev_ids.include?("7e61ac704deecde634b51e59daa8110435dcb3da"),
|
|
| 140 |
"Expected #{rev_ids.inspect} to include \"7e61ac704deecde634b51e59daa8110435dcb3da\""
|
|
| 141 |
end |
|
| 142 | ||
| 98 | 143 |
def test_revisions_reverse |
| 99 | 144 |
revs1 = @adapter.revisions('',nil,nil,{:all => true, :reverse => true })
|
| 100 | 145 |
assert_equal 21, revs1.length |
| test/unit/repository_git_test.rb | ||
|---|---|---|
| 87 | 87 |
assert_equal "README", change.path |
| 88 | 88 |
assert_equal "A", change.action |
| 89 | 89 | |
| 90 |
assert_equal 4, @repository.extra_info["branches"].size
|
|
| 90 |
assert_equal 4, @repository.extra_info["head_scmids"].size
|
|
| 91 | 91 |
end |
| 92 | 92 | |
| 93 | 93 |
def test_fetch_changesets_incremental |
| ... | ... | |
| 96 | 96 |
@project.reload |
| 97 | 97 |
assert_equal NUM_REV, @repository.changesets.count |
| 98 | 98 |
assert_equal 33, @repository.changes.count |
| 99 |
extra_info_db = @repository.extra_info["branches"]
|
|
| 99 |
extra_info_db = @repository.extra_info["head_scmids"]
|
|
| 100 | 100 |
assert_equal 4, extra_info_db.size |
| 101 |
assert_equal "1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127",
|
|
| 102 |
extra_info_db["latin-1-path-encoding"]["last_scmid"]
|
|
| 103 |
assert_equal "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
|
|
| 104 |
extra_info_db["master"]["last_scmid"]
|
|
| 101 |
assert extra_info_db.include?("1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127"),
|
|
| 102 |
"Expected #{extra_info_db.inspect} to include \"1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127\""
|
|
| 103 |
assert extra_info_db.include?("83ca5fd546063a3c7dc2e568ba3355661a9e2b2c"),
|
|
| 104 |
"Expected #{extra_info_db.inspect} to include \"83ca5fd546063a3c7dc2e568ba3355661a9e2b2c\""
|
|
| 105 | 105 | |
| 106 | 106 |
del_revs = [ |
| 107 | 107 |
"83ca5fd546063a3c7dc2e568ba3355661a9e2b2c", |
| ... | ... | |
| 118 | 118 |
cs1 = @repository.changesets |
| 119 | 119 |
assert_equal 15, cs1.count |
| 120 | 120 |
h = @repository.extra_info.dup |
| 121 |
h["branches"]["master"]["last_scmid"] =
|
|
| 122 |
"4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8"
|
|
| 121 |
h["head_scmids"].delete("83ca5fd546063a3c7dc2e568ba3355661a9e2b2c")
|
|
| 122 |
h["head_scmids"] << "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8"
|
|
| 123 | 123 |
@repository.merge_extra_info(h) |
| 124 | 124 |
@repository.save |
| 125 | 125 |
@project.reload |
| 126 |
extra_info_db_1 = @repository.extra_info["branches"]
|
|
| 127 |
assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8",
|
|
| 128 |
extra_info_db_1["master"]["last_scmid"]
|
|
| 126 |
extra_info_db_1 = @repository.extra_info["head_scmids"]
|
|
| 127 |
assert extra_info_db_1.include?("4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8"),
|
|
| 128 |
"Expected #{extra_info_db_1.inspect} to include \"4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8\""
|
|
| 129 | 129 | |
| 130 | 130 |
@repository.fetch_changesets |
| 131 | 131 |
@project.reload |
| ... | ... | |
| 136 | 136 |
assert_equal 0, @repository.changesets.count |
| 137 | 137 |
@repository.fetch_changesets |
| 138 | 138 |
@project.reload |
| 139 |
assert_equal NUM_REV, @repository.changesets.count |
|
| 140 |
extra_info_db = @repository.extra_info["branches"] |
|
| 139 |
assert_equal 21, @repository.changesets.count |
|
| 140 |
assert_equal 33, @repository.changes.count |
|
| 141 |
extra_info_db = @repository.extra_info["head_scmids"] |
|
| 141 | 142 |
assert_equal 4, extra_info_db.size |
| 142 |
assert_equal "1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127",
|
|
| 143 |
extra_info_db["latin-1-path-encoding"]["last_scmid"]
|
|
| 144 |
assert_equal "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
|
|
| 145 |
extra_info_db["master"]["last_scmid"]
|
|
| 143 |
assert extra_info_db.include?("1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127"),
|
|
| 144 |
"Expected #{extra_info_db.inspect} to include \"1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127\""
|
|
| 145 |
assert extra_info_db.include?("83ca5fd546063a3c7dc2e568ba3355661a9e2b2c"),
|
|
| 146 |
"Expected #{extra_info_db.inspect} to include \"83ca5fd546063a3c7dc2e568ba3355661a9e2b2c\""
|
|
| 146 | 147 | |
| 147 | 148 |
del_revs = [ |
| 148 | 149 |
"83ca5fd546063a3c7dc2e568ba3355661a9e2b2c", |
| ... | ... | |
| 159 | 160 |
cs1 = @repository.changesets |
| 160 | 161 |
assert_equal 15, cs1.count |
| 161 | 162 |
h = @repository.extra_info.dup |
| 162 |
h["branches"]["master"]["last_scmid"] =
|
|
| 163 |
"abcd1234efgh"
|
|
| 163 |
h["head_scmids"].delete("83ca5fd546063a3c7dc2e568ba3355661a9e2b2c")
|
|
| 164 |
h["head_scmids"] << "abcd1234efgh"
|
|
| 164 | 165 |
@repository.merge_extra_info(h) |
| 165 | 166 |
@repository.save |
| 166 | 167 |
@project.reload |
| 167 |
extra_info_db_1 = @repository.extra_info["branches"]
|
|
| 168 |
assert_equal "abcd1234efgh",
|
|
| 169 |
extra_info_db_1["master"]["last_scmid"]
|
|
| 168 |
extra_info_db_1 = @repository.extra_info["head_scmids"]
|
|
| 169 |
assert extra_info_db_1.include?("abcd1234efgh"),
|
|
| 170 |
"Expected #{extra_info_db_1.inspect} to include \"abcd1234efgh\""
|
|
| 170 | 171 | |
| 171 | 172 |
@repository.fetch_changesets |
| 172 | 173 |
@project.reload |
| ... | ... | |
| 230 | 231 |
assert_equal 15, cs1.count |
| 231 | 232 |
assert_equal 0, @repository.extra_info["db_consistent"]["ordering"] |
| 232 | 233 |
h = @repository.extra_info.dup |
| 233 |
h["branches"]["master"]["last_scmid"] =
|
|
| 234 |
"4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8"
|
|
| 234 |
h["head_scmids"].delete("83ca5fd546063a3c7dc2e568ba3355661a9e2b2c")
|
|
| 235 |
h["head_scmids"] << "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8"
|
|
| 235 | 236 |
@repository.merge_extra_info(h) |
| 236 | 237 |
@repository.save |
| 237 | 238 |
@project.reload |
| 238 |
extra_info_db_1 = @repository.extra_info["branches"]
|
|
| 239 |
assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8",
|
|
| 240 |
extra_info_db_1["master"]["last_scmid"]
|
|
| 239 |
extra_info_db_1 = @repository.extra_info["head_scmids"]
|
|
| 240 |
assert extra_info_db_1.include?("4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8"),
|
|
| 241 |
"Expected #{extra_info_db_1.inspect} to include \"4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8\""
|
|
| 241 | 242 | |
| 242 | 243 |
@repository.fetch_changesets |
| 243 | 244 |
assert_equal NUM_REV, @repository.changesets.count |
| ... | ... | |
| 486 | 487 |
assert_equal 0, @repository.changesets.count |
| 487 | 488 |
@repository.fetch_changesets |
| 488 | 489 |
@project.reload |
| 489 |
assert_equal NUM_REV, @repository.changesets.count |
|
| 490 |
%w|67e7792ce20ccae2e4bb73eed09bb397819c8834 67e7792ce20cca|.each do |r1| |
|
| 490 |
%w|1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127 1ca7f5ed374f3c|.each do |r1| |
|
| 491 | 491 |
changeset = @repository.find_changeset_by_name(r1) |
| 492 | 492 |
assert_nil changeset.next |
| 493 | 493 |
end |