git-annex 3.20111011 → 3.20111122
raw patch · 229 files changed
+5150/−2680 lines, 229 files
Files
- Annex.hs +18/−7
- Annex/Branch.hs +144/−81
- Annex/CatFile.hs +14/−7
- Annex/Content.hs +73/−26
- Annex/Queue.hs +1/−2
- Annex/Ssh.hs +66/−0
- Annex/UUID.hs +73/−0
- Annex/Version.hs +4/−6
- Backend.hs +9/−14
- Backend/SHA.hs +4/−5
- CHANGELOG +90/−0
- Checks.hs +42/−0
- CmdLine.hs +50/−50
- Command.hs +49/−181
- Command/Add.hs +13/−14
- Command/AddUrl.hs +12/−17
- Command/ConfigList.hs +5/−5
- Command/Copy.hs +8/−7
- Command/Describe.hs +6/−10
- Command/Drop.hs +97/−44
- Command/DropKey.hs +12/−10
- Command/DropUnused.hs +11/−14
- Command/Find.hs +11/−7
- Command/Fix.hs +6/−6
- Command/FromKey.hs +11/−11
- Command/Fsck.hs +72/−44
- Command/Get.hs +15/−9
- Command/InAnnex.hs +9/−8
- Command/Init.hs +4/−7
- Command/InitRemote.hs +13/−12
- Command/Lock.hs +2/−2
- Command/Map.hs +22/−14
- Command/Merge.hs +2/−2
- Command/Migrate.hs +34/−22
- Command/Move.hs +56/−59
- Command/PreCommit.hs +5/−4
- Command/RecvKey.hs +2/−2
- Command/Reinject.hs +51/−0
- Command/Semitrust.hs +3/−4
- Command/SendKey.hs +3/−4
- Command/SetKey.hs +0/−47
- Command/Status.hs +63/−38
- Command/Trust.hs +3/−5
- Command/Unannex.hs +15/−20
- Command/Uninit.hs +22/−11
- Command/Unlock.hs +11/−10
- Command/Untrust.hs +3/−4
- Command/Unused.hs +19/−29
- Command/Upgrade.hs +3/−3
- Command/Version.hs +12/−7
- Command/Whereis.hs +9/−11
- Common.hs +21/−42
- Common/Annex.hs +7/−12
- Config.hs +10/−17
- Crypto.hs +7/−3
- Git.hs +98/−62
- Git/CatFile.hs +25/−15
- Git/LsFiles.hs +22/−21
- Git/LsTree.hs +4/−6
- Git/Queue.hs +4/−4
- Git/UnionMerge.hs +65/−48
- GitAnnex.hs +40/−42
- Init.hs +10/−10
- Limit.hs +3/−3
- LocationLog.hs +0/−58
- Locations.hs +7/−7
- Logs/Location.hs +66/−0
- Logs/Presence.hs +104/−0
- Logs/Remote.hs +87/−0
- Logs/Trust.hs +84/−0
- Logs/UUID.hs +89/−0
- Logs/UUIDBased.hs +110/−0
- Logs/Web.hs +52/−0
- Makefile +14/−1
- Messages.hs +36/−16
- Messages/JSON.hs +6/−2
- Options.hs +36/−3
- PresenceLog.hs +0/−124
- Remote.hs +41/−26
- Remote/Bup.hs +17/−16
- Remote/Directory.hs +8/−12
- Remote/Git.hs +62/−31
- Remote/Helper/Special.hs +8/−9
- Remote/Hook.hs +7/−10
- Remote/Rsync.hs +14/−13
- Remote/S3real.hs +6/−8
- Remote/Web.hs +8/−41
- RemoteLog.hs +0/−88
- Seek.hs +115/−0
- Trust.hs +0/−70
- Types.hs +3/−1
- Types/Command.hs +46/−0
- Types/Crypto.hs +0/−8
- Types/Remote.hs +6/−6
- Types/TrustLevel.hs +0/−10
- Types/UUID.hs +11/−2
- UUID.hs +0/−95
- UUIDLog.hs +0/−110
- Upgrade/V0.hs +1/−2
- Upgrade/V1.hs +16/−19
- Upgrade/V2.hs +24/−24
- Utility.hs +0/−106
- Utility/DataUnits.hs +1/−1
- Utility/Misc.hs +47/−0
- Utility/Monad.hs +26/−0
- Utility/Path.hs +22/−0
- Utility/RsyncFile.hs +19/−1
- Utility/SafeCommand.hs +15/−5
- Utility/Ssh.hs +0/−67
- Utility/TempFile.hs +39/−0
- Utility/Url.hs +5/−9
- configure.hs +24/−0
- debian/NEWS +1/−1
- debian/changelog +90/−0
- doc/backends.mdwn +18/−14
- doc/bare_repositories.mdwn +32/−10
- doc/bugs/Cabal_dependency_monadIO_missing.mdwn +3/−0
- doc/bugs/Error_when_moving_annexed_file_to_a_.gitignored_location.mdwn +21/−0
- doc/bugs/Prevent_accidental_merges/comment_1_4c46a193915eab8f308a04175cb2e40a._comment +8/−0
- doc/bugs/Trouble_initializing_git_annex_on_NFS.mdwn +16/−0
- doc/bugs/copy_fast_confusing_with_broken_locationlog.mdwn +1/−1
- doc/bugs/cyclic_drop.mdwn +104/−0
- doc/bugs/directory_remote_and_case_sensitivity_on_FAT.mdwn +28/−0
- doc/bugs/dropping_files_with_a_URL_backend_fails.mdwn +1/−1
- doc/bugs/extraneous_shell_escaping_for_rsync_remotes.mdwn +15/−0
- doc/bugs/fails_to_handle_lot_of_files.mdwn +445/−0
- doc/bugs/fails_to_handle_lot_of_files/comment_1_09d8e4e66d8273fab611bd29e82dc7fc._comment +8/−0
- doc/bugs/fails_to_handle_lot_of_files/comment_2_fd2ec05f4b5a7a6ae6bd9f5dbc3156de._comment +8/−0
- doc/bugs/fat_support.mdwn +0/−2
- doc/bugs/fsck_claims_failed_checksum_when_less_copies_than_required_are_found.mdwn +57/−0
- doc/bugs/git_annex_fsck_is_a_no-op_in_bare_repos.mdwn +2/−0
- doc/bugs/git_annex_initremote_walks_.git-annex.mdwn +1/−1
- doc/bugs/git_annex_map_has_problems_with_urls_containing___126__.mdwn +46/−0
- doc/bugs/git_annex_migrate_leaves_old_backend_versions_around.mdwn +1/−1
- doc/bugs/git_annex_upgrade_output_is_inconsistent_and_spammy.mdwn +15/−0
- doc/bugs/git_annex_version_should_without_being_in_a_repo_.mdwn +7/−0
- doc/bugs/interrupting_migration_causes_problems.mdwn +52/−0
- doc/bugs/making_annex-merge_try_a_fast-forward.mdwn +6/−0
- doc/bugs/minor_bug:_errors_are_not_verbose_enough.mdwn +2/−0
- doc/bugs/old_data_isn__39__t_unused_after_migration.mdwn +66/−0
- doc/bugs/uninit_does_not_work_in_old_repos.mdwn +20/−0
- doc/bugs/uninit_does_not_work_in_old_repos/comment_1_bc0619c6e17139df74639448aa6a0f72._comment +8/−0
- doc/bugs/uninit_should_not_run_when_branch_git-annex_is_checked_out.mdwn +15/−0
- doc/bugs/wishlist:_allow_users_to_provide_UUID_when_running___96__git_annex_init__96__.mdwn +5/−0
- doc/bugs/wishlist:_more_descriptive_commit_messages_in_git-annex_branch.mdwn +3/−0
- doc/bugs/wishlist:_query_things_like_description__44___trust_level.mdwn +4/−0
- doc/bugs/wishlist:_support_drop__44___find_on_special_remotes.mdwn +18/−0
- doc/cheatsheet.mdwn +0/−16
- doc/design/encryption.mdwn +1/−1
- doc/distributed_version_control.mdwn +8/−0
- doc/forum/--print0_option_as_in___34__find__34__.mdwn +5/−0
- doc/forum/Recommended_number_of_repositories.mdwn +4/−0
- doc/forum/Recommended_number_of_repositories/comment_1_3ef256230756be8a9679b107cdbfd018._comment +15/−0
- doc/forum/confusion_with_remotes__44___map.mdwn +113/−0
- doc/forum/confusion_with_remotes__44___map/comment_1_a38ded23b7f288292a843abcb1a56f38._comment +10/−0
- doc/forum/confusion_with_remotes__44___map/comment_2_cd1c98b1276444e859a22c3dbd6f2a79._comment +8/−0
- doc/forum/confusion_with_remotes__44___map/comment_3_18531754089c991b6caefc57a5c17fe9._comment +24/−0
- doc/forum/confusion_with_remotes__44___map/comment_4_3b89b6d1518267fcbc050c9de038b9ca._comment +11/−0
- doc/forum/confusion_with_remotes__44___map/comment_5_27801584325d259fa490f67273f2ff71._comment +16/−0
- doc/forum/confusion_with_remotes__44___map/comment_6_496b0d9b86869bbac3a1356d53a3dda4._comment +8/−0
- doc/forum/confusion_with_remotes__44___map/comment_7_9a456f61f956a3d5e81e723d5a90794c._comment +27/−0
- doc/forum/getting_git_annex_to_do_a_force_copy_to_a_remote.mdwn +3/−1
- doc/forum/git_annex_ls___47___metadata_in_git_annex_whereis.mdwn +1/−0
- doc/forum/git_tag_missing_for_3.20111011.mdwn +1/−0
- doc/forum/git_tag_missing_for_3.20111011/comment_1_7a53bf273f3078ab3351369ef2b5f2a6._comment +8/−0
- doc/forum/wishlist:_special_remote_for_sftp_or_rsync.mdwn +3/−3
- doc/forum/wishlist:_support_for_more_ssh_urls_.mdwn +0/−6
- doc/future_proofing.mdwn +1/−1
- doc/git-annex-shell.mdwn +20/−4
- doc/git-annex.mdwn +47/−32
- doc/git-union-merge.mdwn +1/−1
- doc/index.mdwn +1/−0
- doc/install/Ubuntu.mdwn +7/−1
- doc/meta.mdwn +5/−0
- doc/news/version_3.20110902.mdwn +0/−9
- doc/news/version_3.20110906.mdwn +0/−4
- doc/news/version_3.20110915.mdwn +0/−10
- doc/news/version_3.20110928.mdwn +0/−19
- doc/news/version_3.20111011.mdwn +0/−30
- doc/news/version_3.20111025.mdwn +8/−0
- doc/news/version_3.20111105.mdwn +27/−0
- doc/news/version_3.20111107.mdwn +8/−0
- doc/news/version_3.20111111.mdwn +10/−0
- doc/news/version_3.20111122.mdwn +22/−0
- doc/not.mdwn +1/−1
- doc/special_remotes/S3.mdwn +2/−2
- doc/special_remotes/web.mdwn +1/−1
- doc/tips.mdwn +4/−0
- doc/tips/Internet_Archive_via_S3.mdwn +49/−0
- doc/tips/automatically_getting_files_on_checkout.mdwn +15/−0
- doc/tips/centralized_git_repository_tutorial.mdwn +140/−0
- doc/tips/migrating_data_to_a_new_backend.mdwn +16/−0
- doc/tips/powerful_file_matching.mdwn +36/−0
- doc/tips/recover_data_from_lost+found.mdwn +19/−0
- doc/tips/untrusted_repositories.mdwn +28/−0
- doc/tips/using_Amazon_S3.mdwn +37/−0
- doc/tips/using_gitolite_with_git-annex.mdwn +77/−0
- doc/tips/using_the_SHA1_backend.mdwn +11/−0
- doc/tips/using_the_web_as_a_special_remote.mdwn +32/−0
- doc/tips/what_to_do_when_a_repository_is_corrupted.mdwn +22/−0
- doc/tips/what_to_do_when_you_lose_a_repository.mdwn +19/−0
- doc/todo/auto_remotes.mdwn +6/−0
- doc/todo/avoid_unnecessary_union_merges.mdwn +20/−0
- doc/todo/cache_key_info.mdwn +4/−3
- doc/todo/git-annex_unused_eats_memory.mdwn +4/−2
- doc/todo/gitolite_and_gitosis_support.mdwn +39/−0
- doc/todo/optimise_git-annex_merge.mdwn +23/−0
- doc/todo/smudge.mdwn +4/−0
- doc/todo/support_fsck_in_bare_repos.mdwn +17/−0
- doc/todo/wishlist:___34__git_annex_add__34___multiple_processes.mdwn +3/−0
- doc/upgrades/SHA_size.mdwn +1/−1
- doc/walkthrough/Internet_Archive_via_S3.mdwn +0/−49
- doc/walkthrough/adding_files.mdwn +2/−2
- doc/walkthrough/migrating_data_to_a_new_backend.mdwn +0/−16
- doc/walkthrough/more.mdwn +1/−2
- doc/walkthrough/moving_file_content_between_repositories.mdwn +1/−1
- doc/walkthrough/powerful_file_matching.mdwn +0/−36
- doc/walkthrough/recover_data_from_lost+found.mdwn +0/−19
- doc/walkthrough/untrusted_repositories.mdwn +0/−28
- doc/walkthrough/unused_data.mdwn +7/−7
- doc/walkthrough/using_Amazon_S3.mdwn +0/−37
- doc/walkthrough/using_ssh_remotes.mdwn +1/−1
- doc/walkthrough/using_the_SHA1_backend.mdwn +0/−11
- doc/walkthrough/using_the_web.mdwn +0/−32
- doc/walkthrough/what_to_do_when_you_lose_a_repository.mdwn +0/−19
- git-annex-shell.hs +45/−20
- git-annex.cabal +2/−2
- git-union-merge.hs +3/−3
- test.hs +28/−28
Annex.hs view
@@ -1,6 +1,6 @@ {- git-annex monad -- - Copyright 2010 Joey Hess <joey@kitenet.net>+ - Copyright 2010-2011 Joey Hess <joey@kitenet.net> - - Licensed under the GNU GPL version 3 or higher. -}@@ -12,11 +12,14 @@ AnnexState(..), OutputType(..), new,+ newState, run, eval, getState, changeState,- gitRepo+ gitRepo,+ inRepo,+ fromRepo, ) where import Control.Monad.IO.Control@@ -45,6 +48,8 @@ Applicative ) +data OutputType = NormalOutput | QuietOutput | JSONOutput+ -- internal state storage data AnnexState = AnnexState { repo :: Git.Repo@@ -55,11 +60,11 @@ , force :: Bool , fast :: Bool , auto :: Bool+ , print0 :: Bool , branchstate :: BranchState , catfilehandle :: Maybe CatFileHandle , forcebackend :: Maybe String , forcenumcopies :: Maybe Int- , defaultkey :: Maybe String , toremote :: Maybe String , fromremote :: Maybe String , limit :: Either [Utility.Matcher.Token (FilePath -> Annex Bool)] (Utility.Matcher.Matcher (FilePath -> Annex Bool))@@ -68,8 +73,6 @@ , cipher :: Maybe Cipher } -data OutputType = NormalOutput | QuietOutput | JSONOutput- newState :: Git.Repo -> AnnexState newState gitrepo = AnnexState { repo = gitrepo@@ -80,11 +83,11 @@ , force = False , fast = False , auto = False+ , print0 = False , branchstate = startBranchState , catfilehandle = Nothing , forcebackend = Nothing , forcenumcopies = Nothing- , defaultkey = Nothing , toremote = Nothing , fromremote = Nothing , limit = Left []@@ -115,6 +118,14 @@ changeState :: (AnnexState -> AnnexState) -> Annex () changeState = modify -{- Returns the git repository being acted on -}+{- Returns the annex's git repository. -} gitRepo :: Annex Git.Repo gitRepo = getState repo++{- Runs an IO action in the annex's git repository. -}+inRepo :: (Git.Repo -> IO a) -> Annex a+inRepo a = liftIO . a =<< gitRepo++{- Extracts a value from the annex's git repisitory. -}+fromRepo :: (Git.Repo -> a) -> Annex a+fromRepo a = a <$> gitRepo
Annex/Branch.hs view
@@ -8,6 +8,7 @@ module Annex.Branch ( create, update,+ disableUpdate, get, change, commit,@@ -30,19 +31,17 @@ import qualified Annex import Annex.CatFile -type GitRef = String- {- Name of the branch that is used to store git-annex's information. -}-name :: GitRef-name = "git-annex"+name :: Git.Ref+name = Git.Ref "git-annex" {- Fully qualified name of the branch. -}-fullname :: GitRef-fullname = "refs/heads/" ++ name+fullname :: Git.Ref+fullname = Git.Ref $ "refs/heads/" ++ show name {- Branch's name in origin. -}-originname :: GitRef-originname = "origin/" ++ name+originname :: Git.Ref+originname = Git.Ref $ "origin/" ++ show name {- A separate index file for the branch. -} index :: Git.Repo -> FilePath@@ -55,21 +54,20 @@ - and merge in changes from other branches. -} genIndex :: Git.Repo -> IO ()-genIndex g = Git.UnionMerge.ls_tree g fullname >>= Git.UnionMerge.update_index g+genIndex g = Git.UnionMerge.stream_update_index g+ [Git.UnionMerge.ls_tree fullname g] {- Runs an action using the branch's index file. -} withIndex :: Annex a -> Annex a withIndex = withIndex' False withIndex' :: Bool -> Annex a -> Annex a withIndex' bootstrapping a = do- g <- gitRepo- let f = index g-+ f <- fromRepo index bracketIO (Git.useIndex f) id $ do unlessM (liftIO $ doesFileExist f) $ do unless bootstrapping create liftIO $ createDirectoryIfMissing True $ takeDirectory f- unless bootstrapping $ liftIO $ genIndex g+ unless bootstrapping $ inRepo genIndex a withIndexUpdate :: Annex a -> Annex a@@ -102,81 +100,130 @@ {- Creates the branch, if it does not already exist. -} create :: Annex () create = unlessM hasBranch $ do- g <- gitRepo e <- hasOrigin if e- then liftIO $ Git.run g "branch" [Param name, Param originname]+ then inRepo $ Git.run "branch"+ [Param $ show name, Param $ show originname] else withIndex' True $- liftIO $ Git.commit g "branch created" fullname []+ inRepo $ Git.commit "branch created" fullname [] {- Stages the journal, and commits staged changes to the branch. -} commit :: String -> Annex () commit message = whenM journalDirty $ lockJournal $ do stageJournalFiles- g <- gitRepo- withIndex $ liftIO $ Git.commit g message fullname [fullname]+ withIndex $ inRepo $ Git.commit message fullname [fullname] -{- Ensures that the branch is up-to-date; should be called before- - data is read from it. Runs only once per git-annex run.+{- Ensures that the branch is up-to-date; should be called before data is+ - read from it. Runs only once per git-annex run. -- - Before refs are merged into the index, it's- - important to first stage the journal into the- - index. Otherwise, any changes in the journal- - would later get staged, and might overwrite- - changes made during the merge.+ - Before refs are merged into the index, it's important to first stage the+ - journal into the index. Otherwise, any changes in the journal would+ - later get staged, and might overwrite changes made during the merge. -- - It would be cleaner to handle the merge by- - updating the journal, not the index, with changes- - from the branches.+ - (It would be cleaner to handle the merge by updating the journal, not the+ - index, with changes from the branches.)+ -+ - The index is always updated using a union merge, as that's the most+ - efficient way to update it. However, if the branch can be+ - fast-forwarded, that is then done, rather than adding an unnecessary+ - commit to it. -} update :: Annex () update = onceonly $ do+ -- ensure branch exists+ create -- check what needs updating before taking the lock dirty <- journalDirty- c <- filterM changedbranch =<< siblingBranches+ c <- filterM (changedBranch name . snd) =<< siblingBranches let (refs, branches) = unzip c unless (not dirty && null refs) $ withIndex $ lockJournal $ do- when dirty $ stageJournalFiles- g <- gitRepo+ when dirty stageJournalFiles+ let merge_desc = if null branches+ then "update" + else "merging " +++ (unwords $ map (show . Git.refDescribe) branches) ++ + " into " ++ show name unless (null branches) $ do- showSideAction $ "merging " ++- (unwords $ map Git.refDescribe branches) ++- " into " ++ name+ showSideAction merge_desc {- Note: This merges the branches into the index. - Any unstaged changes in the git-annex branch - (if it's checked out) will be removed. So, - documentation advises users not to directly - modify the branch. -}- liftIO $ Git.UnionMerge.merge_index g branches- liftIO $ Git.commit g "update" fullname (nub $ fullname:refs)+ h <- catFileHandle+ inRepo $ \g -> Git.UnionMerge.merge_index h g branches+ ff <- if dirty then return False else tryFastForwardTo refs+ unless ff $ inRepo $+ Git.commit merge_desc fullname (nub $ fullname:refs) invalidateCache where- changedbranch (_, branch) = do- g <- gitRepo- -- checking with log to see if there have been changes- -- is less expensive than always merging- diffs <- liftIO $ Git.pipeRead g [- Param "log",- Param (name ++ ".." ++ branch),- Params "--oneline -n1"- ]- return $ not $ L.null diffs onceonly a = unlessM (branchUpdated <$> getState) $ do r <- a- Annex.changeState setupdated+ disableUpdate return r++{- Checks if the second branch has any commits not present on the first+ - branch. -}+changedBranch :: Git.Branch -> Git.Branch -> Annex Bool+changedBranch origbranch newbranch = not . L.null <$> diffs+ where+ diffs = inRepo $ Git.pipeRead+ [ Param "log"+ , Param (show origbranch ++ ".." ++ show newbranch)+ , Params "--oneline -n1"+ ]++{- Given a set of refs that are all known to have commits not+ - on the git-annex branch, tries to update the branch by a+ - fast-forward.+ -+ - In order for that to be possible, one of the refs must contain+ - every commit present in all the other refs, as well as in the+ - git-annex branch.+ -}+tryFastForwardTo :: [Git.Ref] -> Annex Bool+tryFastForwardTo [] = return True+tryFastForwardTo (first:rest) = do+ -- First, check that the git-annex branch does not contain any+ -- new commits that are in the first other branch. If it does,+ -- cannot fast-forward.+ diverged <- changedBranch first fullname+ if diverged+ then no_ff+ else maybe no_ff do_ff =<< findbest first rest+ where+ no_ff = return False+ do_ff branch = do+ inRepo $ Git.run "update-ref" [Param $ show fullname, Param $ show branch]+ return True+ findbest c [] = return $ Just c+ findbest c (r:rs)+ | c == r = findbest c rs+ | otherwise = do+ better <- changedBranch c r+ worse <- changedBranch r c+ case (better, worse) of+ (True, True) -> return Nothing -- divergent fail+ (True, False) -> findbest r rs -- better+ (False, True) -> findbest c rs -- worse+ (False, False) -> findbest c rs -- same+ +{- Avoids updating the branch. A useful optimisation when the branch+ - is known to have not changed, or git-annex won't be relying on info+ - from it. -}+disableUpdate :: Annex ()+disableUpdate = Annex.changeState setupdated+ where setupdated s = s { Annex.branchstate = new } where new = old { branchUpdated = True } old = Annex.branchstate s {- Checks if a git ref exists. -}-refExists :: GitRef -> Annex Bool-refExists ref = do- g <- gitRepo- liftIO $ Git.runBool g "show-ref"- [Param "--verify", Param "-q", Param ref]+refExists :: Git.Ref -> Annex Bool+refExists ref = inRepo $ Git.runBool "show-ref"+ [Param "--verify", Param "-q", Param $ show ref] {- Does the main git-annex branch exist? -} hasBranch :: Annex Bool@@ -190,19 +237,23 @@ hasSomeBranch :: Annex Bool hasSomeBranch = not . null <$> siblingBranches -{- List of all git-annex (refs, branches), including the main one and any- - from remotes. -}-siblingBranches :: Annex [(String, String)]+{- List of git-annex (refs, branches), including the main one and any+ - from remotes. Duplicate refs are filtered out. -}+siblingBranches :: Annex [(Git.Ref, Git.Branch)] siblingBranches = do- g <- gitRepo- r <- liftIO $ Git.pipeRead g [Param "show-ref", Param name]- return $ map (pair . words . L.unpack) (L.lines r)+ r <- inRepo $ Git.pipeRead [Param "show-ref", Param $ show name]+ return $ nubBy uref $ map (gen . words . L.unpack) (L.lines r) where- pair l = (head l, last l)+ gen l = (Git.Ref $ head l, Git.Ref $ last l)+ uref (a, _) (b, _) = a == b -{- Applies a function to modifiy the content of a file. -}+{- Applies a function to modifiy the content of a file.+ -+ - Note that this does not cause the branch to be merged, it only+ - modifes the current content of the file on the branch.+ -} change :: FilePath -> (String -> String) -> Annex ()-change file a = lockJournal $ get file >>= return . a >>= set file+change file a = lockJournal $ getStale file >>= return . a >>= set file {- Records new content of a file into the journal. -} set :: FilePath -> String -> Annex ()@@ -215,13 +266,24 @@ - - Returns an empty string if the file doesn't exist yet. -} get :: FilePath -> Annex String-get file = fromcache =<< getCache file+get = get' False++{- Like get, but does not merge the branch, so the info returned may not+ - reflect changes in remotes. (Changing the value this returns, and then+ - merging is always the same as using get, and then changing its value.) -}+getStale :: FilePath -> Annex String+getStale = get' True++get' :: Bool -> FilePath -> Annex String+get' staleok file = fromcache =<< getCache file where fromcache (Just content) = return content fromcache Nothing = fromjournal =<< getJournalFile file fromjournal (Just content) = cache content- fromjournal Nothing = withIndexUpdate $- cache =<< catFile fullname file+ fromjournal Nothing+ | staleok = withIndex frombranch+ | otherwise = withIndexUpdate $ frombranch >>= cache+ frombranch = L.unpack <$> catFile fullname file cache content = do setCache file content return content@@ -229,9 +291,8 @@ {- Lists all files on the branch. There may be duplicates in the list. -} files :: Annex [FilePath] files = withIndexUpdate $ do- g <- gitRepo- bfiles <- liftIO $ Git.pipeNullSplit g- [Params "ls-tree --name-only -r -z", Param fullname]+ bfiles <- inRepo $ Git.pipeNullSplit+ [Params "ls-tree --name-only -r -z", Param $ show fullname] jfiles <- getJournalledFiles return $ jfiles ++ bfiles @@ -242,10 +303,9 @@ setJournalFile :: FilePath -> String -> Annex () setJournalFile file content = do g <- gitRepo- liftIO $ catch (write g) $ const $ do+ liftIO $ doRedo (write g) $ do createDirectoryIfMissing True $ gitAnnexJournalDir g createDirectoryIfMissing True $ gitAnnexTmpDir g- write g where -- journal file is written atomically write g = do@@ -256,10 +316,8 @@ {- Gets any journalled content for a file in the branch. -} getJournalFile :: FilePath -> Annex (Maybe String)-getJournalFile file = do- g <- gitRepo- liftIO $ catch (liftM Just . readFileStrict $ journalFile g file)- (const $ return Nothing)+getJournalFile file = inRepo $ \g -> catchMaybeIO $+ readFileStrict $ journalFile g file {- List of files that have updated content in the journal. -} getJournalledFiles :: Annex [FilePath]@@ -269,8 +327,8 @@ getJournalFiles :: Annex [FilePath] getJournalFiles = do g <- gitRepo- fs <- liftIO $ catch (getDirectoryContents $ gitAnnexJournalDir g)- (const $ return [])+ fs <- liftIO $+ catchDefaultIO (getDirectoryContents $ gitAnnexJournalDir g) [] return $ filter (`notElem` [".", ".."]) fs {- Stages the specified journalfiles. -}@@ -289,17 +347,17 @@ hClose toh exitSuccess hClose toh- s <- hGetContents fromh+ shas <- map Git.Ref . lines <$> hGetContents fromh -- update the index, also in just one command Git.UnionMerge.update_index g $- index_lines (lines s) $ map fileJournal fs+ index_lines shas (map fileJournal fs) hClose fromh forceSuccess pid mapM_ removeFile paths where index_lines shas = map genline . zip shas genline (sha, file) = Git.UnionMerge.update_index_line sha file- git_hash_object g = Git.gitCommandLine g+ git_hash_object = Git.gitCommandLine [Param "hash-object", Param "-w", Param "--stdin-paths"] @@ -329,12 +387,17 @@ - contention with other git-annex processes. -} lockJournal :: Annex a -> Annex a lockJournal a = do- g <- gitRepo- let file = gitAnnexJournalLock g+ file <- fromRepo gitAnnexJournalLock bracketIO (lock file) unlock a where lock file = do- l <- createFile file stdFileMode+ l <- doRedo (createFile file stdFileMode) $+ createDirectoryIfMissing True $ takeDirectory file waitToSetLock l (WriteLock, AbsoluteSeek, 0, 0) return l unlock = closeFd++{- Runs an action, catching failure and running something to fix it up, and+ - retrying if necessary. -}+doRedo :: IO a -> IO b -> IO a+doRedo a b = catch a $ const $ b >> a
Annex/CatFile.hs view
@@ -6,19 +6,26 @@ -} module Annex.CatFile (- catFile+ catFile,+ catFileHandle ) where +import qualified Data.ByteString.Lazy.Char8 as L+ import Common.Annex+import qualified Git import qualified Git.CatFile import qualified Annex -catFile :: String -> FilePath -> Annex String-catFile branch file = maybe startup go =<< Annex.getState Annex.catfilehandle+catFile :: Git.Branch -> FilePath -> Annex L.ByteString+catFile branch file = do+ h <- catFileHandle+ liftIO $ Git.CatFile.catFile h branch file++catFileHandle :: Annex Git.CatFile.CatFileHandle+catFileHandle = maybe startup return =<< Annex.getState Annex.catfilehandle where startup = do- g <- gitRepo- h <- liftIO $ Git.CatFile.catFileStart g+ h <- inRepo Git.CatFile.catFileStart Annex.changeState $ \s -> s { Annex.catfilehandle = Just h }- go h- go h = liftIO $ Git.CatFile.catFile h branch file+ return h
Annex/Content.hs view
@@ -7,6 +7,8 @@ module Annex.Content ( inAnnex,+ inAnnexSafe,+ lockContent, calcGitLink, logStatus, getViaTmp,@@ -21,9 +23,13 @@ saveState ) where +import System.IO.Error (try)+import Control.Exception (bracket_)+import System.Posix.Types+ import Common.Annex-import LocationLog-import UUID+import Logs.Location+import Annex.UUID import qualified Git import qualified Annex import qualified Annex.Queue@@ -33,40 +39,86 @@ import Types.Key import Utility.DataUnits import Config+import Annex.Exception -{- Checks if a given key is currently present in the gitAnnexLocation. -}+{- Checks if a given key's content is currently present. -} inAnnex :: Key -> Annex Bool-inAnnex key = do- g <- gitRepo- when (Git.repoIsUrl g) $ error "inAnnex cannot check remote repo"- liftIO $ doesFileExist $ gitAnnexLocation g key+inAnnex = inAnnex' doesFileExist+inAnnex' :: (FilePath -> IO a) -> Key -> Annex a+inAnnex' a key = do+ whenM (fromRepo Git.repoIsUrl) $+ error "inAnnex cannot check remote repo"+ inRepo $ a . gitAnnexLocation key +{- A safer check; the key's content must not only be present, but+ - is not in the process of being removed. -}+inAnnexSafe :: Key -> Annex (Maybe Bool)+inAnnexSafe = inAnnex' $ \f -> openForLock f False >>= check+ where+ check Nothing = return is_missing+ check (Just h) = do+ v <- getLock h (ReadLock, AbsoluteSeek, 0, 0)+ closeFd h+ return $ case v of+ Just _ -> is_locked+ Nothing -> is_unlocked+ is_locked = Nothing+ is_unlocked = Just True+ is_missing = Just False++{- Content is exclusively locked while running an action that might remove+ - it. (If the content is not present, no locking is done.) -}+lockContent :: Key -> Annex a -> Annex a+lockContent key a = do+ file <- fromRepo $ gitAnnexLocation key+ bracketIO (openForLock file True >>= lock) unlock a+ where+ lock Nothing = return Nothing+ lock (Just l) = do+ v <- try $ setLock l (WriteLock, AbsoluteSeek, 0, 0)+ case v of+ Left _ -> error "content is locked"+ Right _ -> return $ Just l+ unlock Nothing = return ()+ unlock (Just l) = closeFd l++openForLock :: FilePath -> Bool -> IO (Maybe Fd)+openForLock file writelock = bracket_ prep cleanup go+ where+ go = catchMaybeIO $ openFd file mode Nothing defaultFileFlags+ mode = if writelock then ReadWrite else ReadOnly+ {- Since files are stored with the write bit disabled,+ - have to fiddle with permissions to open for an+ - exclusive lock. -}+ forwritelock a = + when writelock $ whenM (doesFileExist file) a+ prep = forwritelock $ allowWrite file+ cleanup = forwritelock $ preventWrite file+ {- Calculates the relative path to use to link a file to a key. -} calcGitLink :: FilePath -> Key -> Annex FilePath calcGitLink file key = do- g <- gitRepo cwd <- liftIO getCurrentDirectory let absfile = fromMaybe whoops $ absNormPath cwd file+ top <- fromRepo Git.workTree return $ relPathDirToFile (parentDir absfile) - (Git.workTree g) </> ".git" </> annexLocation key+ top </> ".git" </> annexLocation key where whoops = error $ "unable to normalize " ++ file -{- Updates the LocationLog when a key's presence changes in the current+{- Updates the Logs.Location when a key's presence changes in the current - repository. -} logStatus :: Key -> LogStatus -> Annex () logStatus key status = do- g <- gitRepo u <- getUUID- logChange g key u status+ logChange key u status {- Runs an action, passing it a temporary filename to download, - and if the action succeeds, moves the temp file into - the annex as a key's content. -} getViaTmp :: Key -> (FilePath -> Annex Bool) -> Annex Bool getViaTmp key action = do- g <- gitRepo- let tmp = gitAnnexTmpLocation g key+ tmp <- fromRepo $ gitAnnexTmpLocation key -- Check that there is enough free disk space. -- When the temp file already exists, count the space@@ -84,8 +136,7 @@ prepTmp :: Key -> Annex FilePath prepTmp key = do- g <- gitRepo- let tmp = gitAnnexTmpLocation g key+ tmp <- fromRepo $ gitAnnexTmpLocation key liftIO $ createDirectoryIfMissing True (parentDir tmp) return tmp @@ -162,8 +213,7 @@ -} moveAnnex :: Key -> FilePath -> Annex () moveAnnex key src = do- g <- gitRepo- let dest = gitAnnexLocation g key+ dest <- fromRepo $ gitAnnexLocation key let dir = parentDir dest e <- liftIO $ doesFileExist dest if e@@ -177,8 +227,7 @@ withObjectLoc :: Key -> ((FilePath, FilePath) -> Annex a) -> Annex a withObjectLoc key a = do- g <- gitRepo- let file = gitAnnexLocation g key+ file <- fromRepo $gitAnnexLocation key let dir = parentDir file a (dir, file) @@ -201,9 +250,9 @@ - returns the file it was moved to. -} moveBad :: Key -> Annex FilePath moveBad key = do- g <- gitRepo- let src = gitAnnexLocation g key- let dest = gitAnnexBadDir g </> takeFileName src+ src <- fromRepo $ gitAnnexLocation key+ bad <- fromRepo gitAnnexBadDir+ let dest = bad </> takeFileName src liftIO $ do createDirectoryIfMissing True (parentDir dest) allowWrite (parentDir src)@@ -214,9 +263,7 @@ {- List of keys whose content exists in .git/annex/objects/ -} getKeysPresent :: Annex [Key]-getKeysPresent = do- g <- gitRepo- getKeysPresent' $ gitAnnexObjectDir g+getKeysPresent = getKeysPresent' =<< fromRepo gitAnnexObjectDir getKeysPresent' :: FilePath -> Annex [Key] getKeysPresent' dir = do exists <- liftIO $ doesDirectoryExist dir
Annex/Queue.hs view
@@ -34,8 +34,7 @@ unless (0 == Git.Queue.size q) $ do unless silent $ showSideAction "Recording state in git"- g <- gitRepo- q' <- liftIO $ Git.Queue.flush g q+ q' <- inRepo $ Git.Queue.flush q store q' store :: Git.Queue.Queue -> Annex ()
+ Annex/Ssh.hs view
@@ -0,0 +1,66 @@+{- git-annex remote access with ssh+ -+ - Copyright 2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Annex.Ssh where++import Control.Monad.State (liftIO)++import qualified Git+import Utility.SafeCommand+import Types+import Config+import Annex.UUID++{- 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+ uuid <- getRepoUUID r+ sshparams <- sshToRepo r [Param $ sshcmd uuid ]+ return $ Just ("ssh", sshparams)+ | otherwise = return Nothing+ where+ dir = Git.workTree r+ shellcmd = "git-annex-shell"+ shellopts = Param command : File dir : params+ sshcmd uuid = unwords $+ shellcmd : (map shellEscape $ toCommand shellopts) +++ uuidcheck uuid+ uuidcheck NoUUID = []+ uuidcheck (UUID u) = ["--uuid", u]++{- 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
+ Annex/UUID.hs view
@@ -0,0 +1,73 @@+{- git-annex uuids+ -+ - Each git repository used by git-annex has an annex.uuid setting that+ - uniquely identifies that repository.+ -+ - UUIDs of remotes are cached in git config, using keys named+ - remote.<name>.annex-uuid+ -+ - Copyright 2010-2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Annex.UUID (+ getUUID,+ getRepoUUID,+ getUncachedUUID,+ prepUUID,+ genUUID+) where++import Common.Annex+import qualified Git+import qualified Build.SysConfig as SysConfig+import Config++configkey :: String+configkey = "annex.uuid"++{- Generates a UUID. There is a library for this, but it's not packaged,+ - so use the command line tool. -}+genUUID :: IO UUID+genUUID = pOpen ReadFromPipe command params $ liftM toUUID . hGetLine+ where+ command = SysConfig.uuid+ params = if command == "uuid"+ -- request a random uuid be generated+ then ["-m"]+ -- uuidgen generates random uuid by default+ else []++{- Get current repository's UUID. -}+getUUID :: Annex UUID+getUUID = getRepoUUID =<< gitRepo++{- Looks up a repo's UUID, caching it in .git/config if it's not already. -}+getRepoUUID :: Git.Repo -> Annex UUID+getRepoUUID r = do+ c <- fromRepo cached+ let u = getUncachedUUID r+ + if c /= u && u /= NoUUID+ then do+ updatecache u+ return u+ else return c+ where+ cached = toUUID . Git.configGet cachekey ""+ updatecache u = do+ g <- gitRepo+ when (g /= r) $ storeUUID cachekey u+ cachekey = remoteConfig r "uuid"++getUncachedUUID :: Git.Repo -> UUID+getUncachedUUID = toUUID . Git.configGet configkey ""++{- Make sure that the repo has an annex.uuid setting. -}+prepUUID :: Annex ()+prepUUID = whenM ((==) NoUUID <$> getUUID) $+ storeUUID configkey =<< liftIO genUUID++storeUUID :: String -> UUID -> Annex ()+storeUUID configfield = setConfig configfield . fromUUID
Annex/Version.hs view
@@ -26,12 +26,10 @@ versionField = "annex.version" getVersion :: Annex (Maybe Version)-getVersion = do- g <- gitRepo- let v = Git.configGet g versionField ""- if not $ null v- then return $ Just v- else return Nothing+getVersion = handle <$> fromRepo (Git.configGet versionField "")+ where+ handle [] = Nothing+ handle v = Just v setVersion :: Annex () setVersion = setConfig versionField defaultVersion
Backend.hs view
@@ -26,12 +26,12 @@ import qualified Types.Backend as B -- When adding a new backend, import it here and add it to the list.-import qualified Backend.WORM import qualified Backend.SHA+import qualified Backend.WORM import qualified Backend.URL list :: [Backend Annex]-list = Backend.WORM.backends ++ Backend.SHA.backends ++ Backend.URL.backends+list = Backend.SHA.backends ++ Backend.WORM.backends ++ Backend.URL.backends {- List of backends in the order to try them when storing a new key. -} orderedList :: Annex [Backend Annex]@@ -47,10 +47,7 @@ l' <- (lookupBackendName name :) <$> standard Annex.changeState $ \s -> s { Annex.backends = l' } return l'- standard = do- g <- gitRepo- return $ parseBackendList $- Git.configGet g "annex.backends" ""+ standard = fromRepo $ parseBackendList . Git.configGet "annex.backends" "" parseBackendList [] = list parseBackendList s = map lookupBackendName $ words s @@ -96,16 +93,14 @@ - That can be configured on a per-file basis in the gitattributes file. -} chooseBackends :: [FilePath] -> Annex [BackendFile]-chooseBackends fs = do- g <- gitRepo- forced <- Annex.getState Annex.forcebackend- if isJust forced- then do+chooseBackends fs = Annex.getState Annex.forcebackend >>= go+ where+ go Nothing = do+ pairs <- inRepo $ Git.checkAttr "annex.backend" fs+ return $ map (\(f,b) -> (maybeLookupBackendName b, f)) pairs+ go (Just _) = do l <- orderedList return $ map (\f -> (Just $ head l, f)) fs- else do- pairs <- liftIO $ Git.checkAttr g "annex.backend" fs- return $ map (\(f,b) -> (maybeLookupBackendName b, f)) pairs {- Looks up a backend by name. May fail if unknown. -} lookupBackendName :: String -> Backend Annex
Backend/SHA.hs view
@@ -16,12 +16,12 @@ type SHASize = Int +-- order is slightly significant; want SHA256 first, and more general+-- sizes earlier sizes :: [Int]-sizes = [1, 256, 512, 224, 384]+sizes = [256, 1, 512, 224, 384] backends :: [Backend Annex]--- order is slightly significant; want sha1 first, and more general--- sizes earlier backends = catMaybes $ map genBackend sizes ++ map genBackendE sizes genBackend :: SHASize -> Maybe (Backend Annex)@@ -98,9 +98,8 @@ {- A key's checksum is checked during fsck. -} checkKeyChecksum :: SHASize -> Key -> Annex Bool checkKeyChecksum size key = do- g <- gitRepo fast <- Annex.getState Annex.fast- let file = gitAnnexLocation g key+ file <- fromRepo $ gitAnnexLocation key present <- liftIO $ doesFileExist file if not present || fast then return True
CHANGELOG view
@@ -1,3 +1,93 @@+git-annex (3.20111122) unstable; urgency=low++ * merge: Improve commit messages to mention what was merged.+ * Avoid doing auto-merging in commands that don't need fully current+ information from the git-annex branch. In particular, git annex add+ no longer needs to auto-merge.+ * init: When run in an already initalized repository, and without+ a description specified, don't delete the old description. + * Optimised union merging; now only runs git cat-file once, and runs+ in constant space.+ * status: Now displays trusted, untrusted, and semitrusted repositories+ separately.+ * status: Include all special remotes in the list of repositories.+ * status: Fix --json mode.+ * status: --fast is back+ * Fix support for insteadOf url remapping. Closes: #644278+ * When not run in a git repository, git-annex can still display a usage+ message, and "git annex version" even works.+ * migrate: Don't fall over a stale temp file.+ * Avoid excessive escaping for rsync special remotes that are not accessed+ over ssh.+ * find: Support --print0++ -- Joey Hess <joeyh@debian.org> Tue, 22 Nov 2011 14:31:45 -0400++git-annex (3.20111111) unstable; urgency=low++ * Handle a case where an annexed file is moved into a gitignored directory,+ by having fix --force add its change.+ * Avoid cyclic drop problems.+ * Optimized copy --from and get --from to avoid checking the location log+ for files that are already present.+ * Automatically fix up badly formatted uuid.log entries produced by+ 3.20111105, whenever the uuid.log is changed (ie, by init or describe).+ * map: Support remotes with /~/ and /~user/++ -- Joey Hess <joeyh@debian.org> Fri, 11 Nov 2011 13:44:18 -0400++git-annex (3.20111107) unstable; urgency=low++ * merge: Use fast-forward merges when possible.+ Thanks Valentin Haenel for a test case showing how non-fast-forward+ merges could result in an ongoing pull/merge/push cycle.+ * Don't try to read config from repos with annex-ignore set.+ * Bugfix: In the past two releases, git-annex init has written the uuid.log+ in the wrong format, with the UUID and description flipped.++ -- Joey Hess <joeyh@debian.org> Mon, 07 Nov 2011 12:47:44 -0400++git-annex (3.20111105) unstable; urgency=low++ * The default backend used when adding files to the annex is changed+ from WORM to SHA256.+ To get old behavior, add a .gitattributes containing: * annex.backend=WORM+ * Sped up some operations on remotes that are on the same host.+ * copy --to: Fixed leak when copying many files to a remote on the same+ host.+ * uninit: Add guard against being run with the git-annex branch checked out.+ * Fail if --from or --to is passed to commands that do not support them.+ * drop --from is now supported to remove file content from a remote.+ * status: Now always shows the current repository, even when it does not+ appear in uuid.log.+ * fsck: Now works in bare repositories. Checks location log information,+ and file contents. Does not check that numcopies is satisfied, as+ .gitattributes information about numcopies is not available in a bare+ repository.+ * unused, dropunused: Now work in bare repositories.+ * Removed the setkey command, and added a reinject command with a more+ useful interface.+ * The fromkey command now takes the key as its first parameter. The --key+ option is no longer used.+ * Built without any filename containing .git being excluded. Closes: #647215+ * Record uuid when auto-initializing a remote so it shows in status.+ * Bugfix: Fixed git-annex init crash in a bare repository when there was+ already an existing git-annex branch.+ * Pass -t to rsync to preserve timestamps.++ -- Joey Hess <joeyh@debian.org> Sat, 05 Nov 2011 15:47:52 -0400++git-annex (3.20111025) unstable; urgency=low++ * A remote can have a annexUrl configured, that is used by git-annex+ instead of its usual url. (Similar to pushUrl.)+ * migrate: Copy url logs for keys when migrating.+ * git-annex-shell: GIT_ANNEX_SHELL_READONLY and GIT_ANNEX_SHELL_LIMITED+ environment variables can be set to limit what commands can be run.+ This is used by gitolite's new git-annex support!++ -- Joey Hess <joeyh@debian.org> Tue, 25 Oct 2011 13:03:08 -0700+ git-annex (3.20111011) unstable; urgency=low * This version of git-annex only works with git 1.7.7 and newer.
+ Checks.hs view
@@ -0,0 +1,42 @@+{- git-annex command checks+ -+ - Common sanity checks for commands, and an interface to selectively+ - remove them, or add others.+ - + - Copyright 2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Checks where++import Common.Annex+import Types.Command+import Init+import qualified Annex++commonChecks :: [CommandCheck]+commonChecks = [fromOpt, toOpt, repoExists]++repoExists :: CommandCheck+repoExists = CommandCheck 0 ensureInitialized++fromOpt :: CommandCheck+fromOpt = CommandCheck 1 $ do+ v <- Annex.getState Annex.fromremote+ unless (isNothing v) $ error "cannot use --from with this command"++toOpt :: CommandCheck+toOpt = CommandCheck 2 $ do+ v <- Annex.getState Annex.toremote+ unless (isNothing v) $ error "cannot use --to with this command"++dontCheck :: CommandCheck -> Command -> Command+dontCheck check cmd = mutateCheck cmd $ \c -> filter (/= check) c++addCheck :: Annex () -> Command -> Command+addCheck check cmd = mutateCheck cmd $+ \c -> CommandCheck (length c + 100) check : c++mutateCheck :: Command -> ([CommandCheck] -> [CommandCheck]) -> Command+mutateCheck cmd@(Command { cmdcheck = c }) a = cmd { cmdcheck = a c }
CmdLine.hs view
@@ -11,7 +11,9 @@ shutdown ) where -import System.IO.Error (try)+import qualified System.IO.Error as IO+import qualified Control.Exception as E+import Control.Exception (throw) import System.Console.GetOpt import Common.Annex@@ -20,49 +22,46 @@ import qualified Git import Annex.Content import Command-import Options-import Init +type Params = [String]+type Flags = [Annex ()]+ {- Runs the passed command line. -}-dispatch :: [String] -> [Command] -> [Option] -> String -> Git.Repo -> IO ()-dispatch args cmds options header gitrepo = do+dispatch :: Params -> [Command] -> [Option] -> String -> IO Git.Repo -> IO ()+dispatch args cmds options header getgitrepo = do setupConsole- state <- Annex.new gitrepo- (actions, state') <- Annex.run state $ parseCmd args header cmds options- tryRun state' $ [startup] ++ actions ++ [shutdown]--{- Parses command line, stores configure flags, and returns a - - list of actions to be run in the Annex monad. -}-parseCmd :: [String] -> String -> [Command] -> [Option] -> Annex [Annex Bool]-parseCmd argv header cmds options = do- (flags, params) <- liftIO getopt- when (null params) $ error $ "missing command" ++ usagemsg- case lookupCmd (head params) of- [] -> error $ "unknown command" ++ usagemsg- [command] -> do- _ <- sequence flags- checkCmdEnviron command- prepCommand command (drop 1 params)- _ -> error "internal error: multiple matching commands"+ r <- E.try getgitrepo :: IO (Either E.SomeException Git.Repo)+ case r of+ Left e -> maybe (throw e) id (cmdnorepo cmd)+ Right g -> do+ state <- Annex.new g+ (actions, state') <- Annex.run state $ do+ sequence_ flags+ prepCommand cmd params+ tryRun state' cmd $ [startup] ++ actions ++ [shutdown] where- getopt = case getOpt Permute options argv of- (flags, params, []) ->- return (flags, params)- (_, _, errs) ->- ioError (userError (concat errs ++ usagemsg))- lookupCmd cmd = filter (\c -> cmd == cmdname c) cmds- usagemsg = "\n\n" ++ usage header cmds options+ (flags, cmd, params) = parseCmd args cmds options header -{- Checks that the command can be run in the current environment. -}-checkCmdEnviron :: Command -> Annex ()-checkCmdEnviron command = when (cmdusesrepo command) ensureInitialized+{- Parses command line, and returns actions to run to configure flags,+ - the Command being run, and the remaining parameters for the command. -} +parseCmd :: Params -> [Command] -> [Option] -> String -> (Flags, Command, Params)+parseCmd argv cmds options header = check $ getOpt Permute options argv+ where+ check (_, [], []) = err "missing command"+ check (flags, name:rest, [])+ | null matches = err $ "unknown command " ++ name+ | otherwise = (flags, head matches, rest)+ where+ matches = filter (\c -> name == cmdname c) cmds+ check (_, _, errs) = err $ concat errs+ err msg = error $ msg ++ "\n\n" ++ usage header cmds options {- Usage message with lists of commands and options. -} usage :: String -> [Command] -> [Option] -> String-usage header cmds options =- usageInfo (header ++ "\n\nOptions:") options ++- "\nCommands:\n" ++ cmddescs+usage header cmds options = usageInfo top options ++ commands where+ top = header ++ "\n\nOptions:"+ commands = "\nCommands:\n" ++ cmddescs cmddescs = unlines $ map (indent . showcmd) cmds showcmd c = cmdname c ++@@ -76,22 +75,23 @@ {- Runs a list of Annex actions. Catches IO errors and continues - (but explicitly thrown errors terminate the whole command). -}-tryRun :: Annex.AnnexState -> [Annex Bool] -> IO ()+tryRun :: Annex.AnnexState -> Command -> [CommandCleanup] -> IO () tryRun = tryRun' 0-tryRun' :: Integer -> Annex.AnnexState -> [Annex Bool] -> IO ()-tryRun' errnum state (a:as) = do- result <- try $ Annex.run state $ do- Annex.Queue.flushWhenFull- a- case result of- Left err -> do- Annex.eval state $ do- showErr err- showEndFail- tryRun' (errnum + 1) state as- Right (True,state') -> tryRun' errnum state' as- Right (False,state') -> tryRun' (errnum + 1) state' as-tryRun' errnum _ [] = when (errnum > 0) $ error $ show errnum ++ " failed"+tryRun' :: Integer -> Annex.AnnexState -> Command -> [CommandCleanup] -> IO ()+tryRun' errnum _ cmd []+ | errnum > 0 = error $ cmdname cmd ++ ": " ++ show errnum ++ " failed"+ | otherwise = return ()+tryRun' errnum state cmd (a:as) = run >>= handle+ where+ run = IO.try $ Annex.run state $ do+ Annex.Queue.flushWhenFull+ a+ handle (Left err) = showerr err >> cont False state+ handle (Right (success, state')) = cont success state'+ cont success s = tryRun' (if success then errnum else errnum + 1) s cmd as+ showerr err = Annex.eval state $ do+ showErr err+ showEndFail {- Actions to perform each time ran. -} startup :: Annex Bool
Command.hs view
@@ -5,54 +5,41 @@ - Licensed under the GNU GPL version 3 or higher. -} -module Command where+module Command (+ command,+ noRepo,+ next,+ stop,+ prepCommand,+ doCommand,+ whenAnnexed,+ notAnnexed,+ notBareRepo,+ isBareRepo,+ autoCopies,+ module ReExported+) where import Common.Annex import qualified Backend import qualified Annex import qualified Git-import qualified Git.LsFiles as LsFiles-import Types.Key-import Trust-import LocationLog+import Types.Command as ReExported+import Seek as ReExported+import Checks as ReExported+import Options as ReExported+import Logs.Trust+import Logs.Location import Config-import Backend-import Limit -{- A command runs in four stages.- -- - 0. The seek stage takes the parameters passed to the command,- - looks through the repo to find the ones that are relevant- - to that command (ie, new files to add), and generates- - a list of start stage actions. -}-type CommandSeek = [String] -> Annex [CommandStart]-{- 1. The start stage is run before anything is printed about the- - command, is passed some input, and can early abort it- - if the input does not make sense. It should run quickly and- - should not modify Annex state. -}-type CommandStart = Annex (Maybe CommandPerform)-{- 2. The perform stage is run after a message is printed about the command- - being run, and it should be where the bulk of the work happens. -}-type CommandPerform = Annex (Maybe CommandCleanup)-{- 3. The cleanup stage is run only if the perform stage succeeds, and it- - returns the overall success/fail of the command. -}-type CommandCleanup = Annex Bool--data Command = Command {- cmdusesrepo :: Bool,- cmdname :: String,- cmdparams :: String,- cmdseek :: [CommandSeek],- cmddesc :: String-}--{- Most commands operate on files in a git repo. -}-repoCommand :: String -> String -> [CommandSeek] -> String -> Command-repoCommand = Command True+{- Generates a normal command -}+command :: String -> String -> [CommandSeek] -> String -> Command+command = Command Nothing commonChecks -{- Others can run anywhere. -}-standaloneCommand :: String -> String -> [CommandSeek] -> String -> Command-standaloneCommand = Command False+{- Adds a fallback action to a command, that will be run if it's used+ - outside a git repository. -}+noRepo :: IO () -> Command -> Command+noRepo a c = c { cmdnorepo = Just a } {- For start and perform stages to indicate what step to run next. -} next :: a -> Annex (Maybe a)@@ -62,159 +49,41 @@ stop :: Annex (Maybe a) stop = return Nothing -{- Prepares a list of actions to run to perform a command, based on- - the parameters passed to it. -}-prepCommand :: Command -> [String] -> Annex [Annex Bool]-prepCommand Command { cmdseek = seek } params =- return . map doCommand . concat =<< mapM (\s -> s params) seek+{- Prepares to run a command via the check and seek stages, returning a+ - list of actions to perform to run the command. -}+prepCommand :: Command -> [String] -> Annex [CommandCleanup]+prepCommand Command { cmdseek = seek, cmdcheck = c } params = do+ mapM_ runCheck c+ map doCommand . concat <$> mapM (\s -> s params) seek {- Runs a command through the start, perform and cleanup stages -} doCommand :: CommandStart -> CommandCleanup doCommand = start where- start = stage $ maybe success perform+ start = stage $ maybe skip perform perform = stage $ maybe failure cleanup- cleanup = stage $ \r -> showEndResult r >> return r+ cleanup = stage $ status stage = (=<<)- success = return True+ skip = return True failure = showEndFail >> return False+ status r = showEndResult r >> return r +{- Modifies an action to only act on files that are already annexed,+ - and passes the key and backend on to it. -}+whenAnnexed :: (FilePath -> (Key, Backend Annex) -> Annex (Maybe a)) -> FilePath -> Annex (Maybe a)+whenAnnexed a file = maybe (return Nothing) (a file) =<< Backend.lookupFile file+ notAnnexed :: FilePath -> Annex (Maybe a) -> Annex (Maybe a) notAnnexed file a = maybe a (const $ return Nothing) =<< Backend.lookupFile file -isAnnexed :: FilePath -> ((Key, Backend Annex) -> Annex (Maybe a)) -> Annex (Maybe a)-isAnnexed file a = maybe (return Nothing) a =<< Backend.lookupFile file- notBareRepo :: Annex a -> Annex a notBareRepo a = do- whenM (Git.repoIsLocalBare <$> gitRepo) $+ whenM isBareRepo $ error "You cannot run this subcommand in a bare repository." a -{- These functions find appropriate files or other things based on a- user's parameters, and prepare actions operating on them. -}-withFilesInGit :: (FilePath -> CommandStart) -> CommandSeek-withFilesInGit a params = do- repo <- gitRepo- runFiltered a $ liftIO $ runPreserveOrder (LsFiles.inRepo repo) params-withAttrFilesInGit :: String -> ((FilePath, String) -> CommandStart) -> CommandSeek-withAttrFilesInGit attr a params = do- repo <- gitRepo- files <- liftIO $ runPreserveOrder (LsFiles.inRepo repo) params- runFilteredGen a fst $ liftIO $ Git.checkAttr repo attr files-withNumCopies :: (FilePath -> Maybe Int -> CommandStart) -> CommandSeek-withNumCopies a params = withAttrFilesInGit "annex.numcopies" go params- where- go (file, v) = a file (readMaybe v)-withBackendFilesInGit :: (BackendFile -> CommandStart) -> CommandSeek-withBackendFilesInGit a params = do- repo <- gitRepo- files <- liftIO $ runPreserveOrder (LsFiles.inRepo repo) params- backendPairs a files-withFilesMissing :: (String -> CommandStart) -> CommandSeek-withFilesMissing a params = runFiltered a $ liftIO $ filterM missing params- where- missing = liftM not . doesFileExist-withFilesNotInGit :: (BackendFile -> CommandStart) -> CommandSeek-withFilesNotInGit a params = do- repo <- gitRepo- force <- Annex.getState Annex.force- newfiles <- liftIO $ runPreserveOrder (LsFiles.notInRepo repo force) params- backendPairs a newfiles-withWords :: ([String] -> CommandStart) -> CommandSeek-withWords a params = return [a params]-withStrings :: (String -> CommandStart) -> CommandSeek-withStrings a params = return $ map a params-withFilesToBeCommitted :: (String -> CommandStart) -> CommandSeek-withFilesToBeCommitted a params = do- repo <- gitRepo- runFiltered a $- liftIO $ runPreserveOrder (LsFiles.stagedNotDeleted repo) params-withFilesUnlocked :: (BackendFile -> CommandStart) -> CommandSeek-withFilesUnlocked = withFilesUnlocked' LsFiles.typeChanged-withFilesUnlockedToBeCommitted :: (BackendFile -> CommandStart) -> CommandSeek-withFilesUnlockedToBeCommitted = withFilesUnlocked' LsFiles.typeChangedStaged-withFilesUnlocked' :: (Git.Repo -> [FilePath] -> IO [FilePath]) -> (BackendFile -> CommandStart) -> CommandSeek-withFilesUnlocked' typechanged a params = do- -- unlocked files have changed type from a symlink to a regular file- repo <- gitRepo- typechangedfiles <- liftIO $ runPreserveOrder (typechanged repo) params- unlockedfiles <- liftIO $ filterM notSymlink $- map (\f -> Git.workTree repo ++ "/" ++ f) typechangedfiles- backendPairs a unlockedfiles-withKeys :: (Key -> CommandStart) -> CommandSeek-withKeys a params = return $ map (a . parse) params- where- parse p = fromMaybe (error "bad key") $ readKey p-withNothing :: CommandStart -> CommandSeek-withNothing a [] = return [a]-withNothing _ _ = error "This command takes no parameters."--runFiltered :: (FilePath -> Annex (Maybe a)) -> Annex [FilePath] -> Annex [Annex (Maybe a)]-runFiltered a = runFilteredGen a id--backendPairs :: (BackendFile -> CommandStart) -> CommandSeek-backendPairs a fs = runFilteredGen a snd (Backend.chooseBackends fs)--runFilteredGen :: (b -> Annex (Maybe a)) -> (b -> FilePath) -> Annex [b] -> Annex [Annex (Maybe a)]-runFilteredGen a d fs = do- matcher <- Limit.getMatcher- liftM (map $ proc matcher) fs- where- proc matcher v = do- let f = d v- ok <- matcher f- if ok then a v else stop--{- filter out symlinks -} -notSymlink :: FilePath -> IO Bool-notSymlink f = liftIO $ not . isSymbolicLink <$> getSymbolicLinkStatus f--{- Descriptions of params used in usage messages. -}-paramPaths :: String-paramPaths = paramOptional $ paramRepeating paramPath -- most often used-paramPath :: String-paramPath = "PATH"-paramKey :: String-paramKey = "KEY"-paramDesc :: String-paramDesc = "DESC"-paramUrl :: String-paramUrl = "URL"-paramNumber :: String-paramNumber = "NUMBER"-paramRemote :: String-paramRemote = "REMOTE"-paramGlob :: String-paramGlob = "GLOB"-paramName :: String-paramName = "NAME"-paramUUID :: String-paramUUID = "UUID"-paramType :: String-paramType = "TYPE"-paramKeyValue :: String-paramKeyValue = "K=V"-paramNothing :: String-paramNothing = ""-paramRepeating :: String -> String-paramRepeating s = s ++ " ..."-paramOptional :: String -> String-paramOptional s = "[" ++ s ++ "]"-paramPair :: String -> String -> String-paramPair a b = a ++ " " ++ b--{- The Key specified by the --key parameter. -}-cmdlineKey :: Annex Key-cmdlineKey = do- k <- Annex.getState Annex.defaultkey- case k of- Nothing -> nokey- Just "" -> nokey- Just kstring -> maybe badkey return $ readKey kstring- where- nokey = error "please specify the key with --key"- badkey = error "bad key"+isBareRepo :: Annex Bool+isBareRepo = fromRepo Git.repoIsLocalBare {- Used for commands that have an auto mode that checks the number of known - copies of a key.@@ -223,11 +92,10 @@ - copies of the key is > or < than the numcopies setting, before running - the action. -} autoCopies :: Key -> (Int -> Int -> Bool) -> Maybe Int -> CommandStart -> CommandStart-autoCopies key vs numcopiesattr a = do- auto <- Annex.getState Annex.auto- if auto- then do+autoCopies key vs numcopiesattr a = Annex.getState Annex.auto >>= auto+ where+ auto False = a+ auto True = do needed <- getNumCopies numcopiesattr (_, have) <- trustPartition UnTrusted =<< keyLocations key if length have `vs` needed then a else stop- else a
Command/Add.hs view
@@ -13,13 +13,13 @@ import qualified Annex import qualified Annex.Queue import qualified Backend-import LocationLog+import Logs.Location import Annex.Content import Utility.Touch import Backend -command :: [Command]-command = [repoCommand "add" paramPaths seek "add files to annex"]+def :: [Command]+def = [command "add" paramPaths seek "add files to annex"] {- Add acts on both files not checked into git yet, and unlocked files. -} seek :: [CommandSeek]@@ -29,7 +29,7 @@ - moving it into the annex directory and setting up the symlink pointing - to its content. -} start :: BackendFile -> CommandStart-start p@(_, file) = notAnnexed file $ do+start p@(_, file) = notBareRepo $ notAnnexed file $ do s <- liftIO $ getSymbolicLinkStatus file if isSymbolicLink s || not (isRegularFile s) then stop@@ -38,11 +38,10 @@ next $ perform p perform :: BackendFile -> CommandPerform-perform (backend, file) = do- k <- Backend.genKey file backend- case k of- Nothing -> stop- Just (key, _) -> do+perform (backend, file) = Backend.genKey file backend >>= go+ where+ go Nothing = stop+ go (Just (key, _)) = do handle (undo file key) $ moveAnnex key file next $ cleanup file key True @@ -61,8 +60,8 @@ -- fromAnnex could fail if the file ownership is weird tryharder :: IOException -> Annex () tryharder _ = do- g <- gitRepo- liftIO $ renameFile (gitAnnexLocation g key) file+ src <- fromRepo $ gitAnnexLocation key+ liftIO $ renameFile src file cleanup :: FilePath -> Key -> Bool -> CommandCleanup cleanup file key hascontent = do@@ -75,9 +74,9 @@ -- touch the symlink to have the same mtime as the -- file it points to- s <- liftIO $ getFileStatus file- let mtime = modificationTime s- liftIO $ touch file (TimeSpec mtime) False+ liftIO $ do+ mtime <- modificationTime <$> getFileStatus file+ touch file (TimeSpec mtime) False force <- Annex.getState Annex.force if force
Command/AddUrl.hs view
@@ -13,30 +13,27 @@ import Command import qualified Backend import qualified Utility.Url as Url-import qualified Remote.Web import qualified Command.Add import qualified Annex import qualified Backend.URL import Annex.Content-import PresenceLog+import Logs.Web -command :: [Command]-command = [repoCommand "addurl" (paramRepeating paramUrl) seek- "add urls to annex"]+def :: [Command]+def = [command "addurl" (paramRepeating paramUrl) seek "add urls to annex"] seek :: [CommandSeek] seek = [withStrings start] start :: String -> CommandStart-start s = do- let u = parseURI s- case u of- Nothing -> error $ "bad url " ++ s- Just url -> do+start s = notBareRepo $ go $ parseURI s+ where+ go Nothing = error $ "bad url " ++ s+ go (Just url) = do file <- liftIO $ url2file url showStart "addurl" file next $ perform s file- + perform :: String -> FilePath -> CommandPerform perform url file = do fast <- Annex.getState Annex.fast@@ -44,12 +41,11 @@ download :: String -> FilePath -> CommandPerform download url file = do- g <- gitRepo showAction $ "downloading " ++ url ++ " " let dummykey = Backend.URL.fromUrl url- let tmp = gitAnnexTmpLocation g dummykey+ tmp <- fromRepo $ gitAnnexTmpLocation dummykey liftIO $ createDirectoryIfMissing True (parentDir tmp)- ok <- Url.download url tmp+ ok <- liftIO $ Url.download url tmp if ok then do [(backend, _)] <- Backend.chooseBackends [file]@@ -58,15 +54,14 @@ Nothing -> stop Just (key, _) -> do moveAnnex key tmp- Remote.Web.setUrl key url InfoPresent+ setUrlPresent key url next $ Command.Add.cleanup file key True else stop nodownload :: String -> FilePath -> CommandPerform nodownload url file = do let key = Backend.URL.fromUrl url- Remote.Web.setUrl key url InfoPresent- + setUrlPresent key url next $ Command.Add.cleanup file key False url2file :: URI -> IO FilePath
Command/ConfigList.hs view
@@ -9,11 +9,11 @@ import Common.Annex import Command-import UUID+import Annex.UUID -command :: [Command]-command = [repoCommand "configlist" paramNothing seek- "outputs relevant git configuration"]+def :: [Command]+def = [command "configlist" paramNothing seek+ "outputs relevant git configuration"] seek :: [CommandSeek] seek = [withNothing start]@@ -21,5 +21,5 @@ start :: CommandStart start = do u <- getUUID- liftIO $ putStrLn $ "annex.uuid=" ++ u+ liftIO $ putStrLn $ "annex.uuid=" ++ fromUUID u stop
Command/Copy.hs view
@@ -7,19 +7,20 @@ module Command.Copy where +import Common.Annex import Command import qualified Command.Move -command :: [Command]-command = [repoCommand "copy" paramPaths seek+def :: [Command]+def = [dontCheck toOpt $ dontCheck fromOpt $ + command "copy" paramPaths seek "copy content of files to/from another repository"] seek :: [CommandSeek]-seek = [withNumCopies start]+seek = [withNumCopies $ \n -> whenAnnexed $ start n] -- A copy is just a move that does not delete the source file. -- However, --auto mode avoids unnecessary copies.-start :: FilePath -> Maybe Int -> CommandStart-start file numcopies = isAnnexed file $ \(key, _) ->- autoCopies key (<) numcopies $- Command.Move.start False file+start :: Maybe Int -> FilePath -> (Key, Backend Annex) -> CommandStart+start numcopies file (key, backend) = autoCopies key (<) numcopies $+ Command.Move.start False file (key, backend)
Command/Describe.hs view
@@ -10,25 +10,21 @@ import Common.Annex import Command import qualified Remote-import UUID+import Logs.UUID -command :: [Command]-command = [repoCommand "describe" (paramPair paramRemote paramDesc) seek+def :: [Command]+def = [command "describe" (paramPair paramRemote paramDesc) seek "change description of a repository"] seek :: [CommandSeek] seek = [withWords start] start :: [String] -> CommandStart-start ws = do- let (name, description) =- case ws of- (n:d) -> (n,unwords d)- _ -> error "Specify a repository and a description."- +start (name:description) = do showStart "describe" name u <- Remote.nameToUUID name- next $ perform u description+ next $ perform u $ unwords description+start _ = error "Specify a repository and a description." perform :: UUID -> String -> CommandPerform perform u description = do
Command/Drop.hs view
@@ -11,79 +11,132 @@ import Command import qualified Remote import qualified Annex-import LocationLog+import Annex.UUID+import Logs.Location+import Logs.Trust import Annex.Content-import Trust import Config -command :: [Command]-command = [repoCommand "drop" paramPaths seek+def :: [Command]+def = [dontCheck fromOpt $ command "drop" paramPaths seek "indicate content of files not currently wanted"] seek :: [CommandSeek]-seek = [withNumCopies start]+seek = [withNumCopies $ \n -> whenAnnexed $ start n] -{- Indicates a file's content is not wanted anymore, and should be removed- - if it's safe to do so. -}-start :: FilePath -> Maybe Int -> CommandStart-start file numcopies = isAnnexed file $ \(key, _) -> do+start :: Maybe Int -> FilePath -> (Key, Backend Annex) -> CommandStart+start numcopies file (key, _) = autoCopies key (>) numcopies $ do+ from <- Annex.getState Annex.fromremote+ case from of+ Nothing -> startLocal file numcopies key+ Just name -> do+ remote <- Remote.byName name+ u <- getUUID+ if Remote.uuid remote == u+ then startLocal file numcopies key+ else startRemote file numcopies key remote++startLocal :: FilePath -> Maybe Int -> Key -> CommandStart+startLocal file numcopies key = do present <- inAnnex key if present- then autoCopies key (>) numcopies $ do+ then do showStart "drop" file- next $ perform key numcopies+ next $ performLocal key numcopies else stop -perform :: Key -> Maybe Int -> CommandPerform-perform key numcopies = do- success <- dropKey key numcopies+startRemote :: FilePath -> Maybe Int -> Key -> Remote.Remote Annex -> CommandStart+startRemote file numcopies key remote = do+ showStart "drop" file+ next $ performRemote key numcopies remote++performLocal :: Key -> Maybe Int -> CommandPerform+performLocal key numcopies = lockContent key $ do+ (remotes, trusteduuids) <- Remote.keyPossibilitiesTrusted key+ untrusteduuids <- trustGet UnTrusted+ let tocheck = Remote.remotesWithoutUUID remotes (trusteduuids++untrusteduuids)+ success <- canDropKey key numcopies trusteduuids tocheck [] if success- then next $ cleanup key+ then do+ whenM (inAnnex key) $ removeAnnex key+ next $ cleanupLocal key else stop -cleanup :: Key -> CommandCleanup-cleanup key = do- whenM (inAnnex key) $ removeAnnex key+performRemote :: Key -> Maybe Int -> Remote.Remote Annex -> CommandPerform+performRemote key numcopies remote = lockContent key $ do+ -- Filter the remote it's being dropped from out of the lists of+ -- places assumed to have the key, and places to check.+ -- When the local repo has the key, that's one additional copy.+ (remotes, trusteduuids) <- Remote.keyPossibilitiesTrusted key+ present <- inAnnex key+ u <- getUUID+ let have = filter (/= uuid) $+ if present then u:trusteduuids else trusteduuids+ untrusteduuids <- trustGet UnTrusted+ let tocheck = filter (/= remote) $+ Remote.remotesWithoutUUID remotes (have++untrusteduuids)+ success <- canDropKey key numcopies have tocheck [uuid]+ if success+ then do+ ok <- Remote.removeKey remote key+ next $ cleanupRemote key remote ok+ else stop+ where+ uuid = Remote.uuid remote++cleanupLocal :: Key -> CommandCleanup+cleanupLocal key = do logStatus key InfoMissing return True -{- Checks remotes to verify that enough copies of a key exist to allow- - for a key to be safely removed (with no data loss), and fails with an- - error if not. -}-dropKey :: Key -> Maybe Int -> Annex Bool-dropKey key numcopiesM = do+cleanupRemote :: Key -> Remote.Remote Annex -> Bool -> CommandCleanup+cleanupRemote key remote ok = do+ -- better safe than sorry: assume the remote dropped the key+ -- even if it seemed to fail; the failure could have occurred+ -- after it really dropped it+ Remote.logStatus remote key False+ return ok++{- Checks specified remotes to verify that enough copies of a key exist to+ - allow it to be safely removed (with no data loss). Can be provided with+ - some locations where the key is known/assumed to be present. -}+canDropKey :: Key -> Maybe Int -> [UUID] -> [Remote.Remote Annex] -> [UUID] -> Annex Bool+canDropKey key numcopiesM have check skip = do force <- Annex.getState Annex.force if force || numcopiesM == Just 0 then return True else do- (remotes, trusteduuids) <- Remote.keyPossibilitiesTrusted key- untrusteduuids <- trustGet UnTrusted- let tocheck = Remote.remotesWithoutUUID remotes (trusteduuids++untrusteduuids)- numcopies <- getNumCopies numcopiesM- findcopies numcopies trusteduuids tocheck []+ need <- getNumCopies numcopiesM+ findCopies key need skip have check++findCopies :: Key -> Int -> [UUID] -> [UUID] -> [Remote.Remote Annex] -> Annex Bool+findCopies key need skip = helper [] where- findcopies need have [] bad+ helper bad have [] | length have >= need = return True- | otherwise = notEnoughCopies need have bad- findcopies need have (r:rs) bad+ | otherwise = notEnoughCopies key need have skip bad+ helper bad have (r:rs) | length have >= need = return True | otherwise = do let u = Remote.uuid r let duplicate = u `elem` have haskey <- Remote.hasKey r key case (duplicate, haskey) of- (False, Right True) -> findcopies need (u:have) rs bad- (False, Left _) -> findcopies need have rs (r:bad)- _ -> findcopies need have rs bad- notEnoughCopies need have bad = do- unsafe- showLongNote $- "Could only verify the existence of " ++- show (length have) ++ " out of " ++ show need ++ - " necessary copies"- Remote.showTriedRemotes bad- Remote.showLocations key have- hint- return False+ (False, Right True) -> helper bad (u:have) rs+ (False, Left _) -> helper (r:bad) have rs+ _ -> helper bad have rs++notEnoughCopies :: Key -> Int -> [UUID] -> [UUID] -> [Remote.Remote Annex] -> Annex Bool+notEnoughCopies key need have skip bad = do+ unsafe+ showLongNote $+ "Could only verify the existence of " +++ show (length have) ++ " out of " ++ show need ++ + " necessary copies"+ Remote.showTriedRemotes bad+ Remote.showLocations key (have++skip)+ hint+ return False+ where unsafe = showNote "unsafe" hint = showLongNote "(Use --force to override this check, or adjust annex.numcopies.)"
Command/DropKey.hs view
@@ -10,11 +10,11 @@ import Common.Annex import Command import qualified Annex-import LocationLog+import Logs.Location import Annex.Content -command :: [Command]-command = [repoCommand "dropkey" (paramRepeating paramKey) seek+def :: [Command]+def = [command "dropkey" (paramRepeating paramKey) seek "drops annexed content for specified keys"] seek :: [CommandSeek]@@ -23,17 +23,19 @@ start :: Key -> CommandStart start key = do present <- inAnnex key- force <- Annex.getState Annex.force if not present then stop- else if not force- then error "dropkey is can cause data loss; use --force if you're sure you want to do this"- else do- showStart "dropkey" (show key)- next $ perform key+ else do+ checkforced+ showStart "dropkey" (show key)+ next $ perform key+ where+ checkforced = + unlessM (Annex.getState Annex.force) $+ error "dropkey can cause data loss; use --force if you're sure you want to do this" perform :: Key -> CommandPerform-perform key = do+perform key = lockContent key $ do removeAnnex key next $ cleanup key
Command/DropUnused.hs view
@@ -13,16 +13,15 @@ import Command import qualified Annex import qualified Command.Drop-import qualified Command.Move import qualified Remote import qualified Git import Types.Key type UnusedMap = M.Map String Key -command :: [Command]-command = [repoCommand "dropunused" (paramRepeating paramNumber) seek- "drop unused file content"]+def :: [Command]+def = [dontCheck fromOpt $ command "dropunused" (paramRepeating paramNumber)+ seek "drop unused file content"] seek :: [CommandSeek] seek = [withUnusedMaps]@@ -36,7 +35,7 @@ return $ map (start (unused, unusedbad, unusedtmp)) params start :: (UnusedMap, UnusedMap, UnusedMap) -> FilePath -> CommandStart-start (unused, unusedbad, unusedtmp) s = notBareRepo $ search+start (unused, unusedbad, unusedtmp) s = search [ (unused, perform) , (unusedbad, performOther gitAnnexBadLocation) , (unusedtmp, performOther gitAnnexTmpLocation)@@ -56,24 +55,22 @@ dropremote name = do r <- Remote.byName name showAction $ "from " ++ Remote.name r- next $ Command.Move.fromCleanup r True key- droplocal = Command.Drop.perform key (Just 0) -- force drop+ ok <- Remote.removeKey r key+ next $ Command.Drop.cleanupRemote key r ok+ droplocal = Command.Drop.performLocal key (Just 0) -- force drop -performOther :: (Git.Repo -> Key -> FilePath) -> Key -> CommandPerform+performOther :: (Key -> Git.Repo -> FilePath) -> Key -> CommandPerform performOther filespec key = do- g <- gitRepo- let f = filespec g key+ f <- fromRepo $ filespec key liftIO $ whenM (doesFileExist f) $ removeFile f next $ return True readUnusedLog :: FilePath -> Annex UnusedMap readUnusedLog prefix = do- g <- gitRepo- let f = gitAnnexUnusedLog prefix g+ f <- fromRepo $ gitAnnexUnusedLog prefix e <- liftIO $ doesFileExist f if e- then return . M.fromList . map parse . lines- =<< liftIO (readFile f)+ then M.fromList . map parse . lines <$> liftIO (readFile f) else return M.empty where parse line = (head ws, fromJust $ readKey $ unwords $ tail ws)
Command/Find.hs view
@@ -11,17 +11,21 @@ import Command import Annex.Content import Limit+import qualified Annex -command :: [Command]-command = [repoCommand "find" paramPaths seek "lists available files"]+def :: [Command]+def = [command "find" paramPaths seek "lists available files"] seek :: [CommandSeek]-seek = [withFilesInGit start]+seek = [withFilesInGit $ whenAnnexed start] -start :: FilePath -> CommandStart-start file = isAnnexed file $ \(key, _) -> do+start :: FilePath -> (Key, Backend Annex) -> CommandStart+start file (key, _) = do -- only files inAnnex are shown, unless the user has requested -- others via a limit- whenM (liftM2 (||) (inAnnex key) limited) $- liftIO $ putStrLn file+ whenM (liftM2 (||) (inAnnex key) limited) $ do+ print0 <- Annex.getState Annex.print0+ if print0+ then liftIO $ putStr (file ++ "\0")+ else liftIO $ putStrLn file stop
Command/Fix.hs view
@@ -12,16 +12,16 @@ import qualified Annex.Queue import Annex.Content -command :: [Command]-command = [repoCommand "fix" paramPaths seek+def :: [Command]+def = [command "fix" paramPaths seek "fix up symlinks to point to annexed content"] seek :: [CommandSeek]-seek = [withFilesInGit start]+seek = [withFilesInGit $ whenAnnexed start] {- Fixes the symlink to an annexed file. -}-start :: FilePath -> CommandStart-start file = isAnnexed file $ \(key, _) -> do+start :: FilePath -> (Key, Backend Annex) -> CommandStart+start file (key, _) = do link <- calcGitLink file key l <- liftIO $ readSymbolicLink file if link == l@@ -39,5 +39,5 @@ cleanup :: FilePath -> CommandCleanup cleanup file = do- Annex.Queue.add "add" [Param "--"] [file]+ Annex.Queue.add "add" [Param "--force", Param "--"] [file] return True
Command/FromKey.hs view
@@ -13,25 +13,25 @@ import Annex.Content import Types.Key -command :: [Command]-command = [repoCommand "fromkey" paramPath seek+def :: [Command]+def = [command "fromkey" (paramPair paramKey paramPath) seek "adds a file using a specific key"] seek :: [CommandSeek]-seek = [withFilesMissing start]+seek = [withWords start] -start :: FilePath -> CommandStart-start file = notBareRepo $ do- key <- cmdlineKey+start :: [String] -> CommandStart+start (keyname:file:[]) = notBareRepo $ do+ let key = fromMaybe (error "bad key") $ readKey keyname inbackend <- inAnnex key unless inbackend $ error $- "key ("++keyName key++") is not present in backend"+ "key ("++ keyname ++") is not present in backend" showStart "fromkey" file- next $ perform file+ next $ perform key file+start _ = error "specify a key and a dest file" -perform :: FilePath -> CommandPerform-perform file = do- key <- cmdlineKey+perform :: Key -> FilePath -> CommandPerform+perform key file = do link <- calcGitLink file key liftIO $ createDirectoryIfMissing True (parentDir file) liftIO $ createSymbolicLink link file
Command/Fsck.hs view
@@ -1,6 +1,6 @@ {- git-annex command -- - Copyright 2010 Joey Hess <joey@kitenet.net>+ - Copyright 2010-2011 Joey Hess <joey@kitenet.net> - - Licensed under the GNU GPL version 3 or higher. -}@@ -12,85 +12,113 @@ import qualified Remote import qualified Types.Backend import qualified Types.Key-import UUID+import qualified Backend import Annex.Content-import LocationLog-import Trust+import Logs.Location+import Logs.Trust+import Annex.UUID import Utility.DataUnits import Utility.FileMode import Config -command :: [Command]-command = [repoCommand "fsck" paramPaths seek "check for problems"]+def :: [Command]+def = [command "fsck" paramPaths seek "check for problems"] seek :: [CommandSeek]-seek = [withNumCopies start]+seek =+ [ withNumCopies $ \n -> whenAnnexed $ start n+ , withBarePresentKeys startBare+ ] -start :: FilePath -> Maybe Int -> CommandStart-start file numcopies = notBareRepo $ isAnnexed file $ \(key, backend) -> do+start :: Maybe Int -> FilePath -> (Key, Backend Annex) -> CommandStart+start numcopies file (key, backend) = do showStart "fsck" file next $ perform key file backend numcopies perform :: Key -> FilePath -> Backend Annex -> Maybe Int -> CommandPerform-perform key file backend numcopies = do- -- the location log is checked first, so that if it has bad data- -- that gets corrected- locationlogok <- verifyLocationLog key file- backendok <- fsckKey backend key (Just file) numcopies- if locationlogok && backendok- then next $ return True- else stop+perform key file backend numcopies = check+ -- order matters+ [ verifyLocationLog key file+ , checkKeySize key+ , checkKeyNumCopies key file numcopies+ , checkBackend backend key+ ] +{- To fsck a bare repository, fsck each key in the location log. -}+withBarePresentKeys :: (Key -> CommandStart) -> CommandSeek+withBarePresentKeys a params = isBareRepo >>= go+ where+ go False = return []+ go True = do+ unless (null params) $+ error "fsck should be run without parameters in a bare repository"+ prepStart a loggedKeys++startBare :: Key -> CommandStart+startBare key = case Backend.maybeLookupBackendName (Types.Key.keyBackendName key) of+ Nothing -> stop+ Just backend -> do+ showStart "fsck" (show key)+ next $ performBare key backend++{- Note that numcopies cannot be checked in a bare repository, because+ - getting the numcopies value requires a working copy with .gitattributes+ - files. -}+performBare :: Key -> Backend Annex -> CommandPerform+performBare key backend = check+ [ verifyLocationLog key (show key)+ , checkKeySize key+ , checkBackend backend key+ ]++check :: [Annex Bool] -> CommandPerform +check = sequence >=> dispatch+ where+ dispatch vs+ | all (== True) vs = next $ return True+ | otherwise = stop+ {- Checks that the location log reflects the current status of the key, in this repository only. -}-verifyLocationLog :: Key -> FilePath -> Annex Bool-verifyLocationLog key file = do- g <- gitRepo+verifyLocationLog :: Key -> String -> Annex Bool+verifyLocationLog key desc = do present <- inAnnex key -- Since we're checking that a key's file is present, throw -- in a permission fixup here too.- when present $ liftIO $ do- let f = gitAnnexLocation g key- preventWrite f- preventWrite (parentDir f)+ when present $ do+ f <- fromRepo $ gitAnnexLocation key+ liftIO $ do+ preventWrite f+ preventWrite (parentDir f) u <- getUUID uuids <- keyLocations key case (present, u `elem` uuids) of (True, False) -> do- fix g u InfoPresent+ fix u InfoPresent -- There is no data loss, so do not fail. return True (False, True) -> do- fix g u InfoMissing+ fix u InfoMissing warning $- "** Based on the location log, " ++ file+ "** Based on the location log, " ++ desc ++ "\n** was expected to be present, " ++ "but its content is missing." return False _ -> return True where- fix g u s = do+ fix u s = do showNote "fixing location log"- logChange g key u s--{- Checks a key for problems. -}-fsckKey :: Backend Annex -> Key -> Maybe FilePath -> Maybe Int -> Annex Bool-fsckKey backend key file numcopies = do- size_ok <- checkKeySize key- copies_ok <- checkKeyNumCopies key file numcopies- backend_ok <- (Types.Backend.fsckKey backend) key- return $ size_ok && copies_ok && backend_ok+ logChange key u s {- The size of the data for a key is checked against the size encoded in - the key's metadata, if available. -} checkKeySize :: Key -> Annex Bool checkKeySize key = do- g <- gitRepo- let file = gitAnnexLocation g key+ file <- fromRepo $ gitAnnexLocation key present <- liftIO $ doesFileExist file case (present, Types.Key.keySize key) of (_, Nothing) -> return True@@ -108,7 +136,10 @@ return False -checkKeyNumCopies :: Key -> Maybe FilePath -> Maybe Int -> Annex Bool+checkBackend :: Backend Annex -> Key -> Annex Bool+checkBackend = Types.Backend.fsckKey++checkKeyNumCopies :: Key -> FilePath -> Maybe Int -> Annex Bool checkKeyNumCopies key file numcopies = do needed <- getNumCopies numcopies (untrustedlocations, safelocations) <- trustPartition UnTrusted =<< keyLocations key@@ -116,12 +147,9 @@ if present < needed then do ppuuids <- Remote.prettyPrintUUIDs "untrusted" untrustedlocations- warning $ missingNote (filename file key) present needed ppuuids+ warning $ missingNote file present needed ppuuids return False else return True- where- filename Nothing k = show k- filename (Just f) _ = f missingNote :: String -> Int -> Int -> String -> String missingNote file 0 _ [] =
Command/Get.hs view
@@ -14,27 +14,33 @@ import Annex.Content import qualified Command.Move -command :: [Command]-command = [repoCommand "get" paramPaths seek- "make content of annexed files available"]+def :: [Command]+def = [dontCheck fromOpt $ command "get" paramPaths seek+ "make content of annexed files available"] seek :: [CommandSeek]-seek = [withNumCopies start]+seek = [withNumCopies $ \n -> whenAnnexed $ start n] -start :: FilePath -> Maybe Int -> CommandStart-start file numcopies = isAnnexed file $ \(key, _) -> do+start :: Maybe Int -> FilePath -> (Key, Backend Annex) -> CommandStart+start numcopies file (key, _) = do inannex <- inAnnex key if inannex then stop else autoCopies key (<) numcopies $ do- showStart "get" file from <- Annex.getState Annex.fromremote case from of- Nothing -> next $ perform key+ Nothing -> go $ perform key Just name -> do -- get --from = copy --from src <- Remote.byName name- next $ Command.Move.fromPerform src False key+ ok <- Command.Move.fromOk src key+ if ok+ then go $ Command.Move.fromPerform src False key+ else stop+ where+ go a = do+ showStart "get" file+ next a perform :: Key -> CommandPerform perform key = do
Command/InAnnex.hs view
@@ -11,16 +11,17 @@ import Command import Annex.Content -command :: [Command]-command = [repoCommand "inannex" (paramRepeating paramKey) seek- "checks if keys are present in the annex"]+def :: [Command]+def = [command "inannex" (paramRepeating paramKey) seek+ "checks if keys are present in the annex"] seek :: [CommandSeek] seek = [withKeys start] start :: Key -> CommandStart-start key = do- present <- inAnnex key- if present- then stop- else liftIO exitFailure+start key = inAnnexSafe key >>= dispatch+ where+ dispatch (Just True) = stop+ dispatch (Just False) = exit 1+ dispatch Nothing = exit 100+ exit n = liftIO $ exitWith $ ExitFailure n
Command/Init.hs view
@@ -9,12 +9,11 @@ import Common.Annex import Command-import UUID import Init -command :: [Command]-command = [standaloneCommand "init" paramDesc seek- "initialize git-annex"]+def :: [Command]+def = [dontCheck repoExists $+ command "init" paramDesc seek "initialize git-annex"] seek :: [CommandSeek] seek = [withWords start]@@ -28,7 +27,5 @@ perform :: String -> CommandPerform perform description = do- initialize- u <- getUUID- describeUUID u description+ initialize $ if null description then Nothing else Just description next $ return True
Command/InitRemote.hs view
@@ -12,15 +12,14 @@ import Common.Annex import Command import qualified Remote-import qualified RemoteLog+import qualified Logs.Remote import qualified Types.Remote as R-import UUID+import Annex.UUID -command :: [Command]-command = [repoCommand "initremote"- (paramPair paramName $- paramOptional $ paramRepeating paramKeyValue) seek- "sets up a special (non-git) remote"]+def :: [Command]+def = [command "initremote"+ (paramPair paramName $ paramOptional $ paramRepeating paramKeyValue)+ seek "sets up a special (non-git) remote"] seek :: [CommandSeek] seek = [withWords start]@@ -38,7 +37,7 @@ where name = head ws- config = RemoteLog.keyValToConfig $ tail ws+ config = Logs.Remote.keyValToConfig $ tail ws needname = do let err s = error $ "Specify a name for the remote. " ++ s names <- remoteNames@@ -54,13 +53,13 @@ cleanup :: UUID -> R.RemoteConfig -> CommandCleanup cleanup u c = do- RemoteLog.configSet u c+ Logs.Remote.configSet u c return True {- Look up existing remote's UUID and config by name, or generate a new one -} findByName :: String -> Annex (UUID, R.RemoteConfig) findByName name = do- m <- RemoteLog.readRemoteLog+ m <- Logs.Remote.readRemoteLog maybe generate return $ findByName' name m where generate = do@@ -68,7 +67,9 @@ return (uuid, M.insert nameKey name M.empty) findByName' :: String -> M.Map UUID R.RemoteConfig -> Maybe (UUID, R.RemoteConfig)-findByName' n m = if null matches then Nothing else Just $ head matches+findByName' n m+ | null matches = Nothing+ | otherwise = Just $ head matches where matches = filter (matching . snd) $ M.toList m matching c = case M.lookup nameKey c of@@ -79,7 +80,7 @@ remoteNames :: Annex [String] remoteNames = do- m <- RemoteLog.readRemoteLog+ m <- Logs.Remote.readRemoteLog return $ mapMaybe (M.lookup nameKey . snd) $ M.toList m {- find the specified remote type -}
Command/Lock.hs view
@@ -12,8 +12,8 @@ import qualified Annex.Queue import Backend -command :: [Command]-command = [repoCommand "lock" paramPaths seek "undo unlock command"]+def :: [Command]+def = [command "lock" paramPaths seek "undo unlock command"] seek :: [CommandSeek] seek = [withFilesUnlocked start, withFilesUnlockedToBeCommitted start]
Command/Map.hs view
@@ -13,24 +13,25 @@ import Common.Annex import Command import qualified Git-import UUID-import Trust-import Utility.Ssh+import Annex.UUID+import Logs.UUID+import Logs.Trust+import Annex.Ssh import qualified Utility.Dot as Dot -- a link from the first repository to the second (its remote) data Link = Link Git.Repo Git.Repo -command :: [Command]-command = [repoCommand "map" paramNothing seek "generate map of repositories"]+def :: [Command]+def = [dontCheck repoExists $+ command "map" paramNothing seek "generate map of repositories"] seek :: [CommandSeek] seek = [withNothing start] start :: CommandStart start = do- g <- gitRepo- rs <- spider g+ rs <- spider =<< gitRepo umap <- uuidMap trusted <- trustGet Trusted@@ -60,7 +61,8 @@ others = map (unreachable . uuidnode) $ filter (`notElem` ruuids) (M.keys umap) trusted = map (trustworthy . uuidnode) ts- uuidnode u = Dot.graphNode u $ M.findWithDefault "" u umap+ uuidnode u = Dot.graphNode (fromUUID u) $+ M.findWithDefault "" u umap hostname :: Git.Repo -> String hostname r@@ -74,7 +76,7 @@ - or the remote name if not. -} repoName :: M.Map UUID String -> Git.Repo -> String repoName umap r- | null repouuid = fallback+ | repouuid == NoUUID = fallback | otherwise = M.findWithDefault fallback repouuid umap where repouuid = getUncachedUUID r@@ -84,8 +86,8 @@ nodeId :: Git.Repo -> String nodeId r = case getUncachedUUID r of- "" -> Git.repoLocation r- u -> u+ NoUUID -> Git.repoLocation r+ UUID u -> u {- A node representing a repo. -} node :: M.Map UUID String -> [Git.Repo] -> Git.Repo -> String@@ -193,11 +195,17 @@ configlist = onRemote r (pipedconfig, Nothing) "configlist" [] manualconfiglist = do- let sshcmd =- "cd " ++ shellEscape(Git.workTree r) ++ " && " ++- "git config --list" sshparams <- sshToRepo r [Param sshcmd] liftIO $ pipedconfig "ssh" sshparams+ where+ sshcmd = cddir ++ " && " +++ "git config --list"+ dir = Git.workTree r+ cddir+ | take 2 dir == "/~" =+ let (userhome, reldir) = span (/= '/') (drop 1 dir)+ in "cd " ++ userhome ++ " && cd " ++ shellEscape (drop 1 reldir)+ | otherwise = "cd " ++ shellEscape dir -- First, try sshing and running git config manually, -- only fall back to git-annex-shell configlist if that
Command/Merge.hs view
@@ -11,8 +11,8 @@ import Command import qualified Annex.Branch -command :: [Command]-command = [repoCommand "merge" paramNothing seek+def :: [Command]+def = [command "merge" paramNothing seek "auto-merge remote changes into git-annex branch"] seek :: [CommandSeek]
Command/Migrate.hs view
@@ -13,17 +13,16 @@ import qualified Types.Key import Annex.Content import qualified Command.Add-import Backend+import Logs.Web -command :: [Command]-command = [repoCommand "migrate" paramPaths seek- "switch data to different backend"]+def :: [Command]+def = [command "migrate" paramPaths seek "switch data to different backend"] seek :: [CommandSeek]-seek = [withBackendFilesInGit start]+seek = [withBackendFilesInGit $ \(b, f) -> whenAnnexed (start b) f] -start :: BackendFile -> CommandStart-start (b, file) = isAnnexed file $ \(key, oldbackend) -> do+start :: Maybe (Backend Annex) -> FilePath -> (Key, Backend Annex) -> CommandStart+start b file (key, oldbackend) = do exists <- inAnnex key newbackend <- choosebackend b if (newbackend /= oldbackend || upgradableKey key) && exists@@ -40,32 +39,45 @@ upgradableKey :: Key -> Bool upgradableKey key = isNothing $ Types.Key.keySize key +{- Store the old backend's key in the new backend+ - The old backend's key is not dropped from it, because there may+ - be other files still pointing at that key.+ -+ - Use the same filename as the file for the temp file name, to support+ - backends that allow the filename to influence the keys they+ - generate.+ -} perform :: FilePath -> Key -> Backend Annex -> CommandPerform perform file oldkey newbackend = do- g <- gitRepo-- -- Store the old backend's cached key in the new backend- -- (the file can't be stored as usual, because it's already a symlink).- -- The old backend's key is not dropped from it, because there may- -- be other files still pointing at that key.- let src = gitAnnexLocation g oldkey- let tmpfile = gitAnnexTmpDir g </> takeFileName file+ src <- fromRepo $ gitAnnexLocation oldkey+ tmp <- fromRepo gitAnnexTmpDir+ let tmpfile = tmp </> takeFileName file+ cleantmp tmpfile liftIO $ createLink src tmpfile k <- Backend.genKey tmpfile $ Just newbackend- liftIO $ cleantmp tmpfile+ cleantmp tmpfile case k of Nothing -> stop Just (newkey, _) -> do- ok <- getViaTmpUnchecked newkey $ \t -> do- -- Make a hard link to the old backend's- -- cached key, to avoid wasting disk space.- liftIO $ unlessM (doesFileExist t) $ createLink src t- return True+ ok <- link src newkey if ok then do -- Update symlink to use the new key. liftIO $ removeFile file++ -- If the old key had some+ -- associated urls, record them for+ -- the new key as well.+ urls <- getUrls oldkey+ unless (null urls) $+ mapM_ (setUrlPresent newkey) urls+ next $ Command.Add.cleanup file newkey True else stop where- cleantmp t = whenM (doesFileExist t) $ removeFile t+ cleantmp t = liftIO $ whenM (doesFileExist t) $ removeFile t+ link src newkey = getViaTmpUnchecked newkey $ \t -> do+ -- Make a hard link to the old backend's+ -- cached key, to avoid wasting disk space.+ liftIO $ unlessM (doesFileExist t) $ createLink src t+ return True
Command/Move.hs view
@@ -1,6 +1,6 @@ {- git-annex command -- - Copyright 2010 Joey Hess <joey@kitenet.net>+ - Copyright 2010-2011 Joey Hess <joey@kitenet.net> - - Licensed under the GNU GPL version 3 or higher. -}@@ -11,24 +11,20 @@ import Command import qualified Command.Drop import qualified Annex-import LocationLog import Annex.Content import qualified Remote-import UUID+import Annex.UUID -command :: [Command]-command = [repoCommand "move" paramPaths seek+def :: [Command]+def = [dontCheck toOpt $ dontCheck fromOpt $+ command "move" paramPaths seek "move content of files to/from another repository"] seek :: [CommandSeek]-seek = [withFilesInGit $ start True]+seek = [withFilesInGit $ whenAnnexed $ start True] -{- Move (or copy) a file either --to or --from a repository.- -- - This only operates on the cached file content; it does not involve- - moving data in the key-value backend. -}-start :: Bool -> FilePath -> CommandStart-start move file = do+start :: Bool -> FilePath -> (Key, Backend Annex) -> CommandStart+start move file (key, _) = do noAuto to <- Annex.getState Annex.toremote from <- Annex.getState Annex.fromremote@@ -36,10 +32,10 @@ (Nothing, Nothing) -> error "specify either --from or --to" (Nothing, Just name) -> do dest <- Remote.byName name- toStart dest move file+ toStart dest move file key (Just name, Nothing) -> do src <- Remote.byName name- fromStart src move file+ fromStart src move file key (_ , _) -> error "only one of --from or --to can be specified" where noAuto = when move $ whenM (Annex.getState Annex.auto) $ error@@ -49,18 +45,6 @@ showMoveAction True file = showStart "move" file showMoveAction False file = showStart "copy" file -{- Used to log a change in a remote's having a key. The change is logged- - in the local repo, not on the remote. The process of transferring the- - key to the remote, or removing the key from it *may* log the change- - on the remote, but this cannot be relied on. -}-remoteHasKey :: Remote.Remote Annex -> Key -> Bool -> Annex ()-remoteHasKey remote key present = do- let remoteuuid = Remote.uuid remote- g <- gitRepo- logChange g key remoteuuid status- where- status = if present then InfoPresent else InfoMissing- {- Moves (or copies) the content of an annexed file to a remote. - - If the remote already has the content, it is still removed from@@ -70,8 +54,8 @@ - A file's content can be moved even if there are insufficient copies to - allow it to be dropped. -}-toStart :: Remote.Remote Annex -> Bool -> FilePath -> CommandStart-toStart dest move file = isAnnexed file $ \(key, _) -> do+toStart :: Remote.Remote Annex -> Bool -> FilePath -> Key -> CommandStart+toStart dest move file key = do u <- getUUID ishere <- inAnnex key if not ishere || u == Remote.uuid dest@@ -80,7 +64,7 @@ showMoveAction move file next $ toPerform dest move key toPerform :: Remote.Remote Annex -> Bool -> Key -> CommandPerform-toPerform dest move key = do+toPerform dest move key = moveLock move key $ do -- Checking the remote is expensive, so not done in the start step. -- In fast mode, location tracking is assumed to be correct, -- and an explicit check is not done, when copying. When moving,@@ -94,24 +78,26 @@ else Remote.hasKey dest key case isthere of Left err -> do- showNote $ show err+ showNote err stop Right False -> do showAction $ "to " ++ Remote.name dest ok <- Remote.storeKey dest key if ok- then next $ toCleanup dest move key+ then finish else do when fastcheck $ warning "This could have failed because --fast is enabled." stop- Right True -> next $ toCleanup dest move key-toCleanup :: Remote.Remote Annex -> Bool -> Key -> CommandCleanup-toCleanup dest move key = do- remoteHasKey dest key True- if move- then Command.Drop.cleanup key- else return True+ Right True -> finish+ where+ finish = do+ Remote.logStatus dest key True+ if move+ then do+ whenM (inAnnex key) $ removeAnnex key+ next $ Command.Drop.cleanupLocal key+ else next $ return True {- Moves (or copies) the content of an annexed file from a remote - to the current repository.@@ -119,32 +105,43 @@ - If the current repository already has the content, it is still removed - from the remote. -}-fromStart :: Remote.Remote Annex -> Bool -> FilePath -> CommandStart-fromStart src move file = isAnnexed file $ \(key, _) -> do+fromStart :: Remote.Remote Annex -> Bool -> FilePath -> Key -> CommandStart+fromStart src move file key+ | move = go+ | otherwise = do+ ishere <- inAnnex key+ if ishere then stop else go+ where+ go = do+ ok <- fromOk src key+ if ok+ then do+ showMoveAction move file+ next $ fromPerform src move key+ else stop+fromOk :: Remote.Remote Annex -> Key -> Annex Bool+fromOk src key = do u <- getUUID remotes <- Remote.keyPossibilities key- if u == Remote.uuid src || not (any (== src) remotes)- then stop- else do- showMoveAction move file- next $ fromPerform src move key+ return $ u /= Remote.uuid src && any (== src) remotes fromPerform :: Remote.Remote Annex -> Bool -> Key -> CommandPerform-fromPerform src move key = do+fromPerform src move key = moveLock move key $ do ishere <- inAnnex key if ishere- then next $ fromCleanup src move key+ then handle move True else do showAction $ "from " ++ Remote.name src ok <- getViaTmp key $ Remote.retrieveKeyFile src key- if ok- then next $ fromCleanup src move key- else stop -- fail-fromCleanup :: Remote.Remote Annex -> Bool -> Key -> CommandCleanup-fromCleanup src True key = do- ok <- Remote.removeKey src key- -- better safe than sorry: assume the src dropped the key- -- even if it seemed to fail; the failure could have occurred- -- after it really dropped it- remoteHasKey src key False- return ok-fromCleanup _ False _ = return True+ handle move ok+ where+ handle _ False = stop -- failed+ handle False True = next $ return True -- copy complete+ handle True True = do -- finish moving+ ok <- Remote.removeKey src key+ next $ Command.Drop.cleanupRemote key src ok++{- Locks a key in order for it to be moved.+ - No lock is needed when a key is being copied. -}+moveLock :: Bool -> Key -> Annex a -> Annex a+moveLock True key a = lockContent key a+moveLock False _ a = a
Command/PreCommit.hs view
@@ -12,14 +12,15 @@ import qualified Command.Fix import Backend -command :: [Command]-command = [repoCommand "pre-commit" paramPaths seek "run by git pre-commit hook"]+def :: [Command]+def = [command "pre-commit" paramPaths seek "run by git pre-commit hook"] {- The pre-commit hook needs to fix symlinks to all files being committed. - And, it needs to inject unlocked files into the annex. -} seek :: [CommandSeek]-seek = [withFilesToBeCommitted Command.Fix.start,- withFilesUnlockedToBeCommitted start]+seek =+ [ withFilesToBeCommitted $ whenAnnexed Command.Fix.start+ , withFilesUnlockedToBeCommitted start] start :: BackendFile -> CommandStart start p = next $ perform p
Command/RecvKey.hs view
@@ -13,8 +13,8 @@ import Annex.Content import Utility.RsyncFile -command :: [Command]-command = [repoCommand "recvkey" paramKey seek+def :: [Command]+def = [command "recvkey" paramKey seek "runs rsync in server mode to receive content"] seek :: [CommandSeek]
+ Command/Reinject.hs view
@@ -0,0 +1,51 @@+{- git-annex command+ -+ - Copyright 2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Command.Reinject where++import Common.Annex+import Command+import Logs.Location+import Annex.Content+import qualified Command.Fsck++def :: [Command]+def = [command "reinject" (paramPair "SRC" "DEST") seek+ "sets content of annexed file"]++seek :: [CommandSeek]+seek = [withWords start]++start :: [FilePath] -> CommandStart+start (src:dest:[])+ | src == dest = stop+ | otherwise = do+ showStart "reinject" dest+ next $ whenAnnexed (perform src) dest+start _ = error "specify a src file and a dest file"++perform :: FilePath -> FilePath -> (Key, Backend Annex) -> CommandPerform+perform src _dest (key, backend) = do+ unlessM move $ error "mv failed!"+ next $ cleanup key backend+ where+ -- the file might be on a different filesystem,+ -- so mv is used rather than simply calling+ -- moveToObjectDir; disk space is also+ -- checked this way.+ move = getViaTmp key $ \tmp ->+ liftIO $ boolSystem "mv" [File src, File tmp]++cleanup :: Key -> Backend Annex -> CommandCleanup+cleanup key backend = do+ logStatus key InfoPresent++ -- fsck the new content+ size_ok <- Command.Fsck.checkKeySize key+ backend_ok <- Command.Fsck.checkBackend backend key++ return $ size_ok && backend_ok
Command/Semitrust.hs view
@@ -10,11 +10,10 @@ import Common.Annex import Command import qualified Remote-import UUID-import Trust+import Logs.Trust -command :: [Command]-command = [repoCommand "semitrust" (paramRepeating paramRemote) seek+def :: [Command]+def = [command "semitrust" (paramRepeating paramRemote) seek "return repository to default trust level"] seek :: [CommandSeek]
Command/SendKey.hs view
@@ -12,8 +12,8 @@ import Annex.Content import Utility.RsyncFile -command :: [Command]-command = [repoCommand "sendkey" paramKey seek+def :: [Command]+def = [command "sendkey" paramKey seek "runs rsync in server mode to send content"] seek :: [CommandSeek]@@ -21,8 +21,7 @@ start :: Key -> CommandStart start key = do- g <- gitRepo- let file = gitAnnexLocation g key+ file <- fromRepo $ gitAnnexLocation key whenM (inAnnex key) $ liftIO $ rsyncServerSend file -- does not return warning "requested key is not present"
− Command/SetKey.hs
@@ -1,47 +0,0 @@-{- git-annex command- -- - Copyright 2010 Joey Hess <joey@kitenet.net>- -- - Licensed under the GNU GPL version 3 or higher.- -}--module Command.SetKey where--import Common.Annex-import Command-import LocationLog-import Annex.Content--command :: [Command]-command = [repoCommand "setkey" paramPath seek- "sets annexed content for a key using a temp file"]--seek :: [CommandSeek]-seek = [withStrings start]--{- Sets cached content for a key. -}-start :: FilePath -> CommandStart-start file = do- showStart "setkey" file- next $ perform file--perform :: FilePath -> CommandPerform-perform file = do- key <- cmdlineKey- -- the file might be on a different filesystem, so mv is used- -- rather than simply calling moveToObjectDir; disk space is also- -- checked this way.- ok <- getViaTmp key $ \dest ->- if dest /= file- then liftIO $- boolSystem "mv" [File file, File dest]- else return True- if ok- then next cleanup- else error "mv failed!"--cleanup :: CommandCleanup-cleanup = do- key <- cmdlineKey- logStatus key InfoPresent- return True
Command/Status.hs view
@@ -11,6 +11,7 @@ import qualified Data.Map as M import qualified Data.Set as S import Data.Set (Set)+import Text.JSON import Common.Annex import qualified Types.Backend as B@@ -18,12 +19,14 @@ import qualified Remote import qualified Command.Unused import qualified Git+import qualified Annex import Command import Utility.DataUnits import Annex.Content import Types.Key import Backend-import UUID+import Logs.UUID+import Logs.Trust import Remote -- a named computation that produces a statistic@@ -38,8 +41,8 @@ -- a state monad for running Stats in type StatState = StateT StatInfo Annex -command :: [Command]-command = [repoCommand "status" paramNothing seek+def :: [Command]+def = [command "status" paramNothing seek "shows status information about the annex"] seek :: [CommandSeek]@@ -48,69 +51,90 @@ {- Order is significant. Less expensive operations, and operations - that share data go together. -}-stats :: [Stat]-stats = +fast_stats :: [Stat]+fast_stats = [ supported_backends , supported_remote_types- , remote_list- , tmp_size+ , remote_list Trusted "trusted"+ , remote_list SemiTrusted "semitrusted"+ , remote_list UnTrusted "untrusted"+ ]+slow_stats :: [Stat]+slow_stats = + [ tmp_size , bad_data_size , local_annex_keys , local_annex_size- , total_annex_keys- , total_annex_size+ , visible_annex_keys+ , visible_annex_size , backend_usage ] start :: CommandStart start = do- evalStateT (mapM_ showStat stats) (StatInfo Nothing Nothing)+ fast <- Annex.getState Annex.fast+ let stats = if fast then fast_stats else fast_stats ++ slow_stats+ showCustom "status" $ do+ evalStateT (mapM_ showStat stats) (StatInfo Nothing Nothing)+ return True stop -stat :: String -> StatState String -> Stat-stat desc a = return $ Just (desc, a)+stat :: String -> (String -> StatState String) -> Stat+stat desc a = return $ Just (desc, a desc) nostat :: Stat nostat = return Nothing +json :: JSON j => (j -> String) -> StatState j -> String -> StatState String+json serialize a desc = do+ j <- a+ lift $ maybeShowJSON [(desc, j)]+ return $ serialize j++nojson :: StatState String -> String -> StatState String+nojson a _ = a+ showStat :: Stat -> StatState () showStat s = calc =<< s where calc (Just (desc, a)) = do- liftIO $ putStr $ desc ++ ": "- liftIO $ hFlush stdout- liftIO . putStrLn =<< a+ (lift . showHeader) desc+ lift . showRaw =<< a calc Nothing = return () supported_backends :: Stat-supported_backends = stat "supported backends" $ - return $ unwords $ map B.name Backend.list+supported_backends = stat "supported backends" $ json unwords $+ return $ map B.name Backend.list supported_remote_types :: Stat-supported_remote_types = stat "supported remote types" $- return $ unwords $ map R.typename Remote.remoteTypes+supported_remote_types = stat "supported remote types" $ json unwords $+ return $ map R.typename Remote.remoteTypes -remote_list :: Stat-remote_list = stat "known repositories" $ lift $ do- s <- prettyPrintUUIDs "repos" =<< M.keys <$> uuidMap- return $ '\n':init s+remote_list :: TrustLevel -> String -> Stat+remote_list level desc = stat n $ nojson $ lift $ do+ us <- M.keys <$> (M.union <$> uuidMap <*> remoteMap)+ rs <- fst <$> trustPartition level us+ s <- prettyPrintUUIDs n rs+ return $ if null s then "0" else show (length rs) ++ "\n" ++ init s+ where+ n = desc ++ " repositories" local_annex_size :: Stat-local_annex_size = stat "local annex size" $+local_annex_size = stat "local annex size" $ json id $ keySizeSum <$> cachedKeysPresent -total_annex_size :: Stat-total_annex_size = stat "total annex size" $- keySizeSum <$> cachedKeysReferenced- local_annex_keys :: Stat-local_annex_keys = stat "local annex keys" $- show . S.size <$> cachedKeysPresent+local_annex_keys = stat "local annex keys" $ json show $+ S.size <$> cachedKeysPresent -total_annex_keys :: Stat-total_annex_keys = stat "total annex keys" $- show . S.size <$> cachedKeysReferenced+visible_annex_size :: Stat+visible_annex_size = stat "visible annex size" $ json id $+ keySizeSum <$> cachedKeysReferenced +visible_annex_keys :: Stat+visible_annex_keys = stat "visible annex keys" $ json show $+ S.size <$> cachedKeysReferenced+ tmp_size :: Stat tmp_size = staleSize "temporary directory size" gitAnnexTmpDir @@ -118,9 +142,10 @@ bad_data_size = staleSize "bad keys size" gitAnnexBadDir backend_usage :: Stat-backend_usage = stat "backend usage" $ usage <$> cachedKeysReferenced+backend_usage = stat "backend usage" $ nojson $+ usage <$> cachedKeysReferenced <*> cachedKeysPresent where- usage ks = pp "" $ reverse . sort $ map swap $ splits $ S.toList ks+ usage a b = pp "" $ reverse . sort $ map swap $ splits $ S.toList $ S.union a b splits :: [Key] -> [(String, Integer)] splits ks = M.toList $ M.fromListWith (+) $ map tcount ks tcount k = (keyBackendName k, 1)@@ -165,9 +190,9 @@ keys <- lift (Command.Unused.staleKeys dirspec) if null keys then nostat- else stat label $ do- let s = keySizeSum $ S.fromList keys- return $ s ++ aside "clean up with git-annex unused"+ else do+ stat label $ json (++ aside "clean up with git-annex unused") $+ return $ keySizeSum $ S.fromList keys aside :: String -> String aside s = " (" ++ s ++ ")"
Command/Trust.hs view
@@ -10,12 +10,10 @@ import Common.Annex import Command import qualified Remote-import Trust-import UUID+import Logs.Trust -command :: [Command]-command = [repoCommand "trust" (paramRepeating paramRemote) seek- "trust a repository"]+def :: [Command]+def = [command "trust" (paramRepeating paramRemote) seek "trust a repository"] seek :: [CommandSeek] seek = [withWords start]
Command/Unannex.hs view
@@ -9,31 +9,30 @@ import Common.Annex import Command-import qualified Command.Drop import qualified Annex import qualified Annex.Queue import Utility.FileMode-import LocationLog+import Logs.Location import Annex.Content import qualified Git import qualified Git.LsFiles as LsFiles -command :: [Command]-command = [repoCommand "unannex" paramPaths seek "undo accidential add command"]+def :: [Command]+def = [command "unannex" paramPaths seek "undo accidential add command"] seek :: [CommandSeek]-seek = [withFilesInGit start]+seek = [withFilesInGit $ whenAnnexed start] {- The unannex subcommand undoes an add. -}-start :: FilePath -> CommandStart-start file = isAnnexed file $ \(key, _) -> do+start :: FilePath -> (Key, Backend Annex) -> CommandStart+start file (key, _) = do ishere <- inAnnex key if ishere then do force <- Annex.getState Annex.force unless force $ do- g <- gitRepo- staged <- liftIO $ LsFiles.staged g [Git.workTree g]+ top <- fromRepo Git.workTree+ staged <- inRepo $ LsFiles.staged [top] unless (null staged) $ error "This command cannot be run when there are already files staged for commit." Annex.changeState $ \s -> s { Annex.force = True }@@ -43,27 +42,23 @@ else stop perform :: FilePath -> Key -> CommandPerform-perform file key = do- ok <- Command.Drop.dropKey key (Just 0) -- always remove- if ok- then next $ cleanup file key- else stop+perform file key = next $ cleanup file key cleanup :: FilePath -> Key -> CommandCleanup cleanup file key = do- g <- gitRepo- liftIO $ removeFile file- liftIO $ Git.run g "rm" [Params "--quiet --", File file]+ inRepo $ Git.run "rm" [Params "--quiet --", File file] -- git rm deletes empty directories; put them back liftIO $ createDirectoryIfMissing True (parentDir file) fast <- Annex.getState Annex.fast if fast- then liftIO $ do+ then do -- fast mode: hard link to content in annex- createLink (gitAnnexLocation g key) file- allowWrite file+ src <- fromRepo $ gitAnnexLocation key+ liftIO $ do+ createLink src file+ allowWrite file else do fromAnnex key file logStatus key InfoMissing
Command/Uninit.hs view
@@ -7,6 +7,8 @@ module Command.Uninit where +import qualified Data.ByteString.Lazy.Char8 as B+ import Common.Annex import Command import qualified Git@@ -16,20 +18,30 @@ import qualified Annex.Branch import Annex.Content -command :: [Command]-command = [repoCommand "uninit" paramPaths seek +def :: [Command]+def = [addCheck check $ command "uninit" paramPaths seek "de-initialize git-annex and clean out repository"] +check :: Annex ()+check = do+ b <- current_branch + when (b == Annex.Branch.name) $ error $+ "cannot uninit when the " ++ show b ++ " branch is checked out"+ where+ current_branch = Git.Ref . head . lines . B.unpack <$> revhead+ revhead = inRepo $ Git.pipeRead + [Params "rev-parse --abbrev-ref HEAD"]+ seek :: [CommandSeek]-seek = [withFilesInGit startUnannex, withNothing start]+seek = [withFilesInGit $ whenAnnexed startUnannex, withNothing start] -startUnannex :: FilePath -> CommandStart-startUnannex file = do+startUnannex :: FilePath -> (Key, Backend Annex) -> CommandStart+startUnannex file info = do -- Force fast mode before running unannex. This way, if multiple -- files link to a key, it will be left in the annex and hardlinked -- to by each. Annex.changeState $ \s -> s { Annex.fast = True }- Command.Unannex.start file+ Command.Unannex.start file info start :: CommandStart start = next perform@@ -39,12 +51,11 @@ cleanup :: CommandCleanup cleanup = do- g <- gitRepo+ annexdir <- fromRepo gitAnnexDir uninitialize mapM_ removeAnnex =<< getKeysPresent- liftIO $ removeDirectoryRecursive (gitAnnexDir g)+ liftIO $ removeDirectoryRecursive annexdir -- avoid normal shutdown saveState- liftIO $ do- Git.run g "branch" [Param "-D", Param Annex.Branch.name]- exitSuccess+ inRepo $ Git.run "branch" [Param "-D", Param $ show Annex.Branch.name]+ liftIO exitSuccess
Command/Unlock.hs view
@@ -13,19 +13,21 @@ import Utility.CopyFile import Utility.FileMode -command :: [Command]-command =- [ repoCommand "unlock" paramPaths seek "unlock files for modification"- , repoCommand "edit" paramPaths seek "same as unlock"+def :: [Command]+def =+ [ c "unlock" "unlock files for modification"+ , c "edit" "same as unlock" ]+ where+ c n = command n paramPaths seek seek :: [CommandSeek]-seek = [withFilesInGit start]+seek = [withFilesInGit $ whenAnnexed start] {- The unlock subcommand replaces the symlink with a copy of the file's - content. -}-start :: FilePath -> CommandStart-start file = isAnnexed file $ \(key, _) -> do+start :: FilePath -> (Key, Backend Annex) -> CommandStart+start file (key, _) = do showStart "unlock" file next $ perform file key @@ -35,9 +37,8 @@ checkDiskSpace key - g <- gitRepo- let src = gitAnnexLocation g key- let tmpdest = gitAnnexTmpLocation g key+ src <- fromRepo $ gitAnnexLocation key+ tmpdest <- fromRepo $ gitAnnexTmpLocation key liftIO $ createDirectoryIfMissing True (parentDir tmpdest) showAction "copying" ok <- liftIO $ copyFileExternal src tmpdest
Command/Untrust.hs view
@@ -10,11 +10,10 @@ import Common.Annex import Command import qualified Remote-import UUID-import Trust+import Logs.Trust -command :: [Command]-command = [repoCommand "untrust" (paramRepeating paramRemote) seek+def :: [Command]+def = [command "untrust" (paramRepeating paramRemote) seek "do not trust a repository"] seek :: [CommandSeek]
Command/Unused.hs view
@@ -16,7 +16,8 @@ import Command import Annex.Content import Utility.FileMode-import LocationLog+import Utility.TempFile+import Logs.Location import qualified Annex import qualified Git import qualified Git.LsFiles as LsFiles@@ -26,8 +27,8 @@ import qualified Annex.Branch import Annex.CatFile -command :: [Command]-command = [repoCommand "unused" paramNothing seek+def :: [Command]+def = [dontCheck fromOpt $ command "unused" paramNothing seek "look for unused file content"] seek :: [CommandSeek]@@ -35,7 +36,7 @@ {- Finds unused content in the annex. -} start :: CommandStart-start = notBareRepo $ do+start = do from <- Annex.getState Annex.fromremote let (name, action) = case from of Nothing -> (".", checkUnused)@@ -66,24 +67,16 @@ checkRemoteUnused' :: Remote.Remote Annex -> Annex () checkRemoteUnused' r = do showAction "checking for unused data"- remotehas <- filterM isthere =<< loggedKeys+ remotehas <- loggedKeysFor (Remote.uuid r) remoteunused <- excludeReferenced remotehas let list = number 0 remoteunused writeUnusedFile "" list unless (null remoteunused) $ showLongNote $ remoteUnusedMsg r list- where- {- This should run strictly to avoid the filterM- - building many thunks containing keyLocations data. -}- isthere k = do- us <- keyLocations k- let !there = uuid `elem` us- return there- uuid = Remote.uuid r writeUnusedFile :: FilePath -> [(Int, Key)] -> Annex () writeUnusedFile prefix l = do- g <- gitRepo- liftIO $ viaTmp writeFile (gitAnnexUnusedLog prefix g) $+ logfile <- fromRepo $ gitAnnexUnusedLog prefix+ liftIO $ viaTmp writeFile logfile $ unlines $ map (\(n, k) -> show n ++ " " ++ show k) l table :: [(Int, Key)] -> [String]@@ -154,18 +147,18 @@ excludeReferenced :: [Key] -> Annex [Key] excludeReferenced [] = return [] -- optimisation excludeReferenced l = do- g <- gitRepo- c <- liftIO $ Git.pipeRead g [Param "show-ref"]+ c <- inRepo $ Git.pipeRead [Param "show-ref"] removewith (getKeysReferenced : map getKeysReferencedInGit (refs c)) (S.fromList l) where -- Skip the git-annex branches, and get all other unique refs.- refs = map last .+ refs = map Git.Ref . + map last . nubBy cmpheads . filter ourbranches . map words . lines . L.unpack cmpheads a b = head a == head b- ourbranchend = '/' : Annex.Branch.name+ ourbranchend = '/' : show (Annex.Branch.name) ourbranches ws = not $ ourbranchend `isSuffixOf` last ws removewith [] s = return $ S.toList s removewith (a:as) s@@ -190,23 +183,22 @@ {- List of keys referenced by symlinks in the git repo. -} getKeysReferenced :: Annex [Key] getKeysReferenced = do- g <- gitRepo- files <- liftIO $ LsFiles.inRepo g [Git.workTree g]+ top <- fromRepo Git.workTree+ files <- inRepo $ LsFiles.inRepo [top] keypairs <- mapM Backend.lookupFile files return $ map fst $ catMaybes keypairs {- List of keys referenced by symlinks in a git ref. -}-getKeysReferencedInGit :: String -> Annex [Key]+getKeysReferencedInGit :: Git.Ref -> Annex [Key] getKeysReferencedInGit ref = do showAction $ "checking " ++ Git.refDescribe ref- g <- gitRepo- findkeys [] =<< liftIO (LsTree.lsTree g ref)+ findkeys [] =<< inRepo (LsTree.lsTree ref) where findkeys c [] = return c findkeys c (l:ls) | isSymLink (LsTree.mode l) = do content <- catFile ref $ LsTree.file l- case fileKey (takeFileName content) of+ case fileKey (takeFileName $ L.unpack content) of Nothing -> findkeys c ls Just k -> findkeys (k:c) ls | otherwise = findkeys c ls@@ -224,16 +216,14 @@ let stale = contents `exclude` present let dups = contents `exclude` stale - g <- gitRepo- let dir = dirspec g+ dir <- fromRepo dirspec liftIO $ forM_ dups $ \t -> removeFile $ dir </> keyFile t return stale staleKeys :: (Git.Repo -> FilePath) -> Annex [Key] staleKeys dirspec = do- g <- gitRepo- let dir = dirspec g+ dir <- fromRepo dirspec exists <- liftIO $ doesDirectoryExist dir if not exists then return []
Command/Upgrade.hs view
@@ -12,9 +12,9 @@ import Upgrade import Annex.Version -command :: [Command]-command = [standaloneCommand "upgrade" paramNothing seek- "upgrade repository layout"]+def :: [Command]+def = [dontCheck repoExists $ -- because an old version may not seem to exist+ command "upgrade" paramNothing seek "upgrade repository layout"] seek :: [CommandSeek] seek = [withNothing start]
Command/Version.hs view
@@ -12,20 +12,25 @@ import qualified Build.SysConfig as SysConfig import Annex.Version -command :: [Command]-command = [standaloneCommand "version" paramNothing seek "show version info"]+def :: [Command]+def = [noRepo showPackageVersion $ dontCheck repoExists $+ command "version" paramNothing seek "show version info"] seek :: [CommandSeek] seek = [withNothing start] start :: CommandStart start = do- liftIO $ putStrLn $ "git-annex version: " ++ SysConfig.packageversion v <- getVersion- liftIO $ putStrLn $ "local repository version: " ++ fromMaybe "unknown" v- liftIO $ putStrLn $ "default repository version: " ++ defaultVersion- liftIO $ putStrLn $ "supported repository versions: " ++ vs supportedVersions- liftIO $ putStrLn $ "upgrade supported from repository versions: " ++ vs upgradableVersions+ liftIO $ do+ showPackageVersion+ putStrLn $ "local repository version: " ++ fromMaybe "unknown" v+ putStrLn $ "default repository version: " ++ defaultVersion+ putStrLn $ "supported repository versions: " ++ vs supportedVersions+ putStrLn $ "upgrade supported from repository versions: " ++ vs upgradableVersions stop where vs = join " "++showPackageVersion :: IO ()+showPackageVersion = putStrLn $ "git-annex version: " ++ SysConfig.packageversion
Command/Whereis.hs view
@@ -8,20 +8,20 @@ module Command.Whereis where import Common.Annex-import LocationLog+import Logs.Location import Command import Remote-import Trust+import Logs.Trust -command :: [Command]-command = [repoCommand "whereis" paramPaths seek+def :: [Command]+def = [command "whereis" paramPaths seek "lists repositories that have file content"] seek :: [CommandSeek]-seek = [withFilesInGit start]+seek = [withFilesInGit $ whenAnnexed start] -start :: FilePath -> CommandStart-start file = isAnnexed file $ \(key, _) -> do+start :: FilePath -> (Key, Backend Annex) -> CommandStart+start file (key, _) = do showStart "whereis" file next $ perform key @@ -31,11 +31,9 @@ let num = length safelocations showNote $ show num ++ " " ++ copiesplural num pp <- prettyPrintUUIDs "whereis" safelocations- unless (null safelocations) $- showLongNote pp+ unless (null safelocations) $ showLongNote pp pp' <- prettyPrintUUIDs "untrusted" untrustedlocations- unless (null untrustedlocations) $- showLongNote $ untrustedheader ++ pp'+ unless (null untrustedlocations) $ showLongNote $ untrustedheader ++ pp' if null safelocations then stop else next $ return True where copiesplural 1 = "copy"
Common.hs view
@@ -1,46 +1,25 @@-module Common (- module Control.Monad,- module Control.Applicative,- module Control.Monad.State,- module Control.Exception.Extensible,- module Data.Maybe,- module Data.List,- module Data.String.Utils,- module System.Path,- module System.FilePath,- module System.Directory,- module System.Cmd.Utils,- module System.IO,- module System.Posix.Files,- module System.Posix.IO,- module System.Posix.Process,- module System.Exit,- module Utility,- module Utility.Conditional,- module Utility.SafeCommand,- module Utility.Path,-) where+module Common (module X) where -import Control.Monad hiding (join)-import Control.Applicative-import Control.Monad.State (liftIO)-import Control.Exception.Extensible (IOException)+import Control.Monad as X hiding (join)+import Control.Applicative as X+import Control.Monad.State as X (liftIO)+import Control.Exception.Extensible as X (IOException) -import Data.Maybe-import Data.List-import Data.String.Utils+import Data.Maybe as X+import Data.List as X+import Data.String.Utils as X -import System.Path-import System.FilePath-import System.Directory-import System.Cmd.Utils-import System.IO hiding (FilePath)-import System.Posix.Files-import System.Posix.IO-import System.Posix.Process hiding (executeFile)-import System.Exit+import System.Path as X+import System.FilePath as X+import System.Directory as X+import System.Cmd.Utils as X hiding (safeSystem)+import System.IO as X hiding (FilePath)+import System.Posix.Files as X+import System.Posix.IO as X+import System.Posix.Process as X hiding (executeFile)+import System.Exit as X -import Utility-import Utility.Conditional-import Utility.SafeCommand-import Utility.Path+import Utility.Misc as X+import Utility.Conditional as X+import Utility.SafeCommand as X+import Utility.Path as X
Common/Annex.hs view
@@ -1,13 +1,8 @@-module Common.Annex (- module Common,- module Types,- module Annex,- module Locations,- module Messages,-) where+module Common.Annex (module X) where -import Common-import Types-import Annex (gitRepo)-import Locations-import Messages+import Common as X+import Types as X+import Types.UUID as X (toUUID, fromUUID)+import Annex as X (gitRepo, inRepo, fromRepo)+import Locations as X+import Messages as X
Config.hs view
@@ -16,27 +16,25 @@ {- Changes a git config setting in both internal state and .git/config -} setConfig :: ConfigKey -> String -> Annex () setConfig k value = do- g <- gitRepo- liftIO $ Git.run g "config" [Param k, Param value]+ inRepo $ Git.run "config" [Param k, Param value] -- re-read git config and update the repo's state- g' <- liftIO $ Git.configRead g- Annex.changeState $ \s -> s { Annex.repo = g' }+ newg <- inRepo Git.configRead+ Annex.changeState $ \s -> s { Annex.repo = newg } {- Looks up a per-remote config setting in git config. - Failing that, tries looking for a global config option. -} getConfig :: Git.Repo -> ConfigKey -> String -> Annex String getConfig r key def = do- g <- gitRepo- let def' = Git.configGet g ("annex." ++ key) def- return $ Git.configGet g (remoteConfig r key) def'+ def' <- fromRepo $ Git.configGet ("annex." ++ key) def+ fromRepo $ Git.configGet (remoteConfig r key) def' +{- Looks up a per-remote config setting in git config. -} remoteConfig :: Git.Repo -> ConfigKey -> String remoteConfig r key = "remote." ++ fromMaybe "" (Git.repoRemoteName r) ++ ".annex-" ++ key {- Calculates cost for a remote. Either the default, or as configured - by remote.<name>.annex-cost, or if remote.<name>.annex-cost-command- - is set and prints a number, that is used.- -}+ - is set and prints a number, that is used. -} remoteCost :: Git.Repo -> Int -> Annex Int remoteCost r def = do cmd <- getConfig r "cost-command" ""@@ -57,7 +55,7 @@ expensiveRemoteCost :: Int expensiveRemoteCost = 200 -{- Adjust's a remote's cost to reflect it being encrypted. -}+{- Adjusts a remote's cost to reflect it being encrypted. -} encryptedRemoteCostAdj :: Int encryptedRemoteCostAdj = 50 @@ -76,9 +74,7 @@ - setting, or on command-line options. Allows command-line to override - annex-ignore. -} repoNotIgnored :: Git.Repo -> Annex Bool-repoNotIgnored r = do- ignored <- getConfig r "ignore" "false"- return $ not $ Git.configTrue ignored+repoNotIgnored r = not . Git.configTrue <$> getConfig r "ignore" "false" {- If a value is specified, it is used; otherwise the default is looked up - in git config. forcenumcopies overrides everything. -}@@ -87,8 +83,5 @@ Annex.getState Annex.forcenumcopies >>= maybe (use v) (return . id) where use (Just n) = return n- use Nothing = do- g <- gitRepo- return $ read $ Git.configGet g config "1"+ use Nothing = read <$> fromRepo (Git.configGet config "1") config = "annex.numcopies"-
Crypto.hs view
@@ -102,14 +102,18 @@ {- Stores an EncryptedCipher in a remote's configuration. -} storeCipher :: RemoteConfig -> EncryptedCipher -> RemoteConfig storeCipher c (EncryptedCipher t ks) = - M.insert "cipher" (toB64 t) $ M.insert "cipherkeys" (show ks) c+ M.insert "cipher" (toB64 t) $ M.insert "cipherkeys" (showkeys ks) c+ where+ showkeys (KeyIds l) = join "," l {- Extracts an EncryptedCipher from a remote's configuration. -} extractCipher :: RemoteConfig -> Maybe EncryptedCipher extractCipher c = case (M.lookup "cipher" c, M.lookup "cipherkeys" c) of- (Just t, Just ks) -> Just $ EncryptedCipher (fromB64 t) (read ks)+ (Just t, Just ks) -> Just $ EncryptedCipher (fromB64 t) (readkeys ks) _ -> Nothing+ where+ readkeys = KeyIds . split "," {- Encrypts a Cipher to the specified KeyIds. -} encryptCipher :: Cipher -> KeyIds -> IO EncryptedCipher@@ -169,7 +173,7 @@ gpgParams params = do -- Enable batch mode if GPG_AGENT_INFO is set, to avoid extraneous -- gpg output about password prompts.- e <- catch (getEnv "GPG_AGENT_INFO") (const $ return "")+ e <- catchDefaultIO (getEnv "GPG_AGENT_INFO") "" let batch = if null e then [] else ["--batch"] return $ batch ++ defaults ++ toCommand params where
Git.hs view
@@ -10,6 +10,10 @@ module Git ( Repo,+ Ref(..),+ Branch,+ Sha,+ Tag, repoFromCwd, repoFromAbsPath, repoFromUnknown,@@ -48,8 +52,10 @@ attributes, remotes, remotesAdd,+ genRemote, repoRemoteName, repoRemoteNameSet,+ repoRemoteNameFromKey, checkAttr, decodeGitFile, encodeGitFile,@@ -92,6 +98,18 @@ remoteName :: Maybe String } deriving (Show, Eq) +{- A git ref. Can be a sha1, or a branch or tag name. -}+newtype Ref = Ref String+ deriving (Eq)++instance Show Ref where+ show (Ref v) = v++{- Aliases for Ref. -}+type Branch = Ref+type Sha = Ref+type Tag = Ref+ newFrom :: RepoLocation -> Repo newFrom l = Repo {@@ -160,9 +178,9 @@ repoDescribe Repo { location = Dir dir } = dir repoDescribe Repo { location = Unknown } = "UNKNOWN" -{- Converts a fully qualified git ref into a user-visible version -}-refDescribe :: String -> String-refDescribe = remove "refs/heads/" . remove "refs/remotes/"+{- Converts a fully qualified git ref into a user-visible version. -}+refDescribe :: Ref -> String+refDescribe = remove "refs/heads/" . remove "refs/remotes/" . show where remove prefix s | prefix `isPrefixOf` s = drop (length prefix) s@@ -185,10 +203,14 @@ repoRemoteName Repo { remoteName = Just name } = Just name repoRemoteName _ = Nothing +{- Sets the name of a remote. -}+repoRemoteNameSet :: String -> Repo -> Repo+repoRemoteNameSet n r = r { remoteName = Just n }+ {- Sets the name of a remote based on the git config key, such as "remote.foo.url". -}-repoRemoteNameSet :: Repo -> String -> Repo-repoRemoteNameSet r k = r { remoteName = Just basename }+repoRemoteNameFromKey :: String -> Repo -> Repo+repoRemoteNameFromKey k = repoRemoteNameSet basename where basename = join "." $ reverse $ drop 1 $ reverse $ drop 1 $ split "." k@@ -274,8 +296,8 @@ - is itself a symlink). But, if the cwd is "/tmp/repo/subdir", - it's best to refer to "../foo". -}-workTreeFile :: Repo -> FilePath -> IO FilePath-workTreeFile repo@(Repo { location = Dir d }) file = do+workTreeFile :: FilePath -> Repo -> IO FilePath+workTreeFile file repo@(Repo { location = Dir d }) = do cwd <- getCurrentDirectory let file' = absfile cwd unless (inrepo file') $@@ -290,7 +312,7 @@ absfile c = fromMaybe file $ secureAbsNormPath c file inrepo f = absrepo `isPrefixOf` f bad = error $ "bad repo" ++ repoDescribe repo-workTreeFile repo _ = assertLocal repo $ error "internal"+workTreeFile _ repo = assertLocal repo $ error "internal" {- Path of an URL repo. -} urlPath :: Repo -> String@@ -332,8 +354,9 @@ {- The full authority portion an URL repo. (ie, "user@host:port") -} urlAuthority :: Repo -> String-urlAuthority r = flip urlAuthPart r $ \a ->- uriUserInfo a ++ uriRegName' a ++ uriPort a+urlAuthority = urlAuthPart assemble+ where+ assemble a = uriUserInfo a ++ uriRegName' a ++ uriPort a {- Applies a function to extract part of the uriAuthority of an URL repo. -} urlAuthPart :: (URIAuth -> a) -> Repo -> a@@ -343,23 +366,23 @@ urlAuthPart _ repo = assertUrl repo $ error "internal" {- Constructs a git command line operating on the specified repo. -}-gitCommandLine :: Repo -> [CommandParam] -> [CommandParam]-gitCommandLine repo@(Repo { location = Dir _ } ) params =+gitCommandLine :: [CommandParam] -> Repo -> [CommandParam]+gitCommandLine params repo@(Repo { location = Dir _ } ) = -- force use of specified repo via --git-dir and --work-tree [ Param ("--git-dir=" ++ gitDir repo) , Param ("--work-tree=" ++ workTree repo) ] ++ params-gitCommandLine repo _ = assertLocal repo $ error "internal"+gitCommandLine _ repo = assertLocal repo $ error "internal" {- Runs git in the specified repo. -}-runBool :: Repo -> String -> [CommandParam] -> IO Bool-runBool repo subcommand params = assertLocal repo $- boolSystem "git" $ gitCommandLine repo $ Param subcommand : params+runBool :: String -> [CommandParam] -> Repo -> IO Bool+runBool subcommand params repo = assertLocal repo $+ boolSystem "git" $ gitCommandLine (Param subcommand : params) repo {- Runs git in the specified repo, throwing an error if it fails. -}-run :: Repo -> String -> [CommandParam] -> IO ()-run repo subcommand params = assertLocal repo $- runBool repo subcommand params+run :: String -> [CommandParam] -> Repo -> IO ()+run subcommand params repo = assertLocal repo $+ runBool subcommand params repo >>! error $ "git " ++ show params ++ " failed" {- Runs a git subcommand and returns its output, lazily. @@ -367,26 +390,26 @@ - Note that this leaves the git process running, and so zombies will - result unless reap is called. -}-pipeRead :: Repo -> [CommandParam] -> IO L.ByteString-pipeRead repo params = assertLocal repo $ do- (_, h) <- hPipeFrom "git" $ toCommand $ gitCommandLine repo params+pipeRead :: [CommandParam] -> Repo -> IO L.ByteString+pipeRead params repo = assertLocal repo $ do+ (_, h) <- hPipeFrom "git" $ toCommand $ gitCommandLine params repo hSetBinaryMode h True L.hGetContents h {- Runs a git subcommand, feeding it input. - You should call either getProcessStatus or forceSuccess on the PipeHandle. -}-pipeWrite :: Repo -> [CommandParam] -> L.ByteString -> IO PipeHandle-pipeWrite repo params s = assertLocal repo $ do- (p, h) <- hPipeTo "git" (toCommand $ gitCommandLine repo params)+pipeWrite :: [CommandParam] -> L.ByteString -> Repo -> IO PipeHandle+pipeWrite params s repo = assertLocal repo $ do+ (p, h) <- hPipeTo "git" (toCommand $ gitCommandLine params repo) L.hPut h s hClose h return p {- Runs a git subcommand, feeding it input, and returning its output. - You should call either getProcessStatus or forceSuccess on the PipeHandle. -}-pipeWriteRead :: Repo -> [CommandParam] -> L.ByteString -> IO (PipeHandle, L.ByteString)-pipeWriteRead repo params s = assertLocal repo $ do- (p, from, to) <- hPipeBoth "git" (toCommand $ gitCommandLine repo params)+pipeWriteRead :: [CommandParam] -> L.ByteString -> Repo -> IO (PipeHandle, L.ByteString)+pipeWriteRead params s repo = assertLocal repo $ do+ (p, from, to) <- hPipeBoth "git" (toCommand $ gitCommandLine params repo) hSetBinaryMode from True L.hPut to s hClose to@@ -395,19 +418,19 @@ {- Reads null terminated output of a git command (as enabled by the -z - parameter), and splits it. -}-pipeNullSplit :: Repo -> [CommandParam] -> IO [String]-pipeNullSplit repo params = map L.unpack <$> pipeNullSplitB repo params+pipeNullSplit :: [CommandParam] -> Repo -> IO [String]+pipeNullSplit params repo = map L.unpack <$> pipeNullSplitB params repo {- For when Strings are not needed. -}-pipeNullSplitB :: Repo -> [CommandParam] -> IO [L.ByteString]-pipeNullSplitB repo params = filter (not . L.null) . L.split '\0' <$>- pipeRead repo params+pipeNullSplitB ::[CommandParam] -> Repo -> IO [L.ByteString]+pipeNullSplitB params repo = filter (not . L.null) . L.split '\0' <$>+ pipeRead params repo {- Reaps any zombie git processes. -} reap :: IO () reap = do -- throws an exception when there are no child processes- r <- catch (getAnyProcessStatus False True) (\_ -> return Nothing)+ r <- catchDefaultIO (getAnyProcessStatus False True) Nothing maybe (return ()) (const reap) r {- Forces git to use the specified index file.@@ -425,7 +448,7 @@ {- Runs an action that causes a git subcommand to emit a sha, and strips any trailing newline, returning the sha. -}-getSha :: String -> IO String -> IO String+getSha :: String -> IO String -> IO Sha getSha subcommand a = do t <- a let t' = if last t == '\n'@@ -433,27 +456,27 @@ else t when (length t' /= shaSize) $ error $ "failed to read sha from git " ++ subcommand ++ " (" ++ t' ++ ")"- return t'+ return $ Ref t' {- Size of a git sha. -} shaSize :: Int shaSize = 40 -{- Commits the index into the specified branch, +{- Commits the index into the specified branch (or other ref), - with the specified parent refs. -}-commit :: Repo -> String -> String -> [String] -> IO ()-commit g message newref parentrefs = do+commit :: String -> Ref -> [Ref] -> Repo -> IO ()+commit message newref parentrefs repo = do tree <- getSha "write-tree" $ asString $- pipeRead g [Param "write-tree"]+ pipeRead [Param "write-tree"] repo sha <- getSha "commit-tree" $ asString $- ignorehandle $ pipeWriteRead g- (map Param $ ["commit-tree", tree] ++ ps)- (L.pack message)- run g "update-ref" [Param newref, Param sha]+ ignorehandle $ pipeWriteRead+ (map Param $ ["commit-tree", show tree] ++ ps)+ (L.pack message) repo+ run "update-ref" [Param $ show newref, Param $ show sha] repo where ignorehandle a = snd <$> a asString a = L.unpack <$> a- ps = concatMap (\r -> ["-p", r]) parentrefs+ ps = concatMap (\r -> ["-p", show r]) parentrefs {- Runs git config and populates a repo with its config. -} configRead :: Repo -> IO Repo@@ -471,13 +494,13 @@ hConfigRead :: Repo -> Handle -> IO Repo hConfigRead repo h = do val <- hGetContentsStrict h- configStore repo val+ configStore val repo {- Stores a git config into a repo, returning the new version of the repo. - The git config may be multiple lines, or a single line. Config settings - can be updated inrementally. -}-configStore :: Repo -> String -> IO Repo-configStore repo s = do+configStore :: String -> Repo -> IO Repo+configStore s repo = do let repo' = repo { config = configParse s `M.union` config repo } rs <- configRemotes repo' return $ repo' { remotes = rs }@@ -500,25 +523,31 @@ filterkeys f = filterconfig (\(k,_) -> f k) remotepairs = filterkeys isremote isremote k = startswith "remote." k && endswith ".url" k- construct (k,v) = do- r <- gen $ calcloc v- return $ repoRemoteNameSet r k- gen v+ construct (k,v) = repoRemoteNameFromKey k <$> genRemote v repo++{- Generates one of a repo's remotes using a given location (ie, an url). -}+genRemote :: String -> Repo -> IO Repo+genRemote s repo = gen $ calcloc s+ where+ filterconfig f = filter f $ M.toList $ config repo+ gen v | scpstyle v = repoFromUrl $ scptourl v | isURI v = repoFromUrl v | otherwise = repoFromRemotePath v repo -- insteadof config can rewrite remote location calcloc l | null insteadofs = l- | otherwise = replacement ++ drop (length replacement) l+ | otherwise = replacement ++ drop (length bestvalue) l where- replacement = take (length bestkey - length prefix) bestkey- bestkey = fst $ maximumBy longestvalue insteadofs+ replacement = drop (length prefix) $+ take (length bestkey - length suffix) bestkey+ (bestkey, bestvalue) = maximumBy longestvalue insteadofs longestvalue (_, a) (_, b) = compare b a insteadofs = filterconfig $ \(k, v) -> - endswith prefix k &&+ startswith prefix k &&+ endswith suffix k && startswith v l- prefix = ".insteadof"+ (prefix, suffix) = ("url." , ".insteadof") -- git remotes can be written scp style -- [user@]host:dir scpstyle v = ":" `isInfixOf` v && not ("//" `isInfixOf` v) scptourl v = "ssh://" ++ host ++ slash dir@@ -536,8 +565,8 @@ configTrue s = map toLower s == "true" {- Returns a single git config setting, or a default value if not set. -}-configGet :: Repo -> String -> String -> String-configGet repo key defaultValue = +configGet :: String -> String -> Repo -> String+configGet key defaultValue repo = M.findWithDefault defaultValue key (config repo) {- Access to raw config Map -}@@ -545,8 +574,11 @@ configMap = config {- Efficiently looks up a gitattributes value for each file in a list. -}-checkAttr :: Repo -> String -> [FilePath] -> IO [(FilePath, String)]-checkAttr repo attr files = do+checkAttr :: String -> [FilePath] -> Repo -> IO [(FilePath, String)]+checkAttr attr files repo = do+ -- git check-attr needs relative filenames input; it will choke+ -- on some absolute filenames. This also means it will output+ -- all relative filenames. cwd <- getCurrentDirectory let relfiles = map (relPathDirToFile cwd . absPathFrom cwd) files (_, fromh, toh) <- hPipeBoth "git" (toCommand params)@@ -558,7 +590,11 @@ hClose toh (map topair . lines) <$> hGetContents fromh where- params = gitCommandLine repo [Param "check-attr", Param attr, Params "-z --stdin"]+ params = gitCommandLine + [ Param "check-attr"+ , Param attr+ , Params "-z --stdin"+ ] repo topair l = (file, value) where file = decodeGitFile $ join sep $ take end bits
Git/CatFile.hs view
@@ -9,13 +9,15 @@ CatFileHandle, catFileStart, catFileStop,- catFile+ catFile,+ catObject ) where import Control.Monad.State import System.Cmd.Utils import System.IO-import qualified Data.ByteString.Char8 as B+import qualified Data.ByteString.Char8 as S+import qualified Data.ByteString.Lazy.Char8 as L import Git import Utility.SafeCommand@@ -25,7 +27,7 @@ {- Starts git cat-file running in batch mode in a repo and returns a handle. -} catFileStart :: Repo -> IO CatFileHandle catFileStart repo = hPipeBoth "git" $ toCommand $- Git.gitCommandLine repo [Param "cat-file", Param "--batch"]+ Git.gitCommandLine [Param "cat-file", Param "--batch"] repo {- Stops git cat-file. -} catFileStop :: CatFileHandle -> IO ()@@ -34,30 +36,38 @@ hClose from forceSuccess pid -{- Uses a running git cat-file read the content of a file from a branch.- - Files that do not exist on the branch will have "" returned. -}-catFile :: CatFileHandle -> String -> FilePath -> IO String-catFile (_, from, to) branch file = do- hPutStrLn to want+{- Reads a file from a specified branch. -}+catFile :: CatFileHandle -> Branch -> FilePath -> IO L.ByteString+catFile h branch file = catObject h $ Ref $ show branch ++ ":" ++ file++{- Uses a running git cat-file read the content of an object.+ - Objects that do not exist will have "" returned. -}+catObject :: CatFileHandle -> Ref -> IO L.ByteString+catObject (_, from, to) object = do+ hPutStrLn to $ show object hFlush to header <- hGetLine from case words header of- [sha, blob, size]+ [sha, objtype, size] | length sha == Git.shaSize &&- blob == "blob" -> handle size+ validobjtype objtype -> handle size | otherwise -> empty _- | header == want ++ " missing" -> empty+ | header == show object ++ " missing" -> empty | otherwise -> error $ "unknown response from git cat-file " ++ header where- want = branch ++ ":" ++ file handle size = case reads size of [(bytes, "")] -> readcontent bytes _ -> empty readcontent bytes = do- content <- B.hGet from bytes+ content <- S.hGet from bytes c <- hGetChar from when (c /= '\n') $ error "missing newline from git cat-file"- return $ B.unpack content- empty = return ""+ return $ L.fromChunks [content]+ empty = return L.empty+ validobjtype t+ | t == "blob" = True+ | t == "commit" = True+ | t == "tree" = True+ | otherwise = False
Git/LsFiles.hs view
@@ -19,51 +19,52 @@ import Utility.SafeCommand {- Scans for files that are checked into git at the specified locations. -}-inRepo :: Repo -> [FilePath] -> IO [FilePath]-inRepo repo l = pipeNullSplit repo $ Params "ls-files --cached -z --" : map File l+inRepo :: [FilePath] -> Repo -> IO [FilePath]+inRepo l = pipeNullSplit $ Params "ls-files --cached -z --" : map File l {- Scans for files at the specified locations that are not checked into git. -}-notInRepo :: Repo -> Bool -> [FilePath] -> IO [FilePath]-notInRepo repo include_ignored l = pipeNullSplit repo $- [Params "ls-files --others"] ++ exclude ++- [Params "-z --"] ++ map File l+notInRepo :: Bool -> [FilePath] -> Repo -> IO [FilePath]+notInRepo include_ignored l repo = pipeNullSplit params repo where+ params = [Params "ls-files --others"] ++ exclude +++ [Params "-z --"] ++ map File l exclude | include_ignored = [] | otherwise = [Param "--exclude-standard"] {- Returns a list of all files that are staged for commit. -}-staged :: Repo -> [FilePath] -> IO [FilePath]-staged repo l = staged' repo l []+staged :: [FilePath] -> Repo -> IO [FilePath]+staged = staged' [] {- Returns a list of the files, staged for commit, that are being added, - moved, or changed (but not deleted), from the specified locations. -}-stagedNotDeleted :: Repo -> [FilePath] -> IO [FilePath]-stagedNotDeleted repo l = staged' repo l [Param "--diff-filter=ACMRT"]+stagedNotDeleted :: [FilePath] -> Repo -> IO [FilePath]+stagedNotDeleted = staged' [Param "--diff-filter=ACMRT"] -staged' :: Repo -> [FilePath] -> [CommandParam] -> IO [FilePath]-staged' repo l middle = pipeNullSplit repo $ start ++ middle ++ end+staged' :: [CommandParam] -> [FilePath] -> Repo -> IO [FilePath]+staged' middle l = pipeNullSplit $ start ++ middle ++ end where start = [Params "diff --cached --name-only -z"] end = Param "--" : map File l {- Returns a list of files that have unstaged changes. -}-changedUnstaged :: Repo -> [FilePath] -> IO [FilePath]-changedUnstaged repo l = pipeNullSplit repo $- Params "diff --name-only -z --" : map File l+changedUnstaged :: [FilePath] -> Repo -> IO [FilePath]+changedUnstaged l = pipeNullSplit params+ where+ params = Params "diff --name-only -z --" : map File l {- Returns a list of the files in the specified locations that are staged - for commit, and whose type has changed. -}-typeChangedStaged :: Repo -> [FilePath] -> IO [FilePath]-typeChangedStaged repo l = typeChanged' repo l [Param "--cached"]+typeChangedStaged :: [FilePath] -> Repo -> IO [FilePath]+typeChangedStaged = typeChanged' [Param "--cached"] {- Returns a list of the files in the specified locations whose type has - changed. Files only staged for commit will not be included. -}-typeChanged :: Repo -> [FilePath] -> IO [FilePath]-typeChanged repo l = typeChanged' repo l []+typeChanged :: [FilePath] -> Repo -> IO [FilePath]+typeChanged = typeChanged' [] -typeChanged' :: Repo -> [FilePath] -> [CommandParam] -> IO [FilePath]-typeChanged' repo l middle = pipeNullSplit repo $ start ++ middle ++ end+typeChanged' :: [CommandParam] -> [FilePath] -> Repo -> IO [FilePath]+typeChanged' middle l = pipeNullSplit $ start ++ middle ++ end where start = [Params "diff --name-only --diff-filter=T -z"] end = Param "--" : map File l
Git/LsTree.hs view
@@ -19,8 +19,6 @@ import Git import Utility.SafeCommand -type Treeish = String- data TreeItem = TreeItem { mode :: FileMode , typeobj :: String@@ -28,10 +26,10 @@ , file :: FilePath } deriving Show -{- Lists the contents of a Treeish -}-lsTree :: Repo -> Treeish -> IO [TreeItem]-lsTree repo t = map parseLsTree <$>- pipeNullSplitB repo [Params "ls-tree --full-tree -z -r --", File t]+{- Lists the contents of a Ref -}+lsTree :: Ref -> Repo -> IO [TreeItem]+lsTree t repo = map parseLsTree <$>+ pipeNullSplitB [Params "ls-tree --full-tree -z -r --", File $ show t] repo {- Parses a line of ls-tree output. - (The --long format is not currently supported.) -}
Git/Queue.hs view
@@ -72,8 +72,8 @@ full (Queue n _) = n > maxSize {- Runs a queue on a git repository. -}-flush :: Repo -> Queue -> IO Queue-flush repo (Queue _ m) = do+flush :: Queue -> Repo -> IO Queue+flush (Queue _ m) repo = do forM_ (M.toList m) $ uncurry $ runAction repo return empty @@ -87,6 +87,6 @@ runAction repo action files = pOpen WriteToPipe "xargs" ("-0":"git":params) feedxargs where- params = toCommand $ gitCommandLine repo- (Param (getSubcommand action):getParams action)+ params = toCommand $ gitCommandLine+ (Param (getSubcommand action):getParams action) repo feedxargs h = hPutStr h $ join "\0" files
Git/UnionMerge.hs view
@@ -9,101 +9,118 @@ merge, merge_index, update_index,+ stream_update_index, update_index_line, ls_tree ) where import System.Cmd.Utils import Data.List-import Data.Maybe-import Data.String.Utils import qualified Data.ByteString.Lazy.Char8 as L import Common import Git+import Git.CatFile +type Streamer = (String -> IO ()) -> IO ()+ {- Performs a union merge between two branches, staging it in the index. - Any previously staged changes in the index will be lost. - - Should be run with a temporary index file configured by Git.useIndex. -}-merge :: Repo -> String -> String -> IO ()-merge g x y = do- a <- ls_tree g x- b <- merge_trees g x y- update_index g (a++b)+merge :: Ref -> Ref -> Repo -> IO ()+merge x y repo = do+ h <- catFileStart repo+ stream_update_index repo+ [ ls_tree x repo+ , merge_trees x y h repo+ ]+ catFileStop h {- Merges a list of branches into the index. Previously staged changed in - the index are preserved (and participate in the merge). -}-merge_index :: Repo -> [String] -> IO ()-merge_index g bs = update_index g =<< concat <$> mapM (merge_tree_index g) bs+merge_index :: CatFileHandle -> Repo -> [Ref] -> IO ()+merge_index h repo bs =+ stream_update_index repo $ map (\b -> merge_tree_index b h repo) bs -{- Feeds a list into update-index. Later items in the list can override+{- Feeds content into update-index. Later items in the list can override - earlier ones, so the list can be generated from any combination of - ls_tree, merge_trees, and merge_tree_index. -} update_index :: Repo -> [String] -> IO ()-update_index g l = togit ["update-index", "-z", "--index-info"] (join "\0" l)+update_index repo ls = stream_update_index repo [\s -> mapM_ s ls]++{- Streams content into update-index. -}+stream_update_index :: Repo -> [Streamer] -> IO ()+stream_update_index repo as = do+ (p, h) <- hPipeTo "git" (toCommand $ Git.gitCommandLine params repo)+ forM_ as (stream h)+ hClose h+ forceSuccess p where- togit ps content = pipeWrite g (map Param ps) (L.pack content)- >>= forceSuccess+ params = map Param ["update-index", "-z", "--index-info"]+ stream h a = a (streamer h)+ streamer h s = do+ hPutStr h s+ hPutStr h "\0" {- Generates a line suitable to be fed into update-index, to add - a given file with a given sha. -}-update_index_line :: String -> FilePath -> String-update_index_line sha file = "100644 blob " ++ sha ++ "\t" ++ file+update_index_line :: Sha -> FilePath -> String+update_index_line sha file = "100644 blob " ++ show sha ++ "\t" ++ file -{- Gets the contents of a tree in a format suitable for update_index. -}-ls_tree :: Repo -> String -> IO [String]-ls_tree g x = pipeNullSplit g $ - map Param ["ls-tree", "-z", "-r", "--full-tree", x]+{- Gets the current tree for a ref. -}+ls_tree :: Ref -> Repo -> Streamer+ls_tree (Ref x) repo streamer = mapM_ streamer =<< pipeNullSplit params repo+ where+ params = map Param ["ls-tree", "-z", "-r", "--full-tree", x] {- For merging two trees. -}-merge_trees :: Repo -> String -> String -> IO [String]-merge_trees g x y = calc_merge g $ "diff-tree":diff_opts ++ [x, y]+merge_trees :: Ref -> Ref -> CatFileHandle -> Repo -> Streamer+merge_trees (Ref x) (Ref y) h = calc_merge h $ "diff-tree":diff_opts ++ [x, y] {- For merging a single tree into the index. -}-merge_tree_index :: Repo -> String -> IO [String]-merge_tree_index g x = calc_merge g $ "diff-index":diff_opts ++ ["--cached", x]+merge_tree_index :: Ref -> CatFileHandle -> Repo -> Streamer+merge_tree_index (Ref x) h = calc_merge h $ "diff-index":diff_opts ++ ["--cached", x] diff_opts :: [String] diff_opts = ["--raw", "-z", "-r", "--no-renames", "-l0"] {- Calculates how to perform a merge, using git to get a raw diff, - and returning a list suitable for update_index. -}-calc_merge :: Repo -> [String] -> IO [String]-calc_merge g differ = do- diff <- pipeNullSplit g $ map Param differ- l <- mapM (mergeFile g) (pairs diff)- return $ catMaybes l- where- pairs [] = []- pairs (_:[]) = error "calc_merge parse error"- pairs (a:b:rest) = (a,b):pairs rest--{- Injects some content into git, returning its hash. -}-hashObject :: Repo -> L.ByteString -> IO String-hashObject repo content = getSha subcmd $ do- (h, s) <- pipeWriteRead repo (map Param params) content- L.length s `seq` do- forceSuccess h- reap -- XXX unsure why this is needed- return $ L.unpack s+calc_merge :: CatFileHandle -> [String] -> Repo -> Streamer+calc_merge ch differ repo streamer = gendiff >>= go where- subcmd = "hash-object"- params = [subcmd, "-w", "--stdin"]+ gendiff = pipeNullSplit (map Param differ) repo+ go [] = return ()+ go (info:file:rest) = mergeFile info file ch repo >>=+ maybe (go rest) (\l -> streamer l >> go rest)+ go (_:[]) = error "calc_merge parse error" {- Given an info line from a git raw diff, and the filename, generates - a line suitable for update_index that union merges the two sides of the - diff. -}-mergeFile :: Repo -> (String, FilePath) -> IO (Maybe String)-mergeFile g (info, file) = case filter (/= nullsha) [asha, bsha] of+mergeFile :: String -> FilePath -> CatFileHandle -> Repo -> IO (Maybe String)+mergeFile info file h repo = case filter (/= nullsha) [Ref asha, Ref bsha] of [] -> return Nothing (sha:[]) -> return $ Just $ update_index_line sha file shas -> do- content <- pipeRead g $ map Param ("show":shas)- sha <- hashObject g $ unionmerge content+ content <- L.concat <$> mapM (catObject h) shas+ sha <- hashObject (unionmerge content) repo return $ Just $ update_index_line sha file where [_colonamode, _bmode, asha, bsha, _status] = words info- nullsha = replicate shaSize '0'+ nullsha = Ref $ replicate shaSize '0' unionmerge = L.unlines . nub . L.lines++{- Injects some content into git, returning its Sha. -}+hashObject :: L.ByteString -> Repo -> IO Sha+hashObject content repo = getSha subcmd $ do+ (h, s) <- pipeWriteRead (map Param params) content repo+ L.length s `seq` do+ forceSuccess h+ reap -- XXX unsure why this is needed+ return $ L.unpack s+ where+ subcmd = "hash-object"+ params = [subcmd, "-w", "--stdin"]
GitAnnex.hs view
@@ -13,7 +13,6 @@ import qualified Git import CmdLine import Command-import Options import Types.TrustLevel import qualified Annex import qualified Remote@@ -27,7 +26,7 @@ import qualified Command.Get import qualified Command.FromKey import qualified Command.DropKey-import qualified Command.SetKey+import qualified Command.Reinject import qualified Command.Fix import qualified Command.Init import qualified Command.Describe@@ -54,45 +53,43 @@ cmds :: [Command] cmds = concat- [ Command.Add.command- , Command.Get.command- , Command.Drop.command- , Command.Move.command- , Command.Copy.command- , Command.Unlock.command- , Command.Lock.command- , Command.Init.command- , Command.Describe.command- , Command.InitRemote.command- , Command.Unannex.command- , Command.Uninit.command- , Command.PreCommit.command- , Command.Trust.command- , Command.Untrust.command- , Command.Semitrust.command- , Command.AddUrl.command- , Command.FromKey.command- , Command.DropKey.command- , Command.SetKey.command- , Command.Fix.command- , Command.Fsck.command- , Command.Unused.command- , Command.DropUnused.command- , Command.Find.command- , Command.Whereis.command- , Command.Merge.command- , Command.Status.command- , Command.Migrate.command- , Command.Map.command- , Command.Upgrade.command- , Command.Version.command+ [ Command.Add.def+ , Command.Get.def+ , Command.Drop.def+ , Command.Move.def+ , Command.Copy.def+ , Command.Unlock.def+ , Command.Lock.def+ , Command.Init.def+ , Command.Describe.def+ , Command.InitRemote.def+ , Command.Reinject.def+ , Command.Unannex.def+ , Command.Uninit.def+ , Command.PreCommit.def+ , Command.Trust.def+ , Command.Untrust.def+ , Command.Semitrust.def+ , Command.AddUrl.def+ , Command.FromKey.def+ , Command.DropKey.def+ , Command.Fix.def+ , Command.Fsck.def+ , Command.Unused.def+ , Command.DropUnused.def+ , Command.Find.def+ , Command.Whereis.def+ , Command.Merge.def+ , Command.Status.def+ , Command.Migrate.def+ , Command.Map.def+ , Command.Upgrade.def+ , Command.Version.def ] options :: [Option] options = commonOptions ++- [ Option ['k'] ["key"] (ReqArg setkey paramKey)- "specify a key to use"- , Option ['t'] ["to"] (ReqArg setto paramRemote)+ [ Option ['t'] ["to"] (ReqArg setto paramRemote) "specify to where to transfer content" , Option ['f'] ["from"] (ReqArg setfrom paramRemote) "specify from where to transfer content"@@ -106,6 +103,8 @@ "override trust setting to untrusted" , Option ['c'] ["config"] (ReqArg setgitconfig "NAME=VALUE") "override git configuration setting"+ , Option [] ["print0"] (NoArg (setprint0 True))+ "terminate filename with null" , Option ['x'] ["exclude"] (ReqArg Limit.addExclude paramGlob) "skip files matching the glob pattern" , Option ['i'] ["in"] (ReqArg Limit.addIn paramRemote)@@ -117,15 +116,14 @@ setto v = Annex.changeState $ \s -> s { Annex.toremote = Just v } setfrom v = Annex.changeState $ \s -> s { Annex.fromremote = Just v } setnumcopies v = Annex.changeState $ \s -> s {Annex.forcenumcopies = readMaybe v }- setkey v = Annex.changeState $ \s -> s { Annex.defaultkey = Just v }+ setprint0 v = Annex.changeState $ \s -> s { Annex.print0 = v } setgitconfig :: String -> Annex () setgitconfig v = do- g <- gitRepo- g' <- liftIO $ Git.configStore g v- Annex.changeState $ \s -> s { Annex.repo = g' }+ newg <- inRepo $ Git.configStore v+ Annex.changeState $ \s -> s { Annex.repo = newg } header :: String header = "Usage: git-annex command [option ..]" run :: [String] -> IO ()-run args = dispatch args cmds options header =<< Git.repoFromCwd+run args = dispatch args cmds options header Git.repoFromCwd
Init.hs view
@@ -12,17 +12,21 @@ ) where import Common.Annex+import Utility.TempFile import qualified Git import qualified Annex.Branch+import Logs.UUID import Annex.Version-import UUID+import Annex.UUID -initialize :: Annex ()-initialize = do+initialize :: Maybe String -> Annex ()+initialize mdescription = do prepUUID Annex.Branch.create setVersion gitPreCommitHookWrite+ u <- getUUID+ maybe (recordUUID u) (describeUUID u) mdescription uninitialize :: Annex () uninitialize = gitPreCommitHookUnWrite@@ -37,7 +41,7 @@ needsinit = do annexed <- Annex.Branch.hasSomeBranch if annexed- then initialize+ then initialize Nothing else error "First run: git-annex init" {- set up a git pre-commit hook, if one is not already present -}@@ -64,14 +68,10 @@ " Edit it to remove call to git annex." unlessBare :: Annex () -> Annex ()-unlessBare a = do- g <- gitRepo- unless (Git.repoIsLocalBare g) a+unlessBare = unlessM $ fromRepo $ Git.repoIsLocalBare preCommitHook :: Annex FilePath-preCommitHook = do- g <- gitRepo- return $ Git.gitDir g ++ "/hooks/pre-commit"+preCommitHook = (</>) <$> fromRepo Git.gitDir <*> pure "hooks/pre-commit" preCommitScript :: String preCommitScript =
Limit.hs view
@@ -15,7 +15,7 @@ import qualified Utility.Matcher import qualified Remote import qualified Backend-import LocationLog+import Logs.Location import Annex.Content type Limit = Utility.Matcher.Token (FilePath -> Annex Bool)@@ -67,7 +67,7 @@ addIn :: String -> Annex () addIn name = addLimit $ check $ if name == "." then inAnnex else inremote where- check a f = Backend.lookupFile f >>= handle a+ check a = Backend.lookupFile >=> handle a handle _ Nothing = return False handle a (Just (key, _)) = a key inremote key = do@@ -83,7 +83,7 @@ Nothing -> error "bad number for --copies" Just n -> addLimit $ check n where- check n f = Backend.lookupFile f >>= handle n+ check n = Backend.lookupFile >=> handle n handle _ Nothing = return False handle n (Just (key, _)) = do us <- keyLocations key
− LocationLog.hs
@@ -1,58 +0,0 @@-{- git-annex location log- -- - git-annex keeps track of which repositories have the contents of annexed- - files.- -- - Repositories record their UUID and the date when they --get or --drop- - a value.- - - - Copyright 2010-2011 Joey Hess <joey@kitenet.net>- -- - Licensed under the GNU GPL version 3 or higher.- -}--module LocationLog (- LogStatus(..),- logChange,- readLog,- keyLocations,- loggedKeys,- logFile,- logFileKey-) where--import Common.Annex-import qualified Git-import qualified Annex.Branch-import UUID-import PresenceLog--{- Log a change in the presence of a key's value in a repository. -}-logChange :: Git.Repo -> Key -> UUID -> LogStatus -> Annex ()-logChange repo key u s- | null u = error $- "unknown UUID for " ++ Git.repoDescribe repo ++ - " (have you run git annex init there?)"- | otherwise = addLog (logFile key) =<< logNow s u--{- Returns a list of repository UUIDs that, according to the log, have- - the value of a key. -}-keyLocations :: Key -> Annex [UUID]-keyLocations = currentLog . logFile--{- Finds all keys that have location log information.- - (There may be duplicate keys in the list.) -}-loggedKeys :: Annex [Key]-loggedKeys = mapMaybe (logFileKey . takeFileName) <$> Annex.Branch.files--{- The filename of the log file for a given key. -}-logFile :: Key -> String-logFile key = hashDirLower key ++ keyFile key ++ ".log"--{- Converts a log filename into a key. -}-logFileKey :: FilePath -> Maybe Key-logFileKey file- | end == ".log" = fileKey beginning- | otherwise = Nothing- where- (beginning, end) = splitAt (length file - 4) file
Locations.hs view
@@ -65,8 +65,8 @@ f = keyFile key {- Annexed file's absolute location in a repository. -}-gitAnnexLocation :: Git.Repo -> Key -> FilePath-gitAnnexLocation r key+gitAnnexLocation :: Key -> Git.Repo -> FilePath+gitAnnexLocation key r | Git.repoIsLocalBare r = Git.workTree r </> annexLocation key | otherwise = Git.workTree r </> ".git" </> annexLocation key @@ -88,16 +88,16 @@ gitAnnexTmpDir r = addTrailingPathSeparator $ gitAnnexDir r </> "tmp" {- The temp file to use for a given key. -}-gitAnnexTmpLocation :: Git.Repo -> Key -> FilePath-gitAnnexTmpLocation r key = gitAnnexTmpDir r </> keyFile key+gitAnnexTmpLocation :: Key -> Git.Repo -> FilePath+gitAnnexTmpLocation key r = gitAnnexTmpDir r </> keyFile key {- .git/annex/bad/ is used for bad files found during fsck -} gitAnnexBadDir :: Git.Repo -> FilePath gitAnnexBadDir r = addTrailingPathSeparator $ gitAnnexDir r </> "bad" {- The bad file to use for a given key. -}-gitAnnexBadLocation :: Git.Repo -> Key -> FilePath-gitAnnexBadLocation r key = gitAnnexBadDir r </> keyFile key+gitAnnexBadLocation :: Key -> Git.Repo -> FilePath+gitAnnexBadLocation key r = gitAnnexBadDir r </> keyFile key {- .git/annex/*unused is used to number possibly unused keys -} gitAnnexUnusedLog :: FilePath -> Git.Repo -> FilePath@@ -127,7 +127,7 @@ - is one to one. - ":" is escaped to "&c", because despite it being 2011, people still care - about FAT.- - -}+ -} keyFile :: Key -> FilePath keyFile key = replace "/" "%" $ replace ":" "&c" $ replace "%" "&s" $ replace "&" "&a" $ show key
+ Logs/Location.hs view
@@ -0,0 +1,66 @@+{- git-annex location log+ -+ - git-annex keeps track of which repositories have the contents of annexed+ - files.+ -+ - Repositories record their UUID and the date when they --get or --drop+ - a value.+ - + - Copyright 2010-2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Logs.Location (+ LogStatus(..),+ logChange,+ readLog,+ keyLocations,+ loggedKeys,+ loggedKeysFor,+ logFile,+ logFileKey+) where++import Common.Annex+import qualified Annex.Branch+import Logs.Presence++{- Log a change in the presence of a key's value in a repository. -}+logChange :: Key -> UUID -> LogStatus -> Annex ()+logChange key (UUID u) s = addLog (logFile key) =<< logNow s u+logChange _ NoUUID _ = return ()++{- Returns a list of repository UUIDs that, according to the log, have+ - the value of a key. -}+keyLocations :: Key -> Annex [UUID]+keyLocations key = map toUUID <$> (currentLog . logFile) key++{- Finds all keys that have location log information.+ - (There may be duplicate keys in the list.) -}+loggedKeys :: Annex [Key]+loggedKeys = mapMaybe (logFileKey . takeFileName) <$> Annex.Branch.files++{- Finds all keys that have location log information indicating+ - they are present for the specified repository. -}+loggedKeysFor :: UUID -> Annex [Key]+loggedKeysFor u = filterM isthere =<< loggedKeys+ where+ {- This should run strictly to avoid the filterM+ - building many thunks containing keyLocations data. -}+ isthere k = do+ us <- keyLocations k+ let !there = u `elem` us+ return there++{- The filename of the log file for a given key. -}+logFile :: Key -> String+logFile key = hashDirLower key ++ keyFile key ++ ".log"++{- Converts a log filename into a key. -}+logFileKey :: FilePath -> Maybe Key+logFileKey file+ | end == ".log" = fileKey beginning+ | otherwise = Nothing+ where+ (beginning, end) = splitAt (length file - 4) file
+ Logs/Presence.hs view
@@ -0,0 +1,104 @@+{- git-annex presence log+ -+ - This is used to store presence information in the git-annex branch in+ - a way that can be union merged.+ -+ - A line of the log will look like: "date N INFO"+ - Where N=1 when the INFO is present, and 0 otherwise.+ - + - Copyright 2010-2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Logs.Presence (+ LogStatus(..),+ addLog,+ readLog,+ parseLog,+ showLog,+ logNow,+ compactLog,+ currentLog,+ LogLine+) where++import Data.Time.Clock.POSIX+import Data.Time+import System.Locale+import qualified Data.Map as M++import Common.Annex+import qualified Annex.Branch++data LogLine = LogLine {+ date :: POSIXTime,+ status :: LogStatus,+ info :: String+} deriving (Eq)++data LogStatus = InfoPresent | InfoMissing+ deriving (Eq)++addLog :: FilePath -> LogLine -> Annex ()+addLog file line = Annex.Branch.change file $ \s -> + showLog $ compactLog (line : parseLog s)++{- Reads a log file.+ - Note that the LogLines returned may be in any order. -}+readLog :: FilePath -> Annex [LogLine]+readLog file = parseLog <$> Annex.Branch.get file++{- Parses a log file. Unparseable lines are ignored. -}+parseLog :: String -> [LogLine]+parseLog = mapMaybe (parseline . words) . lines+ where+ parseline (a:b:c:_) = do+ d <- parseTime defaultTimeLocale "%s%Qs" a+ s <- parsestatus b+ Just $ LogLine (utcTimeToPOSIXSeconds d) s c+ parseline _ = Nothing+ parsestatus "1" = Just InfoPresent+ parsestatus "0" = Just InfoMissing+ parsestatus _ = Nothing++{- Generates a log file. -}+showLog :: [LogLine] -> String+showLog = unlines . map genline+ where+ genline (LogLine d s i) = unwords [show d, genstatus s, i]+ genstatus InfoPresent = "1"+ genstatus InfoMissing = "0"++{- Generates a new LogLine with the current date. -}+logNow :: LogStatus -> String -> Annex LogLine+logNow s i = do+ now <- liftIO getPOSIXTime+ return $ LogLine now s i++{- Reads a log and returns only the info that is still in effect. -}+currentLog :: FilePath -> Annex [String]+currentLog file = map info . filterPresent <$> readLog file++{- Returns the info from LogLines that are in effect. -}+filterPresent :: [LogLine] -> [LogLine]+filterPresent = filter (\l -> InfoPresent == status l) . compactLog++{- Compacts a set of logs, returning a subset that contains the current+ - status. -}+compactLog :: [LogLine] -> [LogLine]+compactLog = M.elems . foldr mapLog M.empty++type LogMap = M.Map String LogLine++{- Inserts a log into a map of logs, if the log has better (ie, newer)+ - information than the other logs in the map -}+mapLog :: LogLine -> LogMap -> LogMap+mapLog l m = + if better+ then M.insert i l m+ else m+ where+ better = maybe True newer $ M.lookup i m+ newer l' = date l' <= date l+ i = info l
+ Logs/Remote.hs view
@@ -0,0 +1,87 @@+{- git-annex remote log+ - + - Copyright 2011 Joey Hess <joey@kitenet.net>+ - + - Licensed under the GNU GPL version 3 or higher.+ -}++module Logs.Remote (+ readRemoteLog,+ configSet,+ keyValToConfig,+ configToKeyVal,++ prop_idempotent_configEscape+) where++import qualified Data.Map as M+import Data.Time.Clock.POSIX+import Data.Char++import Common.Annex+import qualified Annex.Branch+import Types.Remote+import Logs.UUIDBased++{- Filename of remote.log. -}+remoteLog :: FilePath+remoteLog = "remote.log"++{- Adds or updates a remote's config in the log. -}+configSet :: UUID -> RemoteConfig -> Annex ()+configSet u c = do+ ts <- liftIO getPOSIXTime+ Annex.Branch.change remoteLog $+ showLog showConfig . changeLog ts u c . parseLog parseConfig++{- Map of remotes by uuid containing key/value config maps. -}+readRemoteLog :: Annex (M.Map UUID RemoteConfig)+readRemoteLog = (simpleMap . parseLog parseConfig) <$> Annex.Branch.get remoteLog++parseConfig :: String -> Maybe RemoteConfig+parseConfig = Just . keyValToConfig . words++showConfig :: RemoteConfig -> String+showConfig = unwords . configToKeyVal++{- Given Strings like "key=value", generates a RemoteConfig. -}+keyValToConfig :: [String] -> RemoteConfig+keyValToConfig ws = M.fromList $ map (/=/) ws+ where+ (/=/) s = (k, v)+ where+ k = takeWhile (/= '=') s+ v = configUnEscape $ drop (1 + length k) s++configToKeyVal :: M.Map String String -> [String]+configToKeyVal m = map toword $ sort $ M.toList m+ where+ toword (k, v) = k ++ "=" ++ configEscape v++configEscape :: String -> String+configEscape = (>>= escape)+ where+ escape c+ | isSpace c || c `elem` "&" = "&" ++ show (ord c) ++ ";"+ | otherwise = [c]++configUnEscape :: String -> String+configUnEscape = unescape+ where+ unescape [] = []+ unescape (c:rest)+ | c == '&' = entity rest+ | otherwise = c : unescape rest+ entity s = if ok+ then chr (read num) : unescape rest+ else '&' : unescape s+ where+ num = takeWhile isNumber s+ r = drop (length num) s+ rest = drop 1 r+ ok = not (null num) && + not (null r) && head r == ';'++{- for quickcheck -}+prop_idempotent_configEscape :: String -> Bool+prop_idempotent_configEscape s = s == (configUnEscape . configEscape) s
+ Logs/Trust.hs view
@@ -0,0 +1,84 @@+{- git-annex trust+ -+ - Copyright 2010 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Logs.Trust (+ TrustLevel(..),+ trustGet,+ trustSet,+ trustPartition+) where++import qualified Data.Map as M+import Data.Time.Clock.POSIX++import Common.Annex+import Types.TrustLevel+import qualified Annex.Branch+import qualified Annex+import Logs.UUIDBased++{- Filename of trust.log. -}+trustLog :: FilePath+trustLog = "trust.log"++{- Returns a list of UUIDs that the trustLog indicates have the+ - specified trust level.+ - Note that the list can be incomplete for SemiTrusted, since that's+ - the default. -}+trustGet :: TrustLevel -> Annex [UUID]+trustGet level = M.keys . M.filter (== level) <$> trustMap++{- Partitions a list of UUIDs to those matching a TrustLevel and not. -}+trustPartition :: TrustLevel -> [UUID] -> Annex ([UUID], [UUID])+trustPartition level ls+ | level == SemiTrusted = do+ t <- trustGet Trusted+ u <- trustGet UnTrusted+ let uncandidates = t ++ u+ return $ partition (`notElem` uncandidates) ls+ | otherwise = do+ candidates <- trustGet level+ return $ partition (`elem` candidates) ls++{- Read the trustLog into a map, overriding with any+ - values from forcetrust. The map is cached for speed. -}+trustMap :: Annex TrustMap+trustMap = do+ cached <- Annex.getState Annex.trustmap+ case cached of+ Just m -> return m+ Nothing -> do+ overrides <- M.fromList <$> Annex.getState Annex.forcetrust+ m <- (M.union overrides . simpleMap . parseLog parseTrust) <$>+ Annex.Branch.get trustLog+ Annex.changeState $ \s -> s { Annex.trustmap = Just m }+ return m++parseTrust :: String -> Maybe TrustLevel+parseTrust s+ | length w > 0 = Just $ parse $ head w+ -- back-compat; the trust.log used to only list trusted repos+ | otherwise = Just Trusted+ where+ w = words s+ parse "1" = Trusted+ parse "0" = UnTrusted+ parse _ = SemiTrusted++showTrust :: TrustLevel -> String+showTrust SemiTrusted = "?"+showTrust UnTrusted = "0"+showTrust Trusted = "1"++{- Changes the trust level for a uuid in the trustLog. -}+trustSet :: UUID -> TrustLevel -> Annex ()+trustSet uuid@(UUID _) level = do+ ts <- liftIO getPOSIXTime+ Annex.Branch.change trustLog $+ showLog showTrust . changeLog ts uuid level . parseLog parseTrust+ Annex.changeState $ \s -> s { Annex.trustmap = Nothing }+trustSet NoUUID _ = error "unknown UUID; cannot modify trust level"
+ Logs/UUID.hs view
@@ -0,0 +1,89 @@+{- git-annex uuids+ -+ - Each git repository used by git-annex has an annex.uuid setting that+ - uniquely identifies that repository.+ -+ - UUIDs of remotes are cached in git config, using keys named+ - remote.<name>.annex-uuid+ -+ - uuid.log stores a list of known uuids, and their descriptions.+ -+ - Copyright 2010-2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Logs.UUID (+ describeUUID,+ recordUUID,+ uuidMap+) where++import qualified Data.Map as M+import Data.Time.Clock.POSIX++import Common.Annex+import qualified Annex.Branch+import Logs.UUIDBased+import qualified Annex.UUID++{- Filename of uuid.log. -}+logfile :: FilePath+logfile = "uuid.log"++{- Records a description for a uuid in the log. -}+describeUUID :: UUID -> String -> Annex ()+describeUUID uuid desc = do+ ts <- liftIO getPOSIXTime+ Annex.Branch.change logfile $+ showLog id . changeLog ts uuid desc . fixBadUUID . parseLog Just++{- Temporarily here to fix badly formatted uuid logs generated by+ - versions 3.20111105 and 3.20111025. + -+ - Those logs contain entries with the UUID and description flipped.+ - Due to parsing, if the description is multiword, only the first+ - will be taken to be the UUID. So, if the UUID of an entry does+ - not look like a UUID, and the last word of the description does,+ - flip them back.+ -}+fixBadUUID :: Log String -> Log String+fixBadUUID = M.fromList . map fixup . M.toList+ where+ fixup (k, v)+ | isbad = (fixeduuid, LogEntry (Date $ newertime v) fixedvalue)+ | otherwise = (k, v)+ where+ kuuid = fromUUID k+ isbad = (not $ isuuid kuuid) && isuuid lastword+ ws = words $ value v+ lastword = last ws+ fixeduuid = toUUID lastword+ fixedvalue = unwords $ kuuid:(take (length ws - 1) ws)+ -- For the fixed line to take precidence, it should be+ -- slightly newer, but only slightly.+ newertime (LogEntry (Date d) _) = d + minimumPOSIXTimeSlice+ newertime (LogEntry (Unknown) _) = minimumPOSIXTimeSlice+ minimumPOSIXTimeSlice = 0.000001+ isuuid s = length s == 36 && length (split "-" s) == 5++{- Records the uuid in the log, if it's not already there. -}+recordUUID :: UUID -> Annex ()+recordUUID u = go . M.lookup u =<< uuidMap + where+ go (Just "") = set+ go Nothing = set+ go _ = return ()+ set = describeUUID u ""++{- Read the uuidLog into a simple Map.+ -+ - The UUID of the current repository is included explicitly, since+ - it may not have been described and so otherwise would not appear. -}+uuidMap :: Annex (M.Map UUID String)+uuidMap = do+ m <- (simpleMap . parseLog Just) <$> Annex.Branch.get logfile+ u <- Annex.UUID.getUUID+ return $ M.insertWith' preferold u "" m+ where+ preferold = flip const
+ Logs/UUIDBased.hs view
@@ -0,0 +1,110 @@+{- git-annex uuid-based logs+ -+ - This is used to store information about a UUID in a way that can+ - be union merged.+ -+ - A line of the log will look like: "UUID[ INFO[ timestamp=foo]]"+ - The timestamp is last for backwards compatability reasons,+ - and may not be present on old log lines.+ - + - Copyright 2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Logs.UUIDBased (+ Log,+ LogEntry(..),+ TimeStamp(..),+ parseLog,+ showLog,+ changeLog,+ addLog,+ simpleMap,++ prop_TimeStamp_sane,+ prop_addLog_sane,+) where++import qualified Data.Map as M+import Data.Time.Clock.POSIX+import Data.Time+import System.Locale++import Common+import Types.UUID++data TimeStamp = Unknown | Date POSIXTime+ deriving (Eq, Ord, Show)++data LogEntry a = LogEntry+ { changed :: TimeStamp+ , value :: a+ } deriving (Eq, Show)++type Log a = M.Map UUID (LogEntry a)++tskey :: String+tskey = "timestamp="++showLog :: (a -> String) -> Log a -> String+showLog shower = unlines . map showpair . M.toList+ where+ showpair (k, LogEntry (Date p) v) =+ unwords [fromUUID k, shower v, tskey ++ show p]+ showpair (k, LogEntry Unknown v) =+ unwords [fromUUID k, shower v]++parseLog :: (String -> Maybe a) -> String -> Log a+parseLog parser = M.fromListWith best . mapMaybe parse . lines+ where+ parse line+ | null ws = Nothing+ | otherwise = parser (unwords info) >>= makepair+ where+ makepair v = Just (toUUID u, LogEntry ts v)+ ws = words line+ u = head ws+ end = last ws+ ts+ | tskey `isPrefixOf` end =+ pdate $ tail $ dropWhile (/= '=') end+ | otherwise = Unknown+ info+ | ts == Unknown = drop 1 ws+ | otherwise = drop 1 $ init ws+ pdate s = case parseTime defaultTimeLocale "%s%Qs" s of+ Nothing -> Unknown+ Just d -> Date $ utcTimeToPOSIXSeconds d++changeLog :: POSIXTime -> UUID -> a -> Log a -> Log a+changeLog t u v = M.insert u $ LogEntry (Date t) v++{- Only add an LogEntry if it's newer (or at least as new as) than any+ - existing LogEntry for a UUID. -}+addLog :: UUID -> LogEntry a -> Log a -> Log a+addLog = M.insertWith best++{- Converts a Log into a simple Map without the timestamp information.+ - This is a one-way trip, but useful for code that never needs to change+ - the log. -}+simpleMap :: Log a -> M.Map UUID a+simpleMap = M.map value++best :: LogEntry a -> LogEntry a -> LogEntry a+best new old+ | changed old > changed new = old+ | otherwise = new++-- Unknown is oldest.+prop_TimeStamp_sane :: Bool+prop_TimeStamp_sane = Unknown < Date 1++prop_addLog_sane :: Bool+prop_addLog_sane = newWins && newestWins+ where+ newWins = addLog (UUID "foo") (LogEntry (Date 1) "new") l == l2+ newestWins = addLog (UUID "foo") (LogEntry (Date 1) "newest") l2 /= l2++ l = M.fromList [(UUID "foo", LogEntry (Date 0) "old")]+ l2 = M.fromList [(UUID "foo", LogEntry (Date 1) "new")]
+ Logs/Web.hs view
@@ -0,0 +1,52 @@+{- Web url logs.+ -+ - Copyright 2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Logs.Web (+ URLString,+ webUUID,+ setUrl,+ setUrlPresent,+ getUrls+) where++import Common.Annex+import Logs.Presence+import Logs.Location++type URLString = String++-- Dummy uuid for the whole web. Do not alter.+webUUID :: UUID+webUUID = UUID "00000000-0000-0000-0000-000000000001"++{- The urls for a key are stored in remote/web/hash/key.log + - in the git-annex branch. -}+urlLog :: Key -> FilePath+urlLog key = "remote/web" </> hashDirLower key </> keyFile key ++ ".log"+oldurlLog :: Key -> FilePath+{- A bug used to store the urls elsewhere. -}+oldurlLog key = "remote/web" </> hashDirLower key </> show key ++ ".log"++{- Gets all urls that a key might be available from. -}+getUrls :: Key -> Annex [URLString]+getUrls key = do+ us <- currentLog (urlLog key)+ if null us+ then currentLog (oldurlLog key)+ else return us++{- Records a change in an url for a key. -}+setUrl :: Key -> URLString -> LogStatus -> Annex ()+setUrl key url status = do+ addLog (urlLog key) =<< logNow status url++ -- update location log to indicate that the web has the key, or not+ us <- getUrls key+ logChange key webUUID (if null us then InfoMissing else InfoPresent)++setUrlPresent :: Key -> URLString -> Annex ()+setUrlPresent key url = setUrl key url InfoPresent
Makefile view
@@ -1,16 +1,29 @@ PREFIX=/usr IGNORE=-ignore-package monads-fd GHCFLAGS=-O2 -Wall $(IGNORE) -fspec-constr-count=5+ ifdef PROFILE GHCFLAGS=-prof -auto-all -rtsopts -caf-all -fforce-recomp $(IGNORE) endif+ GHCMAKE=ghc $(GHCFLAGS) --make 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+all=$(bins) $(mans) docs++# Am I typing :make in vim? Do a fast build.+ifdef VIM+all=fast+endif++all: $(all)++# Disables optimisation. Not for production use.+fast: GHCFLAGS=-Wall $(IGNORE)+fast: $(bins) Build/SysConfig.hs: configure.hs Build/TestConfig.hs $(GHCMAKE) configure
Messages.hs view
@@ -1,6 +1,6 @@ {- git-annex output messages -- - Copyright 2010 Joey Hess <joey@kitenet.net>+ - Copyright 2010-2011 Joey Hess <joey@kitenet.net> - - Licensed under the GNU GPL version 3 or higher. -}@@ -20,6 +20,10 @@ warning, indent, maybeShowJSON,+ showCustom,+ showHeader,+ showRaw,+ setupConsole ) where @@ -31,7 +35,7 @@ import qualified Messages.JSON as JSON showStart :: String -> String -> Annex ()-showStart command file = handle (JSON.start command file) $+showStart command file = handle (JSON.start command $ Just file) $ flushed $ putStr $ command ++ " " ++ file ++ " " showNote :: String -> Annex ()@@ -64,17 +68,17 @@ showEndFail = showEndResult False showEndResult :: Bool -> Annex ()-showEndResult b = handle (JSON.end b) $ putStrLn msg+showEndResult ok = handle (JSON.end ok) $ putStrLn msg where msg- | b = "ok"+ | ok = "ok" | otherwise = "failed" showErr :: (Show a) => a -> Annex () showErr e = warning' $ "git-annex: " ++ show e warning :: String -> Annex ()-warning w = warning' (indent w)+warning = warning' . indent warning' :: String -> Annex () warning' w = do@@ -84,8 +88,29 @@ hPutStrLn stderr w indent :: String -> String-indent s = join "\n" $ map (\l -> " " ++ l) $ lines s+indent = join "\n" . map (\l -> " " ++ l) . lines +{- Shows a JSON value only when in json mode. -}+maybeShowJSON :: JSON a => [(String, a)] -> Annex ()+maybeShowJSON v = handle (JSON.add v) q++{- Performs an action that outputs nonstandard/customized output, and+ - in JSON mode wraps its output in JSON.start and JSON.end, so it's+ - a complete JSON document.+ - This is only needed when showStart and showEndOk is not used. -}+showCustom :: String -> Annex Bool -> Annex ()+showCustom command a = do+ handle (JSON.start command Nothing) q+ r <- a+ handle (JSON.end r) q++showHeader :: String -> Annex ()+showHeader h = handle q $+ flushed $ putStr $ h ++ ": "++showRaw :: String -> Annex ()+showRaw s = handle q $ putStrLn s+ {- By default, haskell honors the user's locale in its output to stdout - and stderr. While that's great for proper unicode support, for git-annex - all that's really needed is the ability to display simple messages@@ -100,16 +125,11 @@ 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+handle json normal = Annex.getState Annex.output >>= go+ where+ go Annex.NormalOutput = liftIO normal+ go Annex.QuietOutput = q+ go Annex.JSONOutput = liftIO json q :: Monad m => m () q = return ()
Messages/JSON.hs view
@@ -16,8 +16,12 @@ import qualified Utility.JSONStream as Stream -start :: String -> String -> IO ()-start command file = putStr $ Stream.start [("command", command), ("file", file)]+start :: String -> Maybe String -> IO ()+start command file =+ putStr $ Stream.start $ ("command", command) : filepart file+ where+ filepart Nothing = []+ filepart (Just f) = [("file", f)] end :: Bool -> IO () end b = putStr $ Stream.add [("success", b)] ++ Stream.end
Options.hs view
@@ -1,6 +1,6 @@-{- git-annex dashed options+{- git-annex command-line options -- - Copyright 2010 Joey Hess <joey@kitenet.net>+ - Copyright 2010-2011 Joey Hess <joey@kitenet.net> - - Licensed under the GNU GPL version 3 or higher. -}@@ -12,7 +12,6 @@ import Common.Annex import qualified Annex-import Command import Limit {- Each dashed command-line option results in generation of an action@@ -59,3 +58,37 @@ where longopt o = Option [] [o] $ NoArg $ addToken o shortopt o = Option o [] $ NoArg $ addToken o++{- Descriptions of params used in usage messages. -}+paramPaths :: String+paramPaths = paramOptional $ paramRepeating paramPath -- most often used+paramPath :: String+paramPath = "PATH"+paramKey :: String+paramKey = "KEY"+paramDesc :: String+paramDesc = "DESC"+paramUrl :: String+paramUrl = "URL"+paramNumber :: String+paramNumber = "NUMBER"+paramRemote :: String+paramRemote = "REMOTE"+paramGlob :: String+paramGlob = "GLOB"+paramName :: String+paramName = "NAME"+paramUUID :: String+paramUUID = "UUID"+paramType :: String+paramType = "TYPE"+paramKeyValue :: String+paramKeyValue = "K=V"+paramNothing :: String+paramNothing = ""+paramRepeating :: String -> String+paramRepeating s = s ++ " ..."+paramOptional :: String -> String+paramOptional s = "[" ++ s ++ "]"+paramPair :: String -> String -> String+paramPair a b = a ++ " " ++ b
− PresenceLog.hs
@@ -1,124 +0,0 @@-{- git-annex presence log- -- - This is used to store presence information in the git-annex branch in- - a way that can be union merged.- -- - A line of the log will look like: "date N INFO"- - Where N=1 when the INFO is present, and 0 otherwise.- - - - Copyright 2010-2011 Joey Hess <joey@kitenet.net>- -- - Licensed under the GNU GPL version 3 or higher.- -}--module PresenceLog (- LogStatus(..),- addLog,- readLog,- parseLog,- logNow,- compactLog,- currentLog,- LogLine-) where--import Data.Time.Clock.POSIX-import Data.Time-import System.Locale-import qualified Data.Map as M--import Common.Annex-import qualified Annex.Branch--data LogLine = LogLine {- date :: POSIXTime,- status :: LogStatus,- info :: String-} deriving (Eq)--data LogStatus = InfoPresent | InfoMissing | Undefined- deriving (Eq)--instance Show LogStatus where- show InfoPresent = "1"- show InfoMissing = "0"- show Undefined = "undefined"--instance Read LogStatus where- readsPrec _ "1" = [(InfoPresent, "")]- readsPrec _ "0" = [(InfoMissing, "")]- readsPrec _ _ = [(Undefined, "")]--instance Show LogLine where- show (LogLine d s i) = unwords [show d, show s, i]--instance Read LogLine where- -- This parser is robust in that even unparsable log lines are- -- read without an exception being thrown.- -- Such lines have a status of Undefined.- readsPrec _ string = - if length w >= 3- then maybe bad good pdate- else bad- where- w = words string- s = read $ w !! 1- i = w !! 2- pdate :: Maybe UTCTime- pdate = parseTime defaultTimeLocale "%s%Qs" $ head w-- good v = ret $ LogLine (utcTimeToPOSIXSeconds v) s i- bad = ret $ LogLine 0 Undefined ""- ret v = [(v, "")]--addLog :: FilePath -> LogLine -> Annex ()-addLog file line = Annex.Branch.change file $ \s -> - showLog $ compactLog (line : parseLog s)--{- Reads a log file.- - Note that the LogLines returned may be in any order. -}-readLog :: FilePath -> Annex [LogLine]-readLog file = parseLog <$> Annex.Branch.get file--parseLog :: String -> [LogLine]-parseLog = filter parsable . map read . lines- where- -- some lines may be unparseable, avoid them- parsable l = status l /= Undefined--{- Generates a log file. -}-showLog :: [LogLine] -> String-showLog = unlines . map show--{- Generates a new LogLine with the current date. -}-logNow :: LogStatus -> String -> Annex LogLine-logNow s i = do- now <- liftIO getPOSIXTime- return $ LogLine now s i--{- Reads a log and returns only the info that is still in effect. -}-currentLog :: FilePath -> Annex [String]-currentLog file = map info . filterPresent <$> readLog file--{- Returns the info from LogLines that are in effect. -}-filterPresent :: [LogLine] -> [LogLine]-filterPresent = filter (\l -> InfoPresent == status l) . compactLog--{- Compacts a set of logs, returning a subset that contains the current- - status. -}-compactLog :: [LogLine] -> [LogLine]-compactLog = M.elems . foldr mapLog M.empty--type LogMap = M.Map String LogLine--{- Inserts a log into a map of logs, if the log has better (ie, newer)- - information than the other logs in the map -}-mapLog :: LogLine -> LogMap -> LogMap-mapLog l m = - if better- then M.insert i l m- else m- where- better = maybe True newer $ M.lookup i m- newer l' = date l' <= date l- i = info l
Remote.hs view
@@ -16,6 +16,7 @@ hasKeyCheap, remoteTypes,+ remoteMap, byName, prettyPrintUUIDs, remotesWithUUID,@@ -25,7 +26,8 @@ nameToUUID, showTriedRemotes, showLocations,- forceTrust+ forceTrust,+ logStatus ) where import qualified Data.Map as M@@ -34,12 +36,13 @@ import Common.Annex import Types.Remote-import UUID import qualified Annex import Config-import Trust-import LocationLog-import RemoteLog+import Annex.UUID+import Logs.UUID+import Logs.Trust+import Logs.Location+import Logs.Remote import qualified Remote.Git import qualified Remote.S3@@ -81,6 +84,10 @@ u <- getRepoUUID r generate t r u (M.lookup u m) +{- Map of UUIDs of Remotes and their names. -}+remoteMap :: Annex (M.Map UUID String)+remoteMap = M.fromList . map (\r -> (uuid r, name r)) <$> genList+ {- Looks up a remote by name. (Or by UUID.) Only finds currently configured - git remotes. -} byName :: String -> Annex (Remote Annex)@@ -98,24 +105,23 @@ then return $ Left $ "there is no git remote named \"" ++ n ++ "\"" else return $ Right $ head match where- matching r = n == name r || n == uuid r+ matching r = n == name r || toUUID n == uuid r {- Looks up a remote by name (or by UUID, or even by description), - and returns its UUID. Finds even remotes that are not configured in - .git/config. -} nameToUUID :: String -> Annex UUID nameToUUID "." = getUUID -- special case for current repo-nameToUUID n = do- res <- byName' n- case res of- Left e -> fromMaybe (error e) <$> byDescription- Right r -> return $ uuid r+nameToUUID n = byName' n >>= go where- byDescription = do+ go (Right r) = return $ uuid r+ go (Left e) = fromMaybe (error e) <$> bydescription+ bydescription = do m <- uuidMap case M.lookup n $ transform swap m of Just u -> return $ Just u- Nothing -> return $ M.lookup n $ transform double m+ Nothing -> return $ byuuid m+ byuuid m = M.lookup (toUUID n) $ transform double m transform a = M.fromList . map a . M.toList swap (a, b) = (b, a) double (a, _) = (a, a)@@ -129,30 +135,30 @@ - of the UUIDs. -} prettyPrintUUIDs :: String -> [UUID] -> Annex String prettyPrintUUIDs desc uuids = do- here <- getUUID+ hereu <- getUUID m <- M.unionWith addname <$> uuidMap <*> remoteMap- maybeShowJSON [(desc, map (jsonify m here) uuids)]- return $ unwords $ map (\u -> "\t" ++ prettify m here u ++ "\n") uuids+ maybeShowJSON [(desc, map (jsonify m hereu) uuids)]+ return $ unwords $ map (\u -> "\t" ++ prettify m hereu u ++ "\n") uuids where addname d n | d == n = d+ | null d = n | otherwise = n ++ " (" ++ d ++ ")"- remoteMap = M.fromList . map (\r -> (uuid r, name r)) <$> genList findlog m u = M.findWithDefault "" u m- prettify m here u- | not (null d) = u ++ " -- " ++ d- | otherwise = u+ prettify m hereu u+ | not (null d) = fromUUID u ++ " -- " ++ d+ | otherwise = fromUUID u where- ishere = here == u+ ishere = hereu == u n = findlog m u d | null n && ishere = "here" | ishere = addname n "here" | otherwise = n- jsonify m here u = toJSObject- [ ("uuid", toJSON u)+ jsonify m hereu u = toJSObject+ [ ("uuid", toJSON $ fromUUID u) , ("description", toJSON $ findlog m u)- , ("here", toJSON $ here == u)+ , ("here", toJSON $ hereu == u) ] {- Filters a list of remotes to ones that have the listed uuids. -}@@ -163,12 +169,12 @@ remotesWithoutUUID :: [Remote Annex] -> [UUID] -> [Remote Annex] remotesWithoutUUID rs us = filter (\r -> uuid r `notElem` us) rs -{- Cost ordered lists of remotes that the LocationLog indicate may have a key.+{- Cost ordered lists of remotes that the Logs.Location indicate may have a key. -} keyPossibilities :: Key -> Annex [Remote Annex] keyPossibilities key = fst <$> keyPossibilities' False key -{- Cost ordered lists of remotes that the LocationLog indicate may have a key.+{- Cost ordered lists of remotes that the Logs.Location indicate may have a key. - - Also returns a list of UUIDs that are trusted to have the key - (some may not have configured remotes).@@ -223,3 +229,12 @@ r <- nameToUUID remotename Annex.changeState $ \s -> s { Annex.forcetrust = (r, level):Annex.forcetrust s }++{- Used to log a change in a remote's having a key. The change is logged+ - in the local repo, not on the remote. The process of transferring the+ - key to the remote, or removing the key from it *may* log the change+ - on the remote, but this cannot always be relied on. -}+logStatus :: Remote Annex -> Key -> Bool -> Annex ()+logStatus remote key present = logChange key (uuid remote) status+ where+ status = if present then InfoPresent else InfoMissing
Remote/Bup.hs view
@@ -15,9 +15,8 @@ import Common.Annex import Types.Remote import qualified Git-import UUID import Config-import Utility.Ssh+import Annex.Ssh import Remote.Helper.Special import Remote.Helper.Encryptable import Crypto@@ -103,31 +102,29 @@ store :: Git.Repo -> BupRepo -> Key -> Annex Bool store r buprepo k = do- g <- gitRepo- let src = gitAnnexLocation g k+ src <- fromRepo $ gitAnnexLocation k params <- bupSplitParams r buprepo k (File src) liftIO $ boolSystem "bup" params storeEncrypted :: Git.Repo -> BupRepo -> (Cipher, Key) -> Key -> Annex Bool storeEncrypted r buprepo (cipher, enck) k = do- g <- gitRepo- let src = gitAnnexLocation g k+ src <- fromRepo $ gitAnnexLocation k params <- bupSplitParams r buprepo enck (Param "-")- liftIO $ catchBool $+ liftIO $ catchBoolIO $ withEncryptedHandle cipher (L.readFile src) $ \h -> pipeBup params (Just h) Nothing retrieve :: BupRepo -> Key -> FilePath -> Annex Bool retrieve buprepo k f = do let params = bupParams "join" buprepo [Param $ show k]- liftIO $ catchBool $ do+ liftIO $ catchBoolIO $ do tofile <- openFile f WriteMode pipeBup params Nothing (Just tofile) retrieveEncrypted :: BupRepo -> (Cipher, Key) -> FilePath -> Annex Bool retrieveEncrypted buprepo (cipher, enck) f = do let params = bupParams "join" buprepo [Param $ show enck]- liftIO $ catchBool $ do+ liftIO $ catchBoolIO $ do (pid, h) <- hPipeFrom "bup" $ toCommand params withDecryptedContent cipher (L.hGetContents h) $ L.writeFile f forceSuccess pid@@ -142,13 +139,14 @@ - in a bup repository. One way it to check if the git repository has - a branch matching the name (as created by bup split -n). -}-checkPresent :: Git.Repo -> Git.Repo -> Key -> Annex (Either IOException Bool)+checkPresent :: Git.Repo -> Git.Repo -> Key -> Annex (Either String Bool) checkPresent r bupr k | Git.repoIsUrl bupr = do showAction $ "checking " ++ Git.repoDescribe r ok <- onBupRemote bupr boolSystem "git" params return $ Right ok- | otherwise = liftIO $ try $ boolSystem "git" $ Git.gitCommandLine bupr params+ | otherwise = liftIO $ catchMsgIO $+ boolSystem "git" $ Git.gitCommandLine params bupr where params = [ Params "show-ref --quiet --verify"@@ -162,13 +160,16 @@ then do showAction "storing uuid" onBupRemote r boolSystem "git"- [Params $ "config annex.uuid " ++ u]+ [Params $ "config annex.uuid " ++ v] >>! error "ssh failed" else liftIO $ do r' <- Git.configRead r- let olduuid = Git.configGet r' "annex.uuid" ""+ let olduuid = Git.configGet "annex.uuid" "" r' when (olduuid == "") $- Git.run r' "config" [Param "annex.uuid", Param u]+ Git.run "config"+ [Param "annex.uuid", Param v] r'+ where+ v = fromUUID u onBupRemote :: Git.Repo -> (FilePath -> [CommandParam] -> IO a) -> FilePath -> [CommandParam] -> Annex a onBupRemote r a command params = do@@ -193,8 +194,8 @@ | otherwise = liftIO $ do ret <- try $ Git.configRead r case ret of- Right r' -> return (Git.configGet r' "annex.uuid" "", r')- Left _ -> return ("", r)+ Right r' -> return (toUUID $ Git.configGet "annex.uuid" "" r', r')+ Left _ -> return (NoUUID, r) {- Converts a bup remote path spec into a Git.Repo. There are some - differences in path representation between git and bup. -}
Remote/Directory.hs view
@@ -8,14 +8,12 @@ module Remote.Directory (remote) where import qualified Data.ByteString.Lazy.Char8 as L-import System.IO.Error import qualified Data.Map as M import Common.Annex import Utility.CopyFile import Types.Remote import qualified Git-import UUID import Config import Utility.FileMode import Remote.Helper.Special@@ -71,17 +69,15 @@ store :: FilePath -> Key -> Annex Bool store d k = do- g <- gitRepo- let src = gitAnnexLocation g k+ src <- fromRepo $ gitAnnexLocation k let dest = dirKey d k- liftIO $ catchBool $ storeHelper dest $ copyFileExternal src dest+ liftIO $ catchBoolIO $ storeHelper dest $ copyFileExternal src dest storeEncrypted :: FilePath -> (Cipher, Key) -> Key -> Annex Bool storeEncrypted d (cipher, enck) k = do- g <- gitRepo- let src = gitAnnexLocation g k+ src <- fromRepo $ gitAnnexLocation k let dest = dirKey d enck- liftIO $ catchBool $ storeHelper dest $ encrypt src dest+ liftIO $ catchBoolIO $ storeHelper dest $ encrypt src dest where encrypt src dest = do withEncryptedContent cipher (L.readFile src) $ L.writeFile dest@@ -103,12 +99,12 @@ retrieveEncrypted :: FilePath -> (Cipher, Key) -> FilePath -> Annex Bool retrieveEncrypted d (cipher, enck) f =- liftIO $ catchBool $ do+ liftIO $ catchBoolIO $ do withDecryptedContent cipher (L.readFile (dirKey d enck)) $ L.writeFile f return True remove :: FilePath -> Key -> Annex Bool-remove d k = liftIO $ catchBool $ do+remove d k = liftIO $ catchBoolIO $ do allowWrite dir removeFile file removeDirectory dir@@ -117,5 +113,5 @@ file = dirKey d k dir = parentDir file -checkPresent :: FilePath -> Key -> Annex (Either IOException Bool)-checkPresent d k = liftIO $ try $ doesFileExist (dirKey d k)+checkPresent :: FilePath -> Key -> Annex (Either String Bool)+checkPresent d k = liftIO $ catchMsgIO $ doesFileExist (dirKey d k)
Remote/Git.hs view
@@ -13,13 +13,15 @@ import Common.Annex import Utility.CopyFile import Utility.RsyncFile-import Utility.Ssh+import Annex.Ssh import Types.Remote import qualified Git import qualified Annex-import UUID+import Annex.UUID import qualified Annex.Content+import qualified Annex.Branch import qualified Utility.Url as Url+import Utility.TempFile import Config import Init @@ -33,8 +35,16 @@ list :: Annex [Git.Repo] list = do- g <- gitRepo- return $ Git.remotes g+ c <- fromRepo Git.configMap+ mapM (tweakurl c) =<< fromRepo Git.remotes+ where+ annexurl n = "remote." ++ n ++ ".annexurl"+ tweakurl c r = do+ let n = fromJust $ Git.repoRemoteName r+ case M.lookup (annexurl n) c of+ Nothing -> return r+ Just url -> Git.repoRemoteNameSet n <$>+ inRepo (Git.genRemote url) gen :: Git.Repo -> UUID -> Maybe RemoteConfig -> Annex (Remote Annex) gen r u _ = do@@ -43,9 +53,11 @@ - the config of an URL remote is only read when there is no - cached UUID value. -} let cheap = not $ Git.repoIsUrl r- r' <- case (cheap, u) of- (True, _) -> tryGitConfigRead r- (False, "") -> tryGitConfigRead r+ notignored <- repoNotIgnored r+ r' <- case (cheap, notignored, u) of+ (_, False, _) -> return r+ (True, _, _) -> tryGitConfigRead r+ (False, _, NoUUID) -> tryGitConfigRead r _ -> return r u' <- getRepoUUID r'@@ -113,29 +125,49 @@ else old : exchange ls new {- Checks if a given remote has the content for a key inAnnex.- - If the remote cannot be accessed, returns a Left error.+ - If the remote cannot be accessed, or if it cannot determine+ - whether it has the content, returns a Left error message. -}-inAnnex :: Git.Repo -> Key -> Annex (Either IOException Bool)+inAnnex :: Git.Repo -> Key -> Annex (Either String Bool) inAnnex r key- | Git.repoIsHttp r = safely checkhttp+ | Git.repoIsHttp r = checkhttp | Git.repoIsUrl r = checkremote- | otherwise = safely checklocal+ | otherwise = checklocal where- checklocal = onLocal r (Annex.Content.inAnnex key)+ checkhttp = liftIO $ catchMsgIO $ Url.exists $ keyUrl r key checkremote = do showAction $ "checking " ++ Git.repoDescribe r- inannex <- onRemote r (boolSystem, False) "inannex" - [Param (show key)]- return $ Right inannex- checkhttp = Url.exists $ keyUrl r key- safely a = liftIO (try a ::IO (Either IOException Bool))+ onRemote r (check, unknown) "inannex" [Param (show key)]+ where+ check c p = dispatch <$> safeSystem c p+ dispatch ExitSuccess = Right True+ dispatch (ExitFailure 1) = Right False+ dispatch _ = unknown+ checklocal = dispatch <$> check+ where+ check = liftIO $ catchMsgIO $ onLocal r $+ Annex.Content.inAnnexSafe key+ dispatch (Left e) = Left e+ dispatch (Right (Just b)) = Right b+ dispatch (Right Nothing) = unknown+ unknown = Left $ "unable to check " ++ Git.repoDescribe r {- Runs an action on a local repository inexpensively, by making an annex - monad using that repository. -} onLocal :: Git.Repo -> Annex a -> IO a onLocal r a = do- annex <- Annex.new r- Annex.eval annex a+ -- Avoid re-reading the repository's configuration if it was+ -- already read.+ state <- if (M.null $ Git.configMap r)+ then Annex.new r+ else return $ Annex.newState r+ Annex.eval state $ do+ -- No need to update the branch; its data is not used+ -- for anything onLocal is used to do.+ Annex.Branch.disableUpdate+ ret <- a+ liftIO Git.reap+ return ret keyUrl :: Git.Repo -> Key -> String keyUrl r key = Git.repoLocation r ++ "/" ++ annexLocation key@@ -151,26 +183,27 @@ {- Tries to copy a key's content from a remote's annex to a file. -} copyFromRemote :: Git.Repo -> Key -> FilePath -> Annex Bool copyFromRemote r key file- | not $ Git.repoIsUrl r = rsyncOrCopyFile r (gitAnnexLocation r key) file+ | not $ Git.repoIsUrl r = do+ params <- rsyncParams r+ rsyncOrCopyFile params (gitAnnexLocation key r) file | Git.repoIsSsh r = rsyncHelper =<< rsyncParamsRemote r True key file- | Git.repoIsHttp r = Url.download (keyUrl r key) file+ | Git.repoIsHttp r = liftIO $ Url.download (keyUrl r key) file | otherwise = error "copying from non-ssh, non-http repo not supported" {- Tries to copy a key's content to a remote's annex. -} copyToRemote :: Git.Repo -> Key -> Annex Bool copyToRemote r key | not $ Git.repoIsUrl r = do- g <- gitRepo- let keysrc = gitAnnexLocation g key+ keysrc <- fromRepo $ gitAnnexLocation key+ params <- rsyncParams r -- run copy from perspective of remote liftIO $ onLocal r $ do ok <- Annex.Content.getViaTmp key $- rsyncOrCopyFile r keysrc+ rsyncOrCopyFile params keysrc Annex.Content.saveState return ok | Git.repoIsSsh r = do- g <- gitRepo- let keysrc = gitAnnexLocation g key+ keysrc <- fromRepo $ gitAnnexLocation key rsyncHelper =<< rsyncParamsRemote r False key keysrc | otherwise = error "copying to non-ssh repo not supported" @@ -186,15 +219,13 @@ {- Copys a file with rsync unless both locations are on the same - filesystem. Then cp could be faster. -}-rsyncOrCopyFile :: Git.Repo -> FilePath -> FilePath -> Annex Bool-rsyncOrCopyFile r src dest = do+rsyncOrCopyFile :: [CommandParam] -> FilePath -> FilePath -> Annex Bool+rsyncOrCopyFile rsyncparams src dest = do ss <- liftIO $ getFileStatus $ parentDir src ds <- liftIO $ getFileStatus $ parentDir dest if deviceID ss == deviceID ds then liftIO $ copyFileExternal src dest- else do- params <- rsyncParams r- rsyncHelper $ params ++ [Param src, Param dest]+ else rsyncHelper $ rsyncparams ++ [Param src, Param dest] {- Generates rsync parameters that ssh to the remote and asks it - to either receive or send the key's content. -}
Remote/Helper/Special.hs view
@@ -12,7 +12,6 @@ import Common.Annex import Types.Remote import qualified Git-import UUID {- Special remotes don't have a configured url, so Git.Repo does not - automatically generate remotes for them. This looks for a different@@ -20,20 +19,20 @@ -} findSpecialRemotes :: String -> Annex [Git.Repo] findSpecialRemotes s = do- g <- gitRepo- return $ map construct $ remotepairs g+ m <- fromRepo Git.configMap+ return $ map construct $ remotepairs m where- remotepairs r = M.toList $ M.filterWithKey match $ Git.configMap r- construct (k,_) = Git.repoRemoteNameSet Git.repoFromUnknown k+ remotepairs = M.toList . M.filterWithKey match+ construct (k,_) = Git.repoRemoteNameFromKey k Git.repoFromUnknown match k _ = startswith "remote." k && endswith (".annex-"++s) k {- Sets up configuration for a special remote in .git/config. -} gitConfigSpecialRemote :: UUID -> RemoteConfig -> String -> String -> Annex () gitConfigSpecialRemote u c k v = do- g <- gitRepo- liftIO $ do- Git.run g "config" [Param (configsetting $ "annex-"++k), Param v]- Git.run g "config" [Param (configsetting "annex-uuid"), Param u]+ set ("annex-"++k) v+ set ("annex-uuid") (fromUUID u) where+ set a b = inRepo $ Git.run "config"+ [Param (configsetting a), Param b] remotename = fromJust (M.lookup "name" c) configsetting s = "remote." ++ remotename ++ "." ++ s
Remote/Hook.hs view
@@ -9,13 +9,11 @@ import qualified Data.ByteString.Lazy.Char8 as L import qualified Data.Map as M-import System.IO.Error (try) import System.Exit import Common.Annex import Types.Remote import qualified Git-import UUID import Config import Annex.Content import Remote.Helper.Special@@ -99,14 +97,13 @@ store :: String -> Key -> Annex Bool store h k = do- g <- gitRepo- runHook h "store" k (Just $ gitAnnexLocation g k) $ return True+ src <- fromRepo $ gitAnnexLocation k+ runHook h "store" k (Just src) $ return True storeEncrypted :: String -> (Cipher, Key) -> Key -> Annex Bool storeEncrypted h (cipher, enck) k = withTmp enck $ \tmp -> do- g <- gitRepo- let f = gitAnnexLocation g k- liftIO $ withEncryptedContent cipher (L.readFile f) $ \s -> L.writeFile tmp s+ src <- fromRepo $ gitAnnexLocation k+ liftIO $ withEncryptedContent cipher (L.readFile src) $ L.writeFile tmp runHook h "store" enck (Just tmp) $ return True retrieve :: String -> Key -> FilePath -> Annex Bool@@ -114,18 +111,18 @@ retrieveEncrypted :: String -> (Cipher, Key) -> FilePath -> Annex Bool retrieveEncrypted h (cipher, enck) f = withTmp enck $ \tmp ->- runHook h "retrieve" enck (Just tmp) $ liftIO $ catchBool $ do+ runHook h "retrieve" enck (Just tmp) $ liftIO $ catchBoolIO $ do withDecryptedContent cipher (L.readFile tmp) $ L.writeFile f return True remove :: String -> Key -> Annex Bool remove h k = runHook h "remove" k Nothing $ return True -checkPresent :: Git.Repo -> String -> Key -> Annex (Either IOException Bool)+checkPresent :: Git.Repo -> String -> Key -> Annex (Either String Bool) checkPresent r h k = do showAction $ "checking " ++ Git.repoDescribe r v <- lookupHook h "checkpresent"- liftIO (try (check v) ::IO (Either IOException Bool))+ liftIO $ catchMsgIO $ check v where findkey s = show k `elem` lines s env = hookEnv k Nothing
Remote/Rsync.hs view
@@ -13,7 +13,6 @@ import Common.Annex import Types.Remote import qualified Git-import UUID import Config import Annex.Content import Remote.Helper.Special@@ -82,24 +81,26 @@ gitConfigSpecialRemote u c' "rsyncurl" url return c' +rsyncEscape :: RsyncOpts -> String -> String+rsyncEscape o s+ | rsyncUrlIsShell (rsyncUrl o) = shellEscape s+ | otherwise = s+ rsyncKey :: RsyncOpts -> Key -> String-rsyncKey o k = rsyncUrl o </> hashDirMixed k </> shellEscape (f </> f)+rsyncKey o k = rsyncUrl o </> hashDirMixed k </> rsyncEscape o (f </> f) where f = keyFile k rsyncKeyDir :: RsyncOpts -> Key -> String-rsyncKeyDir o k = rsyncUrl o </> hashDirMixed k </> shellEscape (keyFile k)+rsyncKeyDir o k = rsyncUrl o </> hashDirMixed k </> rsyncEscape o (keyFile k) store :: RsyncOpts -> Key -> Annex Bool-store o k = do- g <- gitRepo- rsyncSend o k (gitAnnexLocation g k)+store o k = rsyncSend o k =<< fromRepo (gitAnnexLocation k) storeEncrypted :: RsyncOpts -> (Cipher, Key) -> Key -> Annex Bool storeEncrypted o (cipher, enck) k = withTmp enck $ \tmp -> do- g <- gitRepo- let f = gitAnnexLocation g k- liftIO $ withEncryptedContent cipher (L.readFile f) $ \s -> L.writeFile tmp s+ src <- fromRepo $ gitAnnexLocation k+ liftIO $ withEncryptedContent cipher (L.readFile src) $ L.writeFile tmp rsyncSend o enck tmp retrieve :: RsyncOpts -> Key -> FilePath -> Annex Bool@@ -114,7 +115,7 @@ retrieveEncrypted o (cipher, enck) f = withTmp enck $ \tmp -> do res <- retrieve o enck tmp if res- then liftIO $ catchBool $ do+ then liftIO $ catchBoolIO $ do withDecryptedContent cipher (L.readFile tmp) $ L.writeFile f return True else return res@@ -132,7 +133,7 @@ , Param $ rsyncKeyDir o k ] -checkPresent :: Git.Repo -> RsyncOpts -> Key -> Annex (Either IOException Bool)+checkPresent :: Git.Repo -> RsyncOpts -> Key -> Annex (Either String Bool) checkPresent r o k = do showAction $ "checking " ++ Git.repoDescribe r -- note: Does not currently differnetiate between rsync failing@@ -152,9 +153,9 @@ - up trees for rsync. -} withRsyncScratchDir :: (FilePath -> Annex Bool) -> Annex Bool withRsyncScratchDir a = do- g <- gitRepo pid <- liftIO getProcessID- let tmp = gitAnnexTmpDir g </> "rsynctmp" </> show pid+ t <- fromRepo gitAnnexTmpDir+ let tmp = t </> "rsynctmp" </> show pid nuke tmp liftIO $ createDirectoryIfMissing True tmp res <- a tmp
Remote/S3real.hs view
@@ -21,7 +21,6 @@ import Types.Remote import Types.Key import qualified Git-import UUID import Config import Remote.Helper.Special import Remote.Helper.Encryptable@@ -65,7 +64,7 @@ s3Setup u c = handlehost $ M.lookup "host" c where remotename = fromJust (M.lookup "name" c)- defbucket = remotename ++ "-" ++ u+ defbucket = remotename ++ "-" ++ fromUUID u defaults = M.fromList [ ("datacenter", "US") , ("storageclass", "STANDARD")@@ -113,8 +112,8 @@ store :: Remote Annex -> Key -> Annex Bool store r k = s3Action r False $ \(conn, bucket) -> do- g <- gitRepo- res <- liftIO $ storeHelper (conn, bucket) r k $ gitAnnexLocation g k+ dest <- fromRepo $ gitAnnexLocation k+ res <- liftIO $ storeHelper (conn, bucket) r k dest s3Bool res storeEncrypted :: Remote Annex -> (Cipher, Key) -> Key -> Annex Bool@@ -122,8 +121,7 @@ -- To get file size of the encrypted content, have to use a temp file. -- (An alternative would be chunking to to a constant size.) withTmp enck $ \tmp -> do- g <- gitRepo- let f = gitAnnexLocation g k+ f <- fromRepo $ gitAnnexLocation k liftIO $ withEncryptedContent cipher (L.readFile f) $ \s -> L.writeFile tmp s res <- liftIO $ storeHelper (conn, bucket) r enck tmp s3Bool res@@ -174,7 +172,7 @@ res <- liftIO $ deleteObject conn $ bucketKey r bucket k s3Bool res -checkPresent :: Remote Annex -> Key -> Annex (Either IOException Bool)+checkPresent :: Remote Annex -> Key -> Annex (Either String Bool) checkPresent r k = s3Action r noconn $ \(conn, bucket) -> do showAction $ "checking " ++ name r res <- liftIO $ getObjectInfo conn $ bucketKey r bucket k@@ -288,7 +286,7 @@ _ -> return Nothing else return $ Just (ak, sk) where- getEnvKey s = liftIO $ catch (getEnv s) (const $ return "")+ getEnvKey s = liftIO $ catchDefaultIO (getEnv s) "" {- Stores S3 creds encrypted in the remote's config if possible. -} s3SetCreds :: RemoteConfig -> Annex RemoteConfig
Remote/Web.hs view
@@ -5,21 +5,15 @@ - Licensed under the GNU GPL version 3 or higher. -} -module Remote.Web (- remote,- setUrl-) where+module Remote.Web (remote) where import Common.Annex import Types.Remote import qualified Git-import UUID import Config-import PresenceLog-import LocationLog+import Logs.Web import qualified Utility.Url as Url--type URLString = String+import Utility.Monad remote :: RemoteType Annex remote = RemoteType {@@ -33,11 +27,7 @@ -- (If the web should cease to exist, remove this module and redistribute -- a new release to the survivors by carrier pigeon.) list :: Annex [Git.Repo]-list = return [Git.repoRemoteNameSet Git.repoFromUnknown "remote.web.dummy"]---- Dummy uuid for the whole web. Do not alter.-webUUID :: UUID-webUUID = "00000000-0000-0000-0000-000000000001"+list = return [Git.repoRemoteNameSet "web" Git.repoFromUnknown] gen :: Git.Repo -> UUID -> Maybe RemoteConfig -> Annex (Remote Annex) gen r _ _ = @@ -54,38 +44,15 @@ repo = r } -{- The urls for a key are stored in remote/web/hash/key.log - - in the git-annex branch. -}-urlLog :: Key -> FilePath-urlLog key = "remote/web" </> hashDirLower key </> keyFile key ++ ".log"-oldurlLog :: Key -> FilePath-{- A bug used to store the urls elsewhere. -}-oldurlLog key = "remote/web" </> hashDirLower key </> show key ++ ".log"--getUrls :: Key -> Annex [URLString]-getUrls key = do- us <- currentLog (urlLog key)- if null us- then currentLog (oldurlLog key)- else return us--{- Records a change in an url for a key. -}-setUrl :: Key -> URLString -> LogStatus -> Annex ()-setUrl key url status = do- g <- gitRepo- addLog (urlLog key) =<< logNow status url-- -- update location log to indicate that the web has the key, or not- us <- getUrls key- logChange g key webUUID (if null us then InfoMissing else InfoPresent)- downloadKey :: Key -> FilePath -> Annex Bool downloadKey key file = get =<< getUrls key where get [] = do warning "no known url" return False- get urls = anyM (`Url.download` file) urls+ get urls = do+ showOutput -- make way for download progress bar+ liftIO $ anyM (`Url.download` file) urls uploadKey :: Key -> Annex Bool uploadKey _ = do@@ -97,7 +64,7 @@ warning "removal from web not supported" return False -checkKey :: Key -> Annex (Either IOException Bool)+checkKey :: Key -> Annex (Either String Bool) checkKey key = do us <- getUrls key if null us
− RemoteLog.hs
@@ -1,88 +0,0 @@-{- git-annex remote log- - - - Copyright 2011 Joey Hess <joey@kitenet.net>- - - - Licensed under the GNU GPL version 3 or higher.- -}--module RemoteLog (- readRemoteLog,- configSet,- keyValToConfig,- configToKeyVal,-- prop_idempotent_configEscape-) where--import qualified Data.Map as M-import Data.Time.Clock.POSIX-import Data.Char--import Common.Annex-import qualified Annex.Branch-import Types.Remote-import UUID-import UUIDLog--{- Filename of remote.log. -}-remoteLog :: FilePath-remoteLog = "remote.log"--{- Adds or updates a remote's config in the log. -}-configSet :: UUID -> RemoteConfig -> Annex ()-configSet u c = do- ts <- liftIO $ getPOSIXTime- Annex.Branch.change remoteLog $- showLog showConfig . changeLog ts u c . parseLog parseConfig--{- Map of remotes by uuid containing key/value config maps. -}-readRemoteLog :: Annex (M.Map UUID RemoteConfig)-readRemoteLog = (simpleMap . parseLog parseConfig) <$> Annex.Branch.get remoteLog--parseConfig :: String -> Maybe RemoteConfig-parseConfig = Just . keyValToConfig . words--showConfig :: RemoteConfig -> String-showConfig = unwords . configToKeyVal--{- Given Strings like "key=value", generates a RemoteConfig. -}-keyValToConfig :: [String] -> RemoteConfig-keyValToConfig ws = M.fromList $ map (/=/) ws- where- (/=/) s = (k, v)- where- k = takeWhile (/= '=') s- v = configUnEscape $ drop (1 + length k) s--configToKeyVal :: M.Map String String -> [String]-configToKeyVal m = map toword $ sort $ M.toList m- where- toword (k, v) = k ++ "=" ++ configEscape v--configEscape :: String -> String-configEscape = (>>= escape)- where- escape c- | isSpace c || c `elem` "&" = "&" ++ show (ord c) ++ ";"- | otherwise = [c]--configUnEscape :: String -> String-configUnEscape = unescape- where- unescape [] = []- unescape (c:rest)- | c == '&' = entity rest- | otherwise = c : unescape rest- entity s = if ok- then chr (read num) : unescape rest- else '&' : unescape s- where- num = takeWhile isNumber s- r = drop (length num) s- rest = drop 1 r- ok = not (null num) && - not (null r) && head r == ';'--{- for quickcheck -}-prop_idempotent_configEscape :: String -> Bool-prop_idempotent_configEscape s = s == (configUnEscape . configEscape) s
+ Seek.hs view
@@ -0,0 +1,115 @@+{- git-annex command seeking+ - + - These functions find appropriate files or other things based on+ - the values a user passes to a command, and prepare actions operating+ - on them.+ -+ - Copyright 2010-2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Seek where++import Common.Annex+import Types.Command+import Types.Key+import Backend+import qualified Annex+import qualified Git+import qualified Git.LsFiles as LsFiles+import qualified Limit++seekHelper :: ([FilePath] -> Git.Repo -> IO [FilePath]) -> [FilePath] -> Annex [FilePath]+seekHelper a params = do+ g <- gitRepo+ liftIO $ runPreserveOrder (`a` g) params++withFilesInGit :: (FilePath -> CommandStart) -> CommandSeek+withFilesInGit a params = prepFiltered a $ seekHelper LsFiles.inRepo params++withAttrFilesInGit :: String -> ((FilePath, String) -> CommandStart) -> CommandSeek+withAttrFilesInGit attr a params = do+ files <- seekHelper LsFiles.inRepo params+ prepFilteredGen a fst $ inRepo $ Git.checkAttr attr files++withNumCopies :: (Maybe Int -> FilePath -> CommandStart) -> CommandSeek+withNumCopies a params = withAttrFilesInGit "annex.numcopies" go params+ where+ go (file, v) = a (readMaybe v) file++withBackendFilesInGit :: (BackendFile -> CommandStart) -> CommandSeek+withBackendFilesInGit a params = do+ files <- seekHelper LsFiles.inRepo params+ prepBackendPairs a files++withFilesMissing :: (String -> CommandStart) -> CommandSeek+withFilesMissing a params = prepFiltered a $ liftIO $ filterM missing params+ where+ missing = liftM not . doesFileExist++withFilesNotInGit :: (BackendFile -> CommandStart) -> CommandSeek+withFilesNotInGit a params = do+ force <- Annex.getState Annex.force+ newfiles <- seekHelper (LsFiles.notInRepo force) params+ prepBackendPairs a newfiles++withWords :: ([String] -> CommandStart) -> CommandSeek+withWords a params = return [a params]++withStrings :: (String -> CommandStart) -> CommandSeek+withStrings a params = return $ map a params++withFilesToBeCommitted :: (String -> CommandStart) -> CommandSeek+withFilesToBeCommitted a params = prepFiltered a $+ seekHelper LsFiles.stagedNotDeleted params++withFilesUnlocked :: (BackendFile -> CommandStart) -> CommandSeek+withFilesUnlocked = withFilesUnlocked' LsFiles.typeChanged++withFilesUnlockedToBeCommitted :: (BackendFile -> CommandStart) -> CommandSeek+withFilesUnlockedToBeCommitted = withFilesUnlocked' LsFiles.typeChangedStaged++withFilesUnlocked' :: ([FilePath] -> Git.Repo -> IO [FilePath]) -> (BackendFile -> CommandStart) -> CommandSeek+withFilesUnlocked' typechanged a params = do+ -- unlocked files have changed type from a symlink to a regular file+ top <- fromRepo Git.workTree+ typechangedfiles <- seekHelper typechanged params+ unlockedfiles <- liftIO $ filterM notSymlink $+ map (\f -> top ++ "/" ++ f) typechangedfiles+ prepBackendPairs a unlockedfiles++withKeys :: (Key -> CommandStart) -> CommandSeek+withKeys a params = return $ map (a . parse) params+ where+ parse p = fromMaybe (error "bad key") $ readKey p++withNothing :: CommandStart -> CommandSeek+withNothing a [] = return [a]+withNothing _ _ = error "This command takes no parameters."+++prepFiltered :: (FilePath -> CommandStart) -> Annex [FilePath] -> Annex [CommandStart]+prepFiltered a = prepFilteredGen a id++prepBackendPairs :: (BackendFile -> CommandStart) -> CommandSeek+prepBackendPairs a fs = prepFilteredGen a snd (chooseBackends fs)++prepFilteredGen :: (b -> CommandStart) -> (b -> FilePath) -> Annex [b] -> Annex [CommandStart]+prepFilteredGen a d fs = do+ matcher <- Limit.getMatcher+ prepStart (proc matcher) fs+ where+ proc matcher v = do+ let f = d v+ ok <- matcher f+ if ok then a v else return Nothing++{- Generates a list of CommandStart actions that will be run to perform a+ - command, using a list (ie of files) coming from an action. The list+ - will be produced and consumed lazily. -}+prepStart :: (b -> CommandStart) -> Annex [b] -> Annex [CommandStart]+prepStart a = liftM (map a)++notSymlink :: FilePath -> IO Bool+notSymlink f = liftIO $ not . isSymbolicLink <$> getSymbolicLinkStatus f
− Trust.hs
@@ -1,70 +0,0 @@-{- git-annex trust- -- - Copyright 2010 Joey Hess <joey@kitenet.net>- -- - Licensed under the GNU GPL version 3 or higher.- -}--module Trust (- TrustLevel(..),- trustGet,- trustSet,- trustPartition-) where--import qualified Data.Map as M-import Data.Time.Clock.POSIX--import Common.Annex-import Types.TrustLevel-import qualified Annex.Branch-import qualified Annex--import UUID-import UUIDLog--{- Filename of trust.log. -}-trustLog :: FilePath-trustLog = "trust.log"--{- Returns a list of UUIDs at the specified trust level. -}-trustGet :: TrustLevel -> Annex [UUID]-trustGet level = M.keys . M.filter (== level) <$> trustMap--{- Read the trustLog into a map, overriding with any- - values from forcetrust. The map is cached for speed. -}-trustMap :: Annex TrustMap-trustMap = do- cached <- Annex.getState Annex.trustmap- case cached of- Just m -> return m- Nothing -> do- overrides <- M.fromList <$> Annex.getState Annex.forcetrust- m <- (M.union overrides . simpleMap . parseLog parseTrust) <$>- Annex.Branch.get trustLog- Annex.changeState $ \s -> s { Annex.trustmap = Just m }- return m--parseTrust :: String -> Maybe TrustLevel-parseTrust s- | length w > 0 = readMaybe $ head w- -- back-compat; the trust.log used to only list trusted repos- | otherwise = Just Trusted- where- w = words s--{- Changes the trust level for a uuid in the trustLog. -}-trustSet :: UUID -> TrustLevel -> Annex ()-trustSet uuid level = do- when (null uuid) $- error "unknown UUID; cannot modify trust level"- ts <- liftIO $ getPOSIXTime- Annex.Branch.change trustLog $- showLog show . changeLog ts uuid level . parseLog parseTrust- Annex.changeState $ \s -> s { Annex.trustmap = Nothing }--{- Partitions a list of UUIDs to those matching a TrustLevel and not. -}-trustPartition :: TrustLevel -> [UUID] -> Annex ([UUID], [UUID])-trustPartition level ls = do- candidates <- trustGet level- return $ partition (`elem` candidates) ls
Types.hs view
@@ -8,9 +8,11 @@ module Types ( Annex, Backend,- Key+ Key,+ UUID(..) ) where import Annex import Types.Backend import Types.Key+import Types.UUID
+ Types/Command.hs view
@@ -0,0 +1,46 @@+{- git-annex command data types+ -+ - Copyright 2010-2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Types.Command where++import Types++{- A command runs in these stages.+ -+ - a. The check stage runs checks, that error out if+ - anything prevents the command from running. -}+data CommandCheck = CommandCheck { idCheck :: Int, runCheck :: Annex () }+{- b. The seek stage takes the parameters passed to the command,+ - looks through the repo to find the ones that are relevant+ - to that command (ie, new files to add), and generates+ - a list of start stage actions. -}+type CommandSeek = [String] -> Annex [CommandStart]+{- c. The start stage is run before anything is printed about the+ - command, is passed some input, and can early abort it+ - if the input does not make sense. It should run quickly and+ - should not modify Annex state. -}+type CommandStart = Annex (Maybe CommandPerform)+{- d. The perform stage is run after a message is printed about the command+ - being run, and it should be where the bulk of the work happens. -}+type CommandPerform = Annex (Maybe CommandCleanup)+{- e. The cleanup stage is run only if the perform stage succeeds, and it+ - returns the overall success/fail of the command. -}+type CommandCleanup = Annex Bool++{- A command is defined by specifying these things. -}+data Command = Command {+ cmdnorepo :: Maybe (IO ()),+ cmdcheck :: [CommandCheck],+ cmdname :: String,+ cmdparams :: String,+ cmdseek :: [CommandSeek],+ cmddesc :: String+}++{- CommandCheck functions can be compared using their unique id. -}+instance Eq CommandCheck where+ a == b = idCheck a == idCheck b
Types/Crypto.hs view
@@ -7,17 +7,9 @@ module Types.Crypto where -import Data.String.Utils- -- XXX ideally, this would be a locked memory region newtype Cipher = Cipher String data EncryptedCipher = EncryptedCipher String KeyIds newtype KeyIds = KeyIds [String]--instance Show KeyIds where- show (KeyIds ks) = join "," ks--instance Read KeyIds where- readsPrec _ s = [(KeyIds (split "," s), "")]
Types/Remote.hs view
@@ -9,12 +9,12 @@ module Types.Remote where -import Control.Exception import Data.Map as M import Data.Ord import qualified Git import Types.Key+import Types.UUID type RemoteConfig = M.Map String String @@ -25,15 +25,15 @@ -- enumerates remotes of this type enumerate :: a [Git.Repo], -- generates a remote of this type- generate :: Git.Repo -> String -> Maybe RemoteConfig -> a (Remote a),+ generate :: Git.Repo -> UUID -> Maybe RemoteConfig -> a (Remote a), -- initializes or changes a remote- setup :: String -> RemoteConfig -> a RemoteConfig+ setup :: UUID -> RemoteConfig -> a RemoteConfig } {- An individual remote. -} data Remote a = Remote { -- each Remote has a unique uuid- uuid :: String,+ uuid :: UUID, -- each Remote has a human visible name name :: String, -- Remotes have a use cost; higher is more expensive@@ -45,8 +45,8 @@ -- removes a key's contents removeKey :: Key -> a Bool, -- Checks if a key is present in the remote; if the remote- -- cannot be accessed returns a Left error.- hasKey :: Key -> a (Either IOException Bool),+ -- cannot be accessed returns a Left error message.+ hasKey :: Key -> a (Either String Bool), -- Some remotes can check hasKey without an expensive network -- operation. hasKeyCheap :: Bool,
Types/TrustLevel.hs view
@@ -17,14 +17,4 @@ data TrustLevel = SemiTrusted | UnTrusted | Trusted deriving Eq -instance Show TrustLevel where- show SemiTrusted = "?"- show UnTrusted = "0"- show Trusted = "1"--instance Read TrustLevel where- readsPrec _ "1" = [(Trusted, "")]- readsPrec _ "0" = [(UnTrusted, "")]- readsPrec _ _ = [(SemiTrusted, "")]- type TrustMap = M.Map UUID TrustLevel
Types/UUID.hs view
@@ -7,5 +7,14 @@ module Types.UUID where --- might be nice to have a newtype, but lots of stuff treats uuids as strings-type UUID = String+-- A UUID is either an arbitrary opaque string, or UUID info may be missing.+data UUID = NoUUID | UUID String+ deriving (Eq, Ord, Show)++fromUUID :: UUID -> String+fromUUID (UUID u) = u+fromUUID NoUUID = ""++toUUID :: String -> UUID+toUUID [] = NoUUID+toUUID s = UUID s
− UUID.hs
@@ -1,95 +0,0 @@-{- git-annex uuids- -- - Each git repository used by git-annex has an annex.uuid setting that- - uniquely identifies that repository.- -- - UUIDs of remotes are cached in git config, using keys named- - remote.<name>.annex-uuid- -- - uuid.log stores a list of known uuids, and their descriptions.- -- - Copyright 2010-2011 Joey Hess <joey@kitenet.net>- -- - Licensed under the GNU GPL version 3 or higher.- -}--module UUID (- UUID,- getUUID,- getRepoUUID,- getUncachedUUID,- prepUUID,- genUUID,- describeUUID,- uuidMap-) where--import qualified Data.Map as M-import Data.Time.Clock.POSIX--import Common.Annex-import qualified Git-import qualified Annex.Branch-import Types.UUID-import qualified Build.SysConfig as SysConfig-import Config-import UUIDLog--configkey :: String-configkey = "annex.uuid"--{- Filename of uuid.log. -}-logfile :: FilePath-logfile = "uuid.log"--{- Generates a UUID. There is a library for this, but it's not packaged,- - so use the command line tool. -}-genUUID :: IO UUID-genUUID = pOpen ReadFromPipe command params hGetLine- where- command = SysConfig.uuid- params = if command == "uuid"- -- request a random uuid be generated- then ["-m"]- -- uuidgen generates random uuid by default- else []--getUUID :: Annex UUID-getUUID = getRepoUUID =<< gitRepo--{- Looks up a repo's UUID. May return "" if none is known. -}-getRepoUUID :: Git.Repo -> Annex UUID-getRepoUUID r = do- g <- gitRepo-- let c = cached g- let u = getUncachedUUID r- - if c /= u && u /= ""- then do- updatecache g u- return u- else return c- where- cached g = Git.configGet g cachekey ""- updatecache g u = when (g /= r) $ setConfig cachekey u- cachekey = "remote." ++ fromMaybe "" (Git.repoRemoteName r) ++ ".annex-uuid"--getUncachedUUID :: Git.Repo -> UUID-getUncachedUUID r = Git.configGet r configkey ""--{- Make sure that the repo has an annex.uuid setting. -}-prepUUID :: Annex ()-prepUUID = whenM (null <$> getUUID) $- setConfig configkey =<< liftIO genUUID--{- Records a description for a uuid in the log. -}-describeUUID :: UUID -> String -> Annex ()-describeUUID uuid desc = do- ts <- liftIO $ getPOSIXTime- Annex.Branch.change logfile $- showLog id . changeLog ts uuid desc . parseLog Just--{- Read the uuidLog into a simple Map -}-uuidMap :: Annex (M.Map UUID String)-uuidMap = (simpleMap . parseLog Just) <$> Annex.Branch.get logfile
− UUIDLog.hs
@@ -1,110 +0,0 @@-{- git-annex uuid-based logs- -- - This is used to store information about a UUID in a way that can- - be union merged.- -- - A line of the log will look like: "UUID[ INFO[ timestamp=foo]]"- - The timestamp is last for backwards compatability reasons,- - and may not be present on old log lines.- - - - Copyright 2011 Joey Hess <joey@kitenet.net>- -- - Licensed under the GNU GPL version 3 or higher.- -}--module UUIDLog (- Log,- LogEntry(..),- parseLog,- showLog,- changeLog,- addLog,- simpleMap,-- prop_TimeStamp_sane,- prop_addLog_sane,-) where--import qualified Data.Map as M-import Data.Time.Clock.POSIX-import Data.Time-import System.Locale--import Common-import Types.UUID--data TimeStamp = Unknown | Date POSIXTime- deriving (Eq, Ord, Show)--data LogEntry a = LogEntry- { changed :: TimeStamp- , value :: a- } deriving (Eq, Show)--type Log a = M.Map UUID (LogEntry a)--tskey :: String-tskey = "timestamp="--showLog :: (a -> String) -> Log a -> String-showLog shower = unlines . map showpair . M.toList- where- showpair (k, LogEntry (Date p) v) =- unwords [k, shower v, tskey ++ show p]- showpair (k, LogEntry Unknown v) =- unwords [k, shower v]--parseLog :: (String -> Maybe a) -> String -> Log a-parseLog parser = M.fromListWith best . catMaybes . map pair . lines- where- pair line- | null ws = Nothing- | otherwise = case parser $ unwords info of- Nothing -> Nothing- Just v -> Just (u, LogEntry c v)- where- ws = words line- u = head ws- end = last ws- c- | tskey `isPrefixOf` end =- pdate $ tail $ dropWhile (/= '=') end- | otherwise = Unknown- info- | c == Unknown = drop 1 ws- | otherwise = drop 1 $ init ws- pdate s = case parseTime defaultTimeLocale "%s%Qs" s of- Nothing -> Unknown- Just d -> Date $ utcTimeToPOSIXSeconds d--changeLog :: POSIXTime -> UUID -> a -> Log a -> Log a-changeLog t u v = M.insert u $ LogEntry (Date t) v--{- Only add an LogEntry if it's newer (or at least as new as) than any- - existing LogEntry for a UUID. -}-addLog :: UUID -> LogEntry a -> Log a -> Log a-addLog = M.insertWith best--{- Converts a Log into a simple Map without the timestamp information.- - This is a one-way trip, but useful for code that never needs to change- - the log. -}-simpleMap :: Log a -> M.Map UUID a-simpleMap = M.map value--best :: LogEntry a -> LogEntry a -> LogEntry a-best new old- | changed old > changed new = old- | otherwise = new---- Unknown is oldest.-prop_TimeStamp_sane :: Bool-prop_TimeStamp_sane = Unknown < Date 1--prop_addLog_sane :: Bool-prop_addLog_sane = newWins && newestWins- where- newWins = addLog "foo" (LogEntry (Date 1) "new") l == l2- newestWins = addLog "foo" (LogEntry (Date 1) "newest") l2 /= l2-- l = M.fromList [("foo", LogEntry (Date 0) "old")]- l2 = M.fromList [("foo", LogEntry (Date 1) "new")]
Upgrade/V0.hs view
@@ -16,10 +16,9 @@ upgrade :: Annex Bool upgrade = do showAction "v0 to v1"- g <- gitRepo -- do the reorganisation of the key files- let olddir = gitAnnexDir g+ olddir <- fromRepo gitAnnexDir keys <- getKeysPresent0 olddir forM_ keys $ \k -> moveAnnex k $ olddir </> keyFile0 k
Upgrade/V1.hs view
@@ -14,13 +14,14 @@ import Common.Annex import Types.Key import Annex.Content-import PresenceLog+import Logs.Presence import qualified Annex.Queue import qualified Git import qualified Git.LsFiles as LsFiles import Backend import Annex.Version import Utility.FileMode+import Utility.TempFile import qualified Upgrade.V2 -- v2 adds hashing of filenames of content and location log files.@@ -49,9 +50,9 @@ upgrade :: Annex Bool upgrade = do showAction "v1 to v2"-- g <- gitRepo- if Git.repoIsLocalBare g+ + bare <- fromRepo Git.repoIsLocalBare+ if bare then do moveContent setVersion@@ -82,8 +83,8 @@ updateSymlinks :: Annex () updateSymlinks = do showAction "updating symlinks"- g <- gitRepo- files <- liftIO $ LsFiles.inRepo g [Git.workTree g]+ top <- fromRepo Git.workTree+ files <- inRepo $ LsFiles.inRepo [top] forM_ files fixlink where fixlink f = do@@ -103,8 +104,7 @@ forM_ logkeys move where oldlocationlogs = do- g <- gitRepo- let dir = Upgrade.V2.gitStateDir g+ dir <- fromRepo Upgrade.V2.gitStateDir exists <- liftIO $ doesDirectoryExist dir if exists then do@@ -112,9 +112,8 @@ return $ mapMaybe oldlog2key contents else return [] move (l, k) = do- g <- gitRepo- let dest = logFile2 g k- let dir = Upgrade.V2.gitStateDir g+ dest <- fromRepo $ logFile2 k+ dir <- fromRepo Upgrade.V2.gitStateDir let f = dir </> l liftIO $ createDirectoryIfMissing True (parentDir dest) -- could just git mv, but this way deals with@@ -177,10 +176,10 @@ replace "&a" "&" $ replace "&s" "%" $ replace "%" "/" file writeLog1 :: FilePath -> [LogLine] -> IO ()-writeLog1 file ls = viaTmp writeFile file (unlines $ map show ls)+writeLog1 file ls = viaTmp writeFile file (showLog ls) readLog1 :: FilePath -> IO [LogLine]-readLog1 file = catch (parseLog <$> readFileStrict file) (const $ return [])+readLog1 file = catchDefaultIO (parseLog <$> readFileStrict file) [] lookupFile1 :: FilePath -> Annex (Maybe (Key, Backend Annex)) lookupFile1 file = do@@ -205,9 +204,7 @@ " (unknown backend " ++ bname ++ ")" getKeyFilesPresent1 :: Annex [FilePath]-getKeyFilesPresent1 = do- g <- gitRepo- getKeyFilesPresent1' $ gitAnnexObjectDir g+getKeyFilesPresent1 = getKeyFilesPresent1' =<< fromRepo gitAnnexObjectDir getKeyFilesPresent1' :: FilePath -> Annex [FilePath] getKeyFilesPresent1' dir = do exists <- liftIO $ doesDirectoryExist dir@@ -227,11 +224,11 @@ logFile1 :: Git.Repo -> Key -> String logFile1 repo key = Upgrade.V2.gitStateDir repo ++ keyFile1 key ++ ".log" -logFile2 :: Git.Repo -> Key -> String+logFile2 :: Key -> Git.Repo -> String logFile2 = logFile' hashDirLower -logFile' :: (Key -> FilePath) -> Git.Repo -> Key -> String-logFile' hasher repo key =+logFile' :: (Key -> FilePath) -> Key -> Git.Repo -> String+logFile' hasher key repo = gitStateDir repo ++ hasher key ++ keyFile key ++ ".log" stateDir :: FilePath
Upgrade/V2.hs view
@@ -10,8 +10,9 @@ import Common.Annex import qualified Git import qualified Annex.Branch-import LocationLog+import Logs.Location import Annex.Content+import Utility.TempFile olddir :: Git.Repo -> FilePath olddir g@@ -36,48 +37,48 @@ upgrade :: Annex Bool upgrade = do showAction "v2 to v3"- g <- gitRepo- let bare = Git.repoIsLocalBare g+ bare <- fromRepo Git.repoIsLocalBare+ old <- fromRepo olddir Annex.Branch.create showProgress - e <- liftIO $ doesDirectoryExist (olddir g)+ e <- liftIO $ doesDirectoryExist old when e $ do- mapM_ (\(k, f) -> inject f $ logFile k) =<< locationLogs g- mapM_ (\f -> inject f f) =<< logFiles (olddir g)+ mapM_ (\(k, f) -> inject f $ logFile k) =<< locationLogs+ mapM_ (\f -> inject f f) =<< logFiles old saveState showProgress - when e $ liftIO $ do- Git.run g "rm" [Param "-r", Param "-f", Param "-q", File (olddir g)]- unless bare $ gitAttributesUnWrite g+ when e $ do+ inRepo $ Git.run "rm" [Param "-r", Param "-f", Param "-q", File old]+ unless bare $ inRepo $ gitAttributesUnWrite showProgress unless bare push return True -locationLogs :: Git.Repo -> Annex [(Key, FilePath)]-locationLogs repo = liftIO $ do- levela <- dirContents dir- levelb <- mapM tryDirContents levela- files <- mapM tryDirContents (concat levelb)- return $ mapMaybe islogfile (concat files)+locationLogs :: Annex [(Key, FilePath)]+locationLogs = do+ dir <- fromRepo gitStateDir+ liftIO $ do+ levela <- dirContents dir+ levelb <- mapM tryDirContents levela+ files <- mapM tryDirContents (concat levelb)+ return $ mapMaybe islogfile (concat files) where- tryDirContents d = catch (dirContents d) (return . const [])- dir = gitStateDir repo+ tryDirContents d = catchDefaultIO (dirContents d) [] islogfile f = maybe Nothing (\k -> Just (k, f)) $ logFileKey $ takeFileName f inject :: FilePath -> FilePath -> Annex () inject source dest = do- g <- gitRepo- new <- liftIO (readFile $ olddir g </> source)+ old <- fromRepo olddir+ new <- liftIO (readFile $ old </> source) Annex.Branch.change dest $ \prev -> unlines $ nub $ lines prev ++ lines new- showProgress logFiles :: FilePath -> Annex [FilePath] logFiles dir = return . filter (".log" `isSuffixOf`)@@ -85,7 +86,7 @@ push :: Annex () push = do- origin_master <- Annex.Branch.refExists "origin/master"+ origin_master <- Annex.Branch.refExists $ Git.Ref "origin/master" origin_gitannex <- Annex.Branch.hasOrigin case (origin_master, origin_gitannex) of (_, True) -> do@@ -102,8 +103,7 @@ Annex.Branch.update -- just in case showAction "pushing new git-annex branch to origin" showOutput- g <- gitRepo- liftIO $ Git.run g "push" [Param "origin", Param Annex.Branch.name]+ inRepo $ Git.run "push" [Param "origin", Param $ show Annex.Branch.name] _ -> do -- no origin exists, so just let the user -- know about the new branch@@ -126,7 +126,7 @@ c <- readFileStrict attributes liftIO $ viaTmp writeFile attributes $ unlines $ filter (`notElem` attrLines) $ lines c- Git.run repo "add" [File attributes]+ Git.run "add" [File attributes] repo stateDir :: FilePath stateDir = addTrailingPathSeparator ".git-annex"
− Utility.hs
@@ -1,106 +0,0 @@-{- general purpose utility functions- -- - Copyright 2010-2011 Joey Hess <joey@kitenet.net>- -- - Licensed under the GNU GPL version 3 or higher.- -}--module Utility (- hGetContentsStrict,- readFileStrict,- readMaybe,- viaTmp,- withTempFile,- dirContents,- myHomeDir,- catchBool,- inPath,- firstM,- anyM-) where--import Control.Applicative-import IO (bracket)-import System.IO-import System.Posix.Process hiding (executeFile)-import System.Posix.User-import System.FilePath-import System.Directory-import Utility.Path-import Data.Maybe-import Control.Monad (liftM)--{- A version of hgetContents that is not lazy. Ensures file is - - all read before it gets closed. -}-hGetContentsStrict :: Handle -> IO String-hGetContentsStrict h = hGetContents h >>= \s -> length s `seq` return s--{- A version of readFile that is not lazy. -}-readFileStrict :: FilePath -> IO String-readFileStrict f = readFile f >>= \s -> length s `seq` return s--{- Attempts to read a value from a String. -}-readMaybe :: (Read a) => String -> Maybe a-readMaybe s = case reads s of- ((x,_):_) -> Just x- _ -> Nothing--{- Runs an action like writeFile, writing to a tmp file first and- - then moving it into place. -}-viaTmp :: (FilePath -> String -> IO ()) -> FilePath -> String -> IO ()-viaTmp a file content = do- pid <- getProcessID- let tmpfile = file ++ ".tmp" ++ show pid- createDirectoryIfMissing True (parentDir file)- a tmpfile content- renameFile tmpfile file--{- Runs an action with a temp file, then removes the file. -}-withTempFile :: String -> (FilePath -> Handle -> IO a) -> IO a-withTempFile template a = bracket create remove use- where- create = do- tmpdir <- catch getTemporaryDirectory (const $ return ".")- openTempFile tmpdir template- remove (name, handle) = do- hClose handle- catchBool (removeFile name >> return True)- use (name, handle) = a name handle--{- Lists the contents of a directory.- - Unlike getDirectoryContents, paths are not relative to the directory. -}-dirContents :: FilePath -> IO [FilePath]-dirContents d = map (d </>) . filter notcruft <$> getDirectoryContents d- where- notcruft "." = False- notcruft ".." = False- notcruft _ = True--{- Current user's home directory. -}-myHomeDir :: IO FilePath-myHomeDir = homeDirectory <$> (getUserEntryForID =<< getEffectiveUserID)--{- Catches IO errors and returns a Bool -}-catchBool :: IO Bool -> IO Bool-catchBool = flip catch (const $ return False)--{- 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--{- 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--{- Checks if a command is available in PATH. -}-inPath :: String -> IO Bool-inPath command = getSearchPath >>= anyM indir- where- indir d = doesFileExist $ d </> command
Utility/DataUnits.hs view
@@ -84,7 +84,7 @@ , Unit (p 6) "EiB" "exbibyte" , Unit (p 5) "PiB" "pebibyte" , Unit (p 4) "TiB" "tebibyte"- , Unit (p 3) "GiB" "gigabyte"+ , Unit (p 3) "GiB" "gibibyte" , Unit (p 2) "MiB" "mebibyte" , Unit (p 1) "KiB" "kibibyte" , Unit (p 0) "B" "byte"
+ Utility/Misc.hs view
@@ -0,0 +1,47 @@+{- misc utility functions+ -+ - Copyright 2010-2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Utility.Misc where++import System.IO+import System.IO.Error (try)+import Control.Monad+import Control.Applicative++{- A version of hgetContents that is not lazy. Ensures file is + - all read before it gets closed. -}+hGetContentsStrict :: Handle -> IO String+hGetContentsStrict = hGetContents >=> \s -> length s `seq` return s++{- A version of readFile that is not lazy. -}+readFileStrict :: FilePath -> IO String+readFileStrict = readFile >=> \s -> length s `seq` return s++{- Attempts to read a value from a String. -}+readMaybe :: (Read a) => String -> Maybe a+readMaybe s = case reads s of+ ((x,_):_) -> Just x+ _ -> Nothing++{- Catches IO errors and returns a Bool -}+catchBoolIO :: IO Bool -> IO Bool+catchBoolIO a = catchDefaultIO a False++{- Catches IO errors and returns a Maybe -}+catchMaybeIO :: IO a -> IO (Maybe a)+catchMaybeIO a = catchDefaultIO (Just <$> a) Nothing++{- Catches IO errors and returns a default value. -}+catchDefaultIO :: IO a -> a -> IO a+catchDefaultIO a def = catch a (const $ return def)++{- Catches IO errors and returns the error message. -}+catchMsgIO :: IO a -> IO (Either String a)+catchMsgIO a = dispatch <$> try a+ where+ dispatch (Left e) = Left $ show e+ dispatch (Right v) = Right v
+ Utility/Monad.hs view
@@ -0,0 +1,26 @@+{- monadic stuff+ -+ - Copyright 2010-2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Utility.Monad where++import Data.Maybe+import Control.Monad (liftM)++{- 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++{- 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
Utility/Path.hs view
@@ -14,7 +14,10 @@ import Data.List import Data.Maybe import Control.Applicative+import System.Posix.User +import Utility.Monad+ {- Returns the parent directory of a path. Parent of / is "" -} parentDir :: FilePath -> FilePath parentDir dir@@ -112,3 +115,22 @@ -} runPreserveOrder :: ([FilePath] -> IO [FilePath]) -> [FilePath] -> IO [FilePath] runPreserveOrder a files = preserveOrder files <$> a files++{- Lists the contents of a directory.+ - Unlike getDirectoryContents, paths are not relative to the directory. -}+dirContents :: FilePath -> IO [FilePath]+dirContents d = map (d </>) . filter notcruft <$> getDirectoryContents d+ where+ notcruft "." = False+ notcruft ".." = False+ notcruft _ = True++{- Current user's home directory. -}+myHomeDir :: IO FilePath+myHomeDir = homeDirectory <$> (getUserEntryForID =<< getEffectiveUserID)++{- Checks if a command is available in PATH. -}+inPath :: String -> IO Bool+inPath command = getSearchPath >>= anyM indir+ where+ indir d = doesFileExist $ d </> command
Utility/RsyncFile.hs view
@@ -1,4 +1,4 @@-{- git-annex file copying with rsync+{- file copying with rsync - - Copyright 2010 Joey Hess <joey@kitenet.net> -@@ -8,6 +8,7 @@ module Utility.RsyncFile where import Data.String.Utils+import Data.List import Utility.SafeCommand @@ -35,6 +36,8 @@ [ Param "--server" -- preserve permissions , Param "-p"+ -- preserve timestamps+ , Param "-t" -- allow resuming of transfers of big files , Param "--inplace" -- other options rsync normally uses in server mode@@ -46,3 +49,18 @@ rsyncExec :: [CommandParam] -> IO () rsyncExec params = executeFile "rsync" True (toCommand params) Nothing++{- Checks if an rsync url involves the remote shell (ssh or rsh).+ - Use of such urls with rsync or rsyncExec requires additional shell+ - escaping. -}+rsyncUrlIsShell :: String -> Bool+rsyncUrlIsShell s+ | "rsync://" `isPrefixOf` s = False+ | otherwise = go s+ where+ -- host:dir is rsync protocol, while host:dir is ssh/rsh+ go [] = False+ go (c:cs)+ | c == '/' = False -- got to directory with no colon+ | c == ':' = not $ ":" `isPrefixOf` cs+ | otherwise = go cs
Utility/SafeCommand.hs view
@@ -13,6 +13,7 @@ import System.Posix.Signals import Data.String.Utils import System.Log.Logger+import Control.Applicative {- A type for parameters passed to a shell command. A command can - be passed either some Params (multiple parameters can be included,@@ -36,14 +37,23 @@ {- 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+boolSystemEnv command params env = dispatch <$> safeSystemEnv command params env+ where+ dispatch ExitSuccess = True+ dispatch _ = False++{- Runs a system command, returning the exit status. -}+safeSystem :: FilePath -> [CommandParam] -> IO ExitCode+safeSystem command params = safeSystemEnv command params Nothing++{- SIGINT(ctrl-c) is allowed to propigate and will terminate the program. -}+safeSystemEnv :: FilePath -> [CommandParam] -> Maybe [(String, String)] -> IO ExitCode+safeSystemEnv 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.@@ -55,8 +65,8 @@ mps <- getProcessStatus True False childpid restoresignals oldint oldset case mps of- Just (Exited ExitSuccess) -> return True- _ -> return False+ Just (Exited code) -> return code+ _ -> error $ "unknown error running " ++ command where restoresignals oldint oldset = do _ <- installHandler sigINT oldint Nothing
− Utility/Ssh.hs
@@ -1,67 +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 Utility.Ssh where--import Control.Monad.State (liftIO)--import qualified Git-import Utility.SafeCommand-import Types-import Config-import UUID--{- 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- uuid <- getRepoUUID r- sshparams <- sshToRepo r [Param $ sshcmd uuid ]- return $ Just ("ssh", sshparams)- | otherwise = return Nothing- where- dir = Git.workTree r- shellcmd = "git-annex-shell"- shellopts = Param command : File dir : params- sshcmd uuid = unwords $- shellcmd : (map shellEscape $ toCommand shellopts) ++- uuidcheck uuid- uuidcheck uuid- | null uuid = []- | otherwise = ["--uuid", uuid]--{- 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/TempFile.hs view
@@ -0,0 +1,39 @@+{- temp file functions+ -+ - Copyright 2010-2011 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Utility.TempFile where++import IO (bracket)+import System.IO+import System.Posix.Process hiding (executeFile)+import System.Directory++import Utility.Misc+import Utility.Path++{- Runs an action like writeFile, writing to a temp file first and+ - then moving it into place. The temp file is stored in the same+ - directory as the final file to avoid cross-device renames. -}+viaTmp :: (FilePath -> String -> IO ()) -> FilePath -> String -> IO ()+viaTmp a file content = do+ pid <- getProcessID+ let tmpfile = file ++ ".tmp" ++ show pid+ createDirectoryIfMissing True (parentDir file)+ a tmpfile content+ renameFile tmpfile file++{- Runs an action with a temp file, then removes the file. -}+withTempFile :: String -> (FilePath -> Handle -> IO a) -> IO a+withTempFile template a = bracket create remove use+ where+ create = do+ tmpdir <- catchDefaultIO getTemporaryDirectory "."+ openTempFile tmpdir template+ remove (name, handle) = do+ hClose handle+ catchBoolIO (removeFile name >> return True)+ use (name, handle) = a name handle
Utility/Url.hs view
@@ -12,15 +12,12 @@ ) 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+import Utility.Path type URLString = String @@ -38,13 +35,12 @@ {- 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 :: URLString -> FilePath -> IO Bool download url file = do- showOutput -- make way for program's progress bar- e <- liftIO $ inPath "wget"+ e <- inPath "wget" if e then- liftIO $ boolSystem "wget"+ boolSystem "wget" [Params "-c -O", File file, File url] else -- Uses the -# progress display, because the normal@@ -52,7 +48,7 @@ -- the remainder to download as the whole file, -- and not indicating how much percent was -- downloaded before the resume.- liftIO $ boolSystem "curl" + boolSystem "curl" [Params "-L -C - -# -o", File file, File url] {- Downloads a small file. -}
configure.hs view
@@ -2,12 +2,16 @@ import System.Directory import Data.List+import Data.String.Utils+import System.Cmd.Utils import Build.TestConfig tests :: [TestCase] tests = [ TestCase "version" getVersion+ , TestCase "git" $ requireCmd "git" "git --version >/dev/null"+ , TestCase "git version" checkGitVersion , testCp "cp_a" "-a" , testCp "cp_p" "-p" , testCp "cp_reflink_auto" "--reflink=auto"@@ -52,6 +56,26 @@ return $ middle (words verline !! 1) where middle = drop 1 . init++{- Checks for a new enough version of git. -}+checkGitVersion :: Test+checkGitVersion = do+ (_, s) <- pipeFrom "git" ["--version"]+ let version = last $ words $ head $ lines s+ if dotted version < dotted need+ then error $ "git version " ++ version ++ " too old; need " ++ need+ else return $ Config "gitversion" (StringConfig version)+ where+ -- for git-check-attr behavior change+ need = "1.7.7"+ dotted = sum . mult 1 . reverse . extend 10 . map readi . split "." + extend n l = l ++ replicate (n - length l) 0+ mult _ [] = []+ mult n (x:xs) = (n*x) : (mult (n*100) xs)+ readi :: String -> Integer+ readi s = case reads s of+ ((x,_):_) -> x+ _ -> 0 {- Set up cabal file with version. -} cabalSetup :: IO ()
debian/NEWS view
@@ -4,7 +4,7 @@ -- Joey Hess <joeyh@debian.org> Fri, 01 Jul 2011 15:40:51 -0400 -git-annex (3.20110624) exerimental; urgency=low+git-annex (3.20110624) experimental; urgency=low There has been another change to the git-annex data store. Use `git annex upgrade` to migrate your repositories to the new
debian/changelog view
@@ -1,3 +1,93 @@+git-annex (3.20111122) unstable; urgency=low++ * merge: Improve commit messages to mention what was merged.+ * Avoid doing auto-merging in commands that don't need fully current+ information from the git-annex branch. In particular, git annex add+ no longer needs to auto-merge.+ * init: When run in an already initalized repository, and without+ a description specified, don't delete the old description. + * Optimised union merging; now only runs git cat-file once, and runs+ in constant space.+ * status: Now displays trusted, untrusted, and semitrusted repositories+ separately.+ * status: Include all special remotes in the list of repositories.+ * status: Fix --json mode.+ * status: --fast is back+ * Fix support for insteadOf url remapping. Closes: #644278+ * When not run in a git repository, git-annex can still display a usage+ message, and "git annex version" even works.+ * migrate: Don't fall over a stale temp file.+ * Avoid excessive escaping for rsync special remotes that are not accessed+ over ssh.+ * find: Support --print0++ -- Joey Hess <joeyh@debian.org> Tue, 22 Nov 2011 14:31:45 -0400++git-annex (3.20111111) unstable; urgency=low++ * Handle a case where an annexed file is moved into a gitignored directory,+ by having fix --force add its change.+ * Avoid cyclic drop problems.+ * Optimized copy --from and get --from to avoid checking the location log+ for files that are already present.+ * Automatically fix up badly formatted uuid.log entries produced by+ 3.20111105, whenever the uuid.log is changed (ie, by init or describe).+ * map: Support remotes with /~/ and /~user/++ -- Joey Hess <joeyh@debian.org> Fri, 11 Nov 2011 13:44:18 -0400++git-annex (3.20111107) unstable; urgency=low++ * merge: Use fast-forward merges when possible.+ Thanks Valentin Haenel for a test case showing how non-fast-forward+ merges could result in an ongoing pull/merge/push cycle.+ * Don't try to read config from repos with annex-ignore set.+ * Bugfix: In the past two releases, git-annex init has written the uuid.log+ in the wrong format, with the UUID and description flipped.++ -- Joey Hess <joeyh@debian.org> Mon, 07 Nov 2011 12:47:44 -0400++git-annex (3.20111105) unstable; urgency=low++ * The default backend used when adding files to the annex is changed+ from WORM to SHA256.+ To get old behavior, add a .gitattributes containing: * annex.backend=WORM+ * Sped up some operations on remotes that are on the same host.+ * copy --to: Fixed leak when copying many files to a remote on the same+ host.+ * uninit: Add guard against being run with the git-annex branch checked out.+ * Fail if --from or --to is passed to commands that do not support them.+ * drop --from is now supported to remove file content from a remote.+ * status: Now always shows the current repository, even when it does not+ appear in uuid.log.+ * fsck: Now works in bare repositories. Checks location log information,+ and file contents. Does not check that numcopies is satisfied, as+ .gitattributes information about numcopies is not available in a bare+ repository.+ * unused, dropunused: Now work in bare repositories.+ * Removed the setkey command, and added a reinject command with a more+ useful interface.+ * The fromkey command now takes the key as its first parameter. The --key+ option is no longer used.+ * Built without any filename containing .git being excluded. Closes: #647215+ * Record uuid when auto-initializing a remote so it shows in status.+ * Bugfix: Fixed git-annex init crash in a bare repository when there was+ already an existing git-annex branch.+ * Pass -t to rsync to preserve timestamps.++ -- Joey Hess <joeyh@debian.org> Sat, 05 Nov 2011 15:47:52 -0400++git-annex (3.20111025) unstable; urgency=low++ * A remote can have a annexUrl configured, that is used by git-annex+ instead of its usual url. (Similar to pushUrl.)+ * migrate: Copy url logs for keys when migrating.+ * git-annex-shell: GIT_ANNEX_SHELL_READONLY and GIT_ANNEX_SHELL_LIMITED+ environment variables can be set to limit what commands can be run.+ This is used by gitolite's new git-annex support!++ -- Joey Hess <joeyh@debian.org> Tue, 25 Oct 2011 13:03:08 -0700+ git-annex (3.20111011) unstable; urgency=low * This version of git-annex only works with git 1.7.7 and newer.
doc/backends.mdwn view
@@ -5,17 +5,19 @@ Multiple pluggable key-value backends are supported, and a single repository can use different ones for different files. -* `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+* `SHA256` -- The default backend for new files. 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- researching checksum collisions and wants to annex their colliding data. ;)-* `SHA1E`, `SHA512E`, etc -- Variants that preserve filename extension as+ can make it slower for large files. +* `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 the least expensive backend, recommended for really large+ files or slow systems.+* `SHA512` -- Best currently available hash, for the very paranoid.+* `SHA1` -- Smaller hash than `SHA256` for those who want a checksum+ but are not concerned about security.+* `SHA384`, `SHA224` -- Hashes for people who like unusual sizes.+* `SHA256E`, `SHA1E`, etc -- Variants that preserve filename extension as part of the key. Useful for archival tasks where the filename extension contains metadata that should be preserved. @@ -27,9 +29,11 @@ files, the `.gitattributes` file can be used. The `annex.backend` 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 or copied over time, you could set in-`.gitattributes`:+For example, to use the SHA256 backend for sound files, which tend to be+smallish and might be modified or copied over time,+while using the WORM backend for everything else, you could set+in `.gitattributes`: - *.mp3 annex.backend=SHA1- *.ogg annex.backend=SHA1+ * annex.backend=WORM+ *.mp3 annex.backend=SHA256+ *.ogg annex.backend=SHA256
doc/bare_repositories.mdwn view
@@ -7,16 +7,38 @@ [[git-annex-shell]] installed. If "origin" is on GitWeb, you still can't use git-annex to store stuff there. -Known to work ok:+It took a while, but bare repositories are now supported exactly as well+as non-bare repositories. Except for these caveats: -* `git annex move --to` and `--from`, when pointed at a bare repository.-* `git annex copy` ditto.-* `git annex drop` can check that a bare repository has a copy of data- that is being dropped.-* `git annex get` can transfer data from a bare repository.-* Most other stuff (ie, init, describe, trust, etc.)+* `git annex fsck` works in a bare repository, but does not display+ warnings about insufficient+ [[copies]]. To get those warnings, just run it in one of the non-bare+ checkouts.+* `git annex unused` in a bare repository only knows about keys used in+ branches that have been pushed to the bare repository. So use it with care..+* Commands that need a work tree, like `git annex add` won't work in a bare+ repository, of course. -There are a few caveats to keep in mind when using bare repositories:+*** -* A few subcommands, like `unused` cannot be run in a bare repository.- Those subcommands will refuse to do anything.+Here is a quick example of how to set this up, using `origin` as the remote name, and assuming `~/annex` contains an annex:++On the server:++ mkdir bare-annex+ git init --bare+ git annex init origin++Now configure the remote and do the initial push:++ cd ~/annex+ git remote add origin example.com:bare-annex+ git push origin master git-annex++Now `git annex status` should show the configured bare remote. If it does not, you may have to pull from the remote first (older versions of `git-annex`)++If you wish to configure git such that you can push/pull without arguments, set the upstream branch:++ git branch master --set-upstream origin/master++
doc/bugs/Cabal_dependency_monadIO_missing.mdwn view
@@ -12,3 +12,6 @@ > dependency in the cabal file. Your system might be old/new/or broken, > perhaps it's time to provide some details about the version of haskell > and of `monad-control` you have installed? --[[Joey]] ++>> Closing as apparently user error or a broken system.+>> If you see this problem please do say. [[done]] --[[Joey]]
+ doc/bugs/Error_when_moving_annexed_file_to_a_.gitignored_location.mdwn view
@@ -0,0 +1,21 @@+I just noticed that if you move a git-annex symlink to a location ignored by git, it simply works. Upon committing that change, however, part of git-annex's `fix` function apparently tries to `git-add` the symlink. This fails because the new, ignored location requires a `git-add --force`.++Considering that git proper doesn't fail or warn, I think git-annex shouldn't either.++This is the error message:++ $ git mv annexed-file ignored-dir/+ $ git commit+ fix ignored-dir/annexed-file ok+ (Recording state in git...)+ The following paths are ignored by one of your .gitignore files:+ ignored-dir+ Use -f if you really want to add them.+ fatal: no files added+ Command xargs ["-0","git","--git-dir=/home/[...]/repo/.git","--work-tree=/home/[...]/repo","add","--"] failed; exit code 123++ git-annex: user error (Command xargs ["-0","git","--git-dir=/home/[...]/repo/.git","--work-tree=/home/[...]/repo","add","--"] failed; exit code 123)+ failed+ git-annex: 1 failed++> Weird edge case.. ok, fixed. [[done]] --[[Joey]]
+ doc/bugs/Prevent_accidental_merges/comment_1_4c46a193915eab8f308a04175cb2e40a._comment view
@@ -0,0 +1,8 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawl9sYlePmv1xK-VvjBdN-5doOa_Xw-jH4U"+ nickname="Richard"+ subject="comment 1"+ date="2011-10-23T15:00:48Z"+ content="""+Having run into the same issue again, I still think git-annex should ensure no merges take place. The clutter introduced by a .gitnomerge is neglible, imo.+"""]]
+ doc/bugs/Trouble_initializing_git_annex_on_NFS.mdwn view
@@ -0,0 +1,16 @@+The following occurs in a directory that is shared on an NFS server:++ /media/mybook/movies $ git init+ Initialized empty Git repository in /media/mybook/movies/.git/+ /media/mybook/movies $ git annex init mybook-movies+ init mybook-movies + git-annex: waitToSetLock: resource exhausted (No locks available)+ failed+ git-annex: init: 1 failed+ /media/mybook/movies $++This happens reliably. Is there any way around it? I have shell+access on the NFS server, but it is a NAS, so I don't think it is+capable of running git-annex.++[[done]]
doc/bugs/copy_fast_confusing_with_broken_locationlog.mdwn view
@@ -1,4 +1,4 @@-Conversation moved from [[walkthrough/recover_data_from_lost+found]]+Conversation moved from [[tips/recover_data_from_lost+found]] to a proper bug. --[[Joey]] (Unfortunatly that scrambled the comment creation times and thus order.)
+ doc/bugs/cyclic_drop.mdwn view
@@ -0,0 +1,104 @@+drop's verification that a remote still has content can fail+if the remote is also dropping the content at the same time. Each+repository checks that the other still has the content, and then both+drop it. Could also happen with larger cycles of repositories.++> Confirmed fixed now. All cases tested. [[done]]++---++Fixing this requires locking. (Well, there are other ways, like moving the+content to a holding area when checking if it's safe to drop, but they+seem complicated, and would be hard to implement for move --from.)++Add per-content lock files. An exclusive lock is held on content when+it's in the process of being dropped, or moved. The lock is taken+nonblocking; if it cannot be obtained, something else is acting on the+content and git-annex should refuse to do anything.++Then when checking inannex, try to take a shared lock. Note that to avoid+deadlock, this must be a nonblocking lock. (Actually, with fcntl locking,+can just check if there is a lock, without needing to take one.) +If it fails, the status of the content is unknown, so inannex should fail.+Note that this failure needs to be distinguishable from "not in annex".++> Thinking about these lock files, this would be a lot more files, +> and would possibly break some assumptions that everything in+> `.git/annex/objects` is a key's content. (Or would need lots more+> directories to put the lock files elsewhere.) There would be more+> overhead to manage these and have them on disk.+> +> What if it just locked the actual content file? The obvious limitation+> is only content that was already inannex could be locked, but that+> happens to be exactly what's needed here; if content is not present,+> it's not going to get dropped or moved.+>+> Of course, if some consumer of a file locked it, then it could prevent it+> from being dropped or moved. This could be considered a bug, or a feature. :)+> +> However, this would mean that such a hypothetical consumer could also+> make inannex checks fail.+>+> The other downside is that, for fcntl exclusive locking, the file has to+> be opened for write. Normally the modes of content files are locked down+> to prevent modifcation. Dealt with, but oh so nasty. Almost makes flock+> locking seem worth using.++---++drop --from could also cycle. Locking should fix.++> Confirmed fixed now. ++---++move --to can also be included in the cycle, since it can drop data. ++Consider move to a remote that already has the content and +is at the same time doing a drop (or a move). The remote+verifies the content is present on the movee, and removes its copy.+The movee removes its copy.++So move --to needs to take the content lock on start. Then the inannex+will fail.++This is why it's important for inannex to fail in a way that is+distinguishable from "not in annex". Otherwise, move --to+would see the cycle as the remote not having content, and try to+redundantly send it, drop it locally, and still race.++> Confirmed fixed now.++-- ++move --from is similar. Consider a case where both the local and the remote+are doing a move --from. Both have the content, and confirm the other does,+via inannex checks. Then both run git-annex-shell dropkey, removing both+copies.++So move --from needs to take the content lock on start, so the inannex will+fail. NB: If the content is not locally present, don't take the lock.++> Confirmed fixed now.++---++Another cycle might be running move --to and move --from on the same file,+locally. The exclusivity of the content lock solves this, as only one can+run at a time.++Would it work with a shared lock? The --to would run git-annex-shell+inannex. The --from would also be running, and would run git-annex-shell+dropkey. So inannex and dropkey would end up running on the remote+at the same time. Dropkey takes the content lock, and inannex checks it,+but what if inannex runs first? Then it returns true, and then the content+is removed, so both the --to and --from see success and the --to proceeds+to remove the local content that the --from already caused to be removed+from the remote. So, no, the nasty exclusive lock is needed.++> Confirmed fixed now.++---++Another cycle might involve move --from and drop, both run on the same+file, locally. Again, the exclusive lock solves this.
+ doc/bugs/directory_remote_and_case_sensitivity_on_FAT.mdwn view
@@ -0,0 +1,28 @@+I was copying files to a directory remote with `git annex copy`. Out of 114 files, 9 of them failed with no message, just:++ copy data/foo.dat (to usbdrive...) failed+ copy data/bar.dat (to usbdrive...) failed++According to strace:++ 31338 mkdir("/media/annex/Zp/9v/SHA256-s1362999320--d650297c8cf8c2dc0575110a52d0c5cc0ff266f294a0599f85796a6b44b23492", 0777) = -1 ENOENT (No such file or directory)+ 31338 mkdir("/media/annex/Zp/9v", 0777) = -1 ENOENT (No such file or directory)+ 31338 mkdir("/media/annex/Zp", 0777) = -1 EEXIST (File exists)+ 31338 stat("/media/annex/Zp", 0x7f8449f170d0) = -1 ENOENT (No such file or directory)++The filesystem is FAT32 and has weird case semantics. This was mounted by udisks with its default options:++ /dev/sdb1 on /media/annex type vfat (rw,nosuid,nodev,uhelper=udisks,uid=1000,gid=1000,shortname=mixed,dmask=0077,utf8=1,showexec)++I wonder if the directory remote should use hashDirLower instead of hashDirMixed?++> git-annex intentionally uses the same layout for directory and rsync+> special remotes as it does for the .git/annex directory. As far+> as I know it works ok on case-insensative filesystems.+> +> Based on your strace, if you `ls /media/annex/Zp`, you will see+> "No such file or directory", but if you `mkdir /media/annex/Zp` it will+> fail with "File exists". Doesn't make much sense to me.+> +> I cannot reproduce this problem using a vfat filesystem +> mounted using the same options you show (linux 3.0.0). --[[Joey]]
doc/bugs/dropping_files_with_a_URL_backend_fails.mdwn view
@@ -1,4 +1,4 @@-I was trying out the example with the walkthrough [[walkthrough/using_the_URL_backend]]. I tried dropping files that I had after doing an "git annex get ." which have the URL backend associated with the files it fails with+I was trying out the example with the walkthrough using_the_URL_backend. I tried dropping files that I had after doing an "git annex get ." which have the URL backend associated with the files it fails with <pre>
+ doc/bugs/extraneous_shell_escaping_for_rsync_remotes.mdwn view
@@ -0,0 +1,15 @@+When using `git annex get foo` where foo is available in a rsync remote with encryption I got an error saying that rsync cannot+find the required file but extra ' are here.++I attached a patch for this.++> But you didn't, sadly. :(+>+> I don't seem to see the problem, set up a rsync over ssh with encryption+> and sent over a file "foo", and then got it back from rsync, without+> trouble.+> +> Ah, you're not using rsync over ssh, but just to a local directory,+> right? --[[Joey]]++>> [[fixed|done]] --[[Joey]]
+ doc/bugs/fails_to_handle_lot_of_files.mdwn view
@@ -0,0 +1,445 @@+ git-annex version: 3.20111011+ local repository version: 3+ default repository version: 3+ supported repository versions: 3+ upgrade supported from repository versions: 0 1 2++I just created a new remote on a USB drive and wanted to copy my files over. git-annex wasn't too happy about that ;)+I included a few OK transfers as there was an error before git-annex ran into a wall. As I could easily access that temp file after it aborted, I suspect something went wrong internally before git-annex started to throw those errors.++Please note the "_n TIMES_" comments. It's how often I got the same error message...++++ git annex copy . --to USB --fast+ + copy redacted.JPG (to USB...) + redacted+ 4035668 100% 77.91MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4036374 bytes received 31 bytes 8072810.00 bytes/sec+ total size is 4035668 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 18002094 100% 74.19MB/s 0:00:00 (xfer#1, to-check=0/1)+ WARNING: redacted failed verification -- update retained (will try again).+ redacted+ 18002094 100% 19.60MB/s 0:00:00 (xfer#2, to-check=0/1)+ rsync: open "copy_target/.git/annex/tmp/redacted_E13" failed: Permission denied (13)+ + sent 36008841 bytes received 52 bytes 24005928.67 bytes/sec+ total size is 18002094 speedup is 0.50+ rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1070) [sender=3.0.8]+ + rsync failed -- run git annex again to resume file transfer+ failed+ copy redacted.JPG (to USB...) + redacted+ 3687111 100% 39.16MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 3687773 bytes received 31 bytes 2458536.00 bytes/sec+ total size is 3687111 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 17877177 100% 79.15MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 17879573 bytes received 31 bytes 11919736.00 bytes/sec+ total size is 17877177 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 3694921 100% 40.14MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 3695583 bytes received 31 bytes 2463742.67 bytes/sec+ total size is 3694921 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 17875448 100% 71.20MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 17877844 bytes received 31 bytes 11918583.33 bytes/sec+ total size is 17875448 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 3833377 100% 62.49MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 3834055 bytes received 31 bytes 2556057.33 bytes/sec+ total size is 3833377 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 17938200 100% 65.43MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 17940604 bytes received 31 bytes 11960423.33 bytes/sec+ total size is 17938200 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4512557 100% 83.77MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4513319 bytes received 31 bytes 3008900.00 bytes/sec+ total size is 4512557 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 18001641 100% 76.16MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 18004053 bytes received 31 bytes 12002722.67 bytes/sec+ total size is 18001641 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4394272 100% 50.11MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4395022 bytes received 31 bytes 8790106.00 bytes/sec+ total size is 4394272 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 18095781 100% 73.30MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 18098205 bytes received 31 bytes 12065490.67 bytes/sec+ total size is 18095781 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4683795 100% 65.23MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4684577 bytes received 31 bytes 9369216.00 bytes/sec+ total size is 4683795 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 18172801 100% 74.25MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 18175233 bytes received 31 bytes 36350528.00 bytes/sec+ total size is 18172801 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4486231 100% 77.22MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4486989 bytes received 31 bytes 8974040.00 bytes/sec+ total size is 4486231 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 17860427 100% 68.56MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 17862823 bytes received 31 bytes 35725708.00 bytes/sec+ total size is 17860427 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4499768 100% 36.41MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4500530 bytes received 31 bytes 9001122.00 bytes/sec+ total size is 4499768 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 17840132 100% 74.48MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 17842524 bytes received 31 bytes 11895036.67 bytes/sec+ total size is 17840132 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4358032 100% 75.00MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4358774 bytes received 31 bytes 8717610.00 bytes/sec+ total size is 4358032 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 18084753 100% 61.48MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 18087173 bytes received 31 bytes 12058136.00 bytes/sec+ total size is 18084753 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4270213 100% 68.49MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4270947 bytes received 31 bytes 2847318.67 bytes/sec+ total size is 4270213 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 17661246 100% 68.34MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 17663614 bytes received 31 bytes 11775763.33 bytes/sec+ total size is 17661246 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4538305 100% 63.19MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4539071 bytes received 31 bytes 9078204.00 bytes/sec+ total size is 4538305 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 18672466 100% 68.90MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 18674958 bytes received 31 bytes 12449992.67 bytes/sec+ total size is 18672466 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4453445 100% 73.96MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4454199 bytes received 31 bytes 8908460.00 bytes/sec+ total size is 4453445 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 18495494 100% 59.28MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 18497966 bytes received 31 bytes 12331998.00 bytes/sec+ total size is 18495494 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4255858 100% 70.66MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4256588 bytes received 31 bytes 1702647.60 bytes/sec+ total size is 4255858 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 18376531 100% 69.15MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 18378987 bytes received 31 bytes 36758036.00 bytes/sec+ total size is 18376531 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4013365 100% 48.67MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4014067 bytes received 31 bytes 8028196.00 bytes/sec+ total size is 4013365 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 17606341 100% 51.73MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 17608705 bytes received 31 bytes 11739157.33 bytes/sec+ total size is 17606341 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4179869 100% 74.62MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4180591 bytes received 31 bytes 8361244.00 bytes/sec+ total size is 4179869 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 18382569 100% 67.05MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 18385025 bytes received 31 bytes 12256704.00 bytes/sec+ total size is 18382569 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4318363 100% 44.91MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4319101 bytes received 31 bytes 8638264.00 bytes/sec+ total size is 4318363 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 17715958 100% 72.69MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 17718334 bytes received 31 bytes 11812243.33 bytes/sec+ total size is 17715958 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4241893 100% 65.81MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4242623 bytes received 31 bytes 8485308.00 bytes/sec+ total size is 4241893 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 17717287 100% 71.77MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 17719663 bytes received 31 bytes 11813129.33 bytes/sec+ total size is 17717287 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4488380 100% 49.99MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4489138 bytes received 31 bytes 2992779.33 bytes/sec+ total size is 4488380 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 17770208 100% 38.80MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 17772592 bytes received 31 bytes 11848415.33 bytes/sec+ total size is 17770208 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4603958 100% 76.48MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4604732 bytes received 31 bytes 9209526.00 bytes/sec+ total size is 4603958 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 18744380 100% 74.66MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 18746884 bytes received 31 bytes 12497943.33 bytes/sec+ total size is 18744380 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4592098 100% 79.06MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4592872 bytes received 31 bytes 3061935.33 bytes/sec+ total size is 4592098 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 18746205 100% 43.00MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 18748709 bytes received 31 bytes 12499160.00 bytes/sec+ total size is 18746205 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 7493353 100% 80.85MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 7494479 bytes received 31 bytes 14989020.00 bytes/sec+ total size is 7493353 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 19496768 100% 81.77MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 19499360 bytes received 31 bytes 12999594.00 bytes/sec+ total size is 19496768 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 5462482 100% 82.19MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 5463360 bytes received 31 bytes 10926782.00 bytes/sec+ total size is 5462482 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 19669815 100% 80.37MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 19672431 bytes received 31 bytes 13114974.67 bytes/sec+ total size is 19669815 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 5449487 100% 57.40MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 5450365 bytes received 31 bytes 3633597.33 bytes/sec+ total size is 5449487 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 19633259 100% 74.18MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 19635871 bytes received 31 bytes 13090601.33 bytes/sec+ total size is 19633259 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 5392184 100% 62.33MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 5393054 bytes received 31 bytes 3595390.00 bytes/sec+ total size is 5392184 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 18912104 100% 65.00MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 18914628 bytes received 31 bytes 12609772.67 bytes/sec+ total size is 18912104 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4869300 100% 80.92MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4870106 bytes received 31 bytes 9740274.00 bytes/sec+ total size is 4869300 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 20178932 100% 68.13MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 20181608 bytes received 31 bytes 13454426.00 bytes/sec+ total size is 20178932 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 4995425 100% 86.05MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 4996247 bytes received 31 bytes 9992556.00 bytes/sec+ total size is 4995425 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 19970679 100% 76.36MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 19973331 bytes received 31 bytes 13315574.67 bytes/sec+ total size is 19970679 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 7905795 100% 66.45MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 7906973 bytes received 31 bytes 15814008.00 bytes/sec+ total size is 7905795 speedup is 1.00+ ok+ copy redacted.NEF (to USB...) + redacted+ 21234069 100% 78.07MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 21236877 bytes received 31 bytes 8494763.20 bytes/sec+ total size is 21234069 speedup is 1.00+ ok+ copy redacted.JPG (to USB...) + redacted+ 7963979 100% 62.51MB/s 0:00:00 (xfer#1, to-check=0/1)+ + sent 7965165 bytes received 31 bytes 5310130.67 bytes/sec+ total size is 7963979 speedup is 1.00+ git ["--git-dir=copy_target/.git","--work-tree=copy_target","update-index","-z","--index-info"]: Error in fork: forkProcess: resource exhausted (Resource temporarily unavailable)+ + git-annex: user error (git ["--git-dir=copy_target/.git","--work-tree=copy_target","update-index","-z","--index-info"]: Error in fork: forkProcess: resource exhausted (Resource temporarily unavailable))+ failed+ _506 TIMES_ (user error (Error in fork: forkProcess: resource exhausted (Resource temporarily unavailable))) failed+ _11 TIMES_ copy foo (createPipe: resource exhausted (Too many open files)) failed+ _2 TIMES_ (user error (Error in fork: forkProcess: resource exhausted (Resource temporarily unavailable))) failed+ _8574 TIMES_: copy foo (createPipe: resource exhausted (Too many open files)) failed+ git-annex: createPipe: resource exhausted (Too many open files)+ failed+ git-annex: 9101 failed+ + % ls copy_target/.git/annex/tmp/redacted_E13 copy_target/.git/annex/tmp/SHA512E-redacted_E13 # works+ % find source -type l | wc -l+ 13554+ % find copy_target -type l | wc -l+ 13554+ % find copy_target/.git/annex/objects -type f | wc -l+ 4455+ % find source -type f | wc -l+ 13554++> Fixed unreaped process leak.+> (This has nothing to do with NTFS). Ran test with 10k files+> [[done]] --[[Joey]]
+ doc/bugs/fails_to_handle_lot_of_files/comment_1_09d8e4e66d8273fab611bd29e82dc7fc._comment view
@@ -0,0 +1,8 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawl9sYlePmv1xK-VvjBdN-5doOa_Xw-jH4U"+ nickname="Richard"+ subject="comment 1"+ date="2011-10-26T17:16:52Z"+ content="""+After another run, i am at 8909 files in the remote, now.+"""]]
+ doc/bugs/fails_to_handle_lot_of_files/comment_2_fd2ec05f4b5a7a6ae6bd9f5dbc3156de._comment view
@@ -0,0 +1,8 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawl9sYlePmv1xK-VvjBdN-5doOa_Xw-jH4U"+ nickname="Richard"+ subject="comment 2"+ date="2011-10-26T18:22:34Z"+ content="""+In case this matters, I just realized that this disk has been formatted with NTFS instead of a sane FS.+"""]]
doc/bugs/fat_support.mdwn view
@@ -8,8 +8,6 @@ - Use of ":" in filenames of object files, also not supported. Could easily be fixed by reorganizing the object directory. -[[!tag wishlist]]- [[Done]]; in annex.version 2 repos, colons are entirely avoided in filenames. So a bare git clone can be put on VFAT, and git-annex used to move stuff --to and --from it, for sneakernet.
+ doc/bugs/fsck_claims_failed_checksum_when_less_copies_than_required_are_found.mdwn view
@@ -0,0 +1,57 @@+ (checksum...) failed+ fsck foo (fixing location log) + Only 1 of 2 trustworthy copies exist of foo+ Back it up with git-annex copy.++> You've given me severely partial output, and no test case, but until+> it says "fsck foo", the output is pertaining to some other file than foo.+> As far as I can see, there is no bug here. --[[Joey]]++>> Sorry, I thought it would be obvious, but that's no excuse for not+>> providing additional explanation. The problem is that fsck tells me a+>> file's fsck has failed without printing extra details. In this case, the+>> checksum is OK while I don't have enough copies to satisfy the fsck. The+>> fact that I don't have enough copies is obviously relevant, but I would+>> still like to know if the checksums are OK. -- Richard++>>> I think you're misreading the truncated output you posted. The actual,+>>> full output would make much more sense. --[[Joey]]++>>>> No. I have a total of 14908 annex keys, 3333 of which are on a remote. The only message other than 'checksum OK' and the above is 'git-annex: 11577 failed'.+>>>> I checked several files manually, their checksums are OK so `git annex+>>>> fsck` is reporting those files as completely failed when they "only" miss copies. -- Richard++>>>>> fsck considers not enough copies to be a failure condition; it prints+>>>>> error messages about it etc. That has nothing to do with checksums.+>>>>> --[[Joey]]++>>>>>> I get that. Still, I think it would be _extremely_ useful to know what failures occurred, exactly. Not having enough copies is Not Good, yet not having enough copies and a locally correct file is _lot_ better than having not enough copies and a broken file. I.e. I would prefer:++ (checksum...) OK+ Not enough copies: Only 1 of 2 trustworthy copies exist of foo++>>>>>> or similar and at the end++ git-annex: 0 wrong checksums+ git-annex: 11577 with too few copies++>>>>>> In the end, it comes down to the distinction of different failure classes. -- Richard++>>>>>>> For the third, and final time: +>>>>>>> # You are misreading the truncated output you posted+>>>>>>> The "checksum" line is regarding **different** file than the +>>>>>>> not enough copies message. fsck does not attempt to checksum a file+>>>>>>> that is not present. [[done]] --[[Joey]]+++>>>>>>>> I realized early on that I pasted the wrong cross-passage, but as there is a ton of the same output, I didn't think it would matter. I wasn't aware that it does not try to checksum when there aren't enough copies. To be fair, you only just mentioned that.+>>>>>>>> Personally, I think that's a bug as it makes ensuring local correctness before copying a file to remotes impossible.+>>>>>>>> Either way, I really didn't know it actually _skipped_ checksumming; that part was missing.+>>>>>>>> For the benefit of anyone else who might read this, this is the correct order:++ fsck foo (fixing location log) + Only 1 of 2 trustworthy copies exist of foo+ Back it up with git-annex copy.+ (checksum...) failed++>>>>>>>> If you would like to keep things this way, fine. I think it's less than ideal, but I don't want to argue, either. -- Richard
doc/bugs/git_annex_fsck_is_a_no-op_in_bare_repos.mdwn view
@@ -17,3 +17,5 @@ >>> While storing the data is no longer an issue in bare repos, fsck would >>> need a special mode that examines all the location logs, since it >>> cannot run thru the checked out files. --[[Joey]] ++>>>> [[done]]! --[[Joey]]
doc/bugs/git_annex_initremote_walks_.git-annex.mdwn view
@@ -1,4 +1,4 @@-a [[!taglink minor]] <!-- (a suggestion for introducing severity tags on bugs,+a <!-- (a suggestion for introducing severity tags on bugs, feel free to discard) --> issue: `git annex initremote` (in particular, adding a key as described in [[encryption]] -- `git annex initremote my_remote encryption=my_key`) seems to iterate over the `.git-annex/???/???/*.log` files
+ doc/bugs/git_annex_map_has_problems_with_urls_containing___126__.mdwn view
@@ -0,0 +1,46 @@+I discovered a problem with `git annex map` and relative urls containing `~`.+In this case i have a remote `noam` configured with the the following urls:++ zsh» git remote show noam | head -3+ * remote noam+ Fetch URL: noam:bare-annex+ Push URL: noam:bare-annex++If i try to run `git annex map` i get the following error:++ zsh» git annex map+ map /home/esc/annex ok+ map noam (sshing...)+ bash: line 0: cd: /~/bare-annex/: No such file or directory+ Command ssh ["noam","cd '/~/bare-annex/' && git config --list"] failed; exit code 1+ (sshing...)+ ok++ running: dot -Tx11 map.dot++ ok++If i run the failing command manually, i get:++ zsh» ssh noam "cd ~/bare-annex && git config --list"+ core.repositoryformatversion=0+ core.filemode=true+ core.bare=true+ annex.uuid=f267f55c-0732-11e1-a93b-93119f9aaf54+ annex.version=3++Also i can change the remote url to an absolute one, in which case `git annex+map` works too:++ zsh» git remote set-url noam noam:/home/esc/bare-annex+ zsh» git annex map+ map /home/esc/annex ok+ map noam (sshing...)+ ok++ running: dot -Tx11 map.dot++ ok+++> [[fixed|done]] --[[Joey]]
doc/bugs/git_annex_migrate_leaves_old_backend_versions_around.mdwn view
@@ -2,7 +2,7 @@ would be great if these were purged automatically somehow. > Yes, this is an issue mentioned in the-> [[walkthrough|walkthrough/migrating_data_to_a_new_backend]].+> [[tips/migrating_data_to_a_new_backend]]. > > Since multiple files can point to the same content, it could be that > only one file has been migrated, and the content is still used. So
+ doc/bugs/git_annex_upgrade_output_is_inconsistent_and_spammy.mdwn view
@@ -0,0 +1,15 @@+Upgrading from v1 to v3:++ upgrade . (v1 to v2...) (moving content...) (updating symlinks...) (moving location logs...) (v2 to v3...) ..............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................+ git-annex branch created+ Be sure to push this branch when pushing to remotes.+ ok++A whirly would be preferable, imo.++> Erm, I'm pretty sure you were the one who asked for there to be some+> progress dots, Richard.+> +> I'm not particularly interested in implementing a whirley that would only+> be used in this one place, in code that very few users are going to run+> again. I could remove the dots.. [[done]] --[[Joey]]
+ doc/bugs/git_annex_version_should_without_being_in_a_repo_.mdwn view
@@ -0,0 +1,7 @@+was checking the version of git-annex on a machine before cloning a repo...++ $ git annex version+ git-annex: Not in a git repository.++> made difficult by the Annex monad, but I made it work! --[[Joey]]+> [[done]]
+ doc/bugs/interrupting_migration_causes_problems.mdwn view
@@ -0,0 +1,52 @@+Killing a migration from WORM to SHA256 with ^C breaks things; future attempts to do the migration fail:++ #!/bin/bash+ + BASE=/tmp/migrate-bug+ + set -x+ + chmod -R +w $BASE+ rm -rf $BASE+ mkdir -p $BASE+ cd $BASE+ + # create annex+ git init .+ git annex init+ + # make a big (sparse) file and add it+ dd if=/dev/zero of=bigfile bs=1 count=0 seek=1G+ git annex add --backend WORM bigfile+ git commit -m 'added bigfile'+ + # look at status+ git annex status+ + # now migrate it, but kill migration during checksum+ # Simulate ^C by making a new process group and sending SIGINT+ setsid git annex migrate --backend SHA256 bigfile &+ PID=$!+ sleep 1+ kill -INT -$PID+ wait+ + # look at status+ git annex status+ + # this migration fails+ git annex migrate --backend SHA256 bigfile+ + # but fsck says everything's OK+ git annex fsck++The error:++ migrate bigfile + git-annex: /tmp/migrate-bug/.git/annex/objects/K9/V1/WORM-s1073741824-m1321566308--bigfile/WORM-s1073741824-m1321566308--bigfile: createLink: already exists (File exists)+ failed+ git-annex: migrate: 1 failed++> Fixed it to delete the stale temp file. [[done]]+> +> Thanks for making such clear test cases, Jim! --[[Joey]]
doc/bugs/making_annex-merge_try_a_fast-forward.mdwn view
@@ -27,3 +27,9 @@ > > Although, perhaps fast-forward merge would use slightly > less space. --[[Joey]]++>> To avoid the ladder-merge between two repositories described at+>> <http://sprunge.us/LOMU>, seems a fast-forward should be detected and+>> written to git, even if the index is still updated the current way.+>> [[done]]+>> --[[Joey]]
doc/bugs/minor_bug:_errors_are_not_verbose_enough.mdwn view
@@ -22,3 +22,5 @@ etc pp.++> [[done]] --[[Joey]]
+ doc/bugs/old_data_isn__39__t_unused_after_migration.mdwn view
@@ -0,0 +1,66 @@+Old data isn't listed as unused after migrating backends:++ #!/bin/bash+ + BASE=/tmp/migrate-bug-2+ set -x+ chmod -R +w $BASE+ rm -rf $BASE+ mkdir -p $BASE+ cd $BASE+ + # create annex+ git init .+ git annex init+ + # make a big (sparse) file and add it+ dd if=/dev/zero of=bigfile bs=1 count=0 seek=1G+ git annex add --backend WORM bigfile+ git commit -m 'added bigfile'+ + # migrate it+ git annex migrate --backend SHA256 bigfile+ + # status shows 2 keys taking up 2G+ git annex status+ + # but nothing is unused+ git annex unused++Output:++ ++ git annex status+ supported backends: SHA256 SHA1 SHA512 SHA224 SHA384 SHA256E SHA1E SHA512E SHA224E SHA384E WORM URL+ supported remote types: git S3 bup directory rsync web hook+ known repositories: + ede95a82-1166-11e1-a475-475d55eb0f8f -- here+ local annex keys: 2+ local annex size: 2 gigabytes+ visible annex keys: 1+ visible annex size: 1 gigabyte+ backend usage: + WORM: 1+ SHA256: 1+ ++ git annex unused+ unused . (checking for unused data...) (checking master...) ok++The two files are hardlinked, so it's not taking up extra space, but it would be nice to be able to remove the old keys.++> `git annex unused` checks the content of all branches, and assumes that,+> when a branch contains a file that points to a key, that key is still +> used. In this case, the migration has staged a change to the file,+> but it is not yet committed, so when it checks the master branch, it+> still finds a file referring to the old key. +> +> So, slightly surprising, but not a bug. --[[Joey]] [[done]]++>> Thanks for the explanation. In my real repository, it was a bit trickier:+>> the migration was commited to `master`, but other *remote* branches still+>> referenced those keys. I was just doing a `git pull` from a central repo, but+>> needed a `git remote update` to remove those references from `remotes/foo/master` too.+>> --Jim++>>> I have considered making unused ignore remote tracking branches. +>>> On the one hand, it can be a little bit confusing, and those branches+>>> can be out of date. On the other hand, it can be useful to know you're+>>> not dropping anything that some remote might still refer to. --[[Joey]]
+ doc/bugs/uninit_does_not_work_in_old_repos.mdwn view
@@ -0,0 +1,20 @@+As uninit does not need to actually write out any data, just remove it, it should be possible to uninit in old stores.++ % git annex uninit+ git-annex: Repository version 2 is not supported. Upgrade this repository: git-annex upgrade++If the repo happens to be broken, this essentially locks in data.++> No, because you can always check out the version of git-annex you need+> for that repository.+> +> uninit, as implemented, runs unannex on every file and then does some+> cleanup. The cleanup does not need to write state, but the unannex does.+> And it depends on the object directory layout, which has changed between+> versions. So supporting old versions in this code would complicate it+> quite a lot. I don't want to go there. --[[Joey]]++>>Requiring a version upgrade for unannex is fine. Yet, I see a problem when a git repo is broken; you are stuck without being able to uninit. In this case an uninit that does nothing but undo the symlinking would be useful. -- Richard++>>> As I said, version 2 of git-annex is still there for people who need+>>> it for whatever reason. [[done]] --[[Joey]]
+ doc/bugs/uninit_does_not_work_in_old_repos/comment_1_bc0619c6e17139df74639448aa6a0f72._comment view
@@ -0,0 +1,8 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawl9sYlePmv1xK-VvjBdN-5doOa_Xw-jH4U"+ nickname="Richard"+ subject="comment 1"+ date="2011-10-29T15:30:09Z"+ content="""+After upgrading the repo, I still have to commit the changes, else git-annex won't let me uninit. Arguably a Good Thing, but I wanted to document it here.+"""]]
+ doc/bugs/uninit_should_not_run_when_branch_git-annex_is_checked_out.mdwn view
@@ -0,0 +1,15 @@+Running `git annex uninit` in a repo which has branch git-annex checked out will result in:++ error: Cannot delete the branch 'git-annex' which you are currently on.+ git-annex: git [Param "-D",Param "git-annex"] failed++and trying to checkout branch master afterwards results in:++ error: The following untracked working tree files would be overwritten by checkout:++Both of which is logical. The best thing would be if git-annex refused to run uninit while in branch git-annex.+++Richard++> [[done]] --[[Joey]]
+ doc/bugs/wishlist:_allow_users_to_provide_UUID_when_running___96__git_annex_init__96__.mdwn view
@@ -0,0 +1,5 @@+As there's no way to permanently hide remotes and I have to recreate two repos now, I would love to be able to re-use the old UUIDs to remove clutter.++> git-annex already provides a way to do this: Copy `.git/config` from the+> original repo (or use `git-config` to set `annex.uuid`) *before* running+> `git annex init`. [[done]] --[[Joey]]
doc/bugs/wishlist:_more_descriptive_commit_messages_in_git-annex_branch.mdwn view
@@ -34,3 +34,6 @@ > > It would be possible to make it *less* verbose, with an empty commit > message. :) --[[Joey]] ++>> Closing as this is literally impossible to do without making+>> git-annex worse. [[done]] --[[Joey]]
+ doc/bugs/wishlist:_query_things_like_description__44___trust_level.mdwn view
@@ -0,0 +1,4 @@+It would be helpful to have a way to query things like a repository's description and trust level, without having to poke in the git-annex branch. For example, "git annex describe ." currently clears the description but could print the current one instead.++> `git annex status` now breaks down the repository list by type. [[done]]+> --[[Joey]]
+ doc/bugs/wishlist:_support_drop__44___find_on_special_remotes.mdwn view
@@ -0,0 +1,18 @@+Currently there is no way to drop files, or list what files are available, on a special remote.+It would be good if "git annex drop" and "git annex find" supported the --from argument.++> I agree, drop should support --from.+>> [[done]] --[[Joey]] +> +> To find files *believed* to be present in a given remote, use+> `git annex find --in remote`+> Note that it might show out of date info, since it does not actually go+> check the current contents of the remote. The only reason to support+> `find --from` would be to always check, but I don't think that's needed.+> --[[Joey]] ++For commands that don't support the --from argument, it would also be nice to print an error.+Currently running "git annex drop --from usbdrive" doesn't behave as hoped and instead drops+all content from the local annex.++> This is done now. --[[Joey]]
− doc/cheatsheet.mdwn
@@ -1,16 +0,0 @@-A supplement to the [[walkthrough]].--[[!toc]]--[[!inline feeds=no show=0 template=walkthrough pagenames="""- walkthrough/using_Amazon_S3- walkthrough/using_bup- walkthrough/using_the_web- walkthrough/using_the_SHA1_backend- walkthrough/migrating_data_to_a_new_backend- walkthrough/untrusted_repositories- walkthrough/what_to_do_when_you_lose_a_repository- walkthrough/recover_data_from_lost+found- walkthrough/Internet_Archive_via_S3- walkthrough/powerful_file_matching-"""]]
doc/design/encryption.mdwn view
@@ -1,6 +1,6 @@ This was the design doc for [[/encryption]] and is preserved for the curious. For an example of using git-annex with an encrypted S3 remote,-see [[walkthrough/using_Amazon_S3]].+see [[tips/using_Amazon_S3]]. [[!toc]]
doc/distributed_version_control.mdwn view
@@ -11,3 +11,11 @@ and can copy it to or from other repositories. [[Location_tracking]] information is committed to git, to let repositories inform other repositories what file contents they have available.++--++The [[walkthrough]] shows how to create a distributed set of git-annex+repositories with no central repository.++Prefer a central repository like GitHub? See the+[[tips/centralized_git_repository_tutorial]].
+ doc/forum/--print0_option_as_in___34__find__34__.mdwn view
@@ -0,0 +1,5 @@+It would be nice if git annex find supported a --print0 option as GNU+find does. That way, file names that are printed could be piped to+xargs even if they have spaces. ++> Done. --[[Joey]]
+ doc/forum/Recommended_number_of_repositories.mdwn view
@@ -0,0 +1,4 @@+With git it is easy to create one repository per project, and it almost always makes sense to do so. When using git-annex, what is the recommended setup?++Should one have a single annex containing all files, or is it recommended to create different repositories for things like 'photos', 'music', 'isos' ?+
+ doc/forum/Recommended_number_of_repositories/comment_1_3ef256230756be8a9679b107cdbfd018._comment view
@@ -0,0 +1,15 @@+[[!comment format=mdwn+ username="http://joey.kitenet.net/"+ nickname="joey"+ subject="depends ..."+ date="2011-11-04T19:59:24Z"+ content="""+It makes sense to have separate repositories when you have well-defined uses for them. ++I have a separate repository just for music and podcasts, which I can put various places where I have no need of the overhead of a tree of other files.++If you're using it for whatever arbitrary large files you accumulate, I find it's useful to have them in one repository. This way I can rearrange things as makes sense. It might make sense to have \"photos\" and \"isos\" as categories today, but next year you might prefer to move those under 2011/{photos,isos}. It would certainly make sense to have different repositories for home, work, etc.++How to split repositories up for a home directory is a general problem that the [vcs-home](http://vcs-home.branchable.com)+project has surely considered at one time or another.+"""]]
+ doc/forum/confusion_with_remotes__44___map.mdwn view
@@ -0,0 +1,113 @@+I'm starting out with git-annex and running into some confusion with setting up the remotes.++I have three systems I'm trying to set up (domains edited):++* psychosis: ssh://psychosis.foo.com/vid+* bacon: ssh://bucket.foo.com/vid+* bucket: ssh://bucket.bar.org/vid++And one bare repository so that I can have a single place to push/pull:++* origin: https://git.foo.com/jim/vid.git++On psychosis:++ psychosis$ git config --list | grep ^remote | sort+ remote.bacon.annex-uuid=8f1f0898-f8c1-11e0-9bf2-b387af26ee63+ remote.bacon.fetch=+refs/heads/*:refs/remotes/bacon/*+ remote.bacon.url=ssh://bucket.foo.com/vid+ remote.bucket.annex-uuid=82814942-f8e0-11e0-b053-e70a61e98e19+ remote.bucket.fetch=+refs/heads/*:refs/remotes/bucket/*+ remote.bucket.url=ssh://bucket.bar.org/vid+ remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*+ remote.origin.url=https://git.foo.com/jim/vid.git+ + psychosis$ git annex status+ supported backends: WORM SHA1 SHA256 SHA512 SHA224 SHA384 SHA1E SHA256E SHA512E SHA224E SHA384E URL+ supported remote types: git S3 bup directory rsync web hook+ known repositories: + 09c0b436-f8de-11e0-842f-b7644539d57f -- here (psychosis)+ 82814942-f8e0-11e0-b053-e70a61e98e19 -- bucket+ local annex keys: 2256+ local annex size: 449 gigabytes+ total annex keys: 2256+ total annex size: 449 gigabytes+ backend usage: + WORM: 2256++**First point of confusion**: Why doesn't "bacon" show up in "git annex status"? I can "git annex copy --to bacon filename" and it will copy it there. Is there some step of setting it up that I missed? I basically just did "git remote add bacon ssh://bucket.foo.com/vid".++Now I've started setting up the remotes on each host:++On bacon:+ + bacon$ git config --list | grep ^remote | sort+ remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*+ remote.origin.url=https://git.foo.com/jim/vid.git+ remote.psychosis.annex-uuid=09c0b436-f8de-11e0-842f-b7644539d57f+ remote.psychosis.fetch=+refs/heads/*:refs/remotes/psychosis/*+ remote.psychosis.url=ssh://psychosis.foo.com/vid+ + bacon$ git annex status+ supported backends: WORM SHA1 SHA256 SHA512 SHA224 SHA384 SHA1E SHA256E SHA512E SHA224E SHA384E URL+ supported remote types: git S3 bup directory rsync web hook+ known repositories: + 09c0b436-f8de-11e0-842f-b7644539d57f -- psychosis+ 8f1f0898-f8c1-11e0-9bf2-b387af26ee63 -- here (bacon)+ temporary directory size: 366 megabytes (clean up with git-annex unused)+ local annex keys: 1+ local annex size: 308 bytes+ total annex keys: 2256+ total annex size: 449 gigabytes+ backend usage: + WORM: 2256++On bucket:++ bucket$ git config --list | grep ^remote | sort+ remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*+ remote.origin.url=https://git.foo.com/jim/vid.git+ remote.psychosis.annex-uuid=09c0b436-f8de-11e0-842f-b7644539d57f+ remote.psychosis.fetch=+refs/heads/*:refs/remotes/psychosis/*+ remote.psychosis.url=ssh://psychosis.foo.com/vid++ bucket$ git annex status+ supported backends: WORM SHA1 SHA256 SHA512 SHA224 SHA384 SHA1E SHA256E SHA512E SHA224E SHA384E URL+ supported remote types: git S3 bup directory rsync web hook+ known repositories: + 09c0b436-f8de-11e0-842f-b7644539d57f -- psychosis+ 82814942-f8e0-11e0-b053-e70a61e98e19 -- here (bucket)+ temporary directory size: 183 megabytes (clean up with git-annex unused)+ local annex keys: 3+ local annex size: 550 megabytes+ total annex keys: 2256+ total annex size: 449 gigabytes+ backend usage: + WORM: 2256++But I'm getting weird results if I try to show the map from psychosis:++ psychosis$ git annex map+ $ git annex map+ map /vid/tv ok+ map bacon (sshing...) + ok+ map bucket (sshing...) + ok+ map origin + failed+ map psychosis (sshing...) + jim@psychosis.foo.com's password: + ok+ map psychosis (sshing...) + jim@psychosis.foo.com's password: + ok+ + running: dot -Tx11 map.dot++**Second confusion**: it's as if psychosis was considered a new remote each time?+The generated map has psychosis listed with several redundant links:++++Is this some bug or do I just need to be hit with the clue bat?
+ doc/forum/confusion_with_remotes__44___map/comment_1_a38ded23b7f288292a843abcb1a56f38._comment view
@@ -0,0 +1,10 @@+[[!comment format=mdwn+ username="http://joey.kitenet.net/"+ nickname="joey"+ subject="comment 1"+ date="2011-10-17T19:01:21Z"+ content="""+My guess is that psychosis has not pulled the git-annex branch since bacon was set up (or that bacon's git-annex branch has not been pushed to origin). git-annex status only shows remotes present in git-annex:uuid.log This may be a bug.++The duplicate links in the map I don't quite understand. I only see duplicate links in my maps when I have the same repository configured as two different git remotes (for example, because the same repository can be accessed two different ways). You don't seem to have that in your config.+"""]]
+ doc/forum/confusion_with_remotes__44___map/comment_2_cd1c98b1276444e859a22c3dbd6f2a79._comment view
@@ -0,0 +1,8 @@+[[!comment format=mdwn+ username="http://joey.kitenet.net/"+ nickname="joey"+ subject="comment 2"+ date="2011-10-17T19:02:50Z"+ content="""+Actually, there is a hint that, while you ran the git annex map on psychosis, it decided to ssh to itself two times. That seems to be where the duplicate links came from, I guess you must have some git remotes you did not show.+"""]]
+ doc/forum/confusion_with_remotes__44___map/comment_3_18531754089c991b6caefc57a5c17fe9._comment view
@@ -0,0 +1,24 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawnBJ6Dv1glxzzi4qIzGFNa6F-mfHIvv9Ck"+ nickname="Jim"+ subject="comment 3"+ date="2011-10-17T19:50:06Z"+ content="""+No extra remotes (that I'm aware of); that output was only edited to change hostnames.++On all three hosts, \"git push origin\" and \"git pull origin\" say everything is up to date.++I'm using git-annex 3.20111011 on all hosts (although some were running 3.20110928 when I created the repositories).++Regarding the multiple links, I've put a copy of the dot file [here](http://jim.sh/~jim/tmp/map.dot).+It shows psychosis in three separate subgraphs, that are just getting rendered together as one,+if that helps clarify anything.++Wait, I just realized you said \"the git-annex branch\". My origin only has \"master\".+Do you mean the one specifically named \"git-annex\"? I thought that was something that+gets managed automatically, or is it something I need to manually check out and deal with?++Any other info I could provide?+++"""]]
+ doc/forum/confusion_with_remotes__44___map/comment_4_3b89b6d1518267fcbc050c9de038b9ca._comment view
@@ -0,0 +1,11 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawnBJ6Dv1glxzzi4qIzGFNa6F-mfHIvv9Ck"+ nickname="Jim"+ subject="comment 4"+ date="2011-10-17T20:36:51Z"+ content="""+Ok, after pushing the \"git-annex\" branch to origin, then \"git annex status\" knows all repositories on all hosts, so that part makes sense now. Thanks for the tip. But the \"git annex map\" output hasn't changed.++++"""]]
+ doc/forum/confusion_with_remotes__44___map/comment_5_27801584325d259fa490f67273f2ff71._comment view
@@ -0,0 +1,16 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawnBJ6Dv1glxzzi4qIzGFNa6F-mfHIvv9Ck"+ nickname="Jim"+ subject="comment 5"+ date="2011-10-18T04:59:13Z"+ content="""+I think:++* The first extra edge is because bucket had \"ssh://psychosis.foo.com/vid/\", while+bacon had \"ssh://psychosis.foo.com/vid\" with no trailing slash. That got lost in the hostname/path editing I did, sorry.+Maybe those should be considered matching?+* The second extra edge is because, when running \"git annex map\" from psychosis, it doesn't recognize the remote's+remote URL as pointing back to itself.++For the second case, after the \"spurious\" SSH, it could still recognize that the repositories are the same by the duplicated annex uuid, which currently shows up in `map.dot` twice. I wonder what it would take to avoid the spurious SSH -- maybe some config that lists \"alternate\" URLs that should be considered the same as the current repository? Or actually list URLs in uuid.log? Fortunately, I think this only affects the map, so it's not a big problem.+"""]]
+ doc/forum/confusion_with_remotes__44___map/comment_6_496b0d9b86869bbac3a1356d53a3dda4._comment view
@@ -0,0 +1,8 @@+[[!comment format=mdwn+ username="http://joey.kitenet.net/"+ nickname="joey"+ subject="comment 6"+ date="2011-10-22T01:18:27Z"+ content="""+Hmm, I don't see the spurious ssh edge in the dot file -- that is, I don't see any ssh:// uris in it?+"""]]
+ doc/forum/confusion_with_remotes__44___map/comment_7_9a456f61f956a3d5e81e723d5a90794c._comment view
@@ -0,0 +1,27 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawnBJ6Dv1glxzzi4qIzGFNa6F-mfHIvv9Ck"+ nickname="Jim"+ subject="comment 7"+ date="2011-10-22T05:25:47Z"+ content="""+I think that's because the SSH was successful (I entered the password and let it connect), so it got the UUID and put that in the .dot instead. The same UUID (for psychosis) then ended up in two different \"subgraph\" stanzas, and Graphviz just plotted them together as one node.++<p>Maybe this will clarify:++<p>On psychosis, run \"git annex map\" and press ^C at the ssh password prompt: [map-nossh.dot](http://jim.sh/~jim/tmp/map-nossh.dot)+++<p>On psychosis, run \"git annex map\" and type the correct password: [map-goodssh.dot](http://jim.sh/~jim/tmp/map-goodssh.dot)+++As I see it:++* psychosis (\"localhost\") connects to each of its remotes+* some of them point back to ssh://psychosis+* psychosis doesn't know that ssh://psychosis is itself, so it tries to connect+* if successful:+ * psychosis gets put twice in the .dot as if it was two different hosts, one \"local\" and one \"ssh://psychosis\"+ * graphviz recognizes it as the same node because the UUID is the same, but graphviz still draws the extra connecting lines+* if unsuccessful:+ * ssh://psychosis is shown as an additional host that can't be reached+"""]]
doc/forum/getting_git_annex_to_do_a_force_copy_to_a_remote.mdwn view
@@ -2,7 +2,9 @@ git annex copy --force --to REMOTE . -just zip's through really quickly and doesn't actually force a copy to a remote location. This is just following up on the [[git-annex directory hashing problems on osx]]. I want to just do a force copy of all my data to my portable disk to really make sure that the data is really there. I would similarly would want to make sure I can force a +just zip's through really quickly and doesn't actually force a copy to a+remote location. This is just following up on the+[[bugs/git-annex_directory_hashing_problems_on_osx]]. I want to just do a force copy of all my data to my portable disk to really make sure that the data is really there. I would similarly would want to make sure I can force a git annex copy --force --from REMOTE .
+ doc/forum/git_annex_ls___47___metadata_in_git_annex_whereis.mdwn view
@@ -0,0 +1,1 @@+I just started experimenting with git annex, and I found that I would like to have a way to figure out metadata (well, size. Maybe modification date) of a non-local file. I first checked if there is "git annex ls" (which could list known files in an ls-like way) and found "git annex whereis" as somewhat a replacement, but it does not give metadata information.
+ doc/forum/git_tag_missing_for_3.20111011.mdwn view
@@ -0,0 +1,1 @@+Well, the subject pretty much says it all :)
+ doc/forum/git_tag_missing_for_3.20111011/comment_1_7a53bf273f3078ab3351369ef2b5f2a6._comment view
@@ -0,0 +1,8 @@+[[!comment format=mdwn+ username="http://joey.kitenet.net/"+ nickname="joey"+ subject="fixed that"+ date="2011-10-13T15:36:59Z"+ content="""+:)+"""]]
doc/forum/wishlist:_special_remote_for_sftp_or_rsync.mdwn view
@@ -1,11 +1,11 @@-i think it would be useful to have a fourth kind of [[special remote]]s+i think it would be useful to have a fourth kind of [[special_remotes]] that connects to a dumb storage using sftp or rsync. this can be emulated by using sshfs, but that means lots of round-trips through the system and is limited to platforms where sshfs is available. typical use cases are backups to storate shared between a group of people-where each user only has limited access (sftp or rsync), when using [[bup]]-is not an option.+where each user only has limited access (sftp or rsync), when using+[[special_remotes/bup]] is not an option. an alternative to implementing yet another special remote would be to have some kind of plugin system by which external programs can provide an
− doc/forum/wishlist:_support_for_more_ssh_urls_.mdwn
@@ -1,6 +0,0 @@-git-annex does not seem to support all kinds of urls that git does.--> Please use [[bugs]] for bug reports. Moved [[there|bugs/wishlist:_support_for_more_ssh_urls_]].-> --[[Joey]] -->> And now all fixed! --[[Joey]]
doc/future_proofing.mdwn view
@@ -34,4 +34,4 @@ metadata. So if a filesystem is badly corrupted and all your annexed files end up in `lost+found`, they can easily be lifted back out into another clone of the repository. Even if the filenames are lost,- it's possible to [[walkthrough/recover_data_from_lost+found]].+ it's possible to [[tips/recover_data_from_lost+found]].
doc/git-annex-shell.mdwn view
@@ -19,6 +19,10 @@ Any command not listed below is passed through to git-shell. +Note that the directory parameter should be an absolute path, otherwise+it is assumed to be relative to the user's home directory. Also the+first "/~/" or "/~user/" is expanded to the specified home directory.+ * configlist directory This outputs a subset of the git configuration, in the same form as@@ -44,11 +48,23 @@ # OPTIONS -Same as git-annex or git-shell, depending on the command being run.+Most options are the same as in git-annex. The ones specific+to git-annex-shell are: -Note that the directory parameter should be an absolute path, otherwise-it is assumed to be relative to the user's home directory. Also the-first "/~/" or "/~user/" is expanded to the specified home directory.+* --uuid=UUID++ git-annex uses this to specify the UUID of the repository it was expecting+ git-annex-shell to access, as a sanity check.++# ENVIRONMENT++* GIT_ANNEX_SHELL_READONLY++ If set, disallows any command that could modify the repository.++* GIT_ANNEX_SHELL_LIMITED++ If set, disallows running git-shell to handle unknown commands. # SEE ALSO
doc/git-annex.mdwn view
@@ -72,14 +72,18 @@ will involve copying them from another repository, or downloading them, or transferring them from some kind of key-value store. + Normally git-annex will choose which repository to copy the content from,+ but you can override this using the --from option.+ * drop [path ...] Drops the content of annexed files from this repository. git-annex will refuse to drop content if it cannot verify it is- safe to do so. At least one copy of content needs to exist in another- remote. This can be overridden with the --force switch.+ safe to do so. This can be overridden with the --force switch. + To drop content from a remote, specify --from.+ * move [path ...] When used with the --from option, moves the content of annexed files@@ -228,6 +232,9 @@ With no parameters, defaults to finding all files in the current directory and its subdirectories. + To output filenames terminated with nulls, for use with xargs -0,+ specify --print0.+ * whereis [path ...] Displays a list of repositories known to contain the content of the@@ -236,8 +243,10 @@ * status Displays some statistics and other information, including how much data- is in the annex.+ is in the annex and a list of all known repositories. + To only show the data that can be gathered quickly, use --fast.+ * map Helps you keep track of your repositories, and the connections between them,@@ -270,6 +279,19 @@ 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. +* reinject src dest++ Moves the src file into the annex as the content of the dest file.+ This can be useful if you have obtained the content of a file from+ elsewhere and want to put it in the local annex.++ Automatically runs fsck on dest to check that the expected content was+ provided.++ Example:++ git annex reinject /tmp/foo.iso foo.iso+ * unannex [path ...] Use this to undo an accidental `git annex add` command. You can use@@ -300,7 +322,7 @@ This is meant to be called from git's pre-commit hook. `git annex init` automatically creates a pre-commit hook using this. -* fromkey file+* fromkey key file This plumbing-level command can be used to manually set up a file in the git repository to link to a specified key.@@ -317,15 +339,6 @@ git annex dropkey SHA1-s10-7da006579dd64330eb2456001fd01948430572f2 -* setkey file-- This plumbing-level command sets the annexed data for a key to the- content of the specified file, and then removes the file.-- Example:-- git annex setkey --key=WORM-s3-m1287765018--file /tmp/file- # OPTIONS * --force@@ -397,10 +410,6 @@ are in the annex, their backend is known and this option is not necessary. -* --key=name-- Specifies a key to operate on.- * -c name=value Used to override git configuration settings. May be specified multiple times.@@ -435,7 +444,7 @@ * --copies=number Matches only files that git-annex believes to have the specified number- of copies.+ of copies, or more. * --not @@ -477,6 +486,20 @@ Space-separated list of names of the key-value backends to use. The first listed is used to store new files by default. +* `annex.diskreserve`++ Amount of disk space to reserve. Disk space is checked when transferring+ content to avoid running out, and additional free space can be reserved+ via this option, to make space for more important content (such as git+ commit logs). Can be specified with any commonly used units, for example,+ "0.5 gb" or "100 KiloBytes"++ The default reserve is 1 megabyte.++* `annex.version`++ Automatically maintained, and used to automate upgrades between versions.+ * `remote.<name>.annex-cost` When determining which repository to@@ -501,6 +524,12 @@ Or, it could be used if the network connection between two repositories is too slow to be used normally. +* `remote.<name>.annexUrl`++ Can be used to specify a different url than the regular `remote.<name>.url`+ for git-annex to use when talking with the remote. Similar to the `pushUrl`+ used by git-push.+ * `remote.<name>.annex-uuid` git-annex caches UUIDs of remote repositories here.@@ -525,20 +554,6 @@ Default ssh, rsync, and bup options to use if a remote does not have specific options.--* `annex.diskreserve`-- Amount of disk space to reserve. Disk space is checked when transferring- content to avoid running out, and additional free space can be reserved- via this option, to make space for more important content (such as git- commit logs). Can be specified with any commonly used units, for example,- "0.5 gb" or "100 KiloBytes"-- The default reserve is 1 megabyte.--* `annex.version`-- Automatically maintained, and used to automate upgrades between versions. * `remote.<name>.buprepo`
doc/git-union-merge.mdwn view
@@ -15,7 +15,7 @@ simply by concacenating together lines from all the oldrefs, in any order. So, this is useful only for branches containing log-type data. -That this does not touch the checked out working copy. It operates+Note that this does not touch the checked out working copy. It operates entirely on git refs and branches. # EXAMPLE
doc/index.mdwn view
@@ -7,6 +7,7 @@ * **[[download]]** * [[install]]+* [[tips]] * [[bugs]] * [[todo]] * [[forum]]
doc/install/Ubuntu.mdwn view
@@ -1,5 +1,11 @@-If using Ubuntu natty or newer:+If using Ubuntu Oneiric or newer: sudo apt-get install git-annex Otherwise, see [[manual_installation_instructions|install]].++---++Warning: The version of git-annex shipped in Ubuntu Oneiric+has [a bug that prevents upgrades from v1 git-annex repositories](https://bugs.launchpad.net/ubuntu/+source/git-annex/+bug/875958).+If you need to upgrade such a repository, get a newer version of git-annex.
+ doc/meta.mdwn view
@@ -0,0 +1,5 @@+This wiki contains [[!pagecount pages="*"]] pages.++Broken links:++[[!brokenlinks ]]
− doc/news/version_3.20110902.mdwn
@@ -1,9 +0,0 @@-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/news/version_3.20110906.mdwn
@@ -1,4 +0,0 @@-git-annex 3.20110906 released with [[!toggle text="these changes"]]-[[!toggleable text="""- * Improve display of newlines around error and warning messages.- * Fix Makefile to work with cabal again."""]]
− doc/news/version_3.20110915.mdwn
@@ -1,10 +0,0 @@-git-annex 3.20110915 released with [[!toggle text="these changes"]]-[[!toggleable text="""- * whereis: Show untrusted locations separately and do not include in- location count.- * Fix build without S3.- * addurl: Always use whole url as destination filename, rather than- only its file component.- * get, drop, copy: Added --auto option, which decides whether- to get/drop content as needed to work toward the configured numcopies.- * bugfix: drop and fsck did not honor --exclude"""]]
− doc/news/version_3.20110928.mdwn
@@ -1,19 +0,0 @@-git-annex 3.20110928 released with [[!toggle text="these changes"]]-[[!toggleable text="""- * --in can be used to make git-annex only operate on files- believed to be present in a given repository.- * Arbitrarily complex expressions can be built to limit the files git-annex- operates on, by combining the options --not --and --or -( and -)- Example: git annex get --exclude '*.mp3' --and --not -( --in usbdrive --or --in archive -)- * --copies=N can be used to make git-annex only operate on files with- the specified number of copies. (And --not --copies=N for the inverse.)- * find: Rather than only showing files whose contents are present,- when used with --exclude --copies or --in, displays all files that- match the specified conditions.- * Note that this is a behavior change for git-annex find! Old behavior- can be gotten by using: git-annex find --in .- * status: Massively sped up; remove --fast mode.- * unused: File contents used by branches and tags are no longer- considered unused, even when not used by the current branch. This is- the final piece of the puzzle needed for git-annex to to play nicely- with branches."""]]
− doc/news/version_3.20111011.mdwn
@@ -1,30 +0,0 @@-git-annex 3.20111011 released with [[!toggle text="these changes"]]-[[!toggleable text="""- * This version of git-annex only works with git 1.7.7 and newer.- The breakage with old versions is subtle, and affects the- annex.numcopies settings in .gitattributes, so be sure to upgrade git- to 1.7.7. (Debian package now depends on that version.)- * Don't pass absolute paths to git show-attr, as it started following- symlinks when that's done in 1.7.7. Instead, use relative paths,- which show-attr only handles 100% correctly in 1.7.7. Closes: #[645046](http://bugs.debian.org/645046)- * Fix referring to remotes by uuid.- * New or changed repository descriptions in uuid.log now have a timestamp,- which is used to ensure the newest description is used when the uuid.log- has been merged.- * Note that older versions of git-annex will display the timestamp as part- of the repository description, which is ugly but otherwise harmless.- * Add timestamps to trust.log and remote.log too.- * git-annex-shell: Added the --uuid option.- * git-annex now asks git-annex-shell to verify that it's operating in- the expected repository.- * Note that this git-annex will not interoperate with remotes using- older versions of git-annex-shell.- * Now supports git's insteadOf configuration, to modify the url- used to access a remote. Note that pushInsteadOf is not used;- that and pushurl are reserved for actual git pushes. Closes: #[644278](http://bugs.debian.org/644278)- * status: List all known repositories.- * When displaying a list of repositories, show git remote names- in addition to their descriptions.- * Add locking to avoid races when changing the git-annex branch.- * Various speed improvements gained by using ByteStrings.- * Contain the zombie hordes."""]]
+ doc/news/version_3.20111025.mdwn view
@@ -0,0 +1,8 @@+git-annex 3.20111025 released with [[!toggle text="these changes"]]+[[!toggleable text="""+ * A remote can have a annexUrl configured, that is used by git-annex+ instead of its usual url. (Similar to pushUrl.)+ * migrate: Copy url logs for keys when migrating.+ * git-annex-shell: GIT\_ANNEX\_SHELL\_READONLY and GIT\_ANNEX\_SHELL\_LIMITED+ environment variables can be set to limit what commands can be run.+ This is used by gitolite's new git-annex support!"""]]
+ doc/news/version_3.20111105.mdwn view
@@ -0,0 +1,27 @@+git-annex 3.20111105 released with [[!toggle text="these changes"]]+[[!toggleable text="""+ * The default backend used when adding files to the annex is changed+ from WORM to SHA256.+ To get old behavior, add a .gitattributes containing: * annex.backend=WORM+ * Sped up some operations on remotes that are on the same host.+ * copy --to: Fixed leak when copying many files to a remote on the same+ host.+ * uninit: Add guard against being run with the git-annex branch checked out.+ * Fail if --from or --to is passed to commands that do not support them.+ * drop --from is now supported to remove file content from a remote.+ * status: Now always shows the current repository, even when it does not+ appear in uuid.log.+ * fsck: Now works in bare repositories. Checks location log information,+ and file contents. Does not check that numcopies is satisfied, as+ .gitattributes information about numcopies is not available in a bare+ repository.+ * unused, dropunused: Now work in bare repositories.+ * Removed the setkey command, and added a reinject command with a more+ useful interface.+ * The fromkey command now takes the key as its first parameter. The --key+ option is no longer used.+ * Built without any filename containing .git being excluded. Closes: #[647215](http://bugs.debian.org/647215)+ * Record uuid when auto-initializing a remote so it shows in status.+ * Bugfix: Fixed git-annex init crash in a bare repository when there was+ already an existing git-annex branch.+ * Pass -t to rsync to preserve timestamps."""]]
+ doc/news/version_3.20111107.mdwn view
@@ -0,0 +1,8 @@+git-annex 3.20111107 released with [[!toggle text="these changes"]]+[[!toggleable text="""+ * merge: Use fast-forward merges when possible.+ Thanks Valentin Haenel for a test case showing how non-fast-forward+ merges could result in an ongoing pull/merge/push cycle.+ * Don't try to read config from repos with annex-ignore set.+ * Bugfix: In the past two releases, git-annex init has written the uuid.log+ in the wrong format, with the UUID and description flipped."""]]
+ doc/news/version_3.20111111.mdwn view
@@ -0,0 +1,10 @@+git-annex 3.20111111 released with [[!toggle text="these changes"]]+[[!toggleable text="""+ * Handle a case where an annexed file is moved into a gitignored directory,+ by having fix --force add its change.+ * Avoid cyclic drop problems.+ * Optimized copy --from and get --from to avoid checking the location log+ for files that are already present.+ * Automatically fix up badly formatted uuid.log entries produced by+ 3.20111105, whenever the uuid.log is changed (ie, by init or describe).+ * map: Support remotes with /~/ and /~user/"""]]
+ doc/news/version_3.20111122.mdwn view
@@ -0,0 +1,22 @@+git-annex 3.20111122 released with [[!toggle text="these changes"]]+[[!toggleable text="""+ * merge: Improve commit messages to mention what was merged.+ * Avoid doing auto-merging in commands that don't need fully current+ information from the git-annex branch. In particular, git annex add+ no longer needs to auto-merge.+ * init: When run in an already initalized repository, and without+ a description specified, don't delete the old description.+ * Optimised union merging; now only runs git cat-file once, and runs+ in constant space.+ * status: Now displays trusted, untrusted, and semitrusted repositories+ separately.+ * status: Include all special remotes in the list of repositories.+ * status: Fix --json mode.+ * status: --fast is back+ * Fix support for insteadOf url remapping. Closes: #[644278](http://bugs.debian.org/644278)+ * When not run in a git repository, git-annex can still display a usage+ message, and "git annex version" even works.+ * migrate: Don't fall over a stale temp file.+ * Avoid excessive escaping for rsync special remotes that are not accessed+ over ssh.+ * find: Support --print0"""]]
doc/not.mdwn view
@@ -43,7 +43,7 @@ same repository, which git-annex does, and is an important feature for large-scale archiving. -* git-annex is not the [Mercurial bfiles extension](http://mercurial.selenic.com/wiki/BfilesExtension).+* git-annex is not the [Mercurial largefiles extension](http://mercurial.selenic.com/wiki/LargefilesExtension). Although mercurial and git have some of the same problems around large files, and both try to solve them in similar ways (standin files using mostly hashes of the real content).
doc/special_remotes/S3.mdwn view
@@ -1,8 +1,8 @@ This special remote type stores file contents in a bucket in Amazon S3 or a similar service. -See [[walkthrough/using_Amazon_S3]] and-[[walkthrough/Internet_Archive_via_S3]] for usage examples.+See [[tips/using_Amazon_S3]] and+[[tips/Internet_Archive_via_S3]] for usage examples. ## configuration
doc/special_remotes/web.mdwn view
@@ -1,5 +1,5 @@ git-annex can use the WWW as a special remote, downloading urls to files.-See [[walkthrough/using_the_web]] for usage examples.+See [[tips/using_the_web_as_a_special_remote]] for usage examples. ## notes
+ doc/tips.mdwn view
@@ -0,0 +1,4 @@+This page is a place to document tips and techniques for using git-annex.++[[!inline pages="tips/* and !tips/*/*" archive="yes"+rootpage="tips" postformtext="Add a new tip about:" show=0]]
+ doc/tips/Internet_Archive_via_S3.mdwn view
@@ -0,0 +1,49 @@+[The Internet Archive](http://www.archive.org/) allows members to upload+collections using an Amazon S3 +[compatible API](http://www.archive.org/help/abouts3.txt), and this can+be used with git-annex's [[special_remotes/S3]] support. ++So, you can locally archive things with git-annex, define remotes that+correspond to "items" at the Internet Archive, and use git-annex to upload+your files to there. Of course, your use of the Internet Archive must+comply with their [terms of service](http://www.archive.org/about/terms.php).++Sign up for an account, and get your access keys here:+<http://www.archive.org/account/s3.php>+ + # export AWS_ACCESS_KEY_ID=blahblah+ # export AWS_SECRET_ACCESS_KEY=xxxxxxx++Specify `host=s3.us.archive.org` when doing `initremote` to set up+a remote at the Archive. This will enable a special Internet Archive mode:+Encryption is not allowed; you are required to specify a bucket name+rather than having git-annex pick a random one; and you can optionally+specify `x-archive-meta*` headers to add metadata as explained in their+[documentation](http://www.archive.org/help/abouts3.txt).++[[!template id=note text="""+/!\ There seems to be a bug in either hS3 or the archive that breaks+authentication when the bucket name contains spaces or upper-case letters..+use all lowercase and no spaces when making the bucket with `initremote`.+"""]]++ # git annex initremote archive-panama type=S3 \+ host=s3.us.archive.org bucket=panama-canal-lock-blueprints \+ x-archive-meta-mediatype=texts x-archive-meta-language=eng \+ x-archive-meta-title="original Panama Canal lock design blueprints"+ initremote archive-panama (Internet Archive mode) ok+ # git annex describe archive-panama "a man, a plan, a canal: panama"+ describe archive-panama ok++Then you can annex files and copy them to the remote as usual:++ # git annex add photo1.jpeg --backend=SHA1E+ add photo1.jpeg (checksum...) ok+ # git annex copy photo1.jpeg --fast --to archive-panama+ copy (to archive-panama...) ok++Note the use of the SHA1E [[backend|backends]]. It makes most sense +to use the WORM or SHA1E backend for files that will be stored in+the Internet Archive, since the key name will be exposed as the filename+there, and since the Archive does special processing of files based on+their extension.
+ doc/tips/automatically_getting_files_on_checkout.mdwn view
@@ -0,0 +1,15 @@+Normally git-annex does not retrieve file contents when checking out a+tree. In some use cases, it makes sense to always have the contents of+files available after a `git checkout` or `git update`. This can be+accomplished by installing the following as `.git/hooks/post-checkout`++ #!/bin/sh+ # Uses git-annex to get all files in the specified directories+ # (relative to the top of the repository) on checkout.+ dirs=.+ top="$(git rev-parse --show-toplevel)"+ for dir in "$dirs"; do git annex get $top/$dir"; done++By default, all files in the whole repository will be made available. The+`dirs` setting can be configured if you only want to get files in certian+directories.
+ doc/tips/centralized_git_repository_tutorial.mdwn view
@@ -0,0 +1,140 @@+The [[walkthrough]] builds up a decentralized git repository setup, but+git-annex can also be used with a centralized bare repository, just like+git can. This tutorial shows how to set up a centralized repository hosted on+GitHub.++## set up the repository, and make a checkout++I've created a repository for technical talk videos, which you can+[fork on Github](https://github.com/joeyh/techtalks).+Or make your own repository on GitHub (or elsewhere) now.++On your laptop, [[install]] git-annex, and clone the repository:++ # git clone git@github.com:joeyh/techtalks.git+ # cd techtalks++Tell git-annex to use the repository, and describe where this clone is+located:++ # git annex init 'my laptop'+ init my laptop ok++Let's tell git-annex that GitHub doesn't support running git-annex-shell there.+This means you can't store annexed file *contents* on GitHub; it would+really be better to host the bare repository on your own server, which+would not have this limitation. (If you want to do that, check out+[[using_gitolite_with_git-annex]].)++ # git config remote.origin.annex-ignore true++## add files to the repository++Add some files, obtained however.++ # youtube-dl -t 'http://www.youtube.com/watch?v=b9FagOVqxmI'+ # git annex add *.mp4+ add Haskell_Amuse_Bouche-b9FagOVqxmI.mp4 (checksum) ok+ (Recording state in git...)+ # git commit -m "added a video. I have not watched it yet but it sounds interesting"++This file is available directly from the web; so git-annex can download it:++ # git annex addurl http://kitenet.net/~joey/screencasts/git-annex_coding_in_haskell.ogg+ addurl kitenet.net_~joey_screencasts_git-annex_coding_in_haskell.ogg+ (downloading http://kitenet.net/~joey/screencasts/git-annex_coding_in_haskell.ogg ...)+ (checksum...) ok+ (Recording state in git...)+ # git commit -a -m 'added a screencast I made'++Feel free the rename the files, etc, using normal git commands:++ # git mv Haskell_Amuse_Bouche-b9FagOVqxmI.mp4 Haskell_Amuse_Bouche.mp4+ # git mv kitenet.net_~joey_screencasts_git-annex_coding_in_haskell.ogg git-annex_coding_in_haskell.ogg+ # git commit -m 'better filenames'++Now push your changes back to the central repository. This first time,+remember to push the git-annex branch, which is used to track the file+contents.++ # git push origin master git-annex+ To git@github.com:joeyh/techtalks.git+ * [new branch] master -> master+ * [new branch] git-annex -> git-annex++That push went fast, because it didn't upload large videos to GitHub.+To check this, you can ask git-annex where the contents of the videos are:++ # git annex whereis+ whereis Haskell_Amuse_Bouche.mp4 (1 copy) + 767e8558-0955-11e1-be83-cbbeaab7fff8 -- here+ ok+ whereis git-annex_coding_in_haskell.ogg (2 copies) + 00000000-0000-0000-0000-000000000001 -- web+ 767e8558-0955-11e1-be83-cbbeaab7fff8 -- here+ ok++## make more checkouts++So far you have a central repository, and a checkout on a laptop.+Let's make another checkout that's used as a backup. You can put it anywhere+you like, just make it be somewhere your laptop can access. A few options:++* Put it on a USB drive that you can plug into the laptop.+* Put it on a desktop.+* Put it on some server in the local network.+* Put it on a remote VPS.++I'll use the VPS option, but these instructions should work for+any of the above.++ # ssh server+ server# sudo apt-get install git-annex++Clone the central repository as before. (If the clone fails, you need+to add your server's ssh public key to github -- see+[this page](http://help.github.com/ssh-issues/).)++ server# git clone git@github.com:joeyh/techtalks.git+ server# cd techtalks+ server# git config remote.origin.annex-ignore true+ server# git annex init 'backup'+ init backup (merging origin/git-annex into git-annex...) ok++Notice that the server does not have the contents of any of the files yet.+If you run `ls`, you'll see broken symlinks. We want to populate this+backup with the file contents, by copying them from your laptop.++Back on your laptop, you need to configure a git remote for the backup.+Adjust the ssh url as needed to point to wherever the backup is. (If it+was on a local USB drive, you'd use the path to the repository instead.)++ # git remote add backup ssh://server/~/techtalks++Now git-annex on your laptop knows how to reach the backup repository,+and can do things like copy files to it:++ # git annex copy --to backup git-annex_coding_in_haskell.ogg+ copy git-annex_coding_in_haskell.ogg (checking backup...)+ 12877824 2% 255.11kB/s 00:00+ ok++You can also `git annex move` files to it, to free up space on your laptop.+And then you can `git annex get` files back to your laptop later on, as+desired.++After you use git-annex to move files around, remember to push, +which will broadcast its updated location information.++ # git push++## take it farther++Of course you can create as many checkouts as you desire. If you have a+desktop machine too, you can make a checkout there, and use `git remote+add` to also let your desktop access the backup repository. ++You can add remotes for each direct connection between machines you find you+need -- so make the laptop have the desktop as a remote, and the desktop+have the laptop as a remote, and then on either machine git-annex can+access files stored on the other.
+ doc/tips/migrating_data_to_a_new_backend.mdwn view
@@ -0,0 +1,16 @@+Maybe you started out using the WORM backend, and have now configured+git-annex to use SHA1. But files you added to the annex before still+use the WORM backend. There is a simple command that can migrate that+data:++ # git annex migrate my_cool_big_file+ migrate my_cool_big_file (checksum...) ok++You can only migrate files whose content is currently available. Other+files will be skipped.++After migrating a file to a new backend, the old content in the old backend+will still be present. That is necessary because multiple files+can point to the same content. The `git annex unused` subcommand can be+used to clear up that detritus later. Note that hard links are used,+to avoid wasting disk space.
+ doc/tips/powerful_file_matching.mdwn view
@@ -0,0 +1,36 @@+git-annex has a powerful syntax for making it act on only certian files.++The simplest thing is to exclude some files, using wild cards:++ git annex get --exclude '*.mp3' --exclude '*.ogg'++But you can also exclude files that git-annex's [[location_tracking]]+information indicates are present in a given repository. For example,+if you want to populate newarchive with files, but not those already+on oldarchive, you could do it like this:++ git annex copy --not --in oldarchive --to newarchive++Without the --not, --in makes it act on files that *are* in the specified+repository. So, to remove files that are on oldarchive:++ git annex drop --in oldarchive++Or maybe you're curious which files have a lot of copies, and then+also want to know which files have only one copy:++ git annex find --copies 7+ git annex find --not --copies 2++The above are the simple examples of specifying what files git-annex+should act on. But you can specify anything you can dream up by combining+the things above, with --and --or -( and -). Those last two strange-looking+options are parentheses, for grouping other options. You will probably+have to escape them from your shell.++Here are the mp3 files that are in either of two repositories, but have+less than 3 copies:++ git annex find --not --exclude '*.mp3' --and \+ -\( --in usbdrive --or --in archive -\) --and \+ --not --copies 3
+ doc/tips/recover_data_from_lost+found.mdwn view
@@ -0,0 +1,19 @@+Suppose something goes wrong, and fsck puts all the files in lost+found.+It's actually very easy to recover from this disaster.++First, check out the git repository again. Then, in the new checkout:++ $ mkdir recovered-content+ $ sudo mv ../lost+found/* recovered-content+ $ sudo chown you:you recovered-content+ $ chmod -R u+w recovered-content+ $ git annex add recovered-content+ $ git rm recovered-content+ $ git commit -m "recovered some content"+ $ git annex fsck++The way that works is that when git-annex adds the same content that was in+the repository before, all the old links to that content start working+again. This works particularly well if the SHA* backends are used, but even+with the default backend it will work pretty well, as long as fsck+preserved the modification time of the files.
+ doc/tips/untrusted_repositories.mdwn view
@@ -0,0 +1,28 @@+Suppose you have a USB thumb drive and are using it as a git annex+repository. You don't trust the drive, because you could lose it, or+accidentally run it through the laundry. Or, maybe you have a drive that+you know is dying, and you'd like to be warned if there are any files+on it not backed up somewhere else. Maybe the drive has already died+or been lost.++You can let git-annex know that you don't trust a repository, and it will+adjust its behavior to avoid relying on that repositories's continued+availability.+ + # git annex untrust usbdrive+ untrust usbdrive ok++Now when you do a fsck, you'll be warned appropriately:++ # git annex fsck .+ fsck my_big_file+ Only these untrusted locations may have copies of this file!+ 05e296c4-2989-11e0-bf40-bad1535567fe -- portable USB drive+ Back it up to trusted locations with git-annex copy.+ failed++Also, git-annex will refuse to drop a file from elsewhere just because+it can see a copy on the untrusted repository.++It's also possible to tell git-annex that you have an unusually high+level of trust for a repository. See [[trust]] for details.
+ doc/tips/using_Amazon_S3.mdwn view
@@ -0,0 +1,37 @@+git-annex extends git's usual remotes with some [[special_remotes]], that+are not git repositories. This way you can set up a remote using say,+Amazon S3, and use git-annex to transfer files into the cloud.++First, export your S3 credentials:++ # export ANNEX_S3_ACCESS_KEY_ID="08TJMT99S3511WOZEP91"+ # export ANNEX_S3_SECRET_ACCESS_KEY="s3kr1t"++Now, create a gpg key, if you don't already have one. This will be used+to encrypt everything stored in S3, for your privacy. Once you have+a gpg key, run `gpg --list-secret-keys` to look up its key id, something+like "2512E3C7"++Next, create the S3 remote, and describe it. ++ # git annex initremote cloud type=S3 encryption=2512E3C7+ initremote cloud (encryption setup with gpg key C910D9222512E3C7) (checking bucket) (creating bucket in US) (gpg) ok+ # git annex describe cloud "at Amazon's US datacenter"+ describe cloud ok++The configuration for the S3 remote is stored in git. So to make another+repository use the same S3 remote is easy:++ # cd /media/usb/annex+ # git pull laptop+ # git annex initremote cloud+ initremote cloud (gpg) (checking bucket) ok++Now the remote can be used like any other remote.++ # git annex copy my_cool_big_file --to cloud+ copy my_cool_big_file (gpg) (checking cloud...) (to cloud...) ok+ # git annex move video/hackity_hack_and_kaxxt.mov --to cloud+ move video/hackity_hack_and_kaxxt.mov (checking cloud...) (to cloud...) ok++See [[special_remotes/S3]] for details.
+ doc/tips/using_gitolite_with_git-annex.mdwn view
@@ -0,0 +1,77 @@+[Gitolite](https://github.com/sitaramc/gitolite) is a git repository+manager. Here's how to add git-annex support to gitolite, so you can+`git annex copy` files to a gitolite repository, and `git annex get`+files from it.++A nice feature of using gitolite with git-annex is that users can be given+read-only access to a repository, and this allows them to `git annex get`+file contents, but not change anything.++First, you need new enough versions:++* gitolite 2.2 is needed -- this version contains a git-annex-shell ADC+ and supports "ua" ADCs.+* git-annex 3.20111016 or newer needs to be installed on the gitolite+ server. Don't install an older version, it wouldn't be secure!++And here's how to set it up. The examples are for gitolite as installed+on Debian with apt-get, but the changes described can be made to any+gitolite installation, just with different paths.++Set `$GL_ADC_PATH` in `.gitolite.rc`, if you have not already done so.++<pre>+echo '$GL_ADC_PATH = "/usr/local/lib/gitolite/adc/;' >>~gitolite/.gitolite.rc+</pre>++Make the ADC directory, and a "ua" subdirectory.++<pre> +mkdir -p /usr/local/lib/gitolite/adc/ua+</pre>++Install the git-annex-shell ADC into the "ua" subdirectory and make it+executable.++<pre> +cd /usr/local/lib/gitolite/adc/ua/+wget https://raw.github.com/sitaramc/gitolite/pu/contrib/adc/git-annex-shell+chmod +x git-annex-shell+</pre>++Now all gitolite repositories can be used with git-annex just as any+ssh remote normally would be used. For example:++<pre>+# git clone gitolite@localhost:testing+Cloning into testing...+Receiving objects: 100% (18/18), done.+# cd testing+# git annex init+init ok+# cp /etc/passwd my-cool-big-file+# git annex add my-cool-big-file+add my-cool-big-file ok+(Recording state in git...)+# git commit -m added+[master d36c8b4] added+ 1 files changed, 1 insertions(+), 0 deletions(-)+ create mode 120000 my-cool-big-file+# git push --all+Counting objects: 17, done.+Delta compression using up to 2 threads.+Compressing objects: 100% (12/12), done.+Writing objects: 100% (14/14), 1.39 KiB, done.+Total 14 (delta 0), reused 1 (delta 0)+To gitolite@localhost:testing+ c552a38..db4653e git-annex -> git-annex+ 29cd204..d36c8b4 master -> master+# git annex copy --to origin+copy my-cool-big-file (checking origin...) (to origin...) +WORM-s2502-m1318875140--my-cool-big-file+ 2502 100% 0.00kB/s 0:00:00 (xfer#1, to-check=0/1)++sent 2606 bytes received 31 bytes 1758.00 bytes/sec+total size is 2502 speedup is 0.95+ok+</pre>
+ doc/tips/using_the_SHA1_backend.mdwn view
@@ -0,0 +1,11 @@+A handy alternative to the default [[backend|backends]] is the+SHA1 backend. This backend provides more git-style assurance that your data+has not been damaged. And the checksum means that when you add the same+content to the annex twice, only one copy need be stored in the backend.++The only reason it's not the default is that it needs to checksum+files when they're added to the annex, and this can slow things down+significantly for really big files. To make SHA1 the default, just+add something like this to `.gitattributes`:++ * annex.backend=SHA1
+ doc/tips/using_the_web_as_a_special_remote.mdwn view
@@ -0,0 +1,32 @@+The web can be used as a [[special_remote|special_remotes]] too.++ # git annex addurl http://example.com/video.mpeg+ addurl example.com_video.mpeg (downloading http://example.com/video.mpeg)+ ########################################################## 100.0%+ ok++Now the file is downloaded, and has been added to the annex like any other+file. So it can be renamed, copied to other repositories, and so on.++Note that git-annex assumes that, if the web site does not 404, the file is+still present on the web, and this counts as one [[copy|copies]] of the+file. So it will let you remove your last copy, trusting it can be+downloaded again:++ # git annex drop example.com_video.mpeg+ drop example.com_video.mpeg (checking http://example.com/video.mpeg) ok++If you don't [[trust]] the web to this degree, just let git-annex know:++ # git annex untrust web+ untrust web ok++With the result that it will hang onto files:++ # git annex drop example.com_video.mpeg+ drop example.com_video.mpeg (unsafe) + Could only verify the existence of 0 out of 1 necessary copies+ Also these untrusted repositories may contain the file:+ 00000000-0000-0000-0000-000000000001 -- web+ (Use --force to override this check, or adjust annex.numcopies.)+ failed
+ doc/tips/what_to_do_when_a_repository_is_corrupted.mdwn view
@@ -0,0 +1,22 @@+A git-annex repository on a removable USB drive is great, until the cable+falls out at the wrong time and git's repository gets trashed. The way+git checksums everything and the poor quality of USB media makes this+perhaps more likely than you would expect. If this happens to you,+here's a way to recover that makes the most of whatever data is left+on the drive.++* First, run `git fsck`. If it does not report any problems, your data+ is fine, and you don't need to proceed further.+* So `git fsck` says the git repository is corrupted. But probably the data+ git-annex stored is fine. Your first step is to clone another copy+ of the git repository from somewhere else. Let's call this clone+ "$good", and the corrupted repository "$bad".+* Preserve your git configuration changes, and the `annex.uuid` setting:+ `mv $bad/.git/config $good/.git/config`+* Move annexed data into the new repository: `mkdir $good/.git/annex; mv+ $bad/.git/annex/objects $good/.git/annex/objects`+* Reinitalize git-annex: `cd $good; git annex init`+* Check for any problems with the annexed data: `cd $good; git annex fsck`+* Now you can remove the corrupted repository, the new one is ready to use.++--[[Joey]]
+ doc/tips/what_to_do_when_you_lose_a_repository.mdwn view
@@ -0,0 +1,19 @@+So you lost a thumb drive containing a git-annex repository. Or a hard+drive died or some other misfortune has befallen your data.++Unless you configured backups, git-annex can't get your data back. But it+can help you deal with the loss.++First, go somewhere that knows about the lost repository, and mark it as+untrusted.++ git annex untrust usbdrive++To remind yourself later what happened, you can change its description, too:++ git annex describe usbdrive "USB drive lost in Timbuktu. Probably gone forever."++This retains the [[location_tracking]] information for the repository.+Maybe you'll find the drive later. Maybe that's impossible. Either way,+this lets git-annex tell you why a file is no longer accessible, and+it avoids it relying on that drive to hold any content.
doc/todo/auto_remotes.mdwn view
@@ -21,3 +21,9 @@ (If not just on init.) Whenever it reads in a repo's remotes? > This sounds useful and the log should be updated every time any remote is being accessed. A counter or timestamp (yes, distributed times may be wrong/different) could be used to auto-prune old entries via a global and per-remote config setting. -- RichiH++---++I no longer think I'd use this myself, I find that my repositories quickly+grow the paths I actually use, somewhat organically. Unofficial paths+across university quads come to mind. [[done]] --[[Joey]]
+ doc/todo/avoid_unnecessary_union_merges.mdwn view
@@ -0,0 +1,20 @@+Some commands cause a union merge unnecessarily. For example, `git annex add`+modifies the location log, which first requires reading the current log (if+any), which triggers a merge.++Would be good to avoid these unnecessary union merges. First because it's+faster and second because it avoids a possible delay when a user might+ctrl-c and leave the repo in an inconsistent state. In the case of an add,+the file will be in the annex, but no location log will exist for it (fsck+fixes that).++It may be that all that's needed is to modify Annex.Branch.change+to read the current value, without merging. Then commands like `get`, that+query the branch, will still cause merges, and commands like `add` that+only modify it, will not. Note that for a command like `get`, the merge+occurs before it has done anything, so ctrl-c should not be a problem+there.++This is a delicate change, I need to take care.. --[[Joey]]++> [[done]] (assuming I didn't miss any cases where this is not safe!) --[[Joey]]
doc/todo/cache_key_info.mdwn view
@@ -7,13 +7,14 @@ to save git add from piling up too much in the index.) But currently two git-annex commands are quite slow when annexes become large-in quantity of files. These are unused and stats+in quantity of files. These are unused and status. (Both have --fast versions that don't do as much).+> (Update: status has become acceptably fast; most of its slowdown was due to using a bad data structure; scanning the tree is not particularly slow and it no longer looks at the git-annex branch.) -Both are slow because both need two pieces of information that are not+unused is slow because it needs two pieces of information that are not quick to look up, and require examining the whole repo, very seekily: -1. The keys present in the annex. Found by looking thru .git/annex/objects.+1. The keys present in the annex. Found by looking thru .git/annex/objects 2. The keys referenced by files in git. Found by finding every file in git, and looking at its symlink.
doc/todo/git-annex_unused_eats_memory.mdwn view
@@ -2,12 +2,14 @@ (all keys with content present in the repository, with all keys used by files in the repository), and so uses more memory than git-annex typically needs; around-60-80 mb when run in a repository with 80 thousand files.+50 mb when run in a repository with 80 thousand files. +(Used to be 80 mb, but implementation improved.)+ I would like to reduce this. One idea is to use a bloom filter. For example, construct a bloom filter of all keys used by files in the repository. Then for each key with content present, check if it's-in the bloom filter. Since there can be false negatives, this might+in the bloom filter. Since there can be false positives, this might miss finding some unused keys. The probability/size of filter could be tunable.
+ doc/todo/gitolite_and_gitosis_support.mdwn view
@@ -0,0 +1,39 @@+gitosis and gitolite should support git-annex being used to send/receive+files from the repositories they manage. Users with read-only access+could only get files, while users with write access could also put and drop+files.++Doing this right requires modifying both programs, to add [[git-annex-shell]]+to the list of things they can run, and only allow through appropriate+git-annex-shell subcommands to read-only users.++I have posted an RFC for modifying gitolite to the+[gitolite mailing list](http://groups.google.com/group/gitolite?lnk=srg).++> I have not developed a patch yet, but all that git-annex needs is a way+> to ssh to the server and run the git-annex-shell command there.+> git-annex-shell is very similar to git-shell. So, one way to enable+> it is simply to set GL_ADC_PATH to a directory containing git-annex-shell.+> +> But, that's not optimal, since git-annex-shell will send off receive-pack+> commands to git, which would bypass gitolite's permissions checking.+> Also, it makes sense to limit readonly users to only download, not+> upload/delete files from git-annex. Instead, I suggest adding something+> like this to gitolite's config:+ + # If set, users with W access can write file contents into the git-annex,+ # and users with R access can read file contents from the git-annex.+ $GL_GIT_ANNEX = 0;++> If this makes sense, I'm sure I can put a patch together for your+> review. It would involve modifying gl-auth-command so it knows how+> to run git-annex-shell, and how to parse out the "verb" from a+> git-annex-shell command line, and modifying R_COMMANDS and W_COMMANDS.++As I don't write python, someone else is needed to work on gitosis.+--[[Joey]] ++> [[done]]; support for gitolite is in its `pu` branch, and some changes+> made to git-annefor gitolite is in its `pu` branch, and some changes+> made to git-annex. Word is gitosis is not being maintained so I won't+> worry about try to support it. --[[Joey]]
+ doc/todo/optimise_git-annex_merge.mdwn view
@@ -0,0 +1,23 @@+Typically `git-annex merge` is fast, but it could still be sped up.++`git-annex merge` runs `git-hash-object` once per file that needs to be+merged. Elsewhere in git-annex, `git-hash-object` is used in a faster mode,+reading files from disk via `--stdin-paths`. But here, the data is not+in raw files on disk, and I doubt writing them is the best approach.+Instead, I'd like a way to stream multiple objects into git using stdin.+Sometime, should look at either extending git-hash-object to support that,+or possibly look at using git-fast-import instead.++--- ++`git-annex merge` also runs `git show` once per file that needs to be+merged. This could be reduced to a single call to `git-cat-file --batch`,+There is already a Git.CatFile library that can do this easily. --[[Joey]]++> This is now done, part above remains todo. --[[Joey]] ++---++Merging used to use memory proportional to the size of the diff. It now+streams data, running in constant space. This probably sped it up a lot,+as there's much less allocation and GC action. --[[Joey]]
doc/todo/smudge.mdwn view
@@ -11,6 +11,10 @@ So every `git status` would need to read the entire content of all available files, and checksum them, which is too expensive. +> Update from GitTogether: Peff thinks a new interface could be added to+> git to handle this sort of case in an efficient way.. just needs someone+> to do the work. --[[Joey]] + ---- The clean filter is run when files are staged for commit. So a user could copy
+ doc/todo/support_fsck_in_bare_repos.mdwn view
@@ -0,0 +1,17 @@+What is says on the tin:++ 22:56:54 < RichiH> joeyh_: by the way, i have been thinking about fsck on bare repos+ 22:57:37 < RichiH> joeyh_: the best i could come with is to have a bare and a non-bare access the same repo store+ 22:58:00 < RichiH> joeyh_: alternatively, with the SHA* backend, you have all the information to verify that the local data is correct+ 22:58:41 < RichiH> and verifying that would already be a plus. if there really _is_ a problem, having the SHA is enough to track issues down+ 23:09:50 < joeyh_> oh, I think I have code that fsck could use on bare repos already.. just a matter of wiring it up+ 23:10:42 < joeyh_> feel free to reopen a bug or whatever so I remember.. the unused command's branch content enumeration could be used in a bare repo+ 23:14:51 < joeyh_> unused/dropunused could work in bare repos too btw++> Also `status`'s total annex keys/size could be handled for bare repos. --[[Joey]] ++>> Fsck is done. Rest not done yet. --[[Joey]]++>>> all [[done]]! --[[Joey]] ++[[!meta title="support unused, dropunused in bare repos"]]
doc/todo/wishlist:___34__git_annex_add__34___multiple_processes.mdwn view
@@ -5,3 +5,6 @@ Anyway, thanks for this wonderful piece of software. Jean-Baptiste++> closing as dup of [[parallel possibilities]] (also see comments below)+> [[done]] --[[Joey]]
doc/upgrades/SHA_size.mdwn view
@@ -15,6 +15,6 @@ git annex migrate git commit -m 'migrated keys for v2' -The usual caveats about [[walkthrough/migrating_data_to_a_new_backend]]+The usual caveats about [[tips/migrating_data_to_a_new_backend]] apply; you will end up with unused keys that you can later clean up with `git annex unused`.
− doc/walkthrough/Internet_Archive_via_S3.mdwn
@@ -1,49 +0,0 @@-[The Internet Archive](http://www.archive.org/) allows members to upload-collections using an Amazon S3 -[compatible API](http://www.archive.org/help/abouts3.txt), and this can-be used with git-annex's [[special_remotes/S3]] support. --So, you can locally archive things with git-annex, define remotes that-correspond to "items" at the Internet Archive, and use git-annex to upload-your files to there. Of course, your use of the Internet Archive must-comply with their [terms of service](http://www.archive.org/about/terms.php).--Sign up for an account, and get your access keys here:-<http://www.archive.org/account/s3.php>- - # export AWS_ACCESS_KEY_ID=blahblah- # export AWS_SECRET_ACCESS_KEY=xxxxxxx--Specify `host=s3.us.archive.org` when doing `initremote` to set up-a remote at the Archive. This will enable a special Internet Archive mode:-Encryption is not allowed; you are required to specify a bucket name-rather than having git-annex pick a random one; and you can optionally-specify `x-archive-meta*` headers to add metadata as explained in their-[documentation](http://www.archive.org/help/abouts3.txt).--[[!template id=note text="""-/!\ There seems to be a bug in either hS3 or the archive that breaks-authentication when the bucket name contains spaces or upper-case letters..-use all lowercase and no spaces when making the bucket with `initremote`.-"""]]-- # git annex initremote archive-panama type=S3 \- host=s3.us.archive.org bucket=panama-canal-lock-blueprints \- x-archive-meta-mediatype=texts x-archive-meta-language=eng \- x-archive-meta-title="original Panama Canal lock design blueprints"- initremote archive-panama (Internet Archive mode) ok- # git annex describe archive-panama "a man, a plan, a canal: panama"- describe archive-panama ok--Then you can annex files and copy them to the remote as usual:-- # git annex add photo1.jpeg --backend=SHA1E- add photo1.jpeg (checksum...) ok- # git annex copy photo1.jpeg --fast --to archive-panama- copy (to archive-panama...) ok--Note the use of the SHA1E [[backend|backends]]. It makes most sense -to use the WORM or SHA1E backend for files that will be stored in-the Internet Archive, since the key name will be exposed as the filename-there, and since the Archive does special processing of files based on-their extension.
doc/walkthrough/adding_files.mdwn view
@@ -2,8 +2,8 @@ # cp /tmp/big_file . # cp /tmp/debian.iso . # git annex add .- add big_file ok- add debian.iso ok+ add big_file (checksum...) ok+ add debian.iso (checksum...) ok # git commit -a -m added When you add a file to the annex and commit it, only a symlink to
− doc/walkthrough/migrating_data_to_a_new_backend.mdwn
@@ -1,16 +0,0 @@-Maybe you started out using the WORM backend, and have now configured-git-annex to use SHA1. But files you added to the annex before still-use the WORM backend. There is a simple command that can migrate that-data:-- # git annex migrate my_cool_big_file- migrate my_cool_big_file (checksum...) ok--You can only migrate files whose content is currently available. Other-files will be skipped.--After migrating a file to a new backend, the old content in the old backend-will still be present. That is necessary because multiple files-can point to the same content. The `git annex unused` subcommand can be-used to clear up that detritus later. Note that hard links are used,-to avoid wasting disk space.
doc/walkthrough/more.mdwn view
@@ -1,4 +1,3 @@ So ends the walkthrough. By now you should be able to use git-annex. -Want more? See the [[cheatsheet]] for info about all of git-annex's hidden-features.+Want more? See [[tips]] for lots more features and advice.
doc/walkthrough/moving_file_content_between_repositories.mdwn view
@@ -9,5 +9,5 @@ move my_cool_big_file (to usbdrive...) ok # git annex move video/hackity_hack_and_kaxxt.mov --from fileserver move video/hackity_hack_and_kaxxt.mov (from fileserver...)- WORM-s86050597-m1274316523--hackity_hack_and_kax 100% 82MB 199.1KB/s 07:02+ SHA256-s86050597--6ae2688bc533437766a48aa19f2c06be14d1bab9c70b468af445d4f07b65f41e 100% 82MB 199.1KB/s 07:02 ok
− doc/walkthrough/powerful_file_matching.mdwn
@@ -1,36 +0,0 @@-git-annex has a powerful syntax for making it act on only certian files.--The simplest thing is to exclude some files, using wild cards:-- git annex get --exclude '*.mp3' --exclude '*.ogg'--But you can also exclude files that git-annex's [[location_tracking]]-information indicates are present in a given repository. For example,-if you want to populate newarchive with files, but not those already-on oldarchive, you could do it like this:-- git annex copy --not --in oldarchive --to newarchive--Without the --not, --in makes it act on files that *are* in the specified-repository. So, to remove files that are on oldarchive:-- git annex drop --in oldarchive--Or maybe you're curious which files have a lot of copies, and then-also want to know which files have only one copy:-- git annex find --copies 7- git annex find --not --copies 2--The above are the simple examples of specifying what files git-annex-should act on. But you can specify anything you can dream up by combining-the things above, with --and --or -( and -). Those last two strange-looking-options are parentheses, for grouping other options. You will probably-have to escape them from your shell.--Here are the mp3 files that are in either of two repositories, but have-less than 3 copies:-- git annex find --not --exclude '*.mp3' --and \- -\( --in usbdrive --or --in archive -\) --and \- --not --copies 3
− doc/walkthrough/recover_data_from_lost+found.mdwn
@@ -1,19 +0,0 @@-Suppose something goes wrong, and fsck puts all the files in lost+found.-It's actually very easy to recover from this disaster.--First, check out the git repository again. Then, in the new checkout:-- $ mkdir recovered-content- $ sudo mv ../lost+found/* recovered-content- $ sudo chown you:you recovered-content- $ chmod -R u+w recovered-content- $ git annex add recovered-content- $ git rm recovered-content- $ git commit -m "recovered some content"- $ git annex fsck--The way that works is that when git-annex adds the same content that was in-the repository before, all the old links to that content start working-again. This works particularly well if the SHA* backends are used, but even-with the default backend it will work pretty well, as long as fsck-preserved the modification time of the files.
− doc/walkthrough/untrusted_repositories.mdwn
@@ -1,28 +0,0 @@-Suppose you have a USB thumb drive and are using it as a git annex-repository. You don't trust the drive, because you could lose it, or-accidentally run it through the laundry. Or, maybe you have a drive that-you know is dying, and you'd like to be warned if there are any files-on it not backed up somewhere else. Maybe the drive has already died-or been lost.--You can let git-annex know that you don't trust a repository, and it will-adjust its behavior to avoid relying on that repositories's continued-availability.- - # git annex untrust usbdrive- untrust usbdrive ok--Now when you do a fsck, you'll be warned appropriately:-- # git annex fsck .- fsck my_big_file- Only these untrusted locations may have copies of this file!- 05e296c4-2989-11e0-bf40-bad1535567fe -- portable USB drive- Back it up to trusted locations with git-annex copy.- failed--Also, git-annex will refuse to drop a file from elsewhere just because-it can see a copy on the untrusted repository.--It's also possible to tell git-annex that you have an unusually high-level of trust for a repository. See [[trust]] for details.
doc/walkthrough/unused_data.mdwn view
@@ -1,8 +1,8 @@-It's possible for data to accumulate in the annex that no files point to-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 key-value [[backends|backend]].+It's possible for data to accumulate in the annex that no files in any+branch point to 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 key-value [[backends]]. 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@@ -12,8 +12,8 @@ unused . (checking for unused data...) Some annexed data is no longer used by any files in the repository. NUMBER KEY- 1 WORM-s3-m1289672605--file- 2 WORM-s14-m1289672605--file+ 1 SHA256-s86050597--6ae2688bc533437766a48aa19f2c06be14d1bab9c70b468af445d4f07b65f41e+ 2 SHA1-s14--f1358ec1873d57350e3dc62054dc232bc93c2bd1 (To see where data was previously used, try: git log --stat -S'KEY') (To remove unwanted data: git-annex dropunused NUMBER) ok
− doc/walkthrough/using_Amazon_S3.mdwn
@@ -1,37 +0,0 @@-git-annex extends git's usual remotes with some [[special_remotes]], that-are not git repositories. This way you can set up a remote using say,-Amazon S3, and use git-annex to transfer files into the cloud.--First, export your S3 credentials:-- # export ANNEX_S3_ACCESS_KEY_ID="08TJMT99S3511WOZEP91"- # export ANNEX_S3_SECRET_ACCESS_KEY="s3kr1t"--Now, create a gpg key, if you don't already have one. This will be used-to encrypt everything stored in S3, for your privacy. Once you have-a gpg key, run `gpg --list-secret-keys` to look up its key id, something-like "2512E3C7"--Next, create the S3 remote, and describe it. -- # git annex initremote cloud type=S3 encryption=2512E3C7- initremote cloud (encryption setup with gpg key C910D9222512E3C7) (checking bucket) (creating bucket in US) (gpg) ok- # git annex describe cloud "at Amazon's US datacenter"- describe cloud ok--The configuration for the S3 remote is stored in git. So to make another-repository use the same S3 remote is easy:-- # cd /media/usb/annex- # git pull laptop- # git annex initremote cloud- initremote cloud (gpg) (checking bucket) ok--Now the remote can be used like any other remote.-- # git annex copy my_cool_big_file --to cloud- copy my_cool_big_file (gpg) (checking cloud...) (to cloud...) ok- # git annex move video/hackity_hack_and_kaxxt.mov --to cloud- move video/hackity_hack_and_kaxxt.mov (checking cloud...) (to cloud...) ok--See [[special_remotes/S3]] for details.
doc/walkthrough/using_ssh_remotes.mdwn view
@@ -13,7 +13,7 @@ # git annex get my_cool_big_file get my_cool_big_file (getting UUID for origin...) (from origin...)- WORM-s2159-m1285650548--my_cool_big_file 100% 2159 2.1KB/s 00:00+ SHA256-s86050597--6ae2688bc533437766a48aa19f2c06be14d1bab9c70b468af445d4f07b65f41e 100% 2159 2.1KB/s 00:00 ok When you drop files, git-annex will ssh over to the remote and make
− doc/walkthrough/using_the_SHA1_backend.mdwn
@@ -1,11 +0,0 @@-A handy alternative to the default [[backend|backends]] is the-SHA1 backend. This backend provides more git-style assurance that your data-has not been damaged. And the checksum means that when you add the same-content to the annex twice, only one copy need be stored in the backend.--The only reason it's not the default is that it needs to checksum-files when they're added to the annex, and this can slow things down-significantly for really big files. To make SHA1 the default, just-add something like this to `.gitattributes`:-- * annex.backend=SHA1
− doc/walkthrough/using_the_web.mdwn
@@ -1,32 +0,0 @@-The web can be used as a [[special_remote|special_remotes]] too.-- # git annex addurl http://example.com/video.mpeg- addurl example.com_video.mpeg (downloading http://example.com/video.mpeg)- ########################################################## 100.0%- ok--Now the file is downloaded, and has been added to the annex like any other-file. So it can be renamed, copied to other repositories, and so on.--Note that git-annex assumes that, if the web site does not 404, the file is-still present on the web, and this counts as one [[copy|copies]] of the-file. So it will let you remove your last copy, trusting it can be-downloaded again:-- # git annex drop example.com_video.mpeg- drop example.com_video.mpeg (checking http://example.com/video.mpeg) ok--If you don't [[trust]] the web to this degree, just let git-annex know:-- # git annex untrust web- untrust web ok--With the result that it will hang onto files:-- # git annex drop example.com_video.mpeg- drop example.com_video.mpeg (unsafe) - Could only verify the existence of 0 out of 1 necessary copies- Also these untrusted repositories may contain the file:- 00000000-0000-0000-0000-000000000001 -- web- (Use --force to override this check, or adjust annex.numcopies.)- failed
− doc/walkthrough/what_to_do_when_you_lose_a_repository.mdwn
@@ -1,19 +0,0 @@-So you lost a thumb drive containing a git-annex repository. Or a hard-drive died or some other misfortune has befallen your data.--Unless you configured backups, git-annex can't get your data back. But it-can help you deal with the loss.--First, go somewhere that knows about the lost repository, and mark it as-untrusted.-- git annex untrust usbdrive--To remind yourself later what happened, you can change its description, too:-- git annex describe usbdrive "USB drive lost in Timbuktu. Probably gone forever."--This retains the [[location_tracking]] information for the repository.-Maybe you'll find the drive later. Maybe that's impossible. Either way,-this lets git-annex tell you why a file is no longer accessible, and-it avoids it relying on that drive to hold any content.
git-annex-shell.hs view
@@ -12,8 +12,7 @@ import qualified Git import CmdLine import Command-import Options-import UUID+import Annex.UUID import qualified Command.ConfigList import qualified Command.InAnnex@@ -21,26 +20,37 @@ import qualified Command.RecvKey import qualified Command.SendKey -cmds :: [Command]-cmds = map adddirparam $ concat- [ Command.ConfigList.command- , Command.InAnnex.command- , Command.DropKey.command- , Command.RecvKey.command- , Command.SendKey.command+cmds_readonly :: [Command]+cmds_readonly = concat+ [ Command.ConfigList.def+ , Command.InAnnex.def+ , Command.SendKey.def ]++cmds_notreadonly :: [Command]+cmds_notreadonly = concat+ [ Command.RecvKey.def+ , Command.DropKey.def+ ]++cmds :: [Command]+cmds = map adddirparam $ cmds_readonly ++ cmds_notreadonly where adddirparam c = c { cmdparams = "DIRECTORY " ++ cmdparams c } options :: [OptDescr (Annex ())]-options = uuid : commonOptions+options = commonOptions +++ [ Option [] ["uuid"] (ReqArg checkuuid paramUUID) "repository uuid"+ ] where- uuid = Option [] ["uuid"] (ReqArg check paramUUID) "repository uuid"- check expected = do- u <- getUUID- when (u /= expected) $ error $- "expected repository UUID " ++ expected- ++ " but found UUID " ++ u+ checkuuid expected = getUUID >>= check+ where+ check u | u == toUUID expected = return ()+ check NoUUID = unexpected "uninitialized repository"+ check u = unexpected $ "UUID " ++ fromUUID u+ unexpected s = error $+ "expected repository UUID " +++ expected ++ " but found " ++ s header :: String header = "Usage: git-annex-shell [-c] command [parameters ...] [option ..]"@@ -67,12 +77,14 @@ builtins = map cmdname cmds builtin :: String -> String -> [String] -> IO ()-builtin cmd dir params =- Git.repoAbsPath dir >>= Git.repoFromAbsPath >>=- dispatch (cmd : filterparams params) cmds options header+builtin cmd dir params = do+ checkNotReadOnly cmd+ dispatch (cmd : filterparams params) cmds options header $+ Git.repoAbsPath dir >>= Git.repoFromAbsPath external :: [String] -> IO ()-external params =+external params = do+ checkNotLimited unlessM (boolSystem "git-shell" $ map Param $ "-c":filterparams params) $ error "git-shell failed" @@ -85,3 +97,16 @@ failure :: IO () failure = error $ "bad parameters\n\n" ++ usage header cmds options++checkNotLimited :: IO ()+checkNotLimited = checkEnv "GIT_ANNEX_SHELL_LIMITED"++checkNotReadOnly :: String -> IO ()+checkNotReadOnly cmd+ | cmd `elem` map cmdname cmds_readonly = return ()+ | otherwise = checkEnv "GIT_ANNEX_SHELL_READONLY"++checkEnv :: String -> IO ()+checkEnv var =+ whenM (not . null <$> catchDefaultIO (getEnv var) "") $+ error $ "Action blocked by " ++ var
git-annex.cabal view
@@ -1,5 +1,5 @@ Name: git-annex-Version: 3.20111011+Version: 3.20111122 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: ./Git/UnionMerge.hs ./Git/LsTree.hs ./Git/LsFiles.hs ./Git/CatFile.hs ./Git/Queue.hs ./PresenceLog.hs ./Common/Annex.hs ./.gitignore ./Upgrade/V2.hs ./Upgrade/V1.hs ./Upgrade/V0.hs ./Command.hs ./test.hs ./configure.hs ./LocationLog.hs ./Limit.hs ./RemoteLog.hs ./Annex.hs ./Remote/S3real.hs ./Remote/Web.hs ./Remote/Rsync.hs ./Remote/Helper/Special.hs ./Remote/Helper/Encryptable.hs ./Remote/Hook.hs ./Remote/Git.hs ./Remote/Bup.hs ./Remote/Directory.hs ./Remote/S3stub.hs ./mdwn2man ./Makefile ./Backend/URL.hs ./Backend/SHA.hs ./Backend/WORM.hs ./CHANGELOG ./INSTALL ./Backend.hs ./git-annex.hs ./Command/Fsck.hs ./Command/PreCommit.hs ./Command/AddUrl.hs ./Command/Fix.hs ./Command/Copy.hs ./Command/Unlock.hs ./Command/InitRemote.hs ./Command/Get.hs ./Command/Map.hs ./Command/Unused.hs ./Command/FromKey.hs ./Command/Unannex.hs ./Command/Add.hs ./Command/SetKey.hs ./Command/Uninit.hs ./Command/RecvKey.hs ./Command/DropUnused.hs ./Command/Semitrust.hs ./Command/Untrust.hs ./Command/Version.hs ./Command/SendKey.hs ./Command/Whereis.hs ./Command/DropKey.hs ./Command/Find.hs ./Command/Lock.hs ./Command/InAnnex.hs ./Command/Status.hs ./Command/ConfigList.hs ./Command/Trust.hs ./Command/Move.hs ./Command/Init.hs ./Command/Migrate.hs ./Command/Merge.hs ./Command/Upgrade.hs ./Command/Drop.hs ./Command/Describe.hs ./git-annex-shell.hs ./Utility/FileMode.hs ./Utility/Ssh.hs ./Utility/RsyncFile.hs ./Utility/StatFS.hsc ./Utility/Base64.hs ./Utility/CopyFile.hs ./Utility/Path.hs ./Utility/Touch.hsc ./Utility/JSONStream.hs ./Utility/SafeCommand.hs ./Utility/Url.hs ./Utility/Dot.hs ./Utility/Matcher.hs ./Utility/Conditional.hs ./Utility/DataUnits.hs ./Git.hs ./CmdLine.hs ./Trust.hs ./debian/rules ./debian/NEWS ./debian/compat ./debian/manpages ./debian/control ./debian/doc-base ./debian/changelog ./debian/copyright ./doc/trust.mdwn ./doc/use_case/Alice.mdwn ./doc/use_case/Bob.mdwn ./doc/install/comment_4_82a17eee4a076c6c79fddeda347e0c9a._comment ./doc/install/FreeBSD.mdwn ./doc/install/Debian.mdwn ./doc/install/comment_3_cff163ea3e7cad926f4ed9e78b896598._comment ./doc/install/Ubuntu.mdwn ./doc/install/Fedora.mdwn ./doc/install/OSX/comment_1_0a1760bf0db1f1ba89bdb4c62032f631._comment ./doc/install/Debian/comment_2_648e3467e260cdf233acdb0b53313ce0._comment ./doc/install/Debian/comment_1_029486088d098c2d4f1099f2f0e701a9._comment ./doc/install/OSX.mdwn ./doc/todo/symlink_farming_commit_hook.mdwn ./doc/todo/backendSHA1.mdwn ./doc/todo/using_url_backend.mdwn ./doc/todo/smudge.mdwn ./doc/todo/wishlist:_swift_backend/comment_1_e6efbb35f61ee521b473a92674036788._comment ./doc/todo/wishlist:_swift_backend/comment_2_5d8c83b0485112e98367b7abaab3f4e3._comment ./doc/todo/branching.mdwn ./doc/todo/cache_key_info/comment_1_578df1b3b2cbfdc4aa1805378f35dc48._comment ./doc/todo/git_annex_init_:_include_repo_description_and__47__or_UUID_in_commit_message.mdwn ./doc/todo/wishlist:_swift_backend.mdwn ./doc/todo/parallel_possibilities.mdwn ./doc/todo/parallel_possibilities/comment_2_adb76f06a7997abe4559d3169a3181c3._comment ./doc/todo/parallel_possibilities/comment_1_d8e34fc2bc4e5cf761574608f970d496._comment ./doc/todo/smudge/comment_1_4ea616bcdbc9e9a6fae9f2e2795c31c9._comment ./doc/todo/smudge/comment_2_e04b32caa0d2b4c577cdaf382a3ff7f6._comment ./doc/todo/wishlist:_support_for_more_ssh_urls_.mdwn ./doc/todo/auto_remotes.mdwn ./doc/todo/support_S3_multipart_uploads.mdwn ./doc/todo/add_a_git_backend.mdwn ./doc/todo/wishlist:___34__git_annex_add__34___multiple_processes.mdwn ./doc/todo/pushpull.mdwn ./doc/todo/cache_key_info.mdwn ./doc/todo/done.mdwn ./doc/todo/exclude_files_on_a_given_remote.mdwn ./doc/todo/speed_up_fsck.mdwn ./doc/todo/network_remotes.mdwn ./doc/todo/support-non-utf8-locales.mdwn ./doc/todo/tahoe_lfs_for_reals.mdwn ./doc/todo/immutable_annexed_files.mdwn ./doc/todo/file_copy_progress_bar.mdwn ./doc/todo/use_cp_reflink.mdwn ./doc/todo/fsck.mdwn ./doc/todo/object_dir_reorg_v2.mdwn ./doc/todo/hidden_files.mdwn ./doc/todo/add_--exclude_option_to_git_annex_find.mdwn ./doc/todo/git-annex-shell.mdwn ./doc/todo/checkout.mdwn ./doc/todo/git-annex_unused_eats_memory.mdwn ./doc/todo/object_dir_reorg_v2/comment_6_8834c3a3f1258c4349d23aff8549bf35._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_4_93aada9b1680fed56cc6f0f7c3aca5e5._comment ./doc/todo/object_dir_reorg_v2/comment_1_ba03333dc76ff49eccaba375e68cb525._comment ./doc/todo/object_dir_reorg_v2/comment_2_81276ac309959dc741bc90101c213ab7._comment ./doc/todo/wishlist:_Prevent_repeated_password_prompts_for_one_command.mdwn ./doc/todo/gitrm.mdwn ./doc/todo/rsync.mdwn ./doc/todo/S3.mdwn ./doc/todo/tahoe_lfs_for_reals/comment_1_0a4793ce6a867638f6e510e71dd4bb44._comment ./doc/todo/tahoe_lfs_for_reals/comment_2_80b9e848edfdc7be21baab7d0cef0e3a._comment ./doc/todo/wishlist:_Provide_a___34__git_annex__34___command_that_will_skip_duplicates.mdwn ./doc/todo/union_mounting.mdwn ./doc/todo/auto_remotes/discussion.mdwn ./doc/repomap.png ./doc/users/joey.mdwn ./doc/users/chrysn.mdwn ./doc/users/fmarier.mdwn ./doc/encryption.mdwn ./doc/cheatsheet.mdwn ./doc/upgrades/SHA_size.mdwn ./doc/news/version_3.20110928.mdwn ./doc/news/sharebox_a_FUSE_filesystem_for_git-annex.mdwn ./doc/news/version_3.20110906.mdwn ./doc/news/version_3.20111011.mdwn ./doc/news/version_3.20110902.mdwn ./doc/news/LWN_article.mdwn ./doc/news/version_3.20110915.mdwn ./doc/download.mdwn ./doc/distributed_version_control.mdwn ./doc/special_remotes.mdwn ./doc/design.mdwn ./doc/users.mdwn ./doc/internals.mdwn ./doc/download/comment_2_f85f72b33aedc3425f0c0c47867d02f3._comment ./doc/download/comment_4_10fc013865c7542c2ed9d6c0963bb391._comment ./doc/download/comment_5_c6b1bc40226fc2c8ba3e558150856992._comment ./doc/download/comment_8_59a976de6c7d333709b92f7cd5830850._comment ./doc/download/comment_7_a5eebd214b135f34b18274a682211943._comment ./doc/download/comment_6_3a52993d3553deb9a413debec9a5f92d._comment ./doc/download/comment_3_cf6044ebe99f71158034e21197228abd._comment ./doc/download/comment_1_fbd8b6d39e9d3c71791551358c863966._comment ./doc/special_remotes/bup.mdwn ./doc/special_remotes/directory.mdwn ./doc/special_remotes/web.mdwn ./doc/special_remotes/hook.mdwn ./doc/special_remotes/rsync.mdwn ./doc/special_remotes/S3.mdwn ./doc/forum/Behaviour_of_fsck/comment_2_ead36a23c3e6efa1c41e4555f93e014e._comment ./doc/forum/Behaviour_of_fsck/comment_3_97848f9a3db89c0427cfb671ba13300e._comment ./doc/forum/Behaviour_of_fsck/comment_4_e4911dc6793f98fb81151daacbe49968._comment ./doc/forum/Behaviour_of_fsck/comment_1_0e40f158b3f4ccdcaab1408d858b68b8._comment ./doc/forum/wishlist:_command_options_changes/comment_1_bfba72a696789bf21b2435dea15f967a._comment ./doc/forum/wishlist:_command_options_changes/comment_3_bf1114533d2895804e531e76eb6b8095._comment ./doc/forum/wishlist:_command_options_changes/comment_2_f6a637c78c989382e3c22d41b7fb4cc2._comment ./doc/forum/wishlist:alias_system.mdwn ./doc/forum/sparse_git_checkouts_with_annex/comment_4_04dc14880f31eee2b6d767d4d4258c5a._comment ./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/Is_an_automagic_upgrade_of_the_object_directory_safe__63__.mdwn ./doc/forum/hashing_objects_directories/comment_1_c55c56076be4f54251b0b7f79f28a607._comment ./doc/forum/hashing_objects_directories/comment_4_0de9170e429cbfea66f5afa8980d45ac._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_5_ef6cfd49d24c180c2d0a062e5bd3a0be._comment ./doc/forum/batch_check_on_remote_when_using_copy.mdwn ./doc/forum/advantages_of_SHA__42___over_WORM/comment_1_96c354cac4b5ce5cf6664943bc84db1d._comment ./doc/forum/relying_on_git_for_numcopies.mdwn ./doc/forum/unannex_alternatives.mdwn ./doc/forum/advantages_of_SHA__42___over_WORM.mdwn ./doc/forum/OSX__39__s_default_sshd_behaviour_has_limited_paths_set.mdwn ./doc/forum/seems_to_build_fine_on_haskell_platform_2011.mdwn ./doc/forum/getting_git_annex_to_do_a_force_copy_to_a_remote.mdwn ./doc/forum/tips:_special__95__remotes__47__hook_with_tahoe-lafs.mdwn ./doc/forum/performance_improvement:_git_on_ssd__44___annex_on_spindle_disk.mdwn ./doc/forum/working_without_git-annex_commits.mdwn ./doc/forum/bainstorming:_git_annex_push___38___pull.mdwn ./doc/forum/Error_while_adding_a_file___34__createSymbolicLink:_already_exists__34__.mdwn ./doc/forum/Will_git_annex_work_on_a_FAT32_formatted_key__63__.mdwn ./doc/forum/wishlist:_support_for_more_ssh_urls_.mdwn ./doc/forum/wishlist:_git_annex_put_--_same_as_get__44___but_for_defaults.mdwn ./doc/forum/migrate_existing_git_repository_to_git-annex.mdwn ./doc/forum/Behaviour_of_fsck.mdwn ./doc/forum/wishlist:_git-annex_replicate.mdwn ./doc/forum/location_tracking_cleanup/comment_2_e7395cb6e01f42da72adf71ea3ebcde4._comment ./doc/forum/location_tracking_cleanup/comment_3_c15428cec90e969284a5e690fb4b2fde._comment ./doc/forum/location_tracking_cleanup/comment_1_7d6319e8c94dfe998af9cfcbf170efb2._comment ./doc/forum/migration_to_git-annex_and_rsync.mdwn ./doc/forum/wishlist:_push_to_cia.vc_from_the_website__39__s_repo__44___not_your_personal_one.mdwn ./doc/forum/Problems_with_large_numbers_of_files.mdwn ./doc/forum/OSX__39__s_haskell-platform_statically_links_things.mdwn ./doc/forum/can_git-annex_replace_ddm__63__.mdwn ./doc/forum/relying_on_git_for_numcopies/comment_1_8ad3cccd7f66f6423341d71241ba89fc._comment ./doc/forum/relying_on_git_for_numcopies/comment_3_43d8e1513eb9947f8a503f094c03f307._comment ./doc/forum/relying_on_git_for_numcopies/comment_2_be6acbc26008a9cb54e7b8f498f2c2a2._comment ./doc/forum/__34__git_annex_lock__34___very_slow_for_big_repo.mdwn ./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_1_994bfd12c5d82e08040d6116915c5090._comment ./doc/forum/wishlist:_git_annex_status/comment_2_c2b0ce025805b774dc77ce264a222824._comment ./doc/forum/git-annex_communication_channels/comment_4_1ba6ddf54843c17c7d19a9996f2ab712._comment ./doc/forum/git-annex_communication_channels/comment_1_198325d2e9337c90f026396de89eec0e._comment ./doc/forum/git-annex_communication_channels/comment_5_404b723a681eb93fee015cea8024b6bc._comment ./doc/forum/git-annex_communication_channels/comment_2_c7aeefa6ef9a2e75d8667b479ade1b7f._comment ./doc/forum/git-annex_communication_channels/comment_6_0d87d0e26461494b1d7f8a701a924729._comment ./doc/forum/git-annex_communication_channels/comment_7_2c87c7a0648fe87c2bf6b4391f1cc468._comment ./doc/forum/git-annex_communication_channels/comment_3_1ff08a3e0e63fa0e560cbc9602245caa._comment ./doc/forum/wishlist:_special_remote_for_sftp_or_rsync.mdwn ./doc/forum/unannex_alternatives/comment_2_58a72a9fe0f58c7af0b4d7927a2dd21d._comment ./doc/forum/unannex_alternatives/comment_3_b1687fc8f9e7744327bbeb6f0635d1cd._comment ./doc/forum/unannex_alternatives/comment_1_dcd4cd41280b41512bbdffafaf307993._comment ./doc/forum/new_microfeatures.mdwn ./doc/forum/git-annex_on_OSX.mdwn ./doc/forum/Can_I_store_normal_files_in_the_git-annex_git_repository__63__.mdwn ./doc/forum/sparse_git_checkouts_with_annex.mdwn ./doc/forum/wishlist:_git_annex_status.mdwn ./doc/forum/rsync_over_ssh__63__.mdwn ./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/can_git-annex_replace_ddm__63__/comment_3_4c69097fe2ee81359655e59a03a9bb8d._comment ./doc/forum/location_tracking_cleanup.mdwn ./doc/forum/version_3_upgrade.mdwn ./doc/forum/git-annex_communication_channels.mdwn ./doc/forum/version_3_upgrade/comment_1_05fc9c9cad26c520bebb98c852c71e35._comment ./doc/forum/wishlist:_git_backend_for_git-annex.mdwn ./doc/forum/wishlist:_do_round_robin_downloading_of_data.mdwn ./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/wishlist:_git-annex_replicate/comment_4_63f24abf086d644dced8b01e1a9948c9._comment ./doc/forum/new_microfeatures/comment_7_94045b9078b1fff877933b012d1b49e2._comment ./doc/forum/new_microfeatures/comment_1_058bd517c6fffaf3446b1f5d5be63623._comment ./doc/forum/new_microfeatures/comment_6_31ea08c008500560c0b96c6601bc6362._comment ./doc/forum/new_microfeatures/comment_3_a1a9347b5bc517f2a89a8b292c3f8517._comment ./doc/forum/new_microfeatures/comment_5_3c627d275586ff499d928a8f8136babf._comment ./doc/forum/new_microfeatures/comment_2_41ad904c68e89c85e1fc49c9e9106969._comment ./doc/forum/new_microfeatures/comment_4_5a6786dc52382fff5cc42fdb05770196._comment ./doc/forum/wishlist:_traffic_accounting_for_git-annex.mdwn ./doc/forum/Wishlist:_Ways_of_selecting_files_based_on_meta-information.mdwn ./doc/forum/incompatible_versions__63__.mdwn ./doc/forum/rsync_over_ssh__63__/comment_2_aa690da6ecfb2b30fc5080ad76dc77b1._comment ./doc/forum/rsync_over_ssh__63__/comment_1_ee21f32e90303e20339e0a568321bbbe._comment ./doc/forum/hashing_objects_directories.mdwn ./doc/forum/wishlist:_command_options_changes.mdwn ./doc/forum/example_of_massively_disconnected_operation.mdwn ./doc/forum/wishlist:_define_remotes_that_must_have_all_files.mdwn ./doc/forum/incompatible_versions__63__/comment_1_629f28258746d413e452cbd42a1a43f4._comment ./doc/forum/Need_new_build_instructions_for_Debian_stable.mdwn ./doc/todo.mdwn ./doc/git-annex.mdwn ./doc/templates/bare.tmpl ./doc/templates/walkthrough.tmpl ./doc/bare_repositories.mdwn ./doc/feeds.mdwn ./doc/future_proofing.mdwn ./doc/bugs.mdwn ./doc/comments.mdwn ./doc/design/encryption.mdwn ./doc/design/encryption/comment_3_cca186a9536cd3f6e86994631b14231c._comment ./doc/design/encryption/comment_4_8f3ba3e504b058791fc6e6f9c38154cf._comment ./doc/design/encryption/comment_2_a610b3d056a059899178859a3a821ea5._comment ./doc/design/encryption/comment_1_4715ffafb3c4a9915bc33f2b26aaa9c1._comment ./doc/copies.mdwn ./doc/encryption/comment_1_1afca8d7182075d46db41f6ad3dd5911._comment ./doc/logo.png ./doc/git-annex-shell.mdwn ./doc/logo_small.png ./doc/install.mdwn ./doc/news.mdwn ./doc/forum.mdwn ./doc/transferring_data.mdwn ./doc/GPL ./doc/walkthrough.mdwn ./doc/location_tracking.mdwn ./doc/summary.mdwn ./doc/bugs/git_annex_unused_seems_to_check_for_current_path.mdwn ./doc/bugs/Name_scheme_does_not_follow_git__39__s_rules.mdwn ./doc/bugs/unhappy_without_UTF8_locale.mdwn ./doc/bugs/git_annex_unused_failes_on_empty_repository.mdwn ./doc/bugs/free_space_checking/comment_1_a868e805be43c5a7c19c41f1af8e41e6._comment ./doc/bugs/free_space_checking/comment_2_8a65f6d3dcf5baa3f7f2dbe1346e2615._comment ./doc/bugs/free_space_checking/comment_3_0fc6ff79a357b1619d13018ccacc7c10._comment ./doc/bugs/making_annex-merge_try_a_fast-forward.mdwn ./doc/bugs/git_annex_initremote_walks_.git-annex.mdwn ./doc/bugs/WORM:_Handle_long_filenames_correctly.mdwn ./doc/bugs/annex_add_in_annex.mdwn ./doc/bugs/git_annex_should_use___39__git_add_-f__39___internally.mdwn ./doc/bugs/configure_script_should_detect_uuidgen_instead_of_just_uuid.mdwn ./doc/bugs/support_bare_git_repo__44___with_the_annex_directory_exposed_to_http.mdwn ./doc/bugs/fat_support.mdwn ./doc/bugs/annex_unannex__47__uninit_should_handle_copies.mdwn ./doc/bugs/git_rename_detection_on_file_move/comment_1_0531dcfa833b0321a7009526efe3df33._comment ./doc/bugs/git_rename_detection_on_file_move/comment_2_7101d07400ad5935f880dc00d89bf90e._comment ./doc/bugs/git_rename_detection_on_file_move/comment_3_57010bcaca42089b451ad8659a1e018e._comment ./doc/bugs/git_rename_detection_on_file_move/comment_5_d61f5693d947b9736b29fca1dbc7ad76._comment ./doc/bugs/git_rename_detection_on_file_move/comment_4_79d96599f757757f34d7b784e6c0e81c._comment ./doc/bugs/wishlist:_more_descriptive_commit_messages_in_git-annex_branch.mdwn ./doc/bugs/git_annex_unlock_is_not_atomic.mdwn ./doc/bugs/building_on_lenny.mdwn ./doc/bugs/uuid.log_trust.log_and_remote.log_merge_wackiness.mdwn ./doc/bugs/Error_while_adding_a_file___34__createSymbolicLink:_already_exists__34__.mdwn ./doc/bugs/build_issue_with_latest_release_0.20110522-1-gde817ba.mdwn ./doc/bugs/git_annex_copy_-f_REMOTE_._doesn__39__t_work_as_expected.mdwn ./doc/bugs/Cabal_dependency_monadIO_missing.mdwn ./doc/bugs/git_command_line_constructed_by_unannex_command_has_tons_of_redundant_-a_paramters.mdwn ./doc/bugs/copy_fast_confusing_with_broken_locationlog.mdwn ./doc/bugs/Unfortunate_interaction_with_Calibre.mdwn ./doc/bugs/softlink_mtime.mdwn ./doc/bugs/problem_commit_normal_links.mdwn ./doc/bugs/unannex_vs_unlock_hook_confusion.mdwn ./doc/bugs/check_for_curl_in_configure.hs.mdwn ./doc/bugs/add_range_argument_to___34__git_annex_dropunused__34___.mdwn ./doc/bugs/git_annex_add_eats_files_when_filename_is_too_long.mdwn ./doc/bugs/touch.hsc_has_problems_on_non-linux_based_systems.mdwn ./doc/bugs/encrypted_S3_stalls.mdwn ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_1_5f60006c9bb095167d817f234a14d20b._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_2_cd0123392b16d89db41b45464165c247._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_4_91439d4dbbf1461e281b276eb0003691._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_3_86d9e7244ae492bcbe62720b8c4fc4a9._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_6_f360f0006bc9115bc5a3e2eb9fe58abd._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_5_ca33a9ca0df33f7c1b58353d7ffb943d._comment ./doc/bugs/minor_bug:_errors_are_not_verbose_enough.mdwn ./doc/bugs/error_propigation.mdwn ./doc/bugs/test_suite_shouldn__39__t_fail_silently.mdwn ./doc/bugs/bare_git_repos.mdwn ./doc/bugs/unannex_and_uninit_do_not_work_when_git_index_is_broken.mdwn ./doc/bugs/done.mdwn ./doc/bugs/Makefile_is_missing_dependancies.mdwn ./doc/bugs/S3_memory_leaks.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/problems_with_utf8_names.mdwn ./doc/bugs/Problems_running_make_on_osx.mdwn ./doc/bugs/__39__annex_add__39___fails_to___39__git_add__39___for_parent_relative_path.mdwn ./doc/bugs/Problems_running_make_on_osx/comment_16_5c2dd6002aadaab30841b77a5f5aed34._comment ./doc/bugs/Problems_running_make_on_osx/comment_15_6b8867b8e48bf807c955779c9f8f0909._comment ./doc/bugs/Problems_running_make_on_osx/comment_20_7db27d1a22666c831848bc6c06d66a84._comment ./doc/bugs/Problems_running_make_on_osx/comment_10_94e4ac430140042a2d0fb5a16d86b4e5._comment ./doc/bugs/Problems_running_make_on_osx/comment_5_7f1330a1e541b0f3e2192e596d7f7bee._comment ./doc/bugs/Problems_running_make_on_osx/comment_9_cc283b485b3c95ba7eebc8f0c96969b3._comment ./doc/bugs/Problems_running_make_on_osx/comment_1_34120e82331ace01a6a4960862d38f2d._comment ./doc/bugs/Problems_running_make_on_osx/comment_13_88ed095a448096bf8a69015a04e64df1._comment ./doc/bugs/Problems_running_make_on_osx/comment_2_cc53d1681d576186dbc868dd9801d551._comment ./doc/bugs/Problems_running_make_on_osx/comment_11_56f1143fa191361d63b441741699e17f._comment ./doc/bugs/Problems_running_make_on_osx/comment_18_64fab50d95de619eb2e8f08f90237de1._comment ./doc/bugs/Problems_running_make_on_osx/comment_17_62fccb04b0e4b695312f7a3f32fb96ee._comment ./doc/bugs/Problems_running_make_on_osx/comment_8_efafa203addf8fa79e33e21a87fb5a2b._comment ./doc/bugs/Problems_running_make_on_osx/comment_7_237a137cce58a28abcc736cbf2c420b0._comment ./doc/bugs/Problems_running_make_on_osx/comment_3_68f0f8ae953589ae26d57310b40c878d._comment ./doc/bugs/Problems_running_make_on_osx/comment_6_0c46f5165ceb5a7b9ea9689c33b3a4f8._comment ./doc/bugs/Problems_running_make_on_osx/comment_4_c52be386f79f14c8570a8f1397c68581._comment ./doc/bugs/Problems_running_make_on_osx/comment_19_4253988ed178054c8b6400beeed68a29._comment ./doc/bugs/Problems_running_make_on_osx/comment_14_89a960b6706ed703b390a81a8bc4e311._comment ./doc/bugs/Problems_running_make_on_osx/comment_12_ec5131624d0d2285d3b6880e47033f97._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3.mdwn ./doc/bugs/backend_version_upgrade_leaves_repo_unusable.mdwn ./doc/bugs/fsck_output.mdwn ./doc/bugs/Displayed_copy_speed_is_wrong.mdwn ./doc/bugs/add_script-friendly_output_options.mdwn ./doc/bugs/fsck__47__fix_should_check__47__fix_the_permissions_of_.git__47__annex.mdwn ./doc/bugs/No_version_information_from_cli.mdwn ./doc/bugs/S3_bucket_uses_the_same_key_for_encryption_and_hashing.mdwn ./doc/bugs/dotdot_problem.mdwn ./doc/bugs/git-annex_directory_hashing_problems_on_osx.mdwn ./doc/bugs/git_annex_copy_--fast_does_not_copy_files.mdwn ./doc/bugs/git_annex_get_choke_when_remote_is_an_ssh_url_with_a_port.mdwn ./doc/bugs/--git-dir_and_--work-tree_options.mdwn ./doc/bugs/free_space_checking.mdwn ./doc/bugs/git_annex_gets_confused_about_remotes_with_dots_in_their_names.mdwn ./doc/bugs/ordering.mdwn ./doc/bugs/Makefile_is_missing_dependancies/comment_1_5a3da5f79c8563c7a450aa29728abe7c._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_2_416f12dbd0c2b841fac8164645b81df5._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_7_96fd4725df4b54e670077a18d3ac4943._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_8_a3555e3286cdc2bfeb9cde0ff727ba74._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_3_c38b6f4abc9b9ad413c3b83ca04386c3._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_6_24119fc5d5963ce9dd669f7dcf006859._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_5_0a1c52e2c96d19b9c3eb7e99b8c2434f._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_4_cc13873175edf191047282700315beee._comment ./doc/bugs/No_easy_way_to_re-inject_a_file_into_an_annex.mdwn ./doc/bugs/dropping_files_with_a_URL_backend_fails.mdwn ./doc/bugs/git_annex_migrate_leaves_old_backend_versions_around.mdwn ./doc/bugs/weird_local_clone_confuses.mdwn ./doc/bugs/Cabal_dependency_monadIO_missing/comment_1_14be660aa57fadec0d81b32a8b52c66f._comment ./doc/bugs/Cabal_dependency_monadIO_missing/comment_2_4f4d8e1e00a2a4f7e8a8ab082e16adac._comment ./doc/bugs/git_rename_detection_on_file_move.mdwn ./doc/bugs/concurrent_git-annex_processes_can_lead_to_locking_issues.mdwn ./doc/bugs/upgrade_left_untracked_.git-annex__47____42___directories.mdwn ./doc/bugs/scp_interrupt_to_background.mdwn ./doc/bugs/tests_fail_when_there_is_no_global_.gitconfig_for_the_user.mdwn ./doc/bugs/git-annex_incorrectly_parses_bare_IPv6_addresses.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/error_with_file_names_starting_with_dash.mdwn ./doc/bugs/tmp_file_handling.mdwn ./doc/bugs/conflicting_haskell_packages/comment_1_e552a6cc6d7d1882e14130edfc2d6b3b._comment ./doc/bugs/git-annex_has_issues_with_git_when_staging__47__commiting_logs.mdwn ./doc/bugs/Build_error_on_Mac_OSX_10.6.mdwn ./doc/bugs/Prevent_accidental_merges.mdwn ./doc/bugs/git_annex_fsck_is_a_no-op_in_bare_repos.mdwn ./doc/bugs/conflicting_haskell_packages.mdwn ./doc/bugs/unannex_command_doesn__39__t_all_files.mdwn ./doc/index.mdwn ./doc/backends.mdwn ./doc/walkthrough/more.mdwn ./doc/walkthrough/renaming_files.mdwn ./doc/walkthrough/getting_file_content.mdwn ./doc/walkthrough/unused_data.mdwn ./doc/walkthrough/backups.mdwn ./doc/walkthrough/using_the_web.mdwn ./doc/walkthrough/adding_files.mdwn ./doc/walkthrough/using_Amazon_S3.mdwn ./doc/walkthrough/using_the_SHA1_backend.mdwn ./doc/walkthrough/removing_files/comment_2_64709ea4558915edd5c8ca4486965b07._comment ./doc/walkthrough/removing_files/comment_1_cb65e7c510b75be1c51f655b058667c6._comment ./doc/walkthrough/powerful_file_matching.mdwn ./doc/walkthrough/creating_a_repository.mdwn ./doc/walkthrough/using_ssh_remotes.mdwn ./doc/walkthrough/adding_a_remote/comment_3_60691af4400521b5a8c8d75efe3b44cb._comment ./doc/walkthrough/adding_a_remote/comment_2_f8cd79ef1593a8181a7f1086a87713e8._comment ./doc/walkthrough/adding_a_remote/comment_4_6f7cf5c330272c96b3abeb6612075c9d._comment ./doc/walkthrough/adding_a_remote/comment_1_0a59355bd33a796aec97173607e6adc9._comment ./doc/walkthrough/fsck:_when_things_go_wrong.mdwn ./doc/walkthrough/Internet_Archive_via_S3.mdwn ./doc/walkthrough/modifying_annexed_files.mdwn ./doc/walkthrough/migrating_data_to_a_new_backend.mdwn ./doc/walkthrough/untrusted_repositories.mdwn ./doc/walkthrough/transferring_files:_When_things_go_wrong.mdwn ./doc/walkthrough/removing_files:_When_things_go_wrong.mdwn ./doc/walkthrough/what_to_do_when_you_lose_a_repository.mdwn ./doc/walkthrough/fsck:_verifying_your_data.mdwn ./doc/walkthrough/adding_a_remote.mdwn ./doc/walkthrough/automatically_managing_content.mdwn ./doc/walkthrough/recover_data_from_lost+found.mdwn ./doc/walkthrough/using_bup.mdwn ./doc/walkthrough/moving_file_content_between_repositories.mdwn ./doc/walkthrough/removing_files.mdwn ./doc/not.mdwn ./doc/git-union-merge.mdwn ./doc/contact.mdwn ./doc/upgrades.mdwn ./Options.hs ./Locations.hs ./Init.hs ./Messages.hs ./Upgrade.hs ./GPL ./Remote.hs ./git-annex.cabal ./GitAnnex.hs ./Build/TestConfig.hs ./Messages/JSON.hs ./git-union-merge.hs ./Annex/Branch.hs ./Annex/Exception.hs ./Annex/Version.hs ./Annex/Content.hs ./Annex/CatFile.hs ./Annex/Queue.hs ./Utility.hs ./.gitattributes ./Common.hs ./Types.hs ./UUIDLog.hs ./Types/TrustLevel.hs ./Types/Backend.hs ./Types/BranchState.hs ./Types/Key.hs ./Types/Remote.hs ./Types/UUID.hs ./Types/Crypto.hs ./UUID.hs ./Config.hs ./Crypto.hs ./README ./Setup.hs+Extra-Source-Files: ./debian/NEWS ./debian/control ./debian/rules ./debian/manpages ./debian/changelog ./debian/doc-base ./debian/copyright ./debian/compat ./Messages/JSON.hs ./configure.hs ./Annex.hs ./Seek.hs ./Crypto.hs ./Common.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/Reinject.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 ./Init.hs ./Types.hs ./Common/Annex.hs ./Makefile ./git-annex.cabal ./Upgrade/V1.hs ./Upgrade/V2.hs ./Upgrade/V0.hs ./CHANGELOG ./Git/LsFiles.hs ./Git/UnionMerge.hs ./Git/CatFile.hs ./Git/Queue.hs ./Git/LsTree.hs ./doc/upgrades.mdwn ./doc/forum/Recommended_number_of_repositories.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/wishlist:_git-annex_replicate/comment_4_63f24abf086d644dced8b01e1a9948c9._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/Recommended_number_of_repositories/comment_1_3ef256230756be8a9679b107cdbfd018._comment ./doc/forum/migration_to_git-annex_and_rsync.mdwn ./doc/forum/hashing_objects_directories.mdwn ./doc/forum/git_annex_ls___47___metadata_in_git_annex_whereis.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/location_tracking_cleanup.mdwn ./doc/forum/Wishlist:_Ways_of_selecting_files_based_on_meta-information.mdwn ./doc/forum/confusion_with_remotes__44___map.mdwn ./doc/forum/git-annex_on_OSX.mdwn ./doc/forum/git_tag_missing_for_3.20111011/comment_1_7a53bf273f3078ab3351369ef2b5f2a6._comment ./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/location_tracking_cleanup/comment_3_c15428cec90e969284a5e690fb4b2fde._comment ./doc/forum/location_tracking_cleanup/comment_2_e7395cb6e01f42da72adf71ea3ebcde4._comment ./doc/forum/location_tracking_cleanup/comment_1_7d6319e8c94dfe998af9cfcbf170efb2._comment ./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/git_tag_missing_for_3.20111011.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/confusion_with_remotes__44___map/comment_1_a38ded23b7f288292a843abcb1a56f38._comment ./doc/forum/confusion_with_remotes__44___map/comment_2_cd1c98b1276444e859a22c3dbd6f2a79._comment ./doc/forum/confusion_with_remotes__44___map/comment_6_496b0d9b86869bbac3a1356d53a3dda4._comment ./doc/forum/confusion_with_remotes__44___map/comment_5_27801584325d259fa490f67273f2ff71._comment ./doc/forum/confusion_with_remotes__44___map/comment_4_3b89b6d1518267fcbc050c9de038b9ca._comment ./doc/forum/confusion_with_remotes__44___map/comment_3_18531754089c991b6caefc57a5c17fe9._comment ./doc/forum/confusion_with_remotes__44___map/comment_7_9a456f61f956a3d5e81e723d5a90794c._comment ./doc/forum/--print0_option_as_in___34__find__34__.mdwn ./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/uuid.log_trust.log_and_remote.log_merge_wackiness.mdwn ./doc/bugs/softlink_mtime.mdwn ./doc/bugs/uninit_should_not_run_when_branch_git-annex_is_checked_out.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/uninit_does_not_work_in_old_repos.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/Error_when_moving_annexed_file_to_a_.gitignored_location.mdwn ./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/Build_error_on_Mac_OSX_10.6.mdwn ./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/git_annex_map_has_problems_with_urls_containing___126__.mdwn ./doc/bugs/interrupting_migration_causes_problems.mdwn ./doc/bugs/upgrade_left_untracked_.git-annex__47____42___directories.mdwn ./doc/bugs/cyclic_drop.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/old_data_isn__39__t_unused_after_migration.mdwn ./doc/bugs/fails_to_handle_lot_of_files.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/git_annex_version_should_without_being_in_a_repo_.mdwn ./doc/bugs/Prevent_accidental_merges/comment_1_4c46a193915eab8f308a04175cb2e40a._comment ./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/git_annex_upgrade_output_is_inconsistent_and_spammy.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/wishlist:_query_things_like_description__44___trust_level.mdwn ./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/fsck_claims_failed_checksum_when_less_copies_than_required_are_found.mdwn ./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/extraneous_shell_escaping_for_rsync_remotes.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/Trouble_initializing_git_annex_on_NFS.mdwn ./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/directory_remote_and_case_sensitivity_on_FAT.mdwn ./doc/bugs/wishlist:_allow_users_to_provide_UUID_when_running___96__git_annex_init__96__.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/wishlist:_support_drop__44___find_on_special_remotes.mdwn ./doc/bugs/scp_interrupt_to_background.mdwn ./doc/bugs/copy_fast_confusing_with_broken_locationlog.mdwn ./doc/bugs/uninit_does_not_work_in_old_repos/comment_1_bc0619c6e17139df74639448aa6a0f72._comment ./doc/bugs/configure_script_should_detect_uuidgen_instead_of_just_uuid.mdwn ./doc/bugs/check_for_curl_in_configure.hs.mdwn ./doc/bugs/fails_to_handle_lot_of_files/comment_1_09d8e4e66d8273fab611bd29e82dc7fc._comment ./doc/bugs/fails_to_handle_lot_of_files/comment_2_fd2ec05f4b5a7a6ae6bd9f5dbc3156de._comment ./doc/bugs/Name_scheme_does_not_follow_git__39__s_rules.mdwn ./doc/bugs/concurrent_git-annex_processes_can_lead_to_locking_issues.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/tips.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.20111122.mdwn ./doc/news/version_3.20111025.mdwn ./doc/news/version_3.20111105.mdwn ./doc/news/version_3.20111107.mdwn ./doc/news/LWN_article.mdwn ./doc/news/version_3.20111111.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/exclude_files_on_a_given_remote.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/optimise_git-annex_merge.mdwn ./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/gitolite_and_gitosis_support.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/avoid_unnecessary_union_merges.mdwn ./doc/todo/support_fsck_in_bare_repos.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/automatically_managing_content.mdwn ./doc/walkthrough/removing_files/comment_1_cb65e7c510b75be1c51f655b058667c6._comment ./doc/walkthrough/removing_files/comment_2_64709ea4558915edd5c8ca4486965b07._comment ./doc/walkthrough/moving_file_content_between_repositories.mdwn ./doc/walkthrough/modifying_annexed_files.mdwn ./doc/walkthrough/more.mdwn ./doc/walkthrough/removing_files.mdwn ./doc/walkthrough/creating_a_repository.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/backups.mdwn ./doc/walkthrough/transferring_files:_When_things_go_wrong.mdwn ./doc/GPL ./doc/logo.png ./doc/location_tracking.mdwn ./doc/design.mdwn ./doc/news.mdwn ./doc/tips/untrusted_repositories.mdwn ./doc/tips/automatically_getting_files_on_checkout.mdwn ./doc/tips/what_to_do_when_a_repository_is_corrupted.mdwn ./doc/tips/Internet_Archive_via_S3.mdwn ./doc/tips/what_to_do_when_you_lose_a_repository.mdwn ./doc/tips/using_Amazon_S3.mdwn ./doc/tips/using_the_web_as_a_special_remote.mdwn ./doc/tips/powerful_file_matching.mdwn ./doc/tips/using_gitolite_with_git-annex.mdwn ./doc/tips/recover_data_from_lost+found.mdwn ./doc/tips/centralized_git_repository_tutorial.mdwn ./doc/tips/using_the_SHA1_backend.mdwn ./doc/tips/migrating_data_to_a_new_backend.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/meta.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 ./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 ./Build/TestConfig.hs ./Upgrade.hs ./Annex/Version.hs ./Annex/Exception.hs ./Annex/CatFile.hs ./Annex/Ssh.hs ./Annex/Queue.hs ./Annex/Branch.hs ./Annex/UUID.hs ./Annex/Content.hs ./git-union-merge.hs ./Utility/Path.hs ./Utility/Matcher.hs ./Utility/TempFile.hs ./Utility/Url.hs ./Utility/DataUnits.hs ./Utility/StatFS.hsc ./Utility/Conditional.hs ./Utility/Monad.hs ./Utility/RsyncFile.hs ./Utility/CopyFile.hs ./Utility/Misc.hs ./Utility/SafeCommand.hs ./Utility/FileMode.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/Command.hs ./Types/Backend.hs ./Types/Key.hs ./Config.hs ./Checks.hs ./Command.hs ./.gitignore ./Backend.hs ./Logs/Web.hs ./Logs/UUIDBased.hs ./Logs/Trust.hs ./Logs/Location.hs ./Logs/UUID.hs ./Logs/Remote.hs ./Logs/Presence.hs ./.gitattributes ./Git.hs ./Limit.hs ./GitAnnex.hs Homepage: http://git-annex.branchable.com/ Build-type: Custom Category: Utility
git-union-merge.hs view
@@ -37,10 +37,10 @@ main :: IO () main = do- [aref, bref, newref] <- parseArgs+ [aref, bref, newref] <- map Git.Ref <$> parseArgs g <- Git.configRead =<< Git.repoFromCwd _ <- Git.useIndex (tmpIndex g) setup g- Git.UnionMerge.merge g aref bref- Git.commit g "union merge" newref [aref, bref]+ Git.UnionMerge.merge aref bref g+ Git.commit "union merge" newref [aref, bref] g cleanup g
test.hs view
@@ -23,18 +23,18 @@ import qualified Utility.SafeCommand import qualified Annex+import qualified Annex.UUID import qualified Backend import qualified Git import qualified Locations import qualified Types.Backend import qualified Types import qualified GitAnnex-import qualified LocationLog-import qualified UUID-import qualified UUIDLog-import qualified Trust+import qualified Logs.Location+import qualified Logs.UUIDBased+import qualified Logs.Trust+import qualified Logs.Remote import qualified Remote-import qualified RemoteLog import qualified Command.DropUnused import qualified Types.Key import qualified Config@@ -73,14 +73,14 @@ , qctest "prop_idempotent_key_read_show" Types.Key.prop_idempotent_key_read_show , 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_idempotent_configEscape" Logs.Remote.prop_idempotent_configEscape , 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- , qctest "prop_TimeStamp_sane" UUIDLog.prop_TimeStamp_sane- , qctest "prop_addLog_sane" UUIDLog.prop_addLog_sane+ , qctest "prop_TimeStamp_sane" Logs.UUIDBased.prop_TimeStamp_sane+ , qctest "prop_addLog_sane" Logs.UUIDBased.prop_addLog_sane ] blackbox :: Test@@ -88,7 +88,7 @@ -- test order matters, later tests may rely on state from earlier [ test_init , test_add- , test_setkey+ , test_reinject , test_unannex , test_drop , test_get@@ -118,15 +118,15 @@ writeFile annexedfile $ content annexedfile git_annex "add" ["-q", annexedfile] @? "add failed" annexed_present annexedfile+ writeFile sha1annexedfile $ content sha1annexedfile+ git_annex "add" ["-q", sha1annexedfile, "--backend=SHA1"] @? "add with SHA1 failed"+ annexed_present sha1annexedfile writeFile ingitfile $ content ingitfile 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- writeFile sha1annexedfile $ content sha1annexedfile- git_annex "add" ["-q", sha1annexedfile, "--backend=SHA1"] @? "add with SHA1 failed"- annexed_present sha1annexedfile writeFile sha1annexedfiledup $ content sha1annexedfiledup git_annex "add" ["-q", sha1annexedfiledup, "--backend=SHA1"] @? "add of second file with same SHA1 failed" annexed_present sha1annexedfiledup@@ -140,15 +140,15 @@ changeWorkingDirectory "dir" git_annex "add" ["-q", "../dir2"] @? "add of ../subdir failed" -test_setkey :: Test-test_setkey = "git-annex setkey/fromkey" ~: TestCase $ inmainrepo $ do+test_reinject :: Test+test_reinject = "git-annex reinject/fromkey" ~: TestCase $ intmpclonerepo $ do+ git_annex "drop" ["-q", "--force", sha1annexedfile] @? "drop failed" writeFile tmp $ content sha1annexedfile r <- annexeval $ Types.Backend.getKey backendSHA1 tmp let key = show $ fromJust r- git_annex "setkey" ["-q", "--key", key, tmp] @? "setkey failed"- git_annex "fromkey" ["-q", "--key", key, sha1annexedfile] @? "fromkey failed"- boolSystem "git" [Params "commit -q -a -m commit"] @? "git commit failed"- annexed_present sha1annexedfile+ git_annex "reinject" ["-q", tmp, sha1annexedfile] @? "reinject failed"+ git_annex "fromkey" ["-q", key, sha1annexedfiledup] @? "fromkey failed"+ annexed_present sha1annexedfiledup where tmp = "tmpfile" @@ -341,22 +341,22 @@ test_trust :: Test test_trust = "git-annex trust/untrust/semitrust" ~: intmpclonerepo $ do git_annex "trust" ["-q", repo] @? "trust failed"- trustcheck Trust.Trusted "trusted 1"+ trustcheck Logs.Trust.Trusted "trusted 1" git_annex "trust" ["-q", repo] @? "trust of trusted failed"- trustcheck Trust.Trusted "trusted 2"+ trustcheck Logs.Trust.Trusted "trusted 2" git_annex "untrust" ["-q", repo] @? "untrust failed"- trustcheck Trust.UnTrusted "untrusted 1"+ trustcheck Logs.Trust.UnTrusted "untrusted 1" git_annex "untrust" ["-q", repo] @? "untrust of untrusted failed"- trustcheck Trust.UnTrusted "untrusted 2"+ trustcheck Logs.Trust.UnTrusted "untrusted 2" git_annex "semitrust" ["-q", repo] @? "semitrust failed"- trustcheck Trust.SemiTrusted "semitrusted 1"+ trustcheck Logs.Trust.SemiTrusted "semitrusted 1" git_annex "semitrust" ["-q", repo] @? "semitrust of semitrusted failed"- trustcheck Trust.SemiTrusted "semitrusted 2"+ trustcheck Logs.Trust.SemiTrusted "semitrusted 2" where repo = "origin" trustcheck expected msg = do present <- annexeval $ do- l <- Trust.trustGet expected+ l <- Logs.Trust.trustGet expected u <- Remote.nameToUUID repo return $ u `elem` l assertBool msg present@@ -609,12 +609,12 @@ checklocationlog :: FilePath -> Bool -> Assertion checklocationlog f expected = do- thisuuid <- annexeval UUID.getUUID+ thisuuid <- annexeval Annex.UUID.getUUID r <- annexeval $ Backend.lookupFile f case r of Just (k, _) -> do- uuids <- annexeval $ LocationLog.keyLocations k- assertEqual ("bad content in location log for " ++ f ++ " key " ++ (show k) ++ " uuid " ++ thisuuid)+ uuids <- annexeval $ Logs.Location.keyLocations k+ assertEqual ("bad content in location log for " ++ f ++ " key " ++ (show k) ++ " uuid " ++ show thisuuid) expected (thisuuid `elem` uuids) _ -> assertFailure $ f ++ " failed to look up key"