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