mirror of
https://github.com/beetbox/beets.git
synced 2026-01-07 16:34:45 +01:00
Add reflink routine
This commit is contained in:
parent
2926b49628
commit
5e2856ef87
4 changed files with 85 additions and 1 deletions
|
|
@ -223,7 +223,8 @@ class ImportSession(object):
|
|||
iconfig['incremental'] = False
|
||||
|
||||
if iconfig['reflink']:
|
||||
iconfig['reflink'] = iconfig['reflink'].as_choice(['auto', True, False])
|
||||
iconfig['reflink'] = iconfig['reflink'] \
|
||||
.as_choice(['auto', True, False])
|
||||
|
||||
# Copy, move, reflink, link, and hardlink are mutually exclusive.
|
||||
if iconfig['move']:
|
||||
|
|
|
|||
|
|
@ -747,6 +747,20 @@ class Item(LibModel):
|
|||
util.hardlink(self.path, dest)
|
||||
plugins.send("item_hardlinked", item=self, source=self.path,
|
||||
destination=dest)
|
||||
elif operation == MoveOperation.REFLINK:
|
||||
util.reflink(self.path, dest, fallback=False)
|
||||
plugins.send("item_reflinked", item=self, source=self.path,
|
||||
destination=dest)
|
||||
elif operation == MoveOperation.REFLINK_AUTO:
|
||||
util.reflink(self.path, dest, fallback=True)
|
||||
plugins.send("item_reflinked", item=self, source=self.path,
|
||||
destination=dest)
|
||||
else:
|
||||
plugins.send("before_item_moved", item=self, source=self.path,
|
||||
destination=dest)
|
||||
util.move(self.path, dest)
|
||||
plugins.send("item_moved", item=self, source=self.path,
|
||||
destination=dest)
|
||||
|
||||
# Either copying or moving succeeded, so update the stored path.
|
||||
self.path = dest
|
||||
|
|
@ -1087,6 +1101,12 @@ class Album(LibModel):
|
|||
util.link(old_art, new_art)
|
||||
elif operation == MoveOperation.HARDLINK:
|
||||
util.hardlink(old_art, new_art)
|
||||
elif operation == MoveOperation.REFLINK:
|
||||
util.reflink(old_art, new_art, fallback=False)
|
||||
elif operation == MoveOperation.REFLINK_AUTO:
|
||||
util.reflink(old_art, new_art, fallback=True)
|
||||
else:
|
||||
util.move(old_art, new_art)
|
||||
self.artpath = new_art
|
||||
|
||||
def move(self, operation=MoveOperation.MOVE, basedir=None, store=True):
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ from beets.util import hidden
|
|||
import six
|
||||
from unidecode import unidecode
|
||||
from enum import Enum
|
||||
import reflink as pyreflink
|
||||
|
||||
|
||||
MAX_FILENAME_LENGTH = 200
|
||||
|
|
@ -547,6 +548,28 @@ def hardlink(path, dest, replace=False):
|
|||
traceback.format_exc())
|
||||
|
||||
|
||||
def reflink(path, dest, replace=False, fallback=False):
|
||||
"""Create a reflink from `dest` to `path`. Raises an `OSError` if
|
||||
`dest` already exists, unless `replace` is True. Does nothing if
|
||||
`path` == `dest`. When `fallback` is True, `reflink` falls back on
|
||||
`copy` when the filesystem does not support reflinks.
|
||||
"""
|
||||
if samefile(path, dest):
|
||||
return
|
||||
|
||||
if os.path.exists(syspath(dest)) and not replace:
|
||||
raise FilesystemError(u'file exists', 'rename', (path, dest))
|
||||
|
||||
try:
|
||||
pyreflink.reflink(path, dest)
|
||||
except (NotImplementedError, pyreflink.ReflinkImpossibleError) as exc:
|
||||
if fallback:
|
||||
copy(path, dest, replace)
|
||||
else:
|
||||
raise FilesystemError(u'OS/filesystem does not support reflinks.',
|
||||
'link', (path, dest), traceback.format_exc())
|
||||
|
||||
|
||||
def unique_path(path):
|
||||
"""Returns a version of ``path`` that does not exist on the
|
||||
filesystem. Specifically, if ``path` itself already exists, then
|
||||
|
|
|
|||
|
|
@ -86,6 +86,24 @@ class MoveTest(_common.TestCase):
|
|||
self.i.move(operation=MoveOperation.COPY)
|
||||
self.assertExists(self.path)
|
||||
|
||||
def test_reflink_arrives(self):
|
||||
self.i.move(operation=MoveOperation.REFLINK_AUTO)
|
||||
self.assertExists(self.dest)
|
||||
|
||||
def test_reflink_does_not_depart(self):
|
||||
self.i.move(operation=MoveOperation.REFLINK_AUTO)
|
||||
self.assertExists(self.path)
|
||||
|
||||
@unittest.skipUnless(_common.HAVE_REFLINK, "need reflink")
|
||||
def test_force_reflink_arrives(self):
|
||||
self.i.move(operation=MoveOperation.REFLINK)
|
||||
self.assertExists(self.dest)
|
||||
|
||||
@unittest.skipUnless(_common.HAVE_REFLINK, "need reflink")
|
||||
def test_force_reflink_does_not_depart(self):
|
||||
self.i.move(operation=MoveOperation.REFLINK)
|
||||
self.assertExists(self.path)
|
||||
|
||||
def test_move_changes_path(self):
|
||||
self.i.move()
|
||||
self.assertEqual(self.i.path, util.normpath(self.dest))
|
||||
|
|
@ -249,6 +267,17 @@ class AlbumFileTest(_common.TestCase):
|
|||
self.assertTrue(os.path.exists(oldpath))
|
||||
self.assertTrue(os.path.exists(self.i.path))
|
||||
|
||||
@unittest.skipUnless(_common.HAVE_REFLINK, "need reflink")
|
||||
def test_albuminfo_move_reflinks_file(self):
|
||||
oldpath = self.i.path
|
||||
self.ai.album = u'newAlbumName'
|
||||
self.ai.move(operation=MoveOperation.REFLINK)
|
||||
self.ai.store()
|
||||
self.i.load()
|
||||
|
||||
self.assertTrue(os.path.exists(oldpath))
|
||||
self.assertTrue(os.path.exists(self.i.path))
|
||||
|
||||
def test_albuminfo_move_to_custom_dir(self):
|
||||
self.ai.move(basedir=self.otherdir)
|
||||
self.i.load()
|
||||
|
|
@ -530,6 +559,12 @@ class SafeMoveCopyTest(_common.TestCase):
|
|||
self.assertExists(self.dest)
|
||||
self.assertExists(self.path)
|
||||
|
||||
@unittest.skipUnless(_common.HAVE_REFLINK, "need reflink")
|
||||
def test_successful_reflink(self):
|
||||
util.reflink(self.path, self.dest)
|
||||
self.assertExists(self.dest)
|
||||
self.assertExists(self.path)
|
||||
|
||||
def test_unsuccessful_move(self):
|
||||
with self.assertRaises(util.FilesystemError):
|
||||
util.move(self.path, self.otherpath)
|
||||
|
|
@ -538,6 +573,11 @@ class SafeMoveCopyTest(_common.TestCase):
|
|||
with self.assertRaises(util.FilesystemError):
|
||||
util.copy(self.path, self.otherpath)
|
||||
|
||||
@unittest.skipUnless(_common.HAVE_REFLINK, "need reflink")
|
||||
def test_unsuccessful_reflink(self):
|
||||
with self.assertRaises(util.FilesystemError):
|
||||
util.reflink(self.path, self.otherpath)
|
||||
|
||||
def test_self_move(self):
|
||||
util.move(self.path, self.path)
|
||||
self.assertExists(self.path)
|
||||
|
|
|
|||
Loading…
Reference in a new issue