comparison mercurial/util.py @ 47400:9b841267253c

util: add `nb_bytes` argument to `copyfile` to partially copy a file When set, this allow to copy only the first `nb_bytes` of a file. This will be useful for censor/strip operation with revlogv2. Differential Revision: https://phab.mercurial-scm.org/D10798
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Sun, 30 May 2021 18:08:52 +0200
parents bcafcd779d2e
children 9ea525216edb
comparison
equal deleted inserted replaced
47399:34cc102c73f5 47400:9b841267253c
1907 b'xfs', 1907 b'xfs',
1908 b'zfs', 1908 b'zfs',
1909 } 1909 }
1910 1910
1911 1911
1912 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False): 1912 def copyfile(
1913 src, dest, hardlink=False, copystat=False, checkambig=False, nb_bytes=None
1914 ):
1913 """copy a file, preserving mode and optionally other stat info like 1915 """copy a file, preserving mode and optionally other stat info like
1914 atime/mtime 1916 atime/mtime
1915 1917
1916 checkambig argument is used with filestat, and is useful only if 1918 checkambig argument is used with filestat, and is useful only if
1917 destination file is guarded by any lock (e.g. repo.lock or 1919 destination file is guarded by any lock (e.g. repo.lock or
1918 repo.wlock). 1920 repo.wlock).
1919 1921
1920 copystat and checkambig should be exclusive. 1922 copystat and checkambig should be exclusive.
1923
1924 nb_bytes: if set only copy the first `nb_bytes` of the source file.
1921 """ 1925 """
1922 assert not (copystat and checkambig) 1926 assert not (copystat and checkambig)
1923 oldstat = None 1927 oldstat = None
1924 if os.path.lexists(dest): 1928 if os.path.lexists(dest):
1925 if checkambig: 1929 if checkambig:
1935 if fstype not in _hardlinkfswhitelist: 1939 if fstype not in _hardlinkfswhitelist:
1936 hardlink = False 1940 hardlink = False
1937 if hardlink: 1941 if hardlink:
1938 try: 1942 try:
1939 oslink(src, dest) 1943 oslink(src, dest)
1944 if nb_bytes is not None:
1945 m = "the `nb_bytes` argument is incompatible with `hardlink`"
1946 raise error.ProgrammingError(m)
1940 return 1947 return
1941 except (IOError, OSError): 1948 except (IOError, OSError):
1942 pass # fall back to normal copy 1949 pass # fall back to normal copy
1943 if os.path.islink(src): 1950 if os.path.islink(src):
1944 os.symlink(os.readlink(src), dest) 1951 os.symlink(os.readlink(src), dest)
1945 # copytime is ignored for symlinks, but in general copytime isn't needed 1952 # copytime is ignored for symlinks, but in general copytime isn't needed
1946 # for them anyway 1953 # for them anyway
1954 if nb_bytes is not None:
1955 m = "cannot use `nb_bytes` on a symlink"
1956 raise error.ProgrammingError(m)
1947 else: 1957 else:
1948 try: 1958 try:
1949 shutil.copyfile(src, dest) 1959 shutil.copyfile(src, dest)
1950 if copystat: 1960 if copystat:
1951 # copystat also copies mode 1961 # copystat also copies mode
1958 # stat of copied file is ambiguous to original one 1968 # stat of copied file is ambiguous to original one
1959 advanced = ( 1969 advanced = (
1960 oldstat.stat[stat.ST_MTIME] + 1 1970 oldstat.stat[stat.ST_MTIME] + 1
1961 ) & 0x7FFFFFFF 1971 ) & 0x7FFFFFFF
1962 os.utime(dest, (advanced, advanced)) 1972 os.utime(dest, (advanced, advanced))
1973 # We could do something smarter using `copy_file_range` call or similar
1974 if nb_bytes is not None:
1975 with open(dest, mode='r+') as f:
1976 f.truncate(nb_bytes)
1963 except shutil.Error as inst: 1977 except shutil.Error as inst:
1964 raise error.Abort(stringutil.forcebytestr(inst)) 1978 raise error.Abort(stringutil.forcebytestr(inst))
1965 1979
1966 1980
1967 def copyfiles(src, dst, hardlink=None, progress=None): 1981 def copyfiles(src, dst, hardlink=None, progress=None):