git-annex 6.20160923 → 6.20161012
raw patch · 28 files changed
+400/−267 lines, 28 files
Files
- Annex.hs +4/−2
- Annex/CatFile.hs +14/−2
- Annex/DirHashes.hs +2/−1
- Annex/GitOverlay.hs +23/−4
- Annex/Init.hs +2/−1
- Annex/Link.hs +3/−3
- Annex/Locations.hs +15/−5
- Annex/Version.hs +4/−1
- Assistant/DaemonStatus.hs +9/−9
- Assistant/Types/DaemonStatus.hs +1/−2
- Assistant/Types/TransferrerPool.hs +9/−12
- Build/BundledPrograms.hs +2/−0
- Build/LinuxMkLibs.hs +0/−14
- CHANGELOG +33/−0
- Command/Upgrade.hs +1/−1
- Command/WebApp.hs +1/−1
- Git/CatFile.hs +53/−26
- Git/Env.hs +15/−11
- Git/Tree.hs +24/−13
- Logs/TimeStamp.hs +17/−8
- Messages/JSON.hs +10/−6
- Messages/Progress.hs +7/−8
- Remote/External.hs +107/−101
- Remote/External/Types.hs +15/−26
- Test.hs +22/−0
- Upgrade.hs +5/−4
- Upgrade/V1.hs +1/−5
- git-annex.cabal +1/−1
Annex.hs view
@@ -139,6 +139,7 @@ , activeremotes :: MVar (S.Set (Types.Remote.RemoteA Annex)) , keysdbhandle :: Maybe Keys.DbHandle , cachedcurrentbranch :: Maybe Git.Branch+ , cachedgitenv :: Maybe [(String, String)] } newState :: GitConfig -> Git.Repo -> IO AnnexState@@ -189,6 +190,7 @@ , activeremotes = emptyactiveremotes , keysdbhandle = Nothing , cachedcurrentbranch = Nothing+ , cachedgitenv = Nothing } {- Makes an Annex state object for the specified git repo.@@ -241,10 +243,10 @@ mvar <- ask liftIO $ modifyMVar_ mvar $ return . modifier -withState :: (AnnexState -> (AnnexState, b)) -> Annex b+withState :: (AnnexState -> IO (AnnexState, b)) -> Annex b withState modifier = do mvar <- ask- liftIO $ modifyMVar mvar $ return . modifier+ liftIO $ modifyMVar mvar modifier {- Sets a flag to True -} setFlag :: String -> Annex ()
Annex/CatFile.hs view
@@ -49,6 +49,11 @@ h <- catFileHandle liftIO $ Git.CatFile.catObject h ref +catObjectMetaData :: Git.Ref -> Annex (Maybe (Integer, ObjectType))+catObjectMetaData ref = do+ h <- catFileHandle+ liftIO $ Git.CatFile.catObjectMetaData h ref+ catTree :: Git.Ref -> Annex [(FilePath, FileMode)] catTree ref = do h <- catFileHandle@@ -83,13 +88,20 @@ - nothing is using the handles, eg at shutdown. -} catFileStop :: Annex () catFileStop = do- m <- Annex.withState $ \s ->+ m <- Annex.withState $ pure . \s -> (s { Annex.catfilehandles = M.empty }, Annex.catfilehandles s) liftIO $ mapM_ Git.CatFile.catFileStop (M.elems m) {- From ref to a symlink or a pointer file, get the key. -} catKey :: Ref -> Annex (Maybe Key)-catKey ref = parseLinkOrPointer <$> catObject ref+catKey ref = go =<< catObjectMetaData ref+ where+ go (Just (sz, _))+ -- Avoid catting large files, that cannot be symlinks or+ -- pointer files, which would require buffering their+ -- content in memory, as well as a lot of IO.+ | sz <= maxPointerSz = parseLinkOrPointer <$> catObject ref+ go _ = return Nothing {- Gets a symlink target. -} catSymLinkTarget :: Sha -> Annex String
Annex/DirHashes.hs view
@@ -14,6 +14,7 @@ dirHashes, hashDirMixed, hashDirLower,+ display_32bits_as_dir ) where import Data.Bits@@ -74,7 +75,7 @@ -} display_32bits_as_dir :: Word32 -> String display_32bits_as_dir w = trim $ swap_pairs cs- where + where -- Need 32 characters to use. To avoid inaverdently making -- a real word, use letters that appear less frequently. chars = ['0'..'9'] ++ "zqjxkmvwgpfZQJXKMVWGPF"
Annex/GitOverlay.hs view
@@ -22,9 +22,28 @@ withIndexFile f a = do f' <- liftIO $ indexEnvVal f withAltRepo- (\g -> addGitEnv g indexEnv f')+ (usecachedgitenv $ \g -> liftIO $ addGitEnv g indexEnv f') (\g g' -> g' { gitEnv = gitEnv g }) a+ where+ -- This is an optimisation. Since withIndexFile is run repeatedly,+ -- and addGitEnv uses the slow copyGitEnv when gitEnv is Nothing, + -- we cache the copied environment the first time, and reuse it in+ -- subsequent calls.+ --+ -- (This could be done at another level; eg when creating the+ -- Git object in the first place, but it's more efficient to let+ -- the enviroment be inherited in all calls to git where it+ -- does not need to be modified.)+ usecachedgitenv m g = case gitEnv g of+ Just _ -> m g+ Nothing -> do+ e <- Annex.withState $ \s -> case Annex.cachedgitenv s of+ Nothing -> do+ e <- copyGitEnv+ return (s { Annex.cachedgitenv = Just e }, e)+ Just e -> return (s, e)+ m (g { gitEnv = Just e }) {- Runs an action using a different git work tree. -@@ -52,7 +71,7 @@ withWorkTreeRelated :: FilePath -> Annex a -> Annex a withWorkTreeRelated d = withAltRepo modrepo unmodrepo where- modrepo g = do+ modrepo g = liftIO $ do g' <- addGitEnv g "GIT_COMMON_DIR" =<< absPath (localGitDir g) g'' <- addGitEnv g' "GIT_DIR" d return (g'' { gitEnvOverridesGitDir = True })@@ -62,7 +81,7 @@ } withAltRepo - :: (Repo -> IO Repo)+ :: (Repo -> Annex Repo) -- ^ modify Repo -> (Repo -> Repo -> Repo) -- ^ undo modifications; first Repo is the original and second@@ -71,7 +90,7 @@ -> Annex a withAltRepo modrepo unmodrepo a = do g <- gitRepo- g' <- liftIO $ modrepo g+ g' <- modrepo g q <- Annex.Queue.get v <- tryNonAsync $ do Annex.changeState $ \s -> s
Annex/Init.hs view
@@ -93,7 +93,8 @@ Database.Keys.scanAssociatedFiles v <- checkAdjustedClone case v of- NeedUpgradeForAdjustedClone -> void $ upgrade True+ NeedUpgradeForAdjustedClone -> + void $ upgrade True versionForAdjustedClone InAdjustedClone -> return () NotInAdjustedClone -> ifM (crippledFileSystem <&&> (not <$> isBareRepo))
Annex/Link.hs view
@@ -26,7 +26,6 @@ import Utility.FileMode import qualified Data.ByteString.Lazy as L-import Data.Int type LinkTarget = String @@ -137,7 +136,8 @@ - Only looks at the first line, as pointer files can have subsequent - lines. -} parseLinkOrPointer :: L.ByteString -> Maybe Key-parseLinkOrPointer = parseLinkOrPointer' . decodeBS . L.take maxPointerSz+parseLinkOrPointer = parseLinkOrPointer' + . decodeBS . L.take (fromIntegral maxPointerSz) where {- Want to avoid buffering really big files in git into@@ -146,7 +146,7 @@ - 8192 bytes is plenty for a pointer to a key. - Pad some more to allow for any pointer files that might have - lines after the key explaining what the file is used for. -}-maxPointerSz :: Int64+maxPointerSz :: Integer maxPointerSz = 81920 parseLinkOrPointer' :: String -> Maybe Key
Annex/Locations.hs view
@@ -452,15 +452,25 @@ - can cause existing objects to get lost. -} keyFile :: Key -> FilePath-keyFile key = replace "/" "%" $ replace ":" "&c" $- replace "%" "&s" $ replace "&" "&a" $ key2file key+keyFile = concatMap esc . key2file+ where+ esc '&' = "&a"+ esc '%' = "&s"+ esc ':' = "&c"+ esc '/' = "%"+ esc c = [c] {- Reverses keyFile, converting a filename fragment (ie, the basename of - the symlink target) into a key. -} fileKey :: FilePath -> Maybe Key-fileKey file = file2key $- replace "&a" "&" $ replace "&s" "%" $- replace "&c" ":" $ replace "%" "/" file+fileKey = file2key . unesc [] + where+ unesc r [] = reverse r+ unesc r ('%':cs) = unesc ('/':r) cs+ unesc r ('&':'c':cs) = unesc (':':r) cs+ unesc r ('&':'s':cs) = unesc ('%':r) cs+ unesc r ('&':'a':cs) = unesc ('&':r) cs+ unesc r (c:cs) = unesc (c:r) cs {- for quickcheck -} prop_isomorphic_fileKey :: String -> Bool
Annex/Version.hs view
@@ -22,7 +22,10 @@ latestVersion = "6" supportedVersions :: [Version]-supportedVersions = ["5", "6"]+supportedVersions = ["3", "5", "6"]++versionForAdjustedClone :: Version+versionForAdjustedClone = "6" upgradableVersions :: [Version] #ifndef mingw32_HOST_OS
Assistant/DaemonStatus.hs view
@@ -30,7 +30,7 @@ import qualified Data.Text as T getDaemonStatus :: Assistant DaemonStatus-getDaemonStatus = (atomically . readTMVar) <<~ daemonStatusHandle+getDaemonStatus = (atomically . readTVar) <<~ daemonStatusHandle modifyDaemonStatus_ :: (DaemonStatus -> DaemonStatus) -> Assistant () modifyDaemonStatus_ a = modifyDaemonStatus $ \s -> (a s, ())@@ -40,8 +40,8 @@ dstatus <- getAssistant daemonStatusHandle liftIO $ do (s, b) <- atomically $ do- r@(!s, _) <- a <$> takeTMVar dstatus- putTMVar dstatus s+ r@(!s, _) <- a <$> readTVar dstatus+ writeTVar dstatus s return r sendNotification $ changeNotifier s return b@@ -102,7 +102,7 @@ flip catchDefaultIO (readDaemonStatusFile file) =<< newDaemonStatus transfers <- M.fromList <$> getTransfers addsync <- calcSyncRemotes- liftIO $ atomically $ newTMVar $ addsync $ status+ liftIO $ atomically $ newTVar $ addsync $ status { scanComplete = False , sanityCheckRunning = False , currentTransfers = transfers@@ -162,14 +162,14 @@ - to the caller. -} adjustTransfersSTM :: DaemonStatusHandle -> (TransferMap -> TransferMap) -> STM () adjustTransfersSTM dstatus a = do- s <- takeTMVar dstatus+ s <- readTVar dstatus let !v = a (currentTransfers s)- putTMVar dstatus $ s { currentTransfers = v }+ writeTVar dstatus $ s { currentTransfers = v } {- Checks if a transfer is currently running. -} checkRunningTransferSTM :: DaemonStatusHandle -> Transfer -> STM Bool checkRunningTransferSTM dstatus t = M.member t . currentTransfers- <$> readTMVar dstatus+ <$> readTVar dstatus {- Alters a transfer's info, if the transfer is in the map. -} alterTransferInfo :: Transfer -> (TransferInfo -> TransferInfo) -> Assistant ()@@ -207,14 +207,14 @@ notifyTransfer = do dstatus <- getAssistant daemonStatusHandle liftIO $ sendNotification- =<< transferNotifier <$> atomically (readTMVar dstatus)+ =<< transferNotifier <$> atomically (readTVar dstatus) {- Send a notification when alerts are changed. -} notifyAlert :: Assistant () notifyAlert = do dstatus <- getAssistant daemonStatusHandle liftIO $ sendNotification- =<< alertNotifier <$> atomically (readTMVar dstatus)+ =<< alertNotifier <$> atomically (readTVar dstatus) {- Returns the alert's identifier, which can be used to remove it. -} addAlert :: Alert -> Assistant AlertId
Assistant/Types/DaemonStatus.hs view
@@ -86,8 +86,7 @@ type TransferMap = M.Map Transfer TransferInfo -{- This TMVar is never left empty, so accessing it will never block. -}-type DaemonStatusHandle = TMVar DaemonStatus+type DaemonStatusHandle = TVar DaemonStatus newDaemonStatus :: IO DaemonStatus newDaemonStatus = DaemonStatus
Assistant/Types/TransferrerPool.hs view
@@ -13,8 +13,7 @@ import Control.Concurrent.STM hiding (check) -{- This TMVar is never left empty. -}-type TransferrerPool = TMVar (MkCheckTransferrer, [TransferrerPoolItem])+type TransferrerPool = TVar (MkCheckTransferrer, [TransferrerPoolItem]) type CheckTransferrer = IO Bool type MkCheckTransferrer = IO (IO Bool)@@ -31,24 +30,22 @@ } newTransferrerPool :: MkCheckTransferrer -> IO TransferrerPool-newTransferrerPool c = newTMVarIO (c, [])+newTransferrerPool c = newTVarIO (c, []) popTransferrerPool :: TransferrerPool -> STM (Maybe TransferrerPoolItem, Int) popTransferrerPool p = do- (c, l) <- takeTMVar p+ (c, l) <- readTVar p case l of- [] -> do- putTMVar p (c, [])- return (Nothing, 0)+ [] -> return (Nothing, 0) (i:is) -> do- putTMVar p (c, is)+ writeTVar p (c, is) return $ (Just i, length is) pushTransferrerPool :: TransferrerPool -> TransferrerPoolItem -> STM () pushTransferrerPool p i = do- (c, l) <- takeTMVar p+ (c, l) <- readTVar p let l' = i:l- putTMVar p (c, l')+ writeTVar p (c, l') {- Note that making a CheckTransferrer may allocate resources, - such as a NotificationHandle, so it's important that the returned@@ -56,12 +53,12 @@ - garbage collected. -} mkTransferrerPoolItem :: TransferrerPool -> Transferrer -> IO TransferrerPoolItem mkTransferrerPoolItem p t = do- mkcheck <- atomically $ fst <$> readTMVar p+ mkcheck <- atomically $ fst <$> readTVar p check <- mkcheck return $ TransferrerPoolItem (Just t) check checkNetworkConnections :: DaemonStatusHandle -> MkCheckTransferrer checkNetworkConnections dstatushandle = do- dstatus <- atomically $ readTMVar dstatushandle+ dstatus <- atomically $ readTVar dstatushandle h <- newNotificationHandle False (networkConnectedNotifier dstatus) return $ not <$> checkNotification h
Build/BundledPrograms.hs view
@@ -91,6 +91,8 @@ -- used to unpack the tarball when upgrading , Just "gunzip" , Just "tar"+ -- used by runshell to generate locales+ , Just "localedef" #endif -- nice, ionice, and nocache are not included in the bundle; -- we rely on the system's own version, which may better match
Build/LinuxMkLibs.hs view
@@ -5,8 +5,6 @@ - Licensed under the GNU GPL version 3 or higher. -} -{-# LANGUAGE CPP #-}- module Main where import System.Environment@@ -73,18 +71,6 @@ [ "#!/bin/sh" , "GIT_ANNEX_PROGRAMPATH=\"$0\"" , "export GIT_ANNEX_PROGRAMPATH"-#ifdef MIN_VERSION_GLASGOW_HASKELL-#if ! MIN_VERSION_GLASGOW_HASKELL(7,10,0,0)-#define NEED_LOCPATH_WORKAROUND-#endif-#else-#define NEED_LOCPATH_WORKAROUND-#endif-#ifdef NEED_LOCPATH_WORKAROUND- -- workaround for https://ghc.haskell.org/trac/ghc/ticket/7695- , "LOCPATH=/dev/null"- , "export LOCPATH"-#endif , "exec \"$GIT_ANNEX_DIR/" ++ exelink ++ "\" --library-path \"$GIT_ANNEX_LD_LIBRARY_PATH\" \"$GIT_ANNEX_DIR/shimmed/" ++ base ++ "/" ++ base ++ "\" \"$@\"" ] modifyFileMode exe $ addModes executeModes
CHANGELOG view
@@ -1,3 +1,36 @@+git-annex (6.20161012) unstable; urgency=medium++ * Optimisations to time it takes git-annex to walk working tree and find+ files to work on. Sped up by around 18%.+ * Optimisations to git-annex branch query and setting, avoiding repeated+ copies of the environment. Speeds up commands like + "git-annex find --in remote" by over 50%.+ * Optimised git-annex branch log file timestamp parsing.+ * Add "total-size" field to --json-progress output.+ * Make --json-progress output be shown even when the size of a object+ is not known.+ * Multiple external special remote processes for the same remote will be+ started as needed when using -J. This should not beak any existing+ external special remotes, because running multiple git-annex commands+ at the same time could already start multiple processes for the same+ external special remotes.+ * Linux standalone: Include locale files in the bundle, and generate+ locale definition files for the locales in use when starting runshell.+ (Currently only done for utf-8 locales.)+ * Avoid using a lot of memory when large objects are present in the git+ repository and have to be checked to see if they are a pointed to an+ annexed file. Cases where such memory use could occur included, but+ were not limited to:+ - git commit -a of a large unlocked file (in v5 mode)+ - git-annex adjust when a large file was checked into git directly+ * When auto-upgrading a v3 remote, avoid upgrading to version 6,+ instead keep it at version 5.+ * Support using v3 repositories without upgrading them to v5.+ * sync: Fix bug in adjusted branch merging that could cause recently+ added files to be lost when updating the adjusted branch.++ -- Joey Hess <id@joeyh.name> Wed, 12 Oct 2016 09:37:41 -0400+ git-annex (6.20160923) unstable; urgency=medium * Rate limit console progress display updates to 10 per second.
Command/Upgrade.hs view
@@ -26,5 +26,5 @@ showStart "upgrade" "." whenM (isNothing <$> getVersion) $ do initialize Nothing Nothing- r <- upgrade False+ r <- upgrade False latestVersion next $ next $ return r
Command/WebApp.hs view
@@ -154,7 +154,7 @@ - threadstate. -} let st = error "annex state not available" {- Get a DaemonStatus without running in the Annex monad. -}- dstatus <- atomically . newTMVar =<< newDaemonStatus+ dstatus <- atomically . newTVar =<< newDaemonStatus d <- newAssistantData st dstatus urlrenderer <- newUrlRenderer v <- newEmptyMVar
Git/CatFile.hs view
@@ -16,6 +16,7 @@ catCommit, catObject, catObjectDetails,+ catObjectMetaData, ) where import System.IO@@ -37,21 +38,28 @@ import Git.FilePath import qualified Utility.CoProcess as CoProcess -data CatFileHandle = CatFileHandle CoProcess.CoProcessHandle Repo+data CatFileHandle = CatFileHandle + { catFileProcess :: CoProcess.CoProcessHandle+ , checkFileProcess :: CoProcess.CoProcessHandle+ } catFileStart :: Repo -> IO CatFileHandle catFileStart = catFileStart' True catFileStart' :: Bool -> Repo -> IO CatFileHandle-catFileStart' restartable repo = do- coprocess <- CoProcess.rawMode =<< gitCoProcessStart restartable+catFileStart' restartable repo = CatFileHandle+ <$> startp "--batch"+ <*> startp "--batch-check=%(objectname) %(objecttype) %(objectsize)"+ where+ startp p = CoProcess.rawMode =<< gitCoProcessStart restartable [ Param "cat-file"- , Param "--batch"+ , Param p ] repo- return $ CatFileHandle coprocess repo catFileStop :: CatFileHandle -> IO ()-catFileStop (CatFileHandle p _) = CoProcess.stop p+catFileStop h = do+ CoProcess.stop (catFileProcess h)+ CoProcess.stop (checkFileProcess h) {- Reads a file from a specified branch. -} catFile :: CatFileHandle -> Branch -> FilePath -> IO L.ByteString@@ -68,31 +76,50 @@ catObject h object = maybe L.empty fst3 <$> catObjectDetails h object catObjectDetails :: CatFileHandle -> Ref -> IO (Maybe (L.ByteString, Sha, ObjectType))-catObjectDetails (CatFileHandle hdl _) object = CoProcess.query hdl send receive+catObjectDetails h object = query (catFileProcess h) object $ \from -> do+ header <- hGetLine from+ case parseResp object header of+ Just (ParsedResp sha size objtype) -> do+ content <- S.hGet from (fromIntegral size)+ eatchar '\n' from+ return $ Just (L.fromChunks [content], sha, objtype)+ Just DNE -> return Nothing+ Nothing -> error $ "unknown response from git cat-file " ++ show (header, object) where- query = fromRef object- send to = hPutStrLn to query- receive from = do- header <- hGetLine from- case words header of- [sha, objtype, size]- | length sha == shaSize ->- case (readObjectType objtype, reads size) of- (Just t, [(bytes, "")]) -> readcontent t bytes from sha- _ -> dne- | otherwise -> dne- _- | header == fromRef object ++ " missing" -> dne- | otherwise -> error $ "unknown response from git cat-file " ++ show (header, query)- readcontent objtype bytes from sha = do- content <- S.hGet from bytes- eatchar '\n' from- return $ Just (L.fromChunks [content], Ref sha, objtype)- dne = return Nothing eatchar expected from = do c <- hGetChar from when (c /= expected) $ error $ "missing " ++ (show expected) ++ " from git cat-file"++{- Gets the size and type of an object, without reading its content. -}+catObjectMetaData :: CatFileHandle -> Ref -> IO (Maybe (Integer, ObjectType))+catObjectMetaData h object = query (checkFileProcess h) object $ \from -> do+ resp <- hGetLine from+ case parseResp object resp of+ Just (ParsedResp _ size objtype) ->+ return $ Just (size, objtype)+ Just DNE -> return Nothing+ Nothing -> error $ "unknown response from git cat-file " ++ show (resp, object)++data ParsedResp = ParsedResp Sha Integer ObjectType | DNE++query :: CoProcess.CoProcessHandle -> Ref -> (Handle -> IO a) -> IO a+query hdl object receive = CoProcess.query hdl send receive+ where+ send to = hPutStrLn to (fromRef object)++parseResp :: Ref -> String -> Maybe ParsedResp+parseResp object l = case words l of+ [sha, objtype, size]+ | length sha == shaSize ->+ case (readObjectType objtype, reads size) of+ (Just t, [(bytes, "")]) -> + Just $ ParsedResp (Ref sha) bytes t+ _ -> Nothing+ | otherwise -> Nothing+ _+ | l == fromRef object ++ " missing" -> Just DNE+ | otherwise -> Nothing {- Gets a list of files and directories in a tree. (Not recursive.) -} catTree :: CatFileHandle -> Ref -> IO [(FilePath, FileMode)]
Git/Env.hs view
@@ -18,22 +18,26 @@ - does not have any gitEnv yet. -} adjustGitEnv :: Repo -> ([(String, String)] -> [(String, String)]) -> IO Repo adjustGitEnv g adj = do- e <- maybe copyenv return (gitEnv g)+ e <- maybe copyGitEnv return (gitEnv g) let e' = adj e return $ g { gitEnv = Just e' } where- copyenv = do++{- Copies the current environment, so it can be adjusted when running a git+ - command. -}+copyGitEnv :: IO [(String, String)]+copyGitEnv = do #ifdef __ANDROID__- {- This should not be necessary on Android, but there is some- - weird getEnvironment breakage. See- - https://github.com/neurocyte/ghc-android/issues/7- - Use getEnv to get some key environment variables that- - git expects to have. -}- let keyenv = words "USER PATH GIT_EXEC_PATH HOSTNAME HOME"- let getEnvPair k = maybe Nothing (\v -> Just (k, v)) <$> getEnv k- catMaybes <$> forM keyenv getEnvPair+ {- This should not be necessary on Android, but there is some+ - weird getEnvironment breakage. See+ - https://github.com/neurocyte/ghc-android/issues/7+ - Use getEnv to get some key environment variables that+ - git expects to have. -}+ let keyenv = words "USER PATH GIT_EXEC_PATH HOSTNAME HOME"+ let getEnvPair k = maybe Nothing (\v -> Just (k, v)) <$> getEnv k+ catMaybes <$> forM keyenv getEnvPair #else- getEnvironment+ getEnvironment #endif addGitEnv :: Repo -> String -> String -> IO Repo
Git/Tree.hs view
@@ -143,6 +143,16 @@ where parent = takeDirectory d +{- Flattens the top N levels of a Tree. -}+flattenTree :: Int -> Tree -> Tree+flattenTree 0 t = t+flattenTree n (Tree l) = Tree (concatMap (go n) l)+ where+ go 0 c = [c]+ go _ b@(TreeBlob _ _ _) = [b]+ go n' (RecordedSubTree _ _ l') = concatMap (go (n'-1)) l'+ go n' (NewSubTree _ l') = concatMap (go (n'-1)) l'+ {- Applies an adjustment to items in a tree. - - While less flexible than using getTree and recordTree,@@ -163,41 +173,42 @@ adjustTree adjusttreeitem addtreeitems removefiles r repo = withMkTreeHandle repo $ \h -> do (l, cleanup) <- liftIO $ lsTreeWithObjects r repo- (l', _, _) <- go h False [] inTopTree l- l'' <- adjustlist h inTopTree (const True) l'+ (l', _, _) <- go h False [] 1 inTopTree l+ l'' <- adjustlist h 0 inTopTree (const True) l' sha <- liftIO $ mkTree h l'' void $ liftIO cleanup return sha where- go _ wasmodified c _ [] = return (c, wasmodified, [])- go h wasmodified c intree (i:is)+ go _ wasmodified c _ _ [] = return (c, wasmodified, [])+ go h wasmodified c depth intree (i:is) | intree i = case readObjectType (LsTree.typeobj i) of Just BlobObject -> do let ti = TreeItem (LsTree.file i) (LsTree.mode i) (LsTree.sha i) v <- adjusttreeitem ti case v of- Nothing -> go h True c intree is+ Nothing -> go h True c depth intree is Just ti'@(TreeItem f m s) -> let !modified = wasmodified || ti' /= ti blob = TreeBlob f m s- in go h modified (blob:c) intree is+ in go h modified (blob:c) depth intree is Just TreeObject -> do- (sl, modified, is') <- go h False [] (beneathSubTree i) is- sl' <- adjustlist h (inTree i) (beneathSubTree i) sl- subtree <- if modified || sl' /= sl+ (sl, modified, is') <- go h False [] (depth+1) (beneathSubTree i) is+ sl' <- adjustlist h depth (inTree i) (beneathSubTree i) sl+ let slmodified = sl' /= sl+ subtree <- if modified || slmodified then liftIO $ recordSubTree h $ NewSubTree (LsTree.file i) sl' else return $ RecordedSubTree (LsTree.file i) (LsTree.sha i) [] - let !modified' = modified || wasmodified- go h modified' (subtree : c) intree is'+ let !modified' = modified || slmodified || wasmodified+ go h modified' (subtree : c) depth intree is' _ -> error ("unexpected object type \"" ++ LsTree.typeobj i ++ "\"") | otherwise = return (c, wasmodified, i:is) - adjustlist h ishere underhere l = do+ adjustlist h depth ishere underhere l = do let (addhere, rest) = partition ishere addtreeitems let l' = filter (not . removed) $ map treeItemToTreeContent addhere ++ l let inl i = any (\t -> beneathSubTree t i) l'- let (Tree addunderhere) = treeItemsToTree $+ let (Tree addunderhere) = flattenTree depth $ treeItemsToTree $ filter (\i -> underhere i && not (inl i)) rest addunderhere' <- liftIO $ mapM (recordSubTree h) addunderhere return (addunderhere'++l')
Logs/TimeStamp.hs view
@@ -1,6 +1,6 @@ {- log timestamp parsing -- - Copyright 2015 Joey Hess <id@joeyh.name>+ - Copyright 2015-2016 Joey Hess <id@joeyh.name> - - Licensed under the GNU GPL version 3 or higher. -}@@ -9,22 +9,31 @@ module Logs.TimeStamp where +import Utility.PartialPrelude+import Utility.Misc+ import Data.Time.Clock.POSIX import Data.Time+import Data.Ratio #if ! MIN_VERSION_time(1,5,0) import System.Locale #endif-import Control.Applicative-import Prelude {- Parses how POSIXTime shows itself: "1431286201.113452s" - Also handles the format with no fractional seconds. -} parsePOSIXTime :: String -> Maybe POSIXTime-#if MIN_VERSION_time(1,5,0)-parsePOSIXTime s = utcTimeToPOSIXSeconds <$> parseTimeM True defaultTimeLocale "%s%Qs" s-#else-parsePOSIXTime s = utcTimeToPOSIXSeconds <$> parseTime defaultTimeLocale "%s%Qs" s-#endif+parsePOSIXTime s = do+ let (sn, sd) = separate (== '.') s+ n <- readi sn+ if null sd + then return (fromIntegral n)+ else do+ d <- readi sd+ let r = d % (10 ^ (length sd - 1))+ return (fromIntegral n + fromRational r)+ where+ readi :: String -> Maybe Integer+ readi = readish formatPOSIXTime :: String -> POSIXTime -> String formatPOSIXTime fmt t = formatTime defaultTimeLocale fmt (posixSecondsToUTCTime t)
Messages/JSON.hs view
@@ -95,16 +95,20 @@ -- Show JSON formatted progress, including the current state of the JSON -- object for the action being performed.-progress :: Maybe Object -> Integer -> BytesProcessed -> IO ()-progress maction size bytesprocessed = emit $ case maction of+progress :: Maybe Object -> Maybe Integer -> BytesProcessed -> IO ()+progress maction msize bytesprocessed = emit $ case maction of Just action -> HM.insert "action" (Object action) o Nothing -> o where n = fromBytesProcessed bytesprocessed :: Integer- Object o = object- [ "byte-progress" .= n- , "percent-progress" .= showPercentage 2 (percentage size n)- ]+ Object o = case msize of+ Just size -> object+ [ "byte-progress" .= n+ , "percent-progress" .= showPercentage 2 (percentage size n)+ , "total-size" .= size+ ]+ Nothing -> object+ [ "byte-progress" .= n ] -- A value that can be displayed either normally, or as JSON. data DualDisp = DualDisp
Messages/Progress.hs view
@@ -30,12 +30,11 @@ {- Shows a progress meter while performing a transfer of a key. - The action is passed a callback to use to update the meter. -} metered :: Maybe MeterUpdate -> Key -> (MeterUpdate -> Annex a) -> Annex a-metered othermeter key a = case keySize key of- Nothing -> nometer- Just size -> withMessageState (go $ fromInteger size)+metered othermeter key a = withMessageState $ go (keySize key) where go _ (MessageState { outputType = QuietOutput }) = nometer- go size (MessageState { outputType = NormalOutput, concurrentOutputEnabled = False }) = do+ go Nothing (MessageState { outputType = NormalOutput }) = nometer+ go (Just size) (MessageState { outputType = NormalOutput, concurrentOutputEnabled = False }) = do showOutput (progress, meter) <- mkmeter size m <- liftIO $ rateLimitMeterUpdate 0.1 (Just size) $ \n -> do@@ -44,7 +43,7 @@ r <- a (combinemeter m) liftIO $ clearMeter stdout meter return r- go size (MessageState { outputType = NormalOutput, concurrentOutputEnabled = True }) =+ go (Just size) (MessageState { outputType = NormalOutput, concurrentOutputEnabled = True }) = #if WITH_CONCURRENTOUTPUT withProgressRegion $ \r -> do (progress, meter) <- mkmeter size@@ -57,10 +56,10 @@ nometer #endif go _ (MessageState { outputType = JSONOutput False }) = nometer- go size (MessageState { outputType = JSONOutput True }) = do+ go msize (MessageState { outputType = JSONOutput True }) = do buf <- withMessageState $ return . jsonBuffer- m <- liftIO $ rateLimitMeterUpdate 0.1 (Just size) $- JSON.progress buf size+ m <- liftIO $ rateLimitMeterUpdate 0.1 msize $+ JSON.progress buf msize a (combinemeter m) mkmeter size = do
Remote/External.hs view
@@ -1,6 +1,6 @@ {- External special remote interface. -- - Copyright 2013-2015 Joey Hess <id@joeyh.name>+ - Copyright 2013-2016 Joey Hess <id@joeyh.name> - - Licensed under the GNU GPL version 3 or higher. -}@@ -126,7 +126,8 @@ INITREMOTE_SUCCESS -> Just noop INITREMOTE_FAILURE errmsg -> Just $ error errmsg _ -> Nothing- liftIO $ atomically $ readTMVar $ externalConfig external+ withExternalState external $+ liftIO . atomically . readTVar . externalConfig gitConfigSpecialRemote u c'' "externaltype" externaltype return (c'', u)@@ -201,27 +202,28 @@ - While the external remote is processing the Request, it may send - any number of RemoteRequests, that are handled here. -- - Only one request can be made at a time, so locking is used.+ - An external remote process can only handle one request at a time.+ - Concurrent requests will start up additional processes. - - May throw exceptions, for example on protocol errors, or - when the repository cannot be used. -} handleRequest :: External -> Request -> Maybe MeterUpdate -> (Response -> Maybe (Annex a)) -> Annex a handleRequest external req mp responsehandler = - withExternalLock external $ \lck ->- handleRequest' lck external req mp responsehandler+ withExternalState external $ \st -> + handleRequest' st external req mp responsehandler -handleRequest' :: ExternalLock -> External -> Request -> Maybe MeterUpdate -> (Response -> Maybe (Annex a)) -> Annex a-handleRequest' lck external req mp responsehandler+handleRequest' :: ExternalState -> External -> Request -> Maybe MeterUpdate -> (Response -> Maybe (Annex a)) -> Annex a+handleRequest' st external req mp responsehandler | needsPREPARE req = do- checkPrepared lck external+ checkPrepared st external go | otherwise = go where go = do- sendMessage lck external req+ sendMessage st external req loop- loop = receiveMessage lck external responsehandler+ loop = receiveMessage st external responsehandler (\rreq -> Just $ handleRemoteRequest rreq >> loop) (\msg -> Just $ handleAsyncMessage msg >> loop) @@ -232,23 +234,23 @@ handleRemoteRequest (DIRHASH_LOWER k) = send $ VALUE $ hashDirLower def k handleRemoteRequest (SETCONFIG setting value) =- liftIO $ atomically $ do- let v = externalConfig external- m <- takeTMVar v- putTMVar v $ M.insert setting value m+ liftIO $ atomically $ modifyTVar' (externalConfig st) $+ M.insert setting value handleRemoteRequest (GETCONFIG setting) = do value <- fromMaybe "" . M.lookup setting- <$> liftIO (atomically $ readTMVar $ externalConfig external)+ <$> liftIO (atomically $ readTVar $ externalConfig st) send $ VALUE value handleRemoteRequest (SETCREDS setting login password) = do- c <- liftIO $ atomically $ readTMVar $ externalConfig external- gc <- liftIO $ atomically $ readTMVar $ externalGitConfig external- c' <- setRemoteCredPair encryptionAlreadySetup c gc (credstorage setting) $- Just (login, password)- void $ liftIO $ atomically $ swapTMVar (externalConfig external) c'+ let v = externalConfig st+ c <- liftIO $ atomically $ readTVar v+ let gc = externalGitConfig external+ c' <- setRemoteCredPair encryptionAlreadySetup c gc+ (credstorage setting)+ (Just (login, password))+ void $ liftIO $ atomically $ swapTVar v c' handleRemoteRequest (GETCREDS setting) = do- c <- liftIO $ atomically $ readTMVar $ externalConfig external- gc <- liftIO $ atomically $ readTMVar $ externalGitConfig external+ c <- liftIO $ atomically $ readTVar $ externalConfig st+ let gc = externalGitConfig external creds <- fromMaybe ("", "") <$> getRemoteCredPair c gc (credstorage setting) send $ CREDS (fst creds) (snd creds)@@ -280,11 +282,11 @@ send (VALUE "") -- end of list handleRemoteRequest (DEBUG msg) = liftIO $ debugM "external" msg handleRemoteRequest (VERSION _) =- sendMessage lck external $ ERROR "too late to send VERSION"+ sendMessage st external (ERROR "too late to send VERSION") handleAsyncMessage (ERROR err) = error $ "external special remote error: " ++ err - send = sendMessage lck external+ send = sendMessage st external credstorage setting = CredPairStorage { credPairFile = base@@ -297,34 +299,32 @@ withurl mk uri = handleRemoteRequest $ mk $ setDownloader (show uri) OtherDownloader -sendMessage :: Sendable m => ExternalLock -> External -> m -> Annex ()-sendMessage lck external m = - fromExternal lck external externalSend $ \h ->- liftIO $ do- protocolDebug external True line- hPutStrLn h line- hFlush h+sendMessage :: Sendable m => ExternalState -> External -> m -> Annex ()+sendMessage st external m = liftIO $ do+ protocolDebug external st True line+ hPutStrLn h line+ hFlush h where line = unwords $ formatMessage m+ h = externalSend st {- Waits for a message from the external remote, and passes it to the - apppropriate handler. - - If the handler returns Nothing, this is a protocol error.-} receiveMessage- :: ExternalLock+ :: ExternalState -> External -> (Response -> Maybe (Annex a)) -> (RemoteRequest -> Maybe (Annex a)) -> (AsyncMessage -> Maybe (Annex a)) -> Annex a-receiveMessage lck external handleresponse handlerequest handleasync =- go =<< fromExternal lck external externalReceive- (liftIO . catchMaybeIO . hGetLine)+receiveMessage st external handleresponse handlerequest handleasync =+ go =<< liftIO (catchMaybeIO $ hGetLine $ externalReceive st) where go Nothing = protocolError False "" go (Just s) = do- liftIO $ protocolDebug external False s+ liftIO $ protocolDebug external st False s case parseMessage s :: Maybe Response of Just resp -> maybe (protocolError True s) id (handleresponse resp) Nothing -> case parseMessage s :: Maybe RemoteRequest of@@ -335,46 +335,47 @@ protocolError parsed s = error $ "external special remote protocol error, unexpectedly received \"" ++ s ++ "\" " ++ if parsed then "(command not allowed at this time)" else "(unable to parse command)" -protocolDebug :: External -> Bool -> String -> IO ()-protocolDebug external sendto line = debugM "external" $ unwords- [ externalRemoteProgram (externalType external)+protocolDebug :: External -> ExternalState -> Bool -> String -> IO ()+protocolDebug external st sendto line = debugM "external" $ unwords+ [ externalRemoteProgram (externalType external) ++ + "[" ++ show (externalPid st) ++ "]" , if sendto then "<--" else "-->" , line ] -{- Starts up the external remote if it's not yet running,- - and passes a value extracted from its state to an action.- -}-fromExternal :: ExternalLock -> External -> (ExternalState -> v) -> (v -> Annex a) -> Annex a-fromExternal lck external extractor a =- go =<< liftIO (atomically (tryReadTMVar v))+{- While the action is running, the ExternalState provided to it will not+ - be available to any other calls.+ -+ - Starts up a new process if no ExternalStates are available. -}+withExternalState :: External -> (ExternalState -> Annex a) -> Annex a+withExternalState external = bracket alloc dealloc where- go (Just st) = run st- go Nothing = do- st <- startExternal $ externalType external- void $ liftIO $ atomically $ do- void $ tryReadTMVar v- putTMVar v st-- {- Handle initial protocol startup; check the VERSION- - the remote sends. -}- receiveMessage lck external- (const Nothing)- (checkVersion lck external)- (const Nothing)-- run st-- run st = a $ extractor st v = externalState external -{- Starts an external remote process running, but does not handle checking- - VERSION, etc. -}-startExternal :: ExternalType -> Annex ExternalState-startExternal externaltype = do+ alloc = do+ ms <- liftIO $ atomically $ do+ l <- readTVar v+ case l of+ [] -> return Nothing+ (st:rest) -> do+ writeTVar v rest+ return (Just st)+ maybe (startExternal external) return ms+ + dealloc st = liftIO $ atomically $ modifyTVar' v (st:)++{- Starts an external remote process running, and checks VERSION. -}+startExternal :: External -> Annex ExternalState+startExternal external = do errrelayer <- mkStderrRelayer- g <- Annex.gitRepo- liftIO $ do+ st <- start errrelayer =<< Annex.gitRepo+ receiveMessage st external+ (const Nothing)+ (checkVersion st external)+ (const Nothing)+ return st+ where+ start errrelayer g = liftIO $ do (cmd, ps) <- findShellCommand basecmd let basep = (proc cmd (toCommand ps)) { std_in = CreatePipe@@ -382,23 +383,31 @@ , std_err = CreatePipe } p <- propgit g basep- (Just hin, Just hout, Just herr, pid) <- + (Just hin, Just hout, Just herr, ph) <- createProcess p `catchIO` runerr fileEncoding hin fileEncoding hout fileEncoding herr stderrelay <- async $ errrelayer herr- checkearlytermination =<< getProcessExitCode pid+ checkearlytermination =<< getProcessExitCode ph+ cv <- newTVarIO $ externalDefaultConfig external+ pv <- newTVarIO Unprepared+ pid <- atomically $ do+ n <- succ <$> readTVar (externalLastPid external)+ writeTVar (externalLastPid external) n+ return n return $ ExternalState { externalSend = hin , externalReceive = hout+ , externalPid = pid , externalShutdown = do cancel stderrelay- void $ waitForProcess pid- , externalPrepared = Unprepared+ void $ waitForProcess ph+ , externalPrepared = pv+ , externalConfig = cv }- where- basecmd = externalRemoteProgram externaltype+ + basecmd = externalRemoteProgram $ externalType external propgit g p = do environ <- propGitEnv g@@ -415,50 +424,47 @@ ) stopExternal :: External -> Annex ()-stopExternal external = liftIO $ stop =<< atomically (tryReadTMVar v)+stopExternal external = liftIO $ do+ l <- atomically $ swapTVar (externalState external) []+ mapM_ stop l where- stop Nothing = noop- stop (Just st) = do- void $ atomically $ tryTakeTMVar v+ stop st = do hClose $ externalSend st hClose $ externalReceive st externalShutdown st- v = externalState external externalRemoteProgram :: ExternalType -> String externalRemoteProgram externaltype = "git-annex-remote-" ++ externaltype -checkVersion :: ExternalLock -> External -> RemoteRequest -> Maybe (Annex ())-checkVersion lck external (VERSION v) = Just $+checkVersion :: ExternalState -> External -> RemoteRequest -> Maybe (Annex ())+checkVersion st external (VERSION v) = Just $ if v `elem` supportedProtocolVersions then noop- else sendMessage lck external (ERROR "unsupported VERSION")+ else sendMessage st external (ERROR "unsupported VERSION") checkVersion _ _ _ = Nothing {- If repo has not been prepared, sends PREPARE. - - If the repo fails to prepare, or failed before, throws an exception with - the error message. -}-checkPrepared :: ExternalLock -> External -> Annex ()-checkPrepared lck external = - fromExternal lck external externalPrepared $ \prepared ->- case prepared of- Prepared -> noop- FailedPrepare errmsg -> error errmsg- Unprepared -> - handleRequest' lck external PREPARE Nothing $ \resp ->- case resp of- PREPARE_SUCCESS -> Just $- setprepared Prepared- PREPARE_FAILURE errmsg -> Just $ do- setprepared $ FailedPrepare errmsg- error errmsg- _ -> Nothing+checkPrepared :: ExternalState -> External -> Annex ()+checkPrepared st external = do+ v <- liftIO $ atomically $ readTVar $ externalPrepared st+ case v of+ Prepared -> noop+ FailedPrepare errmsg -> error errmsg+ Unprepared ->+ handleRequest' st external PREPARE Nothing $ \resp ->+ case resp of+ PREPARE_SUCCESS -> Just $+ setprepared Prepared+ PREPARE_FAILURE errmsg -> Just $ do+ setprepared $ FailedPrepare errmsg+ error errmsg+ _ -> Nothing where- setprepared status = liftIO . atomically $ do- let v = externalState external- st <- takeTMVar v- void $ putTMVar v $ st { externalPrepared = status }+ setprepared status = liftIO $ atomically $ void $+ swapTVar (externalPrepared st) status {- Caches the cost in the git config to avoid needing to start up an - external special remote every time time just to ask it what its
Remote/External/Types.hs view
@@ -12,8 +12,6 @@ External(..), newExternal, ExternalType,- ExternalLock,- withExternalLock, ExternalState(..), PrepareStatus(..), Proto.parseMessage,@@ -44,28 +42,25 @@ import Control.Concurrent.STM import Network.URI --- If the remote is not yet running, the ExternalState TMVar is empty. data External = External { externalType :: ExternalType , externalUUID :: UUID- -- Empty until the remote is running.- , externalState :: TMVar ExternalState- -- Empty when a remote is in use.- , externalLock :: TMVar ExternalLock- -- Never left empty.- , externalConfig :: TMVar RemoteConfig- -- Never left empty.- , externalGitConfig :: TMVar RemoteGitConfig+ , externalState :: TVar [ExternalState]+ -- ^ Contains states for external special remote processes+ -- that are not currently in use.+ , externalLastPid :: TVar PID+ , externalDefaultConfig :: RemoteConfig+ , externalGitConfig :: RemoteGitConfig } newExternal :: ExternalType -> UUID -> RemoteConfig -> RemoteGitConfig -> Annex External newExternal externaltype u c gc = liftIO $ External <$> pure externaltype <*> pure u- <*> atomically newEmptyTMVar- <*> atomically (newTMVar ExternalLock)- <*> atomically (newTMVar c)- <*> atomically (newTMVar gc)+ <*> atomically (newTVar [])+ <*> atomically (newTVar 0)+ <*> pure c+ <*> pure gc type ExternalType = String @@ -73,20 +68,14 @@ { externalSend :: Handle , externalReceive :: Handle , externalShutdown :: IO ()- , externalPrepared :: PrepareStatus+ , externalPid :: PID+ , externalPrepared :: TVar PrepareStatus+ , externalConfig :: TVar RemoteConfig } -data PrepareStatus = Unprepared | Prepared | FailedPrepare ErrorMsg---- Constructor is not exported, and only created by newExternal.-data ExternalLock = ExternalLock+type PID = Int -withExternalLock :: External -> (ExternalLock -> Annex a) -> Annex a-withExternalLock external = bracketIO setup cleanup- where- setup = atomically $ takeTMVar v- cleanup = atomically . putTMVar v- v = externalLock external+data PrepareStatus = Unprepared | Prepared | FailedPrepare ErrorMsg -- Messages that can be sent to the external remote to request it do something. data Request
Test.hs view
@@ -234,6 +234,7 @@ , testCase "sync" test_sync , testCase "union merge regression" test_union_merge_regression , testCase "adjusted branch merge regression" test_adjusted_branch_merge_regression+ , testCase "adjusted branch subtree regression" test_adjusted_branch_subtree_regression , testCase "conflict resolution" test_conflict_resolution , testCase "conflict resolution (adjusted branch)" test_conflict_resolution_adjusted_branch , testCase "conflict resolution movein regression" test_conflict_resolution_movein_regression@@ -1424,6 +1425,27 @@ l <- getDirectoryContents "." conflictor `elem` l @? ("conflictor not present after merge in " ++ what)++{- Regression test for a bug in adjusted branch syncing code, where adding+ - a subtree to an existing tree lost files. -}+test_adjusted_branch_subtree_regression :: Assertion+test_adjusted_branch_subtree_regression = + whenM Annex.AdjustedBranch.isGitVersionSupported $ + withtmpclonerepo $ \r -> do+ indir r $ do+ disconnectOrigin+ git_annex "upgrade" [] @? "upgrade failed"+ git_annex "adjust" ["--unlock", "--force"] @? "adjust failed"+ createDirectoryIfMissing True "a/b/c"+ writeFile "a/b/c/d" "foo"+ git_annex "add" ["a/b/c"] @? "add a/b/c failed"+ git_annex "sync" [] @? "sync failed"+ createDirectoryIfMissing True "a/b/x"+ writeFile "a/b/x/y" "foo"+ git_annex "add" ["a/b/x"] @? "add a/b/x failed"+ git_annex "sync" [] @? "sync failed"+ boolSystem "git" [Param "checkout", Param "master"] @? "git checkout master failed"+ doesFileExist "a/b/x/y" @? ("a/b/x/y missing from master after adjusted branch sync") {- Set up repos as remotes of each other. -} pair :: FilePath -> FilePath -> Assertion
Upgrade.hs view
@@ -26,7 +26,7 @@ needsUpgrade :: Version -> Annex (Maybe String) needsUpgrade v | v `elem` supportedVersions = ok- | v `elem` autoUpgradeableVersions = ifM (upgrade True)+ | v `elem` autoUpgradeableVersions = ifM (upgrade True defaultVersion) ( ok , err "Automatic upgrade failed!" )@@ -37,13 +37,14 @@ " is not supported. " ++ msg ok = return Nothing -upgrade :: Bool -> Annex Bool-upgrade automatic = do+upgrade :: Bool -> Version -> Annex Bool+upgrade automatic destversion = do upgraded <- go =<< getVersion when upgraded $- setVersion latestVersion+ setVersion destversion return upgraded where+ go (Just v) | v >= destversion = return True #ifndef mingw32_HOST_OS go (Just "0") = Upgrade.V0.upgrade go (Just "1") = Upgrade.V1.upgrade
Upgrade/V1.hs view
@@ -19,7 +19,6 @@ import qualified Git import qualified Git.LsFiles as LsFiles import Backend-import Annex.Version import Utility.FileMode import Utility.Tmp import qualified Upgrade.V2@@ -52,16 +51,13 @@ showAction "v1 to v2" ifM (fromRepo Git.repoIsLocalBare)- ( do- moveContent- setVersion latestVersion+ ( moveContent , do moveContent updateSymlinks moveLocationLogs Annex.Queue.flush- setVersion latestVersion ) Upgrade.V2.upgrade
git-annex.cabal view
@@ -1,5 +1,5 @@ Name: git-annex-Version: 6.20160923+Version: 6.20161012 Cabal-Version: >= 1.8 License: GPL-3 Maintainer: Joey Hess <id@joeyh.name>