##// END OF EJS Templates
posix: quote the specified string only when it may have to be quoted...
FUJIWARA Katsunori -
r23683:5edb3871 default
parent child Browse files
Show More
@@ -1,599 +1,606 b''
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 import fcntl
11 import fcntl, re
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 211 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
212 212
213 213 >>> normcase('UPPER')
214 214 'upper'
215 215 >>> normcase('Caf\xc3\xa9')
216 216 'cafe\\xcc\\x81'
217 217 >>> normcase('\xc3\x89')
218 218 'e\\xcc\\x81'
219 219 >>> normcase('\xb8\xca\xc3\xca\xbe\xc8.JPG') # issue3918
220 220 '%b8%ca%c3\\xca\\xbe%c8.jpg'
221 221 '''
222 222
223 223 try:
224 224 return encoding.asciilower(path) # exception for non-ASCII
225 225 except UnicodeDecodeError:
226 226 pass
227 227 try:
228 228 u = path.decode('utf-8')
229 229 except UnicodeDecodeError:
230 230 # OS X percent-encodes any bytes that aren't valid utf-8
231 231 s = ''
232 232 g = ''
233 233 l = 0
234 234 for c in path:
235 235 o = ord(c)
236 236 if l and o < 128 or o >= 192:
237 237 # we want a continuation byte, but didn't get one
238 238 s += ''.join(["%%%02X" % ord(x) for x in g])
239 239 g = ''
240 240 l = 0
241 241 if l == 0 and o < 128:
242 242 # ascii
243 243 s += c
244 244 elif l == 0 and 194 <= o < 245:
245 245 # valid leading bytes
246 246 if o < 224:
247 247 l = 1
248 248 elif o < 240:
249 249 l = 2
250 250 else:
251 251 l = 3
252 252 g = c
253 253 elif l > 0 and 128 <= o < 192:
254 254 # valid continuations
255 255 g += c
256 256 l -= 1
257 257 if not l:
258 258 s += g
259 259 g = ''
260 260 else:
261 261 # invalid
262 262 s += "%%%02X" % o
263 263
264 264 # any remaining partial characters
265 265 s += ''.join(["%%%02X" % ord(x) for x in g])
266 266 u = s.decode('utf-8')
267 267
268 268 # Decompose then lowercase (HFS+ technote specifies lower)
269 269 enc = unicodedata.normalize('NFD', u).lower().encode('utf-8')
270 270 # drop HFS+ ignored characters
271 271 return encoding.hfsignoreclean(enc)
272 272
273 273 if sys.platform == 'cygwin':
274 274 # workaround for cygwin, in which mount point part of path is
275 275 # treated as case sensitive, even though underlying NTFS is case
276 276 # insensitive.
277 277
278 278 # default mount points
279 279 cygwinmountpoints = sorted([
280 280 "/usr/bin",
281 281 "/usr/lib",
282 282 "/cygdrive",
283 283 ], reverse=True)
284 284
285 285 # use upper-ing as normcase as same as NTFS workaround
286 286 def normcase(path):
287 287 pathlen = len(path)
288 288 if (pathlen == 0) or (path[0] != os.sep):
289 289 # treat as relative
290 290 return encoding.upper(path)
291 291
292 292 # to preserve case of mountpoint part
293 293 for mp in cygwinmountpoints:
294 294 if not path.startswith(mp):
295 295 continue
296 296
297 297 mplen = len(mp)
298 298 if mplen == pathlen: # mount point itself
299 299 return mp
300 300 if path[mplen] == os.sep:
301 301 return mp + encoding.upper(path[mplen:])
302 302
303 303 return encoding.upper(path)
304 304
305 305 # Cygwin translates native ACLs to POSIX permissions,
306 306 # but these translations are not supported by native
307 307 # tools, so the exec bit tends to be set erroneously.
308 308 # Therefore, disable executable bit access on Cygwin.
309 309 def checkexec(path):
310 310 return False
311 311
312 312 # Similarly, Cygwin's symlink emulation is likely to create
313 313 # problems when Mercurial is used from both Cygwin and native
314 314 # Windows, with other native tools, or on shared volumes
315 315 def checklink(path):
316 316 return False
317 317
318 _needsshellquote = None
318 319 def shellquote(s):
319 320 if os.sys.platform == 'OpenVMS':
320 321 return '"%s"' % s
322 global _needsshellquote
323 if _needsshellquote is None:
324 _needsshellquote = re.compile(r'[^a-zA-Z0-9._/-]').search
325 if not _needsshellquote(s):
326 # "s" shouldn't have to be quoted
327 return s
321 328 else:
322 329 return "'%s'" % s.replace("'", "'\\''")
323 330
324 331 def quotecommand(cmd):
325 332 return cmd
326 333
327 334 def popen(command, mode='r'):
328 335 return os.popen(command, mode)
329 336
330 337 def testpid(pid):
331 338 '''return False if pid dead, True if running or not sure'''
332 339 if os.sys.platform == 'OpenVMS':
333 340 return True
334 341 try:
335 342 os.kill(pid, 0)
336 343 return True
337 344 except OSError, inst:
338 345 return inst.errno != errno.ESRCH
339 346
340 347 def explainexit(code):
341 348 """return a 2-tuple (desc, code) describing a subprocess status
342 349 (codes from kill are negative - not os.system/wait encoding)"""
343 350 if code >= 0:
344 351 return _("exited with status %d") % code, code
345 352 return _("killed by signal %d") % -code, -code
346 353
347 354 def isowner(st):
348 355 """Return True if the stat object st is from the current user."""
349 356 return st.st_uid == os.getuid()
350 357
351 358 def findexe(command):
352 359 '''Find executable for command searching like which does.
353 360 If command is a basename then PATH is searched for command.
354 361 PATH isn't searched if command is an absolute or relative path.
355 362 If command isn't found None is returned.'''
356 363 if sys.platform == 'OpenVMS':
357 364 return command
358 365
359 366 def findexisting(executable):
360 367 'Will return executable if existing file'
361 368 if os.path.isfile(executable) and os.access(executable, os.X_OK):
362 369 return executable
363 370 return None
364 371
365 372 if os.sep in command:
366 373 return findexisting(command)
367 374
368 375 if sys.platform == 'plan9':
369 376 return findexisting(os.path.join('/bin', command))
370 377
371 378 for path in os.environ.get('PATH', '').split(os.pathsep):
372 379 executable = findexisting(os.path.join(path, command))
373 380 if executable is not None:
374 381 return executable
375 382 return None
376 383
377 384 def setsignalhandler():
378 385 pass
379 386
380 387 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
381 388
382 389 def statfiles(files):
383 390 '''Stat each file in files. Yield each stat, or None if a file does not
384 391 exist or has a type we don't care about.'''
385 392 lstat = os.lstat
386 393 getkind = stat.S_IFMT
387 394 for nf in files:
388 395 try:
389 396 st = lstat(nf)
390 397 if getkind(st.st_mode) not in _wantedkinds:
391 398 st = None
392 399 except OSError, err:
393 400 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
394 401 raise
395 402 st = None
396 403 yield st
397 404
398 405 def getuser():
399 406 '''return name of current user'''
400 407 return getpass.getuser()
401 408
402 409 def username(uid=None):
403 410 """Return the name of the user with the given uid.
404 411
405 412 If uid is None, return the name of the current user."""
406 413
407 414 if uid is None:
408 415 uid = os.getuid()
409 416 try:
410 417 return pwd.getpwuid(uid)[0]
411 418 except KeyError:
412 419 return str(uid)
413 420
414 421 def groupname(gid=None):
415 422 """Return the name of the group with the given gid.
416 423
417 424 If gid is None, return the name of the current group."""
418 425
419 426 if gid is None:
420 427 gid = os.getgid()
421 428 try:
422 429 return grp.getgrgid(gid)[0]
423 430 except KeyError:
424 431 return str(gid)
425 432
426 433 def groupmembers(name):
427 434 """Return the list of members of the group with the given
428 435 name, KeyError if the group does not exist.
429 436 """
430 437 return list(grp.getgrnam(name).gr_mem)
431 438
432 439 def spawndetached(args):
433 440 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
434 441 args[0], args)
435 442
436 443 def gethgcmd():
437 444 return sys.argv[:1]
438 445
439 446 def termwidth():
440 447 try:
441 448 import termios, array
442 449 for dev in (sys.stderr, sys.stdout, sys.stdin):
443 450 try:
444 451 try:
445 452 fd = dev.fileno()
446 453 except AttributeError:
447 454 continue
448 455 if not os.isatty(fd):
449 456 continue
450 457 try:
451 458 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
452 459 width = array.array('h', arri)[1]
453 460 if width > 0:
454 461 return width
455 462 except AttributeError:
456 463 pass
457 464 except ValueError:
458 465 pass
459 466 except IOError, e:
460 467 if e[0] == errno.EINVAL:
461 468 pass
462 469 else:
463 470 raise
464 471 except ImportError:
465 472 pass
466 473 return 80
467 474
468 475 def makedir(path, notindexed):
469 476 os.mkdir(path)
470 477
471 478 def unlinkpath(f, ignoremissing=False):
472 479 """unlink and remove the directory if it is empty"""
473 480 try:
474 481 os.unlink(f)
475 482 except OSError, e:
476 483 if not (ignoremissing and e.errno == errno.ENOENT):
477 484 raise
478 485 # try removing directories that might now be empty
479 486 try:
480 487 os.removedirs(os.path.dirname(f))
481 488 except OSError:
482 489 pass
483 490
484 491 def lookupreg(key, name=None, scope=None):
485 492 return None
486 493
487 494 def hidewindow():
488 495 """Hide current shell window.
489 496
490 497 Used to hide the window opened when starting asynchronous
491 498 child process under Windows, unneeded on other systems.
492 499 """
493 500 pass
494 501
495 502 class cachestat(object):
496 503 def __init__(self, path):
497 504 self.stat = os.stat(path)
498 505
499 506 def cacheable(self):
500 507 return bool(self.stat.st_ino)
501 508
502 509 __hash__ = object.__hash__
503 510
504 511 def __eq__(self, other):
505 512 try:
506 513 # Only dev, ino, size, mtime and atime are likely to change. Out
507 514 # of these, we shouldn't compare atime but should compare the
508 515 # rest. However, one of the other fields changing indicates
509 516 # something fishy going on, so return False if anything but atime
510 517 # changes.
511 518 return (self.stat.st_mode == other.stat.st_mode and
512 519 self.stat.st_ino == other.stat.st_ino and
513 520 self.stat.st_dev == other.stat.st_dev and
514 521 self.stat.st_nlink == other.stat.st_nlink and
515 522 self.stat.st_uid == other.stat.st_uid and
516 523 self.stat.st_gid == other.stat.st_gid and
517 524 self.stat.st_size == other.stat.st_size and
518 525 self.stat.st_mtime == other.stat.st_mtime and
519 526 self.stat.st_ctime == other.stat.st_ctime)
520 527 except AttributeError:
521 528 return False
522 529
523 530 def __ne__(self, other):
524 531 return not self == other
525 532
526 533 def executablepath():
527 534 return None # available on Windows only
528 535
529 536 class unixdomainserver(socket.socket):
530 537 def __init__(self, join, subsystem):
531 538 '''Create a unix domain socket with the given prefix.'''
532 539 super(unixdomainserver, self).__init__(socket.AF_UNIX)
533 540 sockname = subsystem + '.sock'
534 541 self.realpath = self.path = join(sockname)
535 542 if os.path.islink(self.path):
536 543 if os.path.exists(self.path):
537 544 self.realpath = os.readlink(self.path)
538 545 else:
539 546 os.unlink(self.path)
540 547 try:
541 548 self.bind(self.realpath)
542 549 except socket.error, err:
543 550 if err.args[0] == 'AF_UNIX path too long':
544 551 tmpdir = tempfile.mkdtemp(prefix='hg-%s-' % subsystem)
545 552 self.realpath = os.path.join(tmpdir, sockname)
546 553 try:
547 554 self.bind(self.realpath)
548 555 os.symlink(self.realpath, self.path)
549 556 except (OSError, socket.error):
550 557 self.cleanup()
551 558 raise
552 559 else:
553 560 raise
554 561 self.listen(5)
555 562
556 563 def cleanup(self):
557 564 def okayifmissing(f, path):
558 565 try:
559 566 f(path)
560 567 except OSError, err:
561 568 if err.errno != errno.ENOENT:
562 569 raise
563 570
564 571 okayifmissing(os.unlink, self.path)
565 572 if self.realpath != self.path:
566 573 okayifmissing(os.unlink, self.realpath)
567 574 okayifmissing(os.rmdir, os.path.dirname(self.realpath))
568 575
569 576 def statislink(st):
570 577 '''check whether a stat result is a symlink'''
571 578 return st and stat.S_ISLNK(st.st_mode)
572 579
573 580 def statisexec(st):
574 581 '''check whether a stat result is an executable file'''
575 582 return st and (st.st_mode & 0100 != 0)
576 583
577 584 def readpipe(pipe):
578 585 """Read all available data from a pipe."""
579 586 # We can't fstat() a pipe because Linux will always report 0.
580 587 # So, we set the pipe to non-blocking mode and read everything
581 588 # that's available.
582 589 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
583 590 flags |= os.O_NONBLOCK
584 591 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
585 592
586 593 try:
587 594 chunks = []
588 595 while True:
589 596 try:
590 597 s = pipe.read()
591 598 if not s:
592 599 break
593 600 chunks.append(s)
594 601 except IOError:
595 602 break
596 603
597 604 return ''.join(chunks)
598 605 finally:
599 606 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
@@ -1,302 +1,302 b''
1 1 $ echo "[extensions]" >> $HGRCPATH
2 2 $ echo "extdiff=" >> $HGRCPATH
3 3
4 4 $ hg init a
5 5 $ cd a
6 6 $ echo a > a
7 7 $ echo b > b
8 8 $ hg add
9 9 adding a
10 10 adding b
11 11
12 12 Should diff cloned directories:
13 13
14 14 $ hg extdiff -o -r $opt
15 15 Only in a: a
16 16 Only in a: b
17 17 [1]
18 18
19 19 $ cat <<EOF >> $HGRCPATH
20 20 > [extdiff]
21 21 > cmd.falabala = echo
22 22 > opts.falabala = diffing
23 23 > cmd.edspace = echo
24 24 > opts.edspace = "name <user@example.com>"
25 25 > EOF
26 26
27 27 $ hg falabala
28 28 diffing a.000000000000 a
29 29 [1]
30 30
31 31 $ hg help falabala
32 32 hg falabala [OPTION]... [FILE]...
33 33
34 34 use 'echo' to diff repository (or selected files)
35 35
36 36 Show differences between revisions for the specified files, using the
37 37 'echo' program.
38 38
39 39 When two revision arguments are given, then changes are shown between
40 40 those revisions. If only one revision is specified then that revision is
41 41 compared to the working directory, and, when no revisions are specified,
42 42 the working directory files are compared to its parent.
43 43
44 44 options ([+] can be repeated):
45 45
46 46 -o --option OPT [+] pass option to comparison program
47 47 -r --rev REV [+] revision
48 48 -c --change REV change made by revision
49 49 -I --include PATTERN [+] include names matching the given patterns
50 50 -X --exclude PATTERN [+] exclude names matching the given patterns
51 51
52 52 (some details hidden, use --verbose to show complete help)
53 53
54 54 $ hg ci -d '0 0' -mtest1
55 55
56 56 $ echo b >> a
57 57 $ hg ci -d '1 0' -mtest2
58 58
59 59 Should diff cloned files directly:
60 60
61 61 $ hg falabala -r 0:1
62 62 diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
63 63 [1]
64 64
65 65 Test diff during merge:
66 66
67 67 $ hg update -C 0
68 68 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
69 69 $ echo c >> c
70 70 $ hg add c
71 71 $ hg ci -m "new branch" -d '1 0'
72 72 created new head
73 73 $ hg merge 1
74 74 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
75 75 (branch merge, don't forget to commit)
76 76
77 77 Should diff cloned file against wc file:
78 78
79 79 $ hg falabala
80 80 diffing */extdiff.*/a.2a13a4d2da36/a */a/a (glob)
81 81 [1]
82 82
83 83
84 84 Test --change option:
85 85
86 86 $ hg ci -d '2 0' -mtest3
87 87 $ hg falabala -c 1
88 88 diffing */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
89 89 [1]
90 90
91 91 Check diff are made from the first parent:
92 92
93 93 $ hg falabala -c 3 || echo "diff-like tools yield a non-zero exit code"
94 94 diffing */extdiff.*/a.2a13a4d2da36/a a.46c0e4daeb72/a (glob)
95 95 diff-like tools yield a non-zero exit code
96 96
97 97 issue4463: usage of command line configuration without additional quoting
98 98
99 99 $ cat <<EOF >> $HGRCPATH
100 100 > [extdiff]
101 101 > cmd.4463a = echo
102 102 > opts.4463a = a-naked 'single quoted' "double quoted"
103 103 > 4463b = echo b-naked 'single quoted' "double quoted"
104 104 > echo =
105 105 > EOF
106 106 $ hg update -q -C 0
107 107 $ echo a >> a
108 108 #if windows
109 109 $ hg --debug 4463a | grep '^running'
110 110 running 'echo a-naked \'single quoted\' "double quoted" *\\a *\\a' in */extdiff.* (glob)
111 111 $ hg --debug 4463b | grep '^running'
112 112 running 'echo b-naked \'single quoted\' "double quoted" *\\a *\\a' in */extdiff.* (glob)
113 113 $ hg --debug echo | grep '^running'
114 114 running '*echo* *\\a *\\a' in */extdiff.* (glob)
115 115 #else
116 116 $ hg --debug 4463a | grep '^running'
117 running '\'echo\' a-naked \'single quoted\' "double quoted" \'*/a\' \'$TESTTMP/a/a\'' in */extdiff.* (glob)
117 running 'echo a-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob)
118 118 $ hg --debug 4463b | grep '^running'
119 running 'echo b-naked \'single quoted\' "double quoted" \'*/a\' \'$TESTTMP/a/a\'' in */extdiff.* (glob)
119 running 'echo b-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob)
120 120 $ hg --debug echo | grep '^running'
121 running "'*echo*' '*/a' '$TESTTMP/a/a'" in */extdiff.* (glob)
121 running '*echo */a $TESTTMP/a/a' in */extdiff.* (glob)
122 122 #endif
123 123
124 124 (getting options from other than extdiff section)
125 125
126 126 $ cat <<EOF >> $HGRCPATH
127 127 > [extdiff]
128 128 > # using diff-tools diffargs
129 129 > 4463b2 = echo
130 130 > # using merge-tools diffargs
131 131 > 4463b3 = echo
132 132 > # no diffargs
133 133 > 4463b4 = echo
134 134 > [diff-tools]
135 135 > 4463b2.diffargs = b2-naked 'single quoted' "double quoted"
136 136 > [merge-tools]
137 137 > 4463b3.diffargs = b3-naked 'single quoted' "double quoted"
138 138 > EOF
139 139 #if windows
140 140 $ hg --debug 4463b2 | grep '^running'
141 141 running 'echo b2-naked \'single quoted\' "double quoted" *\\a *\\a' in */extdiff.* (glob)
142 142 $ hg --debug 4463b3 | grep '^running'
143 143 running 'echo b3-naked \'single quoted\' "double quoted" *\\a *\\a' in */extdiff.* (glob)
144 144 $ hg --debug 4463b4 | grep '^running'
145 145 running 'echo *\\a *\\a' in */extdiff.* (glob)
146 146 $ hg --debug 4463b4 --option b4-naked --option 'being quoted' | grep '^running'
147 147 running 'echo b4-naked "being quoted" *\\a *\\a' in */extdiff.* (glob)
148 148 $ hg --debug extdiff -p echo --option echo-naked --option 'being quoted' | grep '^running'
149 149 running 'echo echo-naked "being quoted" *\\a *\\a' in */extdiff.* (glob)
150 150 #else
151 151 $ hg --debug 4463b2 | grep '^running'
152 running 'echo b2-naked \'single quoted\' "double quoted" \'*/a\' \'$TESTTMP/a/a\'' in */extdiff.* (glob)
152 running 'echo b2-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob)
153 153 $ hg --debug 4463b3 | grep '^running'
154 running 'echo b3-naked \'single quoted\' "double quoted" \'*/a\' \'$TESTTMP/a/a\'' in */extdiff.* (glob)
154 running 'echo b3-naked \'single quoted\' "double quoted" */a $TESTTMP/a/a' in */extdiff.* (glob)
155 155 $ hg --debug 4463b4 | grep '^running'
156 running "echo '*/a' '$TESTTMP/a/a'" in */extdiff.* (glob)
157 $ hg --debug 4463b4 --option 'being quoted' | grep '^running'
158 running "echo 'being quoted' '*/a' '$TESTTMP/a/a'" in */extdiff.* (glob)
159 $ hg --debug extdiff -p echo --option 'being quoted' | grep '^running'
160 running "'echo' 'being quoted' '*/a' '$TESTTMP/a/a'" in */extdiff.* (glob)
156 running 'echo */a $TESTTMP/a/a' in */extdiff.* (glob)
157 $ hg --debug 4463b4 --option b4-naked --option 'being quoted' | grep '^running'
158 running "echo b4-naked 'being quoted' */a $TESTTMP/a/a" in */extdiff.* (glob)
159 $ hg --debug extdiff -p echo --option echo-naked --option 'being quoted' | grep '^running'
160 running "echo echo-naked 'being quoted' */a $TESTTMP/a/a" in */extdiff.* (glob)
161 161 #endif
162 162
163 163 #if execbit
164 164
165 165 Test extdiff of multiple files in tmp dir:
166 166
167 167 $ hg update -C 0 > /dev/null
168 168 $ echo changed > a
169 169 $ echo changed > b
170 170 $ chmod +x b
171 171
172 172 Diff in working directory, before:
173 173
174 174 $ hg diff --git
175 175 diff --git a/a b/a
176 176 --- a/a
177 177 +++ b/a
178 178 @@ -1,1 +1,1 @@
179 179 -a
180 180 +changed
181 181 diff --git a/b b/b
182 182 old mode 100644
183 183 new mode 100755
184 184 --- a/b
185 185 +++ b/b
186 186 @@ -1,1 +1,1 @@
187 187 -b
188 188 +changed
189 189
190 190
191 191 Edit with extdiff -p:
192 192
193 193 Prepare custom diff/edit tool:
194 194
195 195 $ cat > 'diff tool.py' << EOT
196 196 > #!/usr/bin/env python
197 197 > import time
198 198 > time.sleep(1) # avoid unchanged-timestamp problems
199 199 > file('a/a', 'ab').write('edited\n')
200 200 > file('a/b', 'ab').write('edited\n')
201 201 > EOT
202 202
203 203 $ chmod +x 'diff tool.py'
204 204
205 205 will change to /tmp/extdiff.TMP and populate directories a.TMP and a
206 206 and start tool
207 207
208 208 $ hg extdiff -p "`pwd`/diff tool.py"
209 209 [1]
210 210
211 211 Diff in working directory, after:
212 212
213 213 $ hg diff --git
214 214 diff --git a/a b/a
215 215 --- a/a
216 216 +++ b/a
217 217 @@ -1,1 +1,2 @@
218 218 -a
219 219 +changed
220 220 +edited
221 221 diff --git a/b b/b
222 222 old mode 100644
223 223 new mode 100755
224 224 --- a/b
225 225 +++ b/b
226 226 @@ -1,1 +1,2 @@
227 227 -b
228 228 +changed
229 229 +edited
230 230
231 231 Test extdiff with --option:
232 232
233 233 $ hg extdiff -p echo -o this -c 1
234 234 this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
235 235 [1]
236 236
237 237 $ hg falabala -o this -c 1
238 238 diffing this */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
239 239 [1]
240 240
241 241 Test extdiff's handling of options with spaces in them:
242 242
243 243 $ hg edspace -c 1
244 244 name <user@example.com> */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
245 245 [1]
246 246
247 247 $ hg extdiff -p echo -o "name <user@example.com>" -c 1
248 248 name <user@example.com> */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
249 249 [1]
250 250
251 251 Test with revsets:
252 252
253 253 $ hg extdif -p echo -c "rev(1)"
254 254 */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
255 255 [1]
256 256
257 257 $ hg extdif -p echo -r "0::1"
258 258 */extdiff.*/a.8a5febb7f867/a a.34eed99112ab/a (glob)
259 259 [1]
260 260
261 261 Fallback to merge-tools.tool.executable|regkey
262 262 $ mkdir dir
263 263 $ cat > 'dir/tool.sh' << EOF
264 264 > #!/bin/sh
265 265 > echo "** custom diff **"
266 266 > EOF
267 267 $ chmod +x dir/tool.sh
268 268 $ tool=`pwd`/dir/tool.sh
269 269 $ hg --debug tl --config extdiff.tl= --config merge-tools.tl.executable=$tool
270 270 making snapshot of 2 files from rev * (glob)
271 271 a
272 272 b
273 273 making snapshot of 2 files from working directory
274 274 a
275 275 b
276 running "'$TESTTMP/a/dir/tool.sh' 'a.*' 'a'" in */extdiff.* (glob)
276 running '$TESTTMP/a/dir/tool.sh a.* a' in */extdiff.* (glob)
277 277 ** custom diff **
278 278 cleaning up temp directory
279 279 [1]
280 280
281 281 $ cd ..
282 282
283 283 #endif
284 284
285 285 #if symlink
286 286
287 287 Test symlinks handling (issue1909)
288 288
289 289 $ hg init testsymlinks
290 290 $ cd testsymlinks
291 291 $ echo a > a
292 292 $ hg ci -Am adda
293 293 adding a
294 294 $ echo a >> a
295 295 $ ln -s missing linka
296 296 $ hg add linka
297 297 $ hg falabala -r 0 --traceback
298 298 diffing testsymlinks.07f494440405 testsymlinks
299 299 [1]
300 300 $ cd ..
301 301
302 302 #endif
General Comments 0
You need to be logged in to leave comments. Login now