fix unannex data overwrite bug

unannex, uninit: When an annexed file is modified, don't overwrite the
modified version with an older version from the annex

This commit was sponsored by Mark Reidenbach on Patreon.
This commit is contained in:
Joey Hess 2021-02-22 13:35:00 -04:00
parent 224bc7579b
commit 530e96b80e
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
3 changed files with 27 additions and 8 deletions

View file

@ -28,7 +28,9 @@ git-annex (8.20210128) UNRELEASED; urgency=medium
rather than doing it in a second pass. rather than doing it in a second pass.
* Bugfix: fsck --from a ssh remote did not actually check that the * Bugfix: fsck --from a ssh remote did not actually check that the
content on the remote is not corrupted. content on the remote is not corrupted.
* unannex, uninit: Avoid running git rm once per annexed file, * unannex, uninit: When an annexed file is modified, don't overwrite
the modified version with an older version from the annex.
* unannex, uninit: Don't run git rm once per annexed file,
for a large speedup. for a large speedup.
-- Joey Hess <id@joeyh.name> Thu, 28 Jan 2021 12:34:32 -0400 -- Joey Hess <id@joeyh.name> Thu, 28 Jan 2021 12:34:32 -0400

View file

@ -10,6 +10,7 @@ module Command.Unannex where
import Command import Command
import qualified Annex import qualified Annex
import Annex.Perms import Annex.Perms
import Annex.Link
import qualified Annex.Queue import qualified Annex.Queue
import Utility.CopyFile import Utility.CopyFile
import qualified Database.Keys import qualified Database.Keys
@ -41,7 +42,6 @@ start si file key =
perform :: RawFilePath -> Key -> CommandPerform perform :: RawFilePath -> Key -> CommandPerform
perform file key = do perform file key = do
liftIO $ removeFile (fromRawFilePath file)
Annex.Queue.addCommand [] "rm" Annex.Queue.addCommand [] "rm"
[ Param "--cached" [ Param "--cached"
, Param "--force" , Param "--force"
@ -49,19 +49,34 @@ perform file key = do
, Param "--" , Param "--"
] ]
[fromRawFilePath file] [fromRawFilePath file]
next $ cleanup file key isAnnexLink file >>= \case
-- If the file is locked, it needs to be replaced with
-- the content from the annex. Note that it's possible
-- for key' (read from the symlink) to differ from key
-- (cached in git).
Just key' -> do
removeassociated
next $ cleanup file key'
-- If the file is unlocked, it can be unmodified or not and
-- does not need to be replaced either way.
Nothing -> do
removeassociated
next $ return True
where
removeassociated =
Database.Keys.removeAssociatedFile key
=<< inRepo (toTopFilePath file)
cleanup :: RawFilePath -> Key -> CommandCleanup cleanup :: RawFilePath -> Key -> CommandCleanup
cleanup file key = do cleanup file key = do
Database.Keys.removeAssociatedFile key =<< inRepo (toTopFilePath file) liftIO $ removeFile (fromRawFilePath file)
src <- calcRepo (gitAnnexLocation key) src <- calcRepo (gitAnnexLocation key)
ifM (Annex.getState Annex.fast) ifM (Annex.getState Annex.fast)
( do ( do
-- Only make a hard link if the annexed file does not -- Only make a hard link if the annexed file does not
-- already have other hard links pointing at it. -- already have other hard links pointing at it. This
-- This avoids unannexing (and uninit) ending up -- avoids unannexing (and uninit) ending up hard
-- hard linking files together, which would be -- linking files together, which would be surprising.
-- surprising.
s <- liftIO $ R.getFileStatus src s <- liftIO $ R.getFileStatus src
if linkCount s > 1 if linkCount s > 1
then copyfrom src then copyfrom src

View file

@ -4,3 +4,5 @@ This is a data loss bug.
Command.Unannex.cleanup just overwrites whatever's there without checking. Command.Unannex.cleanup just overwrites whatever's there without checking.
Happens with both locked and unlocked files. --[[Joey]] Happens with both locked and unlocked files. --[[Joey]]
> [[fixed|done]] --[[Joey]]