Feature #4455 » ya-hg-overhaul-0.9-stable-2010-03-27.patch
| app/models/changeset.rb | ||
|---|---|---|
| 152 | 152 | def self.normalize_comments(str) | 
| 153 | 153 | to_utf8(str.to_s.strip) | 
| 154 | 154 | end | 
| 155 | ||
| 156 | # Creates a new Change from it's common parameters | |
| 157 | def create_change(change) | |
| 158 | Change.create(:changeset => self, | |
| 159 | :action => change[:action], | |
| 160 | :path => change[:path], | |
| 161 | :from_path => change[:from_path], | |
| 162 | :from_revision => change[:from_revision]) | |
| 163 | end | |
| 155 | 164 |  | 
| 156 | 165 | private | 
| 157 | 166 | |
| app/models/repository/darcs.rb | ||
|---|---|---|
| 85 | 85 | :comments => revision.message) | 
| 86 | 86 |  | 
| 87 | 87 | revision.paths.each do |change| | 
| 88 | Change.create(:changeset => changeset, | |
| 89 | :action => change[:action], | |
| 90 | :path => change[:path], | |
| 91 | :from_path => change[:from_path], | |
| 92 | :from_revision => change[:from_revision]) | |
| 88 | changeset.create_change(change) | |
| 93 | 89 | end | 
| 94 | 90 | next_rev += 1 | 
| 95 | 91 | end if revisions | 
| app/models/repository/mercurial.rb | ||
|---|---|---|
| 78 | 78 | :comments => revision.message) | 
| 79 | 79 |  | 
| 80 | 80 | revision.paths.each do |change| | 
| 81 | Change.create(:changeset => changeset, | |
| 82 | :action => change[:action], | |
| 83 | :path => change[:path], | |
| 84 | :from_path => change[:from_path], | |
| 85 | :from_revision => change[:from_revision]) | |
| 81 | changeset.create_change(change) | |
| 86 | 82 | end | 
| 87 | 83 | end | 
| 88 | 84 | end unless revisions.nil? | 
| app/models/repository/subversion.rb | ||
|---|---|---|
| 63 | 63 | :comments => revision.message) | 
| 64 | 64 |  | 
| 65 | 65 | revision.paths.each do |change| | 
| 66 | Change.create(:changeset => changeset, | |
| 67 | :action => change[:action], | |
| 68 | :path => change[:path], | |
| 69 | :from_path => change[:from_path], | |
| 70 | :from_revision => change[:from_revision]) | |
| 66 | changeset.create_change(change) | |
| 71 | 67 | end unless changeset.new_record? | 
| 72 | 68 | end | 
| 73 | 69 | end unless revisions.nil? | 
| extra/mercurial/redminehelper.py | ||
|---|---|---|
| 1 | # redminehelper: draft extension for Mercurial | |
| 2 | # it's a draft to show a possible way to explore repository by the Redmine overhaul patch | |
| 3 | # see: http://www.redmine.org/issues/4455 | |
| 4 | # | |
| 5 | # Copyright 2010 Alessio Franceschelli (alefranz.net) | |
| 6 | # | |
| 7 | # This software may be used and distributed according to the terms of the | |
| 8 | # GNU General Public License version 2 or any later version. | |
| 9 | ||
| 10 | '''command to list revision of each file | |
| 11 | ''' | |
| 12 | ||
| 13 | from mercurial import cmdutil, commands | |
| 14 | from mercurial.i18n import _ | |
| 15 | ||
| 16 | def overhaul(ui, repo, rev=None, **opts): | |
| 17 | mf = repo[rev].manifest() | |
| 18 | for f in repo[rev]: | |
| 19 | try: | |
| 20 | fctx = repo.filectx(f, fileid=mf[f]) | |
| 21 | ctx = fctx.changectx() | |
| 22 |             ui.write('%s\t%d\t%s\n' % | |
| 23 | (ctx,fctx.size(),f)) | |
| 24 | except LookupError: | |
| 25 | pass | |
| 26 | ||
| 27 | cmdtable = { | |
| 28 |     'overhaul': (overhaul,commands.templateopts, _('hg overhaul [rev]')) | |
| 29 | } | |
| extra/mercurial/redminehelper.py | ||
|---|---|---|
| 1 | # redminehelper: draft extension for Mercurial | |
| 1 | # redminehelper: Redmine helper extension for Mercurial | |
| 2 | 2 | # it's a draft to show a possible way to explore repository by the Redmine overhaul patch | 
| 3 | 3 | # see: http://www.redmine.org/issues/4455 | 
| 4 | 4 | # | 
| 5 | 5 | # Copyright 2010 Alessio Franceschelli (alefranz.net) | 
| 6 | # Copyright 2010 Yuya Nishihara <yuya@tcha.org> | |
| 6 | 7 | # | 
| 7 | 8 | # This software may be used and distributed according to the terms of the | 
| 8 | 9 | # GNU General Public License version 2 or any later version. | 
| ... | ... | |
| 10 | 11 | '''command to list revision of each file | 
| 11 | 12 | ''' | 
| 12 | 13 | |
| 13 | from mercurial import cmdutil, commands | |
| 14 | from mercurial.i18n import _ | |
| 14 | import re, time | |
| 15 | from mercurial import cmdutil, commands, node, error | |
| 15 | 16 | |
| 16 | def overhaul(ui, repo, rev=None, **opts): | |
| 17 | SPECIAL_TAGS = ('tip',) | |
| 18 | ||
| 19 | def rhsummary(ui, repo, **opts): | |
| 20 | """output the summary of the repository""" | |
| 21 | # see mercurial/commands.py:tip | |
| 22 |     ui.write(':tip: rev node\n') | |
| 23 | tipctx = repo[len(repo) - 1] | |
| 24 |     ui.write('%d %s\n' % (tipctx.rev(), tipctx)) | |
| 25 | ||
| 26 | # see mercurial/commands.py:tags | |
| 27 |     ui.write(':tags: rev node name\n') | |
| 28 | for t, n in reversed(repo.tagslist()): | |
| 29 | if t in SPECIAL_TAGS: | |
| 30 | continue | |
| 31 | try: | |
| 32 | r = repo.changelog.rev(n) | |
| 33 | except error.LookupError: | |
| 34 | r = -1 | |
| 35 |         ui.write('%d %s %s\n' % (r, node.short(n), t)) | |
| 36 | ||
| 37 | # see mercurial/commands.py:branches | |
| 38 | def iterbranches(): | |
| 39 | for t, n in repo.branchtags().iteritems(): | |
| 40 | yield t, n, repo.changelog.rev(n) | |
| 41 | ||
| 42 | # TODO: closed branch? | |
| 43 |     ui.write(':branches: rev node name\n') | |
| 44 | for t, n, r in sorted(iterbranches(), key=lambda e: e[2], reverse=True): | |
| 45 |         ui.write('%d %s %s\n' % (r, node.short(n), t)) | |
| 46 | ||
| 47 | def rhentries(ui, repo, path='', **opts): | |
| 48 | """output the entries of the specified directory""" | |
| 49 |     rev = opts.get('rev') | |
| 50 |     pathprefix = (path.rstrip('/') + '/').lstrip('/') | |
| 51 | ||
| 52 | # TODO: clean up | |
| 53 |     dirs, files = {}, {} | |
| 17 | 54 | mf = repo[rev].manifest() | 
| 18 | 55 | for f in repo[rev]: | 
| 19 | try: | |
| 20 | fctx = repo.filectx(f, fileid=mf[f]) | |
| 21 | ctx = fctx.changectx() | |
| 22 |             ui.write('%s\t%d\t%s\n' % | |
| 23 | (ctx,fctx.size(),f)) | |
| 24 | except LookupError: | |
| 25 | pass | |
| 56 | if not f.startswith(pathprefix): | |
| 57 | continue | |
| 58 | ||
| 59 | name = re.sub(r'/.*', '', f[len(pathprefix):]) | |
| 60 | if '/' in f[len(pathprefix):]: | |
| 61 | dirs[name] = (name,) | |
| 62 | else: | |
| 63 | try: | |
| 64 | fctx = repo.filectx(f, fileid=mf[f]) | |
| 65 | ctx = fctx.changectx() | |
| 66 | tm, tzoffset = ctx.date() | |
| 67 | localtime = int(tm) + tzoffset - time.timezone | |
| 68 | files[name] = (ctx.rev(), node.short(ctx.node()), localtime, | |
| 69 | fctx.size(), name) | |
| 70 | except LookupError: # TODO: when this occurs? | |
| 71 | pass | |
| 72 | ||
| 73 |     ui.write(':dirs: name\n') | |
| 74 | for n, v in sorted(dirs.iteritems(), key=lambda e: e[0]): | |
| 75 |         ui.write(' '.join(v) + '\n') | |
| 76 | ||
| 77 |     ui.write(':files: rev node time size name\n') | |
| 78 | for n, v in sorted(files.iteritems(), key=lambda e: e[0]): | |
| 79 |         ui.write(' '.join(str(e) for e in v) + '\n') | |
| 80 | ||
| 26 | 81 | |
| 27 | 82 | cmdtable = { | 
| 28 |     'overhaul': (overhaul,commands.templateopts, _('hg overhaul [rev]')) | |
| 83 | 'rhsummary': (rhsummary, [], 'hg rhsummary'), | |
| 84 | 'rhentries': (rhentries, | |
| 85 |                   [('r', 'rev', '', 'show the specified revision')], | |
| 86 | 'hg rhentries [path]'), | |
| 29 | 87 | } | 
| lib/redmine/scm/adapters/mercurial_adapter.rb | ||
|---|---|---|
| 24 | 24 |  | 
| 25 | 25 | # Mercurial executable name | 
| 26 | 26 | HG_BIN = "hg" | 
| 27 |         HG_ENV = {'HGPLAIN' => '', 'HGENCODING' => 'utf-8', | |
| 28 | 'LANG' => nil, 'LANGUAGE' => nil, 'LC_MESSAGES' => nil} | |
| 27 | 29 | TEMPLATES_DIR = File.dirname(__FILE__) + "/mercurial" | 
| 28 | 30 | TEMPLATE_NAME = "hg-template" | 
| 29 | 31 | TEMPLATE_EXTENSION = "tmpl" | 
| ... | ... | |
| 59 | 61 | end | 
| 60 | 62 |             "#{TEMPLATES_DIR}/#{TEMPLATE_NAME}-#{ver}.#{TEMPLATE_EXTENSION}" | 
| 61 | 63 | end | 
| 64 | ||
| 65 | def shellout(cmd, &block) | |
| 66 | orig = Hash[HG_ENV.keys.zip(ENV.values_at(*HG_ENV.keys))] | |
| 67 | ENV.update(HG_ENV) | |
| 68 | begin | |
| 69 | ret = super | |
| 70 | ensure | |
| 71 | ENV.update(orig) | |
| 72 | ret | |
| 73 | end | |
| 74 | end | |
| 62 | 75 | end | 
| 63 | 76 |  | 
| 64 | 77 | def info | 
| lib/redmine/scm/adapters/mercurial_adapter.rb | ||
|---|---|---|
| 26 | 26 | HG_BIN = "hg" | 
| 27 | 27 |         HG_ENV = {'HGPLAIN' => '', 'HGENCODING' => 'utf-8', | 
| 28 | 28 | 'LANG' => nil, 'LANGUAGE' => nil, 'LC_MESSAGES' => nil} | 
| 29 |         HG_HELPER_EXT = "#{RAILS_ROOT}/extra/mercurial/redminehelper.py" | |
| 29 | 30 | TEMPLATES_DIR = File.dirname(__FILE__) + "/mercurial" | 
| 30 | 31 | TEMPLATE_NAME = "hg-template" | 
| 31 | 32 | TEMPLATE_EXTENSION = "tmpl" | 
| ... | ... | |
| 183 | 184 | end | 
| 184 | 185 |  | 
| 185 | 186 | def cat(path, identifier=nil) | 
| 186 |           cmd = "#{HG_BIN} -R #{target('')} cat" | |
| 187 | cmd << " -r " + (identifier ? identifier.to_s : "tip") | |
| 188 |           cmd << " #{target(path)}" | |
| 189 | cat = nil | |
| 190 | shellout(cmd) do |io| | |
| 187 | hg 'cat', '-r', hgrev(identifier), without_leading_slash(path) do |io| | |
| 191 | 188 | io.binmode | 
| 192 |             cat = io.read | |
| 189 | io.read | |
| 193 | 190 | end | 
| 194 | return nil if $? && $?.exitstatus != 0 | |
| 195 | cat | |
| 196 | 191 | end | 
| 197 | 192 |  | 
| 198 | 193 | def annotate(path, identifier=nil) | 
| ... | ... | |
| 212 | 207 | return nil if $? && $?.exitstatus != 0 | 
| 213 | 208 | blame | 
| 214 | 209 | end | 
| 210 | ||
| 211 | # Runs 'hg' command with the given args | |
| 212 | def hg(*args, &block) | |
| 213 | full_args = [HG_BIN, '--cwd', url] | |
| 214 |           full_args << '--config' << "extensions.redminehelper=#{HG_HELPER_EXT}" | |
| 215 | full_args += args | |
| 216 |           ret = shellout(full_args.map { |e| shell_quote e.to_s }.join(' '), &block) | |
| 217 | if $? && $?.exitstatus != 0 | |
| 218 |             raise CommandFailed, "hg exited with non-zero status: #{$?.exitstatus}" | |
| 219 | end | |
| 220 | ret | |
| 221 | end | |
| 222 | private :hg | |
| 223 | ||
| 224 | # Returns correct revision identifier | |
| 225 | def hgrev(identifier) | |
| 226 | identifier.blank? ? 'tip' : identifier.to_s | |
| 227 | end | |
| 228 | private :hgrev | |
| 215 | 229 | end | 
| 216 | 230 | end | 
| 217 | 231 | end | 
| lib/redmine/scm/adapters/mercurial_adapter.rb | ||
|---|---|---|
| 165 | 165 | end | 
| 166 | 166 |  | 
| 167 | 167 | def diff(path, identifier_from, identifier_to=nil) | 
| 168 |           path ||= '' | |
| 168 |           hg_args = ['diff', '--nodates'] | |
| 169 | 169 | if identifier_to | 
| 170 |             identifier_to = identifier_to.to_i  | |
| 170 |             hg_args << '-r' << hgrev(identifier_to) << '-r' << hgrev(identifier_from) | |
| 171 | 171 | else | 
| 172 |             identifier_to = identifier_from.to_i - 1 | |
| 172 |             hg_args << '-c' << hgrev(identifier_from) | |
| 173 | 173 | end | 
| 174 |           cmd = "#{HG_BIN} -R #{target('')} diff -r #{identifier_to} -r #{identifier_from} --nodates" | |
| 175 |           cmd << " -I #{target(path)}" unless path.empty? | |
| 176 | diff = [] | |
| 177 | shellout(cmd) do |io| | |
| 178 | io.each_line do |line| | |
| 179 | diff << line | |
| 180 | end | |
| 174 | hg_args << without_leading_slash(path) unless path.blank? | |
| 175 | ||
| 176 | hg *hg_args do |io| | |
| 177 | io.collect | |
| 181 | 178 | end | 
| 182 | return nil if $? && $?.exitstatus != 0 | |
| 183 | diff | |
| 184 | 179 | end | 
| 185 | 180 |  | 
| 186 | 181 | def cat(path, identifier=nil) | 
| lib/redmine/scm/adapters/mercurial_adapter.rb | ||
|---|---|---|
| 186 | 186 | end | 
| 187 | 187 |  | 
| 188 | 188 | def annotate(path, identifier=nil) | 
| 189 | path ||= '' | |
| 190 |           cmd = "#{HG_BIN} -R #{target('')}" | |
| 191 | cmd << " annotate -n -u" | |
| 192 | cmd << " -r " + (identifier ? identifier.to_s : "tip") | |
| 193 |           cmd << " -r #{identifier.to_i}" if identifier | |
| 194 |           cmd << " #{target(path)}" | |
| 195 | 189 | blame = Annotate.new | 
| 196 | shellout(cmd) do |io| | |
| 197 | io.each_line do |line| | |
| 198 |               next unless line =~ %r{^([^:]+)\s(\d+):(.*)$} | |
| 199 | blame.add_line($3.rstrip, Revision.new(:identifier => $2.to_i, :author => $1.strip)) | |
| 190 | hg 'annotate', '-ncu', '-r', hgrev(identifier), without_leading_slash(path) do |io| | |
| 191 | io.each do |line| | |
| 192 |               next unless line =~ %r{^([^:]+)\s(\d+)\s([0-9a-f]+):(.*)$} | |
| 193 | blame.add_line($4.rstrip, | |
| 194 | Revision.new(:identifier => $2.to_i, :author => $1.strip, | |
| 195 | :revision => $2, :scmid => $3)) | |
| 200 | 196 | end | 
| 201 | 197 | end | 
| 202 | return nil if $? && $?.exitstatus != 0 | |
| 203 | 198 | blame | 
| 204 | 199 | end | 
| 205 | 200 | |
| lib/redmine/scm/adapters/mercurial/hg-template-0.9.5.tmpl | ||
|---|---|---|
| 9 | 9 | file_copy = '<path-copied copyfrom-path="{source|escape}">{name|urlescape}</path-copied>\n' | 
| 10 | 10 | tag = '<tag>{tag|escape}</tag>\n' | 
| 11 | 11 | header='<?xml version="1.0" encoding="UTF-8" ?>\n<log>\n\n' | 
| 12 | # footer="</log>" | |
| 12 | footer='</log>' | |
| lib/redmine/scm/adapters/mercurial/hg-template-1.0.tmpl | ||
|---|---|---|
| 9 | 9 | file_copy = '<path-copied copyfrom-path="{source|escape}">{name|urlescape}</path-copied>\n' | 
| 10 | 10 | tag = '<tag>{tag|escape}</tag>\n' | 
| 11 | 11 | header='<?xml version="1.0" encoding="UTF-8" ?>\n<log>\n\n' | 
| 12 | # footer="</log>" | |
| 12 | footer='</log>' | |
| lib/redmine/scm/adapters/mercurial_adapter.rb | ||
|---|---|---|
| 16 | 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 
| 17 | 17 | |
| 18 | 18 | require 'redmine/scm/adapters/abstract_adapter' | 
| 19 | require 'rexml/document' | |
| 19 | 20 | |
| 20 | 21 | module Redmine | 
| 21 | 22 | module Scm | 
| ... | ... | |
| 115 | 116 | entries.sort_by_name | 
| 116 | 117 | end | 
| 117 | 118 |  | 
| 118 | # Fetch the revisions by using a template file that | |
| 119 | # TODO: is this api necessary? | |
| 120 |         def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}) | |
| 121 | revisions = Revisions.new | |
| 122 |           each_revision { |e| revisions << e } | |
| 123 | revisions | |
| 124 | end | |
| 125 | ||
| 126 | # Iterates the revisions by using a template file that | |
| 119 | 127 | # makes Mercurial produce a xml output. | 
| 120 |         def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})   | |
| 121 | revisions = Revisions.new | |
| 122 |           cmd = "#{HG_BIN} --debug --encoding utf8 -R #{target('')} log -C --style #{shell_quote self.class.template_path}" | |
| 123 | if identifier_from && identifier_to | |
| 124 |             cmd << " -r #{identifier_from.to_i}:#{identifier_to.to_i}" | |
| 125 | elsif identifier_from | |
| 126 |             cmd << " -r #{identifier_from.to_i}:" | |
| 128 |         def each_revision(path=nil, identifier_from=nil, identifier_to=nil, options={}) | |
| 129 | hg_args = ['log', '--debug', '-C', '--style', self.class.template_path] | |
| 130 |           hg_args << '-r' << "#{hgrev(identifier_from)}:#{hgrev(identifier_to)}" | |
| 131 | hg_args << '--limit' << options[:limit] if options[:limit] | |
| 132 | hg_args << without_leading_slash(path) unless path.blank? | |
| 133 |           doc = hg(*hg_args) { |io| REXML::Document.new(io.read) } | |
| 134 | # TODO: ??? HG doesn't close the XML Document... | |
| 135 | ||
| 136 |           doc.each_element('log/logentry') do |le| | |
| 137 |             cpalist = le.get_elements('paths/path-copied').map do |e| | |
| 138 | [e.text, e.attributes['copyfrom-path']] | |
| 139 | end | |
| 140 | cpmap = Hash[*cpalist.flatten] | |
| 141 | ||
| 142 |             paths = le.get_elements('paths/path').map do |e| | |
| 143 |               {:action => e.attributes['action'], :path => with_leading_slash(e.text), | |
| 144 | :from_path => (cpmap.member?(e.text) ? with_leading_slash(cpmap[e.text]) : nil), | |
| 145 | :from_revision => (cpmap.member?(e.text) ? le.attributes['revision'] : nil)} | |
| 146 |             end.sort { |a, b| a[:path] <=> b[:path] } | |
| 147 | ||
| 148 | yield Revision.new(:identifier => le.attributes['revision'], | |
| 149 | :revision => le.attributes['revision'], | |
| 150 | :scmid => le.attributes['node'], | |
| 151 | :author => (le.elements['author'].text rescue ''), | |
| 152 | :time => Time.parse(le.elements['date'].text).localtime, | |
| 153 | :message => le.elements['msg'].text, | |
| 154 | :paths => paths) | |
| 127 | 155 | end | 
| 128 |           cmd << " --limit #{options[:limit].to_i}" if options[:limit] | |
| 129 |           cmd << " #{path}" if path | |
| 130 | shellout(cmd) do |io| | |
| 131 | begin | |
| 132 | # HG doesn't close the XML Document... | |
| 133 | doc = REXML::Document.new(io.read << "</log>") | |
| 134 |               doc.elements.each("log/logentry") do |logentry| | |
| 135 | paths = [] | |
| 136 |                 copies = logentry.get_elements('paths/path-copied') | |
| 137 |                 logentry.elements.each("paths/path") do |path| | |
| 138 | # Detect if the added file is a copy | |
| 139 |                   if path.attributes['action'] == 'A' and c = copies.find{ |e| e.text == path.text } | |
| 140 | from_path = c.attributes['copyfrom-path'] | |
| 141 | from_rev = logentry.attributes['revision'] | |
| 142 | end | |
| 143 |                   paths << {:action => path.attributes['action'], | |
| 144 |                     :path => "/#{path.text}", | |
| 145 |                     :from_path => from_path ? "/#{from_path}" : nil, | |
| 146 | :from_revision => from_rev ? from_rev : nil | |
| 147 | } | |
| 148 | end | |
| 149 |                 paths.sort! { |x,y| x[:path] <=> y[:path] } | |
| 150 |  | |
| 151 |                 revisions << Revision.new({:identifier => logentry.attributes['revision'], | |
| 152 | :scmid => logentry.attributes['node'], | |
| 153 | :author => (logentry.elements['author'] ? logentry.elements['author'].text : ""), | |
| 154 | :time => Time.parse(logentry.elements['date'].text).localtime, | |
| 155 | :message => logentry.elements['msg'].text, | |
| 156 | :paths => paths | |
| 157 | }) | |
| 158 | end | |
| 159 | rescue | |
| 160 | logger.debug($!) | |
| 161 | end | |
| 162 | end | |
| 163 | return nil if $? && $?.exitstatus != 0 | |
| 164 | revisions | |
| 156 | self | |
| 165 | 157 | end | 
| 166 | 158 |  | 
| 167 | 159 | def diff(path, identifier_from, identifier_to=nil) | 
| lib/redmine/scm/adapters/mercurial_adapter.rb | ||
|---|---|---|
| 77 | 77 | end | 
| 78 | 78 |  | 
| 79 | 79 | def info | 
| 80 |           cmd = "#{HG_BIN} -R #{target('')} root" | |
| 81 | root_url = nil | |
| 82 | shellout(cmd) do |io| | |
| 83 | root_url = io.gets | |
| 84 | end | |
| 85 | return nil if $? && $?.exitstatus != 0 | |
| 86 |           info = Info.new({:root_url => root_url.chomp, | |
| 87 |                             :lastrev => revisions(nil,nil,nil,{:limit => 1}).last | |
| 88 | }) | |
| 89 | info | |
| 90 | rescue CommandFailed | |
| 91 | return nil | |
| 80 | tip = summary['tip'].first | |
| 81 | Info.new(:root_url => root_url, | |
| 82 | :lastrev => Revision.new(:identifier => tip['rev'].to_i, | |
| 83 | :revision => tip['rev'], | |
| 84 | :scmid => tip['node'])) | |
| 92 | 85 | end | 
| 86 | ||
| 87 | def summary | |
| 88 | @summary ||= fetchg 'rhsummary' | |
| 89 | end | |
| 90 | private :summary | |
| 93 | 91 |  | 
| 94 | 92 | def entries(path=nil, identifier=nil) | 
| 95 | 93 | path ||= '' | 
| ... | ... | |
| 203 | 201 | end | 
| 204 | 202 | private :hg | 
| 205 | 203 | |
| 204 | # Runs 'hg' helper, then parses output to return | |
| 205 | def fetchg(*args) | |
| 206 | # command output example: | |
| 207 | # :tip: rev node | |
| 208 | # 100 abcdef012345 | |
| 209 | # :tags: rev node name | |
| 210 | # 100 abcdef012345 tip | |
| 211 | # ... | |
| 212 |           data = Hash.new { |h, k| h[k] = [] } | |
| 213 | hg(*args) do |io| | |
| 214 | key, attrs = nil, nil | |
| 215 | io.each do |line| | |
| 216 | next if line.chomp.empty? | |
| 217 | if /^:(\w+): ([\w ]+)/ =~ line | |
| 218 | key = $1 | |
| 219 | attrs = $2.split(/ /) | |
| 220 | elsif key | |
| 221 | alist = attrs.zip(line.chomp.split(/ /, attrs.size)) | |
| 222 | data[key] << Hash[*alist.flatten] | |
| 223 | end | |
| 224 | end | |
| 225 | end | |
| 226 | data | |
| 227 | end | |
| 228 | private :fetchg | |
| 229 | ||
| 206 | 230 | # Returns correct revision identifier | 
| 207 | 231 | def hgrev(identifier) | 
| 208 | 232 | identifier.blank? ? 'tip' : identifier.to_s | 
| app/models/repository/mercurial.rb | ||
|---|---|---|
| 21 | 21 | attr_protected :root_url | 
| 22 | 22 | validates_presence_of :url | 
| 23 | 23 | |
| 24 | FETCH_AT_ONCE = 100 # number of changesets to fetch at once | |
| 25 | ||
| 24 | 26 | def scm_adapter | 
| 25 | 27 | Redmine::Scm::Adapters::MercurialAdapter | 
| 26 | 28 | end | 
| ... | ... | |
| 53 | 55 | end | 
| 54 | 56 | |
| 55 | 57 | def fetch_changesets | 
| 56 | scm_info = scm.info | |
| 57 | if scm_info | |
| 58 | # latest revision found in database | |
| 59 | db_revision = latest_changeset ? latest_changeset.revision.to_i : -1 | |
| 60 | # latest revision in the repository | |
| 61 | latest_revision = scm_info.lastrev | |
| 62 | return if latest_revision.nil? | |
| 63 | scm_revision = latest_revision.identifier.to_i | |
| 64 | if db_revision < scm_revision | |
| 65 |         logger.debug "Fetching changesets for repository #{url}" if logger && logger.debug? | |
| 66 | identifier_from = db_revision + 1 | |
| 67 | while (identifier_from <= scm_revision) | |
| 68 | # loads changesets by batches of 100 | |
| 69 | identifier_to = [identifier_from + 99, scm_revision].min | |
| 70 |           revisions = scm.revisions('', identifier_from, identifier_to, :with_paths => true) | |
| 71 | transaction do | |
| 72 | revisions.each do |revision| | |
| 73 | changeset = Changeset.create(:repository => self, | |
| 74 | :revision => revision.identifier, | |
| 75 | :scmid => revision.scmid, | |
| 76 | :committer => revision.author, | |
| 77 | :committed_on => revision.time, | |
| 78 | :comments => revision.message) | |
| 79 |  | |
| 80 | revision.paths.each do |change| | |
| 81 | changeset.create_change(change) | |
| 82 | end | |
| 83 | end | |
| 84 | end unless revisions.nil? | |
| 85 | identifier_from = identifier_to + 1 | |
| 58 | scm_rev = scm.info.lastrev.revision.to_i | |
| 59 | db_rev = latest_changeset ? latest_changeset.revision.to_i : -1 | |
| 60 | return unless db_rev < scm_rev # already up-to-date | |
| 61 | ||
| 62 |     logger.debug "Fetching changesets for repository #{url}" if logger | |
| 63 | (db_rev + 1).step(scm_rev, FETCH_AT_ONCE) do |i| | |
| 64 | transaction do | |
| 65 |         scm.each_revision('', i, [i + FETCH_AT_ONCE - 1, scm_rev].min) do |re| | |
| 66 | cs = Changeset.create(:repository => self, | |
| 67 | :revision => re.revision, | |
| 68 | :scmid => re.scmid, | |
| 69 | :committer => re.author, | |
| 70 | :committed_on => re.time, | |
| 71 | :comments => re.message) | |
| 72 |           re.paths.each { |e| cs.create_change(e) } | |
| 86 | 73 | end | 
| 87 | 74 | end | 
| 88 | 75 | end | 
| 76 | self | |
| 89 | 77 | end | 
| 90 | 78 | end | 
| app/models/repository/mercurial.rb | ||
|---|---|---|
| 54 | 54 | entries | 
| 55 | 55 | end | 
| 56 | 56 | |
| 57 | # Returns the latest changesets for +path+ | |
| 58 | def latest_changesets(path, rev, limit=10) | |
| 59 | changesets.find(:all, :include => :user, | |
| 60 | :conditions => latest_changesets_cond(path, rev), | |
| 61 |                     :order => "#{Changeset.table_name}.id DESC", | |
| 62 | :limit => limit) | |
| 63 | end | |
| 64 | ||
| 65 | def latest_changesets_cond(path, rev) | |
| 66 | cond, args = [], [] | |
| 67 | ||
| 68 | if last = rev ? find_changeset_by_name(rev) : nil | |
| 69 |       cond << "#{Changeset.table_name}.id <= ?" | |
| 70 | args << last.id | |
| 71 | end | |
| 72 | ||
| 73 | unless path.blank? | |
| 74 | # TODO: there must be a better way to build sub-query | |
| 75 |       cond << "EXISTS (SELECT * FROM #{Change.table_name} | |
| 76 |                  WHERE #{Change.table_name}.changeset_id = #{Changeset.table_name}.id | |
| 77 |                  AND (#{Change.table_name}.path = ? OR #{Change.table_name}.path LIKE ?))" | |
| 78 |       args << path.with_leading_slash << "#{path.with_leading_slash}/%" | |
| 79 | end | |
| 80 | ||
| 81 |     [cond.join(' AND '), *args] unless cond.empty? | |
| 82 | end | |
| 83 | private :latest_changesets_cond | |
| 84 | ||
| 57 | 85 | def fetch_changesets | 
| 58 | 86 | scm_rev = scm.info.lastrev.revision.to_i | 
| 59 | 87 | db_rev = latest_changeset ? latest_changeset.revision.to_i : -1 | 
| app/models/repository/mercurial.rb | ||
|---|---|---|
| 23 | 23 | |
| 24 | 24 | FETCH_AT_ONCE = 100 # number of changesets to fetch at once | 
| 25 | 25 | |
| 26 | SHORT_NODEID_LEN = 12 | |
| 27 | ||
| 26 | 28 | def scm_adapter | 
| 27 | 29 | Redmine::Scm::Adapters::MercurialAdapter | 
| 28 | 30 | end | 
| ... | ... | |
| 82 | 84 | end | 
| 83 | 85 | private :latest_changesets_cond | 
| 84 | 86 | |
| 87 | # Finds and returns a revision with a number or the beginning of a hash | |
| 88 | def find_changeset_by_name(name) | |
| 89 | if /[^\d]/ =~ name or name.length >= SHORT_NODEID_LEN | |
| 90 | if e = changesets.find(:first, :conditions => ["scmid = ?", name]) | |
| 91 | e | |
| 92 | else | |
| 93 |         changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]) | |
| 94 | end | |
| 95 | else | |
| 96 | super | |
| 97 | end | |
| 98 | end | |
| 99 | ||
| 85 | 100 | def fetch_changesets | 
| 86 | 101 | scm_rev = scm.info.lastrev.revision.to_i | 
| 87 | 102 | db_rev = latest_changeset ? latest_changeset.revision.to_i : -1 | 
| app/models/repository/mercurial.rb | ||
|---|---|---|
| 67 | 67 | def latest_changesets_cond(path, rev) | 
| 68 | 68 | cond, args = [], [] | 
| 69 | 69 | |
| 70 | if last = rev ? find_changeset_by_name(rev) : nil | |
| 70 |     if last = rev ? find_changeset_by_name(scm.tagmap[rev] || rev) : nil | |
| 71 | 71 |       cond << "#{Changeset.table_name}.id <= ?" | 
| 72 | 72 | args << last.id | 
| 73 | 73 | end | 
| lib/redmine/scm/adapters/mercurial_adapter.rb | ||
|---|---|---|
| 84 | 84 | :scmid => tip['node'])) | 
| 85 | 85 | end | 
| 86 | 86 | |
| 87 | def tags | |
| 88 |           summary['tags'].map { |e| e['name'] } | |
| 89 | end | |
| 90 | ||
| 91 |         # Returns map of {'tag' => 'nodeid', ...} | |
| 92 | def tagmap | |
| 93 |           alist = summary['tags'].map { |e| e.values_at('name', 'node') } | |
| 94 | Hash[*alist.flatten] | |
| 95 | end | |
| 96 | ||
| 87 | 97 | def summary | 
| 88 | 98 | @summary ||= fetchg 'rhsummary' | 
| 89 | 99 | end | 
| app/models/repository/mercurial.rb | ||
|---|---|---|
| 34 | 34 | end | 
| 35 | 35 |  | 
| 36 | 36 | def entries(path=nil, identifier=nil) | 
| 37 | entries=scm.entries(path, identifier) | |
| 38 | if entries | |
| 39 | entries.each do |entry| | |
| 40 | next unless entry.is_file? | |
| 41 | # Set the filesize unless browsing a specific revision | |
| 42 | if identifier.nil? | |
| 43 | full_path = File.join(root_url, entry.path) | |
| 44 | entry.size = File.stat(full_path).size if File.file?(full_path) | |
| 45 | end | |
| 46 | # Search the DB for the entry's last change | |
| 47 |         change = changes.find(:first, :conditions => ["path = ?", scm.with_leading_slash(entry.path)], :order => "#{Changeset.table_name}.committed_on DESC") | |
| 48 | if change | |
| 49 | entry.lastrev.identifier = change.changeset.revision | |
| 50 | entry.lastrev.name = change.changeset.revision | |
| 51 | entry.lastrev.author = change.changeset.committer | |
| 52 | entry.lastrev.revision = change.revision | |
| 53 | end | |
| 54 | end | |
| 55 | end | |
| 56 | entries | |
| 37 | scm.entries(path, identifier) | |
| 57 | 38 | end | 
| 58 | 39 | |
| 59 | 40 | # Returns the latest changesets for +path+ | 
| lib/redmine/scm/adapters/mercurial_adapter.rb | ||
|---|---|---|
| 100 | 100 | private :summary | 
| 101 | 101 |  | 
| 102 | 102 | def entries(path=nil, identifier=nil) | 
| 103 | path ||= '' | |
| 104 | 103 | entries = Entries.new | 
| 105 |           cmd = "#{HG_BIN} -R #{target('')} --cwd #{target('')} locate" | |
| 106 | cmd << " -r " + (identifier ? identifier.to_s : "tip") | |
| 107 |           cmd << " " + shell_quote("path:#{path}") unless path.empty? | |
| 108 | shellout(cmd) do |io| | |
| 109 | io.each_line do |line| | |
| 110 | # HG uses antislashs as separator on Windows | |
| 111 | line = line.gsub(/\\/, "/") | |
| 112 |               if path.empty? or e = line.gsub!(%r{^#{with_trailling_slash(path)}},'') | |
| 113 | e ||= line | |
| 114 |                 e = e.chomp.split(%r{[\/\\]}) | |
| 115 |                 entries << Entry.new({:name => e.first, | |
| 116 |                                        :path => (path.nil? or path.empty? ? e.first : "#{with_trailling_slash(path)}#{e.first}"), | |
| 117 | :kind => (e.size > 1 ? 'dir' : 'file'), | |
| 118 | :lastrev => Revision.new | |
| 119 |                                      }) unless e.empty? || entries.detect{|entry| entry.name == e.first} | |
| 120 | end | |
| 121 | end | |
| 104 |           fetched_entries = fetchg('rhentries', '-r', hgrev(identifier), | |
| 105 | without_leading_slash(path.to_s)) | |
| 106 | ||
| 107 | fetched_entries['dirs'].each do |e| | |
| 108 | entries << Entry.new(:name => e['name'], | |
| 109 |                                  :path => "#{with_trailling_slash(path)}#{e['name']}", | |
| 110 | :kind => 'dir') | |
| 122 | 111 | end | 
| 123 | return nil if $? && $?.exitstatus != 0 | |
| 124 | entries.sort_by_name | |
| 112 | ||
| 113 | fetched_entries['files'].each do |e| | |
| 114 | entries << Entry.new(:name => e['name'], | |
| 115 |                                  :path => "#{with_trailling_slash(path)}#{e['name']}", | |
| 116 | :kind => 'file', | |
| 117 | :size => e['size'].to_i, | |
| 118 | :lastrev => Revision.new(:identifier => e['rev'].to_i, | |
| 119 | :time => Time.at(e['time'].to_i))) | |
| 120 | end | |
| 121 | ||
| 122 | entries | |
| 125 | 123 | end | 
| 126 | 124 |  | 
| 127 | 125 | # TODO: is this api necessary? | 
| app/models/repository/mercurial.rb | ||
|---|---|---|
| 39 | 39 | |
| 40 | 40 | # Returns the latest changesets for +path+ | 
| 41 | 41 | def latest_changesets(path, rev, limit=10) | 
| 42 | # TODO: filter by branch if rev is branch name | |
| 42 | 43 | changesets.find(:all, :include => :user, | 
| 43 | :conditions => latest_changesets_cond(path, rev), | |
| 44 |                     :conditions => latest_changesets_cond(path, rev, limit), | |
| 44 | 45 |                     :order => "#{Changeset.table_name}.id DESC", | 
| 45 | 46 | :limit => limit) | 
| 46 | 47 | end | 
| 47 | 48 | |
| 48 | def latest_changesets_cond(path, rev) | |
| 49 |   def latest_changesets_cond(path, rev, limit) | |
| 49 | 50 | cond, args = [], [] | 
| 50 | 51 | |
| 51 | if last = rev ? find_changeset_by_name(scm.tagmap[rev] || rev) : nil | |
| 52 | if scm.branchmap.member? rev | |
| 53 | # dirty hack to filter by branch. branch name should be in database. | |
| 54 |       cond << "#{Changeset.table_name}.scmid IN (?)" | |
| 55 | args << scm.nodes_in_branch(rev, path, rev, 0, :limit => limit) | |
| 56 | elsif last = rev ? find_changeset_by_name(scm.tagmap[rev] || rev) : nil | |
| 52 | 57 |       cond << "#{Changeset.table_name}.id <= ?" | 
| 53 | 58 | args << last.id | 
| 54 | 59 | end | 
| lib/redmine/scm/adapters/mercurial_adapter.rb | ||
|---|---|---|
| 94 | 94 | Hash[*alist.flatten] | 
| 95 | 95 | end | 
| 96 | 96 | |
| 97 | def branches | |
| 98 |           summary['branches'].map { |e| e['name'] } | |
| 99 | end | |
| 100 | ||
| 101 |         # Returns map of {'branch' => 'nodeid', ...} | |
| 102 | def branchmap | |
| 103 |           alist = summary['branches'].map { |e| e.values_at('name', 'node') } | |
| 104 | Hash[*alist.flatten] | |
| 105 | end | |
| 106 | ||
| 107 | # NOTE: DO NOT IMPLEMENT default_branch !! | |
| 108 | # It's used as the default revision by RepositoriesController. | |
| 109 | ||
| 97 | 110 | def summary | 
| 98 | 111 | @summary ||= fetchg 'rhsummary' | 
| 99 | 112 | end | 
| ... | ... | |
| 161 | 174 | end | 
| 162 | 175 | self | 
| 163 | 176 | end | 
| 177 | ||
| 178 | # Returns list of nodes in the specified branch | |
| 179 |         def nodes_in_branch(branch, path=nil, identifier_from=nil, identifier_to=nil, options={}) | |
| 180 |           hg_args = ['log', '--template', '{node|short}\n', '-b', branch] | |
| 181 |           hg_args << '-r' << "#{hgrev(identifier_from)}:#{hgrev(identifier_to)}" | |
| 182 | hg_args << '--limit' << options[:limit] if options[:limit] | |
| 183 | hg_args << without_leading_slash(path) unless path.blank? | |
| 184 |           hg(*hg_args) { |io| io.readlines.map { |e| e.chomp } } | |
| 185 | end | |
| 164 | 186 |  | 
| 165 | 187 | def diff(path, identifier_from, identifier_to=nil) | 
| 166 | 188 | hg_args = ['diff', '--nodates'] | 
| lib/redmine/scm/adapters/mercurial_adapter.rb | ||
|---|---|---|
| 48 | 48 | end | 
| 49 | 49 |  | 
| 50 | 50 | def hgversion_from_command_line | 
| 51 |             %x{#{HG_BIN} --version}.match(/\(version (.*)\)/)[1] | |
| 51 |             shellout("#{HG_BIN} --version") do |io| | |
| 52 | io.read.match(/\(version (.*)\)/)[1] | |
| 53 | end | |
| 52 | 54 | end | 
| 53 | 55 |  | 
| 54 | 56 | def template_path | 
| app/models/issue.rb | ||
|---|---|---|
| 27 | 27 | |
| 28 | 28 | has_many :journals, :as => :journalized, :dependent => :destroy | 
| 29 | 29 | has_many :time_entries, :dependent => :delete_all | 
| 30 |   has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC" | |
| 30 |   has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.id ASC" | |
| 31 | 31 |  | 
| 32 | 32 | has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all | 
| 33 | 33 | has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all | 
| app/models/repository.rb | ||
|---|---|---|
| 17 | 17 | |
| 18 | 18 | class Repository < ActiveRecord::Base | 
| 19 | 19 | belongs_to :project | 
| 20 |   has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC" | |
| 20 |   has_many :changesets, :order => "#{Changeset.table_name}.id DESC" | |
| 21 | 21 | has_many :changes, :through => :changesets | 
| 22 | 22 |  | 
| 23 | 23 | # Raw SQL to delete changesets and changes in the database | 
| ... | ... | |
| 106 | 106 | def latest_changesets(path, rev, limit=10) | 
| 107 | 107 | if path.blank? | 
| 108 | 108 | changesets.find(:all, :include => :user, | 
| 109 |                             :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC", | |
| 109 |                             :order => "#{Changeset.table_name}.id DESC", | |
| 110 | 110 | :limit => limit) | 
| 111 | 111 | else | 
| 112 | 112 |       changes.find(:all, :include => {:changeset => :user},  | 
| 113 | 113 | :conditions => ["path = ?", path.with_leading_slash], | 
| 114 |                          :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC", | |
| 114 |                          :order => "#{Changeset.table_name}.id DESC", | |
| 115 | 115 | :limit => limit).collect(&:changeset) | 
| 116 | 116 | end | 
| 117 | 117 | end | 
| app/models/changeset.rb | ||
|---|---|---|
| 47 | 47 | def revision=(r) | 
| 48 | 48 | write_attribute :revision, (r.nil? ? nil : r.to_s) | 
| 49 | 49 | end | 
| 50 | ||
| 51 | # Returns identifier of the revision. | |
| 52 | # e.g. revision number for centralized system; hash id for DVCS | |
| 53 | def revision_id | |
| 54 | # TODO: should be 'revision' by default, and overriden by sub-class or mixin | |
| 55 | scmid || revision | |
| 56 | end | |
| 57 | ||
| 58 | # Returns human-readable string representing the revision | |
| 59 | def revision_repr | |
| 60 | # TODO: should be 'revision' by default, and overriden by sub-class or mixin | |
| 61 | if scmid and revision != scmid | |
| 62 |       "#{revision}:#{scmid}"  # Mercurial style | |
| 63 | else | |
| 64 | revision[0, 8] # TODO: [0, 8] maybe for git | |
| 65 | end | |
| 66 | end | |
| 50 | 67 |  | 
| 51 | 68 | def comments=(comment) | 
| 52 | 69 | write_attribute(:comments, Changeset.normalize_comments(comment)) | 
| lib/redmine/scm/adapters/abstract_adapter.rb | ||
|---|---|---|
| 285 | 285 | self.branch = attributes[:branch] | 
| 286 | 286 | end | 
| 287 | 287 | |
| 288 | # FIXME: Changeset class has same methods to revision_id and revision_repr. | |
| 289 | # They should be mix-in module or something. | |
| 290 | ||
| 291 | # FIXME: It seems 'revision_id' should be 'identifier', but Revision.save uses | |
| 292 | # 'identifier' for :revision. I need to check usage of Revision class to | |
| 293 | # improve class design. | |
| 294 | ||
| 295 | # Returns identifier of the revision. | |
| 296 | # e.g. revision number for centralized system; hash id for DVCS | |
| 297 | def revision_id # TODO: confusing name; there's already 'identifier' | |
| 298 | # TODO: should be 'revision' by default, and overriden by sub-class or mixin | |
| 299 | scmid || revision || identifier | |
| 300 | end | |
| 301 | ||
| 302 | # Returns human-readable string representing the revision | |
| 303 | def revision_repr | |
| 304 | # TODO: should be 'revision' by default, and overriden by sub-class or mixin | |
| 305 | if scmid and revision != scmid | |
| 306 |             "#{revision}:#{scmid}"  # Mercurial style | |
| 307 | elsif revision | |
| 308 | revision[0, 8] # TODO: [0, 8] maybe for git | |
| 309 | else | |
| 310 |             "#{identifier}"[0, 8] | |
| 311 | end | |
| 312 | end | |
| 313 | ||
| 288 | 314 | def save(repo) | 
| 289 | 315 | Changeset.transaction do | 
| 290 | 316 | changeset = Changeset.new( | 
| app/helpers/application_helper.rb | ||
|---|---|---|
| 99 | 99 | # Generates a link to a SCM revision | 
| 100 | 100 | # Options: | 
| 101 | 101 | # * :text - Link text (default to the formatted revision) | 
| 102 |   def link_to_revision(revision, project, options={}) | |
| 103 | text = options.delete(:text) || format_revision(revision) | |
| 102 |   def link_to_revision(changeset, project, options={}) | |
| 103 | text = options.delete(:text) || format_revision(changeset) | |
| 104 | revision = changeset.respond_to?(:revision_id) ? changeset.revision_id : changeset | |
| 104 | 105 | |
| 105 |     link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision}, :title => l(:label_revision_id, revision)) | |
| 106 |     link_to(text, {:controller => 'repositories', :action => 'revision', :id => project, :rev => revision}, :title => l(:label_revision_id, format_revision(changeset))) | |
| 106 | 107 | end | 
| 107 | 108 | |
| 108 | 109 |   def toggle_link(name, id, options={}) | 
| app/helpers/repositories_helper.rb | ||
|---|---|---|
| 18 | 18 | require 'iconv' | 
| 19 | 19 | |
| 20 | 20 | module RepositoriesHelper | 
| 21 | def format_revision(txt) | |
| 22 | txt.to_s[0,8] | |
| 21 | def format_revision(changeset) | |
| 22 | if changeset.respond_to? :revision_repr | |
| 23 | changeset.revision_repr | |
| 24 | else | |
| 25 | return changeset.to_s[0,8] | |
| 26 | end | |
| 23 | 27 | end | 
| 24 | 28 |  | 
| 25 | 29 | def truncate_at_line_break(text, length = 255) | 
| app/views/repositories/_dir_list_content.rhtml | ||
|---|---|---|
| 17 | 17 | </td> | 
| 18 | 18 | <td class="size"><%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td> | 
| 19 | 19 | <% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %> | 
| 20 | <td class="revision"><%= link_to_revision(changeset.revision, @project) if changeset %></td> | |
| 20 | <td class="revision"><%= link_to_revision(changeset, @project) if changeset %></td> | |
| 21 | 21 | <td class="age"><%= distance_of_time_in_words(entry.lastrev.time, Time.now) if entry.lastrev && entry.lastrev.time %></td> | 
| 22 | 22 | <td class="author"><%= changeset.nil? ? h(entry.lastrev.author.to_s.split('<').first) : changeset.author if entry.lastrev %></td> | 
| 23 | 23 | <td class="comments"><%=h truncate(changeset.comments, :length => 50) unless changeset.nil? %></td> | 
| app/views/repositories/_revisions.rhtml | ||
|---|---|---|
| 13 | 13 | <% line_num = 1 %> | 
| 14 | 14 | <% revisions.each do |changeset| %> | 
| 15 | 15 | <tr class="changeset <%= cycle 'odd', 'even' %>"> | 
| 16 | <td class="id"><%= link_to_revision(changeset.revision, project) %></td> | |
| 16 | <td class="id"><%= link_to_revision(changeset, project) %></td> | |
| 17 | 17 | <td class="checkbox"><%= radio_button_tag('rev', changeset.revision, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < revisions.size) %></td> | 
| 18 | 18 | <td class="checkbox"><%= radio_button_tag('rev_to', changeset.revision, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('cb-#{line_num}').checked==true) {$('cb-#{line_num-1}').checked=true;}") if show_diff && (line_num > 1) %></td> | 
| 19 | 19 | <td class="committed_on"><%= format_time(changeset.committed_on) %></td> | 
| app/views/repositories/revision.rhtml | ||
|---|---|---|
| 1 | 1 | <div class="contextual"> | 
| 2 | 2 | « | 
| 3 | 3 | <% unless @changeset.previous.nil? -%> | 
| 4 |     <%= link_to_revision(@changeset.previous.revision, @project, :text => l(:label_previous)) %> | |
| 4 | <%= link_to_revision(@changeset.previous, @project, :text => l(:label_previous)) %> | |
| 5 | 5 | <% else -%> | 
| 6 | 6 | <%= l(:label_previous) %> | 
| 7 | 7 | <% end -%> | 
| 8 | 8 | | | 
| 9 | 9 | <% unless @changeset.next.nil? -%> | 
| 10 |     <%= link_to_revision(@changeset.next.revision, @project, :text => l(:label_next)) %> | |
| 10 | <%= link_to_revision(@changeset.next, @project, :text => l(:label_next)) %> | |
| 11 | 11 | <% else -%> | 
| 12 | 12 | <%= l(:label_next) %> | 
| 13 | 13 | <% end -%> | 
| ... | ... | |
| 19 | 19 | <% end %> | 
| 20 | 20 | </div> | 
| 21 | 21 | |
| 22 | <h2><%= l(:label_revision) %> <%= format_revision(@changeset.revision) %></h2> | |
| 22 | <h2><%= l(:label_revision) %> <%= format_revision(@changeset) %></h2> | |
| 23 | 23 | |
| 24 | 24 | <p><% if @changeset.scmid %>ID: <%= @changeset.scmid %><br /><% end %> | 
| 25 | 25 | <span class="author"><%= authoring(@changeset.committed_on, @changeset.author) %></span></p> | 
| app/views/repositories/annotate.rhtml | ||
|---|---|---|
| 19 | 19 | <tr class="bloc-<%= revision.nil? ? 0 : colors[revision.identifier || revision.revision] %>"> | 
| 20 | 20 | <th class="line-num" id="L<%= line_num %>"><a href="#L<%= line_num %>"><%= line_num %></a></th> | 
| 21 | 21 | <td class="revision"> | 
| 22 |       <%= (revision.identifier ? link_to(format_revision(revision.identifier), :action => 'revision', :id => @project, :rev => revision.identifier) : format_revision(revision.revision)) if revision %></td> | |
| 22 |       <%= (revision.revision_id ? link_to_revision(revision, @project) : format_revision(revision)) if revision %></td> | |
| 23 | 23 |       <td class="author"><%= h(revision.author.to_s.split('<').first) if revision %></td> | 
| 24 | 24 | <td class="line-code"><pre><%= line %></pre></td> | 
| 25 | 25 | </tr> |