git-annex/Command/Add.hs
Joey Hess 6869e6023e support .git/annex on a different disk than the rest of the repo
The only fully supported thing is to have the main repository on one disk,
and .git/annex on another. Only commands that move data in/out of the annex
will need to copy it across devices.

There is only partial support for putting arbitrary subdirectories of
.git/annex on different devices. For one thing, but this can require more
copies to be done. For example, when .git/annex/tmp is on one device, and
.git/annex/journal on another, every journal write involves a call to
mv(1). Also, there are a few places that make hard links between various
subdirectories of .git/annex with createLink, that are not handled.

In the common case without cross-device, the new moveFile is actually
faster than renameFile, avoiding an unncessary stat to check that a file
(not a directory) is being moved. Of course if a cross-device move is
needed, it is as slow as mv(1) of the data.
2011-11-28 16:17:55 -04:00

85 lines
2.4 KiB
Haskell

{- git-annex command
-
- Copyright 2010 Joey Hess <joey@kitenet.net>
-
- Licensed under the GNU GPL version 3 or higher.
-}
module Command.Add where
import Common.Annex
import Annex.Exception
import Command
import qualified Annex
import qualified Annex.Queue
import qualified Backend
import Logs.Location
import Annex.Content
import Utility.Touch
import Backend
def :: [Command]
def = [command "add" paramPaths seek "add files to annex"]
{- Add acts on both files not checked into git yet, and unlocked files. -}
seek :: [CommandSeek]
seek = [withFilesNotInGit start, withFilesUnlocked start]
{- The add subcommand annexes a file, storing it in a backend, and then
- moving it into the annex directory and setting up the symlink pointing
- to its content. -}
start :: BackendFile -> CommandStart
start p@(_, file) = notBareRepo $ notAnnexed file $ do
s <- liftIO $ getSymbolicLinkStatus file
if isSymbolicLink s || not (isRegularFile s)
then stop
else do
showStart "add" file
next $ perform p
perform :: BackendFile -> CommandPerform
perform (backend, file) = Backend.genKey file backend >>= go
where
go Nothing = stop
go (Just (key, _)) = do
handle (undo file key) $ moveAnnex key file
next $ cleanup file key True
{- On error, put the file back so it doesn't seem to have vanished.
- This can be called before or after the symlink is in place. -}
undo :: FilePath -> Key -> IOException -> Annex a
undo file key e = do
unlessM (inAnnex key) rethrow -- no cleanup to do
liftIO $ whenM (doesFileExist file) $ removeFile file
handle tryharder $ fromAnnex key file
logStatus key InfoMissing
rethrow
where
rethrow = throw e
-- fromAnnex could fail if the file ownership is weird
tryharder :: IOException -> Annex ()
tryharder _ = do
src <- fromRepo $ gitAnnexLocation key
liftIO $ moveFile src file
cleanup :: FilePath -> Key -> Bool -> CommandCleanup
cleanup file key hascontent = do
handle (undo file key) $ do
link <- calcGitLink file key
liftIO $ createSymbolicLink link file
when hascontent $ do
logStatus key InfoPresent
-- touch the symlink to have the same mtime as the
-- file it points to
liftIO $ do
mtime <- modificationTime <$> getFileStatus file
touch file (TimeSpec mtime) False
force <- Annex.getState Annex.force
if force
then Annex.Queue.add "add" [Param "-f", Param "--"] [file]
else Annex.Queue.add "add" [Param "--"] [file]
return True