##// END OF EJS Templates
tests: fix the check-code rule for testing non-existent files...
Matt Harbison -
r35463:e28dedf4 @4 default
parent child Browse files
Show More
@@ -1,737 +1,737 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 """style and portability checker for Mercurial
10 """style and portability checker for Mercurial
11
11
12 when a rule triggers wrong, do one of the following (prefer one from top):
12 when a rule triggers wrong, do one of the following (prefer one from top):
13 * do the work-around the rule suggests
13 * do the work-around the rule suggests
14 * doublecheck that it is a false match
14 * doublecheck that it is a false match
15 * improve the rule pattern
15 * improve the rule pattern
16 * add an ignore pattern to the rule (3rd arg) which matches your good line
16 * add an ignore pattern to the rule (3rd arg) which matches your good line
17 (you can append a short comment and match this, like: #re-raises)
17 (you can append a short comment and match this, like: #re-raises)
18 * change the pattern to a warning and list the exception in test-check-code-hg
18 * change the pattern to a warning and list the exception in test-check-code-hg
19 * ONLY use no--check-code for skipping entire files from external sources
19 * ONLY use no--check-code for skipping entire files from external sources
20 """
20 """
21
21
22 from __future__ import absolute_import, print_function
22 from __future__ import absolute_import, print_function
23 import glob
23 import glob
24 import keyword
24 import keyword
25 import optparse
25 import optparse
26 import os
26 import os
27 import re
27 import re
28 import sys
28 import sys
29 if sys.version_info[0] < 3:
29 if sys.version_info[0] < 3:
30 opentext = open
30 opentext = open
31 else:
31 else:
32 def opentext(f):
32 def opentext(f):
33 return open(f, encoding='ascii')
33 return open(f, encoding='ascii')
34 try:
34 try:
35 xrange
35 xrange
36 except NameError:
36 except NameError:
37 xrange = range
37 xrange = range
38 try:
38 try:
39 import re2
39 import re2
40 except ImportError:
40 except ImportError:
41 re2 = None
41 re2 = None
42
42
43 def compilere(pat, multiline=False):
43 def compilere(pat, multiline=False):
44 if multiline:
44 if multiline:
45 pat = '(?m)' + pat
45 pat = '(?m)' + pat
46 if re2:
46 if re2:
47 try:
47 try:
48 return re2.compile(pat)
48 return re2.compile(pat)
49 except re2.error:
49 except re2.error:
50 pass
50 pass
51 return re.compile(pat)
51 return re.compile(pat)
52
52
53 # check "rules depending on implementation of repquote()" in each
53 # check "rules depending on implementation of repquote()" in each
54 # patterns (especially pypats), before changing around repquote()
54 # patterns (especially pypats), before changing around repquote()
55 _repquotefixedmap = {' ': ' ', '\n': '\n', '.': 'p', ':': 'q',
55 _repquotefixedmap = {' ': ' ', '\n': '\n', '.': 'p', ':': 'q',
56 '%': '%', '\\': 'b', '*': 'A', '+': 'P', '-': 'M'}
56 '%': '%', '\\': 'b', '*': 'A', '+': 'P', '-': 'M'}
57 def _repquoteencodechr(i):
57 def _repquoteencodechr(i):
58 if i > 255:
58 if i > 255:
59 return 'u'
59 return 'u'
60 c = chr(i)
60 c = chr(i)
61 if c in _repquotefixedmap:
61 if c in _repquotefixedmap:
62 return _repquotefixedmap[c]
62 return _repquotefixedmap[c]
63 if c.isalpha():
63 if c.isalpha():
64 return 'x'
64 return 'x'
65 if c.isdigit():
65 if c.isdigit():
66 return 'n'
66 return 'n'
67 return 'o'
67 return 'o'
68 _repquotett = ''.join(_repquoteencodechr(i) for i in xrange(256))
68 _repquotett = ''.join(_repquoteencodechr(i) for i in xrange(256))
69
69
70 def repquote(m):
70 def repquote(m):
71 t = m.group('text')
71 t = m.group('text')
72 t = t.translate(_repquotett)
72 t = t.translate(_repquotett)
73 return m.group('quote') + t + m.group('quote')
73 return m.group('quote') + t + m.group('quote')
74
74
75 def reppython(m):
75 def reppython(m):
76 comment = m.group('comment')
76 comment = m.group('comment')
77 if comment:
77 if comment:
78 l = len(comment.rstrip())
78 l = len(comment.rstrip())
79 return "#" * l + comment[l:]
79 return "#" * l + comment[l:]
80 return repquote(m)
80 return repquote(m)
81
81
82 def repcomment(m):
82 def repcomment(m):
83 return m.group(1) + "#" * len(m.group(2))
83 return m.group(1) + "#" * len(m.group(2))
84
84
85 def repccomment(m):
85 def repccomment(m):
86 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
86 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
87 return m.group(1) + t + "*/"
87 return m.group(1) + t + "*/"
88
88
89 def repcallspaces(m):
89 def repcallspaces(m):
90 t = re.sub(r"\n\s+", "\n", m.group(2))
90 t = re.sub(r"\n\s+", "\n", m.group(2))
91 return m.group(1) + t
91 return m.group(1) + t
92
92
93 def repinclude(m):
93 def repinclude(m):
94 return m.group(1) + "<foo>"
94 return m.group(1) + "<foo>"
95
95
96 def rephere(m):
96 def rephere(m):
97 t = re.sub(r"\S", "x", m.group(2))
97 t = re.sub(r"\S", "x", m.group(2))
98 return m.group(1) + t
98 return m.group(1) + t
99
99
100
100
101 testpats = [
101 testpats = [
102 [
102 [
103 (r'\b(push|pop)d\b', "don't use 'pushd' or 'popd', use 'cd'"),
103 (r'\b(push|pop)d\b', "don't use 'pushd' or 'popd', use 'cd'"),
104 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"),
104 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"),
105 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
105 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
106 (r'(?<!hg )grep.* -a', "don't use 'grep -a', use in-line python"),
106 (r'(?<!hg )grep.* -a', "don't use 'grep -a', use in-line python"),
107 (r'sed.*-i', "don't use 'sed -i', use a temporary file"),
107 (r'sed.*-i', "don't use 'sed -i', use a temporary file"),
108 (r'\becho\b.*\\n', "don't use 'echo \\n', use printf"),
108 (r'\becho\b.*\\n', "don't use 'echo \\n', use printf"),
109 (r'echo -n', "don't use 'echo -n', use printf"),
109 (r'echo -n', "don't use 'echo -n', use printf"),
110 (r'(^|\|\s*)\bwc\b[^|]*$\n(?!.*\(re\))', "filter wc output"),
110 (r'(^|\|\s*)\bwc\b[^|]*$\n(?!.*\(re\))', "filter wc output"),
111 (r'head -c', "don't use 'head -c', use 'dd'"),
111 (r'head -c', "don't use 'head -c', use 'dd'"),
112 (r'tail -n', "don't use the '-n' option to tail, just use '-<num>'"),
112 (r'tail -n', "don't use the '-n' option to tail, just use '-<num>'"),
113 (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"),
113 (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"),
114 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
114 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
115 (r'printf.*[^\\]\\([1-9]|0\d)', r"don't use 'printf \NNN', use Python"),
115 (r'printf.*[^\\]\\([1-9]|0\d)', r"don't use 'printf \NNN', use Python"),
116 (r'printf.*[^\\]\\x', "don't use printf \\x, use Python"),
116 (r'printf.*[^\\]\\x', "don't use printf \\x, use Python"),
117 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
117 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
118 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
118 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
119 (r'\[[^\]]+==', '[ foo == bar ] is a bashism, use [ foo = bar ] instead'),
119 (r'\[[^\]]+==', '[ foo == bar ] is a bashism, use [ foo = bar ] instead'),
120 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
120 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
121 "use egrep for extended grep syntax"),
121 "use egrep for extended grep syntax"),
122 (r'(^|\|\s*)e?grep .*\\S', "don't use \\S in regular expression"),
122 (r'(^|\|\s*)e?grep .*\\S', "don't use \\S in regular expression"),
123 (r'(?<!!)/bin/', "don't use explicit paths for tools"),
123 (r'(?<!!)/bin/', "don't use explicit paths for tools"),
124 (r'#!.*/bash', "don't use bash in shebang, use sh"),
124 (r'#!.*/bash', "don't use bash in shebang, use sh"),
125 (r'[^\n]\Z', "no trailing newline"),
125 (r'[^\n]\Z', "no trailing newline"),
126 (r'export .*=', "don't export and assign at once"),
126 (r'export .*=', "don't export and assign at once"),
127 (r'^source\b', "don't use 'source', use '.'"),
127 (r'^source\b', "don't use 'source', use '.'"),
128 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
128 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
129 (r'\bls +[^|\n-]+ +-', "options to 'ls' must come before filenames"),
129 (r'\bls +[^|\n-]+ +-', "options to 'ls' must come before filenames"),
130 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
130 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
131 (r'^stop\(\)', "don't use 'stop' as a shell function name"),
131 (r'^stop\(\)', "don't use 'stop' as a shell function name"),
132 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"),
132 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"),
133 (r'\[\[\s+[^\]]*\]\]', "don't use '[[ ]]', use '[ ]'"),
133 (r'\[\[\s+[^\]]*\]\]', "don't use '[[ ]]', use '[ ]'"),
134 (r'^alias\b.*=', "don't use alias, use a function"),
134 (r'^alias\b.*=', "don't use alias, use a function"),
135 (r'if\s*!', "don't use '!' to negate exit status"),
135 (r'if\s*!', "don't use '!' to negate exit status"),
136 (r'/dev/u?random', "don't use entropy, use /dev/zero"),
136 (r'/dev/u?random', "don't use entropy, use /dev/zero"),
137 (r'do\s*true;\s*done', "don't use true as loop body, use sleep 0"),
137 (r'do\s*true;\s*done', "don't use true as loop body, use sleep 0"),
138 (r'sed (-e )?\'(\d+|/[^/]*/)i(?!\\\n)',
138 (r'sed (-e )?\'(\d+|/[^/]*/)i(?!\\\n)',
139 "put a backslash-escaped newline after sed 'i' command"),
139 "put a backslash-escaped newline after sed 'i' command"),
140 (r'^diff *-\w*[uU].*$\n(^ \$ |^$)', "prefix diff -u/-U with cmp"),
140 (r'^diff *-\w*[uU].*$\n(^ \$ |^$)', "prefix diff -u/-U with cmp"),
141 (r'^\s+(if)? diff *-\w*[uU]', "prefix diff -u/-U with cmp"),
141 (r'^\s+(if)? diff *-\w*[uU]', "prefix diff -u/-U with cmp"),
142 (r'[\s="`\']python\s(?!bindings)', "don't use 'python', use '$PYTHON'"),
142 (r'[\s="`\']python\s(?!bindings)', "don't use 'python', use '$PYTHON'"),
143 (r'seq ', "don't use 'seq', use $TESTDIR/seq.py"),
143 (r'seq ', "don't use 'seq', use $TESTDIR/seq.py"),
144 (r'\butil\.Abort\b', "directly use error.Abort"),
144 (r'\butil\.Abort\b', "directly use error.Abort"),
145 (r'\|&', "don't use |&, use 2>&1"),
145 (r'\|&', "don't use |&, use 2>&1"),
146 (r'\w = +\w', "only one space after = allowed"),
146 (r'\w = +\w', "only one space after = allowed"),
147 (r'\bsed\b.*[^\\]\\n', "don't use 'sed ... \\n', use a \\ and a newline"),
147 (r'\bsed\b.*[^\\]\\n', "don't use 'sed ... \\n', use a \\ and a newline"),
148 (r'env.*-u', "don't use 'env -u VAR', use 'unset VAR'"),
148 (r'env.*-u', "don't use 'env -u VAR', use 'unset VAR'"),
149 (r'cp.* -r ', "don't use 'cp -r', use 'cp -R'"),
149 (r'cp.* -r ', "don't use 'cp -r', use 'cp -R'"),
150 (r'grep.* -[ABC]', "don't use grep's context flags"),
150 (r'grep.* -[ABC]', "don't use grep's context flags"),
151 (r'find.*-printf',
151 (r'find.*-printf',
152 "don't use 'find -printf', it doesn't exist on BSD find(1)"),
152 "don't use 'find -printf', it doesn't exist on BSD find(1)"),
153 ],
153 ],
154 # warnings
154 # warnings
155 [
155 [
156 (r'^function', "don't use 'function', use old style"),
156 (r'^function', "don't use 'function', use old style"),
157 (r'^diff.*-\w*N', "don't use 'diff -N'"),
157 (r'^diff.*-\w*N', "don't use 'diff -N'"),
158 (r'\$PWD|\${PWD}', "don't use $PWD, use `pwd`"),
158 (r'\$PWD|\${PWD}', "don't use $PWD, use `pwd`"),
159 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"),
159 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"),
160 (r'kill (`|\$\()', "don't use kill, use killdaemons.py")
160 (r'kill (`|\$\()', "don't use kill, use killdaemons.py")
161 ]
161 ]
162 ]
162 ]
163
163
164 testfilters = [
164 testfilters = [
165 (r"( *)(#([^!][^\n]*\S)?)", repcomment),
165 (r"( *)(#([^!][^\n]*\S)?)", repcomment),
166 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
166 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
167 ]
167 ]
168
168
169 uprefix = r"^ \$ "
169 uprefix = r"^ \$ "
170 utestpats = [
170 utestpats = [
171 [
171 [
172 (r'^(\S.*|| [$>] \S.*)[ \t]\n', "trailing whitespace on non-output"),
172 (r'^(\S.*|| [$>] \S.*)[ \t]\n', "trailing whitespace on non-output"),
173 (uprefix + r'.*\|\s*sed[^|>\n]*\n',
173 (uprefix + r'.*\|\s*sed[^|>\n]*\n',
174 "use regex test output patterns instead of sed"),
174 "use regex test output patterns instead of sed"),
175 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
175 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
176 (uprefix + r'.*(?<!\[)\$\?', "explicit exit code checks unnecessary"),
176 (uprefix + r'.*(?<!\[)\$\?', "explicit exit code checks unnecessary"),
177 (uprefix + r'.*\|\| echo.*(fail|error)',
177 (uprefix + r'.*\|\| echo.*(fail|error)',
178 "explicit exit code checks unnecessary"),
178 "explicit exit code checks unnecessary"),
179 (uprefix + r'set -e', "don't use set -e"),
179 (uprefix + r'set -e', "don't use set -e"),
180 (uprefix + r'(\s|fi\b|done\b)', "use > for continued lines"),
180 (uprefix + r'(\s|fi\b|done\b)', "use > for continued lines"),
181 (uprefix + r'.*:\.\S*/', "x:.y in a path does not work on msys, rewrite "
181 (uprefix + r'.*:\.\S*/', "x:.y in a path does not work on msys, rewrite "
182 "as x://.y, or see `hg log -k msys` for alternatives", r'-\S+:\.|' #-Rxxx
182 "as x://.y, or see `hg log -k msys` for alternatives", r'-\S+:\.|' #-Rxxx
183 '# no-msys'), # in test-pull.t which is skipped on windows
183 '# no-msys'), # in test-pull.t which is skipped on windows
184 (r'^ [^$>].*27\.0\.0\.1',
184 (r'^ [^$>].*27\.0\.0\.1',
185 'use $LOCALIP not an explicit loopback address'),
185 'use $LOCALIP not an explicit loopback address'),
186 (r'^ (?![>$] ).*\$LOCALIP.*[^)]$',
186 (r'^ (?![>$] ).*\$LOCALIP.*[^)]$',
187 'mark $LOCALIP output lines with (glob) to help tests in BSD jails'),
187 'mark $LOCALIP output lines with (glob) to help tests in BSD jails'),
188 (r'^ (cat|find): .*: No such file or directory',
188 (r'^ (cat|find): .*: \$ENOENT\$',
189 'use test -f to test for file existence'),
189 'use test -f to test for file existence'),
190 (r'^ diff -[^ -]*p',
190 (r'^ diff -[^ -]*p',
191 "don't use (external) diff with -p for portability"),
191 "don't use (external) diff with -p for portability"),
192 (r' readlink ', 'use readlink.py instead of readlink'),
192 (r' readlink ', 'use readlink.py instead of readlink'),
193 (r'^ [-+][-+][-+] .* [-+]0000 \(glob\)',
193 (r'^ [-+][-+][-+] .* [-+]0000 \(glob\)',
194 "glob timezone field in diff output for portability"),
194 "glob timezone field in diff output for portability"),
195 (r'^ @@ -[0-9]+ [+][0-9]+,[0-9]+ @@',
195 (r'^ @@ -[0-9]+ [+][0-9]+,[0-9]+ @@',
196 "use '@@ -N* +N,n @@ (glob)' style chunk header for portability"),
196 "use '@@ -N* +N,n @@ (glob)' style chunk header for portability"),
197 (r'^ @@ -[0-9]+,[0-9]+ [+][0-9]+ @@',
197 (r'^ @@ -[0-9]+,[0-9]+ [+][0-9]+ @@',
198 "use '@@ -N,n +N* @@ (glob)' style chunk header for portability"),
198 "use '@@ -N,n +N* @@ (glob)' style chunk header for portability"),
199 (r'^ @@ -[0-9]+ [+][0-9]+ @@',
199 (r'^ @@ -[0-9]+ [+][0-9]+ @@',
200 "use '@@ -N* +N* @@ (glob)' style chunk header for portability"),
200 "use '@@ -N* +N* @@ (glob)' style chunk header for portability"),
201 (uprefix + r'hg( +-[^ ]+( +[^ ]+)?)* +extdiff'
201 (uprefix + r'hg( +-[^ ]+( +[^ ]+)?)* +extdiff'
202 r'( +(-[^ po-]+|--(?!program|option)[^ ]+|[^-][^ ]*))*$',
202 r'( +(-[^ po-]+|--(?!program|option)[^ ]+|[^-][^ ]*))*$',
203 "use $RUNTESTDIR/pdiff via extdiff (or -o/-p for false-positives)"),
203 "use $RUNTESTDIR/pdiff via extdiff (or -o/-p for false-positives)"),
204 ],
204 ],
205 # warnings
205 # warnings
206 [
206 [
207 (r'^ (?!.*\$LOCALIP)[^*?/\n]* \(glob\)$',
207 (r'^ (?!.*\$LOCALIP)[^*?/\n]* \(glob\)$',
208 "glob match with no glob string (?, *, /, and $LOCALIP)"),
208 "glob match with no glob string (?, *, /, and $LOCALIP)"),
209 ]
209 ]
210 ]
210 ]
211
211
212 # transform plain test rules to unified test's
212 # transform plain test rules to unified test's
213 for i in [0, 1]:
213 for i in [0, 1]:
214 for tp in testpats[i]:
214 for tp in testpats[i]:
215 p = tp[0]
215 p = tp[0]
216 m = tp[1]
216 m = tp[1]
217 if p.startswith(r'^'):
217 if p.startswith(r'^'):
218 p = r"^ [$>] (%s)" % p[1:]
218 p = r"^ [$>] (%s)" % p[1:]
219 else:
219 else:
220 p = r"^ [$>] .*(%s)" % p
220 p = r"^ [$>] .*(%s)" % p
221 utestpats[i].append((p, m) + tp[2:])
221 utestpats[i].append((p, m) + tp[2:])
222
222
223 # don't transform the following rules:
223 # don't transform the following rules:
224 # " > \t" and " \t" should be allowed in unified tests
224 # " > \t" and " \t" should be allowed in unified tests
225 testpats[0].append((r'^( *)\t', "don't use tabs to indent"))
225 testpats[0].append((r'^( *)\t', "don't use tabs to indent"))
226 utestpats[0].append((r'^( ?)\t', "don't use tabs to indent"))
226 utestpats[0].append((r'^( ?)\t', "don't use tabs to indent"))
227
227
228 utestfilters = [
228 utestfilters = [
229 (r"<<(\S+)((.|\n)*?\n > \1)", rephere),
229 (r"<<(\S+)((.|\n)*?\n > \1)", rephere),
230 (r"( +)(#([^!][^\n]*\S)?)", repcomment),
230 (r"( +)(#([^!][^\n]*\S)?)", repcomment),
231 ]
231 ]
232
232
233 pypats = [
233 pypats = [
234 [
234 [
235 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
235 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
236 "tuple parameter unpacking not available in Python 3+"),
236 "tuple parameter unpacking not available in Python 3+"),
237 (r'lambda\s*\(.*,.*\)',
237 (r'lambda\s*\(.*,.*\)',
238 "tuple parameter unpacking not available in Python 3+"),
238 "tuple parameter unpacking not available in Python 3+"),
239 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
239 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
240 (r'(?<!\.)\breduce\s*\(.*', "reduce is not available in Python 3+"),
240 (r'(?<!\.)\breduce\s*\(.*', "reduce is not available in Python 3+"),
241 (r'\bdict\(.*=', 'dict() is different in Py2 and 3 and is slower than {}',
241 (r'\bdict\(.*=', 'dict() is different in Py2 and 3 and is slower than {}',
242 'dict-from-generator'),
242 'dict-from-generator'),
243 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
243 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
244 (r'\s<>\s', '<> operator is not available in Python 3+, use !='),
244 (r'\s<>\s', '<> operator is not available in Python 3+, use !='),
245 (r'^\s*\t', "don't use tabs"),
245 (r'^\s*\t', "don't use tabs"),
246 (r'\S;\s*\n', "semicolon"),
246 (r'\S;\s*\n', "semicolon"),
247 (r'[^_]_\([ \t\n]*(?:"[^"]+"[ \t\n+]*)+%', "don't use % inside _()"),
247 (r'[^_]_\([ \t\n]*(?:"[^"]+"[ \t\n+]*)+%', "don't use % inside _()"),
248 (r"[^_]_\([ \t\n]*(?:'[^']+'[ \t\n+]*)+%", "don't use % inside _()"),
248 (r"[^_]_\([ \t\n]*(?:'[^']+'[ \t\n+]*)+%", "don't use % inside _()"),
249 (r'(\w|\)),\w', "missing whitespace after ,"),
249 (r'(\w|\)),\w', "missing whitespace after ,"),
250 (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"),
250 (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"),
251 (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"),
251 (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"),
252 (r'\w\s=\s\s+\w', "gratuitous whitespace after ="),
252 (r'\w\s=\s\s+\w', "gratuitous whitespace after ="),
253 ((
253 ((
254 # a line ending with a colon, potentially with trailing comments
254 # a line ending with a colon, potentially with trailing comments
255 r':([ \t]*#[^\n]*)?\n'
255 r':([ \t]*#[^\n]*)?\n'
256 # one that is not a pass and not only a comment
256 # one that is not a pass and not only a comment
257 r'(?P<indent>[ \t]+)[^#][^\n]+\n'
257 r'(?P<indent>[ \t]+)[^#][^\n]+\n'
258 # more lines at the same indent level
258 # more lines at the same indent level
259 r'((?P=indent)[^\n]+\n)*'
259 r'((?P=indent)[^\n]+\n)*'
260 # a pass at the same indent level, which is bogus
260 # a pass at the same indent level, which is bogus
261 r'(?P=indent)pass[ \t\n#]'
261 r'(?P=indent)pass[ \t\n#]'
262 ), 'omit superfluous pass'),
262 ), 'omit superfluous pass'),
263 (r'.{81}', "line too long"),
263 (r'.{81}', "line too long"),
264 (r'[^\n]\Z', "no trailing newline"),
264 (r'[^\n]\Z', "no trailing newline"),
265 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
265 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
266 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=',
266 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=',
267 # "don't use underbars in identifiers"),
267 # "don't use underbars in identifiers"),
268 (r'^\s+(self\.)?[A-Za-z][a-z0-9]+[A-Z]\w* = ',
268 (r'^\s+(self\.)?[A-Za-z][a-z0-9]+[A-Z]\w* = ',
269 "don't use camelcase in identifiers", r'#.*camelcase-required'),
269 "don't use camelcase in identifiers", r'#.*camelcase-required'),
270 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+',
270 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+',
271 "linebreak after :"),
271 "linebreak after :"),
272 (r'class\s[^( \n]+:', "old-style class, use class foo(object)",
272 (r'class\s[^( \n]+:', "old-style class, use class foo(object)",
273 r'#.*old-style'),
273 r'#.*old-style'),
274 (r'class\s[^( \n]+\(\):',
274 (r'class\s[^( \n]+\(\):',
275 "class foo() creates old style object, use class foo(object)",
275 "class foo() creates old style object, use class foo(object)",
276 r'#.*old-style'),
276 r'#.*old-style'),
277 (r'\b(%s)\(' % '|'.join(k for k in keyword.kwlist
277 (r'\b(%s)\(' % '|'.join(k for k in keyword.kwlist
278 if k not in ('print', 'exec')),
278 if k not in ('print', 'exec')),
279 "Python keyword is not a function"),
279 "Python keyword is not a function"),
280 (r',]', "unneeded trailing ',' in list"),
280 (r',]', "unneeded trailing ',' in list"),
281 # (r'class\s[A-Z][^\(]*\((?!Exception)',
281 # (r'class\s[A-Z][^\(]*\((?!Exception)',
282 # "don't capitalize non-exception classes"),
282 # "don't capitalize non-exception classes"),
283 # (r'in range\(', "use xrange"),
283 # (r'in range\(', "use xrange"),
284 # (r'^\s*print\s+', "avoid using print in core and extensions"),
284 # (r'^\s*print\s+', "avoid using print in core and extensions"),
285 (r'[\x80-\xff]', "non-ASCII character literal"),
285 (r'[\x80-\xff]', "non-ASCII character literal"),
286 (r'("\')\.format\(', "str.format() has no bytes counterpart, use %"),
286 (r'("\')\.format\(', "str.format() has no bytes counterpart, use %"),
287 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
287 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
288 "gratuitous whitespace after Python keyword"),
288 "gratuitous whitespace after Python keyword"),
289 (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"),
289 (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"),
290 # (r'\s\s=', "gratuitous whitespace before ="),
290 # (r'\s\s=', "gratuitous whitespace before ="),
291 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
291 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
292 "missing whitespace around operator"),
292 "missing whitespace around operator"),
293 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\s',
293 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\s',
294 "missing whitespace around operator"),
294 "missing whitespace around operator"),
295 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
295 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
296 "missing whitespace around operator"),
296 "missing whitespace around operator"),
297 (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]',
297 (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]',
298 "wrong whitespace around ="),
298 "wrong whitespace around ="),
299 (r'\([^()]*( =[^=]|[^<>!=]= )',
299 (r'\([^()]*( =[^=]|[^<>!=]= )',
300 "no whitespace around = for named parameters"),
300 "no whitespace around = for named parameters"),
301 (r'raise Exception', "don't raise generic exceptions"),
301 (r'raise Exception', "don't raise generic exceptions"),
302 (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$',
302 (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$',
303 "don't use old-style two-argument raise, use Exception(message)"),
303 "don't use old-style two-argument raise, use Exception(message)"),
304 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
304 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
305 (r' [=!]=\s+(True|False|None)',
305 (r' [=!]=\s+(True|False|None)',
306 "comparison with singleton, use 'is' or 'is not' instead"),
306 "comparison with singleton, use 'is' or 'is not' instead"),
307 (r'^\s*(while|if) [01]:',
307 (r'^\s*(while|if) [01]:',
308 "use True/False for constant Boolean expression"),
308 "use True/False for constant Boolean expression"),
309 (r'^\s*if False(:| +and)', 'Remove code instead of using `if False`'),
309 (r'^\s*if False(:| +and)', 'Remove code instead of using `if False`'),
310 (r'(?:(?<!def)\s+|\()hasattr\(',
310 (r'(?:(?<!def)\s+|\()hasattr\(',
311 'hasattr(foo, bar) is broken on py2, use util.safehasattr(foo, bar) '
311 'hasattr(foo, bar) is broken on py2, use util.safehasattr(foo, bar) '
312 'instead', r'#.*hasattr-py3-only'),
312 'instead', r'#.*hasattr-py3-only'),
313 (r'opener\([^)]*\).read\(',
313 (r'opener\([^)]*\).read\(',
314 "use opener.read() instead"),
314 "use opener.read() instead"),
315 (r'opener\([^)]*\).write\(',
315 (r'opener\([^)]*\).write\(',
316 "use opener.write() instead"),
316 "use opener.write() instead"),
317 (r'[\s\(](open|file)\([^)]*\)\.read\(',
317 (r'[\s\(](open|file)\([^)]*\)\.read\(',
318 "use util.readfile() instead"),
318 "use util.readfile() instead"),
319 (r'[\s\(](open|file)\([^)]*\)\.write\(',
319 (r'[\s\(](open|file)\([^)]*\)\.write\(',
320 "use util.writefile() instead"),
320 "use util.writefile() instead"),
321 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
321 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
322 "always assign an opened file to a variable, and close it afterwards"),
322 "always assign an opened file to a variable, and close it afterwards"),
323 (r'[\s\(](open|file)\([^)]*\)\.',
323 (r'[\s\(](open|file)\([^)]*\)\.',
324 "always assign an opened file to a variable, and close it afterwards"),
324 "always assign an opened file to a variable, and close it afterwards"),
325 (r'(?i)descend[e]nt', "the proper spelling is descendAnt"),
325 (r'(?i)descend[e]nt', "the proper spelling is descendAnt"),
326 (r'\.debug\(\_', "don't mark debug messages for translation"),
326 (r'\.debug\(\_', "don't mark debug messages for translation"),
327 (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"),
327 (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"),
328 (r'^\s*except\s*:', "naked except clause", r'#.*re-raises'),
328 (r'^\s*except\s*:', "naked except clause", r'#.*re-raises'),
329 (r'^\s*except\s([^\(,]+|\([^\)]+\))\s*,',
329 (r'^\s*except\s([^\(,]+|\([^\)]+\))\s*,',
330 'legacy exception syntax; use "as" instead of ","'),
330 'legacy exception syntax; use "as" instead of ","'),
331 (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"),
331 (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"),
332 (r'release\(.*wlock, .*lock\)', "wrong lock release order"),
332 (r'release\(.*wlock, .*lock\)', "wrong lock release order"),
333 (r'\bdef\s+__bool__\b', "__bool__ should be __nonzero__ in Python 2"),
333 (r'\bdef\s+__bool__\b', "__bool__ should be __nonzero__ in Python 2"),
334 (r'os\.path\.join\(.*, *(""|\'\')\)',
334 (r'os\.path\.join\(.*, *(""|\'\')\)',
335 "use pathutil.normasprefix(path) instead of os.path.join(path, '')"),
335 "use pathutil.normasprefix(path) instead of os.path.join(path, '')"),
336 (r'\s0[0-7]+\b', 'legacy octal syntax; use "0o" prefix instead of "0"'),
336 (r'\s0[0-7]+\b', 'legacy octal syntax; use "0o" prefix instead of "0"'),
337 # XXX only catch mutable arguments on the first line of the definition
337 # XXX only catch mutable arguments on the first line of the definition
338 (r'def.*[( ]\w+=\{\}', "don't use mutable default arguments"),
338 (r'def.*[( ]\w+=\{\}', "don't use mutable default arguments"),
339 (r'\butil\.Abort\b', "directly use error.Abort"),
339 (r'\butil\.Abort\b', "directly use error.Abort"),
340 (r'^@(\w*\.)?cachefunc', "module-level @cachefunc is risky, please avoid"),
340 (r'^@(\w*\.)?cachefunc', "module-level @cachefunc is risky, please avoid"),
341 (r'^import atexit', "don't use atexit, use ui.atexit"),
341 (r'^import atexit', "don't use atexit, use ui.atexit"),
342 (r'^import Queue', "don't use Queue, use util.queue + util.empty"),
342 (r'^import Queue', "don't use Queue, use util.queue + util.empty"),
343 (r'^import cStringIO', "don't use cStringIO.StringIO, use util.stringio"),
343 (r'^import cStringIO', "don't use cStringIO.StringIO, use util.stringio"),
344 (r'^import urllib', "don't use urllib, use util.urlreq/util.urlerr"),
344 (r'^import urllib', "don't use urllib, use util.urlreq/util.urlerr"),
345 (r'^import SocketServer', "don't use SockerServer, use util.socketserver"),
345 (r'^import SocketServer', "don't use SockerServer, use util.socketserver"),
346 (r'^import urlparse', "don't use urlparse, use util.urlreq"),
346 (r'^import urlparse', "don't use urlparse, use util.urlreq"),
347 (r'^import xmlrpclib', "don't use xmlrpclib, use util.xmlrpclib"),
347 (r'^import xmlrpclib', "don't use xmlrpclib, use util.xmlrpclib"),
348 (r'^import cPickle', "don't use cPickle, use util.pickle"),
348 (r'^import cPickle', "don't use cPickle, use util.pickle"),
349 (r'^import pickle', "don't use pickle, use util.pickle"),
349 (r'^import pickle', "don't use pickle, use util.pickle"),
350 (r'^import httplib', "don't use httplib, use util.httplib"),
350 (r'^import httplib', "don't use httplib, use util.httplib"),
351 (r'^import BaseHTTPServer', "use util.httpserver instead"),
351 (r'^import BaseHTTPServer', "use util.httpserver instead"),
352 (r'^(from|import) mercurial\.(cext|pure|cffi)',
352 (r'^(from|import) mercurial\.(cext|pure|cffi)',
353 "use mercurial.policy.importmod instead"),
353 "use mercurial.policy.importmod instead"),
354 (r'\.next\(\)', "don't use .next(), use next(...)"),
354 (r'\.next\(\)', "don't use .next(), use next(...)"),
355 (r'([a-z]*).revision\(\1\.node\(',
355 (r'([a-z]*).revision\(\1\.node\(',
356 "don't convert rev to node before passing to revision(nodeorrev)"),
356 "don't convert rev to node before passing to revision(nodeorrev)"),
357 (r'platform\.system\(\)', "don't use platform.system(), use pycompat"),
357 (r'platform\.system\(\)', "don't use platform.system(), use pycompat"),
358
358
359 # rules depending on implementation of repquote()
359 # rules depending on implementation of repquote()
360 (r' x+[xpqo%APM][\'"]\n\s+[\'"]x',
360 (r' x+[xpqo%APM][\'"]\n\s+[\'"]x',
361 'string join across lines with no space'),
361 'string join across lines with no space'),
362 (r'''(?x)ui\.(status|progress|write|note|warn)\(
362 (r'''(?x)ui\.(status|progress|write|note|warn)\(
363 [ \t\n#]*
363 [ \t\n#]*
364 (?# any strings/comments might precede a string, which
364 (?# any strings/comments might precede a string, which
365 # contains translatable message)
365 # contains translatable message)
366 ((['"]|\'\'\'|""")[ \npq%bAPMxno]*(['"]|\'\'\'|""")[ \t\n#]+)*
366 ((['"]|\'\'\'|""")[ \npq%bAPMxno]*(['"]|\'\'\'|""")[ \t\n#]+)*
367 (?# sequence consisting of below might precede translatable message
367 (?# sequence consisting of below might precede translatable message
368 # - formatting string: "% 10s", "%05d", "% -3.2f", "%*s", "%%" ...
368 # - formatting string: "% 10s", "%05d", "% -3.2f", "%*s", "%%" ...
369 # - escaped character: "\\", "\n", "\0" ...
369 # - escaped character: "\\", "\n", "\0" ...
370 # - character other than '%', 'b' as '\', and 'x' as alphabet)
370 # - character other than '%', 'b' as '\', and 'x' as alphabet)
371 (['"]|\'\'\'|""")
371 (['"]|\'\'\'|""")
372 ((%([ n]?[PM]?([np]+|A))?x)|%%|b[bnx]|[ \nnpqAPMo])*x
372 ((%([ n]?[PM]?([np]+|A))?x)|%%|b[bnx]|[ \nnpqAPMo])*x
373 (?# this regexp can't use [^...] style,
373 (?# this regexp can't use [^...] style,
374 # because _preparepats forcibly adds "\n" into [^...],
374 # because _preparepats forcibly adds "\n" into [^...],
375 # even though this regexp wants match it against "\n")''',
375 # even though this regexp wants match it against "\n")''',
376 "missing _() in ui message (use () to hide false-positives)"),
376 "missing _() in ui message (use () to hide false-positives)"),
377 ],
377 ],
378 # warnings
378 # warnings
379 [
379 [
380 # rules depending on implementation of repquote()
380 # rules depending on implementation of repquote()
381 (r'(^| )pp +xxxxqq[ \n][^\n]', "add two newlines after '.. note::'"),
381 (r'(^| )pp +xxxxqq[ \n][^\n]', "add two newlines after '.. note::'"),
382 ]
382 ]
383 ]
383 ]
384
384
385 pyfilters = [
385 pyfilters = [
386 (r"""(?msx)(?P<comment>\#.*?$)|
386 (r"""(?msx)(?P<comment>\#.*?$)|
387 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
387 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
388 (?P<text>(([^\\]|\\.)*?))
388 (?P<text>(([^\\]|\\.)*?))
389 (?P=quote))""", reppython),
389 (?P=quote))""", reppython),
390 ]
390 ]
391
391
392 # non-filter patterns
392 # non-filter patterns
393 pynfpats = [
393 pynfpats = [
394 [
394 [
395 (r'pycompat\.osname\s*[=!]=\s*[\'"]nt[\'"]', "use pycompat.iswindows"),
395 (r'pycompat\.osname\s*[=!]=\s*[\'"]nt[\'"]', "use pycompat.iswindows"),
396 (r'pycompat\.osname\s*[=!]=\s*[\'"]posix[\'"]', "use pycompat.isposix"),
396 (r'pycompat\.osname\s*[=!]=\s*[\'"]posix[\'"]', "use pycompat.isposix"),
397 (r'pycompat\.sysplatform\s*[!=]=\s*[\'"]darwin[\'"]',
397 (r'pycompat\.sysplatform\s*[!=]=\s*[\'"]darwin[\'"]',
398 "use pycompat.isdarwin"),
398 "use pycompat.isdarwin"),
399 ],
399 ],
400 # warnings
400 # warnings
401 [],
401 [],
402 ]
402 ]
403
403
404 # extension non-filter patterns
404 # extension non-filter patterns
405 pyextnfpats = [
405 pyextnfpats = [
406 [(r'^"""\n?[A-Z]', "don't capitalize docstring title")],
406 [(r'^"""\n?[A-Z]', "don't capitalize docstring title")],
407 # warnings
407 # warnings
408 [],
408 [],
409 ]
409 ]
410
410
411 txtfilters = []
411 txtfilters = []
412
412
413 txtpats = [
413 txtpats = [
414 [
414 [
415 ('\s$', 'trailing whitespace'),
415 ('\s$', 'trailing whitespace'),
416 ('.. note::[ \n][^\n]', 'add two newlines after note::')
416 ('.. note::[ \n][^\n]', 'add two newlines after note::')
417 ],
417 ],
418 []
418 []
419 ]
419 ]
420
420
421 cpats = [
421 cpats = [
422 [
422 [
423 (r'//', "don't use //-style comments"),
423 (r'//', "don't use //-style comments"),
424 (r'\S\t', "don't use tabs except for indent"),
424 (r'\S\t', "don't use tabs except for indent"),
425 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
425 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
426 (r'.{81}', "line too long"),
426 (r'.{81}', "line too long"),
427 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
427 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
428 (r'return\(', "return is not a function"),
428 (r'return\(', "return is not a function"),
429 (r' ;', "no space before ;"),
429 (r' ;', "no space before ;"),
430 (r'[^;] \)', "no space before )"),
430 (r'[^;] \)', "no space before )"),
431 (r'[)][{]', "space between ) and {"),
431 (r'[)][{]', "space between ) and {"),
432 (r'\w+\* \w+', "use int *foo, not int* foo"),
432 (r'\w+\* \w+', "use int *foo, not int* foo"),
433 (r'\W\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
433 (r'\W\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
434 (r'\w+ (\+\+|--)', "use foo++, not foo ++"),
434 (r'\w+ (\+\+|--)', "use foo++, not foo ++"),
435 (r'\w,\w', "missing whitespace after ,"),
435 (r'\w,\w', "missing whitespace after ,"),
436 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
436 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
437 (r'\w\s=\s\s+\w', "gratuitous whitespace after ="),
437 (r'\w\s=\s\s+\w', "gratuitous whitespace after ="),
438 (r'^#\s+\w', "use #foo, not # foo"),
438 (r'^#\s+\w', "use #foo, not # foo"),
439 (r'[^\n]\Z', "no trailing newline"),
439 (r'[^\n]\Z', "no trailing newline"),
440 (r'^\s*#import\b', "use only #include in standard C code"),
440 (r'^\s*#import\b', "use only #include in standard C code"),
441 (r'strcpy\(', "don't use strcpy, use strlcpy or memcpy"),
441 (r'strcpy\(', "don't use strcpy, use strlcpy or memcpy"),
442 (r'strcat\(', "don't use strcat"),
442 (r'strcat\(', "don't use strcat"),
443
443
444 # rules depending on implementation of repquote()
444 # rules depending on implementation of repquote()
445 ],
445 ],
446 # warnings
446 # warnings
447 [
447 [
448 # rules depending on implementation of repquote()
448 # rules depending on implementation of repquote()
449 ]
449 ]
450 ]
450 ]
451
451
452 cfilters = [
452 cfilters = [
453 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
453 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
454 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
454 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
455 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
455 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
456 (r'(\()([^)]+\))', repcallspaces),
456 (r'(\()([^)]+\))', repcallspaces),
457 ]
457 ]
458
458
459 inutilpats = [
459 inutilpats = [
460 [
460 [
461 (r'\bui\.', "don't use ui in util"),
461 (r'\bui\.', "don't use ui in util"),
462 ],
462 ],
463 # warnings
463 # warnings
464 []
464 []
465 ]
465 ]
466
466
467 inrevlogpats = [
467 inrevlogpats = [
468 [
468 [
469 (r'\brepo\.', "don't use repo in revlog"),
469 (r'\brepo\.', "don't use repo in revlog"),
470 ],
470 ],
471 # warnings
471 # warnings
472 []
472 []
473 ]
473 ]
474
474
475 webtemplatefilters = []
475 webtemplatefilters = []
476
476
477 webtemplatepats = [
477 webtemplatepats = [
478 [],
478 [],
479 [
479 [
480 (r'{desc(\|(?!websub|firstline)[^\|]*)+}',
480 (r'{desc(\|(?!websub|firstline)[^\|]*)+}',
481 'follow desc keyword with either firstline or websub'),
481 'follow desc keyword with either firstline or websub'),
482 ]
482 ]
483 ]
483 ]
484
484
485 allfilesfilters = []
485 allfilesfilters = []
486
486
487 allfilespats = [
487 allfilespats = [
488 [
488 [
489 (r'(http|https)://[a-zA-Z0-9./]*selenic.com/',
489 (r'(http|https)://[a-zA-Z0-9./]*selenic.com/',
490 'use mercurial-scm.org domain URL'),
490 'use mercurial-scm.org domain URL'),
491 (r'mercurial@selenic\.com',
491 (r'mercurial@selenic\.com',
492 'use mercurial-scm.org domain for mercurial ML address'),
492 'use mercurial-scm.org domain for mercurial ML address'),
493 (r'mercurial-devel@selenic\.com',
493 (r'mercurial-devel@selenic\.com',
494 'use mercurial-scm.org domain for mercurial-devel ML address'),
494 'use mercurial-scm.org domain for mercurial-devel ML address'),
495 ],
495 ],
496 # warnings
496 # warnings
497 [],
497 [],
498 ]
498 ]
499
499
500 py3pats = [
500 py3pats = [
501 [
501 [
502 (r'os\.environ', "use encoding.environ instead (py3)", r'#.*re-exports'),
502 (r'os\.environ', "use encoding.environ instead (py3)", r'#.*re-exports'),
503 (r'os\.name', "use pycompat.osname instead (py3)"),
503 (r'os\.name', "use pycompat.osname instead (py3)"),
504 (r'os\.getcwd', "use pycompat.getcwd instead (py3)"),
504 (r'os\.getcwd', "use pycompat.getcwd instead (py3)"),
505 (r'os\.sep', "use pycompat.ossep instead (py3)"),
505 (r'os\.sep', "use pycompat.ossep instead (py3)"),
506 (r'os\.pathsep', "use pycompat.ospathsep instead (py3)"),
506 (r'os\.pathsep', "use pycompat.ospathsep instead (py3)"),
507 (r'os\.altsep', "use pycompat.osaltsep instead (py3)"),
507 (r'os\.altsep', "use pycompat.osaltsep instead (py3)"),
508 (r'sys\.platform', "use pycompat.sysplatform instead (py3)"),
508 (r'sys\.platform', "use pycompat.sysplatform instead (py3)"),
509 (r'getopt\.getopt', "use pycompat.getoptb instead (py3)"),
509 (r'getopt\.getopt', "use pycompat.getoptb instead (py3)"),
510 (r'os\.getenv', "use encoding.environ.get instead"),
510 (r'os\.getenv', "use encoding.environ.get instead"),
511 (r'os\.setenv', "modifying the environ dict is not preferred"),
511 (r'os\.setenv', "modifying the environ dict is not preferred"),
512 ],
512 ],
513 # warnings
513 # warnings
514 [],
514 [],
515 ]
515 ]
516
516
517 checks = [
517 checks = [
518 ('python', r'.*\.(py|cgi)$', r'^#!.*python', pyfilters, pypats),
518 ('python', r'.*\.(py|cgi)$', r'^#!.*python', pyfilters, pypats),
519 ('python', r'.*\.(py|cgi)$', r'^#!.*python', [], pynfpats),
519 ('python', r'.*\.(py|cgi)$', r'^#!.*python', [], pynfpats),
520 ('python', r'.*hgext.*\.py$', '', [], pyextnfpats),
520 ('python', r'.*hgext.*\.py$', '', [], pyextnfpats),
521 ('python 3', r'.*(hgext|mercurial)/(?!demandimport|policy|pycompat).*\.py',
521 ('python 3', r'.*(hgext|mercurial)/(?!demandimport|policy|pycompat).*\.py',
522 '', pyfilters, py3pats),
522 '', pyfilters, py3pats),
523 ('test script', r'(.*/)?test-[^.~]*$', '', testfilters, testpats),
523 ('test script', r'(.*/)?test-[^.~]*$', '', testfilters, testpats),
524 ('c', r'.*\.[ch]$', '', cfilters, cpats),
524 ('c', r'.*\.[ch]$', '', cfilters, cpats),
525 ('unified test', r'.*\.t$', '', utestfilters, utestpats),
525 ('unified test', r'.*\.t$', '', utestfilters, utestpats),
526 ('layering violation repo in revlog', r'mercurial/revlog\.py', '',
526 ('layering violation repo in revlog', r'mercurial/revlog\.py', '',
527 pyfilters, inrevlogpats),
527 pyfilters, inrevlogpats),
528 ('layering violation ui in util', r'mercurial/util\.py', '', pyfilters,
528 ('layering violation ui in util', r'mercurial/util\.py', '', pyfilters,
529 inutilpats),
529 inutilpats),
530 ('txt', r'.*\.txt$', '', txtfilters, txtpats),
530 ('txt', r'.*\.txt$', '', txtfilters, txtpats),
531 ('web template', r'mercurial/templates/.*\.tmpl', '',
531 ('web template', r'mercurial/templates/.*\.tmpl', '',
532 webtemplatefilters, webtemplatepats),
532 webtemplatefilters, webtemplatepats),
533 ('all except for .po', r'.*(?<!\.po)$', '',
533 ('all except for .po', r'.*(?<!\.po)$', '',
534 allfilesfilters, allfilespats),
534 allfilesfilters, allfilespats),
535 ]
535 ]
536
536
537 def _preparepats():
537 def _preparepats():
538 for c in checks:
538 for c in checks:
539 failandwarn = c[-1]
539 failandwarn = c[-1]
540 for pats in failandwarn:
540 for pats in failandwarn:
541 for i, pseq in enumerate(pats):
541 for i, pseq in enumerate(pats):
542 # fix-up regexes for multi-line searches
542 # fix-up regexes for multi-line searches
543 p = pseq[0]
543 p = pseq[0]
544 # \s doesn't match \n
544 # \s doesn't match \n
545 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
545 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
546 # [^...] doesn't match newline
546 # [^...] doesn't match newline
547 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p)
547 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p)
548
548
549 pats[i] = (re.compile(p, re.MULTILINE),) + pseq[1:]
549 pats[i] = (re.compile(p, re.MULTILINE),) + pseq[1:]
550 filters = c[3]
550 filters = c[3]
551 for i, flt in enumerate(filters):
551 for i, flt in enumerate(filters):
552 filters[i] = re.compile(flt[0]), flt[1]
552 filters[i] = re.compile(flt[0]), flt[1]
553
553
554 class norepeatlogger(object):
554 class norepeatlogger(object):
555 def __init__(self):
555 def __init__(self):
556 self._lastseen = None
556 self._lastseen = None
557
557
558 def log(self, fname, lineno, line, msg, blame):
558 def log(self, fname, lineno, line, msg, blame):
559 """print error related a to given line of a given file.
559 """print error related a to given line of a given file.
560
560
561 The faulty line will also be printed but only once in the case
561 The faulty line will also be printed but only once in the case
562 of multiple errors.
562 of multiple errors.
563
563
564 :fname: filename
564 :fname: filename
565 :lineno: line number
565 :lineno: line number
566 :line: actual content of the line
566 :line: actual content of the line
567 :msg: error message
567 :msg: error message
568 """
568 """
569 msgid = fname, lineno, line
569 msgid = fname, lineno, line
570 if msgid != self._lastseen:
570 if msgid != self._lastseen:
571 if blame:
571 if blame:
572 print("%s:%d (%s):" % (fname, lineno, blame))
572 print("%s:%d (%s):" % (fname, lineno, blame))
573 else:
573 else:
574 print("%s:%d:" % (fname, lineno))
574 print("%s:%d:" % (fname, lineno))
575 print(" > %s" % line)
575 print(" > %s" % line)
576 self._lastseen = msgid
576 self._lastseen = msgid
577 print(" " + msg)
577 print(" " + msg)
578
578
579 _defaultlogger = norepeatlogger()
579 _defaultlogger = norepeatlogger()
580
580
581 def getblame(f):
581 def getblame(f):
582 lines = []
582 lines = []
583 for l in os.popen('hg annotate -un %s' % f):
583 for l in os.popen('hg annotate -un %s' % f):
584 start, line = l.split(':', 1)
584 start, line = l.split(':', 1)
585 user, rev = start.split()
585 user, rev = start.split()
586 lines.append((line[1:-1], user, rev))
586 lines.append((line[1:-1], user, rev))
587 return lines
587 return lines
588
588
589 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
589 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
590 blame=False, debug=False, lineno=True):
590 blame=False, debug=False, lineno=True):
591 """checks style and portability of a given file
591 """checks style and portability of a given file
592
592
593 :f: filepath
593 :f: filepath
594 :logfunc: function used to report error
594 :logfunc: function used to report error
595 logfunc(filename, linenumber, linecontent, errormessage)
595 logfunc(filename, linenumber, linecontent, errormessage)
596 :maxerr: number of error to display before aborting.
596 :maxerr: number of error to display before aborting.
597 Set to false (default) to report all errors
597 Set to false (default) to report all errors
598
598
599 return True if no error is found, False otherwise.
599 return True if no error is found, False otherwise.
600 """
600 """
601 blamecache = None
601 blamecache = None
602 result = True
602 result = True
603
603
604 try:
604 try:
605 with opentext(f) as fp:
605 with opentext(f) as fp:
606 try:
606 try:
607 pre = post = fp.read()
607 pre = post = fp.read()
608 except UnicodeDecodeError as e:
608 except UnicodeDecodeError as e:
609 print("%s while reading %s" % (e, f))
609 print("%s while reading %s" % (e, f))
610 return result
610 return result
611 except IOError as e:
611 except IOError as e:
612 print("Skipping %s, %s" % (f, str(e).split(':', 1)[0]))
612 print("Skipping %s, %s" % (f, str(e).split(':', 1)[0]))
613 return result
613 return result
614
614
615 for name, match, magic, filters, pats in checks:
615 for name, match, magic, filters, pats in checks:
616 post = pre # discard filtering result of previous check
616 post = pre # discard filtering result of previous check
617 if debug:
617 if debug:
618 print(name, f)
618 print(name, f)
619 fc = 0
619 fc = 0
620 if not (re.match(match, f) or (magic and re.search(magic, pre))):
620 if not (re.match(match, f) or (magic and re.search(magic, pre))):
621 if debug:
621 if debug:
622 print("Skipping %s for %s it doesn't match %s" % (
622 print("Skipping %s for %s it doesn't match %s" % (
623 name, match, f))
623 name, match, f))
624 continue
624 continue
625 if "no-" "check-code" in pre:
625 if "no-" "check-code" in pre:
626 # If you're looking at this line, it's because a file has:
626 # If you're looking at this line, it's because a file has:
627 # no- check- code
627 # no- check- code
628 # but the reason to output skipping is to make life for
628 # but the reason to output skipping is to make life for
629 # tests easier. So, instead of writing it with a normal
629 # tests easier. So, instead of writing it with a normal
630 # spelling, we write it with the expected spelling from
630 # spelling, we write it with the expected spelling from
631 # tests/test-check-code.t
631 # tests/test-check-code.t
632 print("Skipping %s it has no-che?k-code (glob)" % f)
632 print("Skipping %s it has no-che?k-code (glob)" % f)
633 return "Skip" # skip checking this file
633 return "Skip" # skip checking this file
634 for p, r in filters:
634 for p, r in filters:
635 post = re.sub(p, r, post)
635 post = re.sub(p, r, post)
636 nerrs = len(pats[0]) # nerr elements are errors
636 nerrs = len(pats[0]) # nerr elements are errors
637 if warnings:
637 if warnings:
638 pats = pats[0] + pats[1]
638 pats = pats[0] + pats[1]
639 else:
639 else:
640 pats = pats[0]
640 pats = pats[0]
641 # print post # uncomment to show filtered version
641 # print post # uncomment to show filtered version
642
642
643 if debug:
643 if debug:
644 print("Checking %s for %s" % (name, f))
644 print("Checking %s for %s" % (name, f))
645
645
646 prelines = None
646 prelines = None
647 errors = []
647 errors = []
648 for i, pat in enumerate(pats):
648 for i, pat in enumerate(pats):
649 if len(pat) == 3:
649 if len(pat) == 3:
650 p, msg, ignore = pat
650 p, msg, ignore = pat
651 else:
651 else:
652 p, msg = pat
652 p, msg = pat
653 ignore = None
653 ignore = None
654 if i >= nerrs:
654 if i >= nerrs:
655 msg = "warning: " + msg
655 msg = "warning: " + msg
656
656
657 pos = 0
657 pos = 0
658 n = 0
658 n = 0
659 for m in p.finditer(post):
659 for m in p.finditer(post):
660 if prelines is None:
660 if prelines is None:
661 prelines = pre.splitlines()
661 prelines = pre.splitlines()
662 postlines = post.splitlines(True)
662 postlines = post.splitlines(True)
663
663
664 start = m.start()
664 start = m.start()
665 while n < len(postlines):
665 while n < len(postlines):
666 step = len(postlines[n])
666 step = len(postlines[n])
667 if pos + step > start:
667 if pos + step > start:
668 break
668 break
669 pos += step
669 pos += step
670 n += 1
670 n += 1
671 l = prelines[n]
671 l = prelines[n]
672
672
673 if ignore and re.search(ignore, l, re.MULTILINE):
673 if ignore and re.search(ignore, l, re.MULTILINE):
674 if debug:
674 if debug:
675 print("Skipping %s for %s:%s (ignore pattern)" % (
675 print("Skipping %s for %s:%s (ignore pattern)" % (
676 name, f, n))
676 name, f, n))
677 continue
677 continue
678 bd = ""
678 bd = ""
679 if blame:
679 if blame:
680 bd = 'working directory'
680 bd = 'working directory'
681 if not blamecache:
681 if not blamecache:
682 blamecache = getblame(f)
682 blamecache = getblame(f)
683 if n < len(blamecache):
683 if n < len(blamecache):
684 bl, bu, br = blamecache[n]
684 bl, bu, br = blamecache[n]
685 if bl == l:
685 if bl == l:
686 bd = '%s@%s' % (bu, br)
686 bd = '%s@%s' % (bu, br)
687
687
688 errors.append((f, lineno and n + 1, l, msg, bd))
688 errors.append((f, lineno and n + 1, l, msg, bd))
689 result = False
689 result = False
690
690
691 errors.sort()
691 errors.sort()
692 for e in errors:
692 for e in errors:
693 logfunc(*e)
693 logfunc(*e)
694 fc += 1
694 fc += 1
695 if maxerr and fc >= maxerr:
695 if maxerr and fc >= maxerr:
696 print(" (too many errors, giving up)")
696 print(" (too many errors, giving up)")
697 break
697 break
698
698
699 return result
699 return result
700
700
701 def main():
701 def main():
702 parser = optparse.OptionParser("%prog [options] [files | -]")
702 parser = optparse.OptionParser("%prog [options] [files | -]")
703 parser.add_option("-w", "--warnings", action="store_true",
703 parser.add_option("-w", "--warnings", action="store_true",
704 help="include warning-level checks")
704 help="include warning-level checks")
705 parser.add_option("-p", "--per-file", type="int",
705 parser.add_option("-p", "--per-file", type="int",
706 help="max warnings per file")
706 help="max warnings per file")
707 parser.add_option("-b", "--blame", action="store_true",
707 parser.add_option("-b", "--blame", action="store_true",
708 help="use annotate to generate blame info")
708 help="use annotate to generate blame info")
709 parser.add_option("", "--debug", action="store_true",
709 parser.add_option("", "--debug", action="store_true",
710 help="show debug information")
710 help="show debug information")
711 parser.add_option("", "--nolineno", action="store_false",
711 parser.add_option("", "--nolineno", action="store_false",
712 dest='lineno', help="don't show line numbers")
712 dest='lineno', help="don't show line numbers")
713
713
714 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False,
714 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False,
715 lineno=True)
715 lineno=True)
716 (options, args) = parser.parse_args()
716 (options, args) = parser.parse_args()
717
717
718 if len(args) == 0:
718 if len(args) == 0:
719 check = glob.glob("*")
719 check = glob.glob("*")
720 elif args == ['-']:
720 elif args == ['-']:
721 # read file list from stdin
721 # read file list from stdin
722 check = sys.stdin.read().splitlines()
722 check = sys.stdin.read().splitlines()
723 else:
723 else:
724 check = args
724 check = args
725
725
726 _preparepats()
726 _preparepats()
727
727
728 ret = 0
728 ret = 0
729 for f in check:
729 for f in check:
730 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
730 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
731 blame=options.blame, debug=options.debug,
731 blame=options.blame, debug=options.debug,
732 lineno=options.lineno):
732 lineno=options.lineno):
733 ret = 1
733 ret = 1
734 return ret
734 return ret
735
735
736 if __name__ == "__main__":
736 if __name__ == "__main__":
737 sys.exit(main())
737 sys.exit(main())
@@ -1,683 +1,682 b''
1 # Initial setup
1 # Initial setup
2
2
3 $ cat >> $HGRCPATH << EOF
3 $ cat >> $HGRCPATH << EOF
4 > [extensions]
4 > [extensions]
5 > lfs=
5 > lfs=
6 > [lfs]
6 > [lfs]
7 > threshold=1000B
7 > threshold=1000B
8 > EOF
8 > EOF
9
9
10 $ LONG=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
10 $ LONG=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
11
11
12 # Prepare server and enable extension
12 # Prepare server and enable extension
13 $ hg init server
13 $ hg init server
14 $ hg clone -q server client
14 $ hg clone -q server client
15 $ cd client
15 $ cd client
16
16
17 # Commit small file
17 # Commit small file
18 $ echo s > smallfile
18 $ echo s > smallfile
19 $ hg commit -Aqm "add small file"
19 $ hg commit -Aqm "add small file"
20
20
21 # Commit large file
21 # Commit large file
22 $ echo $LONG > largefile
22 $ echo $LONG > largefile
23 $ grep lfs .hg/requires
23 $ grep lfs .hg/requires
24 [1]
24 [1]
25 $ hg commit --traceback -Aqm "add large file"
25 $ hg commit --traceback -Aqm "add large file"
26 $ grep lfs .hg/requires
26 $ grep lfs .hg/requires
27 lfs
27 lfs
28
28
29 # Ensure metadata is stored
29 # Ensure metadata is stored
30 $ hg debugdata largefile 0
30 $ hg debugdata largefile 0
31 version https://git-lfs.github.com/spec/v1
31 version https://git-lfs.github.com/spec/v1
32 oid sha256:f11e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
32 oid sha256:f11e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
33 size 1501
33 size 1501
34 x-is-binary 0
34 x-is-binary 0
35
35
36 # Check the blobstore is populated
36 # Check the blobstore is populated
37 $ find .hg/store/lfs/objects | sort
37 $ find .hg/store/lfs/objects | sort
38 .hg/store/lfs/objects
38 .hg/store/lfs/objects
39 .hg/store/lfs/objects/f1
39 .hg/store/lfs/objects/f1
40 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
40 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
41
41
42 # Check the blob stored contains the actual contents of the file
42 # Check the blob stored contains the actual contents of the file
43 $ cat .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
43 $ cat .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
44 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
44 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
45
45
46 # Push changes to the server
46 # Push changes to the server
47
47
48 $ hg push
48 $ hg push
49 pushing to $TESTTMP/server
49 pushing to $TESTTMP/server
50 searching for changes
50 searching for changes
51 abort: lfs.url needs to be configured
51 abort: lfs.url needs to be configured
52 [255]
52 [255]
53
53
54 $ cat >> $HGRCPATH << EOF
54 $ cat >> $HGRCPATH << EOF
55 > [lfs]
55 > [lfs]
56 > url=file:$TESTTMP/dummy-remote/
56 > url=file:$TESTTMP/dummy-remote/
57 > EOF
57 > EOF
58
58
59 $ hg push -v | egrep -v '^(uncompressed| )'
59 $ hg push -v | egrep -v '^(uncompressed| )'
60 pushing to $TESTTMP/server
60 pushing to $TESTTMP/server
61 searching for changes
61 searching for changes
62 2 changesets found
62 2 changesets found
63 adding changesets
63 adding changesets
64 adding manifests
64 adding manifests
65 adding file changes
65 adding file changes
66 added 2 changesets with 2 changes to 2 files
66 added 2 changesets with 2 changes to 2 files
67
67
68 # Unknown URL scheme
68 # Unknown URL scheme
69
69
70 $ hg push --config lfs.url=ftp://foobar
70 $ hg push --config lfs.url=ftp://foobar
71 abort: lfs: unknown url scheme: ftp
71 abort: lfs: unknown url scheme: ftp
72 [255]
72 [255]
73
73
74 $ cd ../
74 $ cd ../
75
75
76 # Initialize new client (not cloning) and setup extension
76 # Initialize new client (not cloning) and setup extension
77 $ hg init client2
77 $ hg init client2
78 $ cd client2
78 $ cd client2
79 $ cat >> .hg/hgrc <<EOF
79 $ cat >> .hg/hgrc <<EOF
80 > [paths]
80 > [paths]
81 > default = $TESTTMP/server
81 > default = $TESTTMP/server
82 > EOF
82 > EOF
83
83
84 # Pull from server
84 # Pull from server
85 $ hg pull default
85 $ hg pull default
86 pulling from $TESTTMP/server
86 pulling from $TESTTMP/server
87 requesting all changes
87 requesting all changes
88 adding changesets
88 adding changesets
89 adding manifests
89 adding manifests
90 adding file changes
90 adding file changes
91 added 2 changesets with 2 changes to 2 files
91 added 2 changesets with 2 changes to 2 files
92 new changesets b29ba743f89d:00c137947d30
92 new changesets b29ba743f89d:00c137947d30
93 (run 'hg update' to get a working copy)
93 (run 'hg update' to get a working copy)
94
94
95 # Check the blobstore is not yet populated
95 # Check the blobstore is not yet populated
96 $ [ -d .hg/store/lfs/objects ]
96 $ [ -d .hg/store/lfs/objects ]
97 [1]
97 [1]
98
98
99 # Update to the last revision containing the large file
99 # Update to the last revision containing the large file
100 $ hg update
100 $ hg update
101 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
102
102
103 # Check the blobstore has been populated on update
103 # Check the blobstore has been populated on update
104 $ find .hg/store/lfs/objects | sort
104 $ find .hg/store/lfs/objects | sort
105 .hg/store/lfs/objects
105 .hg/store/lfs/objects
106 .hg/store/lfs/objects/f1
106 .hg/store/lfs/objects/f1
107 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
107 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
108
108
109 # Check the contents of the file are fetched from blobstore when requested
109 # Check the contents of the file are fetched from blobstore when requested
110 $ hg cat -r . largefile
110 $ hg cat -r . largefile
111 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
111 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
112
112
113 # Check the file has been copied in the working copy
113 # Check the file has been copied in the working copy
114 $ cat largefile
114 $ cat largefile
115 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
115 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
116
116
117 $ cd ..
117 $ cd ..
118
118
119 # Check rename, and switch between large and small files
119 # Check rename, and switch between large and small files
120
120
121 $ hg init repo3
121 $ hg init repo3
122 $ cd repo3
122 $ cd repo3
123 $ cat >> .hg/hgrc << EOF
123 $ cat >> .hg/hgrc << EOF
124 > [lfs]
124 > [lfs]
125 > threshold=10B
125 > threshold=10B
126 > EOF
126 > EOF
127
127
128 $ echo LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS > large
128 $ echo LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS > large
129 $ echo SHORTER > small
129 $ echo SHORTER > small
130 $ hg add . -q
130 $ hg add . -q
131 $ hg commit -m 'commit with lfs content'
131 $ hg commit -m 'commit with lfs content'
132
132
133 $ hg mv large l
133 $ hg mv large l
134 $ hg mv small s
134 $ hg mv small s
135 $ hg commit -m 'renames'
135 $ hg commit -m 'renames'
136
136
137 $ echo SHORT > l
137 $ echo SHORT > l
138 $ echo BECOME-LARGER-FROM-SHORTER > s
138 $ echo BECOME-LARGER-FROM-SHORTER > s
139 $ hg commit -m 'large to small, small to large'
139 $ hg commit -m 'large to small, small to large'
140
140
141 $ echo 1 >> l
141 $ echo 1 >> l
142 $ echo 2 >> s
142 $ echo 2 >> s
143 $ hg commit -m 'random modifications'
143 $ hg commit -m 'random modifications'
144
144
145 $ echo RESTORE-TO-BE-LARGE > l
145 $ echo RESTORE-TO-BE-LARGE > l
146 $ echo SHORTER > s
146 $ echo SHORTER > s
147 $ hg commit -m 'switch large and small again'
147 $ hg commit -m 'switch large and small again'
148
148
149 # Test lfs_files template
149 # Test lfs_files template
150
150
151 $ hg log -r 'all()' -T '{rev} {join(lfs_files, ", ")}\n'
151 $ hg log -r 'all()' -T '{rev} {join(lfs_files, ", ")}\n'
152 0 large
152 0 large
153 1 l
153 1 l
154 2 s
154 2 s
155 3 s
155 3 s
156 4 l
156 4 l
157
157
158 # Push and pull the above repo
158 # Push and pull the above repo
159
159
160 $ hg --cwd .. init repo4
160 $ hg --cwd .. init repo4
161 $ hg push ../repo4
161 $ hg push ../repo4
162 pushing to ../repo4
162 pushing to ../repo4
163 searching for changes
163 searching for changes
164 adding changesets
164 adding changesets
165 adding manifests
165 adding manifests
166 adding file changes
166 adding file changes
167 added 5 changesets with 10 changes to 4 files
167 added 5 changesets with 10 changes to 4 files
168
168
169 $ hg --cwd .. init repo5
169 $ hg --cwd .. init repo5
170 $ hg --cwd ../repo5 pull ../repo3
170 $ hg --cwd ../repo5 pull ../repo3
171 pulling from ../repo3
171 pulling from ../repo3
172 requesting all changes
172 requesting all changes
173 adding changesets
173 adding changesets
174 adding manifests
174 adding manifests
175 adding file changes
175 adding file changes
176 added 5 changesets with 10 changes to 4 files
176 added 5 changesets with 10 changes to 4 files
177 new changesets fd47a419c4f7:5adf850972b9
177 new changesets fd47a419c4f7:5adf850972b9
178 (run 'hg update' to get a working copy)
178 (run 'hg update' to get a working copy)
179
179
180 $ cd ..
180 $ cd ..
181
181
182 # Test clone
182 # Test clone
183
183
184 $ hg init repo6
184 $ hg init repo6
185 $ cd repo6
185 $ cd repo6
186 $ cat >> .hg/hgrc << EOF
186 $ cat >> .hg/hgrc << EOF
187 > [lfs]
187 > [lfs]
188 > threshold=30B
188 > threshold=30B
189 > EOF
189 > EOF
190
190
191 $ echo LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES > large
191 $ echo LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES > large
192 $ echo SMALL > small
192 $ echo SMALL > small
193 $ hg commit -Aqm 'create a lfs file' large small
193 $ hg commit -Aqm 'create a lfs file' large small
194 $ hg debuglfsupload -r 'all()' -v
194 $ hg debuglfsupload -r 'all()' -v
195
195
196 $ cd ..
196 $ cd ..
197
197
198 $ hg clone repo6 repo7
198 $ hg clone repo6 repo7
199 updating to branch default
199 updating to branch default
200 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
200 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
201 $ cd repo7
201 $ cd repo7
202 $ hg config extensions --debug | grep lfs
202 $ hg config extensions --debug | grep lfs
203 $TESTTMP/repo7/.hg/hgrc:*: extensions.lfs= (glob)
203 $TESTTMP/repo7/.hg/hgrc:*: extensions.lfs= (glob)
204 $ cat large
204 $ cat large
205 LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES
205 LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES
206 $ cat small
206 $ cat small
207 SMALL
207 SMALL
208
208
209 $ cd ..
209 $ cd ..
210
210
211 $ hg --config extensions.share= share repo7 sharedrepo
211 $ hg --config extensions.share= share repo7 sharedrepo
212 updating working directory
212 updating working directory
213 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
213 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
214 $ hg -R sharedrepo config extensions --debug | grep lfs
214 $ hg -R sharedrepo config extensions --debug | grep lfs
215 $TESTTMP/sharedrepo/.hg/hgrc:*: extensions.lfs= (glob)
215 $TESTTMP/sharedrepo/.hg/hgrc:*: extensions.lfs= (glob)
216
216
217 # Test rename and status
217 # Test rename and status
218
218
219 $ hg init repo8
219 $ hg init repo8
220 $ cd repo8
220 $ cd repo8
221 $ cat >> .hg/hgrc << EOF
221 $ cat >> .hg/hgrc << EOF
222 > [lfs]
222 > [lfs]
223 > threshold=10B
223 > threshold=10B
224 > EOF
224 > EOF
225
225
226 $ echo THIS-IS-LFS-BECAUSE-10-BYTES > a1
226 $ echo THIS-IS-LFS-BECAUSE-10-BYTES > a1
227 $ echo SMALL > a2
227 $ echo SMALL > a2
228 $ hg commit -m a -A a1 a2
228 $ hg commit -m a -A a1 a2
229 $ hg status
229 $ hg status
230 $ hg mv a1 b1
230 $ hg mv a1 b1
231 $ hg mv a2 a1
231 $ hg mv a2 a1
232 $ hg mv b1 a2
232 $ hg mv b1 a2
233 $ hg commit -m b
233 $ hg commit -m b
234 $ hg status
234 $ hg status
235 >>> with open('a2', 'wb') as f:
235 >>> with open('a2', 'wb') as f:
236 ... f.write(b'\1\nSTART-WITH-HG-FILELOG-METADATA')
236 ... f.write(b'\1\nSTART-WITH-HG-FILELOG-METADATA')
237 >>> with open('a1', 'wb') as f:
237 >>> with open('a1', 'wb') as f:
238 ... f.write(b'\1\nMETA\n')
238 ... f.write(b'\1\nMETA\n')
239 $ hg commit -m meta
239 $ hg commit -m meta
240 $ hg status
240 $ hg status
241 $ hg log -T '{rev}: {file_copies} | {file_dels} | {file_adds}\n'
241 $ hg log -T '{rev}: {file_copies} | {file_dels} | {file_adds}\n'
242 2: | |
242 2: | |
243 1: a1 (a2)a2 (a1) | |
243 1: a1 (a2)a2 (a1) | |
244 0: | | a1 a2
244 0: | | a1 a2
245
245
246 $ for n in a1 a2; do
246 $ for n in a1 a2; do
247 > for r in 0 1 2; do
247 > for r in 0 1 2; do
248 > printf '\n%s @ %s\n' $n $r
248 > printf '\n%s @ %s\n' $n $r
249 > hg debugdata $n $r
249 > hg debugdata $n $r
250 > done
250 > done
251 > done
251 > done
252
252
253 a1 @ 0
253 a1 @ 0
254 version https://git-lfs.github.com/spec/v1
254 version https://git-lfs.github.com/spec/v1
255 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
255 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
256 size 29
256 size 29
257 x-is-binary 0
257 x-is-binary 0
258
258
259 a1 @ 1
259 a1 @ 1
260 \x01 (esc)
260 \x01 (esc)
261 copy: a2
261 copy: a2
262 copyrev: 50470ad23cf937b1f4b9f80bfe54df38e65b50d9
262 copyrev: 50470ad23cf937b1f4b9f80bfe54df38e65b50d9
263 \x01 (esc)
263 \x01 (esc)
264 SMALL
264 SMALL
265
265
266 a1 @ 2
266 a1 @ 2
267 \x01 (esc)
267 \x01 (esc)
268 \x01 (esc)
268 \x01 (esc)
269 \x01 (esc)
269 \x01 (esc)
270 META
270 META
271
271
272 a2 @ 0
272 a2 @ 0
273 SMALL
273 SMALL
274
274
275 a2 @ 1
275 a2 @ 1
276 version https://git-lfs.github.com/spec/v1
276 version https://git-lfs.github.com/spec/v1
277 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
277 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
278 size 29
278 size 29
279 x-hg-copy a1
279 x-hg-copy a1
280 x-hg-copyrev be23af27908a582af43e5cda209a5a9b319de8d4
280 x-hg-copyrev be23af27908a582af43e5cda209a5a9b319de8d4
281 x-is-binary 0
281 x-is-binary 0
282
282
283 a2 @ 2
283 a2 @ 2
284 version https://git-lfs.github.com/spec/v1
284 version https://git-lfs.github.com/spec/v1
285 oid sha256:876dadc86a8542f9798048f2c47f51dbf8e4359aed883e8ec80c5db825f0d943
285 oid sha256:876dadc86a8542f9798048f2c47f51dbf8e4359aed883e8ec80c5db825f0d943
286 size 32
286 size 32
287 x-is-binary 0
287 x-is-binary 0
288
288
289 # Verify commit hashes include rename metadata
289 # Verify commit hashes include rename metadata
290
290
291 $ hg log -T '{rev}:{node|short} {desc}\n'
291 $ hg log -T '{rev}:{node|short} {desc}\n'
292 2:0fae949de7fa meta
292 2:0fae949de7fa meta
293 1:9cd6bdffdac0 b
293 1:9cd6bdffdac0 b
294 0:7f96794915f7 a
294 0:7f96794915f7 a
295
295
296 $ cd ..
296 $ cd ..
297
297
298 # Test bundle
298 # Test bundle
299
299
300 $ hg init repo9
300 $ hg init repo9
301 $ cd repo9
301 $ cd repo9
302 $ cat >> .hg/hgrc << EOF
302 $ cat >> .hg/hgrc << EOF
303 > [lfs]
303 > [lfs]
304 > threshold=10B
304 > threshold=10B
305 > [diff]
305 > [diff]
306 > git=1
306 > git=1
307 > EOF
307 > EOF
308
308
309 $ for i in 0 single two three 4; do
309 $ for i in 0 single two three 4; do
310 > echo 'THIS-IS-LFS-'$i > a
310 > echo 'THIS-IS-LFS-'$i > a
311 > hg commit -m a-$i -A a
311 > hg commit -m a-$i -A a
312 > done
312 > done
313
313
314 $ hg update 2 -q
314 $ hg update 2 -q
315 $ echo 'THIS-IS-LFS-2-CHILD' > a
315 $ echo 'THIS-IS-LFS-2-CHILD' > a
316 $ hg commit -m branching -q
316 $ hg commit -m branching -q
317
317
318 $ hg bundle --base 1 bundle.hg -v
318 $ hg bundle --base 1 bundle.hg -v
319 4 changesets found
319 4 changesets found
320 uncompressed size of bundle content:
320 uncompressed size of bundle content:
321 * (changelog) (glob)
321 * (changelog) (glob)
322 * (manifests) (glob)
322 * (manifests) (glob)
323 * a (glob)
323 * a (glob)
324 $ hg --config extensions.strip= strip -r 2 --no-backup --force -q
324 $ hg --config extensions.strip= strip -r 2 --no-backup --force -q
325 $ hg -R bundle.hg log -p -T '{rev} {desc}\n' a
325 $ hg -R bundle.hg log -p -T '{rev} {desc}\n' a
326 5 branching
326 5 branching
327 diff --git a/a b/a
327 diff --git a/a b/a
328 --- a/a
328 --- a/a
329 +++ b/a
329 +++ b/a
330 @@ -1,1 +1,1 @@
330 @@ -1,1 +1,1 @@
331 -THIS-IS-LFS-two
331 -THIS-IS-LFS-two
332 +THIS-IS-LFS-2-CHILD
332 +THIS-IS-LFS-2-CHILD
333
333
334 4 a-4
334 4 a-4
335 diff --git a/a b/a
335 diff --git a/a b/a
336 --- a/a
336 --- a/a
337 +++ b/a
337 +++ b/a
338 @@ -1,1 +1,1 @@
338 @@ -1,1 +1,1 @@
339 -THIS-IS-LFS-three
339 -THIS-IS-LFS-three
340 +THIS-IS-LFS-4
340 +THIS-IS-LFS-4
341
341
342 3 a-three
342 3 a-three
343 diff --git a/a b/a
343 diff --git a/a b/a
344 --- a/a
344 --- a/a
345 +++ b/a
345 +++ b/a
346 @@ -1,1 +1,1 @@
346 @@ -1,1 +1,1 @@
347 -THIS-IS-LFS-two
347 -THIS-IS-LFS-two
348 +THIS-IS-LFS-three
348 +THIS-IS-LFS-three
349
349
350 2 a-two
350 2 a-two
351 diff --git a/a b/a
351 diff --git a/a b/a
352 --- a/a
352 --- a/a
353 +++ b/a
353 +++ b/a
354 @@ -1,1 +1,1 @@
354 @@ -1,1 +1,1 @@
355 -THIS-IS-LFS-single
355 -THIS-IS-LFS-single
356 +THIS-IS-LFS-two
356 +THIS-IS-LFS-two
357
357
358 1 a-single
358 1 a-single
359 diff --git a/a b/a
359 diff --git a/a b/a
360 --- a/a
360 --- a/a
361 +++ b/a
361 +++ b/a
362 @@ -1,1 +1,1 @@
362 @@ -1,1 +1,1 @@
363 -THIS-IS-LFS-0
363 -THIS-IS-LFS-0
364 +THIS-IS-LFS-single
364 +THIS-IS-LFS-single
365
365
366 0 a-0
366 0 a-0
367 diff --git a/a b/a
367 diff --git a/a b/a
368 new file mode 100644
368 new file mode 100644
369 --- /dev/null
369 --- /dev/null
370 +++ b/a
370 +++ b/a
371 @@ -0,0 +1,1 @@
371 @@ -0,0 +1,1 @@
372 +THIS-IS-LFS-0
372 +THIS-IS-LFS-0
373
373
374 $ hg bundle -R bundle.hg --base 1 bundle-again.hg -q
374 $ hg bundle -R bundle.hg --base 1 bundle-again.hg -q
375 $ hg -R bundle-again.hg log -p -T '{rev} {desc}\n' a
375 $ hg -R bundle-again.hg log -p -T '{rev} {desc}\n' a
376 5 branching
376 5 branching
377 diff --git a/a b/a
377 diff --git a/a b/a
378 --- a/a
378 --- a/a
379 +++ b/a
379 +++ b/a
380 @@ -1,1 +1,1 @@
380 @@ -1,1 +1,1 @@
381 -THIS-IS-LFS-two
381 -THIS-IS-LFS-two
382 +THIS-IS-LFS-2-CHILD
382 +THIS-IS-LFS-2-CHILD
383
383
384 4 a-4
384 4 a-4
385 diff --git a/a b/a
385 diff --git a/a b/a
386 --- a/a
386 --- a/a
387 +++ b/a
387 +++ b/a
388 @@ -1,1 +1,1 @@
388 @@ -1,1 +1,1 @@
389 -THIS-IS-LFS-three
389 -THIS-IS-LFS-three
390 +THIS-IS-LFS-4
390 +THIS-IS-LFS-4
391
391
392 3 a-three
392 3 a-three
393 diff --git a/a b/a
393 diff --git a/a b/a
394 --- a/a
394 --- a/a
395 +++ b/a
395 +++ b/a
396 @@ -1,1 +1,1 @@
396 @@ -1,1 +1,1 @@
397 -THIS-IS-LFS-two
397 -THIS-IS-LFS-two
398 +THIS-IS-LFS-three
398 +THIS-IS-LFS-three
399
399
400 2 a-two
400 2 a-two
401 diff --git a/a b/a
401 diff --git a/a b/a
402 --- a/a
402 --- a/a
403 +++ b/a
403 +++ b/a
404 @@ -1,1 +1,1 @@
404 @@ -1,1 +1,1 @@
405 -THIS-IS-LFS-single
405 -THIS-IS-LFS-single
406 +THIS-IS-LFS-two
406 +THIS-IS-LFS-two
407
407
408 1 a-single
408 1 a-single
409 diff --git a/a b/a
409 diff --git a/a b/a
410 --- a/a
410 --- a/a
411 +++ b/a
411 +++ b/a
412 @@ -1,1 +1,1 @@
412 @@ -1,1 +1,1 @@
413 -THIS-IS-LFS-0
413 -THIS-IS-LFS-0
414 +THIS-IS-LFS-single
414 +THIS-IS-LFS-single
415
415
416 0 a-0
416 0 a-0
417 diff --git a/a b/a
417 diff --git a/a b/a
418 new file mode 100644
418 new file mode 100644
419 --- /dev/null
419 --- /dev/null
420 +++ b/a
420 +++ b/a
421 @@ -0,0 +1,1 @@
421 @@ -0,0 +1,1 @@
422 +THIS-IS-LFS-0
422 +THIS-IS-LFS-0
423
423
424 $ cd ..
424 $ cd ..
425
425
426 # Test isbinary
426 # Test isbinary
427
427
428 $ hg init repo10
428 $ hg init repo10
429 $ cd repo10
429 $ cd repo10
430 $ cat >> .hg/hgrc << EOF
430 $ cat >> .hg/hgrc << EOF
431 > [extensions]
431 > [extensions]
432 > lfs=
432 > lfs=
433 > [lfs]
433 > [lfs]
434 > threshold=1
434 > threshold=1
435 > EOF
435 > EOF
436 $ $PYTHON <<'EOF'
436 $ $PYTHON <<'EOF'
437 > def write(path, content):
437 > def write(path, content):
438 > with open(path, 'wb') as f:
438 > with open(path, 'wb') as f:
439 > f.write(content)
439 > f.write(content)
440 > write('a', b'\0\0')
440 > write('a', b'\0\0')
441 > write('b', b'\1\n')
441 > write('b', b'\1\n')
442 > write('c', b'\1\n\0')
442 > write('c', b'\1\n\0')
443 > write('d', b'xx')
443 > write('d', b'xx')
444 > EOF
444 > EOF
445 $ hg add a b c d
445 $ hg add a b c d
446 $ hg diff --stat
446 $ hg diff --stat
447 a | Bin
447 a | Bin
448 b | 1 +
448 b | 1 +
449 c | Bin
449 c | Bin
450 d | 1 +
450 d | 1 +
451 4 files changed, 2 insertions(+), 0 deletions(-)
451 4 files changed, 2 insertions(+), 0 deletions(-)
452 $ hg commit -m binarytest
452 $ hg commit -m binarytest
453 $ cat > $TESTTMP/dumpbinary.py << EOF
453 $ cat > $TESTTMP/dumpbinary.py << EOF
454 > def reposetup(ui, repo):
454 > def reposetup(ui, repo):
455 > for n in 'abcd':
455 > for n in 'abcd':
456 > ui.write(('%s: binary=%s\n') % (n, repo['.'][n].isbinary()))
456 > ui.write(('%s: binary=%s\n') % (n, repo['.'][n].isbinary()))
457 > EOF
457 > EOF
458 $ hg --config extensions.dumpbinary=$TESTTMP/dumpbinary.py id --trace
458 $ hg --config extensions.dumpbinary=$TESTTMP/dumpbinary.py id --trace
459 a: binary=True
459 a: binary=True
460 b: binary=False
460 b: binary=False
461 c: binary=True
461 c: binary=True
462 d: binary=False
462 d: binary=False
463 b55353847f02 tip
463 b55353847f02 tip
464
464
465 $ cd ..
465 $ cd ..
466
466
467 # Test fctx.cmp fastpath - diff without LFS blobs
467 # Test fctx.cmp fastpath - diff without LFS blobs
468
468
469 $ hg init repo11
469 $ hg init repo11
470 $ cd repo11
470 $ cd repo11
471 $ cat >> .hg/hgrc <<EOF
471 $ cat >> .hg/hgrc <<EOF
472 > [lfs]
472 > [lfs]
473 > threshold=1
473 > threshold=1
474 > EOF
474 > EOF
475 $ cat > ../patch.diff <<EOF
475 $ cat > ../patch.diff <<EOF
476 > # HG changeset patch
476 > # HG changeset patch
477 > 2
477 > 2
478 >
478 >
479 > diff --git a/a b/a
479 > diff --git a/a b/a
480 > old mode 100644
480 > old mode 100644
481 > new mode 100755
481 > new mode 100755
482 > EOF
482 > EOF
483
483
484 $ for i in 1 2 3; do
484 $ for i in 1 2 3; do
485 > cp ../repo10/a a
485 > cp ../repo10/a a
486 > if [ $i = 3 ]; then
486 > if [ $i = 3 ]; then
487 > # make a content-only change
487 > # make a content-only change
488 > hg import -q --bypass ../patch.diff
488 > hg import -q --bypass ../patch.diff
489 > hg update -q
489 > hg update -q
490 > rm ../patch.diff
490 > rm ../patch.diff
491 > else
491 > else
492 > echo $i >> a
492 > echo $i >> a
493 > hg commit -m $i -A a
493 > hg commit -m $i -A a
494 > fi
494 > fi
495 > done
495 > done
496 $ [ -d .hg/store/lfs/objects ]
496 $ [ -d .hg/store/lfs/objects ]
497
497
498 $ cd ..
498 $ cd ..
499
499
500 $ hg clone repo11 repo12 --noupdate
500 $ hg clone repo11 repo12 --noupdate
501 $ cd repo12
501 $ cd repo12
502 $ hg log --removed -p a -T '{desc}\n' --config diff.nobinary=1 --git
502 $ hg log --removed -p a -T '{desc}\n' --config diff.nobinary=1 --git
503 2
503 2
504 diff --git a/a b/a
504 diff --git a/a b/a
505 old mode 100644
505 old mode 100644
506 new mode 100755
506 new mode 100755
507
507
508 2
508 2
509 diff --git a/a b/a
509 diff --git a/a b/a
510 Binary file a has changed
510 Binary file a has changed
511
511
512 1
512 1
513 diff --git a/a b/a
513 diff --git a/a b/a
514 new file mode 100644
514 new file mode 100644
515 Binary file a has changed
515 Binary file a has changed
516
516
517 $ [ -d .hg/store/lfs/objects ]
517 $ [ -d .hg/store/lfs/objects ]
518 [1]
518 [1]
519
519
520 $ cd ..
520 $ cd ..
521
521
522 # Verify the repos
522 # Verify the repos
523
523
524 $ cat > $TESTTMP/dumpflog.py << EOF
524 $ cat > $TESTTMP/dumpflog.py << EOF
525 > # print raw revision sizes, flags, and hashes for certain files
525 > # print raw revision sizes, flags, and hashes for certain files
526 > import hashlib
526 > import hashlib
527 > from mercurial import revlog
527 > from mercurial import revlog
528 > from mercurial.node import short
528 > from mercurial.node import short
529 > def hash(rawtext):
529 > def hash(rawtext):
530 > h = hashlib.sha512()
530 > h = hashlib.sha512()
531 > h.update(rawtext)
531 > h.update(rawtext)
532 > return h.hexdigest()[:4]
532 > return h.hexdigest()[:4]
533 > def reposetup(ui, repo):
533 > def reposetup(ui, repo):
534 > # these 2 files are interesting
534 > # these 2 files are interesting
535 > for name in ['l', 's']:
535 > for name in ['l', 's']:
536 > fl = repo.file(name)
536 > fl = repo.file(name)
537 > if len(fl) == 0:
537 > if len(fl) == 0:
538 > continue
538 > continue
539 > sizes = [revlog.revlog.rawsize(fl, i) for i in fl]
539 > sizes = [revlog.revlog.rawsize(fl, i) for i in fl]
540 > texts = [fl.revision(i, raw=True) for i in fl]
540 > texts = [fl.revision(i, raw=True) for i in fl]
541 > flags = [int(fl.flags(i)) for i in fl]
541 > flags = [int(fl.flags(i)) for i in fl]
542 > hashes = [hash(t) for t in texts]
542 > hashes = [hash(t) for t in texts]
543 > print(' %s: rawsizes=%r flags=%r hashes=%r'
543 > print(' %s: rawsizes=%r flags=%r hashes=%r'
544 > % (name, sizes, flags, hashes))
544 > % (name, sizes, flags, hashes))
545 > EOF
545 > EOF
546
546
547 $ for i in client client2 server repo3 repo4 repo5 repo6 repo7 repo8 repo9 \
547 $ for i in client client2 server repo3 repo4 repo5 repo6 repo7 repo8 repo9 \
548 > repo10; do
548 > repo10; do
549 > echo 'repo:' $i
549 > echo 'repo:' $i
550 > hg --cwd $i verify --config extensions.dumpflog=$TESTTMP/dumpflog.py -q
550 > hg --cwd $i verify --config extensions.dumpflog=$TESTTMP/dumpflog.py -q
551 > done
551 > done
552 repo: client
552 repo: client
553 repo: client2
553 repo: client2
554 repo: server
554 repo: server
555 repo: repo3
555 repo: repo3
556 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
556 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
557 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
557 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
558 repo: repo4
558 repo: repo4
559 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
559 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
560 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
560 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
561 repo: repo5
561 repo: repo5
562 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
562 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
563 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
563 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
564 repo: repo6
564 repo: repo6
565 repo: repo7
565 repo: repo7
566 repo: repo8
566 repo: repo8
567 repo: repo9
567 repo: repo9
568 repo: repo10
568 repo: repo10
569
569
570 repo12 doesn't have any cached lfs files and its source never pushed its
570 repo12 doesn't have any cached lfs files and its source never pushed its
571 files. Therefore, the files don't exist in the remote store. Use the files in
571 files. Therefore, the files don't exist in the remote store. Use the files in
572 the user cache.
572 the user cache.
573
573
574 $ find $TESTTMP/repo12/.hg/store/lfs/objects -type f
574 $ test -d $TESTTMP/repo12/.hg/store/lfs/objects
575 find: */repo12/.hg/store/lfs/objects*: $ENOENT$ (glob)
576 [1]
575 [1]
577
576
578 $ hg --config extensions.share= share repo12 repo13
577 $ hg --config extensions.share= share repo12 repo13
579 updating working directory
578 updating working directory
580 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
579 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
581 $ hg -R repo13 -q verify
580 $ hg -R repo13 -q verify
582
581
583 $ hg clone repo12 repo14
582 $ hg clone repo12 repo14
584 updating to branch default
583 updating to branch default
585 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
584 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
586 $ hg -R repo14 -q verify
585 $ hg -R repo14 -q verify
587
586
588 If the source repo doesn't have the blob (maybe it was pulled or cloned with
587 If the source repo doesn't have the blob (maybe it was pulled or cloned with
589 --noupdate), the blob is still accessible via the global cache to send to the
588 --noupdate), the blob is still accessible via the global cache to send to the
590 remote store.
589 remote store.
591
590
592 $ rm -rf $TESTTMP/repo14/.hg/store/lfs
591 $ rm -rf $TESTTMP/repo14/.hg/store/lfs
593 $ hg init repo15
592 $ hg init repo15
594 $ hg -R repo14 push repo15
593 $ hg -R repo14 push repo15
595 pushing to repo15
594 pushing to repo15
596 searching for changes
595 searching for changes
597 adding changesets
596 adding changesets
598 adding manifests
597 adding manifests
599 adding file changes
598 adding file changes
600 added 3 changesets with 2 changes to 1 files
599 added 3 changesets with 2 changes to 1 files
601 $ hg -R repo14 -q verify
600 $ hg -R repo14 -q verify
602
601
603 lfs -> normal -> lfs round trip conversions are possible. The threshold for the
602 lfs -> normal -> lfs round trip conversions are possible. The threshold for the
604 lfs destination is specified here because it was originally listed in the local
603 lfs destination is specified here because it was originally listed in the local
605 .hgrc, and the global one is too high to trigger lfs usage. For lfs -> normal,
604 .hgrc, and the global one is too high to trigger lfs usage. For lfs -> normal,
606 there's no 'lfs' destination repo requirement. For normal -> lfs, there is.
605 there's no 'lfs' destination repo requirement. For normal -> lfs, there is.
607
606
608 XXX: There's not a great way to ensure that the conversion to normal files
607 XXX: There's not a great way to ensure that the conversion to normal files
609 actually converts _everything_ to normal. The extension needs to be loaded for
608 actually converts _everything_ to normal. The extension needs to be loaded for
610 the source, but there's no way to disable it for the destination. The best that
609 the source, but there's no way to disable it for the destination. The best that
611 can be done is to raise the threshold so that lfs isn't used on the destination.
610 can be done is to raise the threshold so that lfs isn't used on the destination.
612 It doesn't like using '!' to unset the value on the command line.
611 It doesn't like using '!' to unset the value on the command line.
613
612
614 $ hg --config extensions.convert= --config lfs.threshold=1000M \
613 $ hg --config extensions.convert= --config lfs.threshold=1000M \
615 > convert repo8 convert_normal
614 > convert repo8 convert_normal
616 initializing destination convert_normal repository
615 initializing destination convert_normal repository
617 scanning source...
616 scanning source...
618 sorting...
617 sorting...
619 converting...
618 converting...
620 2 a
619 2 a
621 1 b
620 1 b
622 0 meta
621 0 meta
623 $ grep 'lfs' convert_normal/.hg/requires
622 $ grep 'lfs' convert_normal/.hg/requires
624 [1]
623 [1]
625 $ hg --cwd convert_normal debugdata a1 0
624 $ hg --cwd convert_normal debugdata a1 0
626 THIS-IS-LFS-BECAUSE-10-BYTES
625 THIS-IS-LFS-BECAUSE-10-BYTES
627
626
628 $ hg --config extensions.convert= --config lfs.threshold=10B \
627 $ hg --config extensions.convert= --config lfs.threshold=10B \
629 > convert convert_normal convert_lfs
628 > convert convert_normal convert_lfs
630 initializing destination convert_lfs repository
629 initializing destination convert_lfs repository
631 scanning source...
630 scanning source...
632 sorting...
631 sorting...
633 converting...
632 converting...
634 2 a
633 2 a
635 1 b
634 1 b
636 0 meta
635 0 meta
637 $ hg --cwd convert_lfs debugdata a1 0
636 $ hg --cwd convert_lfs debugdata a1 0
638 version https://git-lfs.github.com/spec/v1
637 version https://git-lfs.github.com/spec/v1
639 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
638 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
640 size 29
639 size 29
641 x-is-binary 0
640 x-is-binary 0
642 $ grep 'lfs' convert_lfs/.hg/requires
641 $ grep 'lfs' convert_lfs/.hg/requires
643 lfs
642 lfs
644
643
645 This convert is trickier, because it contains deleted files (via `hg mv`)
644 This convert is trickier, because it contains deleted files (via `hg mv`)
646
645
647 $ hg --config extensions.convert= --config lfs.threshold=1000M \
646 $ hg --config extensions.convert= --config lfs.threshold=1000M \
648 > convert repo3 convert_normal2
647 > convert repo3 convert_normal2
649 initializing destination convert_normal2 repository
648 initializing destination convert_normal2 repository
650 scanning source...
649 scanning source...
651 sorting...
650 sorting...
652 converting...
651 converting...
653 4 commit with lfs content
652 4 commit with lfs content
654 3 renames
653 3 renames
655 2 large to small, small to large
654 2 large to small, small to large
656 1 random modifications
655 1 random modifications
657 0 switch large and small again
656 0 switch large and small again
658 $ grep 'lfs' convert_normal2/.hg/requires
657 $ grep 'lfs' convert_normal2/.hg/requires
659 [1]
658 [1]
660 $ hg --cwd convert_normal2 debugdata large 0
659 $ hg --cwd convert_normal2 debugdata large 0
661 LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS
660 LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS
662
661
663 $ hg --config extensions.convert= --config lfs.threshold=10B \
662 $ hg --config extensions.convert= --config lfs.threshold=10B \
664 > convert convert_normal2 convert_lfs2
663 > convert convert_normal2 convert_lfs2
665 initializing destination convert_lfs2 repository
664 initializing destination convert_lfs2 repository
666 scanning source...
665 scanning source...
667 sorting...
666 sorting...
668 converting...
667 converting...
669 4 commit with lfs content
668 4 commit with lfs content
670 3 renames
669 3 renames
671 2 large to small, small to large
670 2 large to small, small to large
672 1 random modifications
671 1 random modifications
673 0 switch large and small again
672 0 switch large and small again
674 $ grep 'lfs' convert_lfs2/.hg/requires
673 $ grep 'lfs' convert_lfs2/.hg/requires
675 lfs
674 lfs
676 $ hg --cwd convert_lfs2 debugdata large 0
675 $ hg --cwd convert_lfs2 debugdata large 0
677 version https://git-lfs.github.com/spec/v1
676 version https://git-lfs.github.com/spec/v1
678 oid sha256:66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
677 oid sha256:66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
679 size 39
678 size 39
680 x-is-binary 0
679 x-is-binary 0
681
680
682 $ hg -R convert_lfs2 config --debug extensions | grep lfs
681 $ hg -R convert_lfs2 config --debug extensions | grep lfs
683 $TESTTMP/convert_lfs2/.hg/hgrc:*: extensions.lfs= (glob)
682 $TESTTMP/convert_lfs2/.hg/hgrc:*: extensions.lfs= (glob)
General Comments 0
You need to be logged in to leave comments. Login now