direct mode committing

This commit is contained in:
Joey Hess 2012-12-12 19:20:38 -04:00
parent bfb446604a
commit e7b8cb0063
11 changed files with 186 additions and 54 deletions

View file

@ -9,6 +9,7 @@ module Annex.CatFile (
catFile,
catObject,
catObjectDetails,
catKey,
catFileHandle
) where
@ -42,3 +43,7 @@ catFileHandle = maybe startup return =<< Annex.getState Annex.catfilehandle
h <- inRepo Git.CatFile.catFileStart
Annex.changeState $ \s -> s { Annex.catfilehandle = Just h }
return h
{- From the Sha of a symlink back to the key. -}
catKey :: Sha -> Annex (Maybe Key)
catKey sha = fileKey . takeFileName . encodeW8 . L.unpack <$> catObject sha

View file

@ -10,7 +10,6 @@ module Annex.Content (
inAnnexSafe,
lockContent,
calcGitLink,
logStatus,
getViaTmp,
getViaTmpUnchecked,
withTmp,
@ -33,7 +32,6 @@ import System.IO.Unsafe (unsafeInterleaveIO)
import Common.Annex
import Logs.Location
import Annex.UUID
import qualified Git
import qualified Git.Config
import qualified Annex
@ -132,13 +130,6 @@ calcGitLink file key = do
where
whoops = error $ "unable to normalize " ++ file
{- Updates the Logs.Location when a key's presence changes in the current
- repository. -}
logStatus :: Key -> LogStatus -> Annex ()
logStatus key status = do
u <- getUUID
logChange key u status
{- Runs an action, passing it a temporary filename to get,
- and if the action succeeds, moves the temp file into
- the annex as a key's content. -}

View file

@ -7,13 +7,18 @@
module Annex.Content.Direct (
associatedFiles,
changeAssociatedFiles,
removeAssociatedFile,
addAssociatedFile,
updateAssociatedFiles,
goodContent,
updateCache,
recordedCache,
compareCache,
removeCache
writeCache,
removeCache,
genCache,
toCache,
Cache
) where
import Common.Annex
@ -23,9 +28,9 @@ import Git.Sha
import Annex.CatFile
import Utility.TempFile
import Utility.FileMode
import Logs.Location
import System.Posix.Types
import qualified Data.ByteString.Lazy as L
{- Files in the tree that are associated with a key. -}
associatedFiles :: Key -> Annex [FilePath]
@ -42,19 +47,24 @@ associatedFilesRelative key = do
liftIO $ catchDefaultIO [] $ lines <$> readFile mapping
{- Changes the associated files information for a key, applying a
- transformation to the list. -}
changeAssociatedFiles :: Key -> ([FilePath] -> [FilePath]) -> Annex ()
- transformation to the list. Returns a copy of the new info. -}
changeAssociatedFiles :: Key -> ([FilePath] -> [FilePath]) -> Annex [FilePath]
changeAssociatedFiles key transform = do
mapping <- inRepo $ gitAnnexMapping key
files <- associatedFilesRelative key
let files' = transform files
when (files /= files') $
liftIO $ viaTmp writeFile mapping $ unlines files'
return files'
removeAssociatedFile :: Key -> FilePath -> Annex ()
removeAssociatedFile key file = changeAssociatedFiles key $ filter (/= file)
removeAssociatedFile :: Key -> FilePath -> Annex [FilePath]
removeAssociatedFile key file = do
fs <- changeAssociatedFiles key $ filter (/= file)
when (null fs) $
logStatus key InfoMissing
return fs
addAssociatedFile :: Key -> FilePath -> Annex ()
addAssociatedFile :: Key -> FilePath -> Annex [FilePath]
addAssociatedFile key file = changeAssociatedFiles key $ \files ->
if file `elem` files
then files
@ -74,10 +84,8 @@ updateAssociatedFiles oldsha newsha = do
where
go getsha getmode a =
when (getsha item /= nullSha && isSymLink (getmode item)) $ do
key <- getkey $ getsha item
maybe noop (\k -> a k $ DiffTree.file item) key
getkey sha = fileKey . takeFileName . encodeW8 . L.unpack
<$> catObject sha
key <- catKey (getsha item)
maybe noop (\k -> void $ a k $ DiffTree.file item) key
{- Checks if a file in the tree, associated with a key, has not been modified.
-
@ -103,10 +111,13 @@ compareCache file old = do
{- Stores a cache of attributes for a file that is associated with a key. -}
updateCache :: Key -> FilePath -> Annex ()
updateCache key file = do
withCacheFile key $ \cachefile -> do
createDirectoryIfMissing True (parentDir cachefile)
maybe noop (writeFile cachefile . showCache) =<< genCache file
updateCache key file = maybe noop (writeCache key) =<< liftIO (genCache file)
{- Writes a cache for a key. -}
writeCache :: Key -> Cache -> Annex ()
writeCache key cache = withCacheFile key $ \cachefile -> do
createDirectoryIfMissing True (parentDir cachefile)
writeFile cachefile $ showCache cache
{- Removes a cache. -}
removeCache :: Key -> Annex ()
@ -115,7 +126,7 @@ removeCache key = withCacheFile key nukeFile
{- Cache a file's inode, size, and modification time to determine if it's
- been changed. -}
data Cache = Cache FileID FileOffset EpochTime
deriving (Eq)
deriving (Eq, Show)
showCache :: Cache -> String
showCache (Cache inode size mtime) = unwords

105
Annex/Direct.hs Normal file
View file

@ -0,0 +1,105 @@
{- git-annex direct mode
-
- Copyright 2012 Joey Hess <joey@kitenet.net>
-
- Licensed under the GNU GPL version 3 or higher.
-}
module Annex.Direct where
import Common.Annex
import qualified Git
import qualified Git.LsFiles
import qualified Git.UpdateIndex
import qualified Git.HashObject
import qualified Annex.Queue
import Git.Types
import Annex.CatFile
import Logs.Location
import Backend
import Types.KeySource
import Annex.Content
import Annex.Content.Direct
{- Uses git ls-files to find files that need to be committed, and stages
- them into the index. Returns True if some changes were staged. -}
stageDirect :: Annex Bool
stageDirect = do
Annex.Queue.flush
top <- fromRepo Git.repoPath
(l, cleanup) <- inRepo $ Git.LsFiles.stagedDetails [top]
forM_ l go
void $ liftIO cleanup
staged <- Annex.Queue.size
Annex.Queue.flush
return $ staged /= 0
where
{- Determine what kind of modified or deleted file this is, as
- efficiently as we can, by getting any key that's associated
- with it in git, as well as its stat info. -}
go (file, Just sha) = do
mkey <- catKey sha
mstat <- liftIO $ catchMaybeIO $ getSymbolicLinkStatus file
case (mkey, mstat, toCache =<< mstat) of
(Just key, _, Just cache) -> do
{- All direct mode files will show as
- modified, so compare the cache to see if
- it really was. -}
oldcache <- recordedCache key
when (oldcache /= Just cache) $
modifiedannexed file key cache
(Just key, Nothing, _) -> deletedannexed file key
(Nothing, Nothing, _) -> deletegit file
(_, Just _, _) -> addgit file
go (file, Nothing) = do
mstat <- liftIO $ catchMaybeIO $ getSymbolicLinkStatus file
case (mstat, toCache =<< mstat) of
(Nothing, _) -> noop
(Just stat, Just cache)
| isSymbolicLink stat -> addgit file
| otherwise -> void $ addDirect file cache
(Just stat, Nothing)
| isSymbolicLink stat -> addgit file
| otherwise -> noop
modifiedannexed file oldkey cache = do
void $ removeAssociatedFile oldkey file
void $ addDirect file cache
deletedannexed file key = do
void $ removeAssociatedFile key file
deletegit file
addgit file = Annex.Queue.addCommand "add" [Param "-f"] [file]
deletegit file = Annex.Queue.addCommand "rm" [Param "-f"] [file]
{- Adds a file to the annex in direct mode. Can fail, if the file is
- modified or deleted while it's being added. -}
addDirect :: FilePath -> Cache -> Annex Bool
addDirect file cache = do
showStart "add" file
let source = KeySource
{ keyFilename = file
, contentLocation = file
}
got =<< genKey source =<< chooseBackend file
where
got Nothing = do
showEndFail
return False
got (Just (key, _)) = ifM (compareCache file $ Just cache)
( do
link <- calcGitLink file key
sha <- inRepo $ Git.HashObject.hashObject BlobObject link
Annex.Queue.addUpdateIndex =<<
inRepo (Git.UpdateIndex.stageSymlink file sha)
writeCache key cache
void $ addAssociatedFile key file
logStatus key InfoPresent
showEndOk
return True
, do
showEndFail
return False
)

View file

@ -52,8 +52,7 @@ orderedList = do
parseBackendList s = map lookupBackendName $ words s
{- Generates a key for a file, trying each backend in turn until one
- accepts it.
-}
- accepts it. -}
genKey :: KeySource -> Maybe Backend -> Annex (Maybe (Key, Backend))
genKey source trybackend = do
bs <- orderedList
@ -94,8 +93,7 @@ lookupFile file = do
return Nothing
{- Looks up the backend that should be used for a file.
- That can be configured on a per-file basis in the gitattributes file.
-}
- That can be configured on a per-file basis in the gitattributes file. -}
chooseBackend :: FilePath -> Annex (Maybe Backend)
chooseBackend f = Annex.getState Annex.forcebackend >>= go
where

View file

@ -16,6 +16,7 @@ import qualified Annex.Branch
import qualified Annex.Queue
import Annex.Content
import Annex.Content.Direct
import Annex.Direct
import Annex.CatFile
import qualified Git.Command
import qualified Git.LsFiles as LsFiles
@ -29,7 +30,6 @@ import qualified Remote.Git
import Types.Key
import Config
import qualified Data.ByteString.Lazy as L
import Data.Hash.MD5
def :: [Command]
@ -79,14 +79,20 @@ syncRemotes rs = ifM (Annex.getState Annex.fast) ( nub <$> pickfast , wanted )
fastest = fromMaybe [] . headMaybe . Remote.byCost
commit :: CommandStart
commit = do
showStart "commit" ""
next $ next $ do
commit = next $ next $ do
Annex.Branch.commit "update"
ifM isDirect
( ifM stageDirect
( runcommit [] , return True )
, runcommit [Param "-a"]
)
where
runcommit ps = do
showStart "commit" ""
showOutput
Annex.Branch.commit "update"
-- Commit will fail when the tree is clean, so ignore failure.
_ <- inRepo $ Git.Command.runBool "commit"
[Param "-a", Param "-m", Param "git-annex automatic sync"]
_ <- inRepo $ Git.Command.runBool "commit" $ ps ++
[Param "-m", Param "git-annex automatic sync"]
return True
mergeLocal :: Git.Ref -> CommandStart
@ -136,7 +142,7 @@ mergeRemote remote b = case b of
Nothing -> do
branch <- inRepo Git.Branch.currentUnsafe
all id <$> (mapM merge $ branchlist branch)
Just branch -> all id <$> (mapM merge =<< tomerge (branchlist b))
Just _ -> all id <$> (mapM merge =<< tomerge (branchlist b))
where
merge = mergeFrom . remoteBranch remote
tomerge branches = filterM (changed remote) branches
@ -259,9 +265,7 @@ resolveMerge' u
case msha of
Nothing -> a Nothing
Just sha -> do
key <- fileKey . takeFileName
. encodeW8 . L.unpack
<$> catObject sha
key <- catKey sha
maybe (return False) (a . Just) key
{- The filename to use when resolving a conflicted merge of a file,

View file

@ -10,7 +10,7 @@ module Git.LsFiles (
notInRepo,
staged,
stagedNotDeleted,
notStaged,
stagedDetails,
typeChanged,
typeChangedStaged,
Conflicting(..),
@ -53,13 +53,21 @@ staged' ps l = pipeNullSplit $ prefix ++ ps ++ suffix
prefix = [Params "diff --cached --name-only -z"]
suffix = Param "--" : map File l
{- Returns a list of all files that have unstaged changes. This includes
- any new files, that have not been added yet. -}
notStaged :: [FilePath] -> Repo -> IO ([FilePath], IO Bool)
notStaged l repo = pipeNullSplit params repo
{- Returns details about files that are staged in the index
- (including the Sha of their staged contents),
- as well as files not yet in git. -}
stagedDetails :: [FilePath] -> Repo -> IO ([(FilePath, Maybe Sha)], IO Bool)
stagedDetails l repo = do
(ls, cleanup) <- pipeNullSplit params repo
return (map parse ls, cleanup)
where
params = [Params "ls-files --others --deleted --modified --exclude-standard -z --"] ++
params = [Params "ls-files --others --exclude-standard --stage -z --"] ++
map File l
parse s
| null file = (s, Nothing)
| otherwise = (file, extractSha $ take shaSize $ drop 7 metadata)
where
(metadata, file) = separate (== '\t') s
{- Returns a list of the files in the specified locations that are staged
- for commit, and whose type has changed. -}

View file

@ -15,6 +15,7 @@
module Logs.Location (
LogStatus(..),
logStatus,
logChange,
loggedLocations,
loggedKeys,
@ -26,6 +27,13 @@ module Logs.Location (
import Common.Annex
import qualified Annex.Branch
import Logs.Presence
import Annex.UUID
{- Log a change in the presence of a key's value in current repository. -}
logStatus :: Key -> LogStatus -> Annex ()
logStatus key status = do
u <- getUUID
logChange key u status
{- Log a change in the presence of a key's value in a repository. -}
logChange :: Key -> UUID -> LogStatus -> Annex ()

View file

@ -51,7 +51,7 @@ import qualified Annex
import Annex.UUID
import Logs.UUID
import Logs.Trust
import Logs.Location
import Logs.Location hiding (logStatus)
import Remote.List
import qualified Git

View file

@ -37,6 +37,7 @@ import Config
import Init
import Types.Key
import qualified Fields
import Logs.Location
import Control.Concurrent
import Control.Concurrent.MSampleVar
@ -243,7 +244,7 @@ dropKey r key
whenM (Annex.Content.inAnnex key) $ do
Annex.Content.lockContent key $
Annex.Content.removeAnnex key
Annex.Content.logStatus key InfoMissing
logStatus key InfoMissing
Annex.Content.saveState True
return True
| Git.repoIsHttp r = error "dropping from http repo not supported"

View file

@ -20,11 +20,9 @@ deleted or modified at any time). To do so: `git annex untrust .`
## use a direct mode repository
You can use `git annex add` to add files to your direct mode repository.
The main command that's supported in direct mode repositories is
`git annex sync`. This automatically commits all changed files to git,
pushes them out, pulls down any changes, etc.
The main command that's used in direct mode repositories is
`git annex sync`. This automatically adds new files, commits all
changed files to git, pushes them out, pulls down any changes, etc.
You can also run `git annex get` to transfer the content of files into your
direct mode repository. Or if the direct mode repository is a remote of
@ -39,6 +37,9 @@ You can use `git log` and other git query commands.
## what doesn't work in direct mode
Don't use `git annex add` -- it thinks all direct mode files are unlocked,
and locks them.
In general git-annex commands will only work in direct mode repositories on
files whose content is not present. That's because such files are still
represented as symlinks, which git-annex commands know how to operate on.