##// END OF EJS Templates
darwin: omit ignorable codepoints when normcase()ing a file path...
Augie Fackler -
r23597:7a5bcd47 stable
parent child Browse files
Show More
@@ -1,596 +1,599
1 1 # posix.py - Posix utility function implementations for Mercurial
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 import encoding
10 10 import os, sys, errno, stat, getpass, pwd, grp, socket, tempfile, unicodedata
11 11 import fcntl
12 12
13 13 posixfile = open
14 14 normpath = os.path.normpath
15 15 samestat = os.path.samestat
16 16 oslink = os.link
17 17 unlink = os.unlink
18 18 rename = os.rename
19 19 expandglobs = False
20 20
21 21 umask = os.umask(0)
22 22 os.umask(umask)
23 23
24 24 def split(p):
25 25 '''Same as posixpath.split, but faster
26 26
27 27 >>> import posixpath
28 28 >>> for f in ['/absolute/path/to/file',
29 29 ... 'relative/path/to/file',
30 30 ... 'file_alone',
31 31 ... 'path/to/directory/',
32 32 ... '/multiple/path//separators',
33 33 ... '/file_at_root',
34 34 ... '///multiple_leading_separators_at_root',
35 35 ... '']:
36 36 ... assert split(f) == posixpath.split(f), f
37 37 '''
38 38 ht = p.rsplit('/', 1)
39 39 if len(ht) == 1:
40 40 return '', p
41 41 nh = ht[0].rstrip('/')
42 42 if nh:
43 43 return nh, ht[1]
44 44 return ht[0] + '/', ht[1]
45 45
46 46 def openhardlinks():
47 47 '''return true if it is safe to hold open file handles to hardlinks'''
48 48 return True
49 49
50 50 def nlinks(name):
51 51 '''return number of hardlinks for the given file'''
52 52 return os.lstat(name).st_nlink
53 53
54 54 def parsepatchoutput(output_line):
55 55 """parses the output produced by patch and returns the filename"""
56 56 pf = output_line[14:]
57 57 if os.sys.platform == 'OpenVMS':
58 58 if pf[0] == '`':
59 59 pf = pf[1:-1] # Remove the quotes
60 60 else:
61 61 if pf.startswith("'") and pf.endswith("'") and " " in pf:
62 62 pf = pf[1:-1] # Remove the quotes
63 63 return pf
64 64
65 65 def sshargs(sshcmd, host, user, port):
66 66 '''Build argument list for ssh'''
67 67 args = user and ("%s@%s" % (user, host)) or host
68 68 return port and ("%s -p %s" % (args, port)) or args
69 69
70 70 def isexec(f):
71 71 """check whether a file is executable"""
72 72 return (os.lstat(f).st_mode & 0100 != 0)
73 73
74 74 def setflags(f, l, x):
75 75 s = os.lstat(f).st_mode
76 76 if l:
77 77 if not stat.S_ISLNK(s):
78 78 # switch file to link
79 79 fp = open(f)
80 80 data = fp.read()
81 81 fp.close()
82 82 os.unlink(f)
83 83 try:
84 84 os.symlink(data, f)
85 85 except OSError:
86 86 # failed to make a link, rewrite file
87 87 fp = open(f, "w")
88 88 fp.write(data)
89 89 fp.close()
90 90 # no chmod needed at this point
91 91 return
92 92 if stat.S_ISLNK(s):
93 93 # switch link to file
94 94 data = os.readlink(f)
95 95 os.unlink(f)
96 96 fp = open(f, "w")
97 97 fp.write(data)
98 98 fp.close()
99 99 s = 0666 & ~umask # avoid restatting for chmod
100 100
101 101 sx = s & 0100
102 102 if x and not sx:
103 103 # Turn on +x for every +r bit when making a file executable
104 104 # and obey umask.
105 105 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
106 106 elif not x and sx:
107 107 # Turn off all +x bits
108 108 os.chmod(f, s & 0666)
109 109
110 110 def copymode(src, dst, mode=None):
111 111 '''Copy the file mode from the file at path src to dst.
112 112 If src doesn't exist, we're using mode instead. If mode is None, we're
113 113 using umask.'''
114 114 try:
115 115 st_mode = os.lstat(src).st_mode & 0777
116 116 except OSError, inst:
117 117 if inst.errno != errno.ENOENT:
118 118 raise
119 119 st_mode = mode
120 120 if st_mode is None:
121 121 st_mode = ~umask
122 122 st_mode &= 0666
123 123 os.chmod(dst, st_mode)
124 124
125 125 def checkexec(path):
126 126 """
127 127 Check whether the given path is on a filesystem with UNIX-like exec flags
128 128
129 129 Requires a directory (like /foo/.hg)
130 130 """
131 131
132 132 # VFAT on some Linux versions can flip mode but it doesn't persist
133 133 # a FS remount. Frequently we can detect it if files are created
134 134 # with exec bit on.
135 135
136 136 try:
137 137 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
138 138 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
139 139 try:
140 140 os.close(fh)
141 141 m = os.stat(fn).st_mode & 0777
142 142 new_file_has_exec = m & EXECFLAGS
143 143 os.chmod(fn, m ^ EXECFLAGS)
144 144 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
145 145 finally:
146 146 os.unlink(fn)
147 147 except (IOError, OSError):
148 148 # we don't care, the user probably won't be able to commit anyway
149 149 return False
150 150 return not (new_file_has_exec or exec_flags_cannot_flip)
151 151
152 152 def checklink(path):
153 153 """check whether the given path is on a symlink-capable filesystem"""
154 154 # mktemp is not racy because symlink creation will fail if the
155 155 # file already exists
156 156 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
157 157 try:
158 158 fd = tempfile.NamedTemporaryFile(dir=path, prefix='hg-checklink-')
159 159 try:
160 160 os.symlink(os.path.basename(fd.name), name)
161 161 os.unlink(name)
162 162 return True
163 163 finally:
164 164 fd.close()
165 165 except AttributeError:
166 166 return False
167 167 except OSError, inst:
168 168 # sshfs might report failure while successfully creating the link
169 169 if inst[0] == errno.EIO and os.path.exists(name):
170 170 os.unlink(name)
171 171 return False
172 172
173 173 def checkosfilename(path):
174 174 '''Check that the base-relative path is a valid filename on this platform.
175 175 Returns None if the path is ok, or a UI string describing the problem.'''
176 176 pass # on posix platforms, every path is ok
177 177
178 178 def setbinary(fd):
179 179 pass
180 180
181 181 def pconvert(path):
182 182 return path
183 183
184 184 def localpath(path):
185 185 return path
186 186
187 187 def samefile(fpath1, fpath2):
188 188 """Returns whether path1 and path2 refer to the same file. This is only
189 189 guaranteed to work for files, not directories."""
190 190 return os.path.samefile(fpath1, fpath2)
191 191
192 192 def samedevice(fpath1, fpath2):
193 193 """Returns whether fpath1 and fpath2 are on the same device. This is only
194 194 guaranteed to work for files, not directories."""
195 195 st1 = os.lstat(fpath1)
196 196 st2 = os.lstat(fpath2)
197 197 return st1.st_dev == st2.st_dev
198 198
199 199 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
200 200 def normcase(path):
201 201 return path.lower()
202 202
203 203 if sys.platform == 'darwin':
204 204
205 205 def normcase(path):
206 206 '''
207 207 Normalize a filename for OS X-compatible comparison:
208 208 - escape-encode invalid characters
209 209 - decompose to NFD
210 210 - lowercase
211 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
211 212
212 213 >>> normcase('UPPER')
213 214 'upper'
214 215 >>> normcase('Caf\xc3\xa9')
215 216 'cafe\\xcc\\x81'
216 217 >>> normcase('\xc3\x89')
217 218 'e\\xcc\\x81'
218 219 >>> normcase('\xb8\xca\xc3\xca\xbe\xc8.JPG') # issue3918
219 220 '%b8%ca%c3\\xca\\xbe%c8.jpg'
220 221 '''
221 222
222 223 try:
223 224 return encoding.asciilower(path) # exception for non-ASCII
224 225 except UnicodeDecodeError:
225 226 pass
226 227 try:
227 228 u = path.decode('utf-8')
228 229 except UnicodeDecodeError:
229 230 # OS X percent-encodes any bytes that aren't valid utf-8
230 231 s = ''
231 232 g = ''
232 233 l = 0
233 234 for c in path:
234 235 o = ord(c)
235 236 if l and o < 128 or o >= 192:
236 237 # we want a continuation byte, but didn't get one
237 238 s += ''.join(["%%%02X" % ord(x) for x in g])
238 239 g = ''
239 240 l = 0
240 241 if l == 0 and o < 128:
241 242 # ascii
242 243 s += c
243 244 elif l == 0 and 194 <= o < 245:
244 245 # valid leading bytes
245 246 if o < 224:
246 247 l = 1
247 248 elif o < 240:
248 249 l = 2
249 250 else:
250 251 l = 3
251 252 g = c
252 253 elif l > 0 and 128 <= o < 192:
253 254 # valid continuations
254 255 g += c
255 256 l -= 1
256 257 if not l:
257 258 s += g
258 259 g = ''
259 260 else:
260 261 # invalid
261 262 s += "%%%02X" % o
262 263
263 264 # any remaining partial characters
264 265 s += ''.join(["%%%02X" % ord(x) for x in g])
265 266 u = s.decode('utf-8')
266 267
267 268 # Decompose then lowercase (HFS+ technote specifies lower)
268 return unicodedata.normalize('NFD', u).lower().encode('utf-8')
269 enc = unicodedata.normalize('NFD', u).lower().encode('utf-8')
270 # drop HFS+ ignored characters
271 return encoding.hfsignoreclean(enc)
269 272
270 273 if sys.platform == 'cygwin':
271 274 # workaround for cygwin, in which mount point part of path is
272 275 # treated as case sensitive, even though underlying NTFS is case
273 276 # insensitive.
274 277
275 278 # default mount points
276 279 cygwinmountpoints = sorted([
277 280 "/usr/bin",
278 281 "/usr/lib",
279 282 "/cygdrive",
280 283 ], reverse=True)
281 284
282 285 # use upper-ing as normcase as same as NTFS workaround
283 286 def normcase(path):
284 287 pathlen = len(path)
285 288 if (pathlen == 0) or (path[0] != os.sep):
286 289 # treat as relative
287 290 return encoding.upper(path)
288 291
289 292 # to preserve case of mountpoint part
290 293 for mp in cygwinmountpoints:
291 294 if not path.startswith(mp):
292 295 continue
293 296
294 297 mplen = len(mp)
295 298 if mplen == pathlen: # mount point itself
296 299 return mp
297 300 if path[mplen] == os.sep:
298 301 return mp + encoding.upper(path[mplen:])
299 302
300 303 return encoding.upper(path)
301 304
302 305 # Cygwin translates native ACLs to POSIX permissions,
303 306 # but these translations are not supported by native
304 307 # tools, so the exec bit tends to be set erroneously.
305 308 # Therefore, disable executable bit access on Cygwin.
306 309 def checkexec(path):
307 310 return False
308 311
309 312 # Similarly, Cygwin's symlink emulation is likely to create
310 313 # problems when Mercurial is used from both Cygwin and native
311 314 # Windows, with other native tools, or on shared volumes
312 315 def checklink(path):
313 316 return False
314 317
315 318 def shellquote(s):
316 319 if os.sys.platform == 'OpenVMS':
317 320 return '"%s"' % s
318 321 else:
319 322 return "'%s'" % s.replace("'", "'\\''")
320 323
321 324 def quotecommand(cmd):
322 325 return cmd
323 326
324 327 def popen(command, mode='r'):
325 328 return os.popen(command, mode)
326 329
327 330 def testpid(pid):
328 331 '''return False if pid dead, True if running or not sure'''
329 332 if os.sys.platform == 'OpenVMS':
330 333 return True
331 334 try:
332 335 os.kill(pid, 0)
333 336 return True
334 337 except OSError, inst:
335 338 return inst.errno != errno.ESRCH
336 339
337 340 def explainexit(code):
338 341 """return a 2-tuple (desc, code) describing a subprocess status
339 342 (codes from kill are negative - not os.system/wait encoding)"""
340 343 if code >= 0:
341 344 return _("exited with status %d") % code, code
342 345 return _("killed by signal %d") % -code, -code
343 346
344 347 def isowner(st):
345 348 """Return True if the stat object st is from the current user."""
346 349 return st.st_uid == os.getuid()
347 350
348 351 def findexe(command):
349 352 '''Find executable for command searching like which does.
350 353 If command is a basename then PATH is searched for command.
351 354 PATH isn't searched if command is an absolute or relative path.
352 355 If command isn't found None is returned.'''
353 356 if sys.platform == 'OpenVMS':
354 357 return command
355 358
356 359 def findexisting(executable):
357 360 'Will return executable if existing file'
358 361 if os.path.isfile(executable) and os.access(executable, os.X_OK):
359 362 return executable
360 363 return None
361 364
362 365 if os.sep in command:
363 366 return findexisting(command)
364 367
365 368 if sys.platform == 'plan9':
366 369 return findexisting(os.path.join('/bin', command))
367 370
368 371 for path in os.environ.get('PATH', '').split(os.pathsep):
369 372 executable = findexisting(os.path.join(path, command))
370 373 if executable is not None:
371 374 return executable
372 375 return None
373 376
374 377 def setsignalhandler():
375 378 pass
376 379
377 380 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
378 381
379 382 def statfiles(files):
380 383 '''Stat each file in files. Yield each stat, or None if a file does not
381 384 exist or has a type we don't care about.'''
382 385 lstat = os.lstat
383 386 getkind = stat.S_IFMT
384 387 for nf in files:
385 388 try:
386 389 st = lstat(nf)
387 390 if getkind(st.st_mode) not in _wantedkinds:
388 391 st = None
389 392 except OSError, err:
390 393 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
391 394 raise
392 395 st = None
393 396 yield st
394 397
395 398 def getuser():
396 399 '''return name of current user'''
397 400 return getpass.getuser()
398 401
399 402 def username(uid=None):
400 403 """Return the name of the user with the given uid.
401 404
402 405 If uid is None, return the name of the current user."""
403 406
404 407 if uid is None:
405 408 uid = os.getuid()
406 409 try:
407 410 return pwd.getpwuid(uid)[0]
408 411 except KeyError:
409 412 return str(uid)
410 413
411 414 def groupname(gid=None):
412 415 """Return the name of the group with the given gid.
413 416
414 417 If gid is None, return the name of the current group."""
415 418
416 419 if gid is None:
417 420 gid = os.getgid()
418 421 try:
419 422 return grp.getgrgid(gid)[0]
420 423 except KeyError:
421 424 return str(gid)
422 425
423 426 def groupmembers(name):
424 427 """Return the list of members of the group with the given
425 428 name, KeyError if the group does not exist.
426 429 """
427 430 return list(grp.getgrnam(name).gr_mem)
428 431
429 432 def spawndetached(args):
430 433 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
431 434 args[0], args)
432 435
433 436 def gethgcmd():
434 437 return sys.argv[:1]
435 438
436 439 def termwidth():
437 440 try:
438 441 import termios, array
439 442 for dev in (sys.stderr, sys.stdout, sys.stdin):
440 443 try:
441 444 try:
442 445 fd = dev.fileno()
443 446 except AttributeError:
444 447 continue
445 448 if not os.isatty(fd):
446 449 continue
447 450 try:
448 451 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
449 452 width = array.array('h', arri)[1]
450 453 if width > 0:
451 454 return width
452 455 except AttributeError:
453 456 pass
454 457 except ValueError:
455 458 pass
456 459 except IOError, e:
457 460 if e[0] == errno.EINVAL:
458 461 pass
459 462 else:
460 463 raise
461 464 except ImportError:
462 465 pass
463 466 return 80
464 467
465 468 def makedir(path, notindexed):
466 469 os.mkdir(path)
467 470
468 471 def unlinkpath(f, ignoremissing=False):
469 472 """unlink and remove the directory if it is empty"""
470 473 try:
471 474 os.unlink(f)
472 475 except OSError, e:
473 476 if not (ignoremissing and e.errno == errno.ENOENT):
474 477 raise
475 478 # try removing directories that might now be empty
476 479 try:
477 480 os.removedirs(os.path.dirname(f))
478 481 except OSError:
479 482 pass
480 483
481 484 def lookupreg(key, name=None, scope=None):
482 485 return None
483 486
484 487 def hidewindow():
485 488 """Hide current shell window.
486 489
487 490 Used to hide the window opened when starting asynchronous
488 491 child process under Windows, unneeded on other systems.
489 492 """
490 493 pass
491 494
492 495 class cachestat(object):
493 496 def __init__(self, path):
494 497 self.stat = os.stat(path)
495 498
496 499 def cacheable(self):
497 500 return bool(self.stat.st_ino)
498 501
499 502 __hash__ = object.__hash__
500 503
501 504 def __eq__(self, other):
502 505 try:
503 506 # Only dev, ino, size, mtime and atime are likely to change. Out
504 507 # of these, we shouldn't compare atime but should compare the
505 508 # rest. However, one of the other fields changing indicates
506 509 # something fishy going on, so return False if anything but atime
507 510 # changes.
508 511 return (self.stat.st_mode == other.stat.st_mode and
509 512 self.stat.st_ino == other.stat.st_ino and
510 513 self.stat.st_dev == other.stat.st_dev and
511 514 self.stat.st_nlink == other.stat.st_nlink and
512 515 self.stat.st_uid == other.stat.st_uid and
513 516 self.stat.st_gid == other.stat.st_gid and
514 517 self.stat.st_size == other.stat.st_size and
515 518 self.stat.st_mtime == other.stat.st_mtime and
516 519 self.stat.st_ctime == other.stat.st_ctime)
517 520 except AttributeError:
518 521 return False
519 522
520 523 def __ne__(self, other):
521 524 return not self == other
522 525
523 526 def executablepath():
524 527 return None # available on Windows only
525 528
526 529 class unixdomainserver(socket.socket):
527 530 def __init__(self, join, subsystem):
528 531 '''Create a unix domain socket with the given prefix.'''
529 532 super(unixdomainserver, self).__init__(socket.AF_UNIX)
530 533 sockname = subsystem + '.sock'
531 534 self.realpath = self.path = join(sockname)
532 535 if os.path.islink(self.path):
533 536 if os.path.exists(self.path):
534 537 self.realpath = os.readlink(self.path)
535 538 else:
536 539 os.unlink(self.path)
537 540 try:
538 541 self.bind(self.realpath)
539 542 except socket.error, err:
540 543 if err.args[0] == 'AF_UNIX path too long':
541 544 tmpdir = tempfile.mkdtemp(prefix='hg-%s-' % subsystem)
542 545 self.realpath = os.path.join(tmpdir, sockname)
543 546 try:
544 547 self.bind(self.realpath)
545 548 os.symlink(self.realpath, self.path)
546 549 except (OSError, socket.error):
547 550 self.cleanup()
548 551 raise
549 552 else:
550 553 raise
551 554 self.listen(5)
552 555
553 556 def cleanup(self):
554 557 def okayifmissing(f, path):
555 558 try:
556 559 f(path)
557 560 except OSError, err:
558 561 if err.errno != errno.ENOENT:
559 562 raise
560 563
561 564 okayifmissing(os.unlink, self.path)
562 565 if self.realpath != self.path:
563 566 okayifmissing(os.unlink, self.realpath)
564 567 okayifmissing(os.rmdir, os.path.dirname(self.realpath))
565 568
566 569 def statislink(st):
567 570 '''check whether a stat result is a symlink'''
568 571 return st and stat.S_ISLNK(st.st_mode)
569 572
570 573 def statisexec(st):
571 574 '''check whether a stat result is an executable file'''
572 575 return st and (st.st_mode & 0100 != 0)
573 576
574 577 def readpipe(pipe):
575 578 """Read all available data from a pipe."""
576 579 # We can't fstat() a pipe because Linux will always report 0.
577 580 # So, we set the pipe to non-blocking mode and read everything
578 581 # that's available.
579 582 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
580 583 flags |= os.O_NONBLOCK
581 584 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
582 585
583 586 try:
584 587 chunks = []
585 588 while True:
586 589 try:
587 590 s = pipe.read()
588 591 if not s:
589 592 break
590 593 chunks.append(s)
591 594 except IOError:
592 595 break
593 596
594 597 return ''.join(chunks)
595 598 finally:
596 599 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
@@ -1,211 +1,210
1 1 #require icasefs
2 2
3 3 $ hg debugfs | grep 'case-sensitive:'
4 4 case-sensitive: no
5 5
6 6 test file addition with bad case
7 7
8 8 $ hg init repo1
9 9 $ cd repo1
10 10 $ echo a > a
11 11 $ hg add A
12 12 adding a
13 13 $ hg st
14 14 A a
15 15 $ hg ci -m adda
16 16 $ hg manifest
17 17 a
18 18 $ cd ..
19 19
20 20 test case collision on rename (issue750)
21 21
22 22 $ hg init repo2
23 23 $ cd repo2
24 24 $ echo a > a
25 25 $ hg --debug ci -Am adda
26 26 adding a
27 27 a
28 28 committed changeset 0:07f4944404050f47db2e5c5071e0e84e7a27bba9
29 29
30 30 Case-changing renames should work:
31 31
32 32 $ hg mv a A
33 33 $ hg mv A a
34 34 $ hg st
35 35
36 36 test changing case of path components
37 37
38 38 $ mkdir D
39 39 $ echo b > D/b
40 40 $ hg ci -Am addb D/b
41 41 $ hg mv D/b d/b
42 42 D/b: not overwriting - file exists
43 43 $ hg mv D/b d/c
44 44 $ hg st
45 45 A D/c
46 46 R D/b
47 47 $ mv D temp
48 48 $ mv temp d
49 49 $ hg st
50 50 A D/c
51 51 R D/b
52 52 $ hg revert -aq
53 53 $ rm d/c
54 54 $ echo c > D/c
55 55 $ hg add D/c
56 56 $ hg st
57 57 A D/c
58 58 $ hg ci -m addc D/c
59 59 $ hg mv d/b d/e
60 60 moving D/b to D/e (glob)
61 61 $ hg st
62 62 A D/e
63 63 R D/b
64 64 $ hg revert -aq
65 65 $ rm d/e
66 66 $ hg mv d/b D/B
67 67 moving D/b to D/B (glob)
68 68 $ hg st
69 69 A D/B
70 70 R D/b
71 71 $ cd ..
72 72
73 73 test case collision between revisions (issue912)
74 74
75 75 $ hg init repo3
76 76 $ cd repo3
77 77 $ echo a > a
78 78 $ hg ci -Am adda
79 79 adding a
80 80 $ hg rm a
81 81 $ hg ci -Am removea
82 82 $ echo A > A
83 83
84 84 on linux hfs keeps the old case stored, force it
85 85
86 86 $ mv a aa
87 87 $ mv aa A
88 88 $ hg ci -Am addA
89 89 adding A
90 90
91 91 used to fail under case insensitive fs
92 92
93 93 $ hg up -C 0
94 94 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
95 95 $ hg up -C
96 96 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
97 97
98 98 no clobbering of untracked files with wrong casing
99 99
100 100 $ hg up -r null
101 101 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
102 102 $ echo gold > a
103 103 $ hg up
104 104 A: untracked file differs
105 105 abort: untracked files in working directory differ from files in requested revision
106 106 [255]
107 107 $ cat a
108 108 gold
109 109 $ rm a
110 110
111 111 test that normal file in different case on target context is not
112 112 unlinked by largefiles extension.
113 113
114 114 $ cat >> .hg/hgrc <<EOF
115 115 > [extensions]
116 116 > largefiles=
117 117 > EOF
118 118 $ hg update -q -C 1
119 119 $ hg status -A
120 120 $ echo 'A as largefiles' > A
121 121 $ hg add --large A
122 122 $ hg commit -m '#3'
123 123 created new head
124 124 $ hg manifest -r 3
125 125 .hglf/A
126 126 $ hg manifest -r 0
127 127 a
128 128 $ hg update -q -C 0
129 129 $ hg status -A
130 130 C a
131 131 $ hg update -q -C 3
132 132 $ hg update -q 0
133 133
134 134 $ cd ..
135 135
136 136 issue 3342: file in nested directory causes unexpected abort
137 137
138 138 $ hg init issue3342
139 139 $ cd issue3342
140 140
141 141 $ mkdir -p a/B/c/D
142 142 $ echo e > a/B/c/D/e
143 143 $ hg add a/B/c/D/e
144 144
145 145 $ cd ..
146 146
147 147 issue 3340: mq does not handle case changes correctly
148 148
149 149 in addition to reported case, 'hg qrefresh' is also tested against
150 150 case changes.
151 151
152 152 $ echo "[extensions]" >> $HGRCPATH
153 153 $ echo "mq=" >> $HGRCPATH
154 154
155 155 $ hg init issue3340
156 156 $ cd issue3340
157 157
158 158 $ echo a > mIxEdCaSe
159 159 $ hg add mIxEdCaSe
160 160 $ hg commit -m '#0'
161 161 $ hg rename mIxEdCaSe tmp
162 162 $ hg rename tmp MiXeDcAsE
163 163 $ hg status -A
164 164 A MiXeDcAsE
165 165 mIxEdCaSe
166 166 R mIxEdCaSe
167 167 $ hg qnew changecase
168 168 $ hg status -A
169 169 C MiXeDcAsE
170 170
171 171 $ hg qpop -a
172 172 popping changecase
173 173 patch queue now empty
174 174 $ hg qnew refresh-casechange
175 175 $ hg status -A
176 176 C mIxEdCaSe
177 177 $ hg rename mIxEdCaSe tmp
178 178 $ hg rename tmp MiXeDcAsE
179 179 $ hg status -A
180 180 A MiXeDcAsE
181 181 mIxEdCaSe
182 182 R mIxEdCaSe
183 183 $ hg qrefresh
184 184 $ hg status -A
185 185 C MiXeDcAsE
186 186
187 187 $ hg qpop -a
188 188 popping refresh-casechange
189 189 patch queue now empty
190 190 $ hg qnew refresh-pattern
191 191 $ hg status
192 192 $ echo A > A
193 193 $ hg add
194 194 adding A
195 195 $ hg qrefresh a # issue 3271, qrefresh with file handled case wrong
196 196 $ hg status # empty status means the qrefresh worked
197 197
198 198 #if osx
199 199
200 200 We assume anyone running the tests on a case-insensitive volume on OS
201 201 X will be using HFS+. If that's not true, this test will fail.
202 202
203 Bug: some codepoints are to be ignored on HFS+:
204
205 203 $ rm A
206 204 >>> open(u'a\u200c'.encode('utf-8'), 'w').write('unicode is fun')
207 205 $ hg status
208 206 M A
209 ? a\xe2\x80\x8c (esc)
207
210 208 #endif
209
211 210 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now