##// END OF EJS Templates
tests: use `pwd` instead of ${PWD} in test-convert-git.t - because of Solaris
Mads Kiilerich -
r18508:813b7a1f stable
parent child Browse files
Show More
@@ -1,457 +1,457 b''
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'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
49 49 (r'sed.*-i', "don't use 'sed -i', use a temporary file"),
50 50 (r'\becho\b.*\\n', "don't use 'echo \\n', use printf"),
51 51 (r'echo -n', "don't use 'echo -n', use printf"),
52 52 (r'(^| )wc[^|]*$\n(?!.*\(re\))', "filter wc output"),
53 53 (r'head -c', "don't use 'head -c', use 'dd'"),
54 54 (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"),
55 55 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
56 56 (r'printf.*\\([1-9]|0\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+)*[^|]*[(|]\w',
61 61 "use egrep for extended grep syntax"),
62 62 (r'/bin/', "don't use explicit paths for tools"),
63 63 (r'[^\n]\Z', "no trailing newline"),
64 64 (r'export.*=', "don't export and assign at once"),
65 65 (r'^source\b', "don't use 'source', use '.'"),
66 66 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
67 67 (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"),
68 68 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
69 69 (r'^stop\(\)', "don't use 'stop' as a shell function name"),
70 70 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"),
71 71 (r'^alias\b.*=', "don't use alias, use a function"),
72 72 (r'if\s*!', "don't use '!' to negate exit status"),
73 73 (r'/dev/u?random', "don't use entropy, use /dev/zero"),
74 74 (r'do\s*true;\s*done', "don't use true as loop body, use sleep 0"),
75 75 (r'^( *)\t', "don't use tabs to indent"),
76 76 ],
77 77 # warnings
78 78 [
79 79 (r'^function', "don't use 'function', use old style"),
80 80 (r'^diff.*-\w*N', "don't use 'diff -N'"),
81 (r'\$PWD', "don't use $PWD, use `pwd`"),
81 (r'\$PWD|\${PWD}', "don't use $PWD, use `pwd`"),
82 82 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"),
83 83 ]
84 84 ]
85 85
86 86 testfilters = [
87 87 (r"( *)(#([^\n]*\S)?)", repcomment),
88 88 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
89 89 ]
90 90
91 91 uprefix = r"^ \$ "
92 92 utestpats = [
93 93 [
94 94 (r'^(\S.*|| [$>] .*)[ \t]\n', "trailing whitespace on non-output"),
95 95 (uprefix + r'.*\|\s*sed[^|>\n]*\n',
96 96 "use regex test output patterns instead of sed"),
97 97 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
98 98 (uprefix + r'.*(?<!\[)\$\?', "explicit exit code checks unnecessary"),
99 99 (uprefix + r'.*\|\| echo.*(fail|error)',
100 100 "explicit exit code checks unnecessary"),
101 101 (uprefix + r'set -e', "don't use set -e"),
102 102 (uprefix + r'\s', "don't indent commands, use > for continued lines"),
103 103 (r'^ saved backup bundle to \$TESTTMP.*\.hg$',
104 104 "use (glob) to match Windows paths too"),
105 105 ],
106 106 # warnings
107 107 []
108 108 ]
109 109
110 110 for i in [0, 1]:
111 111 for p, m in testpats[i]:
112 112 if p.startswith(r'^'):
113 113 p = r"^ [$>] (%s)" % p[1:]
114 114 else:
115 115 p = r"^ [$>] .*(%s)" % p
116 116 utestpats[i].append((p, m))
117 117
118 118 utestfilters = [
119 119 (r"<<(\S+)((.|\n)*?\n > \1)", rephere),
120 120 (r"( *)(#([^\n]*\S)?)", repcomment),
121 121 ]
122 122
123 123 pypats = [
124 124 [
125 125 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
126 126 "tuple parameter unpacking not available in Python 3+"),
127 127 (r'lambda\s*\(.*,.*\)',
128 128 "tuple parameter unpacking not available in Python 3+"),
129 129 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
130 130 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
131 131 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
132 132 (r'\s<>\s', '<> operator is not available in Python 3+, use !='),
133 133 (r'^\s*\t', "don't use tabs"),
134 134 (r'\S;\s*\n', "semicolon"),
135 135 (r'[^_]_\("[^"]+"\s*%', "don't use % inside _()"),
136 136 (r"[^_]_\('[^']+'\s*%", "don't use % inside _()"),
137 137 (r'(\w|\)),\w', "missing whitespace after ,"),
138 138 (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"),
139 139 (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"),
140 140 (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n'
141 141 r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Python 2.4'),
142 142 (r'(\s+)try:\n((?:\n|\1\s.*\n)*?)\1\s*yield\b.*?'
143 143 r'((?:\n|\1\s.*\n)+?)\1finally:',
144 144 'no yield inside try/finally in Python 2.4'),
145 145 (r'.{81}', "line too long"),
146 146 (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'),
147 147 (r'[^\n]\Z', "no trailing newline"),
148 148 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
149 149 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=',
150 150 # "don't use underbars in identifiers"),
151 151 (r'^\s+(self\.)?[A-za-z][a-z0-9]+[A-Z]\w* = ',
152 152 "don't use camelcase in identifiers"),
153 153 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+',
154 154 "linebreak after :"),
155 155 (r'class\s[^( \n]+:', "old-style class, use class foo(object)"),
156 156 (r'class\s[^( \n]+\(\):',
157 157 "class foo() not available in Python 2.4, use class foo(object)"),
158 158 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
159 159 "Python keyword is not a function"),
160 160 (r',]', "unneeded trailing ',' in list"),
161 161 # (r'class\s[A-Z][^\(]*\((?!Exception)',
162 162 # "don't capitalize non-exception classes"),
163 163 # (r'in range\(', "use xrange"),
164 164 # (r'^\s*print\s+', "avoid using print in core and extensions"),
165 165 (r'[\x80-\xff]', "non-ASCII character literal"),
166 166 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
167 167 (r'^\s*with\s+', "with not available in Python 2.4"),
168 168 (r'\.isdisjoint\(', "set.isdisjoint not available in Python 2.4"),
169 169 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
170 170 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
171 171 (r'(?<!def)\s+(any|all|format)\(',
172 172 "any/all/format not available in Python 2.4"),
173 173 (r'(?<!def)\s+(callable)\(',
174 174 "callable not available in Python 3, use getattr(f, '__call__', None)"),
175 175 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
176 176 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
177 177 "gratuitous whitespace after Python keyword"),
178 178 (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"),
179 179 # (r'\s\s=', "gratuitous whitespace before ="),
180 180 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
181 181 "missing whitespace around operator"),
182 182 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\s',
183 183 "missing whitespace around operator"),
184 184 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
185 185 "missing whitespace around operator"),
186 186 (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]',
187 187 "wrong whitespace around ="),
188 188 (r'raise Exception', "don't raise generic exceptions"),
189 189 (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$',
190 190 "don't use old-style two-argument raise, use Exception(message)"),
191 191 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
192 192 (r' [=!]=\s+(True|False|None)',
193 193 "comparison with singleton, use 'is' or 'is not' instead"),
194 194 (r'^\s*(while|if) [01]:',
195 195 "use True/False for constant Boolean expression"),
196 196 (r'(?:(?<!def)\s+|\()hasattr',
197 197 'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
198 198 (r'opener\([^)]*\).read\(',
199 199 "use opener.read() instead"),
200 200 (r'BaseException', 'not in Python 2.4, use Exception'),
201 201 (r'os\.path\.relpath', 'os.path.relpath is not in Python 2.5'),
202 202 (r'opener\([^)]*\).write\(',
203 203 "use opener.write() instead"),
204 204 (r'[\s\(](open|file)\([^)]*\)\.read\(',
205 205 "use util.readfile() instead"),
206 206 (r'[\s\(](open|file)\([^)]*\)\.write\(',
207 207 "use util.readfile() instead"),
208 208 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
209 209 "always assign an opened file to a variable, and close it afterwards"),
210 210 (r'[\s\(](open|file)\([^)]*\)\.',
211 211 "always assign an opened file to a variable, and close it afterwards"),
212 212 (r'(?i)descendent', "the proper spelling is descendAnt"),
213 213 (r'\.debug\(\_', "don't mark debug messages for translation"),
214 214 (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"),
215 215 (r'^\s*except\s*:', "warning: naked except clause", r'#.*re-raises'),
216 216 (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"),
217 217 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
218 218 "missing _() in ui message (use () to hide false-positives)"),
219 219 ],
220 220 # warnings
221 221 [
222 222 ]
223 223 ]
224 224
225 225 pyfilters = [
226 226 (r"""(?msx)(?P<comment>\#.*?$)|
227 227 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
228 228 (?P<text>(([^\\]|\\.)*?))
229 229 (?P=quote))""", reppython),
230 230 ]
231 231
232 232 cpats = [
233 233 [
234 234 (r'//', "don't use //-style comments"),
235 235 (r'^ ', "don't use spaces to indent"),
236 236 (r'\S\t', "don't use tabs except for indent"),
237 237 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
238 238 (r'.{81}', "line too long"),
239 239 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
240 240 (r'return\(', "return is not a function"),
241 241 (r' ;', "no space before ;"),
242 242 (r'\w+\* \w+', "use int *foo, not int* foo"),
243 243 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
244 244 (r'\w+ (\+\+|--)', "use foo++, not foo ++"),
245 245 (r'\w,\w', "missing whitespace after ,"),
246 246 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
247 247 (r'^#\s+\w', "use #foo, not # foo"),
248 248 (r'[^\n]\Z', "no trailing newline"),
249 249 (r'^\s*#import\b', "use only #include in standard C code"),
250 250 ],
251 251 # warnings
252 252 []
253 253 ]
254 254
255 255 cfilters = [
256 256 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
257 257 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
258 258 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
259 259 (r'(\()([^)]+\))', repcallspaces),
260 260 ]
261 261
262 262 inutilpats = [
263 263 [
264 264 (r'\bui\.', "don't use ui in util"),
265 265 ],
266 266 # warnings
267 267 []
268 268 ]
269 269
270 270 inrevlogpats = [
271 271 [
272 272 (r'\brepo\.', "don't use repo in revlog"),
273 273 ],
274 274 # warnings
275 275 []
276 276 ]
277 277
278 278 checks = [
279 279 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
280 280 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
281 281 ('c', r'.*\.c$', cfilters, cpats),
282 282 ('unified test', r'.*\.t$', utestfilters, utestpats),
283 283 ('layering violation repo in revlog', r'mercurial/revlog\.py', pyfilters,
284 284 inrevlogpats),
285 285 ('layering violation ui in util', r'mercurial/util\.py', pyfilters,
286 286 inutilpats),
287 287 ]
288 288
289 289 class norepeatlogger(object):
290 290 def __init__(self):
291 291 self._lastseen = None
292 292
293 293 def log(self, fname, lineno, line, msg, blame):
294 294 """print error related a to given line of a given file.
295 295
296 296 The faulty line will also be printed but only once in the case
297 297 of multiple errors.
298 298
299 299 :fname: filename
300 300 :lineno: line number
301 301 :line: actual content of the line
302 302 :msg: error message
303 303 """
304 304 msgid = fname, lineno, line
305 305 if msgid != self._lastseen:
306 306 if blame:
307 307 print "%s:%d (%s):" % (fname, lineno, blame)
308 308 else:
309 309 print "%s:%d:" % (fname, lineno)
310 310 print " > %s" % line
311 311 self._lastseen = msgid
312 312 print " " + msg
313 313
314 314 _defaultlogger = norepeatlogger()
315 315
316 316 def getblame(f):
317 317 lines = []
318 318 for l in os.popen('hg annotate -un %s' % f):
319 319 start, line = l.split(':', 1)
320 320 user, rev = start.split()
321 321 lines.append((line[1:-1], user, rev))
322 322 return lines
323 323
324 324 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
325 325 blame=False, debug=False, lineno=True):
326 326 """checks style and portability of a given file
327 327
328 328 :f: filepath
329 329 :logfunc: function used to report error
330 330 logfunc(filename, linenumber, linecontent, errormessage)
331 331 :maxerr: number of error to display before aborting.
332 332 Set to false (default) to report all errors
333 333
334 334 return True if no error is found, False otherwise.
335 335 """
336 336 blamecache = None
337 337 result = True
338 338 for name, match, filters, pats in checks:
339 339 if debug:
340 340 print name, f
341 341 fc = 0
342 342 if not re.match(match, f):
343 343 if debug:
344 344 print "Skipping %s for %s it doesn't match %s" % (
345 345 name, match, f)
346 346 continue
347 347 fp = open(f)
348 348 pre = post = fp.read()
349 349 fp.close()
350 350 if "no-" + "check-code" in pre:
351 351 if debug:
352 352 print "Skipping %s for %s it has no- and check-code" % (
353 353 name, f)
354 354 break
355 355 for p, r in filters:
356 356 post = re.sub(p, r, post)
357 357 if warnings:
358 358 pats = pats[0] + pats[1]
359 359 else:
360 360 pats = pats[0]
361 361 # print post # uncomment to show filtered version
362 362
363 363 if debug:
364 364 print "Checking %s for %s" % (name, f)
365 365
366 366 prelines = None
367 367 errors = []
368 368 for pat in pats:
369 369 if len(pat) == 3:
370 370 p, msg, ignore = pat
371 371 else:
372 372 p, msg = pat
373 373 ignore = None
374 374
375 375 # fix-up regexes for multi-line searches
376 376 po = p
377 377 # \s doesn't match \n
378 378 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
379 379 # [^...] doesn't match newline
380 380 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p)
381 381
382 382 #print po, '=>', p
383 383
384 384 pos = 0
385 385 n = 0
386 386 for m in re.finditer(p, post, re.MULTILINE):
387 387 if prelines is None:
388 388 prelines = pre.splitlines()
389 389 postlines = post.splitlines(True)
390 390
391 391 start = m.start()
392 392 while n < len(postlines):
393 393 step = len(postlines[n])
394 394 if pos + step > start:
395 395 break
396 396 pos += step
397 397 n += 1
398 398 l = prelines[n]
399 399
400 400 if "check-code" + "-ignore" in l:
401 401 if debug:
402 402 print "Skipping %s for %s:%s (check-code -ignore)" % (
403 403 name, f, n)
404 404 continue
405 405 elif ignore and re.search(ignore, l, re.MULTILINE):
406 406 continue
407 407 bd = ""
408 408 if blame:
409 409 bd = 'working directory'
410 410 if not blamecache:
411 411 blamecache = getblame(f)
412 412 if n < len(blamecache):
413 413 bl, bu, br = blamecache[n]
414 414 if bl == l:
415 415 bd = '%s@%s' % (bu, br)
416 416 errors.append((f, lineno and n + 1, l, msg, bd))
417 417 result = False
418 418
419 419 errors.sort()
420 420 for e in errors:
421 421 logfunc(*e)
422 422 fc += 1
423 423 if maxerr and fc >= maxerr:
424 424 print " (too many errors, giving up)"
425 425 break
426 426
427 427 return result
428 428
429 429 if __name__ == "__main__":
430 430 parser = optparse.OptionParser("%prog [options] [files]")
431 431 parser.add_option("-w", "--warnings", action="store_true",
432 432 help="include warning-level checks")
433 433 parser.add_option("-p", "--per-file", type="int",
434 434 help="max warnings per file")
435 435 parser.add_option("-b", "--blame", action="store_true",
436 436 help="use annotate to generate blame info")
437 437 parser.add_option("", "--debug", action="store_true",
438 438 help="show debug information")
439 439 parser.add_option("", "--nolineno", action="store_false",
440 440 dest='lineno', help="don't show line numbers")
441 441
442 442 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False,
443 443 lineno=True)
444 444 (options, args) = parser.parse_args()
445 445
446 446 if len(args) == 0:
447 447 check = glob.glob("*")
448 448 else:
449 449 check = args
450 450
451 451 ret = 0
452 452 for f in check:
453 453 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
454 454 blame=options.blame, debug=options.debug,
455 455 lineno=options.lineno):
456 456 ret = 1
457 457 sys.exit(ret)
@@ -1,347 +1,347 b''
1 1
2 2 $ "$TESTDIR/hghave" git || exit 80
3 3 $ echo "[core]" >> $HOME/.gitconfig
4 4 $ echo "autocrlf = false" >> $HOME/.gitconfig
5 5 $ echo "[core]" >> $HOME/.gitconfig
6 6 $ echo "autocrlf = false" >> $HOME/.gitconfig
7 7 $ echo "[extensions]" >> $HGRCPATH
8 8 $ echo "convert=" >> $HGRCPATH
9 9 $ echo 'hgext.graphlog =' >> $HGRCPATH
10 10 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
11 11 $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
12 12 $ GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
13 13 $ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
14 14 $ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
15 15 $ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
16 16 $ count=10
17 17 $ commit()
18 18 > {
19 19 > GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000"
20 20 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
21 21 > git commit "$@" >/dev/null 2>/dev/null || echo "git commit error"
22 22 > count=`expr $count + 1`
23 23 > }
24 24 $ mkdir git-repo
25 25 $ cd git-repo
26 26 $ git init-db >/dev/null 2>/dev/null
27 27 $ echo a > a
28 28 $ mkdir d
29 29 $ echo b > d/b
30 30 $ git add a d
31 31 $ commit -a -m t1
32 32
33 33 Remove the directory, then try to replace it with a file
34 34 (issue 754)
35 35
36 36 $ git rm -f d/b
37 37 rm 'd/b'
38 38 $ commit -m t2
39 39 $ echo d > d
40 40 $ git add d
41 41 $ commit -m t3
42 42 $ echo b >> a
43 43 $ commit -a -m t4.1
44 44 $ git checkout -b other HEAD~ >/dev/null 2>/dev/null
45 45 $ echo c > a
46 46 $ echo a >> a
47 47 $ commit -a -m t4.2
48 48 $ git checkout master >/dev/null 2>/dev/null
49 49 $ git pull --no-commit . other > /dev/null 2>/dev/null
50 50 $ commit -m 'Merge branch other'
51 51 $ cd ..
52 52 $ hg convert --datesort git-repo
53 53 assuming destination git-repo-hg
54 54 initializing destination git-repo-hg repository
55 55 scanning source...
56 56 sorting...
57 57 converting...
58 58 5 t1
59 59 4 t2
60 60 3 t3
61 61 2 t4.1
62 62 1 t4.2
63 63 0 Merge branch other
64 64 updating bookmarks
65 65 $ hg up -q -R git-repo-hg
66 66 $ hg -R git-repo-hg tip -v
67 67 changeset: 5:c78094926be2
68 68 bookmark: master
69 69 tag: tip
70 70 parent: 3:f5f5cb45432b
71 71 parent: 4:4e174f80c67c
72 72 user: test <test@example.org>
73 73 date: Mon Jan 01 00:00:15 2007 +0000
74 74 files: a
75 75 description:
76 76 Merge branch other
77 77
78 78
79 79 $ count=10
80 80 $ mkdir git-repo2
81 81 $ cd git-repo2
82 82 $ git init-db >/dev/null 2>/dev/null
83 83 $ echo foo > foo
84 84 $ git add foo
85 85 $ commit -a -m 'add foo'
86 86 $ echo >> foo
87 87 $ commit -a -m 'change foo'
88 88 $ git checkout -b Bar HEAD~ >/dev/null 2>/dev/null
89 89 $ echo quux >> quux
90 90 $ git add quux
91 91 $ commit -a -m 'add quux'
92 92 $ echo bar > bar
93 93 $ git add bar
94 94 $ commit -a -m 'add bar'
95 95 $ git checkout -b Baz HEAD~ >/dev/null 2>/dev/null
96 96 $ echo baz > baz
97 97 $ git add baz
98 98 $ commit -a -m 'add baz'
99 99 $ git checkout master >/dev/null 2>/dev/null
100 100 $ git pull --no-commit . Bar Baz > /dev/null 2>/dev/null
101 101 $ commit -m 'Octopus merge'
102 102 $ echo bar >> bar
103 103 $ commit -a -m 'change bar'
104 104 $ git checkout -b Foo HEAD~ >/dev/null 2>/dev/null
105 105 $ echo >> foo
106 106 $ commit -a -m 'change foo'
107 107 $ git checkout master >/dev/null 2>/dev/null
108 108 $ git pull --no-commit -s ours . Foo > /dev/null 2>/dev/null
109 109 $ commit -m 'Discard change to foo'
110 110 $ cd ..
111 111 $ glog()
112 112 > {
113 113 > hg glog --template '{rev} "{desc|firstline}" files: {files}\n' "$@"
114 114 > }
115 115 $ splitrepo()
116 116 > {
117 117 > msg="$1"
118 118 > files="$2"
119 119 > opts=$3
120 120 > echo "% $files: $msg"
121 121 > prefix=`echo "$files" | sed -e 's/ /-/g'`
122 122 > fmap="$prefix.fmap"
123 123 > repo="$prefix.repo"
124 124 > for i in $files; do
125 125 > echo "include $i" >> "$fmap"
126 126 > done
127 127 > hg -q convert $opts --filemap "$fmap" --datesort git-repo2 "$repo"
128 128 > hg up -q -R "$repo"
129 129 > glog -R "$repo"
130 130 > hg -R "$repo" manifest --debug
131 131 > }
132 132
133 133 full conversion
134 134
135 135 $ hg -q convert --datesort git-repo2 fullrepo
136 136 $ hg up -q -R fullrepo
137 137 $ glog -R fullrepo
138 138 @ 9 "Discard change to foo" files: foo
139 139 |\
140 140 | o 8 "change foo" files: foo
141 141 | |
142 142 o | 7 "change bar" files: bar
143 143 |/
144 144 o 6 "(octopus merge fixup)" files:
145 145 |\
146 146 | o 5 "Octopus merge" files: baz
147 147 | |\
148 148 o | | 4 "add baz" files: baz
149 149 | | |
150 150 +---o 3 "add bar" files: bar
151 151 | |
152 152 o | 2 "add quux" files: quux
153 153 | |
154 154 | o 1 "change foo" files: foo
155 155 |/
156 156 o 0 "add foo" files: foo
157 157
158 158 $ hg -R fullrepo manifest --debug
159 159 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
160 160 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
161 161 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
162 162 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
163 163 $ splitrepo 'octopus merge' 'foo bar baz'
164 164 % foo bar baz: octopus merge
165 165 @ 8 "Discard change to foo" files: foo
166 166 |\
167 167 | o 7 "change foo" files: foo
168 168 | |
169 169 o | 6 "change bar" files: bar
170 170 |/
171 171 o 5 "(octopus merge fixup)" files:
172 172 |\
173 173 | o 4 "Octopus merge" files: baz
174 174 | |\
175 175 o | | 3 "add baz" files: baz
176 176 | | |
177 177 +---o 2 "add bar" files: bar
178 178 | |
179 179 | o 1 "change foo" files: foo
180 180 |/
181 181 o 0 "add foo" files: foo
182 182
183 183 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
184 184 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
185 185 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
186 186 $ splitrepo 'only some parents of an octopus merge; "discard" a head' 'foo baz quux'
187 187 % foo baz quux: only some parents of an octopus merge; "discard" a head
188 188 @ 6 "Discard change to foo" files: foo
189 189 |
190 190 o 5 "change foo" files: foo
191 191 |
192 192 o 4 "Octopus merge" files:
193 193 |\
194 194 | o 3 "add baz" files: baz
195 195 | |
196 196 | o 2 "add quux" files: quux
197 197 | |
198 198 o | 1 "change foo" files: foo
199 199 |/
200 200 o 0 "add foo" files: foo
201 201
202 202 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
203 203 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
204 204 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
205 205
206 206 test binary conversion (issue 1359)
207 207
208 208 $ mkdir git-repo3
209 209 $ cd git-repo3
210 210 $ git init-db >/dev/null 2>/dev/null
211 211 $ python -c 'file("b", "wb").write("".join([chr(i) for i in range(256)])*16)'
212 212 $ git add b
213 213 $ commit -a -m addbinary
214 214 $ cd ..
215 215
216 216 convert binary file
217 217
218 218 $ hg convert git-repo3 git-repo3-hg
219 219 initializing destination git-repo3-hg repository
220 220 scanning source...
221 221 sorting...
222 222 converting...
223 223 0 addbinary
224 224 updating bookmarks
225 225 $ cd git-repo3-hg
226 226 $ hg up -C
227 227 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
228 228 $ python -c 'print len(file("b", "rb").read())'
229 229 4096
230 230 $ cd ..
231 231
232 232 test author vs committer
233 233
234 234 $ mkdir git-repo4
235 235 $ cd git-repo4
236 236 $ git init-db >/dev/null 2>/dev/null
237 237 $ echo >> foo
238 238 $ git add foo
239 239 $ commit -a -m addfoo
240 240 $ echo >> foo
241 241 $ GIT_AUTHOR_NAME="nottest"
242 242 $ commit -a -m addfoo2
243 243 $ cd ..
244 244
245 245 convert author committer
246 246
247 247 $ hg convert git-repo4 git-repo4-hg
248 248 initializing destination git-repo4-hg repository
249 249 scanning source...
250 250 sorting...
251 251 converting...
252 252 1 addfoo
253 253 0 addfoo2
254 254 updating bookmarks
255 255 $ hg -R git-repo4-hg log -v
256 256 changeset: 1:d63e967f93da
257 257 bookmark: master
258 258 tag: tip
259 259 user: nottest <test@example.org>
260 260 date: Mon Jan 01 00:00:21 2007 +0000
261 261 files: foo
262 262 description:
263 263 addfoo2
264 264
265 265 committer: test <test@example.org>
266 266
267 267
268 268 changeset: 0:0735477b0224
269 269 user: test <test@example.org>
270 270 date: Mon Jan 01 00:00:20 2007 +0000
271 271 files: foo
272 272 description:
273 273 addfoo
274 274
275 275
276 276
277 277 --sourceorder should fail
278 278
279 279 $ hg convert --sourcesort git-repo4 git-repo4-sourcesort-hg
280 280 initializing destination git-repo4-sourcesort-hg repository
281 281 abort: --sourcesort is not supported by this data source
282 282 [255]
283 283
284 284 damage git repository and convert again
285 285
286 286 $ cat > damage.py <<EOF
287 287 > import os
288 288 > import stat
289 289 > for root, dirs, files in os.walk('git-repo4/.git/objects'):
290 290 > if files:
291 291 > path = os.path.join(root, files[0])
292 292 > if os.name == 'nt':
293 293 > os.chmod(path, stat.S_IWUSR)
294 294 > os.remove(path)
295 295 > break
296 296 > EOF
297 297 $ python damage.py
298 298 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | \
299 299 > grep 'abort:' | sed 's/abort:.*/abort:/g'
300 300 abort:
301 301
302 302 test sub modules
303 303
304 304 $ mkdir git-repo5
305 305 $ cd git-repo5
306 306 $ git init-db >/dev/null 2>/dev/null
307 307 $ echo 'sub' >> foo
308 308 $ git add foo
309 309 $ commit -a -m 'addfoo'
310 $ BASE=${PWD}
310 $ BASE=`pwd`
311 311 $ cd ..
312 312 $ mkdir git-repo6
313 313 $ cd git-repo6
314 314 $ git init-db >/dev/null 2>/dev/null
315 315 $ git submodule add ${BASE} >/dev/null 2>/dev/null
316 316 $ commit -a -m 'addsubmodule' >/dev/null 2>/dev/null
317 317 $ cd ..
318 318
319 319 convert sub modules
320 320 $ hg convert git-repo6 git-repo6-hg
321 321 initializing destination git-repo6-hg repository
322 322 scanning source...
323 323 sorting...
324 324 converting...
325 325 0 addsubmodule
326 326 updating bookmarks
327 327 $ hg -R git-repo6-hg log -v
328 328 changeset: 0:* (glob)
329 329 bookmark: master
330 330 tag: tip
331 331 user: nottest <test@example.org>
332 332 date: Mon Jan 01 00:00:23 2007 +0000
333 333 files: .hgsub .hgsubstate
334 334 description:
335 335 addsubmodule
336 336
337 337 committer: test <test@example.org>
338 338
339 339
340 340
341 341 $ cd git-repo6-hg
342 342 $ hg up >/dev/null 2>/dev/null
343 343 $ cat .hgsubstate
344 344 * git-repo5 (glob)
345 345 $ cd git-repo5
346 346 $ cat foo
347 347 sub
General Comments 0
You need to be logged in to leave comments. Login now