##// END OF EJS Templates
tests: check path separator in moves
Brendan Cully -
r19133:101b80eb default
parent child Browse files
Show More
@@ -1,485 +1,486 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 l = len(comment.rstrip())
22 l = len(comment.rstrip())
23 return "#" * l + comment[l:]
23 return "#" * l + comment[l:]
24 return repquote(m)
24 return repquote(m)
25
25
26 def repcomment(m):
26 def repcomment(m):
27 return m.group(1) + "#" * len(m.group(2))
27 return m.group(1) + "#" * len(m.group(2))
28
28
29 def repccomment(m):
29 def repccomment(m):
30 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
30 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
31 return m.group(1) + t + "*/"
31 return m.group(1) + t + "*/"
32
32
33 def repcallspaces(m):
33 def repcallspaces(m):
34 t = re.sub(r"\n\s+", "\n", m.group(2))
34 t = re.sub(r"\n\s+", "\n", m.group(2))
35 return m.group(1) + t
35 return m.group(1) + t
36
36
37 def repinclude(m):
37 def repinclude(m):
38 return m.group(1) + "<foo>"
38 return m.group(1) + "<foo>"
39
39
40 def rephere(m):
40 def rephere(m):
41 t = re.sub(r"\S", "x", m.group(2))
41 t = re.sub(r"\S", "x", m.group(2))
42 return m.group(1) + t
42 return m.group(1) + t
43
43
44
44
45 testpats = [
45 testpats = [
46 [
46 [
47 (r'pushd|popd', "don't use 'pushd' or 'popd', use 'cd'"),
47 (r'pushd|popd', "don't use 'pushd' or 'popd', use 'cd'"),
48 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"),
48 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"),
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'sed.*-i', "don't use 'sed -i', use a temporary file"),
50 (r'sed.*-i', "don't use 'sed -i', use a temporary file"),
51 (r'\becho\b.*\\n', "don't use 'echo \\n', use printf"),
51 (r'\becho\b.*\\n', "don't use 'echo \\n', use printf"),
52 (r'echo -n', "don't use 'echo -n', use printf"),
52 (r'echo -n', "don't use 'echo -n', use printf"),
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.*\\([1-9]|0\d)', "don't use 'printf \NNN', use Python"),
57 (r'printf.*\\([1-9]|0\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'[^\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 (r'^source\b', "don't use 'source', use '.'"),
66 (r'^source\b', "don't use 'source', use '.'"),
67 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
67 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
68 (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"),
68 (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"),
69 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
69 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
70 (r'^stop\(\)', "don't use 'stop' as a shell function name"),
70 (r'^stop\(\)', "don't use 'stop' as a shell function name"),
71 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"),
71 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"),
72 (r'^alias\b.*=', "don't use alias, use a function"),
72 (r'^alias\b.*=', "don't use alias, use a function"),
73 (r'if\s*!', "don't use '!' to negate exit status"),
73 (r'if\s*!', "don't use '!' to negate exit status"),
74 (r'/dev/u?random', "don't use entropy, use /dev/zero"),
74 (r'/dev/u?random', "don't use entropy, use /dev/zero"),
75 (r'do\s*true;\s*done', "don't use true as loop body, use sleep 0"),
75 (r'do\s*true;\s*done', "don't use true as loop body, use sleep 0"),
76 (r'^( *)\t', "don't use tabs to indent"),
76 (r'^( *)\t', "don't use tabs to indent"),
77 (r'sed (-e )?\'(\d+|/[^/]*/)i(?!\\\n)',
77 (r'sed (-e )?\'(\d+|/[^/]*/)i(?!\\\n)',
78 "put a backslash-escaped newline after sed 'i' command"),
78 "put a backslash-escaped newline after sed 'i' command"),
79 ],
79 ],
80 # warnings
80 # warnings
81 [
81 [
82 (r'^function', "don't use 'function', use old style"),
82 (r'^function', "don't use 'function', use old style"),
83 (r'^diff.*-\w*N', "don't use 'diff -N'"),
83 (r'^diff.*-\w*N', "don't use 'diff -N'"),
84 (r'\$PWD|\${PWD}', "don't use $PWD, use `pwd`"),
84 (r'\$PWD|\${PWD}', "don't use $PWD, use `pwd`"),
85 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"),
85 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"),
86 (r'kill (`|\$\()', "don't use kill, use killdaemons.py")
86 (r'kill (`|\$\()', "don't use kill, use killdaemons.py")
87 ]
87 ]
88 ]
88 ]
89
89
90 testfilters = [
90 testfilters = [
91 (r"( *)(#([^\n]*\S)?)", repcomment),
91 (r"( *)(#([^\n]*\S)?)", repcomment),
92 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
92 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
93 ]
93 ]
94
94
95 winglobmsg = "use (glob) to match Windows paths too"
95 winglobmsg = "use (glob) to match Windows paths too"
96 uprefix = r"^ \$ "
96 uprefix = r"^ \$ "
97 utestpats = [
97 utestpats = [
98 [
98 [
99 (r'^(\S.*|| [$>] .*)[ \t]\n', "trailing whitespace on non-output"),
99 (r'^(\S.*|| [$>] .*)[ \t]\n', "trailing whitespace on non-output"),
100 (uprefix + r'.*\|\s*sed[^|>\n]*\n',
100 (uprefix + r'.*\|\s*sed[^|>\n]*\n',
101 "use regex test output patterns instead of sed"),
101 "use regex test output patterns instead of sed"),
102 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
102 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
103 (uprefix + r'.*(?<!\[)\$\?', "explicit exit code checks unnecessary"),
103 (uprefix + r'.*(?<!\[)\$\?', "explicit exit code checks unnecessary"),
104 (uprefix + r'.*\|\| echo.*(fail|error)',
104 (uprefix + r'.*\|\| echo.*(fail|error)',
105 "explicit exit code checks unnecessary"),
105 "explicit exit code checks unnecessary"),
106 (uprefix + r'set -e', "don't use set -e"),
106 (uprefix + r'set -e', "don't use set -e"),
107 (uprefix + r'\s', "don't indent commands, use > for continued lines"),
107 (uprefix + r'\s', "don't indent commands, use > for continued lines"),
108 (r'^ saved backup bundle to \$TESTTMP.*\.hg$', winglobmsg),
108 (r'^ saved backup bundle to \$TESTTMP.*\.hg$', winglobmsg),
109 (r'^ changeset .* references (corrupted|missing) \$TESTTMP/.*[^)]$',
109 (r'^ changeset .* references (corrupted|missing) \$TESTTMP/.*[^)]$',
110 winglobmsg),
110 winglobmsg),
111 (r'^ pulling from \$TESTTMP/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
111 (r'^ pulling from \$TESTTMP/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
112 (r'^ reverting .*/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
112 (r'^ reverting .*/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
113 (r'^ cloning subrepo \S+/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
113 (r'^ cloning subrepo \S+/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
114 (r'^ pushing to \$TESTTMP/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
114 (r'^ pushing to \$TESTTMP/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
115 (r'^ moving \S+/.*[^)]$', winglobmsg),
115 (r'^ no changes made to subrepo since.*/.*[^)]$',
116 (r'^ no changes made to subrepo since.*/.*[^)]$',
116 winglobmsg, '\$TESTTMP/unix-repo$'),
117 winglobmsg, '\$TESTTMP/unix-repo$'),
117 (r'^ .*: largefile \S+ not available from file:.*/.*[^)]$',
118 (r'^ .*: largefile \S+ not available from file:.*/.*[^)]$',
118 winglobmsg, '\$TESTTMP/unix-repo$'),
119 winglobmsg, '\$TESTTMP/unix-repo$'),
119 ],
120 ],
120 # warnings
121 # warnings
121 [
122 [
122 (r'^ [^*?/\n]* \(glob\)$',
123 (r'^ [^*?/\n]* \(glob\)$',
123 "warning: glob match with no glob character (?*/)"),
124 "warning: glob match with no glob character (?*/)"),
124 ]
125 ]
125 ]
126 ]
126
127
127 for i in [0, 1]:
128 for i in [0, 1]:
128 for p, m in testpats[i]:
129 for p, m in testpats[i]:
129 if p.startswith(r'^'):
130 if p.startswith(r'^'):
130 p = r"^ [$>] (%s)" % p[1:]
131 p = r"^ [$>] (%s)" % p[1:]
131 else:
132 else:
132 p = r"^ [$>] .*(%s)" % p
133 p = r"^ [$>] .*(%s)" % p
133 utestpats[i].append((p, m))
134 utestpats[i].append((p, m))
134
135
135 utestfilters = [
136 utestfilters = [
136 (r"<<(\S+)((.|\n)*?\n > \1)", rephere),
137 (r"<<(\S+)((.|\n)*?\n > \1)", rephere),
137 (r"( *)(#([^\n]*\S)?)", repcomment),
138 (r"( *)(#([^\n]*\S)?)", repcomment),
138 ]
139 ]
139
140
140 pypats = [
141 pypats = [
141 [
142 [
142 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
143 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
143 "tuple parameter unpacking not available in Python 3+"),
144 "tuple parameter unpacking not available in Python 3+"),
144 (r'lambda\s*\(.*,.*\)',
145 (r'lambda\s*\(.*,.*\)',
145 "tuple parameter unpacking not available in Python 3+"),
146 "tuple parameter unpacking not available in Python 3+"),
146 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
147 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
147 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
148 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
148 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
149 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
149 (r'\s<>\s', '<> operator is not available in Python 3+, use !='),
150 (r'\s<>\s', '<> operator is not available in Python 3+, use !='),
150 (r'^\s*\t', "don't use tabs"),
151 (r'^\s*\t', "don't use tabs"),
151 (r'\S;\s*\n', "semicolon"),
152 (r'\S;\s*\n', "semicolon"),
152 (r'[^_]_\("[^"]+"\s*%', "don't use % inside _()"),
153 (r'[^_]_\("[^"]+"\s*%', "don't use % inside _()"),
153 (r"[^_]_\('[^']+'\s*%", "don't use % inside _()"),
154 (r"[^_]_\('[^']+'\s*%", "don't use % inside _()"),
154 (r'(\w|\)),\w', "missing whitespace after ,"),
155 (r'(\w|\)),\w', "missing whitespace after ,"),
155 (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"),
156 (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"),
156 (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"),
157 (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"),
157 (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n'
158 (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n'
158 r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Python 2.4'),
159 r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Python 2.4'),
159 (r'(\s+)try:\n((?:\n|\1\s.*\n)*?)\1\s*yield\b.*?'
160 (r'(\s+)try:\n((?:\n|\1\s.*\n)*?)\1\s*yield\b.*?'
160 r'((?:\n|\1\s.*\n)+?)\1finally:',
161 r'((?:\n|\1\s.*\n)+?)\1finally:',
161 'no yield inside try/finally in Python 2.4'),
162 'no yield inside try/finally in Python 2.4'),
162 (r'.{81}', "line too long"),
163 (r'.{81}', "line too long"),
163 (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'),
164 (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'),
164 (r'[^\n]\Z', "no trailing newline"),
165 (r'[^\n]\Z', "no trailing newline"),
165 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
166 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
166 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=',
167 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=',
167 # "don't use underbars in identifiers"),
168 # "don't use underbars in identifiers"),
168 (r'^\s+(self\.)?[A-za-z][a-z0-9]+[A-Z]\w* = ',
169 (r'^\s+(self\.)?[A-za-z][a-z0-9]+[A-Z]\w* = ',
169 "don't use camelcase in identifiers"),
170 "don't use camelcase in identifiers"),
170 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+',
171 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+',
171 "linebreak after :"),
172 "linebreak after :"),
172 (r'class\s[^( \n]+:', "old-style class, use class foo(object)"),
173 (r'class\s[^( \n]+:', "old-style class, use class foo(object)"),
173 (r'class\s[^( \n]+\(\):',
174 (r'class\s[^( \n]+\(\):',
174 "class foo() not available in Python 2.4, use class foo(object)"),
175 "class foo() not available in Python 2.4, use class foo(object)"),
175 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
176 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
176 "Python keyword is not a function"),
177 "Python keyword is not a function"),
177 (r',]', "unneeded trailing ',' in list"),
178 (r',]', "unneeded trailing ',' in list"),
178 # (r'class\s[A-Z][^\(]*\((?!Exception)',
179 # (r'class\s[A-Z][^\(]*\((?!Exception)',
179 # "don't capitalize non-exception classes"),
180 # "don't capitalize non-exception classes"),
180 # (r'in range\(', "use xrange"),
181 # (r'in range\(', "use xrange"),
181 # (r'^\s*print\s+', "avoid using print in core and extensions"),
182 # (r'^\s*print\s+', "avoid using print in core and extensions"),
182 (r'[\x80-\xff]', "non-ASCII character literal"),
183 (r'[\x80-\xff]', "non-ASCII character literal"),
183 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
184 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
184 (r'^\s*with\s+', "with not available in Python 2.4"),
185 (r'^\s*with\s+', "with not available in Python 2.4"),
185 (r'\.isdisjoint\(', "set.isdisjoint not available in Python 2.4"),
186 (r'\.isdisjoint\(', "set.isdisjoint not available in Python 2.4"),
186 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
187 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
187 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
188 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
188 (r'(?<!def)\s+(any|all|format)\(',
189 (r'(?<!def)\s+(any|all|format)\(',
189 "any/all/format not available in Python 2.4"),
190 "any/all/format not available in Python 2.4"),
190 (r'(?<!def)\s+(callable)\(',
191 (r'(?<!def)\s+(callable)\(',
191 "callable not available in Python 3, use getattr(f, '__call__', None)"),
192 "callable not available in Python 3, use getattr(f, '__call__', None)"),
192 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
193 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
193 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
194 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
194 "gratuitous whitespace after Python keyword"),
195 "gratuitous whitespace after Python keyword"),
195 (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"),
196 (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"),
196 # (r'\s\s=', "gratuitous whitespace before ="),
197 # (r'\s\s=', "gratuitous whitespace before ="),
197 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
198 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
198 "missing whitespace around operator"),
199 "missing whitespace around operator"),
199 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\s',
200 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\s',
200 "missing whitespace around operator"),
201 "missing whitespace around operator"),
201 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
202 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
202 "missing whitespace around operator"),
203 "missing whitespace around operator"),
203 (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]',
204 (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]',
204 "wrong whitespace around ="),
205 "wrong whitespace around ="),
205 (r'raise Exception', "don't raise generic exceptions"),
206 (r'raise Exception', "don't raise generic exceptions"),
206 (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$',
207 (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$',
207 "don't use old-style two-argument raise, use Exception(message)"),
208 "don't use old-style two-argument raise, use Exception(message)"),
208 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
209 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
209 (r' [=!]=\s+(True|False|None)',
210 (r' [=!]=\s+(True|False|None)',
210 "comparison with singleton, use 'is' or 'is not' instead"),
211 "comparison with singleton, use 'is' or 'is not' instead"),
211 (r'^\s*(while|if) [01]:',
212 (r'^\s*(while|if) [01]:',
212 "use True/False for constant Boolean expression"),
213 "use True/False for constant Boolean expression"),
213 (r'(?:(?<!def)\s+|\()hasattr',
214 (r'(?:(?<!def)\s+|\()hasattr',
214 'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
215 'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
215 (r'opener\([^)]*\).read\(',
216 (r'opener\([^)]*\).read\(',
216 "use opener.read() instead"),
217 "use opener.read() instead"),
217 (r'BaseException', 'not in Python 2.4, use Exception'),
218 (r'BaseException', 'not in Python 2.4, use Exception'),
218 (r'os\.path\.relpath', 'os.path.relpath is not in Python 2.5'),
219 (r'os\.path\.relpath', 'os.path.relpath is not in Python 2.5'),
219 (r'opener\([^)]*\).write\(',
220 (r'opener\([^)]*\).write\(',
220 "use opener.write() instead"),
221 "use opener.write() instead"),
221 (r'[\s\(](open|file)\([^)]*\)\.read\(',
222 (r'[\s\(](open|file)\([^)]*\)\.read\(',
222 "use util.readfile() instead"),
223 "use util.readfile() instead"),
223 (r'[\s\(](open|file)\([^)]*\)\.write\(',
224 (r'[\s\(](open|file)\([^)]*\)\.write\(',
224 "use util.readfile() instead"),
225 "use util.readfile() instead"),
225 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
226 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
226 "always assign an opened file to a variable, and close it afterwards"),
227 "always assign an opened file to a variable, and close it afterwards"),
227 (r'[\s\(](open|file)\([^)]*\)\.',
228 (r'[\s\(](open|file)\([^)]*\)\.',
228 "always assign an opened file to a variable, and close it afterwards"),
229 "always assign an opened file to a variable, and close it afterwards"),
229 (r'(?i)descendent', "the proper spelling is descendAnt"),
230 (r'(?i)descendent', "the proper spelling is descendAnt"),
230 (r'\.debug\(\_', "don't mark debug messages for translation"),
231 (r'\.debug\(\_', "don't mark debug messages for translation"),
231 (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"),
232 (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"),
232 (r'^\s*except\s*:', "naked except clause", r'#.*re-raises'),
233 (r'^\s*except\s*:', "naked except clause", r'#.*re-raises'),
233 (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"),
234 (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"),
234 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
235 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
235 "missing _() in ui message (use () to hide false-positives)"),
236 "missing _() in ui message (use () to hide false-positives)"),
236 (r'release\(.*wlock, .*lock\)', "wrong lock release order"),
237 (r'release\(.*wlock, .*lock\)', "wrong lock release order"),
237 ],
238 ],
238 # warnings
239 # warnings
239 [
240 [
240 ]
241 ]
241 ]
242 ]
242
243
243 pyfilters = [
244 pyfilters = [
244 (r"""(?msx)(?P<comment>\#.*?$)|
245 (r"""(?msx)(?P<comment>\#.*?$)|
245 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
246 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
246 (?P<text>(([^\\]|\\.)*?))
247 (?P<text>(([^\\]|\\.)*?))
247 (?P=quote))""", reppython),
248 (?P=quote))""", reppython),
248 ]
249 ]
249
250
250 txtfilters = []
251 txtfilters = []
251
252
252 txtpats = [
253 txtpats = [
253 [
254 [
254 ('\s$', 'trailing whitespace'),
255 ('\s$', 'trailing whitespace'),
255 ],
256 ],
256 []
257 []
257 ]
258 ]
258
259
259 cpats = [
260 cpats = [
260 [
261 [
261 (r'//', "don't use //-style comments"),
262 (r'//', "don't use //-style comments"),
262 (r'^ ', "don't use spaces to indent"),
263 (r'^ ', "don't use spaces to indent"),
263 (r'\S\t', "don't use tabs except for indent"),
264 (r'\S\t', "don't use tabs except for indent"),
264 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
265 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
265 (r'.{81}', "line too long"),
266 (r'.{81}', "line too long"),
266 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
267 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
267 (r'return\(', "return is not a function"),
268 (r'return\(', "return is not a function"),
268 (r' ;', "no space before ;"),
269 (r' ;', "no space before ;"),
269 (r'\w+\* \w+', "use int *foo, not int* foo"),
270 (r'\w+\* \w+', "use int *foo, not int* foo"),
270 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
271 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
271 (r'\w+ (\+\+|--)', "use foo++, not foo ++"),
272 (r'\w+ (\+\+|--)', "use foo++, not foo ++"),
272 (r'\w,\w', "missing whitespace after ,"),
273 (r'\w,\w', "missing whitespace after ,"),
273 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
274 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
274 (r'^#\s+\w', "use #foo, not # foo"),
275 (r'^#\s+\w', "use #foo, not # foo"),
275 (r'[^\n]\Z', "no trailing newline"),
276 (r'[^\n]\Z', "no trailing newline"),
276 (r'^\s*#import\b', "use only #include in standard C code"),
277 (r'^\s*#import\b', "use only #include in standard C code"),
277 ],
278 ],
278 # warnings
279 # warnings
279 []
280 []
280 ]
281 ]
281
282
282 cfilters = [
283 cfilters = [
283 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
284 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
284 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
285 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
285 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
286 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
286 (r'(\()([^)]+\))', repcallspaces),
287 (r'(\()([^)]+\))', repcallspaces),
287 ]
288 ]
288
289
289 inutilpats = [
290 inutilpats = [
290 [
291 [
291 (r'\bui\.', "don't use ui in util"),
292 (r'\bui\.', "don't use ui in util"),
292 ],
293 ],
293 # warnings
294 # warnings
294 []
295 []
295 ]
296 ]
296
297
297 inrevlogpats = [
298 inrevlogpats = [
298 [
299 [
299 (r'\brepo\.', "don't use repo in revlog"),
300 (r'\brepo\.', "don't use repo in revlog"),
300 ],
301 ],
301 # warnings
302 # warnings
302 []
303 []
303 ]
304 ]
304
305
305 checks = [
306 checks = [
306 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
307 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
307 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
308 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
308 ('c', r'.*\.c$', cfilters, cpats),
309 ('c', r'.*\.c$', cfilters, cpats),
309 ('unified test', r'.*\.t$', utestfilters, utestpats),
310 ('unified test', r'.*\.t$', utestfilters, utestpats),
310 ('layering violation repo in revlog', r'mercurial/revlog\.py', pyfilters,
311 ('layering violation repo in revlog', r'mercurial/revlog\.py', pyfilters,
311 inrevlogpats),
312 inrevlogpats),
312 ('layering violation ui in util', r'mercurial/util\.py', pyfilters,
313 ('layering violation ui in util', r'mercurial/util\.py', pyfilters,
313 inutilpats),
314 inutilpats),
314 ('txt', r'.*\.txt$', txtfilters, txtpats),
315 ('txt', r'.*\.txt$', txtfilters, txtpats),
315 ]
316 ]
316
317
317 class norepeatlogger(object):
318 class norepeatlogger(object):
318 def __init__(self):
319 def __init__(self):
319 self._lastseen = None
320 self._lastseen = None
320
321
321 def log(self, fname, lineno, line, msg, blame):
322 def log(self, fname, lineno, line, msg, blame):
322 """print error related a to given line of a given file.
323 """print error related a to given line of a given file.
323
324
324 The faulty line will also be printed but only once in the case
325 The faulty line will also be printed but only once in the case
325 of multiple errors.
326 of multiple errors.
326
327
327 :fname: filename
328 :fname: filename
328 :lineno: line number
329 :lineno: line number
329 :line: actual content of the line
330 :line: actual content of the line
330 :msg: error message
331 :msg: error message
331 """
332 """
332 msgid = fname, lineno, line
333 msgid = fname, lineno, line
333 if msgid != self._lastseen:
334 if msgid != self._lastseen:
334 if blame:
335 if blame:
335 print "%s:%d (%s):" % (fname, lineno, blame)
336 print "%s:%d (%s):" % (fname, lineno, blame)
336 else:
337 else:
337 print "%s:%d:" % (fname, lineno)
338 print "%s:%d:" % (fname, lineno)
338 print " > %s" % line
339 print " > %s" % line
339 self._lastseen = msgid
340 self._lastseen = msgid
340 print " " + msg
341 print " " + msg
341
342
342 _defaultlogger = norepeatlogger()
343 _defaultlogger = norepeatlogger()
343
344
344 def getblame(f):
345 def getblame(f):
345 lines = []
346 lines = []
346 for l in os.popen('hg annotate -un %s' % f):
347 for l in os.popen('hg annotate -un %s' % f):
347 start, line = l.split(':', 1)
348 start, line = l.split(':', 1)
348 user, rev = start.split()
349 user, rev = start.split()
349 lines.append((line[1:-1], user, rev))
350 lines.append((line[1:-1], user, rev))
350 return lines
351 return lines
351
352
352 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
353 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
353 blame=False, debug=False, lineno=True):
354 blame=False, debug=False, lineno=True):
354 """checks style and portability of a given file
355 """checks style and portability of a given file
355
356
356 :f: filepath
357 :f: filepath
357 :logfunc: function used to report error
358 :logfunc: function used to report error
358 logfunc(filename, linenumber, linecontent, errormessage)
359 logfunc(filename, linenumber, linecontent, errormessage)
359 :maxerr: number of error to display before aborting.
360 :maxerr: number of error to display before aborting.
360 Set to false (default) to report all errors
361 Set to false (default) to report all errors
361
362
362 return True if no error is found, False otherwise.
363 return True if no error is found, False otherwise.
363 """
364 """
364 blamecache = None
365 blamecache = None
365 result = True
366 result = True
366 for name, match, filters, pats in checks:
367 for name, match, filters, pats in checks:
367 if debug:
368 if debug:
368 print name, f
369 print name, f
369 fc = 0
370 fc = 0
370 if not re.match(match, f):
371 if not re.match(match, f):
371 if debug:
372 if debug:
372 print "Skipping %s for %s it doesn't match %s" % (
373 print "Skipping %s for %s it doesn't match %s" % (
373 name, match, f)
374 name, match, f)
374 continue
375 continue
375 fp = open(f)
376 fp = open(f)
376 pre = post = fp.read()
377 pre = post = fp.read()
377 fp.close()
378 fp.close()
378 if "no-" + "check-code" in pre:
379 if "no-" + "check-code" in pre:
379 if debug:
380 if debug:
380 print "Skipping %s for %s it has no- and check-code" % (
381 print "Skipping %s for %s it has no- and check-code" % (
381 name, f)
382 name, f)
382 break
383 break
383 for p, r in filters:
384 for p, r in filters:
384 post = re.sub(p, r, post)
385 post = re.sub(p, r, post)
385 if warnings:
386 if warnings:
386 pats = pats[0] + pats[1]
387 pats = pats[0] + pats[1]
387 else:
388 else:
388 pats = pats[0]
389 pats = pats[0]
389 # print post # uncomment to show filtered version
390 # print post # uncomment to show filtered version
390
391
391 if debug:
392 if debug:
392 print "Checking %s for %s" % (name, f)
393 print "Checking %s for %s" % (name, f)
393
394
394 prelines = None
395 prelines = None
395 errors = []
396 errors = []
396 for pat in pats:
397 for pat in pats:
397 if len(pat) == 3:
398 if len(pat) == 3:
398 p, msg, ignore = pat
399 p, msg, ignore = pat
399 else:
400 else:
400 p, msg = pat
401 p, msg = pat
401 ignore = None
402 ignore = None
402
403
403 # fix-up regexes for multi-line searches
404 # fix-up regexes for multi-line searches
404 po = p
405 po = p
405 # \s doesn't match \n
406 # \s doesn't match \n
406 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
407 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
407 # [^...] doesn't match newline
408 # [^...] doesn't match newline
408 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p)
409 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p)
409
410
410 #print po, '=>', p
411 #print po, '=>', p
411
412
412 pos = 0
413 pos = 0
413 n = 0
414 n = 0
414 for m in re.finditer(p, post, re.MULTILINE):
415 for m in re.finditer(p, post, re.MULTILINE):
415 if prelines is None:
416 if prelines is None:
416 prelines = pre.splitlines()
417 prelines = pre.splitlines()
417 postlines = post.splitlines(True)
418 postlines = post.splitlines(True)
418
419
419 start = m.start()
420 start = m.start()
420 while n < len(postlines):
421 while n < len(postlines):
421 step = len(postlines[n])
422 step = len(postlines[n])
422 if pos + step > start:
423 if pos + step > start:
423 break
424 break
424 pos += step
425 pos += step
425 n += 1
426 n += 1
426 l = prelines[n]
427 l = prelines[n]
427
428
428 if "check-code" + "-ignore" in l:
429 if "check-code" + "-ignore" in l:
429 if debug:
430 if debug:
430 print "Skipping %s for %s:%s (check-code -ignore)" % (
431 print "Skipping %s for %s:%s (check-code -ignore)" % (
431 name, f, n)
432 name, f, n)
432 continue
433 continue
433 elif ignore and re.search(ignore, l, re.MULTILINE):
434 elif ignore and re.search(ignore, l, re.MULTILINE):
434 continue
435 continue
435 bd = ""
436 bd = ""
436 if blame:
437 if blame:
437 bd = 'working directory'
438 bd = 'working directory'
438 if not blamecache:
439 if not blamecache:
439 blamecache = getblame(f)
440 blamecache = getblame(f)
440 if n < len(blamecache):
441 if n < len(blamecache):
441 bl, bu, br = blamecache[n]
442 bl, bu, br = blamecache[n]
442 if bl == l:
443 if bl == l:
443 bd = '%s@%s' % (bu, br)
444 bd = '%s@%s' % (bu, br)
444 errors.append((f, lineno and n + 1, l, msg, bd))
445 errors.append((f, lineno and n + 1, l, msg, bd))
445 result = False
446 result = False
446
447
447 errors.sort()
448 errors.sort()
448 for e in errors:
449 for e in errors:
449 logfunc(*e)
450 logfunc(*e)
450 fc += 1
451 fc += 1
451 if maxerr and fc >= maxerr:
452 if maxerr and fc >= maxerr:
452 print " (too many errors, giving up)"
453 print " (too many errors, giving up)"
453 break
454 break
454
455
455 return result
456 return result
456
457
457 if __name__ == "__main__":
458 if __name__ == "__main__":
458 parser = optparse.OptionParser("%prog [options] [files]")
459 parser = optparse.OptionParser("%prog [options] [files]")
459 parser.add_option("-w", "--warnings", action="store_true",
460 parser.add_option("-w", "--warnings", action="store_true",
460 help="include warning-level checks")
461 help="include warning-level checks")
461 parser.add_option("-p", "--per-file", type="int",
462 parser.add_option("-p", "--per-file", type="int",
462 help="max warnings per file")
463 help="max warnings per file")
463 parser.add_option("-b", "--blame", action="store_true",
464 parser.add_option("-b", "--blame", action="store_true",
464 help="use annotate to generate blame info")
465 help="use annotate to generate blame info")
465 parser.add_option("", "--debug", action="store_true",
466 parser.add_option("", "--debug", action="store_true",
466 help="show debug information")
467 help="show debug information")
467 parser.add_option("", "--nolineno", action="store_false",
468 parser.add_option("", "--nolineno", action="store_false",
468 dest='lineno', help="don't show line numbers")
469 dest='lineno', help="don't show line numbers")
469
470
470 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False,
471 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False,
471 lineno=True)
472 lineno=True)
472 (options, args) = parser.parse_args()
473 (options, args) = parser.parse_args()
473
474
474 if len(args) == 0:
475 if len(args) == 0:
475 check = glob.glob("*")
476 check = glob.glob("*")
476 else:
477 else:
477 check = args
478 check = args
478
479
479 ret = 0
480 ret = 0
480 for f in check:
481 for f in check:
481 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
482 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
482 blame=options.blame, debug=options.debug,
483 blame=options.blame, debug=options.debug,
483 lineno=options.lineno):
484 lineno=options.lineno):
484 ret = 1
485 ret = 1
485 sys.exit(ret)
486 sys.exit(ret)
@@ -1,243 +1,243 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > graphlog=
3 > graphlog=
4 > rebase=
4 > rebase=
5 >
5 >
6 > [alias]
6 > [alias]
7 > tlog = log --template "{rev}: '{desc}' {branches}\n"
7 > tlog = log --template "{rev}: '{desc}' {branches}\n"
8 > tglog = tlog --graph
8 > tglog = tlog --graph
9 > EOF
9 > EOF
10
10
11
11
12 $ hg init a
12 $ hg init a
13 $ cd a
13 $ cd a
14
14
15 $ mkdir d
15 $ mkdir d
16 $ echo a > a
16 $ echo a > a
17 $ hg ci -Am A
17 $ hg ci -Am A
18 adding a
18 adding a
19
19
20 $ echo b > d/b
20 $ echo b > d/b
21 $ hg ci -Am B
21 $ hg ci -Am B
22 adding d/b
22 adding d/b
23
23
24 $ hg mv d d-renamed
24 $ hg mv d d-renamed
25 moving d/b to d-renamed/b
25 moving d/b to d-renamed/b (glob)
26 $ hg ci -m 'rename B'
26 $ hg ci -m 'rename B'
27
27
28 $ hg up -q -C 1
28 $ hg up -q -C 1
29
29
30 $ hg mv a a-renamed
30 $ hg mv a a-renamed
31 $ echo x > d/x
31 $ echo x > d/x
32 $ hg add d/x
32 $ hg add d/x
33
33
34 $ hg ci -m 'rename A'
34 $ hg ci -m 'rename A'
35 created new head
35 created new head
36
36
37 $ hg tglog
37 $ hg tglog
38 @ 3: 'rename A'
38 @ 3: 'rename A'
39 |
39 |
40 | o 2: 'rename B'
40 | o 2: 'rename B'
41 |/
41 |/
42 o 1: 'B'
42 o 1: 'B'
43 |
43 |
44 o 0: 'A'
44 o 0: 'A'
45
45
46
46
47 Rename is tracked:
47 Rename is tracked:
48
48
49 $ hg tlog -p --git -r tip
49 $ hg tlog -p --git -r tip
50 3: 'rename A'
50 3: 'rename A'
51 diff --git a/a b/a-renamed
51 diff --git a/a b/a-renamed
52 rename from a
52 rename from a
53 rename to a-renamed
53 rename to a-renamed
54 diff --git a/d/x b/d/x
54 diff --git a/d/x b/d/x
55 new file mode 100644
55 new file mode 100644
56 --- /dev/null
56 --- /dev/null
57 +++ b/d/x
57 +++ b/d/x
58 @@ -0,0 +1,1 @@
58 @@ -0,0 +1,1 @@
59 +x
59 +x
60
60
61 Rebase the revision containing the rename:
61 Rebase the revision containing the rename:
62
62
63 $ hg rebase -s 3 -d 2
63 $ hg rebase -s 3 -d 2
64 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
64 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
65
65
66 $ hg tglog
66 $ hg tglog
67 @ 3: 'rename A'
67 @ 3: 'rename A'
68 |
68 |
69 o 2: 'rename B'
69 o 2: 'rename B'
70 |
70 |
71 o 1: 'B'
71 o 1: 'B'
72 |
72 |
73 o 0: 'A'
73 o 0: 'A'
74
74
75
75
76 Rename is not lost:
76 Rename is not lost:
77
77
78 $ hg tlog -p --git -r tip
78 $ hg tlog -p --git -r tip
79 3: 'rename A'
79 3: 'rename A'
80 diff --git a/a b/a-renamed
80 diff --git a/a b/a-renamed
81 rename from a
81 rename from a
82 rename to a-renamed
82 rename to a-renamed
83 diff --git a/d-renamed/x b/d-renamed/x
83 diff --git a/d-renamed/x b/d-renamed/x
84 new file mode 100644
84 new file mode 100644
85 --- /dev/null
85 --- /dev/null
86 +++ b/d-renamed/x
86 +++ b/d-renamed/x
87 @@ -0,0 +1,1 @@
87 @@ -0,0 +1,1 @@
88 +x
88 +x
89
89
90
90
91 Rebased revision does not contain information about b (issue3739)
91 Rebased revision does not contain information about b (issue3739)
92
92
93 $ hg log -r 3 --debug
93 $ hg log -r 3 --debug
94 changeset: 3:032a9b75e83bff1dcfb6cbfa4ef50a704bf1b569
94 changeset: 3:032a9b75e83bff1dcfb6cbfa4ef50a704bf1b569
95 tag: tip
95 tag: tip
96 phase: draft
96 phase: draft
97 parent: 2:220d0626d185f372d9d8f69d9c73b0811d7725f7
97 parent: 2:220d0626d185f372d9d8f69d9c73b0811d7725f7
98 parent: -1:0000000000000000000000000000000000000000
98 parent: -1:0000000000000000000000000000000000000000
99 manifest: 3:035d66b27a1b06b2d12b46d41a39adb7a200c370
99 manifest: 3:035d66b27a1b06b2d12b46d41a39adb7a200c370
100 user: test
100 user: test
101 date: Thu Jan 01 00:00:00 1970 +0000
101 date: Thu Jan 01 00:00:00 1970 +0000
102 files+: a-renamed d-renamed/x
102 files+: a-renamed d-renamed/x
103 files-: a
103 files-: a
104 extra: branch=default
104 extra: branch=default
105 extra: rebase_source=73a3ee40125d6f0f347082e5831ceccb3f005f8a
105 extra: rebase_source=73a3ee40125d6f0f347082e5831ceccb3f005f8a
106 description:
106 description:
107 rename A
107 rename A
108
108
109
109
110
110
111 $ cd ..
111 $ cd ..
112
112
113
113
114 $ hg init b
114 $ hg init b
115 $ cd b
115 $ cd b
116
116
117 $ echo a > a
117 $ echo a > a
118 $ hg ci -Am A
118 $ hg ci -Am A
119 adding a
119 adding a
120
120
121 $ echo b > b
121 $ echo b > b
122 $ hg ci -Am B
122 $ hg ci -Am B
123 adding b
123 adding b
124
124
125 $ hg cp b b-copied
125 $ hg cp b b-copied
126 $ hg ci -Am 'copy B'
126 $ hg ci -Am 'copy B'
127
127
128 $ hg up -q -C 1
128 $ hg up -q -C 1
129
129
130 $ hg cp a a-copied
130 $ hg cp a a-copied
131 $ hg ci -m 'copy A'
131 $ hg ci -m 'copy A'
132 created new head
132 created new head
133
133
134 $ hg tglog
134 $ hg tglog
135 @ 3: 'copy A'
135 @ 3: 'copy A'
136 |
136 |
137 | o 2: 'copy B'
137 | o 2: 'copy B'
138 |/
138 |/
139 o 1: 'B'
139 o 1: 'B'
140 |
140 |
141 o 0: 'A'
141 o 0: 'A'
142
142
143 Copy is tracked:
143 Copy is tracked:
144
144
145 $ hg tlog -p --git -r tip
145 $ hg tlog -p --git -r tip
146 3: 'copy A'
146 3: 'copy A'
147 diff --git a/a b/a-copied
147 diff --git a/a b/a-copied
148 copy from a
148 copy from a
149 copy to a-copied
149 copy to a-copied
150
150
151 Rebase the revision containing the copy:
151 Rebase the revision containing the copy:
152
152
153 $ hg rebase -s 3 -d 2
153 $ hg rebase -s 3 -d 2
154 saved backup bundle to $TESTTMP/b/.hg/strip-backup/*-backup.hg (glob)
154 saved backup bundle to $TESTTMP/b/.hg/strip-backup/*-backup.hg (glob)
155
155
156 $ hg tglog
156 $ hg tglog
157 @ 3: 'copy A'
157 @ 3: 'copy A'
158 |
158 |
159 o 2: 'copy B'
159 o 2: 'copy B'
160 |
160 |
161 o 1: 'B'
161 o 1: 'B'
162 |
162 |
163 o 0: 'A'
163 o 0: 'A'
164
164
165
165
166 Copy is not lost:
166 Copy is not lost:
167
167
168 $ hg tlog -p --git -r tip
168 $ hg tlog -p --git -r tip
169 3: 'copy A'
169 3: 'copy A'
170 diff --git a/a b/a-copied
170 diff --git a/a b/a-copied
171 copy from a
171 copy from a
172 copy to a-copied
172 copy to a-copied
173
173
174
174
175 Rebased revision does not contain information about b (issue3739)
175 Rebased revision does not contain information about b (issue3739)
176
176
177 $ hg log -r 3 --debug
177 $ hg log -r 3 --debug
178 changeset: 3:98f6e6dbf45ab54079c2237fbd11066a5c41a11d
178 changeset: 3:98f6e6dbf45ab54079c2237fbd11066a5c41a11d
179 tag: tip
179 tag: tip
180 phase: draft
180 phase: draft
181 parent: 2:39e588434882ff77d01229d169cdc77f29e8855e
181 parent: 2:39e588434882ff77d01229d169cdc77f29e8855e
182 parent: -1:0000000000000000000000000000000000000000
182 parent: -1:0000000000000000000000000000000000000000
183 manifest: 3:2232f329d66fffe3930d43479ae624f66322b04d
183 manifest: 3:2232f329d66fffe3930d43479ae624f66322b04d
184 user: test
184 user: test
185 date: Thu Jan 01 00:00:00 1970 +0000
185 date: Thu Jan 01 00:00:00 1970 +0000
186 files+: a-copied
186 files+: a-copied
187 extra: branch=default
187 extra: branch=default
188 extra: rebase_source=0a8162ff18a8900df8df8ef7ac0046955205613e
188 extra: rebase_source=0a8162ff18a8900df8df8ef7ac0046955205613e
189 description:
189 description:
190 copy A
190 copy A
191
191
192
192
193
193
194 $ cd ..
194 $ cd ..
195
195
196
196
197 Test rebase across repeating renames:
197 Test rebase across repeating renames:
198
198
199 $ hg init repo
199 $ hg init repo
200
200
201 $ cd repo
201 $ cd repo
202
202
203 $ echo testing > file1.txt
203 $ echo testing > file1.txt
204 $ hg add file1.txt
204 $ hg add file1.txt
205 $ hg ci -m "Adding file1"
205 $ hg ci -m "Adding file1"
206
206
207 $ hg rename file1.txt file2.txt
207 $ hg rename file1.txt file2.txt
208 $ hg ci -m "Rename file1 to file2"
208 $ hg ci -m "Rename file1 to file2"
209
209
210 $ echo Unrelated change > unrelated.txt
210 $ echo Unrelated change > unrelated.txt
211 $ hg add unrelated.txt
211 $ hg add unrelated.txt
212 $ hg ci -m "Unrelated change"
212 $ hg ci -m "Unrelated change"
213
213
214 $ hg rename file2.txt file1.txt
214 $ hg rename file2.txt file1.txt
215 $ hg ci -m "Rename file2 back to file1"
215 $ hg ci -m "Rename file2 back to file1"
216
216
217 $ hg update -r -2
217 $ hg update -r -2
218 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
218 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
219
219
220 $ echo Another unrelated change >> unrelated.txt
220 $ echo Another unrelated change >> unrelated.txt
221 $ hg ci -m "Another unrelated change"
221 $ hg ci -m "Another unrelated change"
222 created new head
222 created new head
223
223
224 $ hg tglog
224 $ hg tglog
225 @ 4: 'Another unrelated change'
225 @ 4: 'Another unrelated change'
226 |
226 |
227 | o 3: 'Rename file2 back to file1'
227 | o 3: 'Rename file2 back to file1'
228 |/
228 |/
229 o 2: 'Unrelated change'
229 o 2: 'Unrelated change'
230 |
230 |
231 o 1: 'Rename file1 to file2'
231 o 1: 'Rename file1 to file2'
232 |
232 |
233 o 0: 'Adding file1'
233 o 0: 'Adding file1'
234
234
235
235
236 $ hg rebase -s 4 -d 3
236 $ hg rebase -s 4 -d 3
237 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-backup.hg (glob)
237 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-backup.hg (glob)
238
238
239 $ hg diff --stat -c .
239 $ hg diff --stat -c .
240 unrelated.txt | 1 +
240 unrelated.txt | 1 +
241 1 files changed, 1 insertions(+), 0 deletions(-)
241 1 files changed, 1 insertions(+), 0 deletions(-)
242
242
243 $ cd ..
243 $ cd ..
@@ -1,157 +1,157 b''
1 $ hg init t
1 $ hg init t
2 $ cd t
2 $ cd t
3
3
4 $ mkdir a
4 $ mkdir a
5 $ echo foo > a/a
5 $ echo foo > a/a
6 $ echo bar > a/b
6 $ echo bar > a/b
7 $ hg ci -Am "0"
7 $ hg ci -Am "0"
8 adding a/a
8 adding a/a
9 adding a/b
9 adding a/b
10
10
11 $ hg co -C 0
11 $ hg co -C 0
12 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
12 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
13 $ hg mv a b
13 $ hg mv a b
14 moving a/a to b/a (glob)
14 moving a/a to b/a (glob)
15 moving a/b to b/b (glob)
15 moving a/b to b/b (glob)
16 $ hg ci -m "1 mv a/ b/"
16 $ hg ci -m "1 mv a/ b/"
17
17
18 $ hg co -C 0
18 $ hg co -C 0
19 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
19 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
20 $ echo baz > a/c
20 $ echo baz > a/c
21 $ echo quux > a/d
21 $ echo quux > a/d
22 $ hg add a/c
22 $ hg add a/c
23 $ hg ci -m "2 add a/c"
23 $ hg ci -m "2 add a/c"
24 created new head
24 created new head
25
25
26 $ hg merge --debug 1
26 $ hg merge --debug 1
27 searching for copies back to rev 1
27 searching for copies back to rev 1
28 unmatched files in local:
28 unmatched files in local:
29 a/c
29 a/c
30 unmatched files in other:
30 unmatched files in other:
31 b/a
31 b/a
32 b/b
32 b/b
33 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
33 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
34 src: 'a/a' -> dst: 'b/a'
34 src: 'a/a' -> dst: 'b/a'
35 src: 'a/b' -> dst: 'b/b'
35 src: 'a/b' -> dst: 'b/b'
36 checking for directory renames
36 checking for directory renames
37 discovered dir src: 'a/' -> dst: 'b/'
37 discovered dir src: 'a/' -> dst: 'b/'
38 pending file src: 'a/c' -> dst: 'b/c'
38 pending file src: 'a/c' -> dst: 'b/c'
39 resolving manifests
39 resolving manifests
40 branchmerge: True, force: False, partial: False
40 branchmerge: True, force: False, partial: False
41 ancestor: f9b20c0d4c51, local: ce36d17b18fb+, remote: 397f8b00a740
41 ancestor: f9b20c0d4c51, local: ce36d17b18fb+, remote: 397f8b00a740
42 a/a: other deleted -> r
42 a/a: other deleted -> r
43 a/b: other deleted -> r
43 a/b: other deleted -> r
44 a/c: remote renamed directory to b/c -> d
44 a/c: remote renamed directory to b/c -> d
45 b/a: remote created -> g
45 b/a: remote created -> g
46 b/b: remote created -> g
46 b/b: remote created -> g
47 removing a/a
47 removing a/a
48 removing a/b
48 removing a/b
49 updating: a/b 2/5 files (40.00%)
49 updating: a/b 2/5 files (40.00%)
50 getting b/a
50 getting b/a
51 getting b/b
51 getting b/b
52 updating: b/b 4/5 files (80.00%)
52 updating: b/b 4/5 files (80.00%)
53 updating: a/c 5/5 files (100.00%)
53 updating: a/c 5/5 files (100.00%)
54 moving a/c to b/c
54 moving a/c to b/c (glob)
55 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
55 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
56 (branch merge, don't forget to commit)
56 (branch merge, don't forget to commit)
57
57
58 $ echo a/* b/*
58 $ echo a/* b/*
59 a/d b/a b/b b/c
59 a/d b/a b/b b/c
60 $ hg st -C
60 $ hg st -C
61 M b/a
61 M b/a
62 M b/b
62 M b/b
63 A b/c
63 A b/c
64 a/c
64 a/c
65 R a/a
65 R a/a
66 R a/b
66 R a/b
67 R a/c
67 R a/c
68 ? a/d
68 ? a/d
69 $ hg ci -m "3 merge 2+1"
69 $ hg ci -m "3 merge 2+1"
70 $ hg debugrename b/c
70 $ hg debugrename b/c
71 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88 (glob)
71 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88 (glob)
72
72
73 $ hg co -C 1
73 $ hg co -C 1
74 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
74 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
75 $ hg merge --debug 2
75 $ hg merge --debug 2
76 searching for copies back to rev 1
76 searching for copies back to rev 1
77 unmatched files in local:
77 unmatched files in local:
78 b/a
78 b/a
79 b/b
79 b/b
80 unmatched files in other:
80 unmatched files in other:
81 a/c
81 a/c
82 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
82 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
83 src: 'a/a' -> dst: 'b/a'
83 src: 'a/a' -> dst: 'b/a'
84 src: 'a/b' -> dst: 'b/b'
84 src: 'a/b' -> dst: 'b/b'
85 checking for directory renames
85 checking for directory renames
86 discovered dir src: 'a/' -> dst: 'b/'
86 discovered dir src: 'a/' -> dst: 'b/'
87 pending file src: 'a/c' -> dst: 'b/c'
87 pending file src: 'a/c' -> dst: 'b/c'
88 resolving manifests
88 resolving manifests
89 branchmerge: True, force: False, partial: False
89 branchmerge: True, force: False, partial: False
90 ancestor: f9b20c0d4c51, local: 397f8b00a740+, remote: ce36d17b18fb
90 ancestor: f9b20c0d4c51, local: 397f8b00a740+, remote: ce36d17b18fb
91 None: local renamed directory to b/c -> d
91 None: local renamed directory to b/c -> d
92 updating:None 1/1 files (100.00%)
92 updating:None 1/1 files (100.00%)
93 getting a/c to b/c
93 getting a/c to b/c
94 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
94 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
95 (branch merge, don't forget to commit)
95 (branch merge, don't forget to commit)
96
96
97 $ echo a/* b/*
97 $ echo a/* b/*
98 a/d b/a b/b b/c
98 a/d b/a b/b b/c
99 $ hg st -C
99 $ hg st -C
100 A b/c
100 A b/c
101 a/c
101 a/c
102 ? a/d
102 ? a/d
103 $ hg ci -m "4 merge 1+2"
103 $ hg ci -m "4 merge 1+2"
104 created new head
104 created new head
105 $ hg debugrename b/c
105 $ hg debugrename b/c
106 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88 (glob)
106 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88 (glob)
107
107
108
108
109 Second scenario with two repos:
109 Second scenario with two repos:
110
110
111 $ cd ..
111 $ cd ..
112 $ hg init r1
112 $ hg init r1
113 $ cd r1
113 $ cd r1
114 $ mkdir a
114 $ mkdir a
115 $ echo foo > a/f
115 $ echo foo > a/f
116 $ hg add a
116 $ hg add a
117 adding a/f (glob)
117 adding a/f (glob)
118 $ hg ci -m "a/f == foo"
118 $ hg ci -m "a/f == foo"
119 $ cd ..
119 $ cd ..
120
120
121 $ hg clone r1 r2
121 $ hg clone r1 r2
122 updating to branch default
122 updating to branch default
123 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
123 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
124 $ cd r2
124 $ cd r2
125 $ hg mv a b
125 $ hg mv a b
126 moving a/f to b/f (glob)
126 moving a/f to b/f (glob)
127 $ echo foo1 > b/f
127 $ echo foo1 > b/f
128 $ hg ci -m" a -> b, b/f == foo1"
128 $ hg ci -m" a -> b, b/f == foo1"
129 $ cd ..
129 $ cd ..
130
130
131 $ cd r1
131 $ cd r1
132 $ mkdir a/aa
132 $ mkdir a/aa
133 $ echo bar > a/aa/g
133 $ echo bar > a/aa/g
134 $ hg add a/aa
134 $ hg add a/aa
135 adding a/aa/g (glob)
135 adding a/aa/g (glob)
136 $ hg ci -m "a/aa/g"
136 $ hg ci -m "a/aa/g"
137 $ hg pull ../r2
137 $ hg pull ../r2
138 pulling from ../r2
138 pulling from ../r2
139 searching for changes
139 searching for changes
140 adding changesets
140 adding changesets
141 adding manifests
141 adding manifests
142 adding file changes
142 adding file changes
143 added 1 changesets with 1 changes to 1 files (+1 heads)
143 added 1 changesets with 1 changes to 1 files (+1 heads)
144 (run 'hg heads' to see heads, 'hg merge' to merge)
144 (run 'hg heads' to see heads, 'hg merge' to merge)
145
145
146 $ hg merge
146 $ hg merge
147 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
147 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
148 (branch merge, don't forget to commit)
148 (branch merge, don't forget to commit)
149
149
150 $ hg st -C
150 $ hg st -C
151 M b/f
151 M b/f
152 A b/aa/g
152 A b/aa/g
153 a/aa/g
153 a/aa/g
154 R a/aa/g
154 R a/aa/g
155 R a/f
155 R a/f
156
156
157 $ cd ..
157 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now