##// END OF EJS Templates
dirstate: ignore symlinks when fs cannot handle them (issue1888)...
dirstate: ignore symlinks when fs cannot handle them (issue1888) When the filesystem cannot handle the executable bit, we currently ignore it completely when looking for modified files. Similarly, it is impossible to set or clear the bit when the filesystem ignores it. This patch makes Mercurial treat symbolic links the same way. Symlinks are a little different since they manifest themselves as small files containing a filename (the symlink target). On Windows, these files show up as regular files, and on Linux and Mac they show up as real symlinks. Issue1888 presents a case where the symlink files are better ignored from the Windows side. A Linux client creates symlinks in a working copy which is shared over a network between Linux and Windows clients. The Samba server is helpful and defererences the symlink when the Windows client looks at it. This means that Mercurial on the Windows side sees file content instead of a file name in the symlink, and hence flags the link as modified. Ignoring the change would be much more helpful, similarly to how Mercurial does not report any changes when executable bits are ignored in a checkout on Windows. An initial checkout of a symbolic link on a file system that cannot handle symbolic links will still result in a regular file containing the target file name as its content. Sharing such a checkout with a Linux client will not turn the file into a symlink automatically, but 'hg revert' can fix that. After the revert, the Windows client will see the correct file content (provided by the Samba server when it follows the link on the Linux side) and otherwise ignore the change. Running 'hg perfstatus' 10 times gives these results: Before: After: min: 0.544703 min: 0.546549 med: 0.547592 med: 0.548881 avg: 0.549146 avg: 0.548549 max: 0.564112 max: 0.551504 The median time is increased about 0.24%.

File last commit:

r11304:8c377f2f default
r11769:ca6cebd8 stable
Show More
win32.py
204 lines | 7.1 KiB | text/x-python | PythonLexer
Martin Geisler
put license and copyright info into comment blocks
r8226 # win32.py - utility functions that use win32 API
#
# Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
#
# This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Martin Geisler
turn some comments back into module docstrings
r8227
"""Utility functions that use win32 API.
Mark Hammond's win32all package allows better functionality on
Windows. This module overrides definitions in util.py. If not
available, import of this module will fail, and generic code will be
used.
"""
Matt Mackall
util: split out posix, windows, and win32 modules
r7890
import win32api
import errno, os, sys, pywintypes, win32con, win32file, win32process
Patrick Mezard
win32: detect console width on Windows...
r11012 import winerror, win32gui, win32console
Matt Mackall
move encoding bits from util to encoding...
r7948 import osutil, encoding
Martin Geisler
coding style: use a space after comma...
r9198 from win32com.shell import shell, shellcon
Matt Mackall
util: split out posix, windows, and win32 modules
r7890
def os_link(src, dst):
try:
win32file.CreateHardLink(dst, src)
# CreateHardLink sometimes succeeds on mapped drives but
# following nlinks() returns 1. Check it now and bail out.
if nlinks(src) < 2:
try:
win32file.DeleteFile(dst)
except:
pass
# Fake hardlinking error
Henrik Stuart
windows: fix use of undefined exception (issue1707)...
r8951 raise OSError(errno.EINVAL, 'Hardlinking not supported')
Dirkjan Ochtman
cleanups: unused variables
r11304 except pywintypes.error:
Henrik Stuart
windows: fix use of undefined exception (issue1707)...
r8951 raise OSError(errno.EINVAL, 'target implements hardlinks improperly')
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 except NotImplementedError: # Another fake error win Win98
Henrik Stuart
windows: fix use of undefined exception (issue1707)...
r8951 raise OSError(errno.EINVAL, 'Hardlinking not supported')
Matt Mackall
util: split out posix, windows, and win32 modules
r7890
Siddharth Agarwal
Add support for relinking on Windows....
r10218 def _getfileinfo(pathname):
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 """Return number of hardlinks for the given file."""
try:
fh = win32file.CreateFile(pathname,
win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
None, win32file.OPEN_EXISTING, 0, None)
Patrick Mezard
win32: close file when leaving _getfileinfo()
r10219 try:
return win32file.GetFileInformationByHandle(fh)
finally:
fh.Close()
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 except pywintypes.error:
Siddharth Agarwal
Add support for relinking on Windows....
r10218 return None
def nlinks(pathname):
"""Return number of hardlinks for the given file."""
res = _getfileinfo(pathname)
if res is not None:
return res[7]
else:
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 return os.lstat(pathname).st_nlink
Siddharth Agarwal
Add support for relinking on Windows....
r10218 def samefile(fpath1, fpath2):
"""Returns whether fpath1 and fpath2 refer to the same file. This is only
guaranteed to work for files, not directories."""
res1 = _getfileinfo(fpath1)
res2 = _getfileinfo(fpath2)
if res1 is not None and res2 is not None:
# Index 4 is the volume serial number, and 8 and 9 contain the file ID
return res1[4] == res2[4] and res1[8] == res2[8] and res1[9] == res2[9]
else:
return False
def samedevice(fpath1, fpath2):
"""Returns whether fpath1 and fpath2 are on the same device. This is only
guaranteed to work for files, not directories."""
res1 = _getfileinfo(fpath1)
res2 = _getfileinfo(fpath2)
if res1 is not None and res2 is not None:
return res1[4] == res2[4]
else:
return False
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 def testpid(pid):
'''return True if pid is still running or unable to
determine, False otherwise'''
try:
handle = win32api.OpenProcess(
win32con.PROCESS_QUERY_INFORMATION, False, pid)
if handle:
status = win32process.GetExitCodeProcess(handle)
return status == win32con.STILL_ACTIVE
except pywintypes.error, details:
return details[0] != winerror.ERROR_INVALID_PARAMETER
return True
def lookup_reg(key, valname=None, scope=None):
''' Look up a key/value name in the Windows registry.
valname: value name. If unspecified, the default value for the key
is used.
scope: optionally specify scope for registry lookup, this can be
a sequence of scopes to look up in order. Default (CURRENT_USER,
LOCAL_MACHINE).
'''
try:
from _winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, \
QueryValueEx, OpenKey
except ImportError:
return None
if scope is None:
scope = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE)
elif not isinstance(scope, (list, tuple)):
scope = (scope,)
for s in scope:
try:
val = QueryValueEx(OpenKey(s, key), valname)[0]
# never let a Unicode string escape into the wild
Matt Mackall
move encoding bits from util to encoding...
r7948 return encoding.tolocal(val.encode('UTF-8'))
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 except EnvironmentError:
pass
def system_rcpath_win32():
'''return default os-specific hgrc search path'''
proc = win32api.GetCurrentProcess()
try:
# This will fail on windows < NT
filename = win32process.GetModuleFileNameEx(proc, 0)
except:
filename = win32api.GetModuleFileName(0)
# Use mercurial.ini found in directory with hg.exe
progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
if os.path.isfile(progrc):
return [progrc]
Steve Borho
win32: allow hgrc.d on Windows
r10388 # Use hgrc.d found in directory with hg.exe
progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
if os.path.isdir(progrcd):
rcpath = []
for f, kind in osutil.listdir(progrcd):
if f.endswith('.rc'):
rcpath.append(os.path.join(progrcd, f))
return rcpath
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 # else look for a system rcpath in the registry
try:
value = win32api.RegQueryValue(
win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Mercurial')
rcpath = []
for p in value.split(os.pathsep):
if p.lower().endswith('mercurial.ini'):
rcpath.append(p)
elif os.path.isdir(p):
for f, kind in osutil.listdir(p):
if f.endswith('.rc'):
rcpath.append(os.path.join(p, f))
return rcpath
except pywintypes.error:
return []
def user_rcpath_win32():
'''return os-specific hgrc search path to the user dir'''
userdir = os.path.expanduser('~')
if sys.getwindowsversion()[3] != 2 and userdir == '~':
# We are on win < nt: fetch the APPDATA directory location and use
# the parent directory as the user home dir.
appdir = shell.SHGetPathFromIDList(
shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
userdir = os.path.dirname(appdir)
return [os.path.join(userdir, 'mercurial.ini'),
os.path.join(userdir, '.hgrc')]
def getuser():
'''return name of current user'''
return win32api.GetUserName()
def set_signal_handler_win32():
"""Register a termination handler for console events including
CTRL+C. python signal handlers do not work well with socket
operations.
"""
def handler(event):
win32process.ExitProcess(1)
win32api.SetConsoleCtrlHandler(handler)
Patrick Mezard
cmdutil: hide child window created by win32 spawndetached()...
r10240 def hidewindow():
def callback(*args, **kwargs):
hwnd, pid = args
wpid = win32process.GetWindowThreadProcessId(hwnd)[1]
if pid == wpid:
win32gui.ShowWindow(hwnd, win32con.SW_HIDE)
pid = win32process.GetCurrentProcessId()
win32gui.EnumWindows(callback, pid)
Patrick Mezard
win32: detect console width on Windows...
r11012
def termwidth_():
try:
# Query stderr to avoid problems with redirections
screenbuf = win32console.GetStdHandle(win32console.STD_ERROR_HANDLE)
try:
window = screenbuf.GetConsoleScreenBufferInfo()['Window']
width = window.Right - window.Left
return width
finally:
screenbuf.Detach()
except pywintypes.error:
return 79