##// END OF EJS Templates
test-svn-subrepo: fix reference output for svn 1.7...
Patrick Mezard -
r15607:fab28a57 stable
parent child Browse files
Show More
@@ -1,424 +1,424 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # check-code - a style and portability checker for Mercurial
3 # check-code - a style and portability checker for Mercurial
4 #
4 #
5 # Copyright 2010 Matt Mackall <mpm@selenic.com>
5 # Copyright 2010 Matt Mackall <mpm@selenic.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
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.
8 # GNU General Public License version 2 or any later version.
9
9
10 import re, glob, os, sys
10 import re, glob, os, sys
11 import keyword
11 import keyword
12 import optparse
12 import optparse
13
13
14 def repquote(m):
14 def repquote(m):
15 t = re.sub(r"\w", "x", m.group('text'))
15 t = re.sub(r"\w", "x", m.group('text'))
16 t = re.sub(r"[^\s\nx]", "o", t)
16 t = re.sub(r"[^\s\nx]", "o", t)
17 return m.group('quote') + t + m.group('quote')
17 return m.group('quote') + t + m.group('quote')
18
18
19 def reppython(m):
19 def reppython(m):
20 comment = m.group('comment')
20 comment = m.group('comment')
21 if comment:
21 if comment:
22 return "#" * len(comment)
22 return "#" * len(comment)
23 return repquote(m)
23 return repquote(m)
24
24
25 def repcomment(m):
25 def repcomment(m):
26 return m.group(1) + "#" * len(m.group(2))
26 return m.group(1) + "#" * len(m.group(2))
27
27
28 def repccomment(m):
28 def repccomment(m):
29 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
29 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
30 return m.group(1) + t + "*/"
30 return m.group(1) + t + "*/"
31
31
32 def repcallspaces(m):
32 def repcallspaces(m):
33 t = re.sub(r"\n\s+", "\n", m.group(2))
33 t = re.sub(r"\n\s+", "\n", m.group(2))
34 return m.group(1) + t
34 return m.group(1) + t
35
35
36 def repinclude(m):
36 def repinclude(m):
37 return m.group(1) + "<foo>"
37 return m.group(1) + "<foo>"
38
38
39 def rephere(m):
39 def rephere(m):
40 t = re.sub(r"\S", "x", m.group(2))
40 t = re.sub(r"\S", "x", m.group(2))
41 return m.group(1) + t
41 return m.group(1) + t
42
42
43
43
44 testpats = [
44 testpats = [
45 [
45 [
46 (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"),
46 (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"),
47 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"),
47 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"),
48 (r'^function', "don't use 'function', use old style"),
48 (r'^function', "don't use 'function', use old style"),
49 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
49 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
50 (r'echo.*\\n', "don't use 'echo \\n', use printf"),
50 (r'echo.*\\n', "don't use 'echo \\n', use printf"),
51 (r'echo -n', "don't use 'echo -n', use printf"),
51 (r'echo -n', "don't use 'echo -n', use printf"),
52 (r'^diff.*-\w*N', "don't use 'diff -N'"),
52 (r'^diff.*-\w*N', "don't use 'diff -N'"),
53 (r'(^| )wc[^|]*$\n(?!.*\(re\))', "filter wc output"),
53 (r'(^| )wc[^|]*$\n(?!.*\(re\))', "filter wc output"),
54 (r'head -c', "don't use 'head -c', use 'dd'"),
54 (r'head -c', "don't use 'head -c', use 'dd'"),
55 (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"),
55 (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"),
56 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
56 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
57 (r'printf.*\\\d\d\d', "don't use 'printf \NNN', use Python"),
57 (r'printf.*\\\d\d\d', "don't use 'printf \NNN', use Python"),
58 (r'printf.*\\x', "don't use printf \\x, use Python"),
58 (r'printf.*\\x', "don't use printf \\x, use Python"),
59 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
59 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
60 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
60 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
61 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
61 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
62 "use egrep for extended grep syntax"),
62 "use egrep for extended grep syntax"),
63 (r'/bin/', "don't use explicit paths for tools"),
63 (r'/bin/', "don't use explicit paths for tools"),
64 (r'\$PWD', "don't use $PWD, use `pwd`"),
64 (r'\$PWD', "don't use $PWD, use `pwd`"),
65 (r'[^\n]\Z', "no trailing newline"),
65 (r'[^\n]\Z', "no trailing newline"),
66 (r'export.*=', "don't export and assign at once"),
66 (r'export.*=', "don't export and assign at once"),
67 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\\^', "^ must be quoted"),
67 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\\^', "^ must be quoted"),
68 (r'^source\b', "don't use 'source', use '.'"),
68 (r'^source\b', "don't use 'source', use '.'"),
69 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
69 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
70 (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"),
70 (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"),
71 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
71 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
72 (r'^stop\(\)', "don't use 'stop' as a shell function name"),
72 (r'^stop\(\)', "don't use 'stop' as a shell function name"),
73 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"),
73 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"),
74 ],
74 ],
75 # warnings
75 # warnings
76 []
76 []
77 ]
77 ]
78
78
79 testfilters = [
79 testfilters = [
80 (r"( *)(#([^\n]*\S)?)", repcomment),
80 (r"( *)(#([^\n]*\S)?)", repcomment),
81 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
81 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
82 ]
82 ]
83
83
84 uprefix = r"^ \$ "
84 uprefix = r"^ \$ "
85 uprefixc = r"^ > "
85 uprefixc = r"^ > "
86 utestpats = [
86 utestpats = [
87 [
87 [
88 (r'^(\S| $ ).*(\S[ \t]+|^[ \t]+)\n', "trailing whitespace on non-output"),
88 (r'^(\S| $ ).*(\S[ \t]+|^[ \t]+)\n', "trailing whitespace on non-output"),
89 (uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"),
89 (uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"),
90 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
90 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
91 (uprefix + r'.*\$\?', "explicit exit code checks unnecessary"),
91 (uprefix + r'.*(?<!\[)\$\?', "explicit exit code checks unnecessary"),
92 (uprefix + r'.*\|\| echo.*(fail|error)',
92 (uprefix + r'.*\|\| echo.*(fail|error)',
93 "explicit exit code checks unnecessary"),
93 "explicit exit code checks unnecessary"),
94 (uprefix + r'set -e', "don't use set -e"),
94 (uprefix + r'set -e', "don't use set -e"),
95 (uprefixc + r'( *)\t', "don't use tabs to indent"),
95 (uprefixc + r'( *)\t', "don't use tabs to indent"),
96 ],
96 ],
97 # warnings
97 # warnings
98 []
98 []
99 ]
99 ]
100
100
101 for i in [0, 1]:
101 for i in [0, 1]:
102 for p, m in testpats[i]:
102 for p, m in testpats[i]:
103 if p.startswith(r'^'):
103 if p.startswith(r'^'):
104 p = uprefix + p[1:]
104 p = uprefix + p[1:]
105 else:
105 else:
106 p = uprefix + ".*" + p
106 p = uprefix + ".*" + p
107 utestpats[i].append((p, m))
107 utestpats[i].append((p, m))
108
108
109 utestfilters = [
109 utestfilters = [
110 (r"( *)(#([^\n]*\S)?)", repcomment),
110 (r"( *)(#([^\n]*\S)?)", repcomment),
111 ]
111 ]
112
112
113 pypats = [
113 pypats = [
114 [
114 [
115 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
115 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
116 "tuple parameter unpacking not available in Python 3+"),
116 "tuple parameter unpacking not available in Python 3+"),
117 (r'lambda\s*\(.*,.*\)',
117 (r'lambda\s*\(.*,.*\)',
118 "tuple parameter unpacking not available in Python 3+"),
118 "tuple parameter unpacking not available in Python 3+"),
119 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
119 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
120 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
120 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
121 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
121 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
122 (r'^\s*\t', "don't use tabs"),
122 (r'^\s*\t', "don't use tabs"),
123 (r'\S;\s*\n', "semicolon"),
123 (r'\S;\s*\n', "semicolon"),
124 (r'\w,\w', "missing whitespace after ,"),
124 (r'\w,\w', "missing whitespace after ,"),
125 (r'\w[+/*\-<>]\w', "missing whitespace in expression"),
125 (r'\w[+/*\-<>]\w', "missing whitespace in expression"),
126 (r'^\s+\w+=\w+[^,)\n]$', "missing whitespace in assignment"),
126 (r'^\s+\w+=\w+[^,)\n]$', "missing whitespace in assignment"),
127 (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n'
127 (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n'
128 r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Py2.4'),
128 r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Py2.4'),
129 (r'.{85}', "line too long"),
129 (r'.{85}', "line too long"),
130 (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'),
130 (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'),
131 (r'[^\n]\Z', "no trailing newline"),
131 (r'[^\n]\Z', "no trailing newline"),
132 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
132 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
133 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=', "don't use underbars in identifiers"),
133 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=', "don't use underbars in identifiers"),
134 # (r'\w*[a-z][A-Z]\w*\s*=', "don't use camelcase in identifiers"),
134 # (r'\w*[a-z][A-Z]\w*\s*=', "don't use camelcase in identifiers"),
135 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+',
135 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+',
136 "linebreak after :"),
136 "linebreak after :"),
137 (r'class\s[^( \n]+:', "old-style class, use class foo(object)"),
137 (r'class\s[^( \n]+:', "old-style class, use class foo(object)"),
138 (r'class\s[^( \n]+\(\):',
138 (r'class\s[^( \n]+\(\):',
139 "class foo() not available in Python 2.4, use class foo(object)"),
139 "class foo() not available in Python 2.4, use class foo(object)"),
140 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
140 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
141 "Python keyword is not a function"),
141 "Python keyword is not a function"),
142 (r',]', "unneeded trailing ',' in list"),
142 (r',]', "unneeded trailing ',' in list"),
143 # (r'class\s[A-Z][^\(]*\((?!Exception)',
143 # (r'class\s[A-Z][^\(]*\((?!Exception)',
144 # "don't capitalize non-exception classes"),
144 # "don't capitalize non-exception classes"),
145 # (r'in range\(', "use xrange"),
145 # (r'in range\(', "use xrange"),
146 # (r'^\s*print\s+', "avoid using print in core and extensions"),
146 # (r'^\s*print\s+', "avoid using print in core and extensions"),
147 (r'[\x80-\xff]', "non-ASCII character literal"),
147 (r'[\x80-\xff]', "non-ASCII character literal"),
148 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
148 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
149 (r'^\s*with\s+', "with not available in Python 2.4"),
149 (r'^\s*with\s+', "with not available in Python 2.4"),
150 (r'\.isdisjoint\(', "set.isdisjoint not available in Python 2.4"),
150 (r'\.isdisjoint\(', "set.isdisjoint not available in Python 2.4"),
151 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
151 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
152 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
152 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
153 (r'(?<!def)\s+(any|all|format)\(',
153 (r'(?<!def)\s+(any|all|format)\(',
154 "any/all/format not available in Python 2.4"),
154 "any/all/format not available in Python 2.4"),
155 (r'(?<!def)\s+(callable)\(',
155 (r'(?<!def)\s+(callable)\(',
156 "callable not available in Python 3, use getattr(f, '__call__', None)"),
156 "callable not available in Python 3, use getattr(f, '__call__', None)"),
157 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
157 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
158 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
158 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
159 "gratuitous whitespace after Python keyword"),
159 "gratuitous whitespace after Python keyword"),
160 (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"),
160 (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"),
161 # (r'\s\s=', "gratuitous whitespace before ="),
161 # (r'\s\s=', "gratuitous whitespace before ="),
162 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
162 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
163 "missing whitespace around operator"),
163 "missing whitespace around operator"),
164 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\s',
164 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\s',
165 "missing whitespace around operator"),
165 "missing whitespace around operator"),
166 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
166 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
167 "missing whitespace around operator"),
167 "missing whitespace around operator"),
168 (r'[^+=*/!<>&| -](\s=|=\s)[^= ]',
168 (r'[^+=*/!<>&| -](\s=|=\s)[^= ]',
169 "wrong whitespace around ="),
169 "wrong whitespace around ="),
170 (r'raise Exception', "don't raise generic exceptions"),
170 (r'raise Exception', "don't raise generic exceptions"),
171 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
171 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
172 (r' [=!]=\s+(True|False|None)',
172 (r' [=!]=\s+(True|False|None)',
173 "comparison with singleton, use 'is' or 'is not' instead"),
173 "comparison with singleton, use 'is' or 'is not' instead"),
174 (r'^\s*(while|if) [01]:',
174 (r'^\s*(while|if) [01]:',
175 "use True/False for constant Boolean expression"),
175 "use True/False for constant Boolean expression"),
176 (r'(?<!def)\s+hasattr',
176 (r'(?<!def)\s+hasattr',
177 'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
177 'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
178 (r'opener\([^)]*\).read\(',
178 (r'opener\([^)]*\).read\(',
179 "use opener.read() instead"),
179 "use opener.read() instead"),
180 (r'BaseException', 'not in Py2.4, use Exception'),
180 (r'BaseException', 'not in Py2.4, use Exception'),
181 (r'os\.path\.relpath', 'os.path.relpath is not in Py2.5'),
181 (r'os\.path\.relpath', 'os.path.relpath is not in Py2.5'),
182 (r'opener\([^)]*\).write\(',
182 (r'opener\([^)]*\).write\(',
183 "use opener.write() instead"),
183 "use opener.write() instead"),
184 (r'[\s\(](open|file)\([^)]*\)\.read\(',
184 (r'[\s\(](open|file)\([^)]*\)\.read\(',
185 "use util.readfile() instead"),
185 "use util.readfile() instead"),
186 (r'[\s\(](open|file)\([^)]*\)\.write\(',
186 (r'[\s\(](open|file)\([^)]*\)\.write\(',
187 "use util.readfile() instead"),
187 "use util.readfile() instead"),
188 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
188 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
189 "always assign an opened file to a variable, and close it afterwards"),
189 "always assign an opened file to a variable, and close it afterwards"),
190 (r'[\s\(](open|file)\([^)]*\)\.',
190 (r'[\s\(](open|file)\([^)]*\)\.',
191 "always assign an opened file to a variable, and close it afterwards"),
191 "always assign an opened file to a variable, and close it afterwards"),
192 (r'(?i)descendent', "the proper spelling is descendAnt"),
192 (r'(?i)descendent', "the proper spelling is descendAnt"),
193 (r'\.debug\(\_', "don't mark debug messages for translation"),
193 (r'\.debug\(\_', "don't mark debug messages for translation"),
194 ],
194 ],
195 # warnings
195 # warnings
196 [
196 [
197 (r'.{81}', "warning: line over 80 characters"),
197 (r'.{81}', "warning: line over 80 characters"),
198 (r'^\s*except:$', "warning: naked except clause"),
198 (r'^\s*except:$', "warning: naked except clause"),
199 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
199 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
200 "warning: unwrapped ui message"),
200 "warning: unwrapped ui message"),
201 ]
201 ]
202 ]
202 ]
203
203
204 pyfilters = [
204 pyfilters = [
205 (r"""(?msx)(?P<comment>\#.*?$)|
205 (r"""(?msx)(?P<comment>\#.*?$)|
206 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
206 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
207 (?P<text>(([^\\]|\\.)*?))
207 (?P<text>(([^\\]|\\.)*?))
208 (?P=quote))""", reppython),
208 (?P=quote))""", reppython),
209 ]
209 ]
210
210
211 cpats = [
211 cpats = [
212 [
212 [
213 (r'//', "don't use //-style comments"),
213 (r'//', "don't use //-style comments"),
214 (r'^ ', "don't use spaces to indent"),
214 (r'^ ', "don't use spaces to indent"),
215 (r'\S\t', "don't use tabs except for indent"),
215 (r'\S\t', "don't use tabs except for indent"),
216 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
216 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
217 (r'.{85}', "line too long"),
217 (r'.{85}', "line too long"),
218 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
218 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
219 (r'return\(', "return is not a function"),
219 (r'return\(', "return is not a function"),
220 (r' ;', "no space before ;"),
220 (r' ;', "no space before ;"),
221 (r'\w+\* \w+', "use int *foo, not int* foo"),
221 (r'\w+\* \w+', "use int *foo, not int* foo"),
222 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
222 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
223 (r'\S+ (\+\+|--)', "use foo++, not foo ++"),
223 (r'\S+ (\+\+|--)', "use foo++, not foo ++"),
224 (r'\w,\w', "missing whitespace after ,"),
224 (r'\w,\w', "missing whitespace after ,"),
225 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
225 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
226 (r'^#\s+\w', "use #foo, not # foo"),
226 (r'^#\s+\w', "use #foo, not # foo"),
227 (r'[^\n]\Z', "no trailing newline"),
227 (r'[^\n]\Z', "no trailing newline"),
228 (r'^\s*#import\b', "use only #include in standard C code"),
228 (r'^\s*#import\b', "use only #include in standard C code"),
229 ],
229 ],
230 # warnings
230 # warnings
231 []
231 []
232 ]
232 ]
233
233
234 cfilters = [
234 cfilters = [
235 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
235 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
236 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
236 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
237 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
237 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
238 (r'(\()([^)]+\))', repcallspaces),
238 (r'(\()([^)]+\))', repcallspaces),
239 ]
239 ]
240
240
241 inutilpats = [
241 inutilpats = [
242 [
242 [
243 (r'\bui\.', "don't use ui in util"),
243 (r'\bui\.', "don't use ui in util"),
244 ],
244 ],
245 # warnings
245 # warnings
246 []
246 []
247 ]
247 ]
248
248
249 inrevlogpats = [
249 inrevlogpats = [
250 [
250 [
251 (r'\brepo\.', "don't use repo in revlog"),
251 (r'\brepo\.', "don't use repo in revlog"),
252 ],
252 ],
253 # warnings
253 # warnings
254 []
254 []
255 ]
255 ]
256
256
257 checks = [
257 checks = [
258 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
258 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
259 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
259 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
260 ('c', r'.*\.c$', cfilters, cpats),
260 ('c', r'.*\.c$', cfilters, cpats),
261 ('unified test', r'.*\.t$', utestfilters, utestpats),
261 ('unified test', r'.*\.t$', utestfilters, utestpats),
262 ('layering violation repo in revlog', r'mercurial/revlog\.py', pyfilters,
262 ('layering violation repo in revlog', r'mercurial/revlog\.py', pyfilters,
263 inrevlogpats),
263 inrevlogpats),
264 ('layering violation ui in util', r'mercurial/util\.py', pyfilters,
264 ('layering violation ui in util', r'mercurial/util\.py', pyfilters,
265 inutilpats),
265 inutilpats),
266 ]
266 ]
267
267
268 class norepeatlogger(object):
268 class norepeatlogger(object):
269 def __init__(self):
269 def __init__(self):
270 self._lastseen = None
270 self._lastseen = None
271
271
272 def log(self, fname, lineno, line, msg, blame):
272 def log(self, fname, lineno, line, msg, blame):
273 """print error related a to given line of a given file.
273 """print error related a to given line of a given file.
274
274
275 The faulty line will also be printed but only once in the case
275 The faulty line will also be printed but only once in the case
276 of multiple errors.
276 of multiple errors.
277
277
278 :fname: filename
278 :fname: filename
279 :lineno: line number
279 :lineno: line number
280 :line: actual content of the line
280 :line: actual content of the line
281 :msg: error message
281 :msg: error message
282 """
282 """
283 msgid = fname, lineno, line
283 msgid = fname, lineno, line
284 if msgid != self._lastseen:
284 if msgid != self._lastseen:
285 if blame:
285 if blame:
286 print "%s:%d (%s):" % (fname, lineno, blame)
286 print "%s:%d (%s):" % (fname, lineno, blame)
287 else:
287 else:
288 print "%s:%d:" % (fname, lineno)
288 print "%s:%d:" % (fname, lineno)
289 print " > %s" % line
289 print " > %s" % line
290 self._lastseen = msgid
290 self._lastseen = msgid
291 print " " + msg
291 print " " + msg
292
292
293 _defaultlogger = norepeatlogger()
293 _defaultlogger = norepeatlogger()
294
294
295 def getblame(f):
295 def getblame(f):
296 lines = []
296 lines = []
297 for l in os.popen('hg annotate -un %s' % f):
297 for l in os.popen('hg annotate -un %s' % f):
298 start, line = l.split(':', 1)
298 start, line = l.split(':', 1)
299 user, rev = start.split()
299 user, rev = start.split()
300 lines.append((line[1:-1], user, rev))
300 lines.append((line[1:-1], user, rev))
301 return lines
301 return lines
302
302
303 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
303 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
304 blame=False, debug=False):
304 blame=False, debug=False):
305 """checks style and portability of a given file
305 """checks style and portability of a given file
306
306
307 :f: filepath
307 :f: filepath
308 :logfunc: function used to report error
308 :logfunc: function used to report error
309 logfunc(filename, linenumber, linecontent, errormessage)
309 logfunc(filename, linenumber, linecontent, errormessage)
310 :maxerr: number of error to display before arborting.
310 :maxerr: number of error to display before arborting.
311 Set to None (default) to report all errors
311 Set to None (default) to report all errors
312
312
313 return True if no error is found, False otherwise.
313 return True if no error is found, False otherwise.
314 """
314 """
315 blamecache = None
315 blamecache = None
316 result = True
316 result = True
317 for name, match, filters, pats in checks:
317 for name, match, filters, pats in checks:
318 if debug:
318 if debug:
319 print name, f
319 print name, f
320 fc = 0
320 fc = 0
321 if not re.match(match, f):
321 if not re.match(match, f):
322 if debug:
322 if debug:
323 print "Skipping %s for %s it doesn't match %s" % (
323 print "Skipping %s for %s it doesn't match %s" % (
324 name, match, f)
324 name, match, f)
325 continue
325 continue
326 fp = open(f)
326 fp = open(f)
327 pre = post = fp.read()
327 pre = post = fp.read()
328 fp.close()
328 fp.close()
329 if "no-" + "check-code" in pre:
329 if "no-" + "check-code" in pre:
330 if debug:
330 if debug:
331 print "Skipping %s for %s it has no- and check-code" % (
331 print "Skipping %s for %s it has no- and check-code" % (
332 name, f)
332 name, f)
333 break
333 break
334 for p, r in filters:
334 for p, r in filters:
335 post = re.sub(p, r, post)
335 post = re.sub(p, r, post)
336 if warnings:
336 if warnings:
337 pats = pats[0] + pats[1]
337 pats = pats[0] + pats[1]
338 else:
338 else:
339 pats = pats[0]
339 pats = pats[0]
340 # print post # uncomment to show filtered version
340 # print post # uncomment to show filtered version
341
341
342 if debug:
342 if debug:
343 print "Checking %s for %s" % (name, f)
343 print "Checking %s for %s" % (name, f)
344
344
345 prelines = None
345 prelines = None
346 errors = []
346 errors = []
347 for p, msg in pats:
347 for p, msg in pats:
348 # fix-up regexes for multiline searches
348 # fix-up regexes for multiline searches
349 po = p
349 po = p
350 # \s doesn't match \n
350 # \s doesn't match \n
351 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
351 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
352 # [^...] doesn't match newline
352 # [^...] doesn't match newline
353 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p)
353 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p)
354
354
355 #print po, '=>', p
355 #print po, '=>', p
356
356
357 pos = 0
357 pos = 0
358 n = 0
358 n = 0
359 for m in re.finditer(p, post, re.MULTILINE):
359 for m in re.finditer(p, post, re.MULTILINE):
360 if prelines is None:
360 if prelines is None:
361 prelines = pre.splitlines()
361 prelines = pre.splitlines()
362 postlines = post.splitlines(True)
362 postlines = post.splitlines(True)
363
363
364 start = m.start()
364 start = m.start()
365 while n < len(postlines):
365 while n < len(postlines):
366 step = len(postlines[n])
366 step = len(postlines[n])
367 if pos + step > start:
367 if pos + step > start:
368 break
368 break
369 pos += step
369 pos += step
370 n += 1
370 n += 1
371 l = prelines[n]
371 l = prelines[n]
372
372
373 if "check-code" + "-ignore" in l:
373 if "check-code" + "-ignore" in l:
374 if debug:
374 if debug:
375 print "Skipping %s for %s:%s (check-code -ignore)" % (
375 print "Skipping %s for %s:%s (check-code -ignore)" % (
376 name, f, n)
376 name, f, n)
377 continue
377 continue
378 bd = ""
378 bd = ""
379 if blame:
379 if blame:
380 bd = 'working directory'
380 bd = 'working directory'
381 if not blamecache:
381 if not blamecache:
382 blamecache = getblame(f)
382 blamecache = getblame(f)
383 if n < len(blamecache):
383 if n < len(blamecache):
384 bl, bu, br = blamecache[n]
384 bl, bu, br = blamecache[n]
385 if bl == l:
385 if bl == l:
386 bd = '%s@%s' % (bu, br)
386 bd = '%s@%s' % (bu, br)
387 errors.append((f, n + 1, l, msg, bd))
387 errors.append((f, n + 1, l, msg, bd))
388 result = False
388 result = False
389
389
390 errors.sort()
390 errors.sort()
391 for e in errors:
391 for e in errors:
392 logfunc(*e)
392 logfunc(*e)
393 fc += 1
393 fc += 1
394 if maxerr is not None and fc >= maxerr:
394 if maxerr is not None and fc >= maxerr:
395 print " (too many errors, giving up)"
395 print " (too many errors, giving up)"
396 break
396 break
397
397
398 return result
398 return result
399
399
400 if __name__ == "__main__":
400 if __name__ == "__main__":
401 parser = optparse.OptionParser("%prog [options] [files]")
401 parser = optparse.OptionParser("%prog [options] [files]")
402 parser.add_option("-w", "--warnings", action="store_true",
402 parser.add_option("-w", "--warnings", action="store_true",
403 help="include warning-level checks")
403 help="include warning-level checks")
404 parser.add_option("-p", "--per-file", type="int",
404 parser.add_option("-p", "--per-file", type="int",
405 help="max warnings per file")
405 help="max warnings per file")
406 parser.add_option("-b", "--blame", action="store_true",
406 parser.add_option("-b", "--blame", action="store_true",
407 help="use annotate to generate blame info")
407 help="use annotate to generate blame info")
408 parser.add_option("", "--debug", action="store_true",
408 parser.add_option("", "--debug", action="store_true",
409 help="show debug information")
409 help="show debug information")
410
410
411 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False)
411 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False)
412 (options, args) = parser.parse_args()
412 (options, args) = parser.parse_args()
413
413
414 if len(args) == 0:
414 if len(args) == 0:
415 check = glob.glob("*")
415 check = glob.glob("*")
416 else:
416 else:
417 check = args
417 check = args
418
418
419 for f in check:
419 for f in check:
420 ret = 0
420 ret = 0
421 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
421 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
422 blame=options.blame, debug=options.debug):
422 blame=options.blame, debug=options.debug):
423 ret = 1
423 ret = 1
424 sys.exit(ret)
424 sys.exit(ret)
@@ -1,585 +1,545 b''
1 $ "$TESTDIR/hghave" svn15 || exit 80
1 $ "$TESTDIR/hghave" svn15 || exit 80
2
2
3 $ fix_path()
3 $ fix_path()
4 > {
4 > {
5 > tr '\\' /
5 > tr '\\' /
6 > }
6 > }
7
7
8 SVN wants all paths to start with a slash. Unfortunately, Windows ones
8 SVN wants all paths to start with a slash. Unfortunately, Windows ones
9 don't. Handle that.
9 don't. Handle that.
10
10
11 $ escapedwd=`pwd | fix_path`
11 $ escapedwd=`pwd | fix_path`
12 $ expr "$escapedwd" : '\/' > /dev/null || escapedwd="/$escapedwd"
12 $ expr "$escapedwd" : '\/' > /dev/null || escapedwd="/$escapedwd"
13 $ escapedwd=`python -c "import urllib, sys; sys.stdout.write(urllib.quote(sys.argv[1]))" "$escapedwd"`
13 $ escapedwd=`python -c "import urllib, sys; sys.stdout.write(urllib.quote(sys.argv[1]))" "$escapedwd"`
14
14
15 create subversion repo
15 create subversion repo
16
16
17 $ SVNREPO="file://$escapedwd/svn-repo"
17 $ SVNREPO="file://$escapedwd/svn-repo"
18 $ WCROOT="`pwd`/svn-wc"
18 $ WCROOT="`pwd`/svn-wc"
19 $ svnadmin create svn-repo
19 $ svnadmin create svn-repo
20 $ svn co "$SVNREPO" svn-wc
20 $ svn co "$SVNREPO" svn-wc
21 Checked out revision 0.
21 Checked out revision 0.
22 $ cd svn-wc
22 $ cd svn-wc
23 $ mkdir src
23 $ mkdir src
24 $ echo alpha > src/alpha
24 $ echo alpha > src/alpha
25 $ svn add src
25 $ svn add src
26 A src
26 A src
27 A src/alpha
27 A src/alpha
28 $ mkdir externals
28 $ mkdir externals
29 $ echo other > externals/other
29 $ echo other > externals/other
30 $ svn add externals
30 $ svn add externals
31 A externals
31 A externals
32 A externals/other
32 A externals/other
33 $ svn ci -m 'Add alpha'
33 $ svn ci -m 'Add alpha'
34 Adding externals
34 Adding externals
35 Adding externals/other
35 Adding externals/other
36 Adding src
36 Adding src
37 Adding src/alpha
37 Adding src/alpha
38 Transmitting file data ..
38 Transmitting file data ..
39 Committed revision 1.
39 Committed revision 1.
40 $ svn up
40 $ svn up -q
41 At revision 1.
42 $ echo "externals -r1 $SVNREPO/externals" > extdef
41 $ echo "externals -r1 $SVNREPO/externals" > extdef
43 $ svn propset -F extdef svn:externals src
42 $ svn propset -F extdef svn:externals src
44 property 'svn:externals' set on 'src'
43 property 'svn:externals' set on 'src'
45 $ svn ci -m 'Setting externals'
44 $ svn ci -m 'Setting externals'
46 Sending src
45 Sending src
47
46
48 Committed revision 2.
47 Committed revision 2.
49 $ cd ..
48 $ cd ..
50
49
51 create hg repo
50 create hg repo
52
51
53 $ mkdir sub
52 $ mkdir sub
54 $ cd sub
53 $ cd sub
55 $ hg init t
54 $ hg init t
56 $ cd t
55 $ cd t
57
56
58 first revision, no sub
57 first revision, no sub
59
58
60 $ echo a > a
59 $ echo a > a
61 $ hg ci -Am0
60 $ hg ci -Am0
62 adding a
61 adding a
63
62
64 add first svn sub with leading whitespaces
63 add first svn sub with leading whitespaces
65
64
66 $ echo "s = [svn] $SVNREPO/src" >> .hgsub
65 $ echo "s = [svn] $SVNREPO/src" >> .hgsub
67 $ echo "subdir/s = [svn] $SVNREPO/src" >> .hgsub
66 $ echo "subdir/s = [svn] $SVNREPO/src" >> .hgsub
68 $ svn co --quiet "$SVNREPO"/src s
67 $ svn co --quiet "$SVNREPO"/src s
69 $ mkdir subdir
68 $ mkdir subdir
70 $ svn co --quiet "$SVNREPO"/src subdir/s
69 $ svn co --quiet "$SVNREPO"/src subdir/s
71 $ hg add .hgsub
70 $ hg add .hgsub
72 $ hg ci -m1
71 $ hg ci -m1
73 committing subrepository s
72 committing subrepository s
74 committing subrepository subdir/s
73 committing subrepository subdir/s
75
74
76 make sure we avoid empty commits (issue2445)
75 make sure we avoid empty commits (issue2445)
77
76
78 $ hg sum
77 $ hg sum
79 parent: 1:* tip (glob)
78 parent: 1:* tip (glob)
80 1
79 1
81 branch: default
80 branch: default
82 commit: (clean)
81 commit: (clean)
83 update: (current)
82 update: (current)
84 $ hg ci -moops
83 $ hg ci -moops
85 nothing changed
84 nothing changed
86 [1]
85 [1]
87
86
88 debugsub
87 debugsub
89
88
90 $ hg debugsub
89 $ hg debugsub
91 path s
90 path s
92 source file://*/svn-repo/src (glob)
91 source file://*/svn-repo/src (glob)
93 revision 2
92 revision 2
94 path subdir/s
93 path subdir/s
95 source file://*/svn-repo/src (glob)
94 source file://*/svn-repo/src (glob)
96 revision 2
95 revision 2
97
96
98 change file in svn and hg, commit
97 change file in svn and hg, commit
99
98
100 $ echo a >> a
99 $ echo a >> a
101 $ echo alpha >> s/alpha
100 $ echo alpha >> s/alpha
102 $ hg sum
101 $ hg sum
103 parent: 1:* tip (glob)
102 parent: 1:* tip (glob)
104 1
103 1
105 branch: default
104 branch: default
106 commit: 1 modified, 1 subrepos
105 commit: 1 modified, 1 subrepos
107 update: (current)
106 update: (current)
108 $ hg commit --subrepos -m 'Message!'
107 $ hg commit --subrepos -m 'Message!' | grep -v Updating
109 committing subrepository s
108 committing subrepository s
110 Sending*s/alpha (glob)
109 Sending*s/alpha (glob)
111 Transmitting file data .
110 Transmitting file data .
112 Committed revision 3.
111 Committed revision 3.
113
112
114 Fetching external item into '$TESTTMP/sub/t/s/externals'
113 Fetching external item into '*s/externals'* (glob)
115 External at revision 1.
114 External at revision 1.
116
115
117 At revision 3.
116 At revision 3.
118 $ hg debugsub
117 $ hg debugsub
119 path s
118 path s
120 source file://*/svn-repo/src (glob)
119 source file://*/svn-repo/src (glob)
121 revision 3
120 revision 3
122 path subdir/s
121 path subdir/s
123 source file://*/svn-repo/src (glob)
122 source file://*/svn-repo/src (glob)
124 revision 2
123 revision 2
125
124
126 add an unrelated revision in svn and update the subrepo to without
125 add an unrelated revision in svn and update the subrepo to without
127 bringing any changes.
126 bringing any changes.
128
127
129 $ svn mkdir "$SVNREPO/unrelated" -m 'create unrelated'
128 $ svn mkdir "$SVNREPO/unrelated" -m 'create unrelated'
130
129
131 Committed revision 4.
130 Committed revision 4.
132 $ svn up s
131 $ svn up -q s
133
134 Fetching external item into 's/externals'
135 External at revision 1.
136
137 At revision 4.
138 $ hg sum
132 $ hg sum
139 parent: 2:* tip (glob)
133 parent: 2:* tip (glob)
140 Message!
134 Message!
141 branch: default
135 branch: default
142 commit: (clean)
136 commit: (clean)
143 update: (current)
137 update: (current)
144
138
145 $ echo a > s/a
139 $ echo a > s/a
146
140
147 should be empty despite change to s/a
141 should be empty despite change to s/a
148
142
149 $ hg st
143 $ hg st
150
144
151 add a commit from svn
145 add a commit from svn
152
146
153 $ cd "$WCROOT"/src
147 $ cd "$WCROOT"/src
154 $ svn up
148 $ svn up -q
155 U alpha
156
157 Fetching external item into 'externals'
158 A externals/other
159 Updated external to revision 1.
160
161 Updated to revision 4.
162 $ echo xyz >> alpha
149 $ echo xyz >> alpha
163 $ svn propset svn:mime-type 'text/xml' alpha
150 $ svn propset svn:mime-type 'text/xml' alpha
164 property 'svn:mime-type' set on 'alpha'
151 property 'svn:mime-type' set on 'alpha'
165 $ svn ci -m 'amend a from svn'
152 $ svn ci -m 'amend a from svn'
166 Sending src/alpha
153 Sending *alpha (glob)
167 Transmitting file data .
154 Transmitting file data .
168 Committed revision 5.
155 Committed revision 5.
169 $ cd ../../sub/t
156 $ cd ../../sub/t
170
157
171 this commit from hg will fail
158 this commit from hg will fail
172
159
173 $ echo zzz >> s/alpha
160 $ echo zzz >> s/alpha
174 $ hg ci --subrepos -m 'amend alpha from hg'
161 $ (hg ci --subrepos -m 'amend alpha from hg' 2>&1; echo "[$?]") | grep -vi 'out of date'
175 committing subrepository s
162 committing subrepository s
176 abort: svn: Commit failed (details follow):
163 abort: svn:*Commit failed (details follow): (glob)
177 svn: (Out of date)?.*/src/alpha.*(is out of date)? (re)
178 [255]
164 [255]
179 $ svn revert -q s/alpha
165 $ svn revert -q s/alpha
180
166
181 this commit fails because of meta changes
167 this commit fails because of meta changes
182
168
183 $ svn propset svn:mime-type 'text/html' s/alpha
169 $ svn propset svn:mime-type 'text/html' s/alpha
184 property 'svn:mime-type' set on 's/alpha'
170 property 'svn:mime-type' set on 's/alpha'
185 $ hg ci --subrepos -m 'amend alpha from hg'
171 $ (hg ci --subrepos -m 'amend alpha from hg' 2>&1; echo "[$?]") | grep -vi 'out of date'
186 committing subrepository s
172 committing subrepository s
187 abort: svn: Commit failed (details follow):
173 abort: svn:*Commit failed (details follow): (glob)
188 svn: (Out of date)?.*/src/alpha.*(is out of date)? (re)
189 [255]
174 [255]
190 $ svn revert -q s/alpha
175 $ svn revert -q s/alpha
191
176
192 this commit fails because of externals changes
177 this commit fails because of externals changes
193
178
194 $ echo zzz > s/externals/other
179 $ echo zzz > s/externals/other
195 $ hg ci --subrepos -m 'amend externals from hg'
180 $ hg ci --subrepos -m 'amend externals from hg'
196 committing subrepository s
181 committing subrepository s
197 abort: cannot commit svn externals
182 abort: cannot commit svn externals
198 [255]
183 [255]
199 $ hg diff --subrepos -r 1:2 | grep -v diff
184 $ hg diff --subrepos -r 1:2 | grep -v diff
200 --- a/.hgsubstate Thu Jan 01 00:00:00 1970 +0000
185 --- a/.hgsubstate Thu Jan 01 00:00:00 1970 +0000
201 +++ b/.hgsubstate Thu Jan 01 00:00:00 1970 +0000
186 +++ b/.hgsubstate Thu Jan 01 00:00:00 1970 +0000
202 @@ -1,2 +1,2 @@
187 @@ -1,2 +1,2 @@
203 -2 s
188 -2 s
204 +3 s
189 +3 s
205 2 subdir/s
190 2 subdir/s
206 --- a/a Thu Jan 01 00:00:00 1970 +0000
191 --- a/a Thu Jan 01 00:00:00 1970 +0000
207 +++ b/a Thu Jan 01 00:00:00 1970 +0000
192 +++ b/a Thu Jan 01 00:00:00 1970 +0000
208 @@ -1,1 +1,2 @@
193 @@ -1,1 +1,2 @@
209 a
194 a
210 +a
195 +a
211 $ svn revert -q s/externals/other
196 $ svn revert -q s/externals/other
212
197
213 this commit fails because of externals meta changes
198 this commit fails because of externals meta changes
214
199
215 $ svn propset svn:mime-type 'text/html' s/externals/other
200 $ svn propset svn:mime-type 'text/html' s/externals/other
216 property 'svn:mime-type' set on 's/externals/other'
201 property 'svn:mime-type' set on 's/externals/other'
217 $ hg ci --subrepos -m 'amend externals from hg'
202 $ hg ci --subrepos -m 'amend externals from hg'
218 committing subrepository s
203 committing subrepository s
219 abort: cannot commit svn externals
204 abort: cannot commit svn externals
220 [255]
205 [255]
221 $ svn revert -q s/externals/other
206 $ svn revert -q s/externals/other
222
207
223 clone
208 clone
224
209
225 $ cd ..
210 $ cd ..
226 $ hg clone t tc | fix_path
211 $ hg clone t tc | fix_path
227 updating to branch default
212 updating to branch default
228 A tc/s/alpha
213 A tc/s/alpha
229 U tc/s
214 U tc/s
230
215
231 Fetching external item into 'tc/s/externals'
216 Fetching external item into 'tc/s/externals'* (glob)
232 A tc/s/externals/other
217 A tc/s/externals/other
233 Checked out external at revision 1.
218 Checked out external at revision 1.
234
219
235 Checked out revision 3.
220 Checked out revision 3.
236 A tc/subdir/s/alpha
221 A tc/subdir/s/alpha
237 U tc/subdir/s
222 U tc/subdir/s
238
223
239 Fetching external item into 'tc/subdir/s/externals'
224 Fetching external item into 'tc/subdir/s/externals'* (glob)
240 A tc/subdir/s/externals/other
225 A tc/subdir/s/externals/other
241 Checked out external at revision 1.
226 Checked out external at revision 1.
242
227
243 Checked out revision 2.
228 Checked out revision 2.
244 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
229 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
245 $ cd tc
230 $ cd tc
246
231
247 debugsub in clone
232 debugsub in clone
248
233
249 $ hg debugsub
234 $ hg debugsub
250 path s
235 path s
251 source file://*/svn-repo/src (glob)
236 source file://*/svn-repo/src (glob)
252 revision 3
237 revision 3
253 path subdir/s
238 path subdir/s
254 source file://*/svn-repo/src (glob)
239 source file://*/svn-repo/src (glob)
255 revision 2
240 revision 2
256
241
257 verify subrepo is contained within the repo directory
242 verify subrepo is contained within the repo directory
258
243
259 $ python -c "import os.path; print os.path.exists('s')"
244 $ python -c "import os.path; print os.path.exists('s')"
260 True
245 True
261
246
262 update to nullrev (must delete the subrepo)
247 update to nullrev (must delete the subrepo)
263
248
264 $ hg up null
249 $ hg up null
265 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
250 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
266 $ ls
251 $ ls
267
252
268 Check hg update --clean
253 Check hg update --clean
269 $ cd $TESTTMP/sub/t
254 $ cd $TESTTMP/sub/t
270 $ cd s
255 $ cd s
271 $ echo c0 > alpha
256 $ echo c0 > alpha
272 $ echo c1 > f1
257 $ echo c1 > f1
273 $ echo c1 > f2
258 $ echo c1 > f2
274 $ svn add f1 -q
259 $ svn add f1 -q
275 $ svn status
260 $ svn status | sort
261
276 ? * a (glob)
262 ? * a (glob)
277 X * externals (glob)
278 ? * f2 (glob)
263 ? * f2 (glob)
264 A * f1 (glob)
279 M * alpha (glob)
265 M * alpha (glob)
280 A * f1 (glob)
266 Performing status on external item at 'externals'* (glob)
281
267 X * externals (glob)
282 Performing status on external item at 'externals'
283 $ cd ../..
268 $ cd ../..
284 $ hg -R t update -C
269 $ hg -R t update -C
285
270
286 Fetching external item into 't/s/externals'
271 Fetching external item into 't/s/externals'* (glob)
287 Checked out external at revision 1.
272 Checked out external at revision 1.
288
273
289 Checked out revision 3.
274 Checked out revision 3.
290 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
275 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
291 $ cd t/s
276 $ cd t/s
292 $ svn status
277 $ svn status
293 ? * a (glob)
278 ? * a (glob)
294 X * externals (glob)
279 X * externals (glob)
295 ? * f1 (glob)
280 ? * f1 (glob)
296 ? * f2 (glob)
281 ? * f2 (glob)
297
282
298 Performing status on external item at 'externals'
283 Performing status on external item at 'externals'* (glob)
299
284
300 Sticky subrepositories, no changes
285 Sticky subrepositories, no changes
301 $ cd $TESTTMP/sub/t
286 $ cd $TESTTMP/sub/t
302 $ hg id -n
287 $ hg id -n
303 2
288 2
304 $ cd s
289 $ cd s
305 $ svnversion
290 $ svnversion
306 3
291 3
307 $ cd ..
292 $ cd ..
308 $ hg update 1
293 $ hg update 1
309 U $TESTTMP/sub/t/s/alpha
294 U *s/alpha (glob)
310
295
311 Fetching external item into '$TESTTMP/sub/t/s/externals'
296 Fetching external item into '*s/externals'* (glob)
312 Checked out external at revision 1.
297 Checked out external at revision 1.
313
298
314 Checked out revision 2.
299 Checked out revision 2.
315 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
300 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
316 $ hg id -n
301 $ hg id -n
317 1
302 1
318 $ cd s
303 $ cd s
319 $ svnversion
304 $ svnversion
320 2
305 2
321 $ cd ..
306 $ cd ..
322
307
323 Sticky subrepositorys, file changes
308 Sticky subrepositorys, file changes
324 $ touch s/f1
309 $ touch s/f1
325 $ cd s
310 $ cd s
326 $ svn add f1
311 $ svn add f1
327 A f1
312 A f1
328 $ cd ..
313 $ cd ..
329 $ hg id -n
314 $ hg id -n
330 1
315 1
331 $ cd s
316 $ cd s
332 $ svnversion
317 $ svnversion
333 2M
318 2M
334 $ cd ..
319 $ cd ..
335 $ hg update tip
320 $ hg update tip
336 subrepository sources for s differ
321 subrepository sources for s differ
337 use (l)ocal source (2) or (r)emote source (3)?
322 use (l)ocal source (2) or (r)emote source (3)?
338 l
323 l
339 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
324 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
340 $ hg id -n
325 $ hg id -n
341 2+
326 2+
342 $ cd s
327 $ cd s
343 $ svnversion
328 $ svnversion
344 2M
329 2M
345 $ cd ..
330 $ cd ..
346 $ hg update --clean tip
331 $ hg update --clean tip
347 U $TESTTMP/sub/t/s/alpha
332 U *s/alpha (glob)
348
333
349 Fetching external item into '$TESTTMP/sub/t/s/externals'
334 Fetching external item into '*s/externals'* (glob)
350 Checked out external at revision 1.
335 Checked out external at revision 1.
351
336
352 Checked out revision 3.
337 Checked out revision 3.
353 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
338 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
354
339
355 Sticky subrepository, revision updates
340 Sticky subrepository, revision updates
356 $ hg id -n
341 $ hg id -n
357 2
342 2
358 $ cd s
343 $ cd s
359 $ svnversion
344 $ svnversion
360 3
345 3
361 $ cd ..
346 $ cd ..
362 $ cd s
347 $ cd s
363 $ svn update -r 1
348 $ svn update -qr 1
364 U alpha
365 U .
366
367 Fetching external item into 'externals'
368 Updated external to revision 1.
369
370 Updated to revision 1.
371 $ cd ..
349 $ cd ..
372 $ hg update 1
350 $ hg update 1
373 subrepository sources for s differ (in checked out version)
351 subrepository sources for s differ (in checked out version)
374 use (l)ocal source (1) or (r)emote source (2)?
352 use (l)ocal source (1) or (r)emote source (2)?
375 l
353 l
376 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
354 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
377 $ hg id -n
355 $ hg id -n
378 1+
356 1+
379 $ cd s
357 $ cd s
380 $ svnversion
358 $ svnversion
381 1
359 1
382 $ cd ..
360 $ cd ..
383
361
384 Sticky subrepository, file changes and revision updates
362 Sticky subrepository, file changes and revision updates
385 $ touch s/f1
363 $ touch s/f1
386 $ cd s
364 $ cd s
387 $ svn add f1
365 $ svn add f1
388 A f1
366 A f1
389 $ svnversion
367 $ svnversion
390 1M
368 1M
391 $ cd ..
369 $ cd ..
392 $ hg id -n
370 $ hg id -n
393 1+
371 1+
394 $ hg update tip
372 $ hg update tip
395 subrepository sources for s differ
373 subrepository sources for s differ
396 use (l)ocal source (1) or (r)emote source (3)?
374 use (l)ocal source (1) or (r)emote source (3)?
397 l
375 l
398 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
376 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
399 $ hg id -n
377 $ hg id -n
400 2
378 2
401 $ cd s
379 $ cd s
402 $ svnversion
380 $ svnversion
403 1M
381 1M
404 $ cd ..
382 $ cd ..
405
383
406 Sticky repository, update --clean
384 Sticky repository, update --clean
407 $ hg update --clean tip
385 $ hg update --clean tip | grep -v s/externals/other
408 U $TESTTMP/sub/t/s/alpha
386 U *s/alpha (glob)
409 U $TESTTMP/sub/t/s
387 U *s (glob)
410
388
411 Fetching external item into '$TESTTMP/sub/t/s/externals'
389 Fetching external item into '*s/externals'* (glob)
412 Checked out external at revision 1.
390 Checked out external at revision 1.
413
391
414 Checked out revision 3.
392 Checked out revision 3.
415 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
393 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
416 $ hg id -n
394 $ hg id -n
417 2
395 2
418 $ cd s
396 $ cd s
419 $ svnversion
397 $ svnversion
420 3
398 3
421 $ cd ..
399 $ cd ..
422
400
423 Test subrepo already at intended revision:
401 Test subrepo already at intended revision:
424 $ cd s
402 $ cd s
425 $ svn update -r 2
403 $ svn update -qr 2
426 U alpha
427
428 Fetching external item into 'externals'
429 Updated external to revision 1.
430
431 Updated to revision 2.
432 $ cd ..
404 $ cd ..
433 $ hg update 1
405 $ hg update 1
434 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
406 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
435 $ hg id -n
407 $ hg id -n
436 1+
408 1+
437 $ cd s
409 $ cd s
438 $ svnversion
410 $ svnversion
439 2
411 2
440 $ cd ..
412 $ cd ..
441
413
442 Test case where subversion would fail to update the subrepo because there
414 Test case where subversion would fail to update the subrepo because there
443 are unknown directories being replaced by tracked ones (happens with rebase).
415 are unknown directories being replaced by tracked ones (happens with rebase).
444
416
445 $ cd $WCROOT/src
417 $ cd $WCROOT/src
446 $ mkdir dir
418 $ mkdir dir
447 $ echo epsilon.py > dir/epsilon.py
419 $ echo epsilon.py > dir/epsilon.py
448 $ svn add dir
420 $ svn add dir
449 A dir
421 A dir
450 A dir/epsilon.py
422 A dir/epsilon.py
451 $ svn ci -m 'Add dir/epsilon.py'
423 $ svn ci -m 'Add dir/epsilon.py'
452 Adding src/dir
424 Adding *dir (glob)
453 Adding src/dir/epsilon.py
425 Adding *dir/epsilon.py (glob)
454 Transmitting file data .
426 Transmitting file data .
455 Committed revision 6.
427 Committed revision 6.
456 $ cd ../..
428 $ cd ../..
457 $ hg init rebaserepo
429 $ hg init rebaserepo
458 $ cd rebaserepo
430 $ cd rebaserepo
459 $ svn co -r5 --quiet "$SVNREPO"/src s
431 $ svn co -r5 --quiet "$SVNREPO"/src s
460 $ echo "s = [svn] $SVNREPO/src" >> .hgsub
432 $ echo "s = [svn] $SVNREPO/src" >> .hgsub
461 $ hg add .hgsub
433 $ hg add .hgsub
462 $ hg ci -m addsub
434 $ hg ci -m addsub
463 committing subrepository s
435 committing subrepository s
464 $ echo a > a
436 $ echo a > a
465 $ hg ci -Am adda
437 $ hg ci -Am adda
466 adding a
438 adding a
467 $ hg up 0
439 $ hg up 0
468 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
440 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
469 $ svn up -r6 s
441 $ svn up -qr6 s
470 A s/dir
471 A s/dir/epsilon.py
472
473 Fetching external item into 's/externals'
474 Updated external to revision 1.
475
476 Updated to revision 6.
477 $ hg ci -m updatesub
442 $ hg ci -m updatesub
478 committing subrepository s
443 committing subrepository s
479 created new head
444 created new head
480 $ echo pyc > s/dir/epsilon.pyc
445 $ echo pyc > s/dir/epsilon.pyc
481 $ hg up 1
446 $ hg up 1
482 D $TESTTMP/rebaserepo/s/dir
447 D *s/dir (glob)
483
448
484 Fetching external item into '$TESTTMP/rebaserepo/s/externals'
449 Fetching external item into '*s/externals'* (glob)
485 Checked out external at revision 1.
450 Checked out external at revision 1.
486
451
487 Checked out revision 5.
452 Checked out revision 5.
488 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
453 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
489 $ if "$TESTDIR/hghave" -q svn15; then
454 $ if "$TESTDIR/hghave" -q svn15; then
490 > hg up 2 >/dev/null 2>&1 || echo update failed
455 > hg up 2 >/dev/null 2>&1 || echo update failed
491 > fi
456 > fi
492
457
493 Modify one of the externals to point to a different path so we can
458 Modify one of the externals to point to a different path so we can
494 test having obstructions when switching branches on checkout:
459 test having obstructions when switching branches on checkout:
495 $ hg checkout tip
460 $ hg checkout tip
496 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
461 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
497 $ echo "obstruct = [svn] $SVNREPO/externals" >> .hgsub
462 $ echo "obstruct = [svn] $SVNREPO/externals" >> .hgsub
498 $ svn co -r5 --quiet "$SVNREPO"/externals obstruct
463 $ svn co -r5 --quiet "$SVNREPO"/externals obstruct
499 $ hg commit -m 'Start making obstructed working copy'
464 $ hg commit -m 'Start making obstructed working copy'
500 committing subrepository obstruct
465 committing subrepository obstruct
501 $ hg book other
466 $ hg book other
502 $ hg co -r 'p1(tip)'
467 $ hg co -r 'p1(tip)'
503 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
468 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
504 $ echo "obstruct = [svn] $SVNREPO/src" >> .hgsub
469 $ echo "obstruct = [svn] $SVNREPO/src" >> .hgsub
505 $ svn co -r5 --quiet "$SVNREPO"/src obstruct
470 $ svn co -r5 --quiet "$SVNREPO"/src obstruct
506 $ hg commit -m 'Other branch which will be obstructed'
471 $ hg commit -m 'Other branch which will be obstructed'
507 committing subrepository obstruct
472 committing subrepository obstruct
508 created new head
473 created new head
509
474
510 Switching back to the head where we have another path mapped to the
475 Switching back to the head where we have another path mapped to the
511 same subrepo should work if the subrepo is clean.
476 same subrepo should work if the subrepo is clean.
512 $ hg co other
477 $ hg co other
513 A $TESTTMP/rebaserepo/obstruct/other
478 A *obstruct/other (glob)
514 Checked out revision 1.
479 Checked out revision 1.
515 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
480 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
516
481
517 This is surprising, but is also correct based on the current code:
482 This is surprising, but is also correct based on the current code:
518 $ echo "updating should (maybe) fail" > obstruct/other
483 $ echo "updating should (maybe) fail" > obstruct/other
519 $ hg co tip
484 $ hg co tip
520 abort: crosses branches (merge branches or use --clean to discard changes)
485 abort: crosses branches (merge branches or use --clean to discard changes)
521 [255]
486 [255]
522
487
523 Point to a Subversion branch which has since been deleted and recreated
488 Point to a Subversion branch which has since been deleted and recreated
524 First, create that condition in the repository.
489 First, create that condition in the repository.
525
490
526 $ hg ci --subrepos -m cleanup
491 $ hg ci --subrepos -m cleanup | grep -v Updating
527 committing subrepository obstruct
492 committing subrepository obstruct
528 Sending obstruct/other
493 Sending obstruct/other
529 Transmitting file data .
494 Transmitting file data .
530 Committed revision 7.
495 Committed revision 7.
531 At revision 7.
496 At revision 7.
532 $ svn mkdir -m "baseline" $SVNREPO/trunk
497 $ svn mkdir -m "baseline" $SVNREPO/trunk
533
498
534 Committed revision 8.
499 Committed revision 8.
535 $ svn copy -m "initial branch" $SVNREPO/trunk $SVNREPO/branch
500 $ svn copy -m "initial branch" $SVNREPO/trunk $SVNREPO/branch
536
501
537 Committed revision 9.
502 Committed revision 9.
538 $ svn co --quiet "$SVNREPO"/branch tempwc
503 $ svn co --quiet "$SVNREPO"/branch tempwc
539 $ cd tempwc
504 $ cd tempwc
540 $ echo "something old" > somethingold
505 $ echo "something old" > somethingold
541 $ svn add somethingold
506 $ svn add somethingold
542 A somethingold
507 A somethingold
543 $ svn ci -m 'Something old'
508 $ svn ci -m 'Something old'
544 Adding somethingold
509 Adding somethingold
545 Transmitting file data .
510 Transmitting file data .
546 Committed revision 10.
511 Committed revision 10.
547 $ svn rm -m "remove branch" $SVNREPO/branch
512 $ svn rm -m "remove branch" $SVNREPO/branch
548
513
549 Committed revision 11.
514 Committed revision 11.
550 $ svn copy -m "recreate branch" $SVNREPO/trunk $SVNREPO/branch
515 $ svn copy -m "recreate branch" $SVNREPO/trunk $SVNREPO/branch
551
516
552 Committed revision 12.
517 Committed revision 12.
553 $ svn up
518 $ svn up -q
554 D somethingold
555 Updated to revision 12.
556 $ echo "something new" > somethingnew
519 $ echo "something new" > somethingnew
557 $ svn add somethingnew
520 $ svn add somethingnew
558 A somethingnew
521 A somethingnew
559 $ svn ci -m 'Something new'
522 $ svn ci -m 'Something new'
560 Adding somethingnew
523 Adding somethingnew
561 Transmitting file data .
524 Transmitting file data .
562 Committed revision 13.
525 Committed revision 13.
563 $ cd ..
526 $ cd ..
564 $ rm -rf tempwc
527 $ rm -rf tempwc
565 $ svn co "$SVNREPO/branch"@10 recreated
528 $ svn co "$SVNREPO/branch"@10 recreated
566 A recreated/somethingold
529 A recreated/somethingold
567 Checked out revision 10.
530 Checked out revision 10.
568 $ echo "recreated = [svn] $SVNREPO/branch" >> .hgsub
531 $ echo "recreated = [svn] $SVNREPO/branch" >> .hgsub
569 $ hg ci -m addsub
532 $ hg ci -m addsub
570 committing subrepository recreated
533 committing subrepository recreated
571 $ cd recreated
534 $ cd recreated
572 $ svn up
535 $ svn up -q
573 D somethingold
574 A somethingnew
575 Updated to revision 13.
576 $ cd ..
536 $ cd ..
577 $ hg ci -m updatesub
537 $ hg ci -m updatesub
578 committing subrepository recreated
538 committing subrepository recreated
579 $ hg up -r-2
539 $ hg up -r-2
580 D $TESTTMP/rebaserepo/recreated/somethingnew
540 D *recreated/somethingnew (glob)
581 A $TESTTMP/rebaserepo/recreated/somethingold
541 A *recreated/somethingold (glob)
582 Checked out revision 10.
542 Checked out revision 10.
583 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
543 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
584 $ test -f recreated/somethingold
544 $ test -f recreated/somethingold
585
545
General Comments 0
You need to be logged in to leave comments. Login now