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