git-annex 3.20110819 → 3.20110902
raw patch · 98 files changed
+1286/−951 lines, 98 files
Files
- .gitignore +3/−3
- Annex.hs +10/−5
- AnnexQueue.hs +2/−3
- Backend.hs +3/−4
- Backend/SHA.hs +2/−2
- Branch.hs +6/−3
- Build/TestConfig.hs +114/−0
- CHANGELOG +12/−0
- Command.hs +4/−3
- Command/Add.hs +3/−2
- Command/AddUrl.hs +3/−3
- Command/Drop.hs +1/−0
- Command/DropUnused.hs +1/−1
- Command/Find.hs +1/−1
- Command/Fix.hs +2/−1
- Command/FromKey.hs +2/−1
- Command/Fsck.hs +2/−1
- Command/Init.hs +1/−5
- Command/Lock.hs +1/−1
- Command/Map.hs +2/−2
- Command/Migrate.hs +3/−2
- Command/RecvKey.hs +1/−1
- Command/SendKey.hs +1/−1
- Command/SetKey.hs +1/−1
- Command/Status.hs +4/−6
- Command/Unannex.hs +2/−1
- Command/Uninit.hs +1/−1
- Command/Unlock.hs +2/−1
- Command/Unused.hs +6/−1
- Command/Version.hs +1/−1
- Command/Whereis.hs +1/−1
- Config.hs +6/−6
- Content.hs +5/−11
- Crypto.hs +3/−1
- Git.hs +5/−1
- Git/LsFiles.hs +1/−1
- Git/Queue.hs +4/−4
- Git/UnionMerge.hs +1/−1
- INSTALL +2/−1
- Init.hs +1/−0
- LocationLog.hs +2/−1
- Makefile +7/−7
- Messages.hs +48/−18
- Messages/JSON.hs +29/−0
- Options.hs +5/−3
- PresenceLog.hs +2/−1
- Remote.hs +29/−16
- Remote/Bup.hs +3/−1
- Remote/Directory.hs +2/−0
- Remote/Git.hs +4/−2
- Remote/Helper/Special.hs +1/−1
- Remote/Helper/Ssh.hs +0/−61
- Remote/Helper/Url.hs +0/−66
- Remote/Hook.hs +1/−0
- Remote/Rsync.hs +3/−0
- Remote/Web.hs +7/−6
- RemoteLog.hs +2/−1
- StatFS.hsc +0/−125
- TestConfig.hs +0/−114
- Touch.hsc +0/−119
- UUID.hs +1/−1
- Upgrade/V1.hs +5/−2
- Upgrade/V2.hs +2/−0
- Utility.hs +25/−210
- Utility/Conditional.hs +26/−0
- Utility/CopyFile.hs +3/−2
- Utility/DataUnits.hs +1/−2
- Utility/JSONStream.hs +44/−0
- Utility/Path.hs +92/−0
- Utility/RsyncFile.hs +1/−1
- Utility/SafeCommand.hs +104/−0
- Utility/Ssh.hs +61/−0
- Utility/StatFS.hsc +125/−0
- Utility/Touch.hsc +119/−0
- Utility/Url.hs +79/−0
- configure.hs +1/−1
- debian/changelog +12/−0
- debian/control +2/−1
- debian/copyright +1/−1
- doc/backends.mdwn +9/−17
- doc/bugs/add_script-friendly_output_options.mdwn +19/−0
- doc/bugs/test_suite_shouldn__39__t_fail_silently.mdwn +3/−0
- doc/copies.mdwn +3/−3
- doc/forum/advantages_of_SHA__42___over_WORM.mdwn +5/−0
- doc/forum/advantages_of_SHA__42___over_WORM/comment_1_96c354cac4b5ce5cf6664943bc84db1d._comment +8/−0
- doc/git-annex.mdwn +22/−18
- doc/install.mdwn +2/−1
- doc/install/OSX/comment_1_0a1760bf0db1f1ba89bdb4c62032f631._comment +13/−0
- doc/news/version_3.20110705.mdwn +0/−9
- doc/news/version_3.20110902.mdwn +9/−0
- doc/todo/git-annex_unused_eats_memory.mdwn +0/−6
- doc/todo/git_annex_init_:_include_repo_description_and__47__or_UUID_in_commit_message.mdwn +2/−0
- doc/todo/smudge.mdwn +79/−20
- doc/walkthrough/fsck:_verifying_your_data.mdwn +5/−5
- doc/walkthrough/unused_data.mdwn +1/−1
- git-annex-shell.hs +2/−1
- git-annex.cabal +2/−2
- test.hs +27/−21
.gitignore view
@@ -2,7 +2,7 @@ *.o test configure-SysConfig.hs+Build/SysConfig.hs git-annex git-annex-shell git-union-merge@@ -13,7 +13,7 @@ html *.tix .hpc-Touch.hs-StatFS.hs+Utility/Touch.hs+Utility/StatFS.hs Remote/S3.hs dist
Annex.hs view
@@ -10,6 +10,7 @@ module Annex ( Annex, AnnexState(..),+ OutputType(..), new, run, eval,@@ -20,6 +21,7 @@ import Control.Monad.State import Control.Monad.IO.Control+import Control.Applicative hiding (empty) import qualified Git import Git.Queue@@ -33,11 +35,12 @@ -- git-annex's monad newtype Annex a = Annex { runAnnex :: StateT AnnexState IO a } deriving (- Functor, Monad, MonadIO, MonadControlIO,- MonadState AnnexState+ MonadState AnnexState,+ Functor,+ Applicative ) -- internal state storage@@ -46,7 +49,7 @@ , backends :: [Backend Annex] , remotes :: [Remote Annex] , repoqueue :: Queue- , quiet :: Bool+ , output :: OutputType , force :: Bool , fast :: Bool , branchstate :: BranchState@@ -61,13 +64,15 @@ , cipher :: Maybe Cipher } +data OutputType = NormalOutput | QuietOutput | JSONOutput+ newState :: Git.Repo -> AnnexState newState gitrepo = AnnexState { repo = gitrepo , backends = [] , remotes = [] , repoqueue = empty- , quiet = False+ , output = NormalOutput , force = False , fast = False , branchstate = startBranchState@@ -84,7 +89,7 @@ {- Create and returns an Annex state object for the specified git repo. -} new :: Git.Repo -> IO AnnexState-new gitrepo = newState `liftM` Git.configRead gitrepo+new gitrepo = newState <$> Git.configRead gitrepo {- performs an action in the Annex monad -} run :: AnnexState -> Annex a -> IO (a, AnnexState)
AnnexQueue.hs view
@@ -17,10 +17,9 @@ import Annex import Messages import qualified Git.Queue-import Utility+import Utility.SafeCommand -{- Adds a git command to the queue, possibly running previously queued- - actions if enough have accumulated. -}+{- Adds a git command to the queue. -} add :: String -> [CommandParam] -> [FilePath] -> Annex () add command params files = do q <- getState repoqueue
Backend.hs view
@@ -122,8 +122,7 @@ where unknown = error $ "unknown backend " ++ s maybeLookupBackendName :: String -> Maybe (Backend Annex)-maybeLookupBackendName s =- if 1 /= length matches- then Nothing- else Just $ head matches+maybeLookupBackendName s+ | length matches == 1 = Just $ head matches+ | otherwise = Nothing where matches = filter (\b -> s == B.name b) list
Backend/SHA.hs view
@@ -23,8 +23,8 @@ import Types import Types.Backend import Types.Key-import Utility-import qualified SysConfig+import Utility.SafeCommand+import qualified Build.SysConfig as SysConfig type SHASize = Int
Branch.hs view
@@ -20,6 +20,7 @@ import Control.Monad (when, unless, liftM) import Control.Monad.State (liftIO)+import Control.Applicative ((<$>)) import System.FilePath import System.Directory import Data.String.Utils@@ -37,6 +38,8 @@ import qualified Git.UnionMerge import qualified Annex import Utility+import Utility.Conditional+import Utility.SafeCommand import Types import Messages import Locations@@ -156,7 +159,7 @@ staged <- stageJournalFiles refs <- siblingBranches- updated <- catMaybes `liftM` mapM updateRef refs+ updated <- catMaybes <$> mapM updateRef refs g <- Annex.gitRepo unless (null updated && not staged) $ liftIO $ Git.commit g "update" fullname (fullname:updated)@@ -180,7 +183,7 @@ {- Does the git-annex branch or a foo/git-annex branch exist? -} hasSomeBranch :: Annex Bool-hasSomeBranch = liftM (not . null) siblingBranches+hasSomeBranch = not . null <$> siblingBranches {- List of all git-annex branches, including the main one and any - from remotes. -}@@ -321,7 +324,7 @@ {- List of journal files. -} getJournalFiles :: Annex [FilePath]-getJournalFiles = fmap (map fileJournal) getJournalFilesRaw+getJournalFiles = map fileJournal <$> getJournalFilesRaw getJournalFilesRaw :: Annex [FilePath] getJournalFilesRaw = do
+ Build/TestConfig.hs view
@@ -0,0 +1,114 @@+{- Tests the system and generates Build.SysConfig.hs. -}++module Build.TestConfig where++import System.IO+import System.Cmd+import System.Exit++type ConfigKey = String+data ConfigValue =+ BoolConfig Bool |+ StringConfig String |+ MaybeStringConfig (Maybe String)+data Config = Config ConfigKey ConfigValue++type Test = IO Config+type TestName = String+data TestCase = TestCase TestName Test++instance Show ConfigValue where+ show (BoolConfig b) = show b+ show (StringConfig s) = show s+ show (MaybeStringConfig s) = show s++instance Show Config where+ show (Config key value) = unlines+ [ key ++ " :: " ++ valuetype value+ , key ++ " = " ++ show value+ ]+ where+ valuetype (BoolConfig _) = "Bool"+ valuetype (StringConfig _) = "String"+ valuetype (MaybeStringConfig _) = "Maybe String"++writeSysConfig :: [Config] -> IO ()+writeSysConfig config = writeFile "Build/SysConfig.hs" body+ where+ body = unlines $ header ++ map show config ++ footer+ header = [+ "{- Automatically generated. -}"+ , "module Build.SysConfig where"+ , ""+ ]+ footer = []++runTests :: [TestCase] -> IO [Config]+runTests [] = return []+runTests (TestCase tname t : ts) = do+ testStart tname+ c <- t+ testEnd c+ rest <- runTests ts+ return $ c:rest++{- Tests that a command is available, aborting if not. -}+requireCmd :: ConfigKey -> String -> Test+requireCmd k cmdline = do+ ret <- testCmd k cmdline+ handle ret+ where+ handle r@(Config _ (BoolConfig True)) = return r+ handle r = do+ testEnd r+ error $ "** the " ++ c ++ " command is required"+ c = head $ words cmdline++{- Checks if a command is available by running a command line. -}+testCmd :: ConfigKey -> String -> Test+testCmd k cmdline = do+ ret <- system $ quiet cmdline+ return $ Config k (BoolConfig $ ret == ExitSuccess)++{- Ensures that one of a set of commands is available by running each in+ - turn. The Config is set to the first one found. -}+selectCmd :: ConfigKey -> [String] -> String -> Test+selectCmd k = searchCmd+ (return . Config k . StringConfig)+ (\cmds -> do+ testEnd $ Config k $ BoolConfig False+ error $ "* need one of these commands, but none are available: " ++ show cmds+ )++maybeSelectCmd :: ConfigKey -> [String] -> String -> Test+maybeSelectCmd k = searchCmd+ (return . Config k . MaybeStringConfig . Just)+ (\_ -> return $ Config k $ MaybeStringConfig Nothing)++searchCmd :: (String -> Test) -> ([String] -> Test) -> [String] -> String -> Test+searchCmd success failure cmds param = search cmds+ where+ search [] = failure cmds+ search (c:cs) = do+ ret <- system $ quiet c ++ " " ++ param+ if ret == ExitSuccess+ then success c+ else search cs++quiet :: String -> String+quiet s = s ++ " >/dev/null 2>&1"++testStart :: TestName -> IO ()+testStart s = do+ putStr $ " checking " ++ s ++ "..."+ hFlush stdout++testEnd :: Config -> IO ()+testEnd (Config _ (BoolConfig True)) = status "yes"+testEnd (Config _ (BoolConfig False)) = status "no"+testEnd (Config _ (StringConfig s)) = status s+testEnd (Config _ (MaybeStringConfig (Just s))) = status s+testEnd (Config _ (MaybeStringConfig Nothing)) = status "not available"++status :: String -> IO ()+status s = putStrLn $ ' ':s
CHANGELOG view
@@ -1,3 +1,15 @@+git-annex (3.20110902) unstable; urgency=low++ * Set EMAIL when running test suite so that git does not need to be+ configured first. Closes: #638998+ * The wget command will now be used in preference to curl, if available.+ * init: Make description an optional parameter.+ * unused, status: Sped up by avoiding unnecessary stats of annexed files.+ * unused --remote: Reduced memory use to 1/4th what was used before.+ * Add --json switch, to produce machine-consumable output.++ -- Joey Hess <joeyh@debian.org> Fri, 02 Sep 2011 21:20:37 -0400+ git-annex (3.20110819) unstable; urgency=low * Now "git annex init" only has to be run once, when a git repository
Command.hs view
@@ -11,6 +11,7 @@ import System.Directory import System.Posix.Files import Control.Monad (filterM, liftM, when)+import Control.Applicative import System.Path.WildMatch import Text.Regex.PCRE.Light.Char8 import Data.List@@ -183,7 +184,7 @@ withNothing _ _ = error "This command takes no parameters." backendPairs :: CommandSeekBackendFiles-backendPairs a files = liftM (map a) $ Backend.chooseBackends files+backendPairs a files = map a <$> Backend.chooseBackends files {- Filter out files those matching the exclude glob pattern, - if it was specified. -}@@ -204,7 +205,7 @@ {- filter out symlinks -} notSymlink :: FilePath -> IO Bool-notSymlink f = liftM (not . isSymbolicLink) $ liftIO $ getSymbolicLinkStatus f+notSymlink f = liftIO $ not . isSymbolicLink <$> getSymbolicLinkStatus f {- Descriptions of params used in usage messages. -} paramRepeating :: String -> String@@ -271,4 +272,4 @@ - of git file list commands, that assumption tends to hold. -} runPreserveOrder :: ([FilePath] -> IO [FilePath]) -> [FilePath] -> IO [FilePath]-runPreserveOrder a files = liftM (preserveOrder files) (a files)+runPreserveOrder a files = preserveOrder files <$> a files
Command/Add.hs view
@@ -23,8 +23,9 @@ import Types import Content import Messages-import Utility-import Touch+import Utility.Conditional+import Utility.Touch+import Utility.SafeCommand import Locations command :: [Command]
Command/AddUrl.hs view
@@ -14,7 +14,7 @@ import Command import qualified Backend-import qualified Remote.Helper.Url+import qualified Utility.Url as Url import qualified Remote.Web import qualified Command.Add import qualified Annex@@ -23,7 +23,7 @@ import Content import PresenceLog import Locations-import Utility+import Utility.Path command :: [Command] command = [repoCommand "addurl" paramPath seek "add urls to annex"]@@ -53,7 +53,7 @@ let dummykey = Backend.URL.fromUrl url let tmp = gitAnnexTmpLocation g dummykey liftIO $ createDirectoryIfMissing True (parentDir tmp)- ok <- Remote.Helper.Url.download url tmp+ ok <- Url.download url tmp if ok then do [(_, backend)] <- Backend.chooseBackends [file]
Command/Drop.hs view
@@ -15,6 +15,7 @@ import Content import Messages import Utility+import Utility.Conditional import Trust import Config
Command/DropUnused.hs view
@@ -22,7 +22,7 @@ import qualified Remote import qualified Git import Types.Key-import Utility+import Utility.Conditional type UnusedMap = M.Map String Key
Command/Find.hs view
@@ -11,7 +11,7 @@ import Command import Content-import Utility+import Utility.Conditional command :: [Command] command = [repoCommand "find" (paramOptional $ paramRepeating paramPath) seek
Command/Fix.hs view
@@ -13,7 +13,8 @@ import Command import qualified AnnexQueue-import Utility+import Utility.Path+import Utility.SafeCommand import Content import Messages
Command/FromKey.hs view
@@ -14,10 +14,11 @@ import Command import qualified AnnexQueue-import Utility+import Utility.SafeCommand import Content import Messages import Types.Key+import Utility.Path command :: [Command] command = [repoCommand "fromkey" paramPath seek
Command/Fsck.hs view
@@ -27,6 +27,7 @@ import Locations import Trust import Utility.DataUnits+import Utility.Path import Config command :: [Command]@@ -130,7 +131,7 @@ let present = length safelocations if present < needed then do- ppuuids <- Remote.prettyPrintUUIDs untrustedlocations+ ppuuids <- Remote.prettyPrintUUIDs "untrusted" untrustedlocations warning $ missingNote (filename file key) present needed ppuuids return False else return True
Command/Init.hs view
@@ -7,8 +7,6 @@ module Command.Init where -import Control.Monad (when)- import Command import qualified Annex import UUID@@ -17,15 +15,13 @@ command :: [Command] command = [standaloneCommand "init" paramDesc seek- "initialize git-annex with repository description"]+ "initialize git-annex"] seek :: [CommandSeek] seek = [withWords start] start :: CommandStartWords start ws = do- when (null description) $- error "please specify a description of this repository\n" showStart "init" description next $ perform description where
Command/Lock.hs view
@@ -13,7 +13,7 @@ import Command import Messages import qualified AnnexQueue-import Utility+import Utility.SafeCommand command :: [Command] command = [repoCommand "lock" paramPath seek "undo unlock command"]
Command/Map.hs view
@@ -19,10 +19,10 @@ import qualified Git import Messages import Types-import Utility+import Utility.SafeCommand import UUID import Trust-import Remote.Helper.Ssh+import Utility.Ssh import qualified Utility.Dot as Dot -- a link from the first repository to the second (its remote)
Command/Migrate.hs view
@@ -8,6 +8,7 @@ module Command.Migrate where import Control.Monad.State (liftIO)+import Control.Applicative import System.Posix.Files import System.Directory import System.FilePath@@ -20,7 +21,7 @@ import Types import Content import Messages-import Utility+import Utility.Conditional import qualified Command.Add command :: [Command]@@ -39,7 +40,7 @@ next $ perform file key newbackend else stop where- choosebackend Nothing = return . head =<< Backend.orderedList+ choosebackend Nothing = head <$> Backend.orderedList choosebackend (Just backend) = return backend {- Checks if a key is upgradable to a newer representation. -}
Command/RecvKey.hs view
@@ -13,8 +13,8 @@ import Command import CmdLine import Content-import Utility import Utility.RsyncFile+import Utility.Conditional command :: [Command] command = [repoCommand "recvkey" paramKey seek
Command/SendKey.hs view
@@ -14,8 +14,8 @@ import qualified Annex import Command import Content-import Utility import Utility.RsyncFile+import Utility.Conditional import Messages command :: [Command]
Command/SetKey.hs view
@@ -10,7 +10,7 @@ import Control.Monad.State (liftIO) import Command-import Utility+import Utility.SafeCommand import LocationLog import Content import Messages
Command/Status.hs view
@@ -8,6 +8,7 @@ module Command.Status where import Control.Monad.State+import Control.Applicative import Data.Maybe import System.IO import Data.List@@ -112,12 +113,10 @@ cachedKeysReferenced >>= keySizeSum local_annex_keys :: Stat-local_annex_keys = stat "local annex keys" $ - return . show . snd =<< cachedKeysPresent+local_annex_keys = stat "local annex keys" $ show . snd <$> cachedKeysPresent total_annex_keys :: Stat-total_annex_keys = stat "total annex keys" $- return . show . snd =<< cachedKeysReferenced+total_annex_keys = stat "total annex keys" $ show . snd <$> cachedKeysReferenced tmp_size :: Stat tmp_size = staleSize "temporary directory size" gitAnnexTmpDir@@ -126,8 +125,7 @@ bad_data_size = staleSize "bad keys size" gitAnnexBadDir backend_usage :: Stat-backend_usage = stat "backend usage" $- return . usage =<< cachedKeysReferenced+backend_usage = stat "backend usage" $ usage <$> cachedKeysReferenced where usage (ks, _) = pp "" $ sort $ map swap $ splits ks splits :: [Key] -> [(String, Integer)]
Command/Unannex.hs view
@@ -16,7 +16,8 @@ import qualified Command.Drop import qualified Annex import qualified AnnexQueue-import Utility+import Utility.SafeCommand+import Utility.Path import LocationLog import Types import Content
Command/Uninit.hs view
@@ -12,7 +12,7 @@ import System.Exit import Command-import Utility+import Utility.SafeCommand import qualified Git import qualified Annex import qualified Command.Unannex
Command/Unlock.hs view
@@ -16,8 +16,9 @@ import Messages import Locations import Content+import Utility.Conditional import Utility.CopyFile-import Utility+import Utility.Path command :: [Command] command =
Command/Unused.hs view
@@ -5,6 +5,8 @@ - Licensed under the GNU GPL version 3 or higher. -} +{-# LANGUAGE BangPatterns #-}+ module Command.Unused where import Control.Monad (filterM, unless, forM_)@@ -78,9 +80,12 @@ showLongNote $ remoteUnusedMsg r list showLongNote "\n" where+ {- This should run strictly to avoid the filterM+ - building many thunks containing keyLocations data. -} isthere k = do us <- keyLocations k- return $ uuid `elem` us+ let !there = uuid `elem` us+ return there uuid = Remote.uuid r writeUnusedFile :: FilePath -> [(Int, Key)] -> Annex ()
Command/Version.hs view
@@ -12,7 +12,7 @@ import Data.Maybe import Command-import qualified SysConfig+import qualified Build.SysConfig as SysConfig import Version command :: [Command]
Command/Whereis.hs view
@@ -33,7 +33,7 @@ if null uuids then stop else do- pp <- prettyPrintUUIDs uuids+ pp <- prettyPrintUUIDs "whereis" uuids showLongNote pp showOutput next $ return True
Config.hs view
@@ -9,13 +9,14 @@ import Data.Maybe import Control.Monad.State (liftIO)-import Control.Monad (liftM)+import Control.Applicative import System.Cmd.Utils import qualified Git import qualified Annex import Types import Utility+import Utility.SafeCommand type ConfigKey = String @@ -46,16 +47,15 @@ remoteCost :: Git.Repo -> Int -> Annex Int remoteCost r def = do cmd <- getConfig r "cost-command" ""- return . safeparse =<< if not $ null cmd- then liftM snd $ liftIO $ pipeFrom "sh" ["-c", cmd]+ safeparse <$> if not $ null cmd+ then liftIO $ snd <$> pipeFrom "sh" ["-c", cmd] else getConfig r "cost" "" where safeparse v- | null ws || null ps = def- | otherwise = (fst . head) ps+ | null ws = def+ | otherwise = fromMaybe def $ readMaybe $ head ws where ws = words v- ps = reads $ head ws cheapRemoteCost :: Int cheapRemoteCost = 100
Content.hs view
@@ -23,11 +23,10 @@ saveState ) where -import System.IO.Error (try) import System.Directory import Control.Monad.State (liftIO) import System.Path-import Control.Monad (when, filterM)+import Control.Monad import System.Posix.Files import System.FilePath import Data.Maybe@@ -41,7 +40,9 @@ import qualified AnnexQueue import qualified Branch import Utility-import StatFS+import Utility.Conditional+import Utility.StatFS+import Utility.Path import Types.Key import Utility.DataUnits import Config@@ -252,15 +253,8 @@ levela <- dirContents dir levelb <- mapM dirContents levela contents <- mapM dirContents (concat levelb)- files <- filterM present (concat contents)+ let files = concat contents return $ mapMaybe (fileKey . takeFileName) files- where- present d = do- result <- try $- getFileStatus $ d </> takeFileName d- case result of- Right s -> return $ isRegularFile s- Left _ -> return False {- Things to do to record changes to content. -} saveState :: Annex ()
Crypto.hs view
@@ -38,6 +38,7 @@ import System.Posix.IO import System.Posix.Types import System.Posix.Process+import Control.Applicative import Control.Concurrent import Control.Exception (finally) import System.Exit@@ -48,6 +49,7 @@ import Types.Remote import Utility import Utility.Base64+import Utility.SafeCommand import Types.Crypto {- The first half of a Cipher is used for HMAC; the remainder@@ -135,7 +137,7 @@ {- Decrypting an EncryptedCipher is expensive; the Cipher should be cached. -} decryptCipher :: RemoteConfig -> EncryptedCipher -> IO Cipher decryptCipher _ (EncryptedCipher encipher _) = - return . Cipher =<< gpgPipeStrict decrypt encipher+ Cipher <$> gpgPipeStrict decrypt encipher where decrypt = [ Param "--decrypt" ]
Git.hs view
@@ -63,6 +63,7 @@ ) where import Control.Monad (unless, when)+import Control.Applicative import System.Directory import System.FilePath import System.Posix.Directory@@ -85,6 +86,9 @@ import System.Posix.Env (setEnv, unsetEnv, getEnv) import Utility+import Utility.Path+import Utility.Conditional+import Utility.SafeCommand {- There are two types of repositories; those on local disk and those - accessed via an URL. -}@@ -443,7 +447,7 @@ pipeWriteRead g (map Param $ ["commit-tree", tree] ++ ps) message run g "update-ref" [Param newref, Param sha] where- ignorehandle a = return . snd =<< a+ ignorehandle a = snd <$> a ps = concatMap (\r -> ["-p", r]) parentrefs {- Reads null terminated output of a git command (as enabled by the -z
Git/LsFiles.hs view
@@ -16,7 +16,7 @@ ) where import Git-import Utility+import Utility.SafeCommand {- Scans for files that are checked into git at the specified locations. -} inRepo :: Repo -> [FilePath] -> IO [FilePath]
Git/Queue.hs view
@@ -19,15 +19,15 @@ import System.Cmd.Utils import Data.String.Utils import Control.Monad (forM_)-import Utility+import Utility.SafeCommand import Git {- An action to perform in a git repository. The file to act on - is not included, and must be able to be appended after the params. -}-data Action = Action {- getSubcommand :: String,- getParams :: [CommandParam]+data Action = Action+ { getSubcommand :: String+ , getParams :: [CommandParam] } deriving (Show, Eq, Ord) {- A queue of actions to perform (in any order) on a git repository,
Git/UnionMerge.hs view
@@ -18,7 +18,7 @@ import Data.String.Utils import Git-import Utility+import Utility.SafeCommand {- Performs a union merge between two branches, staging it in the index. - Any previously staged changes in the index will be lost.
INSTALL view
@@ -28,13 +28,14 @@ * [QuickCheck 2](http://hackage.haskell.org/package/QuickCheck) * [HTTP](http://hackage.haskell.org/package/HTTP) * [hS3](http://hackage.haskell.org/package/hS3) (optional, but recommended)+ * [json](http://hackage.haskell.org/package/json) * Shell commands * [git](http://git-scm.com/) * [uuid](http://www.ossp.org/pkg/lib/uuid/) (or `uuidgen` from util-linux) * [xargs](http://savannah.gnu.org/projects/findutils/) * [rsync](http://rsync.samba.org/)- * [curl](http://http://curl.haxx.se/) (optional, but recommended)+ * [wget](http://www.gnu.org/software/wget/) or [curl](http://http://curl.haxx.se/) (optional, but recommended) * [sha1sum](ftp://ftp.gnu.org/gnu/coreutils/) (optional, but recommended; a sha1 command will also do) * [gpg](http://gnupg.org/) (optional; needed for encryption)
Init.hs view
@@ -22,6 +22,7 @@ import Messages import Types import Utility+import Utility.Conditional import UUID initialize :: Annex ()
LocationLog.hs view
@@ -24,6 +24,7 @@ import System.FilePath import Control.Monad (when)+import Control.Applicative import Data.Maybe import qualified Git@@ -49,7 +50,7 @@ {- Finds all keys that have location log information. - (There may be duplicate keys in the list.) -} loggedKeys :: Annex [Key]-loggedKeys = return . mapMaybe (logFileKey . takeFileName) =<< Branch.files+loggedKeys = mapMaybe (logFileKey . takeFileName) <$> Branch.files {- The filename of the log file for a given key. -} logFile :: Key -> String
Makefile view
@@ -8,12 +8,11 @@ bins=git-annex git-annex-shell git-union-merge mans=git-annex.1 git-annex-shell.1 git-union-merge.1+sources=Build/SysConfig.hs Utility/StatFS.hs Utility/Touch.hs Remote/S3.hs all: $(bins) $(mans) docs -sources: SysConfig.hs StatFS.hs Touch.hs Remote/S3.hs--SysConfig.hs: configure.hs TestConfig.hs+Build/SysConfig.hs: configure.hs Build/TestConfig.hs $(GHCMAKE) configure ./configure @@ -30,7 +29,7 @@ echo "** building without S3 support"; \ fi -$(bins): SysConfig.hs Touch.hs StatFS.hs Remote/S3.o+$(bins): $(sources) $(GHCMAKE) $@ git-annex.1: doc/git-annex.mdwn@@ -54,7 +53,9 @@ @if ! $(GHCMAKE) -O0 test; then \ echo "** not running test suite" >&2; \ else \- ./test; \+ if ! ./test; then \+ echo "** test suite failed!" >&2; \+ fi; \ fi testcoverage: $(bins)@@ -82,8 +83,7 @@ --exclude='news/.*' clean:- rm -rf build $(bins) $(mans) test configure *.tix .hpc \- StatFS.hs Touch.hs SysConfig.hs Remote/S3.hs+ rm -rf build $(bins) $(mans) test configure *.tix .hpc $(sources) rm -rf doc/.ikiwiki html dist find . \( -name \*.o -or -name \*.hi \) -exec rm {} \;
Messages.hs view
@@ -5,28 +5,40 @@ - Licensed under the GNU GPL version 3 or higher. -} -module Messages where+module Messages (+ showStart,+ showNote,+ showAction,+ showProgress,+ showSideAction,+ showOutput,+ showLongNote,+ showEndOk,+ showEndFail,+ showEndResult,+ showErr,+ warning,+ indent,+ maybeShowJSON,+ setupConsole+) where import Control.Monad.State (liftIO) import System.IO-import Control.Monad (unless) import Data.String.Utils+import Text.JSON import Types import qualified Annex--verbose :: Annex () -> Annex ()-verbose a = do- q <- Annex.getState Annex.quiet- unless q a+import qualified Messages.JSON as JSON showStart :: String -> String -> Annex ()-showStart command file = verbose $ liftIO $ do+showStart command file = handle (JSON.start command file) $ do putStr $ command ++ " " ++ file ++ " " hFlush stdout showNote :: String -> Annex ()-showNote s = verbose $ liftIO $ do+showNote s = handle (JSON.note s) $ do putStr $ "(" ++ s ++ ") " hFlush stdout @@ -34,28 +46,31 @@ showAction s = showNote $ s ++ "..." showProgress :: Annex ()-showProgress = verbose $ liftIO $ do+showProgress = handle q $ do putStr "." hFlush stdout showSideAction :: String -> Annex ()-showSideAction s = verbose $ liftIO $ putStrLn $ "(" ++ s ++ "...)"+showSideAction s = handle q $ putStrLn $ "(" ++ s ++ "...)" showOutput :: Annex ()-showOutput = verbose $ liftIO $ putStr "\n"+showOutput = handle q $ putStr "\n" showLongNote :: String -> Annex ()-showLongNote s = verbose $ liftIO $ putStr $ '\n' : indent s+showLongNote s = handle (JSON.note s) $ putStr $ '\n' : indent s showEndOk :: Annex ()-showEndOk = verbose $ liftIO $ putStrLn "ok"+showEndOk = showEndResult True showEndFail :: Annex ()-showEndFail = verbose $ liftIO $ putStrLn "failed"+showEndFail = showEndResult False showEndResult :: Bool -> Annex ()-showEndResult True = showEndOk-showEndResult False = showEndFail+showEndResult b = handle (JSON.end b) $ putStrLn msg+ where+ msg+ | b = "ok"+ | otherwise = "failed" showErr :: (Show a) => a -> Annex () showErr e = liftIO $ do@@ -64,7 +79,7 @@ warning :: String -> Annex () warning w = do- verbose $ liftIO $ putStr "\n"+ handle q $ putStr "\n" liftIO $ do hFlush stdout hPutStrLn stderr $ indent w@@ -84,3 +99,18 @@ setupConsole = do hSetBinaryMode stdout True hSetBinaryMode stderr True++handle :: IO () -> IO () -> Annex ()+handle json normal = do+ output <- Annex.getState Annex.output+ case output of+ Annex.NormalOutput -> liftIO normal+ Annex.QuietOutput -> q+ Annex.JSONOutput -> liftIO json++{- Shows a JSON value only when in json mode. -}+maybeShowJSON :: JSON a => [(String, a)] -> Annex ()+maybeShowJSON v = handle (JSON.add v) q++q :: Monad m => m ()+q = return ()
+ Messages/JSON.hs view
@@ -0,0 +1,29 @@+{- git-annex JSON output+ -+ - Copyright 2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Messages.JSON (+ start,+ end,+ note,+ add+) where++import Text.JSON++import qualified Utility.JSONStream as Stream++start :: String -> String -> IO ()+start command file = putStr $ Stream.start [("command", command), ("file", file)]++end :: Bool -> IO ()+end b = putStr $ Stream.add [("success", b)] ++ Stream.end++note :: String -> IO ()+note s = add [("note", s)]++add :: JSON a => [(String, a)] -> IO ()+add v = putStr $ Stream.add v
Options.hs view
@@ -26,10 +26,12 @@ "allow actions that may lose annexed data" , Option ['F'] ["fast"] (NoArg (setfast True)) "avoid slow operations"- , Option ['q'] ["quiet"] (NoArg (setquiet True))+ , Option ['q'] ["quiet"] (NoArg (setoutput Annex.QuietOutput)) "avoid verbose output"- , Option ['v'] ["verbose"] (NoArg (setquiet False))+ , Option ['v'] ["verbose"] (NoArg (setoutput Annex.NormalOutput)) "allow verbose output (default)"+ , Option ['j'] ["json"] (NoArg (setoutput Annex.JSONOutput))+ "enable JSON output" , Option ['d'] ["debug"] (NoArg (setdebug)) "show debug messages" , Option ['b'] ["backend"] (ReqArg setforcebackend paramName)@@ -38,7 +40,7 @@ where setforce v = Annex.changeState $ \s -> s { Annex.force = v } setfast v = Annex.changeState $ \s -> s { Annex.fast = v }- setquiet v = Annex.changeState $ \s -> s { Annex.quiet = v }+ setoutput v = Annex.changeState $ \s -> s { Annex.output = v } setforcebackend v = Annex.changeState $ \s -> s { Annex.forcebackend = Just v } setdebug = liftIO $ updateGlobalLogger rootLoggerName $ setLevel DEBUG
PresenceLog.hs view
@@ -28,6 +28,7 @@ import System.Locale import qualified Data.Map as Map import Control.Monad.State (liftIO)+import Control.Applicative import qualified Branch import Types@@ -81,7 +82,7 @@ {- Reads a log file. - Note that the LogLines returned may be in any order. -} readLog :: FilePath -> Annex [LogLine]-readLog file = return . parseLog =<< Branch.get file+readLog file = parseLog <$> Branch.get file parseLog :: String -> [LogLine] parseLog s = filter parsable $ map read $ lines s
Remote.hs view
@@ -29,11 +29,14 @@ forceTrust ) where -import Control.Monad (filterM, liftM2)+import Control.Monad (filterM) import Data.List import qualified Data.Map as M import Data.String.Utils import Data.Maybe+import Control.Applicative+import Text.JSON+import Text.JSON.Generic import Types import Types.Remote@@ -111,30 +114,40 @@ nameToUUID n = do res <- byName' n case res of- Left e -> return . fromMaybe (error e) =<< byDescription+ Left e -> fromMaybe (error e) <$> byDescription Right r -> return $ uuid r where- byDescription = return . M.lookup n . invertMap =<< uuidMap+ byDescription = M.lookup n . invertMap <$> uuidMap invertMap = M.fromList . map swap . M.toList swap (a, b) = (b, a) -{- Pretty-prints a list of UUIDs of remotes. -}-prettyPrintUUIDs :: [UUID] -> Annex String-prettyPrintUUIDs uuids = do+{- Pretty-prints a list of UUIDs of remotes, for human display.+ -+ - Shows descriptions from the uuid log, falling back to remote names,+ - as some remotes may not be in the uuid log.+ -+ - When JSON is enabled, also generates a machine-readable description+ - of the UUIDs. -}+prettyPrintUUIDs :: String -> [UUID] -> Annex String+prettyPrintUUIDs desc uuids = do here <- getUUID =<< Annex.gitRepo- -- Show descriptions from the uuid log, falling back to remote names,- -- as some remotes may not be in the uuid log- m <- liftM2 M.union uuidMap $- return . M.fromList . map (\r -> (uuid r, name r)) =<< genList- return $ unwords $ map (\u -> "\t" ++ prettify m u here ++ "\n") uuids+ m <- M.union <$> uuidMap <*> availMap+ maybeShowJSON [(desc, map (jsonify m here) uuids)]+ return $ unwords $ map (\u -> "\t" ++ prettify m here u ++ "\n") uuids where- prettify m u here = base ++ ishere+ availMap = M.fromList . map (\r -> (uuid r, name r)) <$> genList+ findlog m u = M.findWithDefault "" u m+ prettify m here u = base ++ ishere where base = if not $ null $ findlog m u then u ++ " -- " ++ findlog m u else u ishere = if here == u then " <-- here" else ""- findlog m u = M.findWithDefault "" u m+ jsonify m here u = toJSObject+ [ ("uuid", toJSON u)+ , ("description", toJSON $ findlog m u)+ , ("here", toJSON $ here == u)+ ] {- Filters a list of remotes to ones that have the listed uuids. -} remotesWithUUID :: [Remote Annex] -> [UUID] -> [Remote Annex]@@ -147,7 +160,7 @@ {- Cost ordered lists of remotes that the LocationLog indicate may have a key. -} keyPossibilities :: Key -> Annex [Remote Annex]-keyPossibilities key = return . fst =<< keyPossibilities' False key+keyPossibilities key = fst <$> keyPossibilities' False key {- Cost ordered lists of remotes that the LocationLog indicate may have a key. -@@ -185,8 +198,8 @@ untrusteduuids <- trustGet UnTrusted let uuidswanted = filteruuids uuids (u:exclude++untrusteduuids) let uuidsskipped = filteruuids uuids (u:exclude++uuidswanted)- ppuuidswanted <- Remote.prettyPrintUUIDs uuidswanted- ppuuidsskipped <- Remote.prettyPrintUUIDs uuidsskipped+ ppuuidswanted <- Remote.prettyPrintUUIDs "wanted" uuidswanted+ ppuuidsskipped <- Remote.prettyPrintUUIDs "skipped" uuidsskipped showLongNote $ message ppuuidswanted ppuuidsskipped where filteruuids l x = filter (`notElem` x) l
Remote/Bup.hs view
@@ -29,8 +29,10 @@ import Locations import Config import Utility+import Utility.Conditional+import Utility.SafeCommand import Messages-import Remote.Helper.Ssh+import Utility.Ssh import Remote.Helper.Special import Remote.Helper.Encryptable import Crypto
Remote/Directory.hs view
@@ -27,6 +27,8 @@ import Config import Content import Utility+import Utility.Conditional+import Utility.Path import Remote.Helper.Special import Remote.Helper.Encryptable import Crypto
Remote/Git.hs view
@@ -25,8 +25,10 @@ import Messages import Utility.CopyFile import Utility.RsyncFile-import Remote.Helper.Ssh-import qualified Remote.Helper.Url as Url+import Utility.Ssh+import Utility.SafeCommand+import Utility.Path+import qualified Utility.Url as Url import Config import Init
Remote/Helper/Special.hs view
@@ -17,7 +17,7 @@ import qualified Git import qualified Annex import UUID-import Utility+import Utility.SafeCommand {- Special remotes don't have a configured url, so Git.Repo does not - automatically generate remotes for them. This looks for a different
− Remote/Helper/Ssh.hs
@@ -1,61 +0,0 @@-{- git-annex remote access with ssh- -- - Copyright 2011 Joey Hess <joey@kitenet.net>- -- - Licensed under the GNU GPL version 3 or higher.- -}--module Remote.Helper.Ssh where--import Control.Monad.State (liftIO)--import qualified Git-import Utility-import Types-import Config--{- Generates parameters to ssh to a repository's host and run a command.- - Caller is responsible for doing any neccessary shellEscaping of the- - passed command. -}-sshToRepo :: Git.Repo -> [CommandParam] -> Annex [CommandParam]-sshToRepo repo sshcmd = do- s <- getConfig repo "ssh-options" ""- let sshoptions = map Param (words s)- let sshport = case Git.urlPort repo of- Nothing -> []- Just p -> [Param "-p", Param (show p)]- let sshhost = Param $ Git.urlHostUser repo- return $ sshoptions ++ sshport ++ [sshhost] ++ sshcmd--{- Generates parameters to run a git-annex-shell command on a remote- - repository. -}-git_annex_shell :: Git.Repo -> String -> [CommandParam] -> Annex (Maybe (FilePath, [CommandParam]))-git_annex_shell r command params- | not $ Git.repoIsUrl r = return $ Just (shellcmd, shellopts)- | Git.repoIsSsh r = do- sshparams <- sshToRepo r [Param sshcmd]- return $ Just ("ssh", sshparams)- | otherwise = return Nothing- where- dir = Git.workTree r- shellcmd = "git-annex-shell"- shellopts = Param command : File dir : params- sshcmd = shellcmd ++ " " ++ - unwords (map shellEscape $ toCommand shellopts)--{- Uses a supplied function (such as boolSystem) to run a git-annex-shell- - command on a remote.- -- - Or, if the remote does not support running remote commands, returns- - a specified error value. -}-onRemote - :: Git.Repo- -> (FilePath -> [CommandParam] -> IO a, a)- -> String- -> [CommandParam]- -> Annex a-onRemote r (with, errorval) command params = do- s <- git_annex_shell r command params- case s of- Just (c, ps) -> liftIO $ with c ps- Nothing -> return errorval
− Remote/Helper/Url.hs
@@ -1,66 +0,0 @@-{- Url downloading for remotes.- -- - Copyright 2011 Joey Hess <joey@kitenet.net>- -- - Licensed under the GNU GPL version 3 or higher.- -}--module Remote.Helper.Url (- exists,- download,- get-) where--import Control.Monad (liftM)-import Control.Monad.State (liftIO)-import qualified Network.Browser as Browser-import Network.HTTP-import Network.URI--import Types-import Messages-import Utility--type URLString = String--{- Checks that an url exists and could be successfully downloaded. -}-exists :: URLString -> IO Bool-exists url =- case parseURI url of- Nothing -> return False- Just u -> do- r <- request u HEAD- case rspCode r of- (2,_,_) -> return True- _ -> return False--{- Used to download large files, such as the contents of keys.- - Uses curl program for its progress bar. -}-download :: URLString -> FilePath -> Annex Bool-download url file = do- showOutput -- make way for curl progress bar- liftIO $ boolSystem "curl" [Params "-L -C - -# -o", File file, File url]--{- Downloads a small file. -}-get :: URLString -> IO String-get url =- case parseURI url of- Nothing -> error "url parse error"- Just u -> do- r <- request u GET- case rspCode r of- (2,_,_) -> return $ rspBody r- _ -> error $ rspReason r--{- Makes a http request of an url. For example, HEAD can be used to- - check if the url exists, or GET used to get the url content (best for- - small urls). -}-request :: URI -> RequestMethod -> IO (Response String)-request url requesttype = Browser.browse $ do- Browser.setErrHandler ignore- Browser.setOutHandler ignore- Browser.setAllowRedirects True- liftM snd $ Browser.request- (mkRequest requesttype url :: Request_String)- where- ignore = const $ return ()
Remote/Hook.hs view
@@ -28,6 +28,7 @@ import Config import Content import Utility+import Utility.SafeCommand import Remote.Helper.Special import Remote.Helper.Encryptable import Crypto
Remote/Rsync.hs view
@@ -26,11 +26,14 @@ import Config import Content import Utility+import Utility.Conditional import Remote.Helper.Special import Remote.Helper.Encryptable import Crypto import Messages import Utility.RsyncFile+import Utility.SafeCommand+import Utility.Path type RsyncUrl = String
Remote/Web.hs view
@@ -24,7 +24,8 @@ import PresenceLog import LocationLog import Locations-import qualified Remote.Helper.Url as Url+import Utility+import qualified Utility.Url as Url type URLString = String @@ -86,12 +87,12 @@ logChange g key webUUID (if null us then InfoMissing else InfoPresent) downloadKey :: Key -> FilePath -> Annex Bool-downloadKey key file = iter =<< getUrls key+downloadKey key file = get =<< getUrls key where- iter [] = return False- iter (url:urls) = do- ok <- Url.download url file- if ok then return ok else iter urls+ get [] = do+ warning "no known url"+ return False+ get urls = anyM (`Url.download` file) urls uploadKey :: Key -> Annex Bool uploadKey _ = do
RemoteLog.hs view
@@ -19,6 +19,7 @@ import qualified Data.Map as M import Data.Maybe import Data.Char+import Control.Applicative import qualified Branch import Types@@ -40,7 +41,7 @@ {- Map of remotes by uuid containing key/value config maps. -} readRemoteLog :: Annex (M.Map UUID RemoteConfig)-readRemoteLog = return . remoteLogParse =<< Branch.get remoteLog+readRemoteLog = remoteLogParse <$> Branch.get remoteLog remoteLogParse :: String -> M.Map UUID RemoteConfig remoteLogParse s =
− StatFS.hsc
@@ -1,125 +0,0 @@--------------------------------------------------------------------------------- |------ (This code originally comes from xmobar)--- --- Module : StatFS--- Copyright : (c) Jose A Ortega Ruiz--- License : BSD-3-clause------ All rights reserved.--- --- Redistribution and use in source and binary forms, with or without--- modification, are permitted provided that the following conditions--- are met:--- --- 1. Redistributions of source code must retain the above copyright--- notice, this list of conditions and the following disclaimer.--- 2. Redistributions in binary form must reproduce the above copyright--- notice, this list of conditions and the following disclaimer in the--- documentation and/or other materials provided with the distribution.--- 3. Neither the name of the author nor the names of his contributors--- may be used to endorse or promote products derived from this software--- without specific prior written permission.--- --- THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND--- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE--- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE--- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE--- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL--- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS--- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)--- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT--- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY--- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF--- SUCH DAMAGE.------ Maintainer : Jose A Ortega Ruiz <jao@gnu.org>--- Stability : unstable--- Portability : unportable------ A binding to C's statvfs(2)-----------------------------------------------------------------------------------{-# LANGUAGE CPP, ForeignFunctionInterface, EmptyDataDecls #-}---module StatFS ( FileSystemStats(..), getFileSystemStats ) where--import Foreign-import Foreign.C.Types-import Foreign.C.String-import Data.ByteString (useAsCString)-import Data.ByteString.Char8 (pack)--#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) || defined (__APPLE__)-# include <sys/param.h>-# include <sys/mount.h>-#else-#if defined (__linux__)-#include <sys/vfs.h>-#else-#define UNKNOWN-#endif-#endif--data FileSystemStats = FileSystemStats {- fsStatBlockSize :: Integer- -- ^ Optimal transfer block size.- , fsStatBlockCount :: Integer- -- ^ Total data blocks in file system.- , fsStatByteCount :: Integer- -- ^ Total bytes in file system.- , fsStatBytesFree :: Integer- -- ^ Free bytes in file system.- , fsStatBytesAvailable :: Integer- -- ^ Free bytes available to non-superusers.- , fsStatBytesUsed :: Integer- -- ^ Bytes used.- } deriving (Show, Eq)--data CStatfs--#ifdef UNKNOWN-#warning free space checking code not available for this OS-#else-#if defined(__APPLE__)-foreign import ccall unsafe "sys/mount.h statfs64"-#else-#if defined(__FreeBSD__) || defined (__FreeBSD_kernel__)-foreign import ccall unsafe "sys/mount.h statfs"-#else-foreign import ccall unsafe "sys/vfs.h statfs64"-#endif-#endif- c_statfs :: CString -> Ptr CStatfs -> IO CInt-#endif--toI :: CULong -> Integer-toI = toInteger--getFileSystemStats :: String -> IO (Maybe FileSystemStats)-getFileSystemStats path =-#ifdef UNKNOWN- return Nothing-#else- allocaBytes (#size struct statfs) $ \vfs ->- useAsCString (pack path) $ \cpath -> do- res <- c_statfs cpath vfs- if res == -1 then return Nothing- else do- bsize <- (#peek struct statfs, f_bsize) vfs- bcount <- (#peek struct statfs, f_blocks) vfs- bfree <- (#peek struct statfs, f_bfree) vfs- bavail <- (#peek struct statfs, f_bavail) vfs- let bpb = toI bsize- return $ Just FileSystemStats- { fsStatBlockSize = bpb- , fsStatBlockCount = toI bcount- , fsStatByteCount = toI bcount * bpb- , fsStatBytesFree = toI bfree * bpb- , fsStatBytesAvailable = toI bavail * bpb- , fsStatBytesUsed = toI (bcount - bfree) * bpb- }-#endif
− TestConfig.hs
@@ -1,114 +0,0 @@-{- Tests the system and generates SysConfig.hs. -}--module TestConfig where--import System.IO-import System.Cmd-import System.Exit--type ConfigKey = String-data ConfigValue =- BoolConfig Bool |- StringConfig String |- MaybeStringConfig (Maybe String)-data Config = Config ConfigKey ConfigValue--type Test = IO Config-type TestName = String-data TestCase = TestCase TestName Test--instance Show ConfigValue where- show (BoolConfig b) = show b- show (StringConfig s) = show s- show (MaybeStringConfig s) = show s--instance Show Config where- show (Config key value) = unlines- [ key ++ " :: " ++ valuetype value- , key ++ " = " ++ show value- ]- where- valuetype (BoolConfig _) = "Bool"- valuetype (StringConfig _) = "String"- valuetype (MaybeStringConfig _) = "Maybe String"--writeSysConfig :: [Config] -> IO ()-writeSysConfig config = writeFile "SysConfig.hs" body- where- body = unlines $ header ++ map show config ++ footer- header = [- "{- Automatically generated. -}"- , "module SysConfig where"- , ""- ]- footer = []--runTests :: [TestCase] -> IO [Config]-runTests [] = return []-runTests (TestCase tname t : ts) = do- testStart tname- c <- t- testEnd c- rest <- runTests ts- return $ c:rest--{- Tests that a command is available, aborting if not. -}-requireCmd :: ConfigKey -> String -> Test-requireCmd k cmdline = do- ret <- testCmd k cmdline- handle ret- where- handle r@(Config _ (BoolConfig True)) = return r- handle r = do- testEnd r- error $ "** the " ++ c ++ " command is required"- c = head $ words cmdline--{- Checks if a command is available by running a command line. -}-testCmd :: ConfigKey -> String -> Test-testCmd k cmdline = do- ret <- system $ quiet cmdline- return $ Config k (BoolConfig $ ret == ExitSuccess)--{- Ensures that one of a set of commands is available by running each in- - turn. The Config is set to the first one found. -}-selectCmd :: ConfigKey -> [String] -> String -> Test-selectCmd k = searchCmd- (return . Config k . StringConfig)- (\cmds -> do- testEnd $ Config k $ BoolConfig False- error $ "* need one of these commands, but none are available: " ++ show cmds- )--maybeSelectCmd :: ConfigKey -> [String] -> String -> Test-maybeSelectCmd k = searchCmd- (return . Config k . MaybeStringConfig . Just)- (\_ -> return $ Config k $ MaybeStringConfig Nothing)--searchCmd :: (String -> Test) -> ([String] -> Test) -> [String] -> String -> Test-searchCmd success failure cmds param = search cmds- where- search [] = failure cmds- search (c:cs) = do- ret <- system $ quiet c ++ " " ++ param- if ret == ExitSuccess- then success c- else search cs--quiet :: String -> String-quiet s = s ++ " >/dev/null 2>&1"--testStart :: TestName -> IO ()-testStart s = do- putStr $ " checking " ++ s ++ "..."- hFlush stdout--testEnd :: Config -> IO ()-testEnd (Config _ (BoolConfig True)) = status "yes"-testEnd (Config _ (BoolConfig False)) = status "no"-testEnd (Config _ (StringConfig s)) = status s-testEnd (Config _ (MaybeStringConfig (Just s))) = status s-testEnd (Config _ (MaybeStringConfig Nothing)) = status "not available"--status :: String -> IO ()-status s = putStrLn $ ' ':s
− Touch.hsc
@@ -1,119 +0,0 @@-{- More control over touching a file.- -- - Copyright 2011 Joey Hess <joey@kitenet.net>- -- - Licensed under the GNU GPL version 3 or higher.- -}--{-# LANGUAGE ForeignFunctionInterface #-}--module Touch (- TimeSpec(..),- touchBoth,- touch-) where--import Foreign-import Foreign.C-import Control.Monad (when)--newtype TimeSpec = TimeSpec CTime--{- Changes the access and modification times of an existing file.- Can follow symlinks, or not. Throws IO error on failure. -}-touchBoth :: FilePath -> TimeSpec -> TimeSpec -> Bool -> IO ()--touch :: FilePath -> TimeSpec -> Bool -> IO ()-touch file mtime follow = touchBoth file mtime mtime follow--#include <sys/types.h>-#include <sys/stat.h>-#include <fcntl.h>-#include <sys/time.h>--#ifndef _BSD_SOURCE-#define _BSD_SOURCE-#endif--#if (defined UTIME_OMIT && defined UTIME_NOW && defined AT_FDCWD && defined AT_SYMLINK_NOFOLLOW)--at_fdcwd :: CInt-at_fdcwd = #const AT_FDCWD--at_symlink_nofollow :: CInt-at_symlink_nofollow = #const AT_SYMLINK_NOFOLLOW--instance Storable TimeSpec where- -- use the larger alignment of the two types in the struct- alignment _ = max sec_alignment nsec_alignment- where- sec_alignment = alignment (undefined::CTime)- nsec_alignment = alignment (undefined::CLong)- sizeOf _ = #{size struct timespec}- peek ptr = do- sec <- #{peek struct timespec, tv_sec} ptr- return $ TimeSpec sec- poke ptr (TimeSpec sec) = do- #{poke struct timespec, tv_sec} ptr sec- #{poke struct timespec, tv_nsec} ptr (0 :: CLong)--{- While its interface is beastly, utimensat is in recent- POSIX standards, unlike lutimes. -}-foreign import ccall "utimensat" - c_utimensat :: CInt -> CString -> Ptr TimeSpec -> CInt -> IO CInt--touchBoth file atime mtime follow = - allocaArray 2 $ \ptr ->- withCString file $ \f -> do- pokeArray ptr [atime, mtime]- r <- c_utimensat at_fdcwd f ptr flags- when (r /= 0) $ throwErrno "touchBoth"- where- flags = if follow- then 0- else at_symlink_nofollow --#else-#if 0-{- Using lutimes is needed for BSD.- - - - TODO: test if lutimes is available. May have to do it in configure.- - TODO: TimeSpec uses a CTime, while tv_sec is a CLong. It is implementation- - dependent whether these are the same; need to find a cast that works.- - (Without the cast it works on linux i386, but- - maybe not elsewhere.)- -}--instance Storable TimeSpec where- alignment _ = alignment (undefined::CLong)- sizeOf _ = #{size struct timeval}- peek ptr = do- sec <- #{peek struct timeval, tv_sec} ptr- return $ TimeSpec sec- poke ptr (TimeSpec sec) = do- #{poke struct timeval, tv_sec} ptr sec- #{poke struct timeval, tv_usec} ptr (0 :: CLong) --foreign import ccall "utimes" - c_utimes :: CString -> Ptr TimeSpec -> IO CInt-foreign import ccall "lutimes" - c_lutimes :: CString -> Ptr TimeSpec -> IO CInt--touchBoth file atime mtime follow = - allocaArray 2 $ \ptr ->- withCString file $ \f -> do- pokeArray ptr [atime, mtime]- r <- syscall f ptr- if (r /= 0)- then throwErrno "touchBoth"- else return ()- where- syscall = if follow- then c_lutimes- else c_utimes--#else-#warning "utimensat and lutimes not available; building without symlink timestamp preservation support"-touchBoth _ _ _ _ = return ()-#endif-#endif
UUID.hs view
@@ -33,7 +33,7 @@ import Types import Types.UUID import qualified Annex-import qualified SysConfig+import qualified Build.SysConfig as SysConfig import Config configkey :: String
Upgrade/V1.hs view
@@ -11,6 +11,7 @@ import System.Directory import Control.Monad.State (liftIO) import Control.Monad (filterM, forM_, unless)+import Control.Applicative import System.Posix.Files import System.FilePath import Data.String.Utils@@ -31,6 +32,8 @@ import Messages import Version import Utility+import Utility.SafeCommand+import Utility.Path import qualified Upgrade.V2 -- v2 adds hashing of filenames of content and location log files.@@ -190,7 +193,7 @@ writeLog1 file ls = viaTmp writeFile file (unlines $ map show ls) readLog1 :: FilePath -> IO [LogLine]-readLog1 file = catch (return . parseLog =<< readFileStrict file) (const $ return [])+readLog1 file = catch (parseLog <$> readFileStrict file) (const $ return []) lookupFile1 :: FilePath -> Annex (Maybe (Key, Backend Annex)) lookupFile1 file = do@@ -199,7 +202,7 @@ Left _ -> return Nothing Right l -> makekey l where- getsymlink = return . takeFileName =<< readSymbolicLink file+ getsymlink = takeFileName <$> readSymbolicLink file makekey l = case maybeLookupBackendName bname of Nothing -> do unless (null kname || null bname ||
Upgrade/V2.hs view
@@ -20,6 +20,8 @@ import qualified Branch import Messages import Utility+import Utility.Conditional+import Utility.SafeCommand import LocationLog import Content
Utility.hs view
@@ -6,20 +6,8 @@ -} module Utility (- CommandParam(..),- toCommand, hGetContentsStrict, readFileStrict,- parentDir,- absPath,- absPathFrom,- relPathCwdToFile,- relPathDirToFile,- boolSystem,- boolSystemEnv,- executeFile,- shellEscape,- shellUnEscape, unsetFileMode, readMaybe, viaTmp,@@ -28,124 +16,23 @@ dirContents, myHomeDir, catchBool,- whenM,- (>>?),- unlessM,- (>>!),- - prop_idempotent_shellEscape,- prop_idempotent_shellEscape_multiword,- prop_parentDir_basics,- prop_relPathDirToFile_basics+ inPath,+ firstM,+ anyM ) where import IO (bracket) import System.IO-import System.Exit-import qualified System.Posix.Process import System.Posix.Process hiding (executeFile)-import System.Posix.Signals import System.Posix.Files import System.Posix.Types import System.Posix.User-import Data.String.Utils-import System.Path import System.FilePath import System.Directory import Foreign (complement)-import Data.List+import Utility.Path import Data.Maybe-import Control.Monad (liftM2, when, unless)-import System.Log.Logger--{- A type for parameters passed to a shell command. A command can- - be passed either some Params (multiple parameters can be included,- - whitespace-separated, or a single Param (for when parameters contain- - whitespace), or a File.- -}-data CommandParam = Params String | Param String | File FilePath- deriving (Eq, Show, Ord)--{- Used to pass a list of CommandParams to a function that runs- - a command and expects Strings. -}-toCommand :: [CommandParam] -> [String]-toCommand = (>>= unwrap)- where- unwrap (Param s) = [s]- unwrap (Params s) = filter (not . null) (split " " s)- -- Files that start with a dash are modified to avoid- -- the command interpreting them as options.- unwrap (File ('-':s)) = ["./-" ++ s]- unwrap (File s) = [s]--{- Run a system command, and returns True or False- - if it succeeded or failed.- -- - SIGINT(ctrl-c) is allowed to propigate and will terminate the program.- -}-boolSystem :: FilePath -> [CommandParam] -> IO Bool-boolSystem command params = boolSystemEnv command params Nothing--boolSystemEnv :: FilePath -> [CommandParam] -> Maybe [(String, String)] -> IO Bool-boolSystemEnv command params env = do- -- Going low-level because all the high-level system functions- -- block SIGINT etc. We need to block SIGCHLD, but allow- -- SIGINT to do its default program termination.- let sigset = addSignal sigCHLD emptySignalSet- oldint <- installHandler sigINT Default Nothing- oldset <- getSignalMask- blockSignals sigset- childpid <- forkProcess $ childaction oldint oldset- mps <- getProcessStatus True False childpid- restoresignals oldint oldset- case mps of- Just (Exited ExitSuccess) -> return True- _ -> return False- where- restoresignals oldint oldset = do- _ <- installHandler sigINT oldint Nothing- setSignalMask oldset- childaction oldint oldset = do- restoresignals oldint oldset- executeFile command True (toCommand params) env--{- executeFile with debug logging -}-executeFile :: FilePath -> Bool -> [String] -> Maybe [(String, String)] -> IO ()-executeFile c path p e = do- debugM "Utility.executeFile" $- "Running: " ++ c ++ " " ++ show p ++ " " ++ maybe "" show e- System.Posix.Process.executeFile c path p e--{- Escapes a filename or other parameter to be safely able to be exposed to- - the shell. -}-shellEscape :: String -> String-shellEscape f = "'" ++ escaped ++ "'"- where- -- replace ' with '"'"'- escaped = join "'\"'\"'" $ split "'" f--{- Unescapes a set of shellEscaped words or filenames. -}-shellUnEscape :: String -> [String]-shellUnEscape [] = []-shellUnEscape s = word : shellUnEscape rest- where- (word, rest) = findword "" s- findword w [] = (w, "")- findword w (c:cs)- | c == ' ' = (w, cs)- | c == '\'' = inquote c w cs- | c == '"' = inquote c w cs- | otherwise = findword (w++[c]) cs- inquote _ w [] = (w, "")- inquote q w (c:cs)- | c == q = findword w cs- | otherwise = inquote q (w++[c]) cs--{- For quickcheck. -}-prop_idempotent_shellEscape :: String -> Bool-prop_idempotent_shellEscape s = [s] == (shellUnEscape . shellEscape) s-prop_idempotent_shellEscape_multiword :: [String] -> Bool-prop_idempotent_shellEscape_multiword s = s == (shellUnEscape . unwords . map shellEscape) s+import Control.Monad (liftM) {- A version of hgetContents that is not lazy. Ensures file is - all read before it gets closed. -}@@ -156,82 +43,6 @@ readFileStrict :: FilePath -> IO String readFileStrict f = readFile f >>= \s -> length s `seq` return s -{- Returns the parent directory of a path. Parent of / is "" -}-parentDir :: FilePath -> FilePath-parentDir dir =- if not $ null dirs- then slash ++ join s (take (length dirs - 1) dirs)- else ""- where- dirs = filter (not . null) $ split s dir- slash = if isAbsolute dir then s else ""- s = [pathSeparator]--prop_parentDir_basics :: FilePath -> Bool-prop_parentDir_basics dir- | null dir = True- | dir == "/" = parentDir dir == ""- | otherwise = p /= dir- where- p = parentDir dir--{- Checks if the first FilePath is, or could be said to contain the second.- - For example, "foo/" contains "foo/bar". Also, "foo", "./foo", "foo/" etc- - are all equivilant.- -}-dirContains :: FilePath -> FilePath -> Bool-dirContains a b = a == b || a' == b' || (a'++"/") `isPrefixOf` b'- where- norm p = fromMaybe "" $ absNormPath p "."- a' = norm a- b' = norm b--{- Converts a filename into a normalized, absolute path. -}-absPath :: FilePath -> IO FilePath-absPath file = do- cwd <- getCurrentDirectory- return $ absPathFrom cwd file--{- Converts a filename into a normalized, absolute path- - from the specified cwd. -}-absPathFrom :: FilePath -> FilePath -> FilePath-absPathFrom cwd file = fromMaybe bad $ absNormPath cwd file- where- bad = error $ "unable to normalize " ++ file--{- Constructs a relative path from the CWD to a file.- -- - For example, assuming CWD is /tmp/foo/bar:- - relPathCwdToFile "/tmp/foo" == ".."- - relPathCwdToFile "/tmp/foo/bar" == "" - -}-relPathCwdToFile :: FilePath -> IO FilePath-relPathCwdToFile f = liftM2 relPathDirToFile getCurrentDirectory (absPath f)--{- Constructs a relative path from a directory to a file.- -- - Both must be absolute, and normalized (eg with absNormpath).- -}-relPathDirToFile :: FilePath -> FilePath -> FilePath-relPathDirToFile from to = path- where- s = [pathSeparator]- pfrom = split s from- pto = split s to- common = map fst $ filter same $ zip pfrom pto- same (c,d) = c == d- uncommon = drop numcommon pto- dotdots = replicate (length pfrom - numcommon) ".."- numcommon = length common- path = join s $ dotdots ++ uncommon--prop_relPathDirToFile_basics :: FilePath -> FilePath -> Bool-prop_relPathDirToFile_basics from to- | from == to = null r- | otherwise = not (null r)- where- r = relPathDirToFile from to - {- Removes a FileMode from a file. - For example, call with otherWriteMode to chmod o-w -} unsetFileMode :: FilePath -> FileMode -> IO ()@@ -257,7 +68,7 @@ {- Runs an action with a temp file, then removes the file. -} withTempFile :: String -> (FilePath -> Handle -> IO a) -> IO a-withTempFile template action = bracket create remove use+withTempFile template a = bracket create remove use where create = do tmpdir <- catch getTemporaryDirectory (const $ return ".")@@ -265,7 +76,7 @@ remove (name, handle) = do hClose handle catchBool (removeFile name >> return True)- use (name, handle) = action name handle+ use (name, handle) = a name handle {- Lists the contents of a directory. - Unlike getDirectoryContents, paths are not relative to the directory. -}@@ -289,19 +100,23 @@ catchBool :: IO Bool -> IO Bool catchBool = flip catch (const $ return False) -{- when with a monadic conditional -}-whenM :: Monad m => m Bool -> m () -> m ()-whenM c a = c >>= flip when a--unlessM :: Monad m => m Bool -> m () -> m ()-unlessM c a = c >>= flip unless a--(>>?) :: Monad m => m Bool -> m () -> m ()-(>>?) = whenM+{- Return the first value from a list, if any, satisfying the given+ - predicate -}+firstM :: (Monad m) => (a -> m Bool) -> [a] -> m (Maybe a)+firstM _ [] = return Nothing+firstM p (x:xs) = do+ q <- p x+ if q+ then return (Just x)+ else firstM p xs -(>>!) :: Monad m => m Bool -> m () -> m ()-(>>!) = unlessM+{- Returns true if any value in the list satisfies the preducate,+ - stopping once one is found. -}+anyM :: (Monad m) => (a -> m Bool) -> [a] -> m Bool+anyM p = liftM isJust . firstM p --- low fixity allows eg, foo bar >>! error $ "failed " ++ meep-infixr 0 >>?-infixr 0 >>!+{- Checks if a command is available in PATH. -}+inPath :: String -> IO Bool+inPath command = getSearchPath >>= anyM indir+ where+ indir d = doesFileExist $ d </> command
+ Utility/Conditional.hs view
@@ -0,0 +1,26 @@+{- monadic conditional operators+ -+ - Copyright 2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Utility.Conditional where++import Control.Monad (when, unless)++whenM :: Monad m => m Bool -> m () -> m ()+whenM c a = c >>= flip when a++unlessM :: Monad m => m Bool -> m () -> m ()+unlessM c a = c >>= flip unless a++(>>?) :: Monad m => m Bool -> m () -> m ()+(>>?) = whenM++(>>!) :: Monad m => m Bool -> m () -> m ()+(>>!) = unlessM++-- low fixity allows eg, foo bar >>! error $ "failed " ++ meep+infixr 0 >>?+infixr 0 >>!
Utility/CopyFile.hs view
@@ -9,8 +9,9 @@ import System.Directory (doesFileExist, removeFile) -import Utility-import qualified SysConfig+import Utility.Conditional+import Utility.SafeCommand+import qualified Build.SysConfig as SysConfig {- The cp command is used, because I hate reinventing the wheel, - and because this allows easy access to features like cp --reflink. -}
Utility/DataUnits.hs view
@@ -137,8 +137,7 @@ {- Parses strings like "10 kilobytes" or "0.5tb". -} readSize :: [Unit] -> String -> Maybe ByteSize readSize units input- | null parsednum = Nothing- | null parsedunit = Nothing+ | null parsednum || null parsedunit = Nothing | otherwise = Just $ round $ number * fromIntegral multiplier where (number, rest) = head parsednum
+ Utility/JSONStream.hs view
@@ -0,0 +1,44 @@+{- Streaming JSON output.+ -+ - Copyright 2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Utility.JSONStream (+ start,+ add,+ end+) where++import Text.JSON++{- Text.JSON does not support building up a larger JSON document piece by+ piece as a stream. To support streaming, a hack. The JSObject is converted+ to a string with its final "}" is left off, allowing it to be added to+ later. -}+start :: JSON a => [(String, a)] -> String+start l+ | last s == endchar = take (length s - 1) s+ | otherwise = bad s+ where+ s = encodeStrict $ toJSObject l++add :: JSON a => [(String, a)] -> String+add l+ | head s == startchar = ',' : drop 1 s+ | otherwise = bad s+ where+ s = start l++end :: String+end = [endchar, '\n']++startchar :: Char+startchar = '{'++endchar :: Char+endchar = '}'++bad :: String -> a+bad s = error $ "Text.JSON returned unexpected string: " ++ s
+ Utility/Path.hs view
@@ -0,0 +1,92 @@+{- path manipulation+ -+ - Copyright 2010-2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Utility.Path where++import Data.String.Utils+import System.Path+import System.FilePath+import System.Directory+import Data.List+import Data.Maybe+import Control.Applicative++{- Returns the parent directory of a path. Parent of / is "" -}+parentDir :: FilePath -> FilePath+parentDir dir =+ if not $ null dirs+ then slash ++ join s (take (length dirs - 1) dirs)+ else ""+ where+ dirs = filter (not . null) $ split s dir+ slash = if isAbsolute dir then s else ""+ s = [pathSeparator]++prop_parentDir_basics :: FilePath -> Bool+prop_parentDir_basics dir+ | null dir = True+ | dir == "/" = parentDir dir == ""+ | otherwise = p /= dir+ where+ p = parentDir dir++{- Checks if the first FilePath is, or could be said to contain the second.+ - For example, "foo/" contains "foo/bar". Also, "foo", "./foo", "foo/" etc+ - are all equivilant.+ -}+dirContains :: FilePath -> FilePath -> Bool+dirContains a b = a == b || a' == b' || (a'++"/") `isPrefixOf` b'+ where+ norm p = fromMaybe "" $ absNormPath p "."+ a' = norm a+ b' = norm b++{- Converts a filename into a normalized, absolute path. -}+absPath :: FilePath -> IO FilePath+absPath file = do+ cwd <- getCurrentDirectory+ return $ absPathFrom cwd file++{- Converts a filename into a normalized, absolute path+ - from the specified cwd. -}+absPathFrom :: FilePath -> FilePath -> FilePath+absPathFrom cwd file = fromMaybe bad $ absNormPath cwd file+ where+ bad = error $ "unable to normalize " ++ file++{- Constructs a relative path from the CWD to a file.+ -+ - For example, assuming CWD is /tmp/foo/bar:+ - relPathCwdToFile "/tmp/foo" == ".."+ - relPathCwdToFile "/tmp/foo/bar" == "" + -}+relPathCwdToFile :: FilePath -> IO FilePath+relPathCwdToFile f = relPathDirToFile <$> getCurrentDirectory <*> absPath f++{- Constructs a relative path from a directory to a file.+ -+ - Both must be absolute, and normalized (eg with absNormpath).+ -}+relPathDirToFile :: FilePath -> FilePath -> FilePath+relPathDirToFile from to = path+ where+ s = [pathSeparator]+ pfrom = split s from+ pto = split s to+ common = map fst $ filter same $ zip pfrom pto+ same (c,d) = c == d+ uncommon = drop numcommon pto+ dotdots = replicate (length pfrom - numcommon) ".."+ numcommon = length common+ path = join s $ dotdots ++ uncommon++prop_relPathDirToFile_basics :: FilePath -> FilePath -> Bool+prop_relPathDirToFile_basics from to+ | from == to = null r+ | otherwise = not (null r)+ where+ r = relPathDirToFile from to
Utility/RsyncFile.hs view
@@ -9,7 +9,7 @@ import Data.String.Utils -import Utility+import Utility.SafeCommand {- Generates parameters to make rsync use a specified command as its remote - shell. -}
+ Utility/SafeCommand.hs view
@@ -0,0 +1,104 @@+{- safely running shell commands+ -+ - Copyright 2010-2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Utility.SafeCommand where++import System.Exit+import qualified System.Posix.Process+import System.Posix.Process hiding (executeFile)+import System.Posix.Signals+import Data.String.Utils+import System.Log.Logger++{- A type for parameters passed to a shell command. A command can+ - be passed either some Params (multiple parameters can be included,+ - whitespace-separated, or a single Param (for when parameters contain+ - whitespace), or a File.+ -}+data CommandParam = Params String | Param String | File FilePath+ deriving (Eq, Show, Ord)++{- Used to pass a list of CommandParams to a function that runs+ - a command and expects Strings. -}+toCommand :: [CommandParam] -> [String]+toCommand = (>>= unwrap)+ where+ unwrap (Param s) = [s]+ unwrap (Params s) = filter (not . null) (split " " s)+ -- Files that start with a dash are modified to avoid+ -- the command interpreting them as options.+ unwrap (File s@('-':_)) = ["./" ++ s]+ unwrap (File s) = [s]++{- Run a system command, and returns True or False+ - if it succeeded or failed.+ -+ - SIGINT(ctrl-c) is allowed to propigate and will terminate the program.+ -}+boolSystem :: FilePath -> [CommandParam] -> IO Bool+boolSystem command params = boolSystemEnv command params Nothing++boolSystemEnv :: FilePath -> [CommandParam] -> Maybe [(String, String)] -> IO Bool+boolSystemEnv command params env = do+ -- Going low-level because all the high-level system functions+ -- block SIGINT etc. We need to block SIGCHLD, but allow+ -- SIGINT to do its default program termination.+ let sigset = addSignal sigCHLD emptySignalSet+ oldint <- installHandler sigINT Default Nothing+ oldset <- getSignalMask+ blockSignals sigset+ childpid <- forkProcess $ childaction oldint oldset+ mps <- getProcessStatus True False childpid+ restoresignals oldint oldset+ case mps of+ Just (Exited ExitSuccess) -> return True+ _ -> return False+ where+ restoresignals oldint oldset = do+ _ <- installHandler sigINT oldint Nothing+ setSignalMask oldset+ childaction oldint oldset = do+ restoresignals oldint oldset+ executeFile command True (toCommand params) env++{- executeFile with debug logging -}+executeFile :: FilePath -> Bool -> [String] -> Maybe [(String, String)] -> IO ()+executeFile c path p e = do+ debugM "Utility.SafeCommand.executeFile" $+ "Running: " ++ c ++ " " ++ show p ++ " " ++ maybe "" show e+ System.Posix.Process.executeFile c path p e++{- Escapes a filename or other parameter to be safely able to be exposed to+ - the shell. -}+shellEscape :: String -> String+shellEscape f = "'" ++ escaped ++ "'"+ where+ -- replace ' with '"'"'+ escaped = join "'\"'\"'" $ split "'" f++{- Unescapes a set of shellEscaped words or filenames. -}+shellUnEscape :: String -> [String]+shellUnEscape [] = []+shellUnEscape s = word : shellUnEscape rest+ where+ (word, rest) = findword "" s+ findword w [] = (w, "")+ findword w (c:cs)+ | c == ' ' = (w, cs)+ | c == '\'' = inquote c w cs+ | c == '"' = inquote c w cs+ | otherwise = findword (w++[c]) cs+ inquote _ w [] = (w, "")+ inquote q w (c:cs)+ | c == q = findword w cs+ | otherwise = inquote q (w++[c]) cs++{- For quickcheck. -}+prop_idempotent_shellEscape :: String -> Bool+prop_idempotent_shellEscape s = [s] == (shellUnEscape . shellEscape) s+prop_idempotent_shellEscape_multiword :: [String] -> Bool+prop_idempotent_shellEscape_multiword s = s == (shellUnEscape . unwords . map shellEscape) s
+ Utility/Ssh.hs view
@@ -0,0 +1,61 @@+{- git-annex remote access with ssh+ -+ - Copyright 2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Utility.Ssh where++import Control.Monad.State (liftIO)++import qualified Git+import Utility.SafeCommand+import Types+import Config++{- Generates parameters to ssh to a repository's host and run a command.+ - Caller is responsible for doing any neccessary shellEscaping of the+ - passed command. -}+sshToRepo :: Git.Repo -> [CommandParam] -> Annex [CommandParam]+sshToRepo repo sshcmd = do+ s <- getConfig repo "ssh-options" ""+ let sshoptions = map Param (words s)+ let sshport = case Git.urlPort repo of+ Nothing -> []+ Just p -> [Param "-p", Param (show p)]+ let sshhost = Param $ Git.urlHostUser repo+ return $ sshoptions ++ sshport ++ [sshhost] ++ sshcmd++{- Generates parameters to run a git-annex-shell command on a remote+ - repository. -}+git_annex_shell :: Git.Repo -> String -> [CommandParam] -> Annex (Maybe (FilePath, [CommandParam]))+git_annex_shell r command params+ | not $ Git.repoIsUrl r = return $ Just (shellcmd, shellopts)+ | Git.repoIsSsh r = do+ sshparams <- sshToRepo r [Param sshcmd]+ return $ Just ("ssh", sshparams)+ | otherwise = return Nothing+ where+ dir = Git.workTree r+ shellcmd = "git-annex-shell"+ shellopts = Param command : File dir : params+ sshcmd = shellcmd ++ " " ++ + unwords (map shellEscape $ toCommand shellopts)++{- Uses a supplied function (such as boolSystem) to run a git-annex-shell+ - command on a remote.+ -+ - Or, if the remote does not support running remote commands, returns+ - a specified error value. -}+onRemote + :: Git.Repo+ -> (FilePath -> [CommandParam] -> IO a, a)+ -> String+ -> [CommandParam]+ -> Annex a+onRemote r (with, errorval) command params = do+ s <- git_annex_shell r command params+ case s of+ Just (c, ps) -> liftIO $ with c ps+ Nothing -> return errorval
+ Utility/StatFS.hsc view
@@ -0,0 +1,125 @@+-----------------------------------------------------------------------------+-- |+--+-- (This code originally comes from xmobar)+-- +-- Module : StatFS+-- Copyright : (c) Jose A Ortega Ruiz+-- License : BSD-3-clause+--+-- All rights reserved.+-- +-- Redistribution and use in source and binary forms, with or without+-- modification, are permitted provided that the following conditions+-- are met:+-- +-- 1. Redistributions of source code must retain the above copyright+-- notice, this list of conditions and the following disclaimer.+-- 2. Redistributions in binary form must reproduce the above copyright+-- notice, this list of conditions and the following disclaimer in the+-- documentation and/or other materials provided with the distribution.+-- 3. Neither the name of the author nor the names of his contributors+-- may be used to endorse or promote products derived from this software+-- without specific prior written permission.+-- +-- THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND+-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE+-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+-- SUCH DAMAGE.+--+-- Maintainer : Jose A Ortega Ruiz <jao@gnu.org>+-- Stability : unstable+-- Portability : unportable+--+-- A binding to C's statvfs(2)+--+-----------------------------------------------------------------------------++{-# LANGUAGE CPP, ForeignFunctionInterface, EmptyDataDecls #-}+++module Utility.StatFS ( FileSystemStats(..), getFileSystemStats ) where++import Foreign+import Foreign.C.Types+import Foreign.C.String+import Data.ByteString (useAsCString)+import Data.ByteString.Char8 (pack)++#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) || defined (__APPLE__)+# include <sys/param.h>+# include <sys/mount.h>+#else+#if defined (__linux__)+#include <sys/vfs.h>+#else+#define UNKNOWN+#endif+#endif++data FileSystemStats = FileSystemStats {+ fsStatBlockSize :: Integer+ -- ^ Optimal transfer block size.+ , fsStatBlockCount :: Integer+ -- ^ Total data blocks in file system.+ , fsStatByteCount :: Integer+ -- ^ Total bytes in file system.+ , fsStatBytesFree :: Integer+ -- ^ Free bytes in file system.+ , fsStatBytesAvailable :: Integer+ -- ^ Free bytes available to non-superusers.+ , fsStatBytesUsed :: Integer+ -- ^ Bytes used.+ } deriving (Show, Eq)++data CStatfs++#ifdef UNKNOWN+#warning free space checking code not available for this OS+#else+#if defined(__APPLE__)+foreign import ccall unsafe "sys/mount.h statfs64"+#else+#if defined(__FreeBSD__) || defined (__FreeBSD_kernel__)+foreign import ccall unsafe "sys/mount.h statfs"+#else+foreign import ccall unsafe "sys/vfs.h statfs64"+#endif+#endif+ c_statfs :: CString -> Ptr CStatfs -> IO CInt+#endif++toI :: CULong -> Integer+toI = toInteger++getFileSystemStats :: String -> IO (Maybe FileSystemStats)+getFileSystemStats path =+#ifdef UNKNOWN+ return Nothing+#else+ allocaBytes (#size struct statfs) $ \vfs ->+ useAsCString (pack path) $ \cpath -> do+ res <- c_statfs cpath vfs+ if res == -1 then return Nothing+ else do+ bsize <- (#peek struct statfs, f_bsize) vfs+ bcount <- (#peek struct statfs, f_blocks) vfs+ bfree <- (#peek struct statfs, f_bfree) vfs+ bavail <- (#peek struct statfs, f_bavail) vfs+ let bpb = toI bsize+ return $ Just FileSystemStats+ { fsStatBlockSize = bpb+ , fsStatBlockCount = toI bcount+ , fsStatByteCount = toI bcount * bpb+ , fsStatBytesFree = toI bfree * bpb+ , fsStatBytesAvailable = toI bavail * bpb+ , fsStatBytesUsed = toI (bcount - bfree) * bpb+ }+#endif
+ Utility/Touch.hsc view
@@ -0,0 +1,119 @@+{- More control over touching a file.+ -+ - Copyright 2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++{-# LANGUAGE ForeignFunctionInterface #-}++module Utility.Touch (+ TimeSpec(..),+ touchBoth,+ touch+) where++import Foreign+import Foreign.C+import Control.Monad (when)++newtype TimeSpec = TimeSpec CTime++{- Changes the access and modification times of an existing file.+ Can follow symlinks, or not. Throws IO error on failure. -}+touchBoth :: FilePath -> TimeSpec -> TimeSpec -> Bool -> IO ()++touch :: FilePath -> TimeSpec -> Bool -> IO ()+touch file mtime follow = touchBoth file mtime mtime follow++#include <sys/types.h>+#include <sys/stat.h>+#include <fcntl.h>+#include <sys/time.h>++#ifndef _BSD_SOURCE+#define _BSD_SOURCE+#endif++#if (defined UTIME_OMIT && defined UTIME_NOW && defined AT_FDCWD && defined AT_SYMLINK_NOFOLLOW)++at_fdcwd :: CInt+at_fdcwd = #const AT_FDCWD++at_symlink_nofollow :: CInt+at_symlink_nofollow = #const AT_SYMLINK_NOFOLLOW++instance Storable TimeSpec where+ -- use the larger alignment of the two types in the struct+ alignment _ = max sec_alignment nsec_alignment+ where+ sec_alignment = alignment (undefined::CTime)+ nsec_alignment = alignment (undefined::CLong)+ sizeOf _ = #{size struct timespec}+ peek ptr = do+ sec <- #{peek struct timespec, tv_sec} ptr+ return $ TimeSpec sec+ poke ptr (TimeSpec sec) = do+ #{poke struct timespec, tv_sec} ptr sec+ #{poke struct timespec, tv_nsec} ptr (0 :: CLong)++{- While its interface is beastly, utimensat is in recent+ POSIX standards, unlike lutimes. -}+foreign import ccall "utimensat" + c_utimensat :: CInt -> CString -> Ptr TimeSpec -> CInt -> IO CInt++touchBoth file atime mtime follow = + allocaArray 2 $ \ptr ->+ withCString file $ \f -> do+ pokeArray ptr [atime, mtime]+ r <- c_utimensat at_fdcwd f ptr flags+ when (r /= 0) $ throwErrno "touchBoth"+ where+ flags = if follow+ then 0+ else at_symlink_nofollow ++#else+#if 0+{- Using lutimes is needed for BSD.+ - + - TODO: test if lutimes is available. May have to do it in configure.+ - TODO: TimeSpec uses a CTime, while tv_sec is a CLong. It is implementation+ - dependent whether these are the same; need to find a cast that works.+ - (Without the cast it works on linux i386, but+ - maybe not elsewhere.)+ -}++instance Storable TimeSpec where+ alignment _ = alignment (undefined::CLong)+ sizeOf _ = #{size struct timeval}+ peek ptr = do+ sec <- #{peek struct timeval, tv_sec} ptr+ return $ TimeSpec sec+ poke ptr (TimeSpec sec) = do+ #{poke struct timeval, tv_sec} ptr sec+ #{poke struct timeval, tv_usec} ptr (0 :: CLong) ++foreign import ccall "utimes" + c_utimes :: CString -> Ptr TimeSpec -> IO CInt+foreign import ccall "lutimes" + c_lutimes :: CString -> Ptr TimeSpec -> IO CInt++touchBoth file atime mtime follow = + allocaArray 2 $ \ptr ->+ withCString file $ \f -> do+ pokeArray ptr [atime, mtime]+ r <- syscall f ptr+ if (r /= 0)+ then throwErrno "touchBoth"+ else return ()+ where+ syscall = if follow+ then c_lutimes+ else c_utimes++#else+#warning "utimensat and lutimes not available; building without symlink timestamp preservation support"+touchBoth _ _ _ _ = return ()+#endif+#endif
+ Utility/Url.hs view
@@ -0,0 +1,79 @@+{- Url downloading.+ -+ - Copyright 2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Utility.Url (+ exists,+ download,+ get+) where++import Control.Applicative+import Control.Monad.State (liftIO)+import qualified Network.Browser as Browser+import Network.HTTP+import Network.URI++import Types+import Messages+import Utility.SafeCommand+import Utility++type URLString = String++{- Checks that an url exists and could be successfully downloaded. -}+exists :: URLString -> IO Bool+exists url =+ case parseURI url of+ Nothing -> return False+ Just u -> do+ r <- request u HEAD+ case rspCode r of+ (2,_,_) -> return True+ _ -> return False++{- Used to download large files, such as the contents of keys.+ - Uses wget or curl program for its progress bar. (Wget has a better one,+ - so is preferred.) -}+download :: URLString -> FilePath -> Annex Bool+download url file = do+ showOutput -- make way for program's progress bar+ e <- liftIO $ inPath "wget"+ if e+ then+ liftIO $ boolSystem "wget"+ [Params "-c -O", File file, File url]+ else+ -- Uses the -# progress display, because the normal+ -- one is very confusing when resuming, showing+ -- the remainder to download as the whole file,+ -- and not indicating how much percent was+ -- downloaded before the resume.+ liftIO $ boolSystem "curl" + [Params "-L -C - -# -o", File file, File url]++{- Downloads a small file. -}+get :: URLString -> IO String+get url =+ case parseURI url of+ Nothing -> error "url parse error"+ Just u -> do+ r <- request u GET+ case rspCode r of+ (2,_,_) -> return $ rspBody r+ _ -> error $ rspReason r++{- Makes a http request of an url. For example, HEAD can be used to+ - check if the url exists, or GET used to get the url content (best for+ - small urls). -}+request :: URI -> RequestMethod -> IO (Response String)+request url requesttype = Browser.browse $ do+ Browser.setErrHandler ignore+ Browser.setOutHandler ignore+ Browser.setAllowRedirects True+ snd <$> Browser.request (mkRequest requesttype url :: Request_String)+ where+ ignore = const $ return ()
configure.hs view
@@ -3,7 +3,7 @@ import System.Directory import Data.List -import TestConfig+import Build.TestConfig tests :: [TestCase] tests =
debian/changelog view
@@ -1,3 +1,15 @@+git-annex (3.20110902) unstable; urgency=low++ * Set EMAIL when running test suite so that git does not need to be+ configured first. Closes: #638998+ * The wget command will now be used in preference to curl, if available.+ * init: Make description an optional parameter.+ * unused, status: Sped up by avoiding unnecessary stats of annexed files.+ * unused --remote: Reduced memory use to 1/4th what was used before.+ * Add --json switch, to produce machine-consumable output.++ -- Joey Hess <joeyh@debian.org> Fri, 02 Sep 2011 21:20:37 -0400+ git-annex (3.20110819) unstable; urgency=low * Now "git annex init" only has to be run once, when a git repository
debian/control view
@@ -14,6 +14,7 @@ libghc-hs3-dev (>= 0.5.6), libghc-testpack-dev [any-i386 any-amd64], libghc-monad-control-dev,+ libghc-json-dev, ikiwiki, perlmagick, git | git-core,@@ -31,7 +32,7 @@ git | git-core, uuid, rsync,- curl,+ wget | curl, openssh-client Suggests: graphviz, bup, gnupg Description: manage files with git, without checking their contents into git
debian/copyright view
@@ -8,7 +8,7 @@ this package's source, or in /usr/share/common-licenses/GPL-3 on Debian systems. -Files: StatFS.hsc+Files: Utility/StatFS.hsc Copyright: Jose A Ortega Ruiz <jao@gnu.org> License: BSD-3-clause -- All rights reserved.
doc/backends.mdwn view
@@ -1,24 +1,16 @@-git-annex uses a key-value abstraction layer to allow file contents to be-stored in different ways. In theory, any key-value storage system could be-used to store file contents.- When a file is annexed, a key is generated from its content and/or metadata. The file checked into git symlinks to the key. This key can later be used to retrieve the file's content (its value). -Multiple pluggable backends are supported, and a single repository-can use different backends for different files.--These backends can transfer file contents between configured git remotes.-It's also possible to use [[special_remotes]], such as Amazon S3 with-these backends.+Multiple pluggable key-value backends are supported, and a single repository+can use different ones for different files. -* `WORM` ("Write Once, Read Many") This backend assumes that any file with- the same basename, size, and modification time has the same content. So with- this backend, files can be moved around, but should never be added to- or changed. This is the default, and the least expensive backend.-* `SHA1` -- This backend uses a key based on a sha1 checksum. This backend- allows modifications of files to be tracked. Its need to generate checksums+* `WORM` ("Write Once, Read Many") This assumes that any file with+ the same basename, size, and modification time has the same content.+ This is the default, and the least expensive backend.+* `SHA1` -- This uses a key based on a sha1 checksum. This allows+ verifying that the file content is right, and can avoid duplicates of+ files with the same content. Its need to generate checksums can make it slower for large files. * `SHA512`, `SHA384`, `SHA256`, `SHA224` -- Like SHA1, but larger checksums. Mostly useful for the very paranoid, or anyone who is@@ -36,7 +28,7 @@ attribute can be set to the name of the backend to use for matching files. For example, to use the SHA1 backend for sound files, which tend to be-smallish and might be modified over time, you could set in+smallish and might be modified or copied over time, you could set in `.gitattributes`: *.mp3 annex.backend=SHA1
+ doc/bugs/add_script-friendly_output_options.mdwn view
@@ -0,0 +1,19 @@+I have a need to use git-annex from a larger program. It'd be great if the information output by some of the commands that is descriptive (for example, whereis) could be sent to stdout in a machine-readable format like (preferably) JSON, or XML. That way I can simply read in the output of the command and use the data directly instead of having to parse it via regexes or other such string manipulation.++This could perhaps be triggered by a --json or --xml flag to the relevant commands.++> This is [[done]], --json is supported by all commands, more or less.+> +> Caveats: +> +> * the version, status, and find commands produce custom output and so+> no json. This could change for version and status; find needs to just+> be a simple list of files, I think +> * The "note" fields may repeat multiple times per object with different+> notes and are of course not machine readable, and subject to change.+> * Output of helper commands like rsync is not diverted away, and+> could clutter up the json output badly. Should only affect commands+> that transfer data. And AFAICS, wget and rsync both output their+> progress displays to stderr, so shouldn't be a problem.+> +> --[[Joey]]
+ doc/bugs/test_suite_shouldn__39__t_fail_silently.mdwn view
@@ -0,0 +1,3 @@+When the test suite cannot be compiled, the build just fails silenty. This means that in automated builds there is no easy way to ensure that the generated binaries have passed the test suite, because it may not even have been run! IMHO, "make test" should fail (i.e. return a non-zero exit code) when it can't succeeed.++> Ok, fixed. --[[Joey]] [[done]]
doc/copies.mdwn view
@@ -1,8 +1,8 @@-The WORM and SHA1 key-value [[backends]] store data inside-your git repository's `.git` directory, not in some external data store.+Annexed data is stored inside your git repository's `.git/annex` directory.+Some [[special_remotes]] can store annexed data elsewhere. It's important that data not get lost by an ill-considered `git annex drop`-command. So, then using those backends, git-annex can be configured to try+command. So, git-annex can be configured to try to keep N copies of a file's content available across all repositories. (Although [[untrusted_repositories|trust]] don't count toward this total.)
+ doc/forum/advantages_of_SHA__42___over_WORM.mdwn view
@@ -0,0 +1,5 @@+Thanks for creating git-annex.++I am confused about the advantages of the SHA* backends over WORM. The "backends" page in this wiki says that with WORM, files "can be moved around, but should never be added to or changed". But I don't see any difference to SHA* files as long as the premise of WORM that "any file with the same basename, size, and modification time has the same content" is true. Using "git annex unlock", WORM files can be modified in the same way as SHA* files.++If the storage I use is dependable (i.e. I don't need SHA checksums for detection of corruption), and I don't need to optimize for the case that the modification date of a file is changed but the contents stay the same, and if it is unlikely that several files will be identical, is there actually any advantage in using SHA*?
+ doc/forum/advantages_of_SHA__42___over_WORM/comment_1_96c354cac4b5ce5cf6664943bc84db1d._comment view
@@ -0,0 +1,8 @@+[[!comment format=mdwn+ username="http://joey.kitenet.net/"+ nickname="joey"+ subject="comment 1"+ date="2011-08-29T16:10:38Z"+ content="""+You're right -- as long as nothing changes a file without letting the modification time update, editing WORM files is safe.+"""]]
doc/git-annex.mdwn view
@@ -72,15 +72,15 @@ * get [path ...] - Makes the content of annexed files available in this repository. Depending- on the backend used, this will involve copying them from another repository,- or downloading them, or transferring them from some kind of key-value store.+ Makes the content of annexed files available in this repository. This+ will involve copying them from another repository, or downloading them,+ or transferring them from some kind of key-value store. * drop [path ...] Drops the content of annexed files from this repository. - git-annex may refuse to drop content if the backend does not think+ git-annex may refuse to drop content if it does not think it is safe to do so, typically because of the setting of annex.numcopies. * move [path ...]@@ -119,9 +119,7 @@ Use this to undo an unlock command if you don't want to modify the files, or have made modifications you want to discard. -* init description-- Initializes git-annex with a description of the git repository.+* init [description] Until a repository (or one of its remotes) has been initialized, git-annex will refuse to operate on it, to avoid accidentially@@ -207,14 +205,14 @@ * migrate [path ...] - Changes the specified annexed files to store their content in the- default backend (or the one specified with --backend). Only files whose- content is currently available are migrated.+ Changes the specified annexed files to use the default key-value backend+ (or the one specified with --backend). Only files whose content+ is currently available are migrated. - Note that the content is not removed from the backend it was previously in.- Use `git annex unused` to find and remove such content.+ Note that the content is also still available using the old key after+ migration. Use `git annex unused` to find and remove the old key. - Normally, nothing will be done to files already in the backend.+ Normally, nothing will be done to files already using the new backend. However, if a backend changes the information it uses to construct a key, this can also be used to migrate files to use the new key format. @@ -293,7 +291,7 @@ * fromkey file This plumbing-level command can be used to manually set up a file- to link to a specified key in the key-value backend.+ in the git repository to link to a specified key. * dropkey [key ...] @@ -339,13 +337,19 @@ * --quiet - Avoid the default verbose logging of what is done; only show errors+ Avoid the default verbose display of what is done; only show errors and progress displays. * --verbose - Enable verbose logging.+ Enable verbose display. +* --json++ Rather than the normal output, generate JSON. This is intended to be+ parsed by programs that use git-annex. Each line of output is a JSON+ object.+ * --debug Show debug messages.@@ -500,8 +504,8 @@ # CONFIGURATION VIA .gitattributes -The backend used when adding a new file to the annex can be configured-on a per-file-type basis via `.gitattributes` files. In the file,+The key-value backend used when adding a new file to the annex can be+configured on a per-file-type basis via `.gitattributes` files. In the file, the `annex.backend` attribute can be set to the name of the backend to use. For example, this here's how to use the WORM backend by default, but the SHA1 backend for ogg files:
doc/install.mdwn view
@@ -28,13 +28,14 @@ * [QuickCheck 2](http://hackage.haskell.org/package/QuickCheck) * [HTTP](http://hackage.haskell.org/package/HTTP) * [hS3](http://hackage.haskell.org/package/hS3) (optional, but recommended)+ * [json](http://hackage.haskell.org/package/json) * Shell commands * [git](http://git-scm.com/) * [uuid](http://www.ossp.org/pkg/lib/uuid/) (or `uuidgen` from util-linux) * [xargs](http://savannah.gnu.org/projects/findutils/) * [rsync](http://rsync.samba.org/)- * [curl](http://http://curl.haxx.se/) (optional, but recommended)+ * [wget](http://www.gnu.org/software/wget/) or [curl](http://http://curl.haxx.se/) (optional, but recommended) * [sha1sum](ftp://ftp.gnu.org/gnu/coreutils/) (optional, but recommended; a sha1 command will also do) * [gpg](http://gnupg.org/) (optional; needed for encryption)
+ doc/install/OSX/comment_1_0a1760bf0db1f1ba89bdb4c62032f631._comment view
@@ -0,0 +1,13 @@+[[!comment format=mdwn+ username="http://www.schleptet.net/~cfm/"+ ip="64.30.148.100"+ subject="comment 1"+ date="2011-08-30T14:31:36Z"+ content="""+You can also use Homebrew instead of MacPorts. Homebrew's `haskell-platform` is up-to-date, too:++ brew install haskell-platform git ossp-uuid md5sha1sum coreutils pcre+ ln -s /usr/local/include/pcre.h /usr/include/pcre.h++As of this writing, however, Homebrew's `md5sha1sum` has a broken mirror. I wound up getting that from MacPorts anyway.+"""]]
− doc/news/version_3.20110705.mdwn
@@ -1,9 +0,0 @@-git-annex 3.20110705 released with [[!toggle text="these changes"]]-[[!toggleable text="""- * uninit: Delete the git-annex branch and .git/annex/- * unannex: In --fast mode, file content is left in the annex, and a- hard link made to it.- * uninit: Use unannex in --fast mode, to support unannexing multiple- files that link to the same content.- * Drop the dependency on the haskell curl bindings, use regular haskell HTTP.- * Fix a pipeline stall when upgrading (caused by #624389)."""]]
+ doc/news/version_3.20110902.mdwn view
@@ -0,0 +1,9 @@+git-annex 3.20110902 released with [[!toggle text="these changes"]]+[[!toggleable text="""+ * Set EMAIL when running test suite so that git does not need to be+ configured first. Closes: #[638998](http://bugs.debian.org/638998)+ * The wget command will now be used in preference to curl, if available.+ * init: Make description an optional parameter.+ * unused, status: Sped up by avoiding unnecessary stats of annexed files.+ * unused --remote: Reduced memory use to 1/4th what was used before.+ * Add --json switch, to produce machine-consumable output."""]]
doc/todo/git-annex_unused_eats_memory.mdwn view
@@ -17,9 +17,3 @@ and that would yield a shortlist of keys that are probably not used. Then scan thru all files in the repo to make sure that none point to keys on the shortlist.--------`git annex unused --from remote` is much worse, using hundreds of mb of-memory. It has not been profiled at all yet, and can probably be improved-somewhat by fixing whatever memory leak it (probably) has.
doc/todo/git_annex_init_:_include_repo_description_and__47__or_UUID_in_commit_message.mdwn view
@@ -9,3 +9,5 @@ makes sense. If you want a list of repository UUIDs and descriptions, it's there in machine-usable form in `.git-annex/uuid.log`, there is no need to try to pull this info out of git commit messages. --[[Joey]]++[[done]]
doc/todo/smudge.mdwn view
@@ -1,5 +1,18 @@ git-annex should use smudge/clean filters. +----++Update: Currently, this does not look likely to work. In particular,+the clean filter needs to consume all stdin from git, which consists of the+entire content of the file. It cannot optimise by directly accessing+the file in the repository, because git may be cleaning a different+version of the file during a merge. ++So every `git status` would need to read the entire content of all+available files, and checksum them, which is too expensive.++----+ The clean filter is run when files are staged for commit. So a user could copy any file into the annex, git add it, and git-annex's clean filter causes the file's key to be staged, while its value is added to the annex.@@ -8,7 +21,7 @@ repos have partial content, this would not git annex get the file content. Instead, if the content is not currently available, it would need to do something like return empty file content. (Sadly, it cannot create a-symlink, as git still wants to write the file afterwards.+symlink, as git still wants to write the file afterwards.) So the nice current behavior of unavailable files being clearly missing due to dangling symlinks, would be lost when using smudge/clean filters.@@ -19,24 +32,69 @@ and have it use git-annex when .gitattributes says to. Also, annexed files can be directly modified without having to `git annex unlock`. -### efficiency+### design +In .gitattributes, the user would put something like "* filter=git-annex".+This way they could control which files are annexed vs added normally.++(git-annex could have further controls to allow eg, passing small files+through to regular processing. At least .gitattributes is a special case,+it should never be annexed...)++For files not configured this way, git-annex could continue to use+its symlink method -- this would preserve backwards compatability,+and even allow mixing the two methods in a repo as desired.++To find files in the repository that are annexed, git-annex would do+`ls-files` as now, but would check if found files have the appropriate+filter, rather than the current symlink checks. To determine the key+of a file, rather than reading its symlink, git-annex would need to+look up the git blob associated with the file -- this can be done+efficiently using the existing code in `Branch.catFile`.++The clean filter would inject the file's content into the annex, and hard+link from the annex to the file. Avoiding duplication of data.++The smudge filter can't do that, so to avoid duplication of data, it+might always create an empty file. To get the content, `git annex get`+could be used (which would hard link it). A `post-checkout` hook might+be used to set up hard links for all currently available content.++#### clean+ The trick is doing it efficiently. Since git a2b665d, v1.7.4.1, something like this works to provide a filename to the clean script: git config --global filter.huge.clean huge-clean %f -This avoids it needing to read all the current file content from stdin+This could avoid it needing to read all the current file content from stdin when doing eg, a git status or git commit. Instead it is passed the filename that git is operating on, in the working directory.+(Update: No, doesn't work; git may be cleaning a different file content+than is currently on disk, and git requires all stdin be consumed too.) So, WORM could just look at that file and easily tell if it is one it already knows (same mtime and size). If so, it can short-circuit and do nothing, file content is already cached. SHA1 has a harder job. Would not want to re-sha1 the file every time,-probably. So it'd need a cache of file stat info, mapped to known objects.+probably. So it'd need a local cache of file stat info, mapped to known+objects. +But: Even with %f, git actually passes the full file content to the clean+filter, and if it fails to consume it all, it will crash (may only happen+if the file is larger than some chunk size; tried with 500 mb file and +saw a SIGPIPE.) This means unnecessary works needs to be done, +and it slows down *everything*, from `git status` to `git commit`.+**showstopper** I have sent a patch to the git mailing list to address+this. <http://marc.info/?l=git&m=131465033512157&w=2> (Update: apparently+can't be fixed.)++#### smudge++The smudge script can also be provided a filename with %f, but it+cannot directly write to the file or git gets unhappy.+ ### dealing with partial content availability The smudge filter cannot be allowed to fail, that leaves the tree and@@ -58,12 +116,13 @@ <pre> #!/bin/sh-read sha1-echo "smudging $sha1" >&2-if [ -e ~/$sha1 ]; then- cat ~/$sha1+read f+file="$1"+echo "smudging $f" >&2+if [ -e ~/$f ]; then+ cat ~/$f # possibly expensive copy here else- echo "$sha1 not available"+ echo "$f not available" fi </pre> @@ -71,17 +130,17 @@ <pre> #!/bin/sh-cat >temp-if grep -q 'not available' temp; then- awk '{print $1}' temp # provide what we would if the content were avail!- rm temp+file="$1"+cat >/tmp/file+# in real life, this should be done more efficiently, not trying to read+# the whole file content!+if grep -q 'not available' /tmp/file; then+ awk '{print $1}' /tmp/file # provide what we would if the content were avail! exit 0 fi-sha1=`sha1sum temp | cut -d' ' -f1`-echo "cleaning $sha1" >&2-ls -l temp >&2-mv temp ~/$sha1-echo $sha1+echo "cleaning $file" >&2+# XXX store file content here+echo $file </pre> .gitattributes:@@ -94,6 +153,6 @@ <pre> [filter "huge"]- clean = huge-clean- smudge = huge-smudge+ clean = huge-clean %f+ smudge = huge-smudge %f <pre>
doc/walkthrough/fsck:_verifying_your_data.mdwn view
@@ -1,8 +1,8 @@-You can use the fsck subcommand to check for problems in your data.-What can be checked depends on the [[backend|backends]] you've used to store-the data. For example, when you use the SHA1 backend, fsck will verify that-the checksums of your files are good. Fsck also checks that the annex.numcopies-setting is satisfied for all files.+You can use the fsck subcommand to check for problems in your data. What+can be checked depends on the key-value [[backend|backends]] you've used+for the data. For example, when you use the SHA1 backend, fsck will verify+that the checksums of your files are good. Fsck also checks that the+annex.numcopies setting is satisfied for all files. # git annex fsck fsck some_file (checksum...) ok
doc/walkthrough/unused_data.mdwn view
@@ -2,7 +2,7 @@ anymore. One way it can happen is if you `git rm` a file without first calling `git annex drop`. And, when you modify an annexed file, the old content of the file remains in the annex. Another way is when migrating-between backends.+between key-value [[backends|backend]]. This might be historical data you want to preserve, so git-annex defaults to preserving it. So from time to time, you may want to check for such data and
git-annex-shell.hs view
@@ -11,7 +11,8 @@ import qualified Git import CmdLine import Command-import Utility+import Utility.Conditional+import Utility.SafeCommand import Options import qualified Command.ConfigList
git-annex.cabal view
@@ -1,5 +1,5 @@ Name: git-annex-Version: 3.20110819+Version: 3.20110902 Cabal-Version: >= 1.6 License: GPL Maintainer: Joey Hess <joey@kitenet.net>@@ -7,7 +7,7 @@ Stability: Stable Copyright: 2010-2011 Joey Hess License-File: GPL-Extra-Source-Files: ./debian/NEWS ./debian/control ./debian/rules ./debian/manpages ./debian/changelog ./debian/doc-base ./debian/copyright ./debian/compat ./AnnexQueue.hs ./configure.hs ./Annex.hs ./PresenceLog.hs ./Version.hs ./Crypto.hs ./RemoteLog.hs ./README ./Command/Merge.hs ./Command/AddUrl.hs ./Command/Whereis.hs ./Command/Unlock.hs ./Command/Version.hs ./Command/FromKey.hs ./Command/PreCommit.hs ./Command/Map.hs ./Command/Unused.hs ./Command/Uninit.hs ./Command/Migrate.hs ./Command/Init.hs ./Command/ConfigList.hs ./Command/Trust.hs ./Command/Lock.hs ./Command/SendKey.hs ./Command/RecvKey.hs ./Command/Add.hs ./Command/Fix.hs ./Command/Untrust.hs ./Command/InitRemote.hs ./Command/Semitrust.hs ./Command/Fsck.hs ./Command/Move.hs ./Command/DropUnused.hs ./Command/Get.hs ./Command/Upgrade.hs ./Command/Describe.hs ./Command/Drop.hs ./Command/DropKey.hs ./Command/InAnnex.hs ./Command/Find.hs ./Command/Unannex.hs ./Command/Status.hs ./Command/Copy.hs ./Command/SetKey.hs ./Init.hs ./Types.hs ./Trust.hs ./StatFS.hsc ./Makefile ./git-annex.cabal ./Upgrade/V1.hs ./Upgrade/V2.hs ./Upgrade/V0.hs ./LocationLog.hs ./CHANGELOG ./Git/LsFiles.hs ./Git/UnionMerge.hs ./Git/Queue.hs ./Branch.hs ./doc/upgrades.mdwn ./doc/forum/wishlist:_define_remotes_that_must_have_all_files.mdwn ./doc/forum/Will_git_annex_work_on_a_FAT32_formatted_key__63__.mdwn ./doc/forum/wishlist:_command_options_changes/comment_2_f6a637c78c989382e3c22d41b7fb4cc2._comment ./doc/forum/wishlist:_command_options_changes/comment_3_bf1114533d2895804e531e76eb6b8095._comment ./doc/forum/wishlist:_command_options_changes/comment_1_bfba72a696789bf21b2435dea15f967a._comment ./doc/forum/wishlist:_git-annex_replicate/comment_3_c13f4f9c3d5884fc6255fd04feadc2b1._comment ./doc/forum/wishlist:_git-annex_replicate/comment_1_9926132ec6052760cdf28518a24e2358._comment ./doc/forum/wishlist:_git-annex_replicate/comment_2_c43932f4194aba8fb2470b18e0817599._comment ./doc/forum/Behaviour_of_fsck/comment_4_e4911dc6793f98fb81151daacbe49968._comment ./doc/forum/Behaviour_of_fsck/comment_3_97848f9a3db89c0427cfb671ba13300e._comment ./doc/forum/Behaviour_of_fsck/comment_2_ead36a23c3e6efa1c41e4555f93e014e._comment ./doc/forum/Behaviour_of_fsck/comment_1_0e40f158b3f4ccdcaab1408d858b68b8._comment ./doc/forum/incompatible_versions__63__/comment_1_629f28258746d413e452cbd42a1a43f4._comment ./doc/forum/migration_to_git-annex_and_rsync.mdwn ./doc/forum/hashing_objects_directories.mdwn ./doc/forum/hashing_objects_directories/comment_5_ef6cfd49d24c180c2d0a062e5bd3a0be._comment ./doc/forum/hashing_objects_directories/comment_1_c55c56076be4f54251b0b7f79f28a607._comment ./doc/forum/hashing_objects_directories/comment_2_504c96959c779176f991f4125ea22009._comment ./doc/forum/hashing_objects_directories/comment_3_9134bde0a13aac0b6a4e5ebabd7f22e8._comment ./doc/forum/hashing_objects_directories/comment_4_0de9170e429cbfea66f5afa8980d45ac._comment ./doc/forum/wishlist:_support_for_more_ssh_urls_.mdwn ./doc/forum/Wishlist:_Ways_of_selecting_files_based_on_meta-information.mdwn ./doc/forum/git-annex_on_OSX.mdwn ./doc/forum/version_3_upgrade/comment_1_05fc9c9cad26c520bebb98c852c71e35._comment ./doc/forum/example_of_massively_disconnected_operation.mdwn ./doc/forum/git-annex_communication_channels/comment_2_c7aeefa6ef9a2e75d8667b479ade1b7f._comment ./doc/forum/git-annex_communication_channels/comment_5_404b723a681eb93fee015cea8024b6bc._comment ./doc/forum/git-annex_communication_channels/comment_1_198325d2e9337c90f026396de89eec0e._comment ./doc/forum/git-annex_communication_channels/comment_4_1ba6ddf54843c17c7d19a9996f2ab712._comment ./doc/forum/git-annex_communication_channels/comment_6_0d87d0e26461494b1d7f8a701a924729._comment ./doc/forum/git-annex_communication_channels/comment_3_1ff08a3e0e63fa0e560cbc9602245caa._comment ./doc/forum/git-annex_communication_channels/comment_7_2c87c7a0648fe87c2bf6b4391f1cc468._comment ./doc/forum/wishlist:_git-annex_replicate.mdwn ./doc/forum/performance_improvement:_git_on_ssd__44___annex_on_spindle_disk.mdwn ./doc/forum/Behaviour_of_fsck.mdwn ./doc/forum/working_without_git-annex_commits.mdwn ./doc/forum/migrate_existing_git_repository_to_git-annex.mdwn ./doc/forum/__34__git_annex_lock__34___very_slow_for_big_repo.mdwn ./doc/forum/wishlist:_traffic_accounting_for_git-annex.mdwn ./doc/forum/rsync_over_ssh__63__.mdwn ./doc/forum/unannex_alternatives/comment_3_b1687fc8f9e7744327bbeb6f0635d1cd._comment ./doc/forum/unannex_alternatives/comment_1_dcd4cd41280b41512bbdffafaf307993._comment ./doc/forum/unannex_alternatives/comment_2_58a72a9fe0f58c7af0b4d7927a2dd21d._comment ./doc/forum/wishlist:_git_annex_status/comment_4_9aeeb83d202dc8fb33ff364b0705ad94._comment ./doc/forum/wishlist:_git_annex_status/comment_3_d1fd70c67243971c96d59e1ffb7ef6e7._comment ./doc/forum/wishlist:_git_annex_status/comment_2_c2b0ce025805b774dc77ce264a222824._comment ./doc/forum/wishlist:_git_annex_status/comment_1_994bfd12c5d82e08040d6116915c5090._comment ./doc/forum/wishlist:_command_options_changes.mdwn ./doc/forum/new_microfeatures/comment_2_41ad904c68e89c85e1fc49c9e9106969._comment ./doc/forum/new_microfeatures/comment_1_058bd517c6fffaf3446b1f5d5be63623._comment ./doc/forum/new_microfeatures/comment_3_a1a9347b5bc517f2a89a8b292c3f8517._comment ./doc/forum/new_microfeatures/comment_7_94045b9078b1fff877933b012d1b49e2._comment ./doc/forum/new_microfeatures/comment_5_3c627d275586ff499d928a8f8136babf._comment ./doc/forum/new_microfeatures/comment_4_5a6786dc52382fff5cc42fdb05770196._comment ./doc/forum/new_microfeatures/comment_6_31ea08c008500560c0b96c6601bc6362._comment ./doc/forum/Error_while_adding_a_file___34__createSymbolicLink:_already_exists__34__.mdwn ./doc/forum/wishlist:_git_annex_put_--_same_as_get__44___but_for_defaults.mdwn ./doc/forum/OSX__39__s_default_sshd_behaviour_has_limited_paths_set.mdwn ./doc/forum/bainstorming:_git_annex_push___38___pull.mdwn ./doc/forum/sparse_git_checkouts_with_annex.mdwn ./doc/forum/Need_new_build_instructions_for_Debian_stable.mdwn ./doc/forum/batch_check_on_remote_when_using_copy.mdwn ./doc/forum/tips:_special__95__remotes__47__hook_with_tahoe-lafs.mdwn ./doc/forum/wishlist:alias_system.mdwn ./doc/forum/incompatible_versions__63__.mdwn ./doc/forum/wishlist:_git_backend_for_git-annex.mdwn ./doc/forum/seems_to_build_fine_on_haskell_platform_2011.mdwn ./doc/forum/rsync_over_ssh__63__/comment_1_ee21f32e90303e20339e0a568321bbbe._comment ./doc/forum/rsync_over_ssh__63__/comment_2_aa690da6ecfb2b30fc5080ad76dc77b1._comment ./doc/forum/wishlist:_special_remote_for_sftp_or_rsync.mdwn ./doc/forum/OSX__39__s_haskell-platform_statically_links_things.mdwn ./doc/forum/unannex_alternatives.mdwn ./doc/forum/can_git-annex_replace_ddm__63__.mdwn ./doc/forum/sparse_git_checkouts_with_annex/comment_2_e357db3ccc4079f07a291843975535eb._comment ./doc/forum/sparse_git_checkouts_with_annex/comment_1_c7dc199c5740a0e7ba606dfb5e3e579a._comment ./doc/forum/sparse_git_checkouts_with_annex/comment_3_fcfafca994194d57dccf5319c7c9e646._comment ./doc/forum/sparse_git_checkouts_with_annex/comment_4_04dc14880f31eee2b6d767d4d4258c5a._comment ./doc/forum/git-annex_communication_channels.mdwn ./doc/forum/can_git-annex_replace_ddm__63__/comment_3_4c69097fe2ee81359655e59a03a9bb8d._comment ./doc/forum/can_git-annex_replace_ddm__63__/comment_2_008554306dd082d7f543baf283510e92._comment ./doc/forum/can_git-annex_replace_ddm__63__/comment_1_aa05008dfe800474ff76678a400099e1._comment ./doc/forum/version_3_upgrade.mdwn ./doc/forum/relying_on_git_for_numcopies/comment_3_43d8e1513eb9947f8a503f094c03f307._comment ./doc/forum/relying_on_git_for_numcopies/comment_2_be6acbc26008a9cb54e7b8f498f2c2a2._comment ./doc/forum/relying_on_git_for_numcopies/comment_1_8ad3cccd7f66f6423341d71241ba89fc._comment ./doc/forum/relying_on_git_for_numcopies.mdwn ./doc/forum/wishlist:_do_round_robin_downloading_of_data.mdwn ./doc/forum/wishlist:_git_annex_status.mdwn ./doc/forum/getting_git_annex_to_do_a_force_copy_to_a_remote.mdwn ./doc/forum/Problems_with_large_numbers_of_files.mdwn ./doc/forum/Can_I_store_normal_files_in_the_git-annex_git_repository__63__.mdwn ./doc/forum/Is_an_automagic_upgrade_of_the_object_directory_safe__63__.mdwn ./doc/forum/wishlist:_push_to_cia.vc_from_the_website__39__s_repo__44___not_your_personal_one.mdwn ./doc/forum/new_microfeatures.mdwn ./doc/backends.mdwn ./doc/bugs/__39__annex_add__39___fails_to___39__git_add__39___for_parent_relative_path.mdwn ./doc/bugs/softlink_mtime.mdwn ./doc/bugs/ordering.mdwn ./doc/bugs/dropping_files_with_a_URL_backend_fails.mdwn ./doc/bugs/fsck__47__fix_should_check__47__fix_the_permissions_of_.git__47__annex.mdwn ./doc/bugs/git_annex_get_choke_when_remote_is_an_ssh_url_with_a_port.mdwn ./doc/bugs/unannex_and_uninit_do_not_work_when_git_index_is_broken.mdwn ./doc/bugs/git_annex_migrate_leaves_old_backend_versions_around.mdwn ./doc/bugs/git_annex_unused_failes_on_empty_repository.mdwn ./doc/bugs/git_annex_add_eats_files_when_filename_is_too_long.mdwn ./doc/bugs/S3_bucket_uses_the_same_key_for_encryption_and_hashing.mdwn ./doc/bugs/problem_with_upgrade_v2_-__62___v3.mdwn ./doc/bugs/git_annex_copy_--fast_does_not_copy_files.mdwn ./doc/bugs/Cabal_dependency_monadIO_missing.mdwn ./doc/bugs/dotdot_problem.mdwn ./doc/bugs/Problems_running_make_on_osx.mdwn ./doc/bugs/free_space_checking/comment_2_8a65f6d3dcf5baa3f7f2dbe1346e2615._comment ./doc/bugs/free_space_checking/comment_3_0fc6ff79a357b1619d13018ccacc7c10._comment ./doc/bugs/free_space_checking/comment_1_a868e805be43c5a7c19c41f1af8e41e6._comment ./doc/bugs/done.mdwn ./doc/bugs/git_annex_copy_-f_REMOTE_._doesn__39__t_work_as_expected.mdwn ./doc/bugs/Displayed_copy_speed_is_wrong/comment_2_8b240de1d5ae9229fa2d77d1cc15a552._comment ./doc/bugs/Displayed_copy_speed_is_wrong/comment_1_74de3091e8bfd7acd6795e61f39f07c6._comment ./doc/bugs/bare_git_repos.mdwn ./doc/bugs/fat_support.mdwn ./doc/bugs/problems_with_utf8_names.mdwn ./doc/bugs/error_with_file_names_starting_with_dash.mdwn ./doc/bugs/unhappy_without_UTF8_locale.mdwn ./doc/bugs/S3_memory_leaks.mdwn ./doc/bugs/upgrade_left_untracked_.git-annex__47____42___directories.mdwn ./doc/bugs/--git-dir_and_--work-tree_options.mdwn ./doc/bugs/Problems_running_make_on_osx/comment_9_cc283b485b3c95ba7eebc8f0c96969b3._comment ./doc/bugs/Problems_running_make_on_osx/comment_4_c52be386f79f14c8570a8f1397c68581._comment ./doc/bugs/Problems_running_make_on_osx/comment_18_64fab50d95de619eb2e8f08f90237de1._comment ./doc/bugs/Problems_running_make_on_osx/comment_16_5c2dd6002aadaab30841b77a5f5aed34._comment ./doc/bugs/Problems_running_make_on_osx/comment_10_94e4ac430140042a2d0fb5a16d86b4e5._comment ./doc/bugs/Problems_running_make_on_osx/comment_15_6b8867b8e48bf807c955779c9f8f0909._comment ./doc/bugs/Problems_running_make_on_osx/comment_11_56f1143fa191361d63b441741699e17f._comment ./doc/bugs/Problems_running_make_on_osx/comment_20_7db27d1a22666c831848bc6c06d66a84._comment ./doc/bugs/Problems_running_make_on_osx/comment_12_ec5131624d0d2285d3b6880e47033f97._comment ./doc/bugs/Problems_running_make_on_osx/comment_3_68f0f8ae953589ae26d57310b40c878d._comment ./doc/bugs/Problems_running_make_on_osx/comment_14_89a960b6706ed703b390a81a8bc4e311._comment ./doc/bugs/Problems_running_make_on_osx/comment_17_62fccb04b0e4b695312f7a3f32fb96ee._comment ./doc/bugs/Problems_running_make_on_osx/comment_19_4253988ed178054c8b6400beeed68a29._comment ./doc/bugs/Problems_running_make_on_osx/comment_6_0c46f5165ceb5a7b9ea9689c33b3a4f8._comment ./doc/bugs/Problems_running_make_on_osx/comment_1_34120e82331ace01a6a4960862d38f2d._comment ./doc/bugs/Problems_running_make_on_osx/comment_7_237a137cce58a28abcc736cbf2c420b0._comment ./doc/bugs/Problems_running_make_on_osx/comment_5_7f1330a1e541b0f3e2192e596d7f7bee._comment ./doc/bugs/Problems_running_make_on_osx/comment_2_cc53d1681d576186dbc868dd9801d551._comment ./doc/bugs/Problems_running_make_on_osx/comment_13_88ed095a448096bf8a69015a04e64df1._comment ./doc/bugs/Problems_running_make_on_osx/comment_8_efafa203addf8fa79e33e21a87fb5a2b._comment ./doc/bugs/git-annex_directory_hashing_problems_on_osx.mdwn ./doc/bugs/No_easy_way_to_re-inject_a_file_into_an_annex.mdwn ./doc/bugs/git_annex_unlock_is_not_atomic.mdwn ./doc/bugs/free_space_checking.mdwn ./doc/bugs/wishlist:_more_descriptive_commit_messages_in_git-annex_branch.mdwn ./doc/bugs/Displayed_copy_speed_is_wrong.mdwn ./doc/bugs/git_annex_should_use___39__git_add_-f__39___internally.mdwn ./doc/bugs/minor_bug:_errors_are_not_verbose_enough.mdwn ./doc/bugs/No_version_information_from_cli.mdwn ./doc/bugs/Error_while_adding_a_file___34__createSymbolicLink:_already_exists__34__.mdwn ./doc/bugs/tests_fail_when_there_is_no_global_.gitconfig_for_the_user.mdwn ./doc/bugs/git_annex_initremote_walks_.git-annex.mdwn ./doc/bugs/git-annex_incorrectly_parses_bare_IPv6_addresses.mdwn ./doc/bugs/conflicting_haskell_packages/comment_1_e552a6cc6d7d1882e14130edfc2d6b3b._comment ./doc/bugs/tmp_file_handling.mdwn ./doc/bugs/annex_add_in_annex.mdwn ./doc/bugs/making_annex-merge_try_a_fast-forward.mdwn ./doc/bugs/Makefile_is_missing_dependancies.mdwn ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_4_91439d4dbbf1461e281b276eb0003691._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_6_f360f0006bc9115bc5a3e2eb9fe58abd._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_3_86d9e7244ae492bcbe62720b8c4fc4a9._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_2_cd0123392b16d89db41b45464165c247._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_1_5f60006c9bb095167d817f234a14d20b._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_5_ca33a9ca0df33f7c1b58353d7ffb943d._comment ./doc/bugs/git_rename_detection_on_file_move/comment_3_57010bcaca42089b451ad8659a1e018e._comment ./doc/bugs/git_rename_detection_on_file_move/comment_4_79d96599f757757f34d7b784e6c0e81c._comment ./doc/bugs/git_rename_detection_on_file_move/comment_5_d61f5693d947b9736b29fca1dbc7ad76._comment ./doc/bugs/git_rename_detection_on_file_move/comment_2_7101d07400ad5935f880dc00d89bf90e._comment ./doc/bugs/git_rename_detection_on_file_move/comment_1_0531dcfa833b0321a7009526efe3df33._comment ./doc/bugs/add_range_argument_to___34__git_annex_dropunused__34___.mdwn ./doc/bugs/git_rename_detection_on_file_move.mdwn ./doc/bugs/problem_commit_normal_links.mdwn ./doc/bugs/git_annex_unused_seems_to_check_for_current_path.mdwn ./doc/bugs/Makefile_is_missing_dependancies/comment_6_24119fc5d5963ce9dd669f7dcf006859._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_3_c38b6f4abc9b9ad413c3b83ca04386c3._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_8_a3555e3286cdc2bfeb9cde0ff727ba74._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_2_416f12dbd0c2b841fac8164645b81df5._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_5_0a1c52e2c96d19b9c3eb7e99b8c2434f._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_7_96fd4725df4b54e670077a18d3ac4943._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_1_5a3da5f79c8563c7a450aa29728abe7c._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_4_cc13873175edf191047282700315beee._comment ./doc/bugs/touch.hsc_has_problems_on_non-linux_based_systems.mdwn ./doc/bugs/build_issue_with_latest_release_0.20110522-1-gde817ba.mdwn ./doc/bugs/weird_local_clone_confuses.mdwn ./doc/bugs/fsck_output.mdwn ./doc/bugs/git-annex_has_issues_with_git_when_staging__47__commiting_logs.mdwn ./doc/bugs/git_annex_gets_confused_about_remotes_with_dots_in_their_names.mdwn ./doc/bugs/unannex_vs_unlock_hook_confusion.mdwn ./doc/bugs/Cabal_dependency_monadIO_missing/comment_2_4f4d8e1e00a2a4f7e8a8ab082e16adac._comment ./doc/bugs/Cabal_dependency_monadIO_missing/comment_1_14be660aa57fadec0d81b32a8b52c66f._comment ./doc/bugs/conflicting_haskell_packages.mdwn ./doc/bugs/git_command_line_constructed_by_unannex_command_has_tons_of_redundant_-a_paramters.mdwn ./doc/bugs/backend_version_upgrade_leaves_repo_unusable.mdwn ./doc/bugs/fat_support/comment_3_df3b943bc1081a8f3f7434ae0c8e061e._comment ./doc/bugs/fat_support/comment_4_90a8a15bedd94480945a374f9d706b86._comment ./doc/bugs/fat_support/comment_5_64bbf89de0836673224b83fdefa0407b._comment ./doc/bugs/fat_support/comment_1_04bcc4795d431e8cb32293aab29bbfe2._comment ./doc/bugs/fat_support/comment_2_bb4a97ebadb5c53809fc78431eabd7c8._comment ./doc/bugs/unannex_command_doesn__39__t_all_files.mdwn ./doc/bugs/annex_unannex__47__uninit_should_handle_copies.mdwn ./doc/bugs/git_annex_fsck_is_a_no-op_in_bare_repos.mdwn ./doc/bugs/Unfortunate_interaction_with_Calibre.mdwn ./doc/bugs/error_propigation.mdwn ./doc/bugs/scp_interrupt_to_background.mdwn ./doc/bugs/copy_fast_confusing_with_broken_locationlog.mdwn ./doc/bugs/configure_script_should_detect_uuidgen_instead_of_just_uuid.mdwn ./doc/bugs/check_for_curl_in_configure.hs.mdwn ./doc/bugs/Name_scheme_does_not_follow_git__39__s_rules.mdwn ./doc/bugs/Prevent_accidental_merges.mdwn ./doc/bugs/building_on_lenny.mdwn ./doc/bugs/encrypted_S3_stalls.mdwn ./doc/bugs/support_bare_git_repo__44___with_the_annex_directory_exposed_to_http.mdwn ./doc/bugs/WORM:_Handle_long_filenames_correctly.mdwn ./doc/logo_small.png ./doc/cheatsheet.mdwn ./doc/use_case/Bob.mdwn ./doc/use_case/Alice.mdwn ./doc/upgrades/SHA_size.mdwn ./doc/news/sharebox_a_FUSE_filesystem_for_git-annex.mdwn ./doc/news/version_3.20110817.mdwn ./doc/news/version_3.20110707.mdwn ./doc/news/version_3.20110705.mdwn ./doc/news/LWN_article.mdwn ./doc/news/version_3.20110719.mdwn ./doc/news/version_3.20110819.mdwn ./doc/git-union-merge.mdwn ./doc/special_remotes/S3.mdwn ./doc/special_remotes/web.mdwn ./doc/special_remotes/bup.mdwn ./doc/special_remotes/directory.mdwn ./doc/special_remotes/hook.mdwn ./doc/special_remotes/rsync.mdwn ./doc/users/joey.mdwn ./doc/users/fmarier.mdwn ./doc/users/chrysn.mdwn ./doc/templates/walkthrough.tmpl ./doc/templates/bare.tmpl ./doc/index.mdwn ./doc/summary.mdwn ./doc/forum.mdwn ./doc/encryption/comment_1_1afca8d7182075d46db41f6ad3dd5911._comment ./doc/transferring_data.mdwn ./doc/download.mdwn ./doc/future_proofing.mdwn ./doc/todo/auto_remotes/discussion.mdwn ./doc/todo/file_copy_progress_bar.mdwn ./doc/todo/use_cp_reflink.mdwn ./doc/todo/gitrm.mdwn ./doc/todo/git-annex_unused_eats_memory.mdwn ./doc/todo/add_--exclude_option_to_git_annex_find.mdwn ./doc/todo/tahoe_lfs_for_reals/comment_2_80b9e848edfdc7be21baab7d0cef0e3a._comment ./doc/todo/tahoe_lfs_for_reals/comment_1_0a4793ce6a867638f6e510e71dd4bb44._comment ./doc/todo/object_dir_reorg_v2.mdwn ./doc/todo/S3.mdwn ./doc/todo/auto_remotes.mdwn ./doc/todo/wishlist:_support_for_more_ssh_urls_.mdwn ./doc/todo/support-non-utf8-locales.mdwn ./doc/todo/done.mdwn ./doc/todo/fsck.mdwn ./doc/todo/support_S3_multipart_uploads.mdwn ./doc/todo/wishlist:_Provide_a___34__git_annex__34___command_that_will_skip_duplicates.mdwn ./doc/todo/pushpull.mdwn ./doc/todo/smudge.mdwn ./doc/todo/cache_key_info/comment_1_578df1b3b2cbfdc4aa1805378f35dc48._comment ./doc/todo/object_dir_reorg_v2/comment_3_79bdf9c51dec9f52372ce95b53233bb2._comment ./doc/todo/object_dir_reorg_v2/comment_7_42501404c82ca07147e2cce0cff59474._comment ./doc/todo/object_dir_reorg_v2/comment_5_821c382987f105da72a50e0a5ce61fdc._comment ./doc/todo/object_dir_reorg_v2/comment_1_ba03333dc76ff49eccaba375e68cb525._comment ./doc/todo/object_dir_reorg_v2/comment_2_81276ac309959dc741bc90101c213ab7._comment ./doc/todo/object_dir_reorg_v2/comment_6_8834c3a3f1258c4349d23aff8549bf35._comment ./doc/todo/object_dir_reorg_v2/comment_4_93aada9b1680fed56cc6f0f7c3aca5e5._comment ./doc/todo/union_mounting.mdwn ./doc/todo/wishlist:_swift_backend.mdwn ./doc/todo/wishlist:_swift_backend/comment_1_e6efbb35f61ee521b473a92674036788._comment ./doc/todo/wishlist:_swift_backend/comment_2_5d8c83b0485112e98367b7abaab3f4e3._comment ./doc/todo/git_annex_init_:_include_repo_description_and__47__or_UUID_in_commit_message.mdwn ./doc/todo/wishlist:___34__git_annex_add__34___multiple_processes.mdwn ./doc/todo/hidden_files.mdwn ./doc/todo/smudge/comment_1_4ea616bcdbc9e9a6fae9f2e2795c31c9._comment ./doc/todo/smudge/comment_2_e04b32caa0d2b4c577cdaf382a3ff7f6._comment ./doc/todo/network_remotes.mdwn ./doc/todo/add_a_git_backend.mdwn ./doc/todo/parallel_possibilities/comment_1_d8e34fc2bc4e5cf761574608f970d496._comment ./doc/todo/parallel_possibilities/comment_2_adb76f06a7997abe4559d3169a3181c3._comment ./doc/todo/checkout.mdwn ./doc/todo/immutable_annexed_files.mdwn ./doc/todo/wishlist:_Prevent_repeated_password_prompts_for_one_command.mdwn ./doc/todo/git-annex-shell.mdwn ./doc/todo/speed_up_fsck.mdwn ./doc/todo/tahoe_lfs_for_reals.mdwn ./doc/todo/using_url_backend.mdwn ./doc/todo/branching.mdwn ./doc/todo/rsync.mdwn ./doc/todo/backendSHA1.mdwn ./doc/todo/cache_key_info.mdwn ./doc/todo/parallel_possibilities.mdwn ./doc/todo/symlink_farming_commit_hook.mdwn ./doc/feeds.mdwn ./doc/copies.mdwn ./doc/repomap.png ./doc/special_remotes.mdwn ./doc/todo.mdwn ./doc/walkthrough/using_ssh_remotes.mdwn ./doc/walkthrough/untrusted_repositories.mdwn ./doc/walkthrough/moving_file_content_between_repositories.mdwn ./doc/walkthrough/modifying_annexed_files.mdwn ./doc/walkthrough/Internet_Archive_via_S3.mdwn ./doc/walkthrough/more.mdwn ./doc/walkthrough/removing_files.mdwn ./doc/walkthrough/creating_a_repository.mdwn ./doc/walkthrough/what_to_do_when_you_lose_a_repository.mdwn ./doc/walkthrough/using_Amazon_S3.mdwn ./doc/walkthrough/adding_a_remote.mdwn ./doc/walkthrough/adding_a_remote/comment_1_0a59355bd33a796aec97173607e6adc9._comment ./doc/walkthrough/adding_a_remote/comment_2_f8cd79ef1593a8181a7f1086a87713e8._comment ./doc/walkthrough/adding_a_remote/comment_3_60691af4400521b5a8c8d75efe3b44cb._comment ./doc/walkthrough/adding_a_remote/comment_4_6f7cf5c330272c96b3abeb6612075c9d._comment ./doc/walkthrough/removing_files:_When_things_go_wrong.mdwn ./doc/walkthrough/renaming_files.mdwn ./doc/walkthrough/using_bup.mdwn ./doc/walkthrough/fsck:_when_things_go_wrong.mdwn ./doc/walkthrough/adding_files.mdwn ./doc/walkthrough/unused_data.mdwn ./doc/walkthrough/fsck:_verifying_your_data.mdwn ./doc/walkthrough/getting_file_content.mdwn ./doc/walkthrough/using_the_web.mdwn ./doc/walkthrough/recover_data_from_lost+found.mdwn ./doc/walkthrough/backups.mdwn ./doc/walkthrough/transferring_files:_When_things_go_wrong.mdwn ./doc/walkthrough/using_the_SHA1_backend.mdwn ./doc/walkthrough/migrating_data_to_a_new_backend.mdwn ./doc/GPL ./doc/logo.png ./doc/location_tracking.mdwn ./doc/design.mdwn ./doc/news.mdwn ./doc/encryption.mdwn ./doc/not.mdwn ./doc/download/comment_7_a5eebd214b135f34b18274a682211943._comment ./doc/download/comment_3_cf6044ebe99f71158034e21197228abd._comment ./doc/download/comment_5_c6b1bc40226fc2c8ba3e558150856992._comment ./doc/download/comment_6_3a52993d3553deb9a413debec9a5f92d._comment ./doc/download/comment_2_f85f72b33aedc3425f0c0c47867d02f3._comment ./doc/download/comment_8_59a976de6c7d333709b92f7cd5830850._comment ./doc/download/comment_4_10fc013865c7542c2ed9d6c0963bb391._comment ./doc/download/comment_1_fbd8b6d39e9d3c71791551358c863966._comment ./doc/contact.mdwn ./doc/trust.mdwn ./doc/bare_repositories.mdwn ./doc/distributed_version_control.mdwn ./doc/install.mdwn ./doc/git-annex-shell.mdwn ./doc/bugs.mdwn ./doc/design/encryption/comment_2_a610b3d056a059899178859a3a821ea5._comment ./doc/design/encryption/comment_3_cca186a9536cd3f6e86994631b14231c._comment ./doc/design/encryption/comment_1_4715ffafb3c4a9915bc33f2b26aaa9c1._comment ./doc/design/encryption/comment_4_8f3ba3e504b058791fc6e6f9c38154cf._comment ./doc/design/encryption.mdwn ./doc/comments.mdwn ./doc/git-annex.mdwn ./doc/internals.mdwn ./doc/users.mdwn ./doc/install/Ubuntu.mdwn ./doc/install/Debian.mdwn ./doc/install/OSX.mdwn ./doc/install/FreeBSD.mdwn ./doc/install/comment_3_cff163ea3e7cad926f4ed9e78b896598._comment ./doc/install/Debian/comment_2_648e3467e260cdf233acdb0b53313ce0._comment ./doc/install/Debian/comment_1_029486088d098c2d4f1099f2f0e701a9._comment ./doc/install/Fedora.mdwn ./doc/install/comment_4_82a17eee4a076c6c79fddeda347e0c9a._comment ./doc/walkthrough.mdwn ./CmdLine.hs ./UUID.hs ./Messages.hs ./Setup.hs ./TestConfig.hs ./GPL ./mdwn2man ./Remote.hs ./git-annex.hs ./Locations.hs ./Backend/WORM.hs ./Backend/URL.hs ./Backend/SHA.hs ./Options.hs ./git-annex-shell.hs ./test.hs ./INSTALL ./Remote/Web.hs ./Remote/S3stub.hs ./Remote/Helper/Encryptable.hs ./Remote/Helper/Url.hs ./Remote/Helper/Ssh.hs ./Remote/Helper/Special.hs ./Remote/Directory.hs ./Remote/S3real.hs ./Remote/Bup.hs ./Remote/Hook.hs ./Remote/Rsync.hs ./Remote/Git.hs ./Utility.hs ./Upgrade.hs ./git-union-merge.hs ./Utility/DataUnits.hs ./Utility/RsyncFile.hs ./Utility/CopyFile.hs ./Utility/Dot.hs ./Utility/Base64.hs ./Types/Crypto.hs ./Types/UUID.hs ./Types/TrustLevel.hs ./Types/Remote.hs ./Types/BranchState.hs ./Types/Backend.hs ./Types/Key.hs ./Config.hs ./Command.hs ./.gitignore ./Backend.hs ./Touch.hsc ./Content.hs ./.gitattributes ./Git.hs ./GitAnnex.hs+Extra-Source-Files: ./debian/NEWS ./debian/control ./debian/rules ./debian/manpages ./debian/changelog ./debian/doc-base ./debian/copyright ./debian/compat ./Messages/JSON.hs ./AnnexQueue.hs ./configure.hs ./Annex.hs ./PresenceLog.hs ./Version.hs ./Crypto.hs ./RemoteLog.hs ./README ./Command/Merge.hs ./Command/AddUrl.hs ./Command/Whereis.hs ./Command/Unlock.hs ./Command/Version.hs ./Command/FromKey.hs ./Command/PreCommit.hs ./Command/Map.hs ./Command/Unused.hs ./Command/Uninit.hs ./Command/Migrate.hs ./Command/Init.hs ./Command/ConfigList.hs ./Command/Trust.hs ./Command/Lock.hs ./Command/SendKey.hs ./Command/RecvKey.hs ./Command/Add.hs ./Command/Fix.hs ./Command/Untrust.hs ./Command/InitRemote.hs ./Command/Semitrust.hs ./Command/Fsck.hs ./Command/Move.hs ./Command/DropUnused.hs ./Command/Get.hs ./Command/Upgrade.hs ./Command/Describe.hs ./Command/Drop.hs ./Command/DropKey.hs ./Command/InAnnex.hs ./Command/Find.hs ./Command/Unannex.hs ./Command/Status.hs ./Command/Copy.hs ./Command/SetKey.hs ./Init.hs ./Types.hs ./Trust.hs ./Makefile ./git-annex.cabal ./Upgrade/V1.hs ./Upgrade/V2.hs ./Upgrade/V0.hs ./LocationLog.hs ./CHANGELOG ./Git/LsFiles.hs ./Git/UnionMerge.hs ./Git/Queue.hs ./Branch.hs ./doc/upgrades.mdwn ./doc/forum/wishlist:_define_remotes_that_must_have_all_files.mdwn ./doc/forum/Will_git_annex_work_on_a_FAT32_formatted_key__63__.mdwn ./doc/forum/wishlist:_command_options_changes/comment_2_f6a637c78c989382e3c22d41b7fb4cc2._comment ./doc/forum/wishlist:_command_options_changes/comment_3_bf1114533d2895804e531e76eb6b8095._comment ./doc/forum/wishlist:_command_options_changes/comment_1_bfba72a696789bf21b2435dea15f967a._comment ./doc/forum/wishlist:_git-annex_replicate/comment_3_c13f4f9c3d5884fc6255fd04feadc2b1._comment ./doc/forum/wishlist:_git-annex_replicate/comment_1_9926132ec6052760cdf28518a24e2358._comment ./doc/forum/wishlist:_git-annex_replicate/comment_2_c43932f4194aba8fb2470b18e0817599._comment ./doc/forum/Behaviour_of_fsck/comment_4_e4911dc6793f98fb81151daacbe49968._comment ./doc/forum/Behaviour_of_fsck/comment_3_97848f9a3db89c0427cfb671ba13300e._comment ./doc/forum/Behaviour_of_fsck/comment_2_ead36a23c3e6efa1c41e4555f93e014e._comment ./doc/forum/Behaviour_of_fsck/comment_1_0e40f158b3f4ccdcaab1408d858b68b8._comment ./doc/forum/incompatible_versions__63__/comment_1_629f28258746d413e452cbd42a1a43f4._comment ./doc/forum/migration_to_git-annex_and_rsync.mdwn ./doc/forum/hashing_objects_directories.mdwn ./doc/forum/hashing_objects_directories/comment_5_ef6cfd49d24c180c2d0a062e5bd3a0be._comment ./doc/forum/hashing_objects_directories/comment_1_c55c56076be4f54251b0b7f79f28a607._comment ./doc/forum/hashing_objects_directories/comment_2_504c96959c779176f991f4125ea22009._comment ./doc/forum/hashing_objects_directories/comment_3_9134bde0a13aac0b6a4e5ebabd7f22e8._comment ./doc/forum/hashing_objects_directories/comment_4_0de9170e429cbfea66f5afa8980d45ac._comment ./doc/forum/wishlist:_support_for_more_ssh_urls_.mdwn ./doc/forum/Wishlist:_Ways_of_selecting_files_based_on_meta-information.mdwn ./doc/forum/git-annex_on_OSX.mdwn ./doc/forum/version_3_upgrade/comment_1_05fc9c9cad26c520bebb98c852c71e35._comment ./doc/forum/example_of_massively_disconnected_operation.mdwn ./doc/forum/git-annex_communication_channels/comment_2_c7aeefa6ef9a2e75d8667b479ade1b7f._comment ./doc/forum/git-annex_communication_channels/comment_5_404b723a681eb93fee015cea8024b6bc._comment ./doc/forum/git-annex_communication_channels/comment_1_198325d2e9337c90f026396de89eec0e._comment ./doc/forum/git-annex_communication_channels/comment_4_1ba6ddf54843c17c7d19a9996f2ab712._comment ./doc/forum/git-annex_communication_channels/comment_6_0d87d0e26461494b1d7f8a701a924729._comment ./doc/forum/git-annex_communication_channels/comment_3_1ff08a3e0e63fa0e560cbc9602245caa._comment ./doc/forum/git-annex_communication_channels/comment_7_2c87c7a0648fe87c2bf6b4391f1cc468._comment ./doc/forum/wishlist:_git-annex_replicate.mdwn ./doc/forum/advantages_of_SHA__42___over_WORM/comment_1_96c354cac4b5ce5cf6664943bc84db1d._comment ./doc/forum/performance_improvement:_git_on_ssd__44___annex_on_spindle_disk.mdwn ./doc/forum/Behaviour_of_fsck.mdwn ./doc/forum/working_without_git-annex_commits.mdwn ./doc/forum/migrate_existing_git_repository_to_git-annex.mdwn ./doc/forum/__34__git_annex_lock__34___very_slow_for_big_repo.mdwn ./doc/forum/wishlist:_traffic_accounting_for_git-annex.mdwn ./doc/forum/rsync_over_ssh__63__.mdwn ./doc/forum/unannex_alternatives/comment_3_b1687fc8f9e7744327bbeb6f0635d1cd._comment ./doc/forum/unannex_alternatives/comment_1_dcd4cd41280b41512bbdffafaf307993._comment ./doc/forum/unannex_alternatives/comment_2_58a72a9fe0f58c7af0b4d7927a2dd21d._comment ./doc/forum/wishlist:_git_annex_status/comment_4_9aeeb83d202dc8fb33ff364b0705ad94._comment ./doc/forum/wishlist:_git_annex_status/comment_3_d1fd70c67243971c96d59e1ffb7ef6e7._comment ./doc/forum/wishlist:_git_annex_status/comment_2_c2b0ce025805b774dc77ce264a222824._comment ./doc/forum/wishlist:_git_annex_status/comment_1_994bfd12c5d82e08040d6116915c5090._comment ./doc/forum/wishlist:_command_options_changes.mdwn ./doc/forum/new_microfeatures/comment_2_41ad904c68e89c85e1fc49c9e9106969._comment ./doc/forum/new_microfeatures/comment_1_058bd517c6fffaf3446b1f5d5be63623._comment ./doc/forum/new_microfeatures/comment_3_a1a9347b5bc517f2a89a8b292c3f8517._comment ./doc/forum/new_microfeatures/comment_7_94045b9078b1fff877933b012d1b49e2._comment ./doc/forum/new_microfeatures/comment_5_3c627d275586ff499d928a8f8136babf._comment ./doc/forum/new_microfeatures/comment_4_5a6786dc52382fff5cc42fdb05770196._comment ./doc/forum/new_microfeatures/comment_6_31ea08c008500560c0b96c6601bc6362._comment ./doc/forum/Error_while_adding_a_file___34__createSymbolicLink:_already_exists__34__.mdwn ./doc/forum/advantages_of_SHA__42___over_WORM.mdwn ./doc/forum/wishlist:_git_annex_put_--_same_as_get__44___but_for_defaults.mdwn ./doc/forum/OSX__39__s_default_sshd_behaviour_has_limited_paths_set.mdwn ./doc/forum/bainstorming:_git_annex_push___38___pull.mdwn ./doc/forum/sparse_git_checkouts_with_annex.mdwn ./doc/forum/Need_new_build_instructions_for_Debian_stable.mdwn ./doc/forum/batch_check_on_remote_when_using_copy.mdwn ./doc/forum/tips:_special__95__remotes__47__hook_with_tahoe-lafs.mdwn ./doc/forum/wishlist:alias_system.mdwn ./doc/forum/incompatible_versions__63__.mdwn ./doc/forum/wishlist:_git_backend_for_git-annex.mdwn ./doc/forum/seems_to_build_fine_on_haskell_platform_2011.mdwn ./doc/forum/rsync_over_ssh__63__/comment_1_ee21f32e90303e20339e0a568321bbbe._comment ./doc/forum/rsync_over_ssh__63__/comment_2_aa690da6ecfb2b30fc5080ad76dc77b1._comment ./doc/forum/wishlist:_special_remote_for_sftp_or_rsync.mdwn ./doc/forum/OSX__39__s_haskell-platform_statically_links_things.mdwn ./doc/forum/unannex_alternatives.mdwn ./doc/forum/can_git-annex_replace_ddm__63__.mdwn ./doc/forum/sparse_git_checkouts_with_annex/comment_2_e357db3ccc4079f07a291843975535eb._comment ./doc/forum/sparse_git_checkouts_with_annex/comment_1_c7dc199c5740a0e7ba606dfb5e3e579a._comment ./doc/forum/sparse_git_checkouts_with_annex/comment_3_fcfafca994194d57dccf5319c7c9e646._comment ./doc/forum/sparse_git_checkouts_with_annex/comment_4_04dc14880f31eee2b6d767d4d4258c5a._comment ./doc/forum/git-annex_communication_channels.mdwn ./doc/forum/can_git-annex_replace_ddm__63__/comment_3_4c69097fe2ee81359655e59a03a9bb8d._comment ./doc/forum/can_git-annex_replace_ddm__63__/comment_2_008554306dd082d7f543baf283510e92._comment ./doc/forum/can_git-annex_replace_ddm__63__/comment_1_aa05008dfe800474ff76678a400099e1._comment ./doc/forum/version_3_upgrade.mdwn ./doc/forum/relying_on_git_for_numcopies/comment_3_43d8e1513eb9947f8a503f094c03f307._comment ./doc/forum/relying_on_git_for_numcopies/comment_2_be6acbc26008a9cb54e7b8f498f2c2a2._comment ./doc/forum/relying_on_git_for_numcopies/comment_1_8ad3cccd7f66f6423341d71241ba89fc._comment ./doc/forum/relying_on_git_for_numcopies.mdwn ./doc/forum/wishlist:_do_round_robin_downloading_of_data.mdwn ./doc/forum/wishlist:_git_annex_status.mdwn ./doc/forum/getting_git_annex_to_do_a_force_copy_to_a_remote.mdwn ./doc/forum/Problems_with_large_numbers_of_files.mdwn ./doc/forum/Can_I_store_normal_files_in_the_git-annex_git_repository__63__.mdwn ./doc/forum/Is_an_automagic_upgrade_of_the_object_directory_safe__63__.mdwn ./doc/forum/wishlist:_push_to_cia.vc_from_the_website__39__s_repo__44___not_your_personal_one.mdwn ./doc/forum/new_microfeatures.mdwn ./doc/backends.mdwn ./doc/bugs/__39__annex_add__39___fails_to___39__git_add__39___for_parent_relative_path.mdwn ./doc/bugs/softlink_mtime.mdwn ./doc/bugs/ordering.mdwn ./doc/bugs/dropping_files_with_a_URL_backend_fails.mdwn ./doc/bugs/fsck__47__fix_should_check__47__fix_the_permissions_of_.git__47__annex.mdwn ./doc/bugs/add_script-friendly_output_options.mdwn ./doc/bugs/git_annex_get_choke_when_remote_is_an_ssh_url_with_a_port.mdwn ./doc/bugs/unannex_and_uninit_do_not_work_when_git_index_is_broken.mdwn ./doc/bugs/git_annex_migrate_leaves_old_backend_versions_around.mdwn ./doc/bugs/git_annex_unused_failes_on_empty_repository.mdwn ./doc/bugs/git_annex_add_eats_files_when_filename_is_too_long.mdwn ./doc/bugs/S3_bucket_uses_the_same_key_for_encryption_and_hashing.mdwn ./doc/bugs/problem_with_upgrade_v2_-__62___v3.mdwn ./doc/bugs/git_annex_copy_--fast_does_not_copy_files.mdwn ./doc/bugs/Cabal_dependency_monadIO_missing.mdwn ./doc/bugs/dotdot_problem.mdwn ./doc/bugs/Problems_running_make_on_osx.mdwn ./doc/bugs/free_space_checking/comment_2_8a65f6d3dcf5baa3f7f2dbe1346e2615._comment ./doc/bugs/free_space_checking/comment_3_0fc6ff79a357b1619d13018ccacc7c10._comment ./doc/bugs/free_space_checking/comment_1_a868e805be43c5a7c19c41f1af8e41e6._comment ./doc/bugs/done.mdwn ./doc/bugs/git_annex_copy_-f_REMOTE_._doesn__39__t_work_as_expected.mdwn ./doc/bugs/Displayed_copy_speed_is_wrong/comment_2_8b240de1d5ae9229fa2d77d1cc15a552._comment ./doc/bugs/Displayed_copy_speed_is_wrong/comment_1_74de3091e8bfd7acd6795e61f39f07c6._comment ./doc/bugs/bare_git_repos.mdwn ./doc/bugs/fat_support.mdwn ./doc/bugs/problems_with_utf8_names.mdwn ./doc/bugs/error_with_file_names_starting_with_dash.mdwn ./doc/bugs/unhappy_without_UTF8_locale.mdwn ./doc/bugs/S3_memory_leaks.mdwn ./doc/bugs/upgrade_left_untracked_.git-annex__47____42___directories.mdwn ./doc/bugs/--git-dir_and_--work-tree_options.mdwn ./doc/bugs/Problems_running_make_on_osx/comment_9_cc283b485b3c95ba7eebc8f0c96969b3._comment ./doc/bugs/Problems_running_make_on_osx/comment_4_c52be386f79f14c8570a8f1397c68581._comment ./doc/bugs/Problems_running_make_on_osx/comment_18_64fab50d95de619eb2e8f08f90237de1._comment ./doc/bugs/Problems_running_make_on_osx/comment_16_5c2dd6002aadaab30841b77a5f5aed34._comment ./doc/bugs/Problems_running_make_on_osx/comment_10_94e4ac430140042a2d0fb5a16d86b4e5._comment ./doc/bugs/Problems_running_make_on_osx/comment_15_6b8867b8e48bf807c955779c9f8f0909._comment ./doc/bugs/Problems_running_make_on_osx/comment_11_56f1143fa191361d63b441741699e17f._comment ./doc/bugs/Problems_running_make_on_osx/comment_20_7db27d1a22666c831848bc6c06d66a84._comment ./doc/bugs/Problems_running_make_on_osx/comment_12_ec5131624d0d2285d3b6880e47033f97._comment ./doc/bugs/Problems_running_make_on_osx/comment_3_68f0f8ae953589ae26d57310b40c878d._comment ./doc/bugs/Problems_running_make_on_osx/comment_14_89a960b6706ed703b390a81a8bc4e311._comment ./doc/bugs/Problems_running_make_on_osx/comment_17_62fccb04b0e4b695312f7a3f32fb96ee._comment ./doc/bugs/Problems_running_make_on_osx/comment_19_4253988ed178054c8b6400beeed68a29._comment ./doc/bugs/Problems_running_make_on_osx/comment_6_0c46f5165ceb5a7b9ea9689c33b3a4f8._comment ./doc/bugs/Problems_running_make_on_osx/comment_1_34120e82331ace01a6a4960862d38f2d._comment ./doc/bugs/Problems_running_make_on_osx/comment_7_237a137cce58a28abcc736cbf2c420b0._comment ./doc/bugs/Problems_running_make_on_osx/comment_5_7f1330a1e541b0f3e2192e596d7f7bee._comment ./doc/bugs/Problems_running_make_on_osx/comment_2_cc53d1681d576186dbc868dd9801d551._comment ./doc/bugs/Problems_running_make_on_osx/comment_13_88ed095a448096bf8a69015a04e64df1._comment ./doc/bugs/Problems_running_make_on_osx/comment_8_efafa203addf8fa79e33e21a87fb5a2b._comment ./doc/bugs/git-annex_directory_hashing_problems_on_osx.mdwn ./doc/bugs/No_easy_way_to_re-inject_a_file_into_an_annex.mdwn ./doc/bugs/git_annex_unlock_is_not_atomic.mdwn ./doc/bugs/free_space_checking.mdwn ./doc/bugs/wishlist:_more_descriptive_commit_messages_in_git-annex_branch.mdwn ./doc/bugs/Displayed_copy_speed_is_wrong.mdwn ./doc/bugs/git_annex_should_use___39__git_add_-f__39___internally.mdwn ./doc/bugs/minor_bug:_errors_are_not_verbose_enough.mdwn ./doc/bugs/No_version_information_from_cli.mdwn ./doc/bugs/Error_while_adding_a_file___34__createSymbolicLink:_already_exists__34__.mdwn ./doc/bugs/tests_fail_when_there_is_no_global_.gitconfig_for_the_user.mdwn ./doc/bugs/git_annex_initremote_walks_.git-annex.mdwn ./doc/bugs/git-annex_incorrectly_parses_bare_IPv6_addresses.mdwn ./doc/bugs/conflicting_haskell_packages/comment_1_e552a6cc6d7d1882e14130edfc2d6b3b._comment ./doc/bugs/tmp_file_handling.mdwn ./doc/bugs/annex_add_in_annex.mdwn ./doc/bugs/making_annex-merge_try_a_fast-forward.mdwn ./doc/bugs/Makefile_is_missing_dependancies.mdwn ./doc/bugs/test_suite_shouldn__39__t_fail_silently.mdwn ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_4_91439d4dbbf1461e281b276eb0003691._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_6_f360f0006bc9115bc5a3e2eb9fe58abd._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_3_86d9e7244ae492bcbe62720b8c4fc4a9._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_2_cd0123392b16d89db41b45464165c247._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_1_5f60006c9bb095167d817f234a14d20b._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_5_ca33a9ca0df33f7c1b58353d7ffb943d._comment ./doc/bugs/git_rename_detection_on_file_move/comment_3_57010bcaca42089b451ad8659a1e018e._comment ./doc/bugs/git_rename_detection_on_file_move/comment_4_79d96599f757757f34d7b784e6c0e81c._comment ./doc/bugs/git_rename_detection_on_file_move/comment_5_d61f5693d947b9736b29fca1dbc7ad76._comment ./doc/bugs/git_rename_detection_on_file_move/comment_2_7101d07400ad5935f880dc00d89bf90e._comment ./doc/bugs/git_rename_detection_on_file_move/comment_1_0531dcfa833b0321a7009526efe3df33._comment ./doc/bugs/add_range_argument_to___34__git_annex_dropunused__34___.mdwn ./doc/bugs/git_rename_detection_on_file_move.mdwn ./doc/bugs/problem_commit_normal_links.mdwn ./doc/bugs/git_annex_unused_seems_to_check_for_current_path.mdwn ./doc/bugs/Makefile_is_missing_dependancies/comment_6_24119fc5d5963ce9dd669f7dcf006859._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_3_c38b6f4abc9b9ad413c3b83ca04386c3._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_8_a3555e3286cdc2bfeb9cde0ff727ba74._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_2_416f12dbd0c2b841fac8164645b81df5._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_5_0a1c52e2c96d19b9c3eb7e99b8c2434f._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_7_96fd4725df4b54e670077a18d3ac4943._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_1_5a3da5f79c8563c7a450aa29728abe7c._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_4_cc13873175edf191047282700315beee._comment ./doc/bugs/touch.hsc_has_problems_on_non-linux_based_systems.mdwn ./doc/bugs/build_issue_with_latest_release_0.20110522-1-gde817ba.mdwn ./doc/bugs/weird_local_clone_confuses.mdwn ./doc/bugs/fsck_output.mdwn ./doc/bugs/git-annex_has_issues_with_git_when_staging__47__commiting_logs.mdwn ./doc/bugs/git_annex_gets_confused_about_remotes_with_dots_in_their_names.mdwn ./doc/bugs/unannex_vs_unlock_hook_confusion.mdwn ./doc/bugs/Cabal_dependency_monadIO_missing/comment_2_4f4d8e1e00a2a4f7e8a8ab082e16adac._comment ./doc/bugs/Cabal_dependency_monadIO_missing/comment_1_14be660aa57fadec0d81b32a8b52c66f._comment ./doc/bugs/conflicting_haskell_packages.mdwn ./doc/bugs/git_command_line_constructed_by_unannex_command_has_tons_of_redundant_-a_paramters.mdwn ./doc/bugs/backend_version_upgrade_leaves_repo_unusable.mdwn ./doc/bugs/fat_support/comment_3_df3b943bc1081a8f3f7434ae0c8e061e._comment ./doc/bugs/fat_support/comment_4_90a8a15bedd94480945a374f9d706b86._comment ./doc/bugs/fat_support/comment_5_64bbf89de0836673224b83fdefa0407b._comment ./doc/bugs/fat_support/comment_1_04bcc4795d431e8cb32293aab29bbfe2._comment ./doc/bugs/fat_support/comment_2_bb4a97ebadb5c53809fc78431eabd7c8._comment ./doc/bugs/unannex_command_doesn__39__t_all_files.mdwn ./doc/bugs/annex_unannex__47__uninit_should_handle_copies.mdwn ./doc/bugs/git_annex_fsck_is_a_no-op_in_bare_repos.mdwn ./doc/bugs/Unfortunate_interaction_with_Calibre.mdwn ./doc/bugs/error_propigation.mdwn ./doc/bugs/scp_interrupt_to_background.mdwn ./doc/bugs/copy_fast_confusing_with_broken_locationlog.mdwn ./doc/bugs/configure_script_should_detect_uuidgen_instead_of_just_uuid.mdwn ./doc/bugs/check_for_curl_in_configure.hs.mdwn ./doc/bugs/Name_scheme_does_not_follow_git__39__s_rules.mdwn ./doc/bugs/Prevent_accidental_merges.mdwn ./doc/bugs/building_on_lenny.mdwn ./doc/bugs/encrypted_S3_stalls.mdwn ./doc/bugs/support_bare_git_repo__44___with_the_annex_directory_exposed_to_http.mdwn ./doc/bugs/WORM:_Handle_long_filenames_correctly.mdwn ./doc/logo_small.png ./doc/cheatsheet.mdwn ./doc/use_case/Bob.mdwn ./doc/use_case/Alice.mdwn ./doc/upgrades/SHA_size.mdwn ./doc/news/sharebox_a_FUSE_filesystem_for_git-annex.mdwn ./doc/news/version_3.20110902.mdwn ./doc/news/version_3.20110817.mdwn ./doc/news/version_3.20110707.mdwn ./doc/news/LWN_article.mdwn ./doc/news/version_3.20110719.mdwn ./doc/news/version_3.20110819.mdwn ./doc/git-union-merge.mdwn ./doc/special_remotes/S3.mdwn ./doc/special_remotes/web.mdwn ./doc/special_remotes/bup.mdwn ./doc/special_remotes/directory.mdwn ./doc/special_remotes/hook.mdwn ./doc/special_remotes/rsync.mdwn ./doc/users/joey.mdwn ./doc/users/fmarier.mdwn ./doc/users/chrysn.mdwn ./doc/templates/walkthrough.tmpl ./doc/templates/bare.tmpl ./doc/index.mdwn ./doc/summary.mdwn ./doc/forum.mdwn ./doc/encryption/comment_1_1afca8d7182075d46db41f6ad3dd5911._comment ./doc/transferring_data.mdwn ./doc/download.mdwn ./doc/future_proofing.mdwn ./doc/todo/auto_remotes/discussion.mdwn ./doc/todo/file_copy_progress_bar.mdwn ./doc/todo/use_cp_reflink.mdwn ./doc/todo/gitrm.mdwn ./doc/todo/git-annex_unused_eats_memory.mdwn ./doc/todo/add_--exclude_option_to_git_annex_find.mdwn ./doc/todo/tahoe_lfs_for_reals/comment_2_80b9e848edfdc7be21baab7d0cef0e3a._comment ./doc/todo/tahoe_lfs_for_reals/comment_1_0a4793ce6a867638f6e510e71dd4bb44._comment ./doc/todo/object_dir_reorg_v2.mdwn ./doc/todo/S3.mdwn ./doc/todo/auto_remotes.mdwn ./doc/todo/wishlist:_support_for_more_ssh_urls_.mdwn ./doc/todo/support-non-utf8-locales.mdwn ./doc/todo/done.mdwn ./doc/todo/fsck.mdwn ./doc/todo/support_S3_multipart_uploads.mdwn ./doc/todo/wishlist:_Provide_a___34__git_annex__34___command_that_will_skip_duplicates.mdwn ./doc/todo/pushpull.mdwn ./doc/todo/smudge.mdwn ./doc/todo/cache_key_info/comment_1_578df1b3b2cbfdc4aa1805378f35dc48._comment ./doc/todo/object_dir_reorg_v2/comment_3_79bdf9c51dec9f52372ce95b53233bb2._comment ./doc/todo/object_dir_reorg_v2/comment_7_42501404c82ca07147e2cce0cff59474._comment ./doc/todo/object_dir_reorg_v2/comment_5_821c382987f105da72a50e0a5ce61fdc._comment ./doc/todo/object_dir_reorg_v2/comment_1_ba03333dc76ff49eccaba375e68cb525._comment ./doc/todo/object_dir_reorg_v2/comment_2_81276ac309959dc741bc90101c213ab7._comment ./doc/todo/object_dir_reorg_v2/comment_6_8834c3a3f1258c4349d23aff8549bf35._comment ./doc/todo/object_dir_reorg_v2/comment_4_93aada9b1680fed56cc6f0f7c3aca5e5._comment ./doc/todo/union_mounting.mdwn ./doc/todo/wishlist:_swift_backend.mdwn ./doc/todo/wishlist:_swift_backend/comment_1_e6efbb35f61ee521b473a92674036788._comment ./doc/todo/wishlist:_swift_backend/comment_2_5d8c83b0485112e98367b7abaab3f4e3._comment ./doc/todo/git_annex_init_:_include_repo_description_and__47__or_UUID_in_commit_message.mdwn ./doc/todo/wishlist:___34__git_annex_add__34___multiple_processes.mdwn ./doc/todo/hidden_files.mdwn ./doc/todo/smudge/comment_1_4ea616bcdbc9e9a6fae9f2e2795c31c9._comment ./doc/todo/smudge/comment_2_e04b32caa0d2b4c577cdaf382a3ff7f6._comment ./doc/todo/network_remotes.mdwn ./doc/todo/add_a_git_backend.mdwn ./doc/todo/parallel_possibilities/comment_1_d8e34fc2bc4e5cf761574608f970d496._comment ./doc/todo/parallel_possibilities/comment_2_adb76f06a7997abe4559d3169a3181c3._comment ./doc/todo/checkout.mdwn ./doc/todo/immutable_annexed_files.mdwn ./doc/todo/wishlist:_Prevent_repeated_password_prompts_for_one_command.mdwn ./doc/todo/git-annex-shell.mdwn ./doc/todo/speed_up_fsck.mdwn ./doc/todo/tahoe_lfs_for_reals.mdwn ./doc/todo/using_url_backend.mdwn ./doc/todo/branching.mdwn ./doc/todo/rsync.mdwn ./doc/todo/backendSHA1.mdwn ./doc/todo/cache_key_info.mdwn ./doc/todo/parallel_possibilities.mdwn ./doc/todo/symlink_farming_commit_hook.mdwn ./doc/feeds.mdwn ./doc/copies.mdwn ./doc/repomap.png ./doc/special_remotes.mdwn ./doc/todo.mdwn ./doc/walkthrough/using_ssh_remotes.mdwn ./doc/walkthrough/untrusted_repositories.mdwn ./doc/walkthrough/moving_file_content_between_repositories.mdwn ./doc/walkthrough/modifying_annexed_files.mdwn ./doc/walkthrough/Internet_Archive_via_S3.mdwn ./doc/walkthrough/more.mdwn ./doc/walkthrough/removing_files.mdwn ./doc/walkthrough/creating_a_repository.mdwn ./doc/walkthrough/what_to_do_when_you_lose_a_repository.mdwn ./doc/walkthrough/using_Amazon_S3.mdwn ./doc/walkthrough/adding_a_remote.mdwn ./doc/walkthrough/adding_a_remote/comment_1_0a59355bd33a796aec97173607e6adc9._comment ./doc/walkthrough/adding_a_remote/comment_2_f8cd79ef1593a8181a7f1086a87713e8._comment ./doc/walkthrough/adding_a_remote/comment_3_60691af4400521b5a8c8d75efe3b44cb._comment ./doc/walkthrough/adding_a_remote/comment_4_6f7cf5c330272c96b3abeb6612075c9d._comment ./doc/walkthrough/removing_files:_When_things_go_wrong.mdwn ./doc/walkthrough/renaming_files.mdwn ./doc/walkthrough/using_bup.mdwn ./doc/walkthrough/fsck:_when_things_go_wrong.mdwn ./doc/walkthrough/adding_files.mdwn ./doc/walkthrough/unused_data.mdwn ./doc/walkthrough/fsck:_verifying_your_data.mdwn ./doc/walkthrough/getting_file_content.mdwn ./doc/walkthrough/using_the_web.mdwn ./doc/walkthrough/recover_data_from_lost+found.mdwn ./doc/walkthrough/backups.mdwn ./doc/walkthrough/transferring_files:_When_things_go_wrong.mdwn ./doc/walkthrough/using_the_SHA1_backend.mdwn ./doc/walkthrough/migrating_data_to_a_new_backend.mdwn ./doc/GPL ./doc/logo.png ./doc/location_tracking.mdwn ./doc/design.mdwn ./doc/news.mdwn ./doc/encryption.mdwn ./doc/not.mdwn ./doc/download/comment_7_a5eebd214b135f34b18274a682211943._comment ./doc/download/comment_3_cf6044ebe99f71158034e21197228abd._comment ./doc/download/comment_5_c6b1bc40226fc2c8ba3e558150856992._comment ./doc/download/comment_6_3a52993d3553deb9a413debec9a5f92d._comment ./doc/download/comment_2_f85f72b33aedc3425f0c0c47867d02f3._comment ./doc/download/comment_8_59a976de6c7d333709b92f7cd5830850._comment ./doc/download/comment_4_10fc013865c7542c2ed9d6c0963bb391._comment ./doc/download/comment_1_fbd8b6d39e9d3c71791551358c863966._comment ./doc/contact.mdwn ./doc/trust.mdwn ./doc/bare_repositories.mdwn ./doc/distributed_version_control.mdwn ./doc/install.mdwn ./doc/git-annex-shell.mdwn ./doc/bugs.mdwn ./doc/design/encryption/comment_2_a610b3d056a059899178859a3a821ea5._comment ./doc/design/encryption/comment_3_cca186a9536cd3f6e86994631b14231c._comment ./doc/design/encryption/comment_1_4715ffafb3c4a9915bc33f2b26aaa9c1._comment ./doc/design/encryption/comment_4_8f3ba3e504b058791fc6e6f9c38154cf._comment ./doc/design/encryption.mdwn ./doc/comments.mdwn ./doc/git-annex.mdwn ./doc/internals.mdwn ./doc/users.mdwn ./doc/install/Ubuntu.mdwn ./doc/install/Debian.mdwn ./doc/install/OSX.mdwn ./doc/install/FreeBSD.mdwn ./doc/install/OSX/comment_1_0a1760bf0db1f1ba89bdb4c62032f631._comment ./doc/install/comment_3_cff163ea3e7cad926f4ed9e78b896598._comment ./doc/install/Debian/comment_2_648e3467e260cdf233acdb0b53313ce0._comment ./doc/install/Debian/comment_1_029486088d098c2d4f1099f2f0e701a9._comment ./doc/install/Fedora.mdwn ./doc/install/comment_4_82a17eee4a076c6c79fddeda347e0c9a._comment ./doc/walkthrough.mdwn ./CmdLine.hs ./UUID.hs ./Messages.hs ./Setup.hs ./GPL ./mdwn2man ./Remote.hs ./git-annex.hs ./Locations.hs ./Backend/WORM.hs ./Backend/URL.hs ./Backend/SHA.hs ./Options.hs ./git-annex-shell.hs ./test.hs ./INSTALL ./Remote/Web.hs ./Remote/S3stub.hs ./Remote/Helper/Encryptable.hs ./Remote/Helper/Special.hs ./Remote/Directory.hs ./Remote/S3real.hs ./Remote/Bup.hs ./Remote/Hook.hs ./Remote/Rsync.hs ./Remote/Git.hs ./Utility.hs ./Build/TestConfig.hs ./Upgrade.hs ./git-union-merge.hs ./Utility/Path.hs ./Utility/Url.hs ./Utility/DataUnits.hs ./Utility/StatFS.hsc ./Utility/Conditional.hs ./Utility/Ssh.hs ./Utility/RsyncFile.hs ./Utility/CopyFile.hs ./Utility/SafeCommand.hs ./Utility/JSONStream.hs ./Utility/Dot.hs ./Utility/Touch.hsc ./Utility/Base64.hs ./Types/Crypto.hs ./Types/UUID.hs ./Types/TrustLevel.hs ./Types/Remote.hs ./Types/BranchState.hs ./Types/Backend.hs ./Types/Key.hs ./Config.hs ./Command.hs ./.gitignore ./Backend.hs ./Content.hs ./.gitattributes ./Git.hs ./GitAnnex.hs Homepage: http://git-annex.branchable.com/ Build-type: Custom Category: Utility
test.hs view
@@ -24,11 +24,12 @@ import System.Path (recurseDir) import System.IO.HVFS (SystemFS(..)) +import Utility.SafeCommand+ import qualified Annex import qualified Backend import qualified Git import qualified Locations-import qualified Utility import qualified Types.Backend import qualified Types import qualified GitAnnex@@ -42,6 +43,7 @@ import qualified Types.Key import qualified Config import qualified Crypto+import qualified Utility.Path -- for quickcheck instance Arbitrary Types.Key.Key where@@ -72,11 +74,12 @@ [ qctest "prop_idempotent_deencode" Git.prop_idempotent_deencode , qctest "prop_idempotent_fileKey" Locations.prop_idempotent_fileKey , qctest "prop_idempotent_key_read_show" Types.Key.prop_idempotent_key_read_show- , qctest "prop_idempotent_shellEscape" Utility.prop_idempotent_shellEscape- , qctest "prop_idempotent_shellEscape_multiword" Utility.prop_idempotent_shellEscape_multiword+ , qctest "prop_idempotent_shellEscape" Utility.SafeCommand.prop_idempotent_shellEscape+ , qctest "prop_idempotent_shellEscape_multiword" Utility.SafeCommand.prop_idempotent_shellEscape_multiword , qctest "prop_idempotent_configEscape" RemoteLog.prop_idempotent_configEscape- , qctest "prop_parentDir_basics" Utility.prop_parentDir_basics- , qctest "prop_relPathDirToFile_basics" Utility.prop_relPathDirToFile_basics+ , qctest "prop_parentDir_basics" Utility.Path.prop_parentDir_basics++ , qctest "prop_relPathDirToFile_basics" Utility.Path.prop_relPathDirToFile_basics , qctest "prop_cost_sane" Config.prop_cost_sane , qctest "prop_hmacWithCipher_sane" Crypto.prop_hmacWithCipher_sane ]@@ -117,8 +120,8 @@ git_annex "add" ["-q", annexedfile] @? "add failed" annexed_present annexedfile writeFile ingitfile $ content ingitfile- Utility.boolSystem "git" [Utility.Param "add", Utility.File ingitfile] @? "git add failed"- Utility.boolSystem "git" [Utility.Params "commit -q -a -m commit"] @? "git commit failed"+ boolSystem "git" [Param "add", File ingitfile] @? "git add failed"+ boolSystem "git" [Params "commit -q -a -m commit"] @? "git commit failed" git_annex "add" ["-q", ingitfile] @? "add ingitfile should be no-op" unannexed ingitfile sha1dup = TestCase $ intmpclonerepo $ do@@ -145,7 +148,7 @@ let key = show $ fromJust r git_annex "setkey" ["-q", "--key", key, tmp] @? "setkey failed" git_annex "fromkey" ["-q", "--key", key, sha1annexedfile] @? "fromkey failed"- Utility.boolSystem "git" [Utility.Params "commit -q -a -m commit"] @? "git commit failed"+ boolSystem "git" [Params "commit -q -a -m commit"] @? "git commit failed" annexed_present sha1annexedfile where tmp = "tmpfile"@@ -172,7 +175,7 @@ where noremote = "no remotes" ~: TestCase $ intmpclonerepo $ do git_annex "get" ["-q", annexedfile] @? "get failed"- Utility.boolSystem "git" [Utility.Params "remote rm origin"]+ boolSystem "git" [Params "remote rm origin"] @? "git remote rm origin failed" r <- git_annex "drop" ["-q", annexedfile] not r @? "drop wrongly succeeded with no known copy of file"@@ -303,12 +306,12 @@ then do -- pre-commit depends on the file being -- staged, normally git commit does this- Utility.boolSystem "git" [Utility.Param "add", Utility.File annexedfile]+ boolSystem "git" [Param "add", File annexedfile] @? "git add of edited file failed" git_annex "pre-commit" ["-q"] @? "pre-commit failed" else do- Utility.boolSystem "git" [Utility.Params "commit -q -a -m contentchanged"]+ boolSystem "git" [Params "commit -q -a -m contentchanged"] @? "git commit of edited file failed" runchecks [checklink, checkunwritable] annexedfile c <- readFile annexedfile@@ -326,7 +329,7 @@ git_annex "fix" ["-q", annexedfile] @? "fix of present file failed" annexed_present annexedfile createDirectory subdir- Utility.boolSystem "git" [Utility.Param "mv", Utility.File annexedfile, Utility.File subdir]+ boolSystem "git" [Param "mv", File annexedfile, File subdir] @? "git mv failed" git_annex "fix" ["-q", newfile] @? "fix of moved file failed" runchecks [checklink, checkunwritable] newfile@@ -364,9 +367,9 @@ where basicfsck = TestCase $ intmpclonerepo $ do git_annex "fsck" ["-q"] @? "fsck failed"- Utility.boolSystem "git" [Utility.Params "config annex.numcopies 2"] @? "git config failed"+ boolSystem "git" [Params "config annex.numcopies 2"] @? "git config failed" fsck_should_fail "numcopies unsatisfied"- Utility.boolSystem "git" [Utility.Params "config annex.numcopies 1"] @? "git config failed"+ boolSystem "git" [Params "config annex.numcopies 1"] @? "git config failed" corrupt annexedfile corrupt sha1annexedfile withlocaluntrusted = TestCase $ intmpclonerepo $ do@@ -377,7 +380,7 @@ git_annex "trust" ["-q", "."] @? "trust of current repo failed" git_annex "fsck" ["-q", annexedfile] @? "fsck failed on file present in trusted repo" withremoteuntrusted = TestCase $ intmpclonerepo $ do- Utility.boolSystem "git" [Utility.Params "config annex.numcopies 2"] @? "git config failed"+ boolSystem "git" [Params "config annex.numcopies 2"] @? "git config failed" git_annex "get" ["-q", annexedfile] @? "get failed" git_annex "get" ["-q", sha1annexedfile] @? "get failed" git_annex "fsck" ["-q"] @? "fsck failed with numcopies=2 and 2 copies"@@ -448,9 +451,9 @@ git_annex "get" ["-q", annexedfile] @? "get of file failed" git_annex "get" ["-q", sha1annexedfile] @? "get of file failed" checkunused []- Utility.boolSystem "git" [Utility.Params "rm -q", Utility.File annexedfile] @? "git rm failed"+ boolSystem "git" [Params "rm -q", File annexedfile] @? "git rm failed" checkunused [annexedfilekey]- Utility.boolSystem "git" [Utility.Params "rm -q", Utility.File sha1annexedfile] @? "git rm failed"+ boolSystem "git" [Params "rm -q", File sha1annexedfile] @? "git rm failed" checkunused [annexedfilekey, sha1annexedfilekey] -- good opportunity to test dropkey also@@ -526,10 +529,10 @@ setuprepo dir = do cleanup dir ensuretmpdir- Utility.boolSystem "git" [Utility.Params "init -q", Utility.File dir] @? "git init failed"+ boolSystem "git" [Params "init -q", File dir] @? "git init failed" indir dir $ do- Utility.boolSystem "git" [Utility.Params "config user.name", Utility.Param "Test User"] @? "git config failed"- Utility.boolSystem "git" [Utility.Params "config user.email test@example.com"] @? "git config failed"+ boolSystem "git" [Params "config user.name", Param "Test User"] @? "git config failed"+ boolSystem "git" [Params "config user.email test@example.com"] @? "git config failed" return dir -- clones are always done as local clones; we cannot test ssh clones@@ -537,7 +540,7 @@ clonerepo old new = do cleanup new ensuretmpdir- Utility.boolSystem "git" [Utility.Params "clone -q", Utility.File old, Utility.File new] @? "git clone failed"+ boolSystem "git" [Params "clone -q", File old, File new] @? "git clone failed" indir new $ git_annex "init" ["-q", new] @? "git annex init failed" return new @@ -643,6 +646,9 @@ p <- getEnvDefault "PATH" "" setEnv "PATH" (cwd ++ ":" ++ p) True setEnv "TOPDIR" cwd True+ -- Avoid git complaining if it cannot determine the user's email+ -- address.+ setEnv "EMAIL" "git-annex test <test@example.com>" True changeToTmpDir :: FilePath -> IO () changeToTmpDir t = do