##// END OF EJS Templates
scm: mercurial: use long id in adapter level (#14361)...
Toshi MARUYAMA -
r12486:5872d079e632
parent child
Show More
@@ -1,12 +1,12
1 changeset = 'This template must be used with --debug option\n'
1 changeset = 'This template must be used with --debug option\n'
2 changeset_quiet = 'This template must be used with --debug option\n'
2 changeset_quiet = 'This template must be used with --debug option\n'
3 changeset_verbose = 'This template must be used with --debug option\n'
3 changeset_verbose = 'This template must be used with --debug option\n'
4 changeset_debug = '<logentry revision="{rev}" node="{node|short}">\n<author>{author|escape}</author>\n<date>{date|isodatesec}</date>\n<paths>\n{file_mods}{file_adds}{file_dels}{file_copies}</paths>\n<msg>{desc|escape}</msg>\n<parents>\n{parents}</parents>\n</logentry>\n\n'
4 changeset_debug = '<logentry revision="{rev}" node="{node}">\n<author>{author|escape}</author>\n<date>{date|isodatesec}</date>\n<paths>\n{file_mods}{file_adds}{file_dels}{file_copies}</paths>\n<msg>{desc|escape}</msg>\n<parents>\n{parents}</parents>\n</logentry>\n\n'
5
5
6 file_mod = '<path action="M">{file_mod|urlescape}</path>\n'
6 file_mod = '<path action="M">{file_mod|urlescape}</path>\n'
7 file_add = '<path action="A">{file_add|urlescape}</path>\n'
7 file_add = '<path action="A">{file_add|urlescape}</path>\n'
8 file_del = '<path action="D">{file_del|urlescape}</path>\n'
8 file_del = '<path action="D">{file_del|urlescape}</path>\n'
9 file_copy = '<path-copied copyfrom-path="{source|urlescape}">{name|urlescape}</path-copied>\n'
9 file_copy = '<path-copied copyfrom-path="{source|urlescape}">{name|urlescape}</path-copied>\n'
10 parent = '<parent>{node|short}</parent>\n'
10 parent = '<parent>{node}</parent>\n'
11 header='<?xml version="1.0" encoding="UTF-8" ?>\n<log>\n\n'
11 header='<?xml version="1.0" encoding="UTF-8" ?>\n<log>\n\n'
12 # footer="</log>"
12 # footer="</log>"
@@ -1,220 +1,220
1 # redminehelper: Redmine helper extension for Mercurial
1 # redminehelper: Redmine helper extension for Mercurial
2 #
2 #
3 # Copyright 2010 Alessio Franceschelli (alefranz.net)
3 # Copyright 2010 Alessio Franceschelli (alefranz.net)
4 # Copyright 2010-2011 Yuya Nishihara <yuya@tcha.org>
4 # Copyright 2010-2011 Yuya Nishihara <yuya@tcha.org>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8 """helper commands for Redmine to reduce the number of hg calls
8 """helper commands for Redmine to reduce the number of hg calls
9
9
10 To test this extension, please try::
10 To test this extension, please try::
11
11
12 $ hg --config extensions.redminehelper=redminehelper.py rhsummary
12 $ hg --config extensions.redminehelper=redminehelper.py rhsummary
13
13
14 I/O encoding:
14 I/O encoding:
15
15
16 :file path: urlencoded, raw string
16 :file path: urlencoded, raw string
17 :tag name: utf-8
17 :tag name: utf-8
18 :branch name: utf-8
18 :branch name: utf-8
19 :node: 12-digits (short) hex string
19 :node: hex string
20
20
21 Output example of rhsummary::
21 Output example of rhsummary::
22
22
23 <?xml version="1.0"?>
23 <?xml version="1.0"?>
24 <rhsummary>
24 <rhsummary>
25 <repository root="/foo/bar">
25 <repository root="/foo/bar">
26 <tip revision="1234" node="abcdef0123..."/>
26 <tip revision="1234" node="abcdef0123..."/>
27 <tag revision="123" node="34567abc..." name="1.1.1"/>
27 <tag revision="123" node="34567abc..." name="1.1.1"/>
28 <branch .../>
28 <branch .../>
29 ...
29 ...
30 </repository>
30 </repository>
31 </rhsummary>
31 </rhsummary>
32
32
33 Output example of rhmanifest::
33 Output example of rhmanifest::
34
34
35 <?xml version="1.0"?>
35 <?xml version="1.0"?>
36 <rhmanifest>
36 <rhmanifest>
37 <repository root="/foo/bar">
37 <repository root="/foo/bar">
38 <manifest revision="1234" path="lib">
38 <manifest revision="1234" path="lib">
39 <file name="diff.rb" revision="123" node="34567abc..." time="12345"
39 <file name="diff.rb" revision="123" node="34567abc..." time="12345"
40 size="100"/>
40 size="100"/>
41 ...
41 ...
42 <dir name="redmine"/>
42 <dir name="redmine"/>
43 ...
43 ...
44 </manifest>
44 </manifest>
45 </repository>
45 </repository>
46 </rhmanifest>
46 </rhmanifest>
47 """
47 """
48 import re, time, cgi, urllib
48 import re, time, cgi, urllib
49 from mercurial import cmdutil, commands, node, error, hg
49 from mercurial import cmdutil, commands, node, error, hg
50
50
51 _x = cgi.escape
51 _x = cgi.escape
52 _u = lambda s: cgi.escape(urllib.quote(s))
52 _u = lambda s: cgi.escape(urllib.quote(s))
53
53
54 def _tip(ui, repo):
54 def _tip(ui, repo):
55 # see mercurial/commands.py:tip
55 # see mercurial/commands.py:tip
56 def tiprev():
56 def tiprev():
57 try:
57 try:
58 return len(repo) - 1
58 return len(repo) - 1
59 except TypeError: # Mercurial < 1.1
59 except TypeError: # Mercurial < 1.1
60 return repo.changelog.count() - 1
60 return repo.changelog.count() - 1
61 tipctx = repo.changectx(tiprev())
61 tipctx = repo.changectx(tiprev())
62 ui.write('<tip revision="%d" node="%s"/>\n'
62 ui.write('<tip revision="%d" node="%s"/>\n'
63 % (tipctx.rev(), _x(node.short(tipctx.node()))))
63 % (tipctx.rev(), _x(node.hex(tipctx.node()))))
64
64
65 _SPECIAL_TAGS = ('tip',)
65 _SPECIAL_TAGS = ('tip',)
66
66
67 def _tags(ui, repo):
67 def _tags(ui, repo):
68 # see mercurial/commands.py:tags
68 # see mercurial/commands.py:tags
69 for t, n in reversed(repo.tagslist()):
69 for t, n in reversed(repo.tagslist()):
70 if t in _SPECIAL_TAGS:
70 if t in _SPECIAL_TAGS:
71 continue
71 continue
72 try:
72 try:
73 r = repo.changelog.rev(n)
73 r = repo.changelog.rev(n)
74 except error.LookupError:
74 except error.LookupError:
75 continue
75 continue
76 ui.write('<tag revision="%d" node="%s" name="%s"/>\n'
76 ui.write('<tag revision="%d" node="%s" name="%s"/>\n'
77 % (r, _x(node.short(n)), _x(t)))
77 % (r, _x(node.hex(n)), _x(t)))
78
78
79 def _branches(ui, repo):
79 def _branches(ui, repo):
80 # see mercurial/commands.py:branches
80 # see mercurial/commands.py:branches
81 def iterbranches():
81 def iterbranches():
82 for t, n in repo.branchtags().iteritems():
82 for t, n in repo.branchtags().iteritems():
83 yield t, n, repo.changelog.rev(n)
83 yield t, n, repo.changelog.rev(n)
84 def branchheads(branch):
84 def branchheads(branch):
85 try:
85 try:
86 return repo.branchheads(branch, closed=False)
86 return repo.branchheads(branch, closed=False)
87 except TypeError: # Mercurial < 1.2
87 except TypeError: # Mercurial < 1.2
88 return repo.branchheads(branch)
88 return repo.branchheads(branch)
89 for t, n, r in sorted(iterbranches(), key=lambda e: e[2], reverse=True):
89 for t, n, r in sorted(iterbranches(), key=lambda e: e[2], reverse=True):
90 if repo.lookup(r) in branchheads(t):
90 if repo.lookup(r) in branchheads(t):
91 ui.write('<branch revision="%d" node="%s" name="%s"/>\n'
91 ui.write('<branch revision="%d" node="%s" name="%s"/>\n'
92 % (r, _x(node.short(n)), _x(t)))
92 % (r, _x(node.hex(n)), _x(t)))
93
93
94 def _manifest(ui, repo, path, rev):
94 def _manifest(ui, repo, path, rev):
95 ctx = repo.changectx(rev)
95 ctx = repo.changectx(rev)
96 ui.write('<manifest revision="%d" path="%s">\n'
96 ui.write('<manifest revision="%d" path="%s">\n'
97 % (ctx.rev(), _u(path)))
97 % (ctx.rev(), _u(path)))
98
98
99 known = set()
99 known = set()
100 pathprefix = (path.rstrip('/') + '/').lstrip('/')
100 pathprefix = (path.rstrip('/') + '/').lstrip('/')
101 for f, n in sorted(ctx.manifest().iteritems(), key=lambda e: e[0]):
101 for f, n in sorted(ctx.manifest().iteritems(), key=lambda e: e[0]):
102 if not f.startswith(pathprefix):
102 if not f.startswith(pathprefix):
103 continue
103 continue
104 name = re.sub(r'/.*', '/', f[len(pathprefix):])
104 name = re.sub(r'/.*', '/', f[len(pathprefix):])
105 if name in known:
105 if name in known:
106 continue
106 continue
107 known.add(name)
107 known.add(name)
108
108
109 if name.endswith('/'):
109 if name.endswith('/'):
110 ui.write('<dir name="%s"/>\n'
110 ui.write('<dir name="%s"/>\n'
111 % _x(urllib.quote(name[:-1])))
111 % _x(urllib.quote(name[:-1])))
112 else:
112 else:
113 fctx = repo.filectx(f, fileid=n)
113 fctx = repo.filectx(f, fileid=n)
114 tm, tzoffset = fctx.date()
114 tm, tzoffset = fctx.date()
115 ui.write('<file name="%s" revision="%d" node="%s" '
115 ui.write('<file name="%s" revision="%d" node="%s" '
116 'time="%d" size="%d"/>\n'
116 'time="%d" size="%d"/>\n'
117 % (_u(name), fctx.rev(), _x(node.short(fctx.node())),
117 % (_u(name), fctx.rev(), _x(node.hex(fctx.node())),
118 tm, fctx.size(), ))
118 tm, fctx.size(), ))
119
119
120 ui.write('</manifest>\n')
120 ui.write('</manifest>\n')
121
121
122 def rhannotate(ui, repo, *pats, **opts):
122 def rhannotate(ui, repo, *pats, **opts):
123 rev = urllib.unquote_plus(opts.pop('rev', None))
123 rev = urllib.unquote_plus(opts.pop('rev', None))
124 opts['rev'] = rev
124 opts['rev'] = rev
125 return commands.annotate(ui, repo, *map(urllib.unquote_plus, pats), **opts)
125 return commands.annotate(ui, repo, *map(urllib.unquote_plus, pats), **opts)
126
126
127 def rhcat(ui, repo, file1, *pats, **opts):
127 def rhcat(ui, repo, file1, *pats, **opts):
128 rev = urllib.unquote_plus(opts.pop('rev', None))
128 rev = urllib.unquote_plus(opts.pop('rev', None))
129 opts['rev'] = rev
129 opts['rev'] = rev
130 return commands.cat(ui, repo, urllib.unquote_plus(file1), *map(urllib.unquote_plus, pats), **opts)
130 return commands.cat(ui, repo, urllib.unquote_plus(file1), *map(urllib.unquote_plus, pats), **opts)
131
131
132 def rhdiff(ui, repo, *pats, **opts):
132 def rhdiff(ui, repo, *pats, **opts):
133 """diff repository (or selected files)"""
133 """diff repository (or selected files)"""
134 change = opts.pop('change', None)
134 change = opts.pop('change', None)
135 if change: # add -c option for Mercurial<1.1
135 if change: # add -c option for Mercurial<1.1
136 base = repo.changectx(change).parents()[0].rev()
136 base = repo.changectx(change).parents()[0].rev()
137 opts['rev'] = [str(base), change]
137 opts['rev'] = [str(base), change]
138 opts['nodates'] = True
138 opts['nodates'] = True
139 return commands.diff(ui, repo, *map(urllib.unquote_plus, pats), **opts)
139 return commands.diff(ui, repo, *map(urllib.unquote_plus, pats), **opts)
140
140
141 def rhlog(ui, repo, *pats, **opts):
141 def rhlog(ui, repo, *pats, **opts):
142 rev = opts.pop('rev')
142 rev = opts.pop('rev')
143 bra0 = opts.pop('branch')
143 bra0 = opts.pop('branch')
144 from_rev = urllib.unquote_plus(opts.pop('from', None))
144 from_rev = urllib.unquote_plus(opts.pop('from', None))
145 to_rev = urllib.unquote_plus(opts.pop('to' , None))
145 to_rev = urllib.unquote_plus(opts.pop('to' , None))
146 bra = urllib.unquote_plus(opts.pop('rhbranch', None))
146 bra = urllib.unquote_plus(opts.pop('rhbranch', None))
147 from_rev = from_rev.replace('"', '\\"')
147 from_rev = from_rev.replace('"', '\\"')
148 to_rev = to_rev.replace('"', '\\"')
148 to_rev = to_rev.replace('"', '\\"')
149 if hg.util.version() >= '1.6':
149 if hg.util.version() >= '1.6':
150 opts['rev'] = ['"%s":"%s"' % (from_rev, to_rev)]
150 opts['rev'] = ['"%s":"%s"' % (from_rev, to_rev)]
151 else:
151 else:
152 opts['rev'] = ['%s:%s' % (from_rev, to_rev)]
152 opts['rev'] = ['%s:%s' % (from_rev, to_rev)]
153 opts['branch'] = [bra]
153 opts['branch'] = [bra]
154 return commands.log(ui, repo, *map(urllib.unquote_plus, pats), **opts)
154 return commands.log(ui, repo, *map(urllib.unquote_plus, pats), **opts)
155
155
156 def rhmanifest(ui, repo, path='', **opts):
156 def rhmanifest(ui, repo, path='', **opts):
157 """output the sub-manifest of the specified directory"""
157 """output the sub-manifest of the specified directory"""
158 ui.write('<?xml version="1.0"?>\n')
158 ui.write('<?xml version="1.0"?>\n')
159 ui.write('<rhmanifest>\n')
159 ui.write('<rhmanifest>\n')
160 ui.write('<repository root="%s">\n' % _u(repo.root))
160 ui.write('<repository root="%s">\n' % _u(repo.root))
161 try:
161 try:
162 _manifest(ui, repo, urllib.unquote_plus(path), urllib.unquote_plus(opts.get('rev')))
162 _manifest(ui, repo, urllib.unquote_plus(path), urllib.unquote_plus(opts.get('rev')))
163 finally:
163 finally:
164 ui.write('</repository>\n')
164 ui.write('</repository>\n')
165 ui.write('</rhmanifest>\n')
165 ui.write('</rhmanifest>\n')
166
166
167 def rhsummary(ui, repo, **opts):
167 def rhsummary(ui, repo, **opts):
168 """output the summary of the repository"""
168 """output the summary of the repository"""
169 ui.write('<?xml version="1.0"?>\n')
169 ui.write('<?xml version="1.0"?>\n')
170 ui.write('<rhsummary>\n')
170 ui.write('<rhsummary>\n')
171 ui.write('<repository root="%s">\n' % _u(repo.root))
171 ui.write('<repository root="%s">\n' % _u(repo.root))
172 try:
172 try:
173 _tip(ui, repo)
173 _tip(ui, repo)
174 _tags(ui, repo)
174 _tags(ui, repo)
175 _branches(ui, repo)
175 _branches(ui, repo)
176 # TODO: bookmarks in core (Mercurial>=1.8)
176 # TODO: bookmarks in core (Mercurial>=1.8)
177 finally:
177 finally:
178 ui.write('</repository>\n')
178 ui.write('</repository>\n')
179 ui.write('</rhsummary>\n')
179 ui.write('</rhsummary>\n')
180
180
181 cmdtable = {
181 cmdtable = {
182 'rhannotate': (rhannotate,
182 'rhannotate': (rhannotate,
183 [('r', 'rev', '', 'revision'),
183 [('r', 'rev', '', 'revision'),
184 ('u', 'user', None, 'list the author (long with -v)'),
184 ('u', 'user', None, 'list the author (long with -v)'),
185 ('n', 'number', None, 'list the revision number (default)'),
185 ('n', 'number', None, 'list the revision number (default)'),
186 ('c', 'changeset', None, 'list the changeset'),
186 ('c', 'changeset', None, 'list the changeset'),
187 ],
187 ],
188 'hg rhannotate [-r REV] [-u] [-n] [-c] FILE...'),
188 'hg rhannotate [-r REV] [-u] [-n] [-c] FILE...'),
189 'rhcat': (rhcat,
189 'rhcat': (rhcat,
190 [('r', 'rev', '', 'revision')],
190 [('r', 'rev', '', 'revision')],
191 'hg rhcat ([-r REV] ...) FILE...'),
191 'hg rhcat ([-r REV] ...) FILE...'),
192 'rhdiff': (rhdiff,
192 'rhdiff': (rhdiff,
193 [('r', 'rev', [], 'revision'),
193 [('r', 'rev', [], 'revision'),
194 ('c', 'change', '', 'change made by revision')],
194 ('c', 'change', '', 'change made by revision')],
195 'hg rhdiff ([-c REV] | [-r REV] ...) [FILE]...'),
195 'hg rhdiff ([-c REV] | [-r REV] ...) [FILE]...'),
196 'rhlog': (rhlog,
196 'rhlog': (rhlog,
197 [
197 [
198 ('r', 'rev', [], 'show the specified revision'),
198 ('r', 'rev', [], 'show the specified revision'),
199 ('b', 'branch', [],
199 ('b', 'branch', [],
200 'show changesets within the given named branch'),
200 'show changesets within the given named branch'),
201 ('l', 'limit', '',
201 ('l', 'limit', '',
202 'limit number of changes displayed'),
202 'limit number of changes displayed'),
203 ('d', 'date', '',
203 ('d', 'date', '',
204 'show revisions matching date spec'),
204 'show revisions matching date spec'),
205 ('u', 'user', [],
205 ('u', 'user', [],
206 'revisions committed by user'),
206 'revisions committed by user'),
207 ('', 'from', '',
207 ('', 'from', '',
208 ''),
208 ''),
209 ('', 'to', '',
209 ('', 'to', '',
210 ''),
210 ''),
211 ('', 'rhbranch', '',
211 ('', 'rhbranch', '',
212 ''),
212 ''),
213 ('', 'template', '',
213 ('', 'template', '',
214 'display with template')],
214 'display with template')],
215 'hg rhlog [OPTION]... [FILE]'),
215 'hg rhlog [OPTION]... [FILE]'),
216 'rhmanifest': (rhmanifest,
216 'rhmanifest': (rhmanifest,
217 [('r', 'rev', '', 'show the specified revision')],
217 [('r', 'rev', '', 'show the specified revision')],
218 'hg rhmanifest [-r REV] [PATH]'),
218 'hg rhmanifest [-r REV] [PATH]'),
219 'rhsummary': (rhsummary, [], 'hg rhsummary'),
219 'rhsummary': (rhsummary, [], 'hg rhsummary'),
220 }
220 }
@@ -1,341 +1,341
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 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 require 'cgi'
19 require 'cgi'
20
20
21 module Redmine
21 module Redmine
22 module Scm
22 module Scm
23 module Adapters
23 module Adapters
24 class MercurialAdapter < AbstractAdapter
24 class MercurialAdapter < AbstractAdapter
25
25
26 # Mercurial executable name
26 # Mercurial executable name
27 HG_BIN = Redmine::Configuration['scm_mercurial_command'] || "hg"
27 HG_BIN = Redmine::Configuration['scm_mercurial_command'] || "hg"
28 HELPERS_DIR = File.dirname(__FILE__) + "/mercurial"
28 HELPERS_DIR = File.dirname(__FILE__) + "/mercurial"
29 HG_HELPER_EXT = "#{HELPERS_DIR}/redminehelper.py"
29 HG_HELPER_EXT = "#{HELPERS_DIR}/redminehelper.py"
30 TEMPLATE_NAME = "hg-template"
30 TEMPLATE_NAME = "hg-template"
31 TEMPLATE_EXTENSION = "tmpl"
31 TEMPLATE_EXTENSION = "tmpl"
32
32
33 # raised if hg command exited with error, e.g. unknown revision.
33 # raised if hg command exited with error, e.g. unknown revision.
34 class HgCommandAborted < CommandFailed; end
34 class HgCommandAborted < CommandFailed; end
35
35
36 class << self
36 class << self
37 def client_command
37 def client_command
38 @@bin ||= HG_BIN
38 @@bin ||= HG_BIN
39 end
39 end
40
40
41 def sq_bin
41 def sq_bin
42 @@sq_bin ||= shell_quote_command
42 @@sq_bin ||= shell_quote_command
43 end
43 end
44
44
45 def client_version
45 def client_version
46 @@client_version ||= (hgversion || [])
46 @@client_version ||= (hgversion || [])
47 end
47 end
48
48
49 def client_available
49 def client_available
50 client_version_above?([1, 2])
50 client_version_above?([1, 2])
51 end
51 end
52
52
53 def hgversion
53 def hgversion
54 # The hg version is expressed either as a
54 # The hg version is expressed either as a
55 # release number (eg 0.9.5 or 1.0) or as a revision
55 # release number (eg 0.9.5 or 1.0) or as a revision
56 # id composed of 12 hexa characters.
56 # id composed of 12 hexa characters.
57 theversion = hgversion_from_command_line.dup
57 theversion = hgversion_from_command_line.dup
58 if theversion.respond_to?(:force_encoding)
58 if theversion.respond_to?(:force_encoding)
59 theversion.force_encoding('ASCII-8BIT')
59 theversion.force_encoding('ASCII-8BIT')
60 end
60 end
61 if m = theversion.match(%r{\A(.*?)((\d+\.)+\d+)})
61 if m = theversion.match(%r{\A(.*?)((\d+\.)+\d+)})
62 m[2].scan(%r{\d+}).collect(&:to_i)
62 m[2].scan(%r{\d+}).collect(&:to_i)
63 end
63 end
64 end
64 end
65
65
66 def hgversion_from_command_line
66 def hgversion_from_command_line
67 shellout("#{sq_bin} --version") { |io| io.read }.to_s
67 shellout("#{sq_bin} --version") { |io| io.read }.to_s
68 end
68 end
69
69
70 def template_path
70 def template_path
71 @@template_path ||= template_path_for(client_version)
71 @@template_path ||= template_path_for(client_version)
72 end
72 end
73
73
74 def template_path_for(version)
74 def template_path_for(version)
75 "#{HELPERS_DIR}/#{TEMPLATE_NAME}-1.0.#{TEMPLATE_EXTENSION}"
75 "#{HELPERS_DIR}/#{TEMPLATE_NAME}-1.0.#{TEMPLATE_EXTENSION}"
76 end
76 end
77 end
77 end
78
78
79 def initialize(url, root_url=nil, login=nil, password=nil, path_encoding=nil)
79 def initialize(url, root_url=nil, login=nil, password=nil, path_encoding=nil)
80 super
80 super
81 @path_encoding = path_encoding.blank? ? 'UTF-8' : path_encoding
81 @path_encoding = path_encoding.blank? ? 'UTF-8' : path_encoding
82 end
82 end
83
83
84 def path_encoding
84 def path_encoding
85 @path_encoding
85 @path_encoding
86 end
86 end
87
87
88 def info
88 def info
89 tip = summary['repository']['tip']
89 tip = summary['repository']['tip']
90 Info.new(:root_url => CGI.unescape(summary['repository']['root']),
90 Info.new(:root_url => CGI.unescape(summary['repository']['root']),
91 :lastrev => Revision.new(:revision => tip['revision'],
91 :lastrev => Revision.new(:revision => tip['revision'],
92 :scmid => tip['node']))
92 :scmid => tip['node']))
93 # rescue HgCommandAborted
93 # rescue HgCommandAborted
94 rescue Exception => e
94 rescue Exception => e
95 logger.error "hg: error during getting info: #{e.message}"
95 logger.error "hg: error during getting info: #{e.message}"
96 nil
96 nil
97 end
97 end
98
98
99 def tags
99 def tags
100 as_ary(summary['repository']['tag']).map { |e| e['name'] }
100 as_ary(summary['repository']['tag']).map { |e| e['name'] }
101 end
101 end
102
102
103 # Returns map of {'tag' => 'nodeid', ...}
103 # Returns map of {'tag' => 'nodeid', ...}
104 def tagmap
104 def tagmap
105 alist = as_ary(summary['repository']['tag']).map do |e|
105 alist = as_ary(summary['repository']['tag']).map do |e|
106 e.values_at('name', 'node')
106 e.values_at('name', 'node')
107 end
107 end
108 Hash[*alist.flatten]
108 Hash[*alist.flatten]
109 end
109 end
110
110
111 def branches
111 def branches
112 brs = []
112 brs = []
113 as_ary(summary['repository']['branch']).each do |e|
113 as_ary(summary['repository']['branch']).each do |e|
114 br = Branch.new(e['name'])
114 br = Branch.new(e['name'])
115 br.revision = e['revision']
115 br.revision = e['revision']
116 br.scmid = e['node']
116 br.scmid = e['node']
117 brs << br
117 brs << br
118 end
118 end
119 brs
119 brs
120 end
120 end
121
121
122 # Returns map of {'branch' => 'nodeid', ...}
122 # Returns map of {'branch' => 'nodeid', ...}
123 def branchmap
123 def branchmap
124 alist = as_ary(summary['repository']['branch']).map do |e|
124 alist = as_ary(summary['repository']['branch']).map do |e|
125 e.values_at('name', 'node')
125 e.values_at('name', 'node')
126 end
126 end
127 Hash[*alist.flatten]
127 Hash[*alist.flatten]
128 end
128 end
129
129
130 def summary
130 def summary
131 return @summary if @summary
131 return @summary if @summary
132 hg 'rhsummary' do |io|
132 hg 'rhsummary' do |io|
133 output = io.read
133 output = io.read
134 if output.respond_to?(:force_encoding)
134 if output.respond_to?(:force_encoding)
135 output.force_encoding('UTF-8')
135 output.force_encoding('UTF-8')
136 end
136 end
137 begin
137 begin
138 @summary = parse_xml(output)['rhsummary']
138 @summary = parse_xml(output)['rhsummary']
139 rescue
139 rescue
140 end
140 end
141 end
141 end
142 end
142 end
143 private :summary
143 private :summary
144
144
145 def entries(path=nil, identifier=nil, options={})
145 def entries(path=nil, identifier=nil, options={})
146 p1 = scm_iconv(@path_encoding, 'UTF-8', path)
146 p1 = scm_iconv(@path_encoding, 'UTF-8', path)
147 manifest = hg('rhmanifest', '-r', CGI.escape(hgrev(identifier)),
147 manifest = hg('rhmanifest', '-r', CGI.escape(hgrev(identifier)),
148 CGI.escape(without_leading_slash(p1.to_s))) do |io|
148 CGI.escape(without_leading_slash(p1.to_s))) do |io|
149 output = io.read
149 output = io.read
150 if output.respond_to?(:force_encoding)
150 if output.respond_to?(:force_encoding)
151 output.force_encoding('UTF-8')
151 output.force_encoding('UTF-8')
152 end
152 end
153 begin
153 begin
154 parse_xml(output)['rhmanifest']['repository']['manifest']
154 parse_xml(output)['rhmanifest']['repository']['manifest']
155 rescue
155 rescue
156 end
156 end
157 end
157 end
158 path_prefix = path.blank? ? '' : with_trailling_slash(path)
158 path_prefix = path.blank? ? '' : with_trailling_slash(path)
159
159
160 entries = Entries.new
160 entries = Entries.new
161 as_ary(manifest['dir']).each do |e|
161 as_ary(manifest['dir']).each do |e|
162 n = scm_iconv('UTF-8', @path_encoding, CGI.unescape(e['name']))
162 n = scm_iconv('UTF-8', @path_encoding, CGI.unescape(e['name']))
163 p = "#{path_prefix}#{n}"
163 p = "#{path_prefix}#{n}"
164 entries << Entry.new(:name => n, :path => p, :kind => 'dir')
164 entries << Entry.new(:name => n, :path => p, :kind => 'dir')
165 end
165 end
166
166
167 as_ary(manifest['file']).each do |e|
167 as_ary(manifest['file']).each do |e|
168 n = scm_iconv('UTF-8', @path_encoding, CGI.unescape(e['name']))
168 n = scm_iconv('UTF-8', @path_encoding, CGI.unescape(e['name']))
169 p = "#{path_prefix}#{n}"
169 p = "#{path_prefix}#{n}"
170 lr = Revision.new(:revision => e['revision'], :scmid => e['node'],
170 lr = Revision.new(:revision => e['revision'], :scmid => e['node'],
171 :identifier => e['node'],
171 :identifier => e['node'],
172 :time => Time.at(e['time'].to_i))
172 :time => Time.at(e['time'].to_i))
173 entries << Entry.new(:name => n, :path => p, :kind => 'file',
173 entries << Entry.new(:name => n, :path => p, :kind => 'file',
174 :size => e['size'].to_i, :lastrev => lr)
174 :size => e['size'].to_i, :lastrev => lr)
175 end
175 end
176
176
177 entries
177 entries
178 rescue HgCommandAborted
178 rescue HgCommandAborted
179 nil # means not found
179 nil # means not found
180 end
180 end
181
181
182 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
182 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
183 revs = Revisions.new
183 revs = Revisions.new
184 each_revision(path, identifier_from, identifier_to, options) { |e| revs << e }
184 each_revision(path, identifier_from, identifier_to, options) { |e| revs << e }
185 revs
185 revs
186 end
186 end
187
187
188 # Iterates the revisions by using a template file that
188 # Iterates the revisions by using a template file that
189 # makes Mercurial produce a xml output.
189 # makes Mercurial produce a xml output.
190 def each_revision(path=nil, identifier_from=nil, identifier_to=nil, options={})
190 def each_revision(path=nil, identifier_from=nil, identifier_to=nil, options={})
191 hg_args = ['log', '--debug', '-C', '--style', self.class.template_path]
191 hg_args = ['log', '--debug', '-C', '--style', self.class.template_path]
192 hg_args << '-r' << "#{hgrev(identifier_from)}:#{hgrev(identifier_to)}"
192 hg_args << '-r' << "#{hgrev(identifier_from)}:#{hgrev(identifier_to)}"
193 hg_args << '--limit' << options[:limit] if options[:limit]
193 hg_args << '--limit' << options[:limit] if options[:limit]
194 hg_args << hgtarget(path) unless path.blank?
194 hg_args << hgtarget(path) unless path.blank?
195 log = hg(*hg_args) do |io|
195 log = hg(*hg_args) do |io|
196 output = io.read
196 output = io.read
197 if output.respond_to?(:force_encoding)
197 if output.respond_to?(:force_encoding)
198 output.force_encoding('UTF-8')
198 output.force_encoding('UTF-8')
199 end
199 end
200 begin
200 begin
201 # Mercurial < 1.5 does not support footer template for '</log>'
201 # Mercurial < 1.5 does not support footer template for '</log>'
202 parse_xml("#{output}</log>")['log']
202 parse_xml("#{output}</log>")['log']
203 rescue
203 rescue
204 end
204 end
205 end
205 end
206 as_ary(log['logentry']).each do |le|
206 as_ary(log['logentry']).each do |le|
207 cpalist = as_ary(le['paths']['path-copied']).map do |e|
207 cpalist = as_ary(le['paths']['path-copied']).map do |e|
208 [e['__content__'], e['copyfrom-path']].map do |s|
208 [e['__content__'], e['copyfrom-path']].map do |s|
209 scm_iconv('UTF-8', @path_encoding, CGI.unescape(s))
209 scm_iconv('UTF-8', @path_encoding, CGI.unescape(s))
210 end
210 end
211 end
211 end
212 cpmap = Hash[*cpalist.flatten]
212 cpmap = Hash[*cpalist.flatten]
213 paths = as_ary(le['paths']['path']).map do |e|
213 paths = as_ary(le['paths']['path']).map do |e|
214 p = scm_iconv('UTF-8', @path_encoding, CGI.unescape(e['__content__']) )
214 p = scm_iconv('UTF-8', @path_encoding, CGI.unescape(e['__content__']) )
215 {:action => e['action'],
215 {:action => e['action'],
216 :path => with_leading_slash(p),
216 :path => with_leading_slash(p),
217 :from_path => (cpmap.member?(p) ? with_leading_slash(cpmap[p]) : nil),
217 :from_path => (cpmap.member?(p) ? with_leading_slash(cpmap[p]) : nil),
218 :from_revision => (cpmap.member?(p) ? le['node'] : nil)}
218 :from_revision => (cpmap.member?(p) ? le['node'] : nil)}
219 end.sort { |a, b| a[:path] <=> b[:path] }
219 end.sort { |a, b| a[:path] <=> b[:path] }
220 parents_ary = []
220 parents_ary = []
221 as_ary(le['parents']['parent']).map do |par|
221 as_ary(le['parents']['parent']).map do |par|
222 parents_ary << par['__content__'] if par['__content__'] != "000000000000"
222 parents_ary << par['__content__'] if par['__content__'] != "0000000000000000000000000000000000000000"
223 end
223 end
224 yield Revision.new(:revision => le['revision'],
224 yield Revision.new(:revision => le['revision'],
225 :scmid => le['node'],
225 :scmid => le['node'],
226 :author => (le['author']['__content__'] rescue ''),
226 :author => (le['author']['__content__'] rescue ''),
227 :time => Time.parse(le['date']['__content__']),
227 :time => Time.parse(le['date']['__content__']),
228 :message => le['msg']['__content__'],
228 :message => le['msg']['__content__'],
229 :paths => paths,
229 :paths => paths,
230 :parents => parents_ary)
230 :parents => parents_ary)
231 end
231 end
232 self
232 self
233 end
233 end
234
234
235 # Returns list of nodes in the specified branch
235 # Returns list of nodes in the specified branch
236 def nodes_in_branch(branch, options={})
236 def nodes_in_branch(branch, options={})
237 hg_args = ['rhlog', '--template', '{node|short}\n', '--rhbranch', CGI.escape(branch)]
237 hg_args = ['rhlog', '--template', '{node}\n', '--rhbranch', CGI.escape(branch)]
238 hg_args << '--from' << CGI.escape(branch)
238 hg_args << '--from' << CGI.escape(branch)
239 hg_args << '--to' << '0'
239 hg_args << '--to' << '0'
240 hg_args << '--limit' << options[:limit] if options[:limit]
240 hg_args << '--limit' << options[:limit] if options[:limit]
241 hg(*hg_args) { |io| io.readlines.map { |e| e.chomp } }
241 hg(*hg_args) { |io| io.readlines.map { |e| e.chomp } }
242 end
242 end
243
243
244 def diff(path, identifier_from, identifier_to=nil)
244 def diff(path, identifier_from, identifier_to=nil)
245 hg_args = %w|rhdiff|
245 hg_args = %w|rhdiff|
246 if identifier_to
246 if identifier_to
247 hg_args << '-r' << hgrev(identifier_to) << '-r' << hgrev(identifier_from)
247 hg_args << '-r' << hgrev(identifier_to) << '-r' << hgrev(identifier_from)
248 else
248 else
249 hg_args << '-c' << hgrev(identifier_from)
249 hg_args << '-c' << hgrev(identifier_from)
250 end
250 end
251 unless path.blank?
251 unless path.blank?
252 p = scm_iconv(@path_encoding, 'UTF-8', path)
252 p = scm_iconv(@path_encoding, 'UTF-8', path)
253 hg_args << CGI.escape(hgtarget(p))
253 hg_args << CGI.escape(hgtarget(p))
254 end
254 end
255 diff = []
255 diff = []
256 hg *hg_args do |io|
256 hg *hg_args do |io|
257 io.each_line do |line|
257 io.each_line do |line|
258 diff << line
258 diff << line
259 end
259 end
260 end
260 end
261 diff
261 diff
262 rescue HgCommandAborted
262 rescue HgCommandAborted
263 nil # means not found
263 nil # means not found
264 end
264 end
265
265
266 def cat(path, identifier=nil)
266 def cat(path, identifier=nil)
267 p = CGI.escape(scm_iconv(@path_encoding, 'UTF-8', path))
267 p = CGI.escape(scm_iconv(@path_encoding, 'UTF-8', path))
268 hg 'rhcat', '-r', CGI.escape(hgrev(identifier)), hgtarget(p) do |io|
268 hg 'rhcat', '-r', CGI.escape(hgrev(identifier)), hgtarget(p) do |io|
269 io.binmode
269 io.binmode
270 io.read
270 io.read
271 end
271 end
272 rescue HgCommandAborted
272 rescue HgCommandAborted
273 nil # means not found
273 nil # means not found
274 end
274 end
275
275
276 def annotate(path, identifier=nil)
276 def annotate(path, identifier=nil)
277 p = CGI.escape(scm_iconv(@path_encoding, 'UTF-8', path))
277 p = CGI.escape(scm_iconv(@path_encoding, 'UTF-8', path))
278 blame = Annotate.new
278 blame = Annotate.new
279 hg 'rhannotate', '-ncu', '-r', CGI.escape(hgrev(identifier)), hgtarget(p) do |io|
279 hg 'rhannotate', '-ncu', '-r', CGI.escape(hgrev(identifier)), hgtarget(p) do |io|
280 io.each_line do |line|
280 io.each_line do |line|
281 line.force_encoding('ASCII-8BIT') if line.respond_to?(:force_encoding)
281 line.force_encoding('ASCII-8BIT') if line.respond_to?(:force_encoding)
282 next unless line =~ %r{^([^:]+)\s(\d+)\s([0-9a-f]+):\s(.*)$}
282 next unless line =~ %r{^([^:]+)\s(\d+)\s([0-9a-f]+):\s(.*)$}
283 r = Revision.new(:author => $1.strip, :revision => $2, :scmid => $3,
283 r = Revision.new(:author => $1.strip, :revision => $2, :scmid => $3,
284 :identifier => $3)
284 :identifier => $3)
285 blame.add_line($4.rstrip, r)
285 blame.add_line($4.rstrip, r)
286 end
286 end
287 end
287 end
288 blame
288 blame
289 rescue HgCommandAborted
289 rescue HgCommandAborted
290 # means not found or cannot be annotated
290 # means not found or cannot be annotated
291 Annotate.new
291 Annotate.new
292 end
292 end
293
293
294 class Revision < Redmine::Scm::Adapters::Revision
294 class Revision < Redmine::Scm::Adapters::Revision
295 # Returns the readable identifier
295 # Returns the readable identifier
296 def format_identifier
296 def format_identifier
297 "#{revision}:#{scmid}"
297 "#{revision}:#{scmid}"
298 end
298 end
299 end
299 end
300
300
301 # Runs 'hg' command with the given args
301 # Runs 'hg' command with the given args
302 def hg(*args, &block)
302 def hg(*args, &block)
303 repo_path = root_url || url
303 repo_path = root_url || url
304 full_args = ['-R', repo_path, '--encoding', 'utf-8']
304 full_args = ['-R', repo_path, '--encoding', 'utf-8']
305 full_args << '--config' << "extensions.redminehelper=#{HG_HELPER_EXT}"
305 full_args << '--config' << "extensions.redminehelper=#{HG_HELPER_EXT}"
306 full_args << '--config' << 'diff.git=false'
306 full_args << '--config' << 'diff.git=false'
307 full_args += args
307 full_args += args
308 ret = shellout(
308 ret = shellout(
309 self.class.sq_bin + ' ' + full_args.map { |e| shell_quote e.to_s }.join(' '),
309 self.class.sq_bin + ' ' + full_args.map { |e| shell_quote e.to_s }.join(' '),
310 &block
310 &block
311 )
311 )
312 if $? && $?.exitstatus != 0
312 if $? && $?.exitstatus != 0
313 raise HgCommandAborted, "hg exited with non-zero status: #{$?.exitstatus}"
313 raise HgCommandAborted, "hg exited with non-zero status: #{$?.exitstatus}"
314 end
314 end
315 ret
315 ret
316 end
316 end
317 private :hg
317 private :hg
318
318
319 # Returns correct revision identifier
319 # Returns correct revision identifier
320 def hgrev(identifier, sq=false)
320 def hgrev(identifier, sq=false)
321 rev = identifier.blank? ? 'tip' : identifier.to_s
321 rev = identifier.blank? ? 'tip' : identifier.to_s
322 rev = shell_quote(rev) if sq
322 rev = shell_quote(rev) if sq
323 rev
323 rev
324 end
324 end
325 private :hgrev
325 private :hgrev
326
326
327 def hgtarget(path)
327 def hgtarget(path)
328 path ||= ''
328 path ||= ''
329 root_url + '/' + without_leading_slash(path)
329 root_url + '/' + without_leading_slash(path)
330 end
330 end
331 private :hgtarget
331 private :hgtarget
332
332
333 def as_ary(o)
333 def as_ary(o)
334 return [] unless o
334 return [] unless o
335 o.is_a?(Array) ? o : Array[o]
335 o.is_a?(Array) ? o : Array[o]
336 end
336 end
337 private :as_ary
337 private :as_ary
338 end
338 end
339 end
339 end
340 end
340 end
341 end
341 end
@@ -1,475 +1,475
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 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 begin
19 begin
20 require 'mocha/setup'
20 require 'mocha/setup'
21
21
22 class MercurialAdapterTest < ActiveSupport::TestCase
22 class MercurialAdapterTest < ActiveSupport::TestCase
23 HELPERS_DIR = Redmine::Scm::Adapters::MercurialAdapter::HELPERS_DIR
23 HELPERS_DIR = Redmine::Scm::Adapters::MercurialAdapter::HELPERS_DIR
24 TEMPLATE_NAME = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATE_NAME
24 TEMPLATE_NAME = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATE_NAME
25 TEMPLATE_EXTENSION = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATE_EXTENSION
25 TEMPLATE_EXTENSION = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATE_EXTENSION
26
26
27 REPOSITORY_PATH = Rails.root.join('tmp/test/mercurial_repository').to_s
27 REPOSITORY_PATH = Rails.root.join('tmp/test/mercurial_repository').to_s
28 CHAR_1_HEX = "\xc3\x9c"
28 CHAR_1_HEX = "\xc3\x9c"
29
29
30 if File.directory?(REPOSITORY_PATH)
30 if File.directory?(REPOSITORY_PATH)
31 def setup
31 def setup
32 adapter_class = Redmine::Scm::Adapters::MercurialAdapter
32 adapter_class = Redmine::Scm::Adapters::MercurialAdapter
33 assert adapter_class
33 assert adapter_class
34 assert adapter_class.client_command
34 assert adapter_class.client_command
35 assert_equal true, adapter_class.client_available
35 assert_equal true, adapter_class.client_available
36 assert_equal true, adapter_class.client_version_above?([0, 9, 5])
36 assert_equal true, adapter_class.client_version_above?([0, 9, 5])
37
37
38 @adapter = Redmine::Scm::Adapters::MercurialAdapter.new(
38 @adapter = Redmine::Scm::Adapters::MercurialAdapter.new(
39 REPOSITORY_PATH,
39 REPOSITORY_PATH,
40 nil,
40 nil,
41 nil,
41 nil,
42 nil,
42 nil,
43 'ISO-8859-1')
43 'ISO-8859-1')
44 @diff_c_support = true
44 @diff_c_support = true
45 @char_1 = CHAR_1_HEX.dup
45 @char_1 = CHAR_1_HEX.dup
46 @tag_char_1 = "tag-#{CHAR_1_HEX}-00"
46 @tag_char_1 = "tag-#{CHAR_1_HEX}-00"
47 @branch_char_0 = "branch-#{CHAR_1_HEX}-00"
47 @branch_char_0 = "branch-#{CHAR_1_HEX}-00"
48 @branch_char_1 = "branch-#{CHAR_1_HEX}-01"
48 @branch_char_1 = "branch-#{CHAR_1_HEX}-01"
49 if @tag_char_1.respond_to?(:force_encoding)
49 if @tag_char_1.respond_to?(:force_encoding)
50 @char_1.force_encoding('UTF-8')
50 @char_1.force_encoding('UTF-8')
51 @tag_char_1.force_encoding('UTF-8')
51 @tag_char_1.force_encoding('UTF-8')
52 @branch_char_0.force_encoding('UTF-8')
52 @branch_char_0.force_encoding('UTF-8')
53 @branch_char_1.force_encoding('UTF-8')
53 @branch_char_1.force_encoding('UTF-8')
54 end
54 end
55 end
55 end
56
56
57 def test_hgversion
57 def test_hgversion
58 to_test = { "Mercurial Distributed SCM (version 0.9.5)\n" => [0,9,5],
58 to_test = { "Mercurial Distributed SCM (version 0.9.5)\n" => [0,9,5],
59 "Mercurial Distributed SCM (1.0)\n" => [1,0],
59 "Mercurial Distributed SCM (1.0)\n" => [1,0],
60 "Mercurial Distributed SCM (1e4ddc9ac9f7+20080325)\n" => nil,
60 "Mercurial Distributed SCM (1e4ddc9ac9f7+20080325)\n" => nil,
61 "Mercurial Distributed SCM (1.0.1+20080525)\n" => [1,0,1],
61 "Mercurial Distributed SCM (1.0.1+20080525)\n" => [1,0,1],
62 "Mercurial Distributed SCM (1916e629a29d)\n" => nil,
62 "Mercurial Distributed SCM (1916e629a29d)\n" => nil,
63 "Mercurial SCM Distribuito (versione 0.9.5)\n" => [0,9,5],
63 "Mercurial SCM Distribuito (versione 0.9.5)\n" => [0,9,5],
64 "(1.6)\n(1.7)\n(1.8)" => [1,6],
64 "(1.6)\n(1.7)\n(1.8)" => [1,6],
65 "(1.7.1)\r\n(1.8.1)\r\n(1.9.1)" => [1,7,1]}
65 "(1.7.1)\r\n(1.8.1)\r\n(1.9.1)" => [1,7,1]}
66
66
67 to_test.each do |s, v|
67 to_test.each do |s, v|
68 test_hgversion_for(s, v)
68 test_hgversion_for(s, v)
69 end
69 end
70 end
70 end
71
71
72 def test_template_path
72 def test_template_path
73 to_test = {
73 to_test = {
74 [1,2] => "1.0",
74 [1,2] => "1.0",
75 [] => "1.0",
75 [] => "1.0",
76 [1,2,1] => "1.0",
76 [1,2,1] => "1.0",
77 [1,7] => "1.0",
77 [1,7] => "1.0",
78 [1,7,1] => "1.0",
78 [1,7,1] => "1.0",
79 [2,0] => "1.0",
79 [2,0] => "1.0",
80 }
80 }
81 to_test.each do |v, template|
81 to_test.each do |v, template|
82 test_template_path_for(v, template)
82 test_template_path_for(v, template)
83 end
83 end
84 end
84 end
85
85
86 def test_info
86 def test_info
87 [REPOSITORY_PATH, REPOSITORY_PATH + "/",
87 [REPOSITORY_PATH, REPOSITORY_PATH + "/",
88 REPOSITORY_PATH + "//"].each do |repo|
88 REPOSITORY_PATH + "//"].each do |repo|
89 adp = Redmine::Scm::Adapters::MercurialAdapter.new(repo)
89 adp = Redmine::Scm::Adapters::MercurialAdapter.new(repo)
90 repo_path = adp.info.root_url.gsub(/\\/, "/")
90 repo_path = adp.info.root_url.gsub(/\\/, "/")
91 assert_equal REPOSITORY_PATH, repo_path
91 assert_equal REPOSITORY_PATH, repo_path
92 assert_equal '31', adp.info.lastrev.revision
92 assert_equal '31', adp.info.lastrev.revision
93 assert_equal '31eeee7395c8',adp.info.lastrev.scmid
93 assert_equal '31eeee7395c8c78e66dd54c50addd078d10b2355',adp.info.lastrev.scmid
94 end
94 end
95 end
95 end
96
96
97 def test_revisions
97 def test_revisions
98 revisions = @adapter.revisions(nil, 2, 4)
98 revisions = @adapter.revisions(nil, 2, 4)
99 assert_equal 3, revisions.size
99 assert_equal 3, revisions.size
100 assert_equal '2', revisions[0].revision
100 assert_equal '2', revisions[0].revision
101 assert_equal '400bb8672109', revisions[0].scmid
101 assert_equal '400bb86721098697c7d17b3724c794c57636de70', revisions[0].scmid
102 assert_equal '4', revisions[2].revision
102 assert_equal '4', revisions[2].revision
103 assert_equal 'def6d2f1254a', revisions[2].scmid
103 assert_equal 'def6d2f1254a56fb8fbe9ec3b5c0451674dbd8b8', revisions[2].scmid
104
104
105 revisions = @adapter.revisions(nil, 2, 4, {:limit => 2})
105 revisions = @adapter.revisions(nil, 2, 4, {:limit => 2})
106 assert_equal 2, revisions.size
106 assert_equal 2, revisions.size
107 assert_equal '2', revisions[0].revision
107 assert_equal '2', revisions[0].revision
108 assert_equal '400bb8672109', revisions[0].scmid
108 assert_equal '400bb86721098697c7d17b3724c794c57636de70', revisions[0].scmid
109 end
109 end
110
110
111 def test_parents
111 def test_parents
112 revs1 = @adapter.revisions(nil, 0, 0)
112 revs1 = @adapter.revisions(nil, 0, 0)
113 assert_equal 1, revs1.size
113 assert_equal 1, revs1.size
114 assert_equal [], revs1[0].parents
114 assert_equal [], revs1[0].parents
115 revs2 = @adapter.revisions(nil, 1, 1)
115 revs2 = @adapter.revisions(nil, 1, 1)
116 assert_equal 1, revs2.size
116 assert_equal 1, revs2.size
117 assert_equal 1, revs2[0].parents.size
117 assert_equal 1, revs2[0].parents.size
118 assert_equal "0885933ad4f6", revs2[0].parents[0]
118 assert_equal "0885933ad4f68d77c2649cd11f8311276e7ef7ce", revs2[0].parents[0]
119 revs3 = @adapter.revisions(nil, 30, 30)
119 revs3 = @adapter.revisions(nil, 30, 30)
120 assert_equal 1, revs3.size
120 assert_equal 1, revs3.size
121 assert_equal 2, revs3[0].parents.size
121 assert_equal 2, revs3[0].parents.size
122 assert_equal "a94b0528f24f", revs3[0].parents[0]
122 assert_equal "a94b0528f24fe05ebaef496ae0500bb050772e36", revs3[0].parents[0]
123 assert_equal "3a330eb32958", revs3[0].parents[1]
123 assert_equal "3a330eb329586ea2adb3f83237c23310e744ebe9", revs3[0].parents[1]
124 end
124 end
125
125
126 def test_diff
126 def test_diff
127 if @adapter.class.client_version_above?([1, 2])
127 if @adapter.class.client_version_above?([1, 2])
128 assert_nil @adapter.diff(nil, '100000')
128 assert_nil @adapter.diff(nil, '100000')
129 end
129 end
130 assert_nil @adapter.diff(nil, '100000', '200000')
130 assert_nil @adapter.diff(nil, '100000', '200000')
131 [2, '400bb8672109', '400', 400].each do |r1|
131 [2, '400bb8672109', '400', 400].each do |r1|
132 diff1 = @adapter.diff(nil, r1)
132 diff1 = @adapter.diff(nil, r1)
133 if @diff_c_support
133 if @diff_c_support
134 assert_equal 28, diff1.size
134 assert_equal 28, diff1.size
135 buf = diff1[24].gsub(/\r\n|\r|\n/, "")
135 buf = diff1[24].gsub(/\r\n|\r|\n/, "")
136 assert_equal "+ return true unless klass.respond_to?('watched_by')", buf
136 assert_equal "+ return true unless klass.respond_to?('watched_by')", buf
137 else
137 else
138 assert_equal 0, diff1.size
138 assert_equal 0, diff1.size
139 end
139 end
140 [4, 'def6d2f1254a'].each do |r2|
140 [4, 'def6d2f1254a'].each do |r2|
141 diff2 = @adapter.diff(nil, r1, r2)
141 diff2 = @adapter.diff(nil, r1, r2)
142 assert_equal 49, diff2.size
142 assert_equal 49, diff2.size
143 buf = diff2[41].gsub(/\r\n|\r|\n/, "")
143 buf = diff2[41].gsub(/\r\n|\r|\n/, "")
144 assert_equal "+class WelcomeController < ApplicationController", buf
144 assert_equal "+class WelcomeController < ApplicationController", buf
145 diff3 = @adapter.diff('sources/watchers_controller.rb', r1, r2)
145 diff3 = @adapter.diff('sources/watchers_controller.rb', r1, r2)
146 assert_equal 20, diff3.size
146 assert_equal 20, diff3.size
147 buf = diff3[12].gsub(/\r\n|\r|\n/, "")
147 buf = diff3[12].gsub(/\r\n|\r|\n/, "")
148 assert_equal "+ @watched.remove_watcher(user)", buf
148 assert_equal "+ @watched.remove_watcher(user)", buf
149
149
150 diff4 = @adapter.diff(nil, r2, r1)
150 diff4 = @adapter.diff(nil, r2, r1)
151 assert_equal 49, diff4.size
151 assert_equal 49, diff4.size
152 buf = diff4[41].gsub(/\r\n|\r|\n/, "")
152 buf = diff4[41].gsub(/\r\n|\r|\n/, "")
153 assert_equal "-class WelcomeController < ApplicationController", buf
153 assert_equal "-class WelcomeController < ApplicationController", buf
154 diff5 = @adapter.diff('sources/watchers_controller.rb', r2, r1)
154 diff5 = @adapter.diff('sources/watchers_controller.rb', r2, r1)
155 assert_equal 20, diff5.size
155 assert_equal 20, diff5.size
156 buf = diff5[9].gsub(/\r\n|\r|\n/, "")
156 buf = diff5[9].gsub(/\r\n|\r|\n/, "")
157 assert_equal "- @watched.remove_watcher(user)", buf
157 assert_equal "- @watched.remove_watcher(user)", buf
158 end
158 end
159 end
159 end
160 end
160 end
161
161
162 def test_diff_made_by_revision
162 def test_diff_made_by_revision
163 if @diff_c_support
163 if @diff_c_support
164 [24, '24', '4cddb4e45f52'].each do |r1|
164 [24, '24', '4cddb4e45f52'].each do |r1|
165 diff1 = @adapter.diff(nil, r1)
165 diff1 = @adapter.diff(nil, r1)
166 assert_equal 5, diff1.size
166 assert_equal 5, diff1.size
167 buf = diff1[4].gsub(/\r\n|\r|\n/, "")
167 buf = diff1[4].gsub(/\r\n|\r|\n/, "")
168 assert_equal '+0885933ad4f68d77c2649cd11f8311276e7ef7ce tag-init-revision', buf
168 assert_equal '+0885933ad4f68d77c2649cd11f8311276e7ef7ce tag-init-revision', buf
169 end
169 end
170 end
170 end
171 end
171 end
172
172
173 def test_cat
173 def test_cat
174 [2, '400bb8672109', '400', 400].each do |r|
174 [2, '400bb8672109', '400', 400].each do |r|
175 buf = @adapter.cat('sources/welcome_controller.rb', r)
175 buf = @adapter.cat('sources/welcome_controller.rb', r)
176 assert buf
176 assert buf
177 lines = buf.split("\r\n")
177 lines = buf.split("\r\n")
178 assert_equal 25, lines.length
178 assert_equal 25, lines.length
179 assert_equal 'class WelcomeController < ApplicationController', lines[17]
179 assert_equal 'class WelcomeController < ApplicationController', lines[17]
180 end
180 end
181 assert_nil @adapter.cat('sources/welcome_controller.rb')
181 assert_nil @adapter.cat('sources/welcome_controller.rb')
182 end
182 end
183
183
184 def test_annotate
184 def test_annotate
185 assert_equal [], @adapter.annotate("sources/welcome_controller.rb").lines
185 assert_equal [], @adapter.annotate("sources/welcome_controller.rb").lines
186 [2, '400bb8672109', '400', 400].each do |r|
186 [2, '400bb8672109', '400', 400].each do |r|
187 ann = @adapter.annotate('sources/welcome_controller.rb', r)
187 ann = @adapter.annotate('sources/welcome_controller.rb', r)
188 assert ann
188 assert ann
189 assert_equal '1', ann.revisions[17].revision
189 assert_equal '1', ann.revisions[17].revision
190 assert_equal '9d5b5b004199', ann.revisions[17].identifier
190 assert_equal '9d5b5b004199', ann.revisions[17].identifier
191 assert_equal 'jsmith', ann.revisions[0].author
191 assert_equal 'jsmith', ann.revisions[0].author
192 assert_equal 25, ann.lines.length
192 assert_equal 25, ann.lines.length
193 assert_equal 'class WelcomeController < ApplicationController', ann.lines[17]
193 assert_equal 'class WelcomeController < ApplicationController', ann.lines[17]
194 end
194 end
195 end
195 end
196
196
197 def test_entries
197 def test_entries
198 assert_nil @adapter.entries(nil, '100000')
198 assert_nil @adapter.entries(nil, '100000')
199
199
200 assert_equal 1, @adapter.entries("sources", 3).size
200 assert_equal 1, @adapter.entries("sources", 3).size
201 assert_equal 1, @adapter.entries("sources", 'b3a615152df8').size
201 assert_equal 1, @adapter.entries("sources", 'b3a615152df8').size
202
202
203 [2, '400bb8672109', '400', 400].each do |r|
203 [2, '400bb8672109', '400', 400].each do |r|
204 entries1 = @adapter.entries(nil, r)
204 entries1 = @adapter.entries(nil, r)
205 assert entries1
205 assert entries1
206 assert_equal 3, entries1.size
206 assert_equal 3, entries1.size
207 assert_equal 'sources', entries1[1].name
207 assert_equal 'sources', entries1[1].name
208 assert_equal 'sources', entries1[1].path
208 assert_equal 'sources', entries1[1].path
209 assert_equal 'dir', entries1[1].kind
209 assert_equal 'dir', entries1[1].kind
210 readme = entries1[2]
210 readme = entries1[2]
211 assert_equal 'README', readme.name
211 assert_equal 'README', readme.name
212 assert_equal 'README', readme.path
212 assert_equal 'README', readme.path
213 assert_equal 'file', readme.kind
213 assert_equal 'file', readme.kind
214 assert_equal 27, readme.size
214 assert_equal 27, readme.size
215 assert_equal '1', readme.lastrev.revision
215 assert_equal '1', readme.lastrev.revision
216 assert_equal '9d5b5b004199', readme.lastrev.identifier
216 assert_equal '9d5b5b00419901478496242e0768deba1ce8c51e', readme.lastrev.identifier
217 # 2007-12-14 10:24:01 +0100
217 # 2007-12-14 10:24:01 +0100
218 assert_equal Time.gm(2007, 12, 14, 9, 24, 1), readme.lastrev.time
218 assert_equal Time.gm(2007, 12, 14, 9, 24, 1), readme.lastrev.time
219
219
220 entries2 = @adapter.entries('sources', r)
220 entries2 = @adapter.entries('sources', r)
221 assert entries2
221 assert entries2
222 assert_equal 2, entries2.size
222 assert_equal 2, entries2.size
223 assert_equal 'watchers_controller.rb', entries2[0].name
223 assert_equal 'watchers_controller.rb', entries2[0].name
224 assert_equal 'sources/watchers_controller.rb', entries2[0].path
224 assert_equal 'sources/watchers_controller.rb', entries2[0].path
225 assert_equal 'file', entries2[0].kind
225 assert_equal 'file', entries2[0].kind
226 assert_equal 'welcome_controller.rb', entries2[1].name
226 assert_equal 'welcome_controller.rb', entries2[1].name
227 assert_equal 'sources/welcome_controller.rb', entries2[1].path
227 assert_equal 'sources/welcome_controller.rb', entries2[1].path
228 assert_equal 'file', entries2[1].kind
228 assert_equal 'file', entries2[1].kind
229 end
229 end
230 end
230 end
231
231
232 def test_entries_tag
232 def test_entries_tag
233 entries1 = @adapter.entries(nil, 'tag_test.00')
233 entries1 = @adapter.entries(nil, 'tag_test.00')
234 assert entries1
234 assert entries1
235 assert_equal 3, entries1.size
235 assert_equal 3, entries1.size
236 assert_equal 'sources', entries1[1].name
236 assert_equal 'sources', entries1[1].name
237 assert_equal 'sources', entries1[1].path
237 assert_equal 'sources', entries1[1].path
238 assert_equal 'dir', entries1[1].kind
238 assert_equal 'dir', entries1[1].kind
239 readme = entries1[2]
239 readme = entries1[2]
240 assert_equal 'README', readme.name
240 assert_equal 'README', readme.name
241 assert_equal 'README', readme.path
241 assert_equal 'README', readme.path
242 assert_equal 'file', readme.kind
242 assert_equal 'file', readme.kind
243 assert_equal 21, readme.size
243 assert_equal 21, readme.size
244 assert_equal '0', readme.lastrev.revision
244 assert_equal '0', readme.lastrev.revision
245 assert_equal '0885933ad4f6', readme.lastrev.identifier
245 assert_equal '0885933ad4f68d77c2649cd11f8311276e7ef7ce', readme.lastrev.identifier
246 # 2007-12-14 10:22:52 +0100
246 # 2007-12-14 10:22:52 +0100
247 assert_equal Time.gm(2007, 12, 14, 9, 22, 52), readme.lastrev.time
247 assert_equal Time.gm(2007, 12, 14, 9, 22, 52), readme.lastrev.time
248 end
248 end
249
249
250 def test_entries_branch
250 def test_entries_branch
251 entries1 = @adapter.entries(nil, 'test-branch-00')
251 entries1 = @adapter.entries(nil, 'test-branch-00')
252 assert entries1
252 assert entries1
253 assert_equal 5, entries1.size
253 assert_equal 5, entries1.size
254 assert_equal 'sql_escape', entries1[2].name
254 assert_equal 'sql_escape', entries1[2].name
255 assert_equal 'sql_escape', entries1[2].path
255 assert_equal 'sql_escape', entries1[2].path
256 assert_equal 'dir', entries1[2].kind
256 assert_equal 'dir', entries1[2].kind
257 readme = entries1[4]
257 readme = entries1[4]
258 assert_equal 'README', readme.name
258 assert_equal 'README', readme.name
259 assert_equal 'README', readme.path
259 assert_equal 'README', readme.path
260 assert_equal 'file', readme.kind
260 assert_equal 'file', readme.kind
261 assert_equal 365, readme.size
261 assert_equal 365, readme.size
262 assert_equal '8', readme.lastrev.revision
262 assert_equal '8', readme.lastrev.revision
263 assert_equal 'c51f5bb613cd', readme.lastrev.identifier
263 assert_equal 'c51f5bb613cd60793c2a9fe9df29332e74bb949f', readme.lastrev.identifier
264 # 2001-02-01 00:00:00 -0900
264 # 2001-02-01 00:00:00 -0900
265 assert_equal Time.gm(2001, 2, 1, 9, 0, 0), readme.lastrev.time
265 assert_equal Time.gm(2001, 2, 1, 9, 0, 0), readme.lastrev.time
266 end
266 end
267
267
268 def test_entry
268 def test_entry
269 entry = @adapter.entry()
269 entry = @adapter.entry()
270 assert_equal "", entry.path
270 assert_equal "", entry.path
271 assert_equal "dir", entry.kind
271 assert_equal "dir", entry.kind
272 entry = @adapter.entry('')
272 entry = @adapter.entry('')
273 assert_equal "", entry.path
273 assert_equal "", entry.path
274 assert_equal "dir", entry.kind
274 assert_equal "dir", entry.kind
275 assert_nil @adapter.entry('invalid')
275 assert_nil @adapter.entry('invalid')
276 assert_nil @adapter.entry('/invalid')
276 assert_nil @adapter.entry('/invalid')
277 assert_nil @adapter.entry('/invalid/')
277 assert_nil @adapter.entry('/invalid/')
278 assert_nil @adapter.entry('invalid/invalid')
278 assert_nil @adapter.entry('invalid/invalid')
279 assert_nil @adapter.entry('invalid/invalid/')
279 assert_nil @adapter.entry('invalid/invalid/')
280 assert_nil @adapter.entry('/invalid/invalid')
280 assert_nil @adapter.entry('/invalid/invalid')
281 assert_nil @adapter.entry('/invalid/invalid/')
281 assert_nil @adapter.entry('/invalid/invalid/')
282 ["README", "/README"].each do |path|
282 ["README", "/README"].each do |path|
283 ["0", "0885933ad4f6", "0885933ad4f68d77c2649cd11f8311276e7ef7ce"].each do |rev|
283 ["0", "0885933ad4f6", "0885933ad4f68d77c2649cd11f8311276e7ef7ce"].each do |rev|
284 entry = @adapter.entry(path, rev)
284 entry = @adapter.entry(path, rev)
285 assert_equal "README", entry.path
285 assert_equal "README", entry.path
286 assert_equal "file", entry.kind
286 assert_equal "file", entry.kind
287 assert_equal '0', entry.lastrev.revision
287 assert_equal '0', entry.lastrev.revision
288 assert_equal '0885933ad4f6', entry.lastrev.identifier
288 assert_equal '0885933ad4f68d77c2649cd11f8311276e7ef7ce', entry.lastrev.identifier
289 end
289 end
290 end
290 end
291 ["sources", "/sources", "/sources/"].each do |path|
291 ["sources", "/sources", "/sources/"].each do |path|
292 ["0", "0885933ad4f6", "0885933ad4f68d77c2649cd11f8311276e7ef7ce"].each do |rev|
292 ["0", "0885933ad4f6", "0885933ad4f68d77c2649cd11f8311276e7ef7ce"].each do |rev|
293 entry = @adapter.entry(path, rev)
293 entry = @adapter.entry(path, rev)
294 assert_equal "sources", entry.path
294 assert_equal "sources", entry.path
295 assert_equal "dir", entry.kind
295 assert_equal "dir", entry.kind
296 end
296 end
297 end
297 end
298 ["sources/watchers_controller.rb", "/sources/watchers_controller.rb"].each do |path|
298 ["sources/watchers_controller.rb", "/sources/watchers_controller.rb"].each do |path|
299 ["0", "0885933ad4f6", "0885933ad4f68d77c2649cd11f8311276e7ef7ce"].each do |rev|
299 ["0", "0885933ad4f6", "0885933ad4f68d77c2649cd11f8311276e7ef7ce"].each do |rev|
300 entry = @adapter.entry(path, rev)
300 entry = @adapter.entry(path, rev)
301 assert_equal "sources/watchers_controller.rb", entry.path
301 assert_equal "sources/watchers_controller.rb", entry.path
302 assert_equal "file", entry.kind
302 assert_equal "file", entry.kind
303 assert_equal '0', entry.lastrev.revision
303 assert_equal '0', entry.lastrev.revision
304 assert_equal '0885933ad4f6', entry.lastrev.identifier
304 assert_equal '0885933ad4f68d77c2649cd11f8311276e7ef7ce', entry.lastrev.identifier
305 end
305 end
306 end
306 end
307 end
307 end
308
308
309 def test_locate_on_outdated_repository
309 def test_locate_on_outdated_repository
310 assert_equal 1, @adapter.entries("images", 0).size
310 assert_equal 1, @adapter.entries("images", 0).size
311 assert_equal 2, @adapter.entries("images").size
311 assert_equal 2, @adapter.entries("images").size
312 assert_equal 2, @adapter.entries("images", 2).size
312 assert_equal 2, @adapter.entries("images", 2).size
313 end
313 end
314
314
315 def test_access_by_nodeid
315 def test_access_by_nodeid
316 path = 'sources/welcome_controller.rb'
316 path = 'sources/welcome_controller.rb'
317 assert_equal @adapter.cat(path, 2), @adapter.cat(path, '400bb8672109')
317 assert_equal @adapter.cat(path, 2), @adapter.cat(path, '400bb8672109')
318 end
318 end
319
319
320 def test_access_by_fuzzy_nodeid
320 def test_access_by_fuzzy_nodeid
321 path = 'sources/welcome_controller.rb'
321 path = 'sources/welcome_controller.rb'
322 # falls back to nodeid
322 # falls back to nodeid
323 assert_equal @adapter.cat(path, 2), @adapter.cat(path, '400')
323 assert_equal @adapter.cat(path, 2), @adapter.cat(path, '400')
324 end
324 end
325
325
326 def test_tags
326 def test_tags
327 assert_equal [@tag_char_1, 'tag_test.00', 'tag-init-revision'], @adapter.tags
327 assert_equal [@tag_char_1, 'tag_test.00', 'tag-init-revision'], @adapter.tags
328 end
328 end
329
329
330 def test_tagmap
330 def test_tagmap
331 tm = {
331 tm = {
332 @tag_char_1 => 'adf805632193',
332 @tag_char_1 => 'adf805632193500ad3b615cd04f58f9b0769f576',
333 'tag_test.00' => '6987191f453a',
333 'tag_test.00' => '6987191f453a5f6557018d522feea2c450d5588d',
334 'tag-init-revision' => '0885933ad4f6',
334 'tag-init-revision' => '0885933ad4f68d77c2649cd11f8311276e7ef7ce',
335 }
335 }
336 assert_equal tm, @adapter.tagmap
336 assert_equal tm, @adapter.tagmap
337 end
337 end
338
338
339 def test_branches
339 def test_branches
340 brs = []
340 brs = []
341 @adapter.branches.each do |b|
341 @adapter.branches.each do |b|
342 brs << b
342 brs << b
343 end
343 end
344 assert_equal 7, brs.length
344 assert_equal 7, brs.length
345 assert_equal 'default', brs[0].to_s
345 assert_equal 'default', brs[0].to_s
346 assert_equal '31', brs[0].revision
346 assert_equal '31', brs[0].revision
347 assert_equal '31eeee7395c8', brs[0].scmid
347 assert_equal '31eeee7395c8c78e66dd54c50addd078d10b2355', brs[0].scmid
348 assert_equal 'test-branch-01', brs[1].to_s
348 assert_equal 'test-branch-01', brs[1].to_s
349 assert_equal '30', brs[1].revision
349 assert_equal '30', brs[1].revision
350 assert_equal 'ad4dc4f80284', brs[1].scmid
350 assert_equal 'ad4dc4f80284a4f9168b77e0b6de288e5d207ee7', brs[1].scmid
351 assert_equal @branch_char_1, brs[2].to_s
351 assert_equal @branch_char_1, brs[2].to_s
352 assert_equal '27', brs[2].revision
352 assert_equal '27', brs[2].revision
353 assert_equal '7bbf4c738e71', brs[2].scmid
353 assert_equal '7bbf4c738e7145149d2e5eb1eed1d3a8ddd3b914', brs[2].scmid
354 assert_equal 'branch (1)[2]&,%.-3_4', brs[3].to_s
354 assert_equal 'branch (1)[2]&,%.-3_4', brs[3].to_s
355 assert_equal '25', brs[3].revision
355 assert_equal '25', brs[3].revision
356 assert_equal 'afc61e85bde7', brs[3].scmid
356 assert_equal 'afc61e85bde74de930e5846c8451bd55b5bafc9c', brs[3].scmid
357 assert_equal @branch_char_0, brs[4].to_s
357 assert_equal @branch_char_0, brs[4].to_s
358 assert_equal '23', brs[4].revision
358 assert_equal '23', brs[4].revision
359 assert_equal 'c8d3e4887474', brs[4].scmid
359 assert_equal 'c8d3e4887474af6a589190140508037ebaa9d9c3', brs[4].scmid
360 assert_equal 'test_branch.latin-1', brs[5].to_s
360 assert_equal 'test_branch.latin-1', brs[5].to_s
361 assert_equal '22', brs[5].revision
361 assert_equal '22', brs[5].revision
362 assert_equal 'c2ffe7da686a', brs[5].scmid
362 assert_equal 'c2ffe7da686aa3d956e59f2a2854cf8980a8b768', brs[5].scmid
363 assert_equal 'test-branch-00', brs[6].to_s
363 assert_equal 'test-branch-00', brs[6].to_s
364 assert_equal '13', brs[6].revision
364 assert_equal '13', brs[6].revision
365 assert_equal '3a330eb32958', brs[6].scmid
365 assert_equal '3a330eb329586ea2adb3f83237c23310e744ebe9', brs[6].scmid
366 end
366 end
367
367
368 def test_branchmap
368 def test_branchmap
369 bm = {
369 bm = {
370 'default' => '31eeee7395c8',
370 'default' => '31eeee7395c8c78e66dd54c50addd078d10b2355',
371 'test_branch.latin-1' => 'c2ffe7da686a',
371 'test_branch.latin-1' => 'c2ffe7da686aa3d956e59f2a2854cf8980a8b768',
372 'branch (1)[2]&,%.-3_4' => 'afc61e85bde7',
372 'branch (1)[2]&,%.-3_4' => 'afc61e85bde74de930e5846c8451bd55b5bafc9c',
373 'test-branch-00' => '3a330eb32958',
373 'test-branch-00' => '3a330eb329586ea2adb3f83237c23310e744ebe9',
374 "test-branch-01" => 'ad4dc4f80284',
374 "test-branch-01" => 'ad4dc4f80284a4f9168b77e0b6de288e5d207ee7',
375 @branch_char_0 => 'c8d3e4887474',
375 @branch_char_0 => 'c8d3e4887474af6a589190140508037ebaa9d9c3',
376 @branch_char_1 => '7bbf4c738e71',
376 @branch_char_1 => '7bbf4c738e7145149d2e5eb1eed1d3a8ddd3b914',
377 }
377 }
378 assert_equal bm, @adapter.branchmap
378 assert_equal bm, @adapter.branchmap
379 end
379 end
380
380
381 def test_path_space
381 def test_path_space
382 p = 'README (1)[2]&,%.-3_4'
382 p = 'README (1)[2]&,%.-3_4'
383 [15, '933ca60293d7'].each do |r1|
383 [15, '933ca60293d7'].each do |r1|
384 assert @adapter.diff(p, r1)
384 assert @adapter.diff(p, r1)
385 assert @adapter.cat(p, r1)
385 assert @adapter.cat(p, r1)
386 assert_equal 1, @adapter.annotate(p, r1).lines.length
386 assert_equal 1, @adapter.annotate(p, r1).lines.length
387 [25, 'afc61e85bde7'].each do |r2|
387 [25, 'afc61e85bde7'].each do |r2|
388 assert @adapter.diff(p, r1, r2)
388 assert @adapter.diff(p, r1, r2)
389 end
389 end
390 end
390 end
391 end
391 end
392
392
393 def test_tag_non_ascii
393 def test_tag_non_ascii
394 p = "latin-1-dir/test-#{@char_1}-1.txt"
394 p = "latin-1-dir/test-#{@char_1}-1.txt"
395 assert @adapter.cat(p, @tag_char_1)
395 assert @adapter.cat(p, @tag_char_1)
396 assert_equal 1, @adapter.annotate(p, @tag_char_1).lines.length
396 assert_equal 1, @adapter.annotate(p, @tag_char_1).lines.length
397 end
397 end
398
398
399 def test_branch_non_ascii
399 def test_branch_non_ascii
400 p = "latin-1-dir/test-#{@char_1}-subdir/test-#{@char_1}-1.txt"
400 p = "latin-1-dir/test-#{@char_1}-subdir/test-#{@char_1}-1.txt"
401 assert @adapter.cat(p, @branch_char_1)
401 assert @adapter.cat(p, @branch_char_1)
402 assert_equal 1, @adapter.annotate(p, @branch_char_1).lines.length
402 assert_equal 1, @adapter.annotate(p, @branch_char_1).lines.length
403 end
403 end
404
404
405 def test_nodes_in_branch
405 def test_nodes_in_branch
406 [
406 [
407 'default',
407 'default',
408 @branch_char_1,
408 @branch_char_1,
409 'branch (1)[2]&,%.-3_4',
409 'branch (1)[2]&,%.-3_4',
410 @branch_char_0,
410 @branch_char_0,
411 'test_branch.latin-1',
411 'test_branch.latin-1',
412 'test-branch-00',
412 'test-branch-00',
413 ].each do |bra|
413 ].each do |bra|
414 nib0 = @adapter.nodes_in_branch(bra)
414 nib0 = @adapter.nodes_in_branch(bra)
415 assert nib0
415 assert nib0
416 nib1 = @adapter.nodes_in_branch(bra, :limit => 1)
416 nib1 = @adapter.nodes_in_branch(bra, :limit => 1)
417 assert_equal 1, nib1.size
417 assert_equal 1, nib1.size
418 case bra
418 case bra
419 when 'branch (1)[2]&,%.-3_4'
419 when 'branch (1)[2]&,%.-3_4'
420 if @adapter.class.client_version_above?([1, 6])
420 if @adapter.class.client_version_above?([1, 6])
421 assert_equal 3, nib0.size
421 assert_equal 3, nib0.size
422 assert_equal nib0[0], 'afc61e85bde7'
422 assert_equal 'afc61e85bde74de930e5846c8451bd55b5bafc9c', nib0[0]
423 nib2 = @adapter.nodes_in_branch(bra, :limit => 2)
423 nib2 = @adapter.nodes_in_branch(bra, :limit => 2)
424 assert_equal 2, nib2.size
424 assert_equal 2, nib2.size
425 assert_equal nib2[1], '933ca60293d7'
425 assert_equal '933ca60293d78f7c7979dd123cc0c02431683575', nib2[1]
426 end
426 end
427 when @branch_char_1
427 when @branch_char_1
428 if @adapter.class.client_version_above?([1, 6])
428 if @adapter.class.client_version_above?([1, 6])
429 assert_equal 2, nib0.size
429 assert_equal 2, nib0.size
430 assert_equal nib0[1], '08ff3227303e'
430 assert_equal '08ff3227303ec0dfcc818efa8e9cc652fe81859f', nib0[1]
431 nib2 = @adapter.nodes_in_branch(bra, :limit => 1)
431 nib2 = @adapter.nodes_in_branch(bra, :limit => 1)
432 assert_equal 1, nib2.size
432 assert_equal 1, nib2.size
433 assert_equal nib2[0], '7bbf4c738e71'
433 assert_equal '7bbf4c738e7145149d2e5eb1eed1d3a8ddd3b914', nib2[0]
434 end
434 end
435 end
435 end
436 end
436 end
437 end
437 end
438
438
439 def test_path_encoding_default_utf8
439 def test_path_encoding_default_utf8
440 adpt1 = Redmine::Scm::Adapters::MercurialAdapter.new(
440 adpt1 = Redmine::Scm::Adapters::MercurialAdapter.new(
441 REPOSITORY_PATH
441 REPOSITORY_PATH
442 )
442 )
443 assert_equal "UTF-8", adpt1.path_encoding
443 assert_equal "UTF-8", adpt1.path_encoding
444 adpt2 = Redmine::Scm::Adapters::MercurialAdapter.new(
444 adpt2 = Redmine::Scm::Adapters::MercurialAdapter.new(
445 REPOSITORY_PATH,
445 REPOSITORY_PATH,
446 nil,
446 nil,
447 nil,
447 nil,
448 nil,
448 nil,
449 ""
449 ""
450 )
450 )
451 assert_equal "UTF-8", adpt2.path_encoding
451 assert_equal "UTF-8", adpt2.path_encoding
452 end
452 end
453
453
454 private
454 private
455
455
456 def test_hgversion_for(hgversion, version)
456 def test_hgversion_for(hgversion, version)
457 @adapter.class.expects(:hgversion_from_command_line).returns(hgversion)
457 @adapter.class.expects(:hgversion_from_command_line).returns(hgversion)
458 assert_equal version, @adapter.class.hgversion
458 assert_equal version, @adapter.class.hgversion
459 end
459 end
460
460
461 def test_template_path_for(version, template)
461 def test_template_path_for(version, template)
462 assert_equal "#{HELPERS_DIR}/#{TEMPLATE_NAME}-#{template}.#{TEMPLATE_EXTENSION}",
462 assert_equal "#{HELPERS_DIR}/#{TEMPLATE_NAME}-#{template}.#{TEMPLATE_EXTENSION}",
463 @adapter.class.template_path_for(version)
463 @adapter.class.template_path_for(version)
464 assert File.exist?(@adapter.class.template_path_for(version))
464 assert File.exist?(@adapter.class.template_path_for(version))
465 end
465 end
466 else
466 else
467 puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!"
467 puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!"
468 def test_fake; assert true end
468 def test_fake; assert true end
469 end
469 end
470 end
470 end
471 rescue LoadError
471 rescue LoadError
472 class MercurialMochaFake < ActiveSupport::TestCase
472 class MercurialMochaFake < ActiveSupport::TestCase
473 def test_fake; assert(false, "Requires mocha to run those tests") end
473 def test_fake; assert(false, "Requires mocha to run those tests") end
474 end
474 end
475 end
475 end
General Comments 0
You need to be logged in to leave comments. Login now