git-annex 4.20130417 → 4.20130501
raw patch · 225 files changed
+3779/−887 lines, 225 filesdep +dlistbinary-added
Dependencies added: dlist
Files
- Annex/Content.hs +5/−6
- Annex/Direct.hs +18/−6
- Annex/Environment.hs +31/−0
- Annex/FileMatcher.hs +20/−6
- Annex/Perms.hs +5/−0
- Annex/TaggedPush.hs +1/−1
- Assistant.hs +0/−3
- Assistant/Alert.hs +2/−2
- Assistant/Changes.hs +11/−8
- Assistant/Commits.hs +3/−8
- Assistant/DaemonStatus.hs +5/−4
- Assistant/DeleteRemote.hs +6/−1
- Assistant/Environment.hs +0/−26
- Assistant/Install.hs +4/−1
- Assistant/Install/Menu.hs +30/−0
- Assistant/Install/Menu.o binary
- Assistant/MakeRemote.hs +39/−11
- Assistant/Monad.hs +2/−2
- Assistant/Ssh.hs +5/−5
- Assistant/Sync.hs +6/−3
- Assistant/Threads/Committer.hs +123/−52
- Assistant/Threads/Merger.hs +7/−11
- Assistant/Threads/MountWatcher.hs +1/−1
- Assistant/Threads/NetWatcher.hs +1/−1
- Assistant/Threads/Pusher.hs +2/−18
- Assistant/Threads/Transferrer.hs +1/−1
- Assistant/Threads/Watcher.hs +20/−11
- Assistant/Threads/WebApp.hs +1/−1
- Assistant/Threads/XMPPClient.hs +44/−15
- Assistant/TransferQueue.hs +13/−16
- Assistant/Types/Changes.hs +26/−14
- Assistant/Types/Commits.hs +3/−3
- Assistant/Types/NetMessager.hs +25/−18
- Assistant/Types/TransferQueue.hs +5/−4
- Assistant/WebApp/Configurators.hs +6/−2
- Assistant/WebApp/Configurators/AWS.hs +52/−20
- Assistant/WebApp/Configurators/Delete.hs +1/−1
- Assistant/WebApp/Configurators/Edit.hs +79/−33
- Assistant/WebApp/Configurators/IA.hs +211/−0
- Assistant/WebApp/Configurators/Local.hs +9/−3
- Assistant/WebApp/Configurators/Pairing.hs +3/−5
- Assistant/WebApp/Configurators/Preferences.hs +1/−1
- Assistant/WebApp/Configurators/Ssh.hs +11/−14
- Assistant/WebApp/Configurators/WebDAV.hs +33/−16
- Assistant/WebApp/Configurators/XMPP.hs +23/−7
- Assistant/WebApp/Control.hs +1/−1
- Assistant/WebApp/Form.hs +11/−0
- Assistant/WebApp/OtherRepos.hs +1/−1
- Assistant/WebApp/RepoList.hs +8/−10
- Assistant/WebApp/SideBar.hs +4/−0
- Assistant/WebApp/Utility.hs +1/−1
- Assistant/WebApp/routes +5/−1
- Assistant/XMPP.hs +13/−9
- Assistant/XMPP/Git.hs +18/−13
- Backend/SHA.hs +1/−1
- Build/Configure.o binary
- Build/DesktopFile.hs +3/−20
- Build/DesktopFile.o binary
- Build/EvilSplicer.hs +57/−44
- Build/EvilSplicer.o binary
- Build/InstallDesktopFile.hs +3/−3
- Build/InstallDesktopFile.o binary
- Build/OSXMkLibs.hs +51/−20
- Build/Standalone.hs +8/−5
- Build/Standalone.o binary
- Build/SysConfig.o binary
- Build/TestConfig.o binary
- CHANGELOG +60/−0
- Checks.hs +10/−3
- CmdLine.hs +2/−0
- Command/Add.hs +13/−10
- Command/AddUrl.hs +1/−1
- Command/Assistant.hs +1/−1
- Command/Direct.hs +1/−1
- Command/EnableRemote.hs +56/−0
- Command/Help.hs +5/−3
- Command/Indirect.hs +5/−2
- Command/InitRemote.hs +30/−26
- Command/RmUrl.hs +30/−0
- Command/Status.hs +1/−1
- Command/Sync.hs +6/−4
- Command/TransferKeys.hs +1/−1
- Command/Version.hs +2/−4
- Command/WebApp.hs +9/−3
- Common.hs +2/−2
- Config/Files.hs +60/−0
- Config/Files.o binary
- Git/Construct.hs +2/−1
- Git/Queue.hs +1/−2
- GitAnnex.hs +4/−0
- Limit.hs +8/−3
- Locations/UserConfig.hs +0/−57
- Locations/UserConfig.o binary
- Logs/PreferredContent.hs +16/−13
- Logs/Trust.hs +1/−0
- Logs/Web.hs +4/−1
- Makefile +4/−3
- Messages.hs +1/−2
- Remote.hs +3/−3
- Remote/Bup.hs +1/−1
- Remote/Git.hs +20/−13
- Remote/Helper/Encryptable.hs +1/−1
- Remote/List.hs +0/−4
- Remote/S3.hs +106/−42
- Remote/WebDAV.hs +11/−3
- Setup.o binary
- Test.hs +5/−3
- Types/StandardGroups.hs +18/−0
- Upgrade/V1.hs +3/−3
- Utility/Directory.o binary
- Utility/Lsof.hs +1/−1
- Utility/Rsync.hs +1/−1
- Utility/SafeCommand.o binary
- Utility/TList.hs +59/−0
- Utility/TSet.hs +0/−41
- Utility/TempFile.o binary
- Utility/WebApp.hs +9/−4
- Utility/libkqueue.c +1/−0
- debian/changelog +60/−0
- debian/control +1/−0
- doc/assistant/iaitem.png binary
- doc/assistant/inotify_max_limit_alert.png binary
- doc/assistant/release_notes.mdwn +20/−0
- doc/bugs.mdwn +13/−1
- doc/bugs/Adding_second_remote_repository_over_ssh_fails.mdwn +4/−0
- doc/bugs/Files_disappear_from_locally_paired_annexes_when_edited.mdwn +2/−0
- doc/bugs/Issue_on_OSX_with_some_system_limits.mdwn +2/−0
- doc/bugs/It_is_very_easy_to_turn_git-annex_into_a_zombie.mdwn +1/−1
- doc/bugs/OSX_app_issues/comment_12_62170597c7f441d84d48986857998858._comment +10/−0
- doc/bugs/Out_of_memory_error_in_fsck_whereis_find_and_status_cmds.mdwn +2/−0
- doc/bugs/Repository_deletion_error.mdwn +46/−0
- doc/bugs/Resource_exhausted.mdwn +45/−0
- doc/bugs/Stress_test.mdwn +45/−0
- doc/bugs/Stress_test/comment_1_c4c764488ac082f5c48d3a6b4b5fba42._comment +17/−0
- doc/bugs/Stress_test/comment_2_42125bba09a0ea9821cda7183e458100._comment +47/−0
- doc/bugs/Stress_test/comment_3_8240e61106b494d3600ad91f16eb5b1c._comment +20/−0
- doc/bugs/Stress_test/comment_4_c38d84e0dcc834931804c44bce7f7b7a._comment +11/−0
- doc/bugs/Stress_test/comment_5_60ce20ee255451c4ea809ba475561adb._comment +15/−0
- doc/bugs/Stress_test/comment_6_1371562e201393986cd41597f6f288cb._comment +14/−0
- doc/bugs/Stress_test/comment_7_a14be7699da224a8f6c9b34f1b911219._comment +8/−0
- doc/bugs/Tries_to_upload_to_remote_although_remote_is_dead.mdwn +1/−1
- doc/bugs/True_backup_support.mdwn +5/−0
- doc/bugs/Unable_to_switch_back_to_direct_mode.mdwn +54/−0
- doc/bugs/Unable_to_sync_a_second_machine_through_Box.mdwn +39/−0
- doc/bugs/Unfortunate_interaction_with_Calibre.mdwn +3/−0
- doc/bugs/Weird_behaviour_of_direct_and_indirect_annexes.mdwn +5/−0
- doc/bugs/assistant_does_not_warn_on_files_it_failed_to_add.mdwn +22/−0
- doc/bugs/assistant_hangs_during_commit.mdwn +2/−2
- doc/bugs/error_on_only_repository_copy_deletion.mdwn +16/−0
- doc/bugs/git-annex_get:_requested_key_is_not_present.mdwn +32/−0
- doc/bugs/git_annex_assistant_--autostart_failed.mdwn +39/−0
- doc/bugs/git_annex_does_nothing_useful.mdwn +5/−0
- doc/bugs/make_SHA512E_the_default.mdwn +29/−0
- doc/bugs/moreinfo.mdwn +6/−0
- doc/bugs/random_files_vanishing_when_assistant_gets_restarted.mdwn +1/−1
- doc/bugs/three_character_directories_created.mdwn +5/−0
- doc/bugs/xmpp_needs_one_account_per_distinct_repository.mdwn +107/−0
- doc/design/assistant.mdwn +2/−1
- doc/design/assistant/android.mdwn +0/−10
- doc/design/assistant/blog/day_240__it_builds.mdwn +37/−0
- doc/design/assistant/blog/day_241__cleanup.mdwn +14/−0
- doc/design/assistant/blog/day_242__more_porting.mdwn +4/−0
- doc/design/assistant/blog/day_243__in_the_field.mdwn +21/−0
- doc/design/assistant/blog/day_244__android_porting.mdwn +6/−0
- doc/design/assistant/blog/day_245__misc.mdwn +15/−0
- doc/design/assistant/blog/day_246__bug_treadmill.mdwn +18/−0
- doc/design/assistant/blog/day_247__performance_tuning.mdwn +16/−0
- doc/design/assistant/blog/day_248__Internet_Archive.mdwn +28/−0
- doc/design/assistant/blog/day_249__quiet_day.mdwn +7/−0
- doc/design/assistant/blog/day_250__stymied.mdwn +23/−0
- doc/design/assistant/blog/day_251__xmpp_improvements.mdwn +34/−0
- doc/design/assistant/more_cloud_providers.mdwn +1/−0
- doc/design/assistant/polls/Android_default_directory.mdwn +7/−0
- doc/design/assistant/todo.mdwn +3/−3
- doc/design/assistant/xmpp.mdwn +4/−9
- doc/encryption.mdwn +2/−2
- doc/forum/Automatically_syncronise_centralised_repository.mdwn +14/−0
- doc/forum/Cannot_launch_webapp_on_ubuntu_12.04_using_ppa.mdwn +6/−0
- doc/forum/Need_some_help_to_fix_my_repository.mdwn +31/−0
- doc/forum/Not_sure_how_to_get_my_s3_remote_back.mdwn +31/−0
- doc/forum/Ubuntu_PPA/comment_3_fc9cd51558c47718f243437202a11803._comment +8/−0
- doc/forum/Using_for_Music_repo.mdwn +13/−0
- doc/forum/git-annex_across_two_filesystems.mdwn +30/−0
- doc/forum/managing_multiple_repositories.mdwn +3/−0
- doc/git-annex.mdwn +40/−8
- doc/install/Debian.mdwn +13/−5
- doc/install/OSX/comment_18_537fad5d8854e765499d47602d1ab398._comment +8/−0
- doc/install/OSX/comment_19_18d4377f4ded5604d395d73783ba82c9._comment +8/−0
- doc/install/Ubuntu.mdwn +21/−6
- doc/install/fromscratch.mdwn +1/−0
- doc/news/version_4.20130227.mdwn +0/−29
- doc/news/version_4.20130501.mdwn +57/−0
- doc/preferred_content.mdwn +24/−0
- doc/special_remotes/S3.mdwn +1/−1
- doc/special_remotes/bup.mdwn +1/−1
- doc/special_remotes/directory.mdwn +1/−1
- doc/special_remotes/glacier.mdwn +1/−1
- doc/special_remotes/hook.mdwn +1/−1
- doc/special_remotes/rsync.mdwn +2/−2
- doc/special_remotes/webdav.mdwn +1/−1
- doc/special_remotes/xmpp/comment_2_9fc3f512020b7eb2591d6b7b2e8de2d7._comment +10/−0
- doc/templates/bugtemplate.mdwn +10/−4
- doc/tips/Internet_Archive_via_S3.mdwn +9/−0
- doc/todo/wishlist:_history_of_operations.mdwn +8/−0
- doc/users/fmarier.mdwn +3/−3
- doc/walkthrough/modifying_annexed_files.mdwn +2/−0
- git-annex.1 +38/−8
- git-annex.cabal +2/−3
- standalone/android/Makefile +14/−14
- standalone/android/evilsplicer-headers.hs +1/−0
- standalone/android/haskell-patches/DAV-0.3-0001-build-without-TH.patch +306/−0
- standalone/android/haskell-patches/gsasl-0.3.5-0001-link-with-libgsasl.patch +27/−0
- standalone/android/haskell-patches/lens-3.8.5-0001-build-without-TH.patch +293/−0
- standalone/licences.gz binary
- templates/configurators/addia.hamlet +32/−0
- templates/configurators/addrepository.hamlet +6/−1
- templates/configurators/addrepository/archive.hamlet +11/−0
- templates/configurators/addrepository/cloud.hamlet +1/−8
- templates/configurators/editrepository.hamlet +4/−0
- templates/configurators/enableia.hamlet +22/−0
- templates/configurators/main.hamlet +2/−2
- templates/configurators/pairing/xmpp/end.hamlet +1/−1
- templates/configurators/xmpp/needcloudrepo.hamlet +1/−1
- templates/documentation/repogroup.hamlet +5/−0
- templates/sidebar/alert.hamlet +5/−1
@@ -242,6 +242,7 @@ storedirect' [] = storeobject =<< calcRepo (gitAnnexLocation key) storedirect' (dest:fs) = do+ thawContentDir =<< calcRepo (gitAnnexLocation key) updateInodeCache key src thawContent src replaceFile dest $ liftIO . moveFile src@@ -353,12 +354,12 @@ removeAnnex key = withObjectLoc key remove removedirect where remove file = do- unlessM crippledFileSystem $- liftIO $ allowWrite $ parentDir file+ thawContentDir file liftIO $ nukeFile file removeInodeCache key cleanObjectLoc key removedirect fs = do+ thawContentDir =<< calcRepo (gitAnnexLocation key) cache <- recordedInodeCache key removeInodeCache key mapM_ (resetfile cache) fs@@ -374,8 +375,7 @@ fromAnnex :: Key -> FilePath -> Annex () fromAnnex key dest = do file <- calcRepo $ gitAnnexLocation key- unlessM crippledFileSystem $- liftIO $ allowWrite $ parentDir file+ thawContentDir file thawContent file liftIO $ moveFile file dest cleanObjectLoc key@@ -388,8 +388,7 @@ bad <- fromRepo gitAnnexBadDir let dest = bad </> takeFileName src createAnnexDirectory (parentDir dest)- unlessM crippledFileSystem $- liftIO $ allowWrite (parentDir src)+ thawContentDir src liftIO $ moveFile src dest cleanObjectLoc key logStatus key InfoMissing
@@ -25,6 +25,7 @@ import Annex.Link import Utility.InodeCache import Utility.CopyFile+import Annex.Perms {- Uses git ls-files to find files that need to be committed, and stages - them into the index. Returns True if some changes were staged. -}@@ -43,10 +44,15 @@ - efficiently as we can, by getting any key that's associated - with it in git, as well as its stat info. -} go (file, Just sha) = do- mkey <- catKey sha+ shakey <- catKey sha mstat <- liftIO $ catchMaybeIO $ getSymbolicLinkStatus file- case (mkey, mstat, toInodeCache =<< mstat) of- (Just key, _, Just cache) -> do+ filekey <- isAnnexLink file+ case (shakey, filekey, mstat, toInodeCache =<< mstat) of+ (_, Just key, _, _)+ | shakey == filekey -> noop+ {- A changed symlink. -}+ | otherwise -> stageannexlink file key+ (Just key, _, _, Just cache) -> do {- All direct mode files will show as - modified, so compare the cache to see if - it really was. -}@@ -55,9 +61,9 @@ [] -> modifiedannexed file key cache _ -> unlessM (elemInodeCaches cache oldcache) $ modifiedannexed file key cache- (Just key, Nothing, _) -> deletedannexed file key- (Nothing, Nothing, _) -> deletegit file- (_, Just _, _) -> addgit file+ (Just key, _, Nothing, _) -> deletedannexed file key+ (Nothing, _, Nothing, _) -> deletegit file+ (_, _, Just _, _) -> addgit file go _ = noop modifiedannexed file oldkey cache = do@@ -68,6 +74,11 @@ void $ removeAssociatedFile key file deletegit file + stageannexlink file key = do+ l <- inRepo $ gitAnnexLink file key+ stageSymlink file =<< hashSymlink l+ void $ addAssociatedFile key file+ addgit file = Annex.Queue.addCommand "add" [Param "-f"] [file] deletegit file = Annex.Queue.addCommand "rm" [Param "-f"] [file]@@ -177,6 +188,7 @@ [] -> ifM (liftIO $ doesFileExist loc) ( return $ Just $ do {- Move content from annex to direct file. -}+ thawContentDir loc updateInodeCache k loc thawContent loc replaceFile f $ liftIO . moveFile loc
@@ -0,0 +1,31 @@+{- git-annex environment+ -+ - Copyright 2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Annex.Environment where++import Common.Annex+import Utility.UserInfo+import qualified Git.Config++import System.Posix.Env++{- Checks that the system's environment allows git to function.+ - Git requires a GECOS username, or suitable git configuration, or+ - environment variables. -}+checkEnvironment :: Annex ()+checkEnvironment = do+ gitusername <- fromRepo $ Git.Config.getMaybe "user.name"+ when (gitusername == Nothing || gitusername == Just "") $+ liftIO checkEnvironmentIO++checkEnvironmentIO :: IO ()+checkEnvironmentIO = do+ whenM (null <$> myUserGecos) $ do+ username <- myUserName+ -- existing environment is not overwritten+ setEnv "GIT_AUTHOR_NAME" username False+ setEnv "GIT_COMMITTER_NAME" username False
@@ -14,9 +14,11 @@ import Utility.Matcher import Types.Group import Logs.Group+import Logs.Remote import Annex.UUID import qualified Annex import Git.FilePath+import Types.Remote (RemoteConfig) import Data.Either import qualified Data.Set as S@@ -45,10 +47,22 @@ ([], vs) -> Right $ generate vs (es, _) -> Left $ unwords $ map ("Parse failure: " ++) es -parseToken :: MkLimit -> GroupMap -> String -> Either String (Token MatchFiles)-parseToken checkpresent groupmap t+exprParser :: GroupMap -> M.Map UUID RemoteConfig -> Maybe UUID -> String -> [Either String (Token MatchFiles)]+exprParser groupmap configmap mu expr =+ map parse $ tokenizeMatcher expr+ where+ parse = parseToken + (limitPresent mu)+ (limitInDir preferreddir)+ groupmap+ preferreddir = fromMaybe "public" $+ M.lookup "preferreddir" =<< (`M.lookup` configmap) =<< mu++parseToken :: MkLimit -> MkLimit -> GroupMap -> String -> Either String (Token MatchFiles)+parseToken checkpresent checkpreferreddir groupmap t | t `elem` tokens = Right $ token t | t == "present" = use checkpresent+ | t == "inpreferreddir" = use checkpreferreddir | otherwise = maybe (Left $ "near " ++ show t) use $ M.lookup k $ M.fromList [ ("include", limitInclude)@@ -78,9 +92,9 @@ where go Nothing = return matchAll go (Just expr) = do- m <- groupMap+ gm <- groupMap+ rc <- readRemoteLog u <- getUUID- either badexpr return $ parsedToMatcher $- map (parseToken (limitPresent $ Just u) m)- (tokenizeMatcher expr)+ either badexpr return $+ parsedToMatcher $ exprParser gm rc (Just u) expr badexpr e = error $ "bad annex.largefiles configuration: " ++ e
@@ -12,6 +12,7 @@ noUmask, createContentDir, freezeContentDir,+ thawContentDir, ) where import Common.Annex@@ -86,6 +87,10 @@ go GroupShared = groupWriteRead dir go AllShared = groupWriteRead dir go _ = preventWrite dir++thawContentDir :: FilePath -> Annex ()+thawContentDir file = unlessM crippledFileSystem $+ liftIO $ allowWrite $ parentDir file {- Makes the directory tree to store an annexed file's content, - with appropriate permissions on each level. -}
@@ -30,7 +30,7 @@ - refs, per git-check-ref-format. -} toTaggedBranch :: UUID -> Maybe String -> Git.Branch -> Git.Branch-toTaggedBranch u info b = Git.Ref $ join "/" $ catMaybes+toTaggedBranch u info b = Git.Ref $ intercalate "/" $ catMaybes [ Just "refs/synced" , Just $ fromUUID u , toB64 <$> info
@@ -154,7 +154,6 @@ #warning Building without the webapp. You probably need to install Yesod.. import Assistant.Types.UrlRenderer #endif-import Assistant.Environment import qualified Utility.Daemon import Utility.LogFile import Utility.ThreadScheduler@@ -198,8 +197,6 @@ | otherwise = "watch" start daemonize webappwaiter = withThreadState $ \st -> do checkCanWatch- when assistant- checkEnvironment dstatus <- startDaemonStatus logfile <- fromRepo gitAnnexLogFile liftIO $ debugM desc $ "logging to " ++ logfile
@@ -66,7 +66,7 @@ , alertClosable = True , alertPriority = High , alertIcon = Just ErrorIcon- , alertCombiner = Just $ dataCombiner (++)+ , alertCombiner = Just $ dataCombiner $ \_old new -> new , alertName = Just $ WarningAlert name , alertButton = Nothing }@@ -233,7 +233,7 @@ render fs = tenseWords $ msg : fs combiner new old = take 10 $ new ++ old -addFileAlert :: FilePath -> Alert+addFileAlert :: String -> Alert addFileAlert = fileAlert (Tensed "Adding" "Added") {- This is only used as a success alert after a transfer, not during it. -}
@@ -1,6 +1,6 @@ {- git-annex assistant change tracking -- - Copyright 2012 Joey Hess <joey@kitenet.net>+ - Copyright 2012-2013 Joey Hess <joey@kitenet.net> - - Licensed under the GNU GPL version 3 or higher. -}@@ -9,7 +9,7 @@ import Assistant.Common import Assistant.Types.Changes-import Utility.TSet+import Utility.TList import Data.Time.Clock import Control.Concurrent.STM@@ -28,17 +28,20 @@ {- Gets all unhandled changes. - Blocks until at least one change is made. -} getChanges :: Assistant [Change]-getChanges = (atomically . getTSet) <<~ changeChan+getChanges = (atomically . getTList) <<~ changePool {- Gets all unhandled changes, without blocking. -} getAnyChanges :: Assistant [Change]-getAnyChanges = (atomically . readTSet) <<~ changeChan+getAnyChanges = (atomically . takeTList) <<~ changePool -{- Puts unhandled changes back into the channel.+{- Puts unhandled changes back into the pool. - Note: Original order is not preserved. -} refillChanges :: [Change] -> Assistant ()-refillChanges cs = (atomically . flip putTSet cs) <<~ changeChan+refillChanges cs = (atomically . flip appendTList cs) <<~ changePool -{- Records a change in the channel. -}+{- Records a change to the pool. -} recordChange :: Change -> Assistant ()-recordChange c = (atomically . flip putTSet1 c) <<~ changeChan+recordChange c = (atomically . flip snocTList c) <<~ changePool++recordChanges :: [Change] -> Assistant ()+recordChanges = refillChanges
@@ -9,20 +9,15 @@ import Assistant.Common import Assistant.Types.Commits-import Utility.TSet+import Utility.TList import Control.Concurrent.STM {- Gets all unhandled commits. - Blocks until at least one commit is made. -} getCommits :: Assistant [Commit]-getCommits = (atomically . getTSet) <<~ commitChan--{- Puts unhandled commits back into the channel.- - Note: Original order is not preserved. -}-refillCommits :: [Commit] -> Assistant ()-refillCommits cs = (atomically . flip putTSet cs) <<~ commitChan+getCommits = (atomically . getTList) <<~ commitChan {- Records a commit in the channel. -} recordCommit :: Assistant ()-recordCommit = (atomically . flip putTSet1 Commit) <<~ commitChan+recordCommit = (atomically . flip consTList Commit) <<~ commitChan
@@ -48,18 +48,19 @@ calcSyncRemotes :: Annex (DaemonStatus -> DaemonStatus) calcSyncRemotes = do rs <- filter (remoteAnnexSync . Remote.gitconfig) .- concat . Remote.byCost <$> Remote.enabledRemoteList+ concat . Remote.byCost <$> Remote.remoteList alive <- trustExclude DeadTrusted (map Remote.uuid rs) let good r = Remote.uuid r `elem` alive let syncable = filter good rs- let nonxmpp = filter (not . isXMPPRemote) syncable+ let syncdata = filter (not . remoteAnnexIgnore . Remote.gitconfig) $+ filter (not . isXMPPRemote) syncable return $ \dstatus -> dstatus { syncRemotes = syncable , syncGitRemotes = filter (not . Remote.specialRemote) syncable- , syncDataRemotes = nonxmpp- , syncingToCloudRemote = any iscloud nonxmpp+ , syncDataRemotes = syncdata+ , syncingToCloudRemote = any iscloud syncdata } where iscloud r = not (Remote.readonly r) && Remote.globallyAvailable r
@@ -18,6 +18,7 @@ import qualified Remote import Remote.List import qualified Git.Command+import qualified Git.Version import Logs.Trust import qualified Annex @@ -36,7 +37,11 @@ liftAnnex $ do inRepo $ Git.Command.run [ Param "remote"- , Param "remove"+ -- name of this subcommand changed+ , Param $+ if Git.Version.older "1.8.0"+ then "rm"+ else "remove" , Param (Remote.name remote) ] void $ remoteListRefresh
@@ -1,26 +0,0 @@-{- git-annex assistant environment- -- - Copyright 2012 Joey Hess <joey@kitenet.net>- -- - Licensed under the GNU GPL version 3 or higher.- -}--module Assistant.Environment where--import Assistant.Common-import Utility.UserInfo-import qualified Git.Config--import System.Posix.Env--{- Checks that the system's environment allows git to function.- - Git requires a GECOS username, or suitable git configuration, or- - environment variables. -}-checkEnvironment :: Annex ()-checkEnvironment = do- username <- liftIO myUserName- gecos <- liftIO myUserGecos- gitusername <- fromRepo $ Git.Config.getMaybe "user.name"- when (null gecos && (gitusername == Nothing || gitusername == Just "")) $- -- existing environment is not overwritten- liftIO $ setEnv "GIT_AUTHOR_NAME" username False
@@ -11,8 +11,9 @@ import Assistant.Common import Assistant.Install.AutoStart+import Assistant.Install.Menu import Assistant.Ssh-import Locations.UserConfig+import Config.Files import Utility.FileMode import Utility.Shell import Utility.TempFile@@ -49,6 +50,8 @@ #ifdef darwin_HOST_OS autostartfile <- userAutoStart osxAutoStartLabel #else+ installMenu program+ =<< desktopMenuFilePath "git-annex" <$> userDataDir autostartfile <- autoStartPath "git-annex" <$> userConfigDir #endif installAutoStart program autostartfile
@@ -0,0 +1,30 @@+{- Assistant menu installation.+ -+ - Copyright 2013 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++{-# LANGUAGE CPP #-}++module Assistant.Install.Menu where++import Utility.FreeDesktop++installMenu :: FilePath -> FilePath -> IO ()+installMenu command file =+#ifdef darwin_HOST_OS+ return ()+#else+ writeDesktopMenuFile (fdoDesktopMenu command) file+#endif++{- The command can be either just "git-annex", or the full path to use+ - to run it. -}+fdoDesktopMenu :: FilePath -> DesktopEntry+fdoDesktopMenu command = genDesktopEntry+ "Git Annex"+ "Track and sync the files in your Git Annex"+ False+ (command ++ " webapp")+ ["Network", "FileTransfer"]
binary file changed (absent → 3216 bytes)
@@ -1,6 +1,6 @@ {- git-annex assistant remote creation utilities -- - Copyright 2012 Joey Hess <joey@kitenet.net>+ - Copyright 2012, 2013 Joey Hess <joey@kitenet.net> - - Licensed under the GNU GPL version 3 or higher. -}@@ -22,6 +22,7 @@ import Git.Remote import Config import Config.Cost+import Creds import qualified Data.Text as T import qualified Data.Map as M@@ -69,17 +70,22 @@ , ("type", "rsync") ] -{- Inits a special remote. Currently, only 'weak' ciphers can be- - generated from the assistant, because otherwise GnuPG may block once- - the entropy pool is drained, and as of now there's no way to tell the- - user to perform IO actions to refill the pool. -}+{- Inits a new special remote, or enables an existing one.+ -+ - Currently, only 'weak' ciphers can be generated from the assistant,+ - because otherwise GnuPG may block once the entropy pool is drained,+ - and as of now there's no way to tell the user to perform IO actions+ - to refill the pool. -} makeSpecialRemote :: String -> RemoteType -> R.RemoteConfig -> Annex ()-makeSpecialRemote name remotetype config = do- (u, c) <- Command.InitRemote.findByName name- c' <- R.setup remotetype u $- M.insert "highRandomQuality" "false" $ M.union config c- describeUUID u name- configSet u c'+makeSpecialRemote name remotetype config =+ go =<< Command.InitRemote.findExisting name+ where+ go Nothing = go =<< Just <$> Command.InitRemote.generateNew name+ go (Just (u, c)) = do+ c' <- R.setup remotetype u $+ M.insert "highRandomQuality" "false" $ M.union config c+ describeUUID u name+ configSet u c' {- Returns the name of the git remote it created. If there's already a - remote at the location, returns its name. -}@@ -120,3 +126,25 @@ | n == 0 = legalbasename | otherwise = legalbasename ++ show n legalbasename = makeLegalName basename++{- Finds a CredPair belonging to any Remote that is of a given type+ - and matches some other criteria.+ -+ - This can be used as a default when another repository is being set up+ - using the same service.+ -+ - A function must be provided that returns the CredPairStorage+ - to use for a particular Remote's uuid.+ -}+previouslyUsedCredPair+ :: (UUID -> CredPairStorage)+ -> RemoteType+ -> (Remote -> Bool)+ -> Annex (Maybe CredPair)+previouslyUsedCredPair getstorage remotetype criteria =+ getM fromstorage =<< filter criteria . filter sametype <$> remoteList+ where+ sametype r = R.typename (R.remotetype r) == R.typename remotetype+ fromstorage r = do+ let storage = getstorage (R.uuid r)+ getRemoteCredPair (R.config r) storage
@@ -66,7 +66,7 @@ , transferrerPool :: TransferrerPool , failedPushMap :: FailedPushMap , commitChan :: CommitChan- , changeChan :: ChangeChan+ , changePool :: ChangePool , branchChangeHandle :: BranchChangeHandle , buddyList :: BuddyList , netMessager :: NetMessager@@ -83,7 +83,7 @@ <*> newTransferrerPool <*> newFailedPushMap <*> newCommitChan- <*> newChangeChan+ <*> newChangePool <*> newBranchChangeHandle <*> newBuddyList <*> newNetMessager
@@ -105,11 +105,11 @@ - present. -} addAuthorizedKeysCommand :: Bool -> FilePath -> SshPubKey -> String-addAuthorizedKeysCommand rsynconly dir pubkey = join "&&"+addAuthorizedKeysCommand rsynconly dir pubkey = intercalate "&&" [ "mkdir -p ~/.ssh"- , join "; "+ , intercalate "; " [ "if [ ! -e " ++ wrapper ++ " ]"- , "then (" ++ join ";" (map echoval script) ++ ") > " ++ wrapper+ , "then (" ++ intercalate ";" (map echoval script) ++ ") > " ++ wrapper , "fi" ] , "chmod 700 " ++ wrapper@@ -217,7 +217,7 @@ mangleSshHostName sshdata = "git-annex-" ++ T.unpack (sshHostName sshdata) ++ "-" ++ filter safe extra where- extra = join "_" $ map T.unpack $ catMaybes+ extra = intercalate "_" $ map T.unpack $ catMaybes [ sshUserName sshdata , Just $ sshDirectory sshdata ]@@ -229,7 +229,7 @@ {- Extracts the real hostname from a mangled ssh hostname. -} unMangleSshHostName :: String -> String unMangleSshHostName h = case split "-" h of- ("git":"annex":rest) -> join "-" (beginning rest)+ ("git":"annex":rest) -> intercalate "-" (beginning rest) _ -> h {- Does ssh have known_hosts data for a hostname? -}
@@ -68,7 +68,9 @@ go = do (failed, diverged) <- sync =<< liftAnnex (inRepo Git.Branch.current)- addScanRemotes diverged nonxmppremotes+ addScanRemotes diverged $+ filter (not . remoteAnnexIgnore . Remote.gitconfig)+ nonxmppremotes return failed {- Updates the local sync branch, then pushes it to all remotes, in@@ -111,7 +113,7 @@ let (xmppremotes, normalremotes) = partition isXMPPRemote remotes ret <- go True branch g u normalremotes forM_ xmppremotes $ \r ->- sendNetMessage $ Pushing (getXMPPClientID r) CanPush+ sendNetMessage $ Pushing (getXMPPClientID r) (CanPush u) return ret where go _ Nothing _ _ _ = return [] -- no branch, so nothing to do@@ -200,8 +202,9 @@ haddiverged <- liftAnnex Annex.Branch.forceUpdate forM_ normalremotes $ \r -> liftAnnex $ Command.Sync.mergeRemote r currentbranch+ u <- liftAnnex getUUID forM_ xmppremotes $ \r ->- sendNetMessage $ Pushing (getXMPPClientID r) PushRequest+ sendNetMessage $ Pushing (getXMPPClientID r) (PushRequest u) return (catMaybes failed, haddiverged) {- Start syncing a remote, using a background thread. -}
@@ -52,7 +52,7 @@ =<< annexDelayAdd <$> Annex.getGitConfig waitChangeTime $ \(changes, time) -> do readychanges <- handleAdds delayadd changes- if shouldCommit time readychanges+ if shouldCommit time (length readychanges) readychanges then do debug [ "committing"@@ -62,8 +62,12 @@ void $ alertWhile commitAlert $ liftAnnex commitStaged recordCommit+ let numchanges = length readychanges mapM_ checkChangeContent readychanges- else refill readychanges+ return numchanges+ else do+ refill readychanges+ return 0 refill :: [Change] -> Assistant () refill [] = noop@@ -72,21 +76,33 @@ refillChanges cs {- Wait for one or more changes to arrive to be committed. -}-waitChangeTime :: (([Change], UTCTime) -> Assistant ()) -> Assistant ()-waitChangeTime a = runEvery (Seconds 1) <~> do- -- We already waited one second as a simple rate limiter.- -- Next, wait until at least one change is available for- -- processing.- changes <- getChanges- -- See if now's a good time to commit.- now <- liftIO getCurrentTime- case (shouldCommit now changes, possiblyrename changes) of- (True, False) -> a (changes, now)- (True, True) -> do- morechanges <- getrelatedchanges changes- a (changes ++ morechanges, now)- _ -> refill changes+waitChangeTime :: (([Change], UTCTime) -> Assistant Int) -> Assistant ()+waitChangeTime a = go [] 0 where+ go unhandled lastcommitsize = do+ -- Wait one one second as a simple rate limiter.+ liftIO $ threadDelaySeconds (Seconds 1)+ -- Now, wait until at least one change is available for+ -- processing.+ cs <- getChanges+ let changes = unhandled ++ cs+ let len = length changes+ -- See if now's a good time to commit.+ now <- liftIO getCurrentTime+ case (lastcommitsize >= maxCommitSize, shouldCommit now len changes, possiblyrename changes) of+ (True, True, _)+ | len > maxCommitSize -> + go [] =<< a (changes, now)+ | otherwise -> aftermaxcommit changes+ (_, True, False) ->+ go [] =<< a (changes, now)+ (_, True, True) -> do+ morechanges <- getrelatedchanges changes+ go [] =<< a (changes ++ morechanges, now)+ _ -> do+ refill changes+ go [] lastcommitsize+ {- Did we perhaps only get one of the AddChange and RmChange pair - that make up a file rename? Or some of the pairs that make up - a directory rename?@@ -116,6 +132,41 @@ then return cs else getbatchchanges (cs':cs) + {- The last commit was maximum size, so it's very likely there+ - are more changes and we'd like to ensure we make another commit+ - of maximum size if possible.+ -+ - But, it can take a while for the Watcher to wake back up+ - after a commit. It can get blocked by another thread+ - that is using the Annex state, such as a git-annex branch+ - commit. Especially after such a large commit, this can+ - take several seconds. When this happens, it defeats the+ - normal commit batching, which sees some old changes the+ - Watcher found while the commit was being prepared, and sees+ - no recent ones, and wants to commit immediately.+ -+ - All that we need to do, then, is wait for the Watcher to+ - wake up, and queue up one more change.+ -+ - However, it's also possible that we're at the end of changes for+ - now. So to avoid waiting a really long time before committing+ - those changes we have, poll for up to 30 seconds, and then+ - commit them.+ -+ - Also, try to run something in Annex, to ensure we block+ - longer if the Annex state is indeed blocked.+ -}+ aftermaxcommit oldchanges = loop (30 :: Int)+ where+ loop 0 = go oldchanges 0+ loop n = do+ liftAnnex noop -- ensure Annex state is free+ liftIO $ threadDelaySeconds (Seconds 1)+ changes <- getAnyChanges+ if null changes+ then loop (n - 1)+ else go (oldchanges ++ changes) 0+ isRmChange :: Change -> Bool isRmChange (Change { changeInfo = i }) | i == RmChange = True isRmChange _ = False@@ -131,20 +182,22 @@ humanImperceptibleDelay = threadDelay $ truncate $ humanImperceptibleInterval * fromIntegral oneSecond +maxCommitSize :: Int+maxCommitSize = 5000+ {- Decide if now is a good time to make a commit. - Note that the list of changes has an undefined order. - - Current strategy: If there have been 10 changes within the past second, - a batch activity is taking place, so wait for later. -}-shouldCommit :: UTCTime -> [Change] -> Bool-shouldCommit now changes+shouldCommit :: UTCTime -> Int -> [Change] -> Bool+shouldCommit now len changes | len == 0 = False- | len > 10000 = True -- avoid bloating queue too much+ | len >= maxCommitSize = True | length recentchanges < 10 = True | otherwise = False -- batch activity where- len = length changes thissecond c = timeDelta c <= 1 recentchanges = filter thissecond changes timeDelta c = now `diffUTCTime` changeTime c@@ -157,28 +210,30 @@ case v of Left _ -> return False Right _ -> do- direct <- isDirect- let params = nomessage $ catMaybes- [ Just $ Param "--quiet"- {- In indirect mode, avoid running the- - usual git-annex pre-commit hook;- - watch does the same symlink fixing,- - and we don't want to deal with unlocked- - files in these commits. -}- , if direct then Nothing else Just $ Param "--no-verify"- ] {- Empty commits may be made if tree changes cancel - each other out, etc. Git returns nonzero on those, - so don't propigate out commit failures. -} void $ inRepo $ catchMaybeIO . - Git.Command.runQuiet (Param "commit" : params)+ Git.Command.runQuiet+ (Param "commit" : nomessage params) return True where+ params =+ [ Param "--quiet"+ {- Avoid running the usual pre-commit hook;+ - the Watcher does the same symlink fixing,+ - and direct mode bookkeeping updating. -}+ , Param "--no-verify"+ ] nomessage ps- | Git.Version.older "1.7.2" = Param "-m"- : Param "autocommit" : ps- | otherwise = Param "--allow-empty-message" - : Param "-m" : Param "" : ps+ | Git.Version.older "1.7.2" =+ Param "-m" : Param "autocommit" : ps+ | Git.Version.older "1.7.8" =+ Param "--allow-empty-message" :+ Param "-m" : Param "" : ps+ | otherwise =+ Param "--allow-empty-message" :+ Param "--no-edit" : Param "-m" : Param "" : ps {- OSX needs a short delay after a file is added before locking it down, - when using a non-direct mode repository, as pasting a file seems to@@ -227,9 +282,10 @@ refillChanges postponed returnWhen (null toadd) $ do- added <- catMaybes <$> if direct- then adddirect toadd- else forM toadd add+ added <- addaction toadd $+ catMaybes <$> if direct+ then adddirect toadd+ else forM toadd add if DirWatcher.eventsCoalesce || null added || direct then return $ added ++ otherchanges else do@@ -252,19 +308,13 @@ | otherwise = a add :: Change -> Assistant (Maybe Change)- add change@(InProcessAddChange { keySource = ks }) = do- alertWhile' (addFileAlert $ keyFilename ks) $- liftM ret $ catchMaybeIO <~> do- sanitycheck ks $ do- key <- liftAnnex $ do- showStart "add" $ keyFilename ks- Command.Add.ingest $ Just ks- maybe failedingest (done change $ keyFilename ks) key- where- {- Add errors tend to be transient and will be automatically- - dealt with, so don't pass to the alert code. -}- ret (Just j@(Just _)) = (True, j)- ret _ = (True, Nothing)+ add change@(InProcessAddChange { keySource = ks }) = + catchDefaultIO Nothing <~> do+ sanitycheck ks $ do+ key <- liftAnnex $ do+ showStart "add" $ keyFilename ks+ Command.Add.ingest $ Just ks+ maybe (failedingest change) (done change $ keyFilename ks) key add _ = return Nothing {- In direct mode, avoid overhead of re-injesting a renamed@@ -302,7 +352,8 @@ mkpairs k = map (\c -> (inodeCacheToKey ct c, k)) <$> recordedInodeCache k - failedingest = do+ failedingest change = do+ refill [retryChange change] liftAnnex showEndFail return Nothing @@ -330,6 +381,26 @@ when (contentLocation keysource /= keyFilename keysource) $ void $ liftIO $ tryIO $ removeFile $ contentLocation keysource return Nothing++ {- Shown an alert while performing an action to add a file or+ - files. When only one file is added, its name is shown+ - in the alert. When it's a batch add, the number of files added+ - is shown.+ -+ - Add errors tend to be transient and will be+ - automatically dealt with, so the alert is always told+ - the add succeeded.+ -}+ addaction [] a = a+ addaction toadd a = alertWhile' (addFileAlert msg) $+ (,) + <$> pure True+ <*> a+ where+ msg = case toadd of+ (InProcessAddChange { keySource = ks }:[]) ->+ keyFilename ks+ _ -> show (length toadd) ++ " files" {- Files can Either be Right to be added now, - or are unsafe, and must be Left for later.
@@ -32,10 +32,11 @@ let dir = Git.localGitDir g </> "refs" liftIO $ createDirectoryIfMissing True dir let hook a = Just <$> asIO2 (runHandler a)- addhook <- hook onAdd+ changehook <- hook onChange errhook <- hook onErr let hooks = mkWatchHooks- { addHook = addhook+ { addHook = changehook+ , modifyHook = changehook , errHook = errhook } void $ liftIO $ watchDir dir (const False) hooks id@@ -55,19 +56,14 @@ onErr :: Handler onErr msg = error msg -{- Called when a new branch ref is written.- -- - This relies on git's atomic method of updating branch ref files,- - which is to first write the new file to .lock, and then rename it- - over the old file. So, ignore .lock files, and the rename ensures- - the watcher sees a new file being added on each update.+{- Called when a new branch ref is written, or a branch ref is modified. - - At startup, synthetic add events fire, causing this to run, but that's - ok; it ensures that any changes pushed since the last time the assistant - ran are merged in. -}-onAdd :: Handler-onAdd file+onChange :: Handler+onChange file | ".lock" `isSuffixOf` file = noop | isAnnexBranch file = do branchChanged@@ -75,7 +71,7 @@ when diverged $ unlessM handleDesynced $ queueDeferredDownloads "retrying deferred download" Later- | "/synced/" `isInfixOf` file = do+ | "/synced/" `isInfixOf` file = mergecurrent =<< liftAnnex (inRepo Git.Branch.current) | otherwise = noop where
@@ -157,7 +157,7 @@ handleMount dir = do debug ["detected mount of", dir] rs <- filter (Git.repoIsLocal . Remote.repo) <$> remotesUnder dir- reconnectRemotes True $ filter (not . remoteAnnexIgnore . Remote.gitconfig) rs+ reconnectRemotes True rs {- Finds remotes located underneath the mount point. -
@@ -128,4 +128,4 @@ {- Finds network remotes. -} networkRemotes :: Assistant [Remote] networkRemotes = liftAnnex $- filter (isNothing . Remote.localpath) <$> enabledRemoteList+ filter (isNothing . Remote.localpath) <$> remoteList
@@ -9,7 +9,6 @@ import Assistant.Common import Assistant.Commits-import Assistant.Types.Commits import Assistant.Pushes import Assistant.DaemonStatus import Assistant.Sync@@ -33,13 +32,9 @@ pushThread = namedThread "Pusher" $ runEvery (Seconds 2) <~> do -- We already waited two seconds as a simple rate limiter. -- Next, wait until at least one commit has been made- commits <- getCommits+ void getCommits -- Now see if now's a good time to push.- if shouldPush commits- then void $ pushToRemotes True =<< pushTargets- else do- debug ["delaying push of", show (length commits), "commits"]- refillCommits commits+ void $ pushToRemotes True =<< pushTargets {- We want to avoid pushing to remotes that are marked readonly. -@@ -51,14 +46,3 @@ where candidates = filter (not . Remote.readonly) . syncGitRemotes available = maybe (return True) doesDirectoryExist . Remote.localpath--{- Decide if now is a good time to push to remotes.- -- - Current strategy: Immediately push all commits. The commit machinery- - already determines batches of changes, so we can't easily determine- - batches better.- -}-shouldPush :: [Commit] -> Bool-shouldPush commits- | not (null commits) = True- | otherwise = False
@@ -22,7 +22,7 @@ import qualified Remote import qualified Types.Remote as Remote import qualified Git-import Locations.UserConfig+import Config.Files import Assistant.Threads.TransferWatcher import Annex.Wanted
@@ -130,8 +130,7 @@ top <- liftAnnex $ fromRepo Git.repoPath (fs, cleanup) <- liftAnnex $ inRepo $ LsFiles.deleted [top] forM_ fs $ \f -> do- liftAnnex $ Annex.Queue.addUpdateIndex =<<- inRepo (Git.UpdateIndex.unstageFile f)+ liftAnnex $ onDel' f maybe noop recordChange =<< madeChange f RmChange void $ liftIO $ cleanup @@ -204,9 +203,12 @@ - really modified, but it might have - just been deleted and been put back, - so it symlink is restaged to make sure. -}- ( do- link <- liftAnnex $ inRepo $ gitAnnexLink file key- addLink file link (Just key)+ ( ifM (scanComplete <$> getDaemonStatus)+ ( do+ link <- liftAnnex $ inRepo $ gitAnnexLink file key+ addLink file link (Just key)+ , noChange+ ) , guardSymlinkStandin (Just key) $ do debug ["changed direct", file] liftAnnex $ changedDirect key file@@ -296,11 +298,19 @@ onDel :: Handler onDel file _ = do debug ["file deleted", file]- liftAnnex $ - Annex.Queue.addUpdateIndex =<<- inRepo (Git.UpdateIndex.unstageFile file)+ liftAnnex $ onDel' file madeChange file RmChange +onDel' :: FilePath -> Annex ()+onDel' file = do+ whenM isDirect $ do+ mkey <- catKeyFile file+ case mkey of+ Nothing -> noop+ Just key -> void $ removeAssociatedFile key file+ Annex.Queue.addUpdateIndex =<<+ inRepo (Git.UpdateIndex.unstageFile file)+ {- A directory has been deleted, or moved, so tell git to remove anything - that was inside it from its cache. Since it could reappear at any time, - use --cached to only delete it from the index.@@ -312,13 +322,12 @@ debug ["directory deleted", dir] (fs, clean) <- liftAnnex $ inRepo $ LsFiles.deleted [dir] - liftAnnex $ forM_ fs $ \f -> Annex.Queue.addUpdateIndex =<<- inRepo (Git.UpdateIndex.unstageFile f)+ liftAnnex $ mapM_ onDel' fs -- Get the events queued up as fast as possible, so the -- committer sees them all in one block. now <- liftIO getCurrentTime- forM_ fs $ \f -> recordChange $ Change now f RmChange+ recordChanges $ map (\f -> Change now f RmChange) fs void $ liftIO $ clean liftAnnex $ Annex.Queue.flushWhenFull
@@ -22,6 +22,7 @@ import Assistant.WebApp.Configurators.Ssh import Assistant.WebApp.Configurators.Pairing import Assistant.WebApp.Configurators.AWS+import Assistant.WebApp.Configurators.IA import Assistant.WebApp.Configurators.WebDAV import Assistant.WebApp.Configurators.XMPP import Assistant.WebApp.Configurators.Preferences@@ -37,7 +38,6 @@ import Git import Yesod-import Yesod.Static import Network.Socket (SockAddr, HostName) import Data.Text (pack, unpack)
@@ -25,6 +25,7 @@ import Assistant.Pairing import Assistant.XMPP.Git import Annex.UUID+import Logs.UUID import Network.Protocol.XMPP import Control.Concurrent@@ -84,7 +85,7 @@ inAssistant $ do modifyDaemonStatus_ $ \s -> s { xmppClientID = Just $ xmppJID creds }- debug ["connected", show selfjid]+ debug ["connected", logJid selfjid] xmppThread $ receivenotifications selfjid forever $ do@@ -94,7 +95,7 @@ receivenotifications selfjid = forever $ do l <- decodeStanza selfjid <$> getStanza inAssistant $ debug- ["received:", show $ map sanitizeXMPPEvent l]+ ["received:", show $ map logXMPPEvent l] mapM_ (handle selfjid) l handle selfjid (PresenceMessage p) = do@@ -123,8 +124,8 @@ let msg' = readdressNetMessage msg c inAssistant $ debug [ "sending to new client:"- , show c- , show $ sanitizeNetMessage msg'+ , logJid jid+ , show $ logNetMessage msg' ] a <- inAssistant $ convertNetMsg msg' selfjid a@@ -139,10 +140,29 @@ | ProtocolError ReceivedStanza deriving Show -sanitizeXMPPEvent :: XMPPEvent -> XMPPEvent-sanitizeXMPPEvent (GotNetMessage m) = GotNetMessage $ sanitizeNetMessage m-sanitizeXMPPEvent v = v+logXMPPEvent :: XMPPEvent -> String+logXMPPEvent (GotNetMessage m) = logNetMessage m+logXMPPEvent (PresenceMessage p) = logPresence p+logXMPPEvent (Ignorable (ReceivedPresence p)) = "Ignorable " ++ logPresence p+logXMPPEvent v = show v +logPresence :: Presence -> String+logPresence (p@Presence { presenceFrom = Just jid }) = unwords+ [ "Presence from"+ , logJid jid+ , show $ extractGitAnnexTag p+ ]+logPresence _ = "Presence from unknown"++logJid :: JID -> String+logJid jid =+ let name = T.unpack (buddyName jid)+ resource = maybe "" (T.unpack . strResource) (jidResource jid)+ in take 1 name ++ show (length name) ++ "/" ++ resource++logClient :: Client -> String+logClient (Client jid) = logJid jid+ {- Decodes an XMPP stanza into one or more events. -} decodeStanza :: JID -> ReceivedStanza -> [XMPPEvent] decodeStanza selfjid s@(ReceivedPresence p)@@ -180,7 +200,7 @@ relayNetMessage :: JID -> Assistant (XMPP ()) relayNetMessage selfjid = do msg <- waitNetMessage- debug ["sending:", show $ sanitizeNetMessage msg]+ debug ["sending:", logNetMessage msg] a1 <- handleImportant msg a2 <- convert msg return (a1 >> a2)@@ -197,10 +217,12 @@ then do clients <- maybe [] (S.toList . buddyAssistants) <$> getBuddy (genBuddyKey tojid) <<~ buddyList- debug ["exploded undirected message to clients", show clients]+ debug ["exploded undirected message to clients", unwords $ map logClient clients] return $ forM_ (clients) $ \(Client jid) -> putStanza $ pushMessage pushstage jid selfjid- else return $ putStanza $ pushMessage pushstage tojid selfjid+ else do+ debug ["to client:", logJid tojid]+ return $ putStanza $ pushMessage pushstage tojid selfjid convert msg = convertNetMsg msg selfjid {- Converts a NetMessage to an XMPP action. -}@@ -261,19 +283,26 @@ unlessM (null . fst <$> manualPull branch [r]) $ pullone rs branch +{- PairReq from another client using our JID is automatically+ - accepted. This is so pairing devices all using the same XMPP+ - account works without confirmations.+ -+ - Also, autoaccept PairReq from the same JID of any repo we've+ - already paired with, as long as the UUID in the PairReq is+ - one we know about.+-} pairMsgReceived :: UrlRenderer -> PairStage -> UUID -> JID -> JID -> Assistant () pairMsgReceived urlrenderer PairReq theiruuid selfjid theirjid | baseJID selfjid == baseJID theirjid = autoaccept | otherwise = do knownjids <- catMaybes . map (parseJID . getXMPPClientID) . filter isXMPPRemote . syncRemotes <$> getDaemonStatus- if any (== baseJID theirjid) knownjids+ um <- liftAnnex uuidMap+ if any (== baseJID theirjid) knownjids && M.member theiruuid um then autoaccept else showalert where- -- PairReq from another client using our JID, or the JID of- -- any repo we're already paired with is automatically accepted. autoaccept = do selfuuid <- liftAnnex getUUID sendNetMessage $@@ -288,9 +317,9 @@ (T.unpack $ buddyName theirjid) button +{- PairAck must come from one of the buddies we are pairing with;+ - don't pair with just anyone. -} pairMsgReceived _ PairAck theiruuid _selfjid theirjid =- {- PairAck must come from one of the buddies we are pairing with;- - don't pair with just anyone. -} whenM (isBuddyPairing theirjid) $ do changeBuddyPairing theirjid False selfuuid <- liftAnnex getUUID
@@ -29,6 +29,7 @@ import qualified Remote import qualified Types.Remote as Remote import Annex.Wanted+import Utility.TList import Control.Concurrent.STM import qualified Data.Map as M@@ -38,7 +39,7 @@ {- Reads the queue's content without blocking or changing it. -} getTransferQueue :: Assistant [(Transfer, TransferInfo)]-getTransferQueue = (atomically . readTVar . queuelist) <<~ transferQueue+getTransferQueue = (atomically . readTList . queuelist) <<~ transferQueue stubInfo :: AssociatedFile -> Remote -> TransferInfo stubInfo f r = stubTransferInfo@@ -94,8 +95,7 @@ | direction == Download = do q <- getAssistant transferQueue void $ liftIO $ atomically $- modifyTVar' (deferreddownloads q) $- \l -> (k, f):l+ consTList (deferreddownloads q) (k, f) | otherwise = noop {- Queues any deferred downloads that can now be accomplished, leaving@@ -103,12 +103,11 @@ queueDeferredDownloads :: Reason -> Schedule -> Assistant () queueDeferredDownloads reason schedule = do q <- getAssistant transferQueue- l <- liftIO $ atomically $ swapTVar (deferreddownloads q) []+ l <- liftIO $ atomically $ readTList (deferreddownloads q) rs <- syncDataRemotes <$> getDaemonStatus left <- filterM (queue rs) l unless (null left) $- liftIO $ atomically $ modifyTVar' (deferreddownloads q) $- \new -> new ++ left+ liftIO $ atomically $ appendTList (deferreddownloads q) left where queue rs (k, f) = do uuids <- liftAnnex $ Remote.keyLocations k@@ -127,10 +126,9 @@ enqueue :: Reason -> Schedule -> Transfer -> TransferInfo -> Assistant () enqueue reason schedule t info- | schedule == Next = go (new:)- | otherwise = go (\l -> l++[new])+ | schedule == Next = go consTList+ | otherwise = go snocTList where- new = (t, info) go modlist = whenM (add modlist) $ do debug [ "queued", describeTransfer t info, ": " ++ reason ] notifyTransfer@@ -140,11 +138,11 @@ liftIO $ atomically $ ifM (checkRunningTransferSTM dstatus t) ( return False , do- l <- readTVar (queuelist q)+ l <- readTList (queuelist q) if (t `notElem` map fst l) then do void $ modifyTVar' (queuesize q) succ- void $ modifyTVar' (queuelist q) modlist+ void $ modlist (queuelist q) (t, info) return True else return False )@@ -185,9 +183,9 @@ if sz < 1 then retry -- blocks until queuesize changes else do- (r@(t,info):rest) <- readTVar (queuelist q)- writeTVar (queuelist q) rest+ (r@(t,info):rest) <- readTList (queuelist q) void $ modifyTVar' (queuesize q) pred+ setTList (queuelist q) rest if acceptable info then do adjustTransfersSTM dstatus $@@ -219,8 +217,7 @@ dequeueTransfersSTM :: TransferQueue -> (Transfer -> Bool) -> STM [(Transfer, TransferInfo)] dequeueTransfersSTM q c = do- (removed, ts) <- partition (c . fst)- <$> readTVar (queuelist q)+ (removed, ts) <- partition (c . fst) <$> readTList (queuelist q) void $ writeTVar (queuesize q) (length ts)- void $ writeTVar (queuelist q) ts+ setTList (queuelist q) ts return removed
@@ -1,6 +1,6 @@ {- git-annex assistant change tracking -- - Copyright 2012 Joey Hess <joey@kitenet.net>+ - Copyright 2012-2013 Joey Hess <joey@kitenet.net> - - Licensed under the GNU GPL version 3 or higher. -}@@ -9,23 +9,22 @@ import Types.KeySource import Types.Key-import Utility.TSet+import Utility.TList -import Data.Time.Clock import Control.Concurrent.STM--data ChangeInfo = AddKeyChange Key | AddFileChange | LinkChange (Maybe Key) | RmChange- deriving (Show, Eq)--changeInfoKey :: ChangeInfo -> Maybe Key-changeInfoKey (AddKeyChange k) = Just k-changeInfoKey (LinkChange (Just k)) = Just k-changeInfoKey _ = Nothing+import Data.Time.Clock -type ChangeChan = TSet Change+{- An un-ordered pool of Changes that have been noticed and should be+ - staged and committed. Changes will typically be in order, but ordering+ - may be lost. In any case, order should not matter, as any given Change+ - may later be reverted by a later Change (ie, a file is added and then+ - deleted). Code that processes the changes needs to deal with such+ - scenarios.+ -}+type ChangePool = TList Change -newChangeChan :: IO ChangeChan-newChangeChan = atomically newTSet+newChangePool :: IO ChangePool+newChangePool = atomically newTList data Change = Change @@ -43,6 +42,14 @@ } deriving (Show) +data ChangeInfo = AddKeyChange Key | AddFileChange | LinkChange (Maybe Key) | RmChange+ deriving (Show, Eq, Ord)++changeInfoKey :: ChangeInfo -> Maybe Key+changeInfoKey (AddKeyChange k) = Just k+changeInfoKey (LinkChange (Just k)) = Just k+changeInfoKey _ = Nothing+ changeFile :: Change -> FilePath changeFile (Change _ f _) = f changeFile (PendingAddChange _ f) = f@@ -55,6 +62,11 @@ isInProcessAddChange :: Change -> Bool isInProcessAddChange (InProcessAddChange {}) = True isInProcessAddChange _ = False++retryChange :: Change -> Change+retryChange (InProcessAddChange time ks) =+ PendingAddChange time (keyFilename ks)+retryChange c = c finishedChange :: Change -> Key -> Change finishedChange c@(InProcessAddChange { keySource = ks }) k = Change
@@ -7,13 +7,13 @@ module Assistant.Types.Commits where -import Utility.TSet+import Utility.TList import Control.Concurrent.STM -type CommitChan = TSet Commit+type CommitChan = TList Commit data Commit = Commit newCommitChan :: IO CommitChan-newCommitChan = atomically newTSet+newCommitChan = atomically newTList
@@ -10,11 +10,12 @@ import Common.Annex import Assistant.Pairing -import Data.Text (Text) import Control.Concurrent.STM import Control.Concurrent.MSampleVar import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as B8+import Data.Text (Text)+import qualified Data.Text as T import qualified Data.Set as S import qualified Data.Map as M @@ -36,11 +37,11 @@ data PushStage -- indicates that we have data to push over the out of band network- = CanPush+ = CanPush UUID -- request that a git push be sent over the out of band network- | PushRequest+ | PushRequest UUID -- indicates that a push is starting- | StartingPush+ | StartingPush UUID -- a chunk of output of git receive-pack | ReceivePackOutput SequenceNum ByteString -- a chuck of output of git send-pack@@ -57,8 +58,8 @@ {- NetMessages that are important (and small), and should be stored to be - resent when new clients are seen. -} isImportantNetMessage :: NetMessage -> Maybe ClientID-isImportantNetMessage (Pushing c CanPush) = Just c-isImportantNetMessage (Pushing c PushRequest) = Just c+isImportantNetMessage (Pushing c (CanPush _)) = Just c+isImportantNetMessage (Pushing c (PushRequest _)) = Just c isImportantNetMessage _ = Nothing readdressNetMessage :: NetMessage -> ClientID -> NetMessage@@ -67,29 +68,35 @@ readdressNetMessage m _ = m {- Convert a NetMessage to something that can be logged. -}-sanitizeNetMessage :: NetMessage -> NetMessage-sanitizeNetMessage (Pushing c stage) = Pushing c $ case stage of- ReceivePackOutput n _ -> ReceivePackOutput n elided- SendPackOutput n _ -> SendPackOutput n elided- s -> s+logNetMessage :: NetMessage -> String+logNetMessage (Pushing c stage) = show $ Pushing (logClientID c) $+ case stage of+ ReceivePackOutput n _ -> ReceivePackOutput n elided+ SendPackOutput n _ -> SendPackOutput n elided+ s -> s where elided = B8.pack "<elided>"-sanitizeNetMessage m = m+logNetMessage (PairingNotification stage c uuid) =+ show $ PairingNotification stage (logClientID c) uuid+logNetMessage m = show m +logClientID :: ClientID -> ClientID+logClientID c = T.concat [T.take 1 c, T.pack $ show $ T.length c]+ {- Things that initiate either side of a push, but do not actually send data. -} isPushInitiation :: PushStage -> Bool-isPushInitiation CanPush = True-isPushInitiation PushRequest = True-isPushInitiation StartingPush = True+isPushInitiation (CanPush _) = True+isPushInitiation (PushRequest _) = True+isPushInitiation (StartingPush _) = True isPushInitiation _ = False data PushSide = SendPack | ReceivePack deriving (Eq, Ord) pushDestinationSide :: PushStage -> PushSide-pushDestinationSide CanPush = ReceivePack-pushDestinationSide PushRequest = SendPack-pushDestinationSide StartingPush = ReceivePack+pushDestinationSide (CanPush _) = ReceivePack+pushDestinationSide (PushRequest _) = SendPack+pushDestinationSide (StartingPush _) = ReceivePack pushDestinationSide (ReceivePackOutput _ _) = SendPack pushDestinationSide (SendPackOutput _ _) = ReceivePack pushDestinationSide (ReceivePackDone _) = SendPack
@@ -12,11 +12,12 @@ import Types.Remote import Control.Concurrent.STM+import Utility.TList data TransferQueue = TransferQueue { queuesize :: TVar Int- , queuelist :: TVar [(Transfer, TransferInfo)]- , deferreddownloads :: TVar [(Key, AssociatedFile)]+ , queuelist :: TList (Transfer, TransferInfo)+ , deferreddownloads :: TList (Key, AssociatedFile) } data Schedule = Next | Later@@ -25,5 +26,5 @@ newTransferQueue :: IO TransferQueue newTransferQueue = atomically $ TransferQueue <$> newTVar 0- <*> newTVar []- <*> newTVar []+ <*> newTList+ <*> newTList
@@ -36,5 +36,9 @@ makeMiscRepositories :: Widget makeMiscRepositories = $(widgetFile "configurators/addrepository/misc") -makeCloudRepositories :: Bool -> Widget-makeCloudRepositories onlyTransfer = $(widgetFile "configurators/addrepository/cloud")+makeCloudRepositories :: Widget+makeCloudRepositories = $(widgetFile "configurators/addrepository/cloud")++makeArchiveRepositories :: Widget+makeArchiveRepositories = $(widgetFile "configurators/addrepository/archive")+
@@ -19,9 +19,11 @@ import qualified Remote.Helper.AWS as AWS import Logs.Remote import qualified Remote+import qualified Types.Remote as Remote import Types.Remote (RemoteConfig) import Types.StandardGroups import Logs.PreferredContent+import Creds import qualified Data.Text as T import qualified Data.Map as M@@ -61,10 +63,10 @@ extractCreds :: AWSInput -> AWSCreds extractCreds i = AWSCreds (accessKeyID i) (secretAccessKey i) -s3InputAForm :: AForm WebApp WebApp AWSInput-s3InputAForm = AWSInput- <$> accessKeyIDField- <*> secretAccessKeyField+s3InputAForm :: Maybe CredPair -> AForm WebApp WebApp AWSInput+s3InputAForm defcreds = AWSInput+ <$> accessKeyIDFieldWithHelp (T.pack . fst <$> defcreds)+ <*> secretAccessKeyField (T.pack . snd <$> defcreds) <*> datacenterField AWS.S3 <*> areq (selectFieldList storageclasses) "Storage class" (Just StandardRedundancy) <*> areq textField "Repository name" (Just "S3")@@ -76,30 +78,33 @@ , ("Reduced redundancy (costs less)", ReducedRedundancy) ] -glacierInputAForm :: AForm WebApp WebApp AWSInput-glacierInputAForm = AWSInput- <$> accessKeyIDField- <*> secretAccessKeyField+glacierInputAForm :: Maybe CredPair -> AForm WebApp WebApp AWSInput+glacierInputAForm defcreds = AWSInput+ <$> accessKeyIDFieldWithHelp (T.pack . fst <$> defcreds)+ <*> secretAccessKeyField (T.pack . snd <$> defcreds) <*> datacenterField AWS.Glacier <*> pure StandardRedundancy <*> areq textField "Repository name" (Just "glacier") <*> enableEncryptionField -awsCredsAForm :: AForm WebApp WebApp AWSCreds-awsCredsAForm = AWSCreds- <$> accessKeyIDField- <*> secretAccessKeyField+awsCredsAForm :: Maybe CredPair -> AForm WebApp WebApp AWSCreds+awsCredsAForm defcreds = AWSCreds+ <$> accessKeyIDFieldWithHelp (T.pack . fst <$> defcreds)+ <*> secretAccessKeyField (T.pack . snd <$> defcreds) -accessKeyIDField :: AForm WebApp WebApp Text-accessKeyIDField = areq (textField `withNote` help) "Access Key ID" Nothing+accessKeyIDField :: Widget -> Maybe Text -> AForm WebApp WebApp Text+accessKeyIDField help def = areq (textField `withNote` help) "Access Key ID" def++accessKeyIDFieldWithHelp :: Maybe Text -> AForm WebApp WebApp Text+accessKeyIDFieldWithHelp def = accessKeyIDField help def where help = [whamlet| <a href="https://portal.aws.amazon.com/gp/aws/securityCredentials#id_block"> Get Amazon access keys |] -secretAccessKeyField :: AForm WebApp WebApp Text-secretAccessKeyField = areq passwordField "Secret Access Key" Nothing+secretAccessKeyField :: Maybe Text -> AForm WebApp WebApp Text+secretAccessKeyField def = areq passwordField "Secret Access Key" def datacenterField :: AWS.Service -> AForm WebApp WebApp Text datacenterField service = areq (selectFieldList list) "Datacenter" defregion@@ -113,8 +118,9 @@ postAddS3R :: Handler RepHtml #ifdef WITH_S3 postAddS3R = awsConfigurator $ do+ defcreds <- liftAnnex previouslyUsedAWSCreds ((result, form), enctype) <- lift $- runFormPost $ renderBootstrap s3InputAForm+ runFormPost $ renderBootstrap $ s3InputAForm defcreds case result of FormSuccess input -> lift $ do let name = T.unpack $ repoName input@@ -137,8 +143,9 @@ postAddGlacierR :: Handler RepHtml postAddGlacierR = glacierConfigurator $ do+ defcreds <- liftAnnex previouslyUsedAWSCreds ((result, form), enctype) <- lift $- runFormPost $ renderBootstrap glacierInputAForm+ runFormPost $ renderBootstrap $ glacierInputAForm defcreds case result of FormSuccess input -> lift $ do let name = T.unpack $ repoName input@@ -153,11 +160,19 @@ setStandardGroup (Remote.uuid r) SmallArchiveGroup getEnableS3R :: UUID -> Handler RepHtml+#ifdef WITH_S3+getEnableS3R uuid = do+ m <- liftAnnex readRemoteLog+ if isIARemoteConfig $ fromJust $ M.lookup uuid m+ then redirect $ EnableIAR uuid+ else postEnableS3R uuid+#else getEnableS3R = postEnableS3R+#endif postEnableS3R :: UUID -> Handler RepHtml #ifdef WITH_S3-postEnableS3R = awsConfigurator . enableAWSRemote S3.remote+postEnableS3R uuid = awsConfigurator $ enableAWSRemote S3.remote uuid #else postEnableS3R _ = error "S3 not supported by this build" #endif@@ -170,8 +185,9 @@ enableAWSRemote :: RemoteType -> UUID -> Widget enableAWSRemote remotetype uuid = do+ defcreds <- liftAnnex previouslyUsedAWSCreds ((result, form), enctype) <- lift $- runFormPost $ renderBootstrap awsCredsAForm+ runFormPost $ renderBootstrap $ awsCredsAForm defcreds case result of FormSuccess creds -> lift $ do m <- liftAnnex readRemoteLog@@ -199,3 +215,19 @@ hostname = case filter isAlphaNum name of [] -> "aws" n -> n++getRepoInfo :: RemoteConfig -> Widget+getRepoInfo c = [whamlet|S3 remote using bucket: #{bucket}|]+ where+ bucket = fromMaybe "" $ M.lookup "bucket" c++#ifdef WITH_S3+isIARemoteConfig :: RemoteConfig -> Bool+isIARemoteConfig = S3.isIAHost . fromMaybe "" . M.lookup "host"+#endif++previouslyUsedAWSCreds :: Annex (Maybe CredPair)+previouslyUsedAWSCreds = getM gettype [S3.remote, Glacier.remote]+ where+ gettype t = previouslyUsedCredPair AWS.creds t $+ not . isIARemoteConfig . Remote.config
@@ -16,7 +16,7 @@ import Assistant.ScanRemotes import qualified Remote import qualified Git-import Locations.UserConfig+import Config.Files import Utility.FileMode import Logs.Trust import Logs.Remote
@@ -5,7 +5,7 @@ - Licensed under the GNU AGPL version 3 or higher. -} -{-# LANGUAGE TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings, RankNTypes #-}+{-# LANGUAGE CPP, TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings, RankNTypes #-} module Assistant.WebApp.Configurators.Edit where @@ -15,12 +15,18 @@ import Assistant.MakeRemote (uniqueRemoteName) import Assistant.WebApp.Configurators.XMPP (xmppNeeded) import Assistant.ScanRemotes+import qualified Assistant.WebApp.Configurators.AWS as AWS+import qualified Assistant.WebApp.Configurators.IA as IA+#ifdef WITH_S3+import qualified Remote.S3 as S3+#endif import qualified Remote import qualified Types.Remote as Remote import qualified Remote.List as Remote import Logs.UUID import Logs.Group import Logs.PreferredContent+import Logs.Remote import Types.StandardGroups import qualified Git import qualified Git.Command@@ -39,26 +45,32 @@ { repoName :: Text , repoDescription :: Maybe Text , repoGroup :: RepoGroup+ , repoAssociatedDirectory :: Maybe Text , repoSyncable :: Bool } deriving (Show) getRepoConfig :: UUID -> Maybe Remote -> Annex RepoConfig-getRepoConfig uuid mremote = RepoConfig- <$> pure (T.pack $ maybe "here" Remote.name mremote)- <*> (maybe Nothing (Just . T.pack) . M.lookup uuid <$> uuidMap)- <*> getrepogroup- <*> getsyncing- where- getrepogroup = do- groups <- lookupGroups uuid- return $ - maybe (RepoGroupCustom $ unwords $ S.toList groups) RepoGroupStandard- (getStandardGroup groups)- getsyncing = case mremote of+getRepoConfig uuid mremote = do+ groups <- lookupGroups uuid+ remoteconfig <- M.lookup uuid <$> readRemoteLog+ let (repogroup, associateddirectory) = case getStandardGroup groups of+ Nothing -> (RepoGroupCustom $ unwords $ S.toList groups, Nothing)+ Just g -> (RepoGroupStandard g, associatedDirectory remoteconfig g)+ + description <- maybe Nothing (Just . T.pack) . M.lookup uuid <$> uuidMap++ syncable <- case mremote of Just r -> return $ remoteAnnexSync $ Remote.gitconfig r Nothing -> annexAutoCommit <$> Annex.getGitConfig + return $ RepoConfig+ (T.pack $ maybe "here" Remote.name mremote)+ description+ repogroup+ (T.pack <$> associateddirectory)+ syncable+ setRepoConfig :: UUID -> Maybe Remote -> RepoConfig -> RepoConfig -> Handler () setRepoConfig uuid mremote oldc newc = do when descriptionChanged $ liftAnnex $ do@@ -85,6 +97,17 @@ ] void $ Remote.remoteListRefresh liftAssistant updateSyncRemotes+ when associatedDirectoryChanged $ case repoAssociatedDirectory newc of+ Nothing -> noop+ Just t+ | T.null t -> noop+ | otherwise -> liftAnnex $ do+ let dir = takeBaseName $ T.unpack t+ m <- readRemoteLog+ case M.lookup uuid m of+ Nothing -> noop+ Just remoteconfig -> configSet uuid $+ M.insert "preferreddir" dir remoteconfig when groupChanged $ do liftAnnex $ case repoGroup newc of RepoGroupStandard g -> setStandardGroup uuid g@@ -102,6 +125,7 @@ changeSyncable mremote (repoSyncable newc) where syncableChanged = repoSyncable oldc /= repoSyncable newc+ associatedDirectoryChanged = repoAssociatedDirectory oldc /= repoAssociatedDirectory newc groupChanged = repoGroup oldc /= repoGroup newc nameChanged = isJust mremote && legalName oldc /= legalName newc descriptionChanged = repoDescription oldc /= repoDescription newc@@ -113,6 +137,7 @@ <$> areq textField "Name" (Just $ repoName def) <*> aopt textField "Description" (Just $ repoDescription def) <*> areq (selectFieldList groups `withNote` help) "Repository group" (Just $ repoGroup def)+ <*> associateddirectory <*> areq checkBoxField "Syncing enabled" (Just $ repoSyncable def) where groups = customgroups ++ standardgroups@@ -125,6 +150,10 @@ _ -> [] help = [whamlet|<a href="@{RepoGroupR}">What's this?</a>|] + associateddirectory = case repoAssociatedDirectory def of+ Nothing -> aopt hiddenField "" Nothing+ Just d -> aopt textField "Associated directory" (Just $ Just d)+ getEditRepositoryR :: UUID -> Handler RepHtml getEditRepositoryR = postEditRepositoryR @@ -147,30 +176,47 @@ editForm new uuid = page "Configure repository" (Just Configuration) $ do mremote <- liftAnnex $ Remote.remoteFromUUID uuid curr <- liftAnnex $ getRepoConfig uuid mremote- lift $ checkarchivedirectory curr+ liftAnnex $ checkAssociatedDirectory curr mremote ((result, form), enctype) <- lift $ runFormPost $ renderBootstrap $ editRepositoryAForm curr case result of FormSuccess input -> lift $ do- checkarchivedirectory input setRepoConfig uuid mremote curr input+ liftAnnex $ checkAssociatedDirectory input mremote redirect DashboardR- _ -> showform form enctype curr- where- showform form enctype curr = do- let istransfer = repoGroup curr == RepoGroupStandard TransferGroup- $(widgetFile "configurators/editrepository")+ _ -> do+ let istransfer = repoGroup curr == RepoGroupStandard TransferGroup+ repoInfo <- getRepoInfo mremote . M.lookup uuid+ <$> liftAnnex readRemoteLog+ $(widgetFile "configurators/editrepository") - {- Makes a toplevel archive directory, so the user can get on with- - using it. This is done both when displaying the form, as well- - as after it's posted, because the user may not post the form,- - but may see that the repo is set up to use the archive- - directory. -}- checkarchivedirectory cfg- | repoGroup cfg == RepoGroupStandard SmallArchiveGroup = go- | repoGroup cfg == RepoGroupStandard FullArchiveGroup = go- | otherwise = noop- where- go = liftAnnex $ inRepo $ \g ->- createDirectoryIfMissing True $- Git.repoPath g </> "archive"+{- Makes any directory associated with the repository. -}+checkAssociatedDirectory :: RepoConfig -> Maybe Remote -> Annex ()+checkAssociatedDirectory _ Nothing = noop+checkAssociatedDirectory cfg (Just r) = do+ repoconfig <- M.lookup (Remote.uuid r) <$> readRemoteLog+ case repoGroup cfg of+ RepoGroupStandard gr -> case associatedDirectory repoconfig gr of+ Just d -> inRepo $ \g ->+ createDirectoryIfMissing True $+ Git.repoPath g </> d+ Nothing -> noop+ _ -> noop++getRepoInfo :: Maybe Remote.Remote -> Maybe Remote.RemoteConfig -> Widget+getRepoInfo (Just r) (Just c) = case M.lookup "type" c of+ Just "S3"+#ifdef WITH_S3+ | S3.isIA c -> IA.getRepoInfo c+#endif+ | otherwise -> AWS.getRepoInfo c+ Just t+ | t /= "git" -> [whamlet|#{t} remote|]+ _ -> getGitRepoInfo $ Remote.repo r+getRepoInfo (Just r) _ = getRepoInfo (Just r) (Just $ Remote.config r)+getRepoInfo _ _ = [whamlet|git repository|]++getGitRepoInfo :: Git.Repo -> Widget+getGitRepoInfo r = do+ let loc = Git.repoLocation r+ [whamlet|git repository located at <tt>#{loc}</tt>|]
@@ -0,0 +1,211 @@+{- git-annex assistant webapp configurators for Internet Archive+ -+ - Copyright 2013 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU AGPL version 3 or higher.+ -}++{-# LANGUAGE CPP, FlexibleContexts, TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings, RankNTypes #-}++module Assistant.WebApp.Configurators.IA where++import Assistant.WebApp.Common+import qualified Assistant.WebApp.Configurators.AWS as AWS+#ifdef WITH_S3+import qualified Remote.S3 as S3+import qualified Remote.Helper.AWS as AWS+import Assistant.MakeRemote+#endif+import qualified Remote+import qualified Types.Remote as Remote+import Types.StandardGroups+import Types.Remote (RemoteConfig)+import Logs.PreferredContent+import Logs.Remote+import qualified Utility.Url as Url+import Creds++import qualified Data.Text as T+import qualified Data.Map as M+import Data.Char+import Network.URI++iaConfigurator :: Widget -> Handler RepHtml+iaConfigurator = page "Add an Internet Archive repository" (Just Configuration)++data IAInput = IAInput+ { accessKeyID :: Text+ , secretAccessKey :: Text+ , mediaType :: MediaType+ , itemName :: Text+ }++extractCreds :: IAInput -> AWS.AWSCreds+extractCreds i = AWS.AWSCreds (accessKeyID i) (secretAccessKey i)++{- IA defines only a few media types currently, or the media type+ - may be omitted+ -+ - We add a few other common types, mapped to what we've been told+ - is the closest match.+ -}+data MediaType = MediaImages | MediaAudio | MediaVideo | MediaText | MediaSoftware | MediaOmitted+ deriving (Eq, Ord, Enum, Bounded)++{- Format a MediaType for entry into the IA metadata -}+formatMediaType :: MediaType -> String+formatMediaType MediaText = "texts"+formatMediaType MediaImages = "image"+formatMediaType MediaSoftware = "software"+formatMediaType MediaVideo = "movies"+formatMediaType MediaAudio = "audio"+formatMediaType MediaOmitted = ""++{- A default collection to use for each Mediatype. -}+collectionMediaType :: MediaType -> Maybe String+collectionMediaType MediaText = Just "opensource"+collectionMediaType MediaImages = Just "opensource" -- not ideal+collectionMediaType MediaSoftware = Just "opensource" -- not ideal+collectionMediaType MediaVideo = Just "opensource_movies"+collectionMediaType MediaAudio = Just "opensource_audio"+collectionMediaType MediaOmitted = Just "opensource"++{- Format a MediaType for user display. -}+showMediaType :: MediaType -> String+showMediaType MediaText = "texts"+showMediaType MediaImages = "photos & images"+showMediaType MediaSoftware = "software"+showMediaType MediaVideo = "videos & movies"+showMediaType MediaAudio = "audio & music"+showMediaType MediaOmitted = "other"++iaInputAForm :: Maybe CredPair -> AForm WebApp WebApp IAInput+iaInputAForm defcreds = IAInput+ <$> accessKeyIDFieldWithHelp (T.pack . fst <$> defcreds)+ <*> AWS.secretAccessKeyField (T.pack . snd <$> defcreds)+ <*> areq (selectFieldList mediatypes) "Media Type" (Just MediaOmitted)+ <*> areq (textField `withExpandableNote` ("Help", itemNameHelp)) "Item Name" Nothing+ where+ mediatypes :: [(Text, MediaType)]+ mediatypes = map (\t -> (T.pack $ showMediaType t, t)) [minBound..]++itemNameHelp :: Widget+itemNameHelp = [whamlet|+<div>+ Each item stored in the Internet Archive must have a unique name.+<div>+ Once you create the item, a special directory will appear #+ with a name matching the item name. Files you put in that directory #+ will be uploaded to your Internet Archive item.+|]++iaCredsAForm :: Maybe CredPair -> AForm WebApp WebApp AWS.AWSCreds+iaCredsAForm defcreds = AWS.AWSCreds+ <$> accessKeyIDFieldWithHelp (T.pack . fst <$> defcreds)+ <*> AWS.secretAccessKeyField (T.pack . snd <$> defcreds)++#ifdef WITH_S3+previouslyUsedIACreds :: Annex (Maybe CredPair)+previouslyUsedIACreds = previouslyUsedCredPair AWS.creds S3.remote $+ AWS.isIARemoteConfig . Remote.config+#endif++accessKeyIDFieldWithHelp :: Maybe Text -> AForm WebApp WebApp Text+accessKeyIDFieldWithHelp def = AWS.accessKeyIDField help def+ where+ help = [whamlet|+<a href="http://archive.org/account/s3.php">+ Get Internet Archive access keys+|]++getAddIAR :: Handler RepHtml+getAddIAR = postAddIAR++postAddIAR :: Handler RepHtml+#ifdef WITH_S3+postAddIAR = iaConfigurator $ do+ defcreds <- liftAnnex previouslyUsedIACreds+ ((result, form), enctype) <- lift $+ runFormPost $ renderBootstrap $ iaInputAForm defcreds+ case result of+ FormSuccess input -> lift $ do+ let name = escapeBucket $ T.unpack $ itemName input+ AWS.makeAWSRemote S3.remote (extractCreds input) name setgroup $+ M.fromList $ catMaybes+ [ Just $ configureEncryption NoEncryption+ , Just ("type", "S3")+ , Just ("host", S3.iaHost)+ , Just ("bucket", escapeHeader name)+ , Just ("x-archive-meta-title", escapeHeader $ T.unpack $ itemName input)+ , if mediaType input == MediaOmitted+ then Nothing+ else Just ("x-archive-mediatype", formatMediaType $ mediaType input)+ , (,) <$> pure "x-archive-meta-collection" <*> collectionMediaType (mediaType input)+ -- Make item show up ASAP.+ , Just ("x-archive-interactive-priority", "1")+ , Just ("preferreddir", name)+ ]+ _ -> $(widgetFile "configurators/addia")+ where+ setgroup r = liftAnnex $+ setStandardGroup (Remote.uuid r) PublicGroup+#else+postAddIAR = error "S3 not supported by this build"+#endif++getEnableIAR :: UUID -> Handler RepHtml+getEnableIAR = postEnableIAR++postEnableIAR :: UUID -> Handler RepHtml+#ifdef WITH_S3+postEnableIAR = iaConfigurator . enableIARemote+#else+postEnableIAR _ = error "S3 not supported by this build"+#endif++#ifdef WITH_S3+enableIARemote :: UUID -> Widget+enableIARemote uuid = do+ defcreds <- liftAnnex previouslyUsedIACreds+ ((result, form), enctype) <- lift $+ runFormPost $ renderBootstrap $ iaCredsAForm defcreds+ case result of+ FormSuccess creds -> lift $ do+ m <- liftAnnex readRemoteLog+ let name = fromJust $ M.lookup "name" $+ fromJust $ M.lookup uuid m+ AWS.makeAWSRemote S3.remote creds name (const noop) M.empty+ _ -> do+ description <- liftAnnex $+ T.pack <$> Remote.prettyUUID uuid+ $(widgetFile "configurators/enableia")+#endif++{- Convert a description into a bucket item name, which will also be+ - used as the repository name, and the preferreddir.+ - IA seems to need only lower case, and no spaces. -}+escapeBucket :: String -> String+escapeBucket = map toLower . replace " " "-"++{- IA S3 API likes headers to be URI escaped, escaping spaces looks ugly. -}+escapeHeader :: String -> String+escapeHeader = escapeURIString (\c -> isUnescapedInURI c && c /= ' ')++getRepoInfo :: RemoteConfig -> Widget+getRepoInfo c = do+ exists <- liftIO $ catchDefaultIO False $ fst <$> Url.exists url []+ [whamlet|+<a href="#{url}">+ Internet Archive item+$if (not exists)+ <p>+ The page will only be available once some files #+ have been uploaded, and the Internet Archive has processed them.+|]+ where+ bucket = fromMaybe "" $ M.lookup "bucket" c+#ifdef WITH_S3+ url = S3.iaItemUrl bucket+#else+ url = ""+#endif
@@ -19,7 +19,7 @@ import qualified Git.Config import qualified Git.Command import qualified Annex-import Locations.UserConfig+import Config.Files import Utility.FreeDesktop #ifdef WITH_CLIBS import Utility.Mounts@@ -98,14 +98,19 @@ - ~/Desktop/annex, when a Desktop directory exists, and ~/annex otherwise. - - If run in another directory, that the user can write to,- - the user probably wants to put it there. -}+ - the user probably wants to put it there. Unless that directory+ - contains a git-annex file, in which case the user has probably+ - browsed to a directory with git-annex and run it from there. -} defaultRepositoryPath :: Bool -> IO FilePath defaultRepositoryPath firstrun = do cwd <- liftIO $ getCurrentDirectory home <- myHomeDir if home == cwd && firstrun then inhome- else ifM (canWrite cwd) ( return cwd, inhome )+ else ifM (legit cwd <&&> canWrite cwd)+ ( return cwd+ , inhome+ ) where inhome = do desktop <- userDesktopDir@@ -113,6 +118,7 @@ ( relHome $ desktop </> gitAnnexAssistantDefaultDir , return $ "~" </> gitAnnexAssistantDefaultDir )+ legit d = not <$> doesFileExist (d </> "git-annex") newRepositoryForm :: FilePath -> Form RepositoryPath newRepositoryForm defpath msg = do
@@ -14,6 +14,7 @@ import Assistant.WebApp.Common import Assistant.WebApp.Configurators import Assistant.Types.Buddies+import Annex.UUID #ifdef WITH_PAIRING import Assistant.Pairing.Network import Assistant.Pairing.MakeRemote@@ -22,7 +23,6 @@ import Assistant.DaemonStatus import Utility.Verifiable import Utility.Network-import Annex.UUID #endif #ifdef WITH_XMPP import Assistant.XMPP.Client@@ -60,8 +60,7 @@ $(widgetFile "configurators/pairing/xmpp/friend/prompt") , do -- go get XMPP configured, then come back- setUltDestCurrent- redirect XMPPR+ redirect XMPPConfigForPairFriendR ) #else getStartXMPPPairFriendR = noXMPPPairing@@ -76,8 +75,7 @@ where go Nothing = do -- go get XMPP configured, then come back- setUltDestCurrent- redirect XMPPR+ redirect XMPPConfigForPairSelfR go (Just creds) = do {- Ask buddies to send presence info, to get - the buddy list populated. -}
@@ -16,7 +16,7 @@ import qualified Annex import qualified Git import Config-import Locations.UserConfig+import Config.Files import Utility.DataUnits import qualified Data.Text as T
@@ -205,7 +205,7 @@ , rsyncOnly = status == UsableRsyncServer } probe extraopts = do- let remotecommand = shellWrap $ join ";"+ let remotecommand = shellWrap $ intercalate ";" [ report "loggedin" , checkcommand "git-annex-shell" , checkcommand "rsync"@@ -287,10 +287,10 @@ where sshhost = genSshHost (sshHostName sshdata) (sshUserName sshdata) remotedir = T.unpack $ sshDirectory sshdata- remoteCommand = shellWrap $ join "&&" $ catMaybes+ remoteCommand = shellWrap $ intercalate "&&" $ catMaybes [ Just $ "mkdir -p " ++ shellEscape remotedir , Just $ "cd " ++ shellEscape remotedir- , if rsync then Nothing else Just "git init --bare --shared"+ , if rsync then Nothing else Just "if [ ! -d .git ]; then git init --bare --shared; fi" , if rsync then Nothing else Just "git annex init" , if needsPubKey sshdata then addAuthorizedKeysCommand (rsyncOnly sshdata) remotedir . sshPubKey <$> keypair@@ -323,17 +323,14 @@ "That is not a rsync.net host name." _ -> showform UntestedServer where- hostnamefield = textField `withNote` help+ hostnamefield = textField `withExpandableNote` ("Help", help) help = [whamlet|-<a .btn data-toggle="collapse" data-target="#help">- Help-<div #help .collapse>- <div>- When you sign up for a Rsync.net account, you should receive an #- email from them with the host name and user name to put here.- <div>- The host name will be something like "usw-s001.rsync.net", and the #- user name something like "7491"+<div>+ When you sign up for a Rsync.net account, you should receive an #+ email from them with the host name and user name to put here.+<div>+ The host name will be something like "usw-s001.rsync.net", and the #+ user name something like "7491" |] makeRsyncNet :: SshInput -> String -> (Remote -> Handler ()) -> Handler RepHtml@@ -353,7 +350,7 @@ - one recommended by rsync.net documentation. I touch the file first - to not need to use a different method to create it. -}- let remotecommand = join ";"+ let remotecommand = intercalate ";" [ "mkdir -p .ssh" , "touch .ssh/authorized_keys" , "dd of=.ssh/authorized_keys oflag=append conv=notrunc"
@@ -1,6 +1,6 @@ {- git-annex assistant webapp configurators for WebDAV remotes -- - Copyright 2012 Joey Hess <joey@kitenet.net>+ - Copyright 2012, 2013 Joey Hess <joey@kitenet.net> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -10,20 +10,21 @@ module Assistant.WebApp.Configurators.WebDAV where import Assistant.WebApp.Common-import Assistant.MakeRemote-import Assistant.Sync+import Creds #ifdef WITH_WEBDAV import qualified Remote.WebDAV as WebDAV-#endif+import Assistant.MakeRemote+import Assistant.Sync import qualified Remote import Types.Remote (RemoteConfig) import Types.StandardGroups import Logs.PreferredContent import Logs.Remote-import Creds -import qualified Data.Text as T import qualified Data.Map as M+#endif+import qualified Data.Text as T+import Network.URI webDAVConfigurator :: Widget -> Handler RepHtml webDAVConfigurator = page "Add a WebDAV repository" (Just Configuration)@@ -42,18 +43,18 @@ toCredPair :: WebDAVInput -> CredPair toCredPair input = (T.unpack $ user input, T.unpack $ password input) -boxComAForm :: AForm WebApp WebApp WebDAVInput-boxComAForm = WebDAVInput- <$> areq textField "Username or Email" Nothing- <*> areq passwordField "Box.com Password" Nothing+boxComAForm :: Maybe CredPair -> AForm WebApp WebApp WebDAVInput+boxComAForm defcreds = WebDAVInput+ <$> areq textField "Username or Email" (T.pack . fst <$> defcreds)+ <*> areq passwordField "Box.com Password" (T.pack . snd <$> defcreds) <*> areq checkBoxField "Share this account with other devices and friends?" (Just True) <*> areq textField "Directory" (Just "annex") <*> enableEncryptionField -webDAVCredsAForm :: AForm WebApp WebApp WebDAVInput-webDAVCredsAForm = WebDAVInput- <$> areq textField "Username or Email" Nothing- <*> areq passwordField "Password" Nothing+webDAVCredsAForm :: Maybe CredPair -> AForm WebApp WebApp WebDAVInput+webDAVCredsAForm defcreds = WebDAVInput+ <$> areq textField "Username or Email" (T.pack . fst <$> defcreds)+ <*> areq passwordField "Password" (T.pack . snd <$> defcreds) <*> pure False <*> pure T.empty <*> pure NoEncryption -- not used!@@ -63,8 +64,9 @@ postAddBoxComR :: Handler RepHtml #ifdef WITH_WEBDAV postAddBoxComR = boxConfigurator $ do+ defcreds <- liftAnnex $ previouslyUsedWebDAVCreds "box.com" ((result, form), enctype) <- lift $- runFormPost $ renderBootstrap boxComAForm+ runFormPost $ renderBootstrap $ boxComAForm defcreds case result of FormSuccess input -> lift $ makeWebDavRemote "box.com" (toCredPair input) setgroup $ M.fromList@@ -106,8 +108,11 @@ webDAVConfigurator $ showform name url where showform name url = do+ defcreds <- liftAnnex $ + maybe (pure Nothing) previouslyUsedWebDAVCreds $+ urlHost url ((result, form), enctype) <- lift $- runFormPost $ renderBootstrap webDAVCredsAForm+ runFormPost $ renderBootstrap $ webDAVCredsAForm defcreds case result of FormSuccess input -> lift $ makeWebDavRemote name (toCredPair input) (const noop) M.empty@@ -131,3 +136,15 @@ liftAssistant $ syncRemote r redirect $ EditNewCloudRepositoryR $ Remote.uuid r #endif++{- Only returns creds previously used for the same hostname. -}+previouslyUsedWebDAVCreds :: String -> Annex (Maybe CredPair)+previouslyUsedWebDAVCreds hostname =+ previouslyUsedCredPair WebDAV.davCreds WebDAV.remote samehost+ where+ samehost url = case urlHost =<< WebDAV.configUrl url of+ Nothing -> False+ Just h -> h == hostname++urlHost :: String -> Maybe String+urlHost url = uriRegName <$> (uriAuthority =<< parseURI url)
@@ -43,7 +43,7 @@ close <- asIO1 removeAlert addAlert $ xmppNeededAlert $ AlertButton { buttonLabel = "Configure a Jabber account"- , buttonUrl = urlrender XMPPR+ , buttonUrl = urlrender XMPPConfigR , buttonAction = Just close } #else@@ -91,11 +91,27 @@ $(widgetFile "configurators/xmpp/disabled") #endif -getXMPPR :: Handler RepHtml-getXMPPR = postXMPPR-postXMPPR :: Handler RepHtml+getXMPPConfigR :: Handler RepHtml+getXMPPConfigR = postXMPPConfigR++postXMPPConfigR :: Handler RepHtml+postXMPPConfigR = xmppform DashboardR++getXMPPConfigForPairFriendR :: Handler RepHtml+getXMPPConfigForPairFriendR = postXMPPConfigForPairFriendR++postXMPPConfigForPairFriendR :: Handler RepHtml+postXMPPConfigForPairFriendR = xmppform StartXMPPPairFriendR++getXMPPConfigForPairSelfR :: Handler RepHtml+getXMPPConfigForPairSelfR = postXMPPConfigForPairSelfR++postXMPPConfigForPairSelfR :: Handler RepHtml+postXMPPConfigForPairSelfR = xmppform StartXMPPPairSelfR++xmppform :: Route WebApp -> Handler RepHtml #ifdef WITH_XMPP-postXMPPR = xmppPage $ do+xmppform next = xmppPage $ do ((result, form), enctype) <- lift $ do oldcreds <- liftAnnex getXMPPCreds runFormPost $ renderBootstrap $ xmppAForm $@@ -109,9 +125,9 @@ storecreds creds = do void $ liftAnnex $ setXMPPCreds creds liftAssistant notifyNetMessagerRestart- redirectUltDest DashboardR+ redirect next #else-postXMPPR = xmppPage $+xmppform = xmppPage $ $(widgetFile "configurators/xmpp/disabled") #endif
@@ -10,7 +10,7 @@ module Assistant.WebApp.Control where import Assistant.WebApp.Common-import Locations.UserConfig+import Config.Files import Utility.LogFile import Assistant.DaemonStatus import Assistant.WebApp.Utility
@@ -47,6 +47,17 @@ let fieldwidget = (fieldView field) theId name attrs val isReq in [whamlet|^{fieldwidget} <span>^{note}</span>|] +{- Note that the toggle string must be unique on the form. -}+withExpandableNote :: Field sub master v -> (String, GWidget sub master ()) -> Field sub master v+withExpandableNote field (toggle, note) = withNote field $ [whamlet|+<a .btn data-toggle="collapse" data-target="##{ident}">+ #{toggle}+<div ##{ident} .collapse>+ ^{note}+|]+ where+ ident = "toggle_" ++ toggle+ data EnableEncryption = SharedEncryption | NoEncryption deriving (Eq)
@@ -14,7 +14,7 @@ import Assistant.WebApp.Page import qualified Git.Construct import qualified Git.Config-import Locations.UserConfig+import Config.Files import qualified Utility.Url as Url import Utility.Yesod
@@ -133,7 +133,7 @@ unwanted <- S.fromList <$> filterM inUnwantedGroup (S.toList syncing) rs <- filter selectedrepo . concat . Remote.byCost- <$> Remote.enabledRemoteList+ <$> Remote.remoteList let us = map Remote.uuid rs let maker u | u `S.member` unwanted = mkUnwantedRepoActions u@@ -163,19 +163,17 @@ selectedremote (Just (iscloud, _)) | onlyCloud reposelector = iscloud | otherwise = True- findinfo m u = case M.lookup u m of- Nothing -> Nothing- Just c -> case M.lookup "type" c of- Just "rsync" -> val True EnableRsyncR- Just "directory" -> val False EnableDirectoryR+ findinfo m u = case M.lookup "type" =<< M.lookup u m of+ Just "rsync" -> val True EnableRsyncR+ Just "directory" -> val False EnableDirectoryR #ifdef WITH_S3- Just "S3" -> val True EnableS3R+ Just "S3" -> val True EnableS3R #endif- Just "glacier" -> val True EnableGlacierR+ Just "glacier" -> val True EnableGlacierR #ifdef WITH_WEBDAV- Just "webdav" -> val True EnableWebDAVR+ Just "webdav" -> val True EnableWebDAVR #endif- _ -> Nothing+ _ -> Nothing where val iscloud r = Just (iscloud, (u, DisabledRepoActions $ r u)) list l = liftAnnex $ do
@@ -20,6 +20,7 @@ import Yesod import Data.Text (Text)+import qualified Data.Text as T import qualified Data.Map as M import Control.Concurrent @@ -47,6 +48,9 @@ let closable = alertClosable alert let block = alertBlockDisplay alert let divclass = bootstrapclass $ alertClass alert+ let message = renderAlertMessage alert+ let messagelines = T.lines message+ let multiline = length messagelines > 1 $(widgetFile "sidebar/alert") {- Called by client to get a sidebar display.
@@ -19,8 +19,8 @@ import qualified Remote.List as Remote import qualified Assistant.Threads.Transferrer as Transferrer import Logs.Transfer-import Locations.UserConfig import qualified Config+import Config.Files import Git.Config import Assistant.Threads.Watcher import Assistant.NamedThread
@@ -15,7 +15,9 @@ /config ConfigurationR GET /config/preferences PreferencesR GET POST-/config/xmpp XMPPR GET POST+/config/xmpp XMPPConfigR GET POST+/config/xmpp/for/self XMPPConfigForPairSelfR GET POST+/config/xmpp/for/frield XMPPConfigForPairFriendR GET POST /config/xmpp/needcloudrepo/#UUID NeedCloudRepoR GET /config/addrepository AddRepositoryR GET@@ -40,6 +42,7 @@ /config/repository/add/ssh/make/rsync/#SshData MakeSshRsyncR GET /config/repository/add/cloud/rsync.net AddRsyncNetR GET POST /config/repository/add/cloud/S3 AddS3R GET POST+/config/repository/add/cloud/IA AddIAR GET POST /config/repository/add/cloud/glacier AddGlacierR GET POST /config/repository/add/cloud/box.com AddBoxComR GET POST @@ -58,6 +61,7 @@ /config/repository/enable/rsync/#UUID EnableRsyncR GET POST /config/repository/enable/directory/#UUID EnableDirectoryR GET /config/repository/enable/S3/#UUID EnableS3R GET POST+/config/repository/enable/IA/#UUID EnableIAR GET POST /config/repository/enable/glacier/#UUID EnableGlacierR GET POST /config/repository/enable/webdav/#UUID EnableWebDAVR GET POST
@@ -131,9 +131,12 @@ pushMessage :: PushStage -> JID -> JID -> Message pushMessage = gitAnnexMessage . encode where- encode CanPush = gitAnnexTag canPushAttr T.empty- encode PushRequest = gitAnnexTag pushRequestAttr T.empty- encode StartingPush = gitAnnexTag startingPushAttr T.empty+ encode (CanPush u) =+ gitAnnexTag canPushAttr $ T.pack $ fromUUID u+ encode (PushRequest u) =+ gitAnnexTag pushRequestAttr $ T.pack $ fromUUID u+ encode (StartingPush u) =+ gitAnnexTag startingPushAttr $ T.pack $ fromUUID u encode (ReceivePackOutput n b) = gitAnnexTagContent receivePackAttr (val n) $ encodeTagContent b encode (SendPackOutput n b) =@@ -157,11 +160,11 @@ , receivePackDoneAttr ] [ decodePairingNotification- , pushdecoder $ const $ Just CanPush- , pushdecoder $ const $ Just PushRequest- , pushdecoder $ const $ Just StartingPush- , pushdecoder $ gen ReceivePackOutput- , pushdecoder $ gen SendPackOutput+ , pushdecoder $ gen CanPush+ , pushdecoder $ gen PushRequest+ , pushdecoder $ gen StartingPush+ , pushdecoder $ seqgen ReceivePackOutput+ , pushdecoder $ seqgen SendPackOutput , pushdecoder $ fmap (ReceivePackDone . decodeExitCode) . readish . T.unpack . tagValue@@ -169,7 +172,8 @@ pushdecoder a m' i = Pushing <$> (formatJID <$> messageFrom m') <*> a i- gen c i = do+ gen c = Just . c . toUUID . T.unpack . tagValue+ seqgen c i = do packet <- decodeTagContent $ tagElement i let seqnum = fromMaybe 0 $ readish $ T.unpack $ tagValue i return $ c seqnum packet
@@ -21,11 +21,12 @@ import qualified Command.Sync import qualified Annex.Branch import Annex.UUID+import Logs.UUID import Annex.TaggedPush import Config import Git import qualified Git.Branch-import Locations.UserConfig+import Config.Files import qualified Types.Remote as Remote import qualified Remote as Remote import Remote.List@@ -84,7 +85,8 @@ -} xmppPush :: ClientID -> (Git.Repo -> IO Bool) -> (NetMessage -> Assistant ()) -> Assistant Bool xmppPush cid gitpush handledeferred = runPush SendPack cid handledeferred $ do- sendNetMessage $ Pushing cid StartingPush+ u <- liftAnnex getUUID+ sendNetMessage $ Pushing cid (StartingPush u) (Fd inf, writepush) <- liftIO createPipe (readpush, Fd outf) <- liftIO createPipe@@ -97,7 +99,7 @@ env <- liftIO getEnvironment path <- liftIO getSearchPath let myenv = M.fromList- [ ("PATH", join [searchPathSeparator] $ tmpdir:path)+ [ ("PATH", intercalate [searchPathSeparator] $ tmpdir:path) , (relayIn, show inf) , (relayOut, show outf) , (relayControl, show controlf)@@ -247,26 +249,29 @@ hClose inh killThread =<< myThreadId -xmppRemotes :: ClientID -> Assistant [Remote]-xmppRemotes cid = case baseJID <$> parseJID cid of+xmppRemotes :: ClientID -> UUID -> Assistant [Remote]+xmppRemotes cid theiruuid = case baseJID <$> parseJID cid of Nothing -> return [] Just jid -> do let loc = gitXMPPLocation jid- filter (matching loc . Remote.repo) . syncGitRemotes + um <- liftAnnex uuidMap+ filter (matching loc . Remote.repo) . filter (knownuuid um) . syncGitRemotes <$> getDaemonStatus where matching loc r = repoIsUrl r && repoLocation r == loc+ knownuuid um r = Remote.uuid r == theiruuid || M.member theiruuid um handlePushInitiation :: (Remote -> Assistant ()) -> NetMessage -> Assistant ()-handlePushInitiation _ (Pushing cid CanPush) =- unlessM (null <$> xmppRemotes cid) $ - sendNetMessage $ Pushing cid PushRequest-handlePushInitiation checkcloudrepos (Pushing cid PushRequest) =+handlePushInitiation _ (Pushing cid (CanPush theiruuid)) =+ unlessM (null <$> xmppRemotes cid theiruuid) $ do+ u <- liftAnnex getUUID+ sendNetMessage $ Pushing cid (PushRequest u)+handlePushInitiation checkcloudrepos (Pushing cid (PushRequest theiruuid)) = go =<< liftAnnex (inRepo Git.Branch.current) where go Nothing = noop go (Just branch) = do- rs <- xmppRemotes cid+ rs <- xmppRemotes cid theiruuid liftAnnex $ Annex.Branch.commit "update" (g, u) <- liftAnnex $ (,) <$> gitRepo@@ -279,8 +284,8 @@ (taggedPush u selfjid branch r) (handleDeferred checkcloudrepos) checkcloudrepos r-handlePushInitiation checkcloudrepos (Pushing cid StartingPush) = do- rs <- xmppRemotes cid+handlePushInitiation checkcloudrepos (Pushing cid (StartingPush theiruuid)) = do+ rs <- xmppRemotes cid theiruuid unless (null rs) $ do void $ alertWhile (syncAlert rs) $ xmppReceivePack cid (handleDeferred checkcloudrepos)
@@ -121,7 +121,7 @@ selectExtension :: FilePath -> String selectExtension f | null es = ""- | otherwise = join "." ("":es)+ | otherwise = intercalate "." ("":es) where es = filter (not . null) $ reverse $ take 2 $ takeWhile shortenough $
binary file changed (49700 → 49700 bytes)
@@ -14,9 +14,10 @@ import Utility.FreeDesktop import Utility.Path import Utility.Monad-import Locations.UserConfig+import Config.Files import Utility.OSX import Assistant.Install.AutoStart+import Assistant.Install.Menu import Control.Applicative import System.Directory@@ -26,24 +27,6 @@ import System.FilePath import Data.Maybe -{- The command can be either just "git-annex", or the full path to use- - to run it. -}-desktop :: FilePath -> DesktopEntry-desktop command = genDesktopEntry- "Git Annex"- "Track and sync the files in your Git Annex"- False- (command ++ " webapp")- ["Network", "FileTransfer"]--autostart :: FilePath -> DesktopEntry-autostart command = genDesktopEntry- "Git Annex Assistant"- "Autostart"- False- (command ++ " assistant --autostart")- []- systemwideInstall :: IO Bool systemwideInstall = isroot <||> destdirset where@@ -60,7 +43,7 @@ writeFDODesktop :: FilePath -> IO () writeFDODesktop command = do datadir <- ifM systemwideInstall ( return systemDataDir, userDataDir )- writeDesktopMenuFile (desktop command) + installMenu command =<< inDestDir (desktopMenuFilePath "git-annex" datadir) configdir <- ifM systemwideInstall ( return systemConfigDir, userConfigDir )
binary file changed (absent → 13604 bytes)
@@ -1,5 +1,8 @@ {- Expands template haskell splices -+ - You should probably just use http://hackage.haskell.org/package/zeroth+ - instead. I wish I had known about it before writing this.+ - - First, the code must be built with a ghc that supports TH, - and the splices dumped to a log. For example: - cabal build --ghc-options=-ddump-splices 2>&1 | tee log@@ -290,17 +293,61 @@ {- Tweaks code output by GHC in splices to actually build. Yipes. -} mangleCode :: String -> String-mangleCode = declaration_parens+mangleCode = flip_colon+ . lambdaparens+ . declaration_parens . case_layout . case_layout_multiline- . remove_declaration_splices . yesod_url_render_hack- . yesod_static_route_render_hack . nested_instances . collapse_multiline_strings . remove_package_version . emptylambda where+ {- Lambdas are often output without parens around them.+ - This breaks when the lambda is immediately applied to a+ - parameter.+ - + - For example:+ -+ - renderRoute (StaticR sub_a1nUH)+ - = \ (a_a1nUI, b_a1nUJ)+ - -> (((pack "static") : a_a1nUI),+ - b_a1nUJ)+ - (renderRoute sub_a1nUH)+ -+ - There are sometimes many lines of lambda code that need to be+ - parenthesised. Approach: find the "->" and scan down the+ - column to the first non-whitespace. This is assumed+ - to be the expression after the lambda.+ -+ - Runs recursively on the body of the lambda, to handle nested+ - lambdas.+ -}+ lambdaparens = parsecAndReplace $ do+ -- skip lambdas inside tuples or parens+ prefix <- noneOf "(, \n"+ preindent <- many1 $ oneOf " \n"+ string "\\ "+ lambdaparams <- restofline+ indent <- many1 $ char ' '+ string "-> "+ firstline <- restofline+ lambdalines <- many $ try $ do+ string indent+ char ' '+ l <- restofline+ return $ indent ++ " " ++ l+ return $ concat + [ prefix:preindent+ , "(\\ " ++ lambdaparams ++ "\n"+ , indent ++ "-> "+ , lambdaparens $ intercalate "\n" (firstline:lambdalines)+ , ")\n"+ ]++ restofline = manyTill (noneOf "\n") newline+ {- For some reason, GHC sometimes doesn't like the multiline - strings it creates. It seems to get hung up on \{ at the - start of a new line sometimes, wanting it to not be escaped.@@ -357,7 +404,7 @@ case_layout_multiline = parsecAndReplace $ do newline indent <- many1 $ char ' '- firstline <- manyTill (noneOf "\n") newline+ firstline <- restofline string indent indent2 <- many1 $ char ' '@@ -420,44 +467,10 @@ oken <- many $ satisfy isAlphaNum <|> oneOf "-.'" return $ t:oken -{- This works around a problem in the expanded template haskell for Yesod's- - static site route rendering.- -- - renderRoute (StaticR sub_a1nUH)- - = \ (a_a1nUI, b_a1nUJ)- - -> (((pack "static") : a_a1nUI), b_a1nUJ)- - (renderRoute sub_a1nUH)- -- - That is missing parens around the lambda expression (which - - is supposed to be applied to renderRoute). Add those parens.- -}-yesod_static_route_render_hack :: String -> String-yesod_static_route_render_hack = parsecAndReplace $ do- def <- string "renderRoute (StaticR sub_a1nUH)"- whitespace- string "= \\ ("- t1 <- token- string ", "- t2 <- token- string ")"- whitespace- f <- string "-> (((pack \"static\") : "- string t1- string "), "- string t2- string ")"- return $ concat- [ def- , " = (\\ (", t1, ",", t2, ") "- , f, t1, "), ", t2, "))"- ]- where- whitespace :: Parser String- whitespace = many $ oneOf " \t\r\n"-- token :: Parser String- token = many1 $ satisfy isAlphaNum <|> oneOf "_"-+ {- This works when it's "GHC.Types.:", but we strip+ - that above, so have to fix up after it here. + - The ; is added by case_layout. -}+ flip_colon = replace "; : _ " "; _ : " {- This works around a problem in the expanded template haskell for Yesod - type-safe url rendering.@@ -486,13 +499,13 @@ whitespace string "(\\" whitespace- token+ wtf <- token whitespace string "->" whitespace renderer <- token whitespace- token+ string wtf whitespace return $ "(toHtml (flip " ++ renderer ++ " " where
binary file changed (157660 → 158876 bytes)
@@ -6,11 +6,11 @@ - Licensed under the GNU GPL version 3 or higher. -} -{-# LANGUAGE CPP #-}- module Main where -import Build.InstallDesktopFile+import Build.DesktopFile++import System.Environment main :: IO () main = getArgs >>= go
binary file changed (17560 → 2536 bytes)
@@ -22,6 +22,7 @@ import Utility.Monad import Utility.SafeCommand import Utility.Path+import Utility.Exception import qualified Data.Map as M import qualified Data.Set as S@@ -29,16 +30,16 @@ type LibMap = M.Map FilePath String {- Recursively find and install libs, until nothing new to install is found. -}-mklibs :: FilePath -> [FilePath] -> LibMap -> IO ()-mklibs appbase libdirs libmap = do- (new, libmap') <- installLibs appbase libmap+mklibs :: FilePath -> [FilePath] -> [(FilePath, FilePath)] -> LibMap -> IO ()+mklibs appbase libdirs replacement_libs libmap = do+ (new, replacement_libs', libmap') <- installLibs appbase replacement_libs libmap unless (null new) $- mklibs appbase (libdirs++new) libmap'+ mklibs appbase (libdirs++new) replacement_libs' libmap' {- Returns directories into which new libs were installed. -}-installLibs :: FilePath -> LibMap -> IO ([FilePath], LibMap)-installLibs appbase libmap = do- (needlibs, libmap') <- otool appbase libmap+installLibs :: FilePath -> [(FilePath, FilePath)] -> LibMap -> IO ([FilePath], [(FilePath, FilePath)], LibMap)+installLibs appbase replacement_libs libmap = do+ (needlibs, replacement_libs', libmap') <- otool appbase replacement_libs libmap libs <- forM needlibs $ \lib -> do let shortlib = fromMaybe (error "internal") (M.lookup lib libmap') let fulllib = dropWhile (== '/') lib@@ -54,25 +55,55 @@ _ <- boolSystem "ln" [Param "-s", File fulllib, File symdest] return $ Just appbase )- return (catMaybes libs, libmap')+ return (catMaybes libs, replacement_libs', libmap') {- Returns libraries to install. -}-otool :: FilePath -> LibMap -> IO ([FilePath], LibMap)-otool appbase libmap = do+otool :: FilePath -> [(FilePath, FilePath)] -> LibMap -> IO ([FilePath], [(FilePath, FilePath)], LibMap)+otool appbase replacement_libs libmap = do files <- filterM doesFileExist =<< dirContentsRecursive appbase- process [] files libmap+ process [] files replacement_libs libmap where want s = not ("@executable_path" `isInfixOf` s) && not (".framework" `isInfixOf` s) && not ("libSystem.B" `isInfixOf` s)- process c [] m = return (nub $ concat c, m)- process c (file:rest) m = do+ process c [] rls m = return (nub $ concat c, rls, m)+ process c (file:rest) rls m = do _ <- boolSystem "chmod" [Param "755", File file] libs <- filter want . parseOtool <$> readProcess "otool" ["-L", file]- m' <- install_name_tool file libs m- process (libs:c) rest m'+ expanded_libs <- expand_rpath libs replacement_libs file+ let rls' = nub $ rls ++ (zip libs expanded_libs)+ m' <- install_name_tool file libs expanded_libs m+ process (expanded_libs:c) rest rls' m' +{- Expands any @rpath in the list of libraries.+ -+ - This is done by the nasty method of running the command with a dummy+ - option (so it doesn't do anything.. hopefully!) and asking the dynamic+ - linker to print expanded rpaths.+ -}+expand_rpath :: [String] -> [(FilePath, FilePath)] -> FilePath -> IO [String]+expand_rpath libs replacement_libs cmd+ | any ("@rpath" `isInfixOf`) libs = do+ installed <- M.fromList . Prelude.read+ <$> readFile "tmp/standalone-installed"+ let origcmd = case M.lookup cmd installed of+ Nothing -> cmd+ Just cmd' -> cmd'+ s <- catchDefaultIO "" $ readProcess "sh" ["-c", probe origcmd]+ let m = if (null s)+ then M.fromList replacement_libs+ else M.fromList $ mapMaybe parse $ lines s+ return $ map (replace m) libs+ | otherwise = return libs+ where+ probe c = "DYLD_PRINT_RPATHS=1 " ++ c ++ " --getting-rpath-dummy-option 2>&1 | grep RPATH"+ parse s = case words s of+ ("RPATH":"successful":"expansion":"of":old:"to:":new:[]) -> + Just (old, new)+ _ -> Nothing+ replace m l = fromMaybe l $ M.lookup l m+ parseOtool :: String -> [FilePath] parseOtool = catMaybes . map parse . lines where@@ -82,10 +113,10 @@ {- Adjusts binaries to use libraries bundled with it, rather than the - system libraries. -}-install_name_tool :: FilePath -> [FilePath] -> LibMap -> IO LibMap-install_name_tool _ [] libmap = return libmap-install_name_tool binary libs libmap = do- let (libnames, libmap') = getLibNames libs libmap+install_name_tool :: FilePath -> [FilePath] -> [FilePath] -> LibMap -> IO LibMap+install_name_tool _ [] _ libmap = return libmap+install_name_tool binary libs expanded_libs libmap = do+ let (libnames, libmap') = getLibNames expanded_libs libmap let params = concatMap change $ zip libs libnames ok <- boolSystem "install_name_tool" $ params ++ [File binary] unless ok $@@ -123,4 +154,4 @@ main = getArgs >>= go where go [] = error "specify OSXAPP_BASE"- go (appbase:_) = mklibs appbase [] M.empty+ go (appbase:_) = mklibs appbase [] [] M.empty
@@ -60,12 +60,15 @@ progDir topdir = topdir </> "bin" #endif -installProg :: FilePath -> FilePath -> IO ()+installProg :: FilePath -> FilePath -> IO (FilePath, FilePath) installProg dir prog = searchPath prog >>= go where go Nothing = error $ "cannot find " ++ prog ++ " in PATH"- go (Just f) = unlessM (boolSystem "install" [File f, File dir]) $- error $ "install failed for " ++ prog+ go (Just f) = do+ let dest = dir </> takeFileName f+ unlessM (boolSystem "install" [File f, File dest]) $+ error $ "install failed for " ++ prog+ return (dest, f) main = getArgs >>= go where@@ -73,5 +76,5 @@ go (topdir:_) = do let dir = progDir topdir createDirectoryIfMissing True dir- forM_ thirdpartyProgs $ installProg dir- + installed <- forM thirdpartyProgs $ installProg dir+ writeFile "tmp/standalone-installed" (show installed)
binary file changed (absent → 13632 bytes)
binary file changed (absent → 4860 bytes)
binary file changed (32380 → 32380 bytes)
@@ -1,3 +1,63 @@+git-annex (4.20130501) unstable; urgency=low++ * sync, assistant: Behavior changes: Sync with remotes that have+ annex-ignore set, so that git remotes on servers without git-annex+ installed can be used to keep clients' git repos in sync.+ * assistant: Work around misfeature in git 1.8.2 that makes+ `git commit --alow-empty -m ""` run an editor.+ * sync: Bug fix, avoid adding to the annex the + dummy symlinks used on crippled filesystems.+ * Add public repository group.+ (And inpreferreddir to preferred content expressions.)+ * webapp: Can now set up Internet Archive repositories.+ * S3: Dropping content from the Internet Archive doesn't work, but+ their API indicates it does. Always refuse to drop from there.+ * Automatically register public urls for files uploaded to the+ Internet Archive.+ * To enable an existing special remote, the new enableremote command+ must be used. The initremote command now is used only to create+ new special remotes.+ * initremote: If two existing remotes have the same name,+ prefer the one with a higher trust level.+ * assistant: Improved XMPP protocol to better support multiple repositories+ using the same XMPP account. Fixes bad behavior when sharing with a friend+ when you or the friend have multiple reposotories on an XMPP account.+ Note that XMPP pairing with your own devices still pairs with all+ repositories using your XMPP account.+ * assistant: Fix bug that could cause incoming pushes to not get+ merged into the local tree. Particularly affected XMPP pushes.+ * webapp: Display some additional information about a repository on+ its edit page.+ * webapp: Install FDO desktop menu file when started in standalone mode.+ * webapp: Don't default to making repository in cwd when started+ from within a directory containing a git-annex file (eg, standalone+ tarball directory).+ * Detect systems that have no user name set in GECOS, and also+ don't have user.name set in git config, and put in a workaround+ so that commits to the git-annex branch (and the assistant)+ will still succeed despite git not liking the system configuration.+ * webapp: When told to add a git repository on a remote server, and+ the repository already exists as a non-bare repository, use it,+ rather than initializing a bare repository in the same directory.+ * direct, indirect: Refuse to do anything when the assistant+ or git-annex watch daemon is running.+ * assistant: When built with git before 1.8.0, use `git remote rm`+ to delete a remote. Newer git uses `git remote remove`.+ * rmurl: New command, removes one of the recorded urls for a file.+ * Detect when the remote is broken like bitbucket is, and exits 0 when+ it fails to run git-annex-shell.+ * assistant: Several improvements to performance and behavior when+ performing bulk adds of a large number of files (tens to hundreds+ of thousands).+ * assistant: Sanitize XMPP presence information logged for debugging.+ * webapp: Now automatically fills in any creds used by an existing remote+ when creating a new remote of the same type. Done for Internet Archive,+ S3, Glacier, and Box.com remotes.+ * Store an annex-uuid file in the bucket when setting up a new S3 remote.+ * Support building with DAV 0.4.++ -- Joey Hess <joeyh@debian.org> Wed, 01 May 2013 01:42:46 -0400+ git-annex (4.20130417) unstable; urgency=low * initremote: Generates encryption keys with high quality entropy.
@@ -3,7 +3,7 @@ - Common sanity checks for commands, and an interface to selectively - remove them, or add others. - - - Copyright 2011 Joey Hess <joey@kitenet.net>+ - Copyright 2011-2013 Joey Hess <joey@kitenet.net> - - Licensed under the GNU GPL version 3 or higher. -}@@ -14,6 +14,7 @@ import Types.Command import Init import Config+import Utility.Daemon import qualified Git commonChecks :: [CommandCheck]@@ -24,11 +25,17 @@ notDirect :: Command -> Command notDirect = addCheck $ whenM isDirect $- error "You cannot run this subcommand in a direct mode repository."+ error "You cannot run this command in a direct mode repository." notBareRepo :: Command -> Command notBareRepo = addCheck $ whenM (fromRepo Git.repoIsLocalBare) $- error "You cannot run this subcommand in a bare repository."+ error "You cannot run this command in a bare repository."++noDaemonRunning :: Command -> Command+noDaemonRunning = addCheck $ whenM (isJust <$> daemonpid) $+ error "You cannot run this command while git-annex watch or git-annex assistant is running."+ where+ daemonpid = liftIO . checkDaemon =<< fromRepo gitAnnexPidFile dontCheck :: CommandCheck -> Command -> Command dontCheck check cmd = mutateCheck cmd $ \c -> filter (/= check) c
@@ -24,6 +24,7 @@ import qualified Git.AutoCorrect import Annex.Content import Annex.Ssh+import Annex.Environment import Command type Params = [String]@@ -39,6 +40,7 @@ Right g -> do state <- Annex.new g (actions, state') <- Annex.run state $ do+ checkEnvironment checkfuzzy forM_ fields $ uncurry Annex.setField sequence_ flags
@@ -56,12 +56,14 @@ start file = ifAnnexed file addpresent add where add = do- s <- liftIO $ getSymbolicLinkStatus file- if isSymbolicLink s || not (isRegularFile s)- then stop- else do- showStart "add" file- next $ perform file+ ms <- liftIO $ catchMaybeIO $ getSymbolicLinkStatus file+ case ms of+ Nothing -> stop+ Just s+ | isSymbolicLink s || not (isRegularFile s) -> stop+ | otherwise -> do+ showStart "add" file+ next $ perform file addpresent (key, _) = ifM isDirect ( ifM (goodContent key file) ( stop , add ) , fixup key@@ -120,7 +122,7 @@ case (cache, inodeCache source) of (_, Nothing) -> go k cache (Just newc, Just c) | compareStrong c newc -> go k cache- _ -> failure+ _ -> failure "changed while it was being added" where go k cache = ifM isDirect ( godirect k cache , goindirect k cache ) @@ -129,15 +131,16 @@ moveAnnex key $ contentLocation source liftIO $ nukeFile $ keyFilename source return $ Just key- goindirect Nothing _ = failure+ goindirect Nothing _ = failure "failed to generate a key" godirect (Just (key, _)) (Just cache) = do addInodeCache key cache finishIngestDirect key source return $ Just key- godirect _ _ = failure+ godirect _ _ = failure "failed to generate a key" - failure = do+ failure msg = do+ warning $ keyFilename source ++ " " ++ msg when (contentLocation source /= keyFilename source) $ liftIO $ nukeFile $ contentLocation source return Nothing
@@ -156,7 +156,7 @@ | otherwise -> error "bad --pathdepth" where fullurl = uriRegName auth ++ uriPath url ++ uriQuery url- frombits a = join "/" $ a urlbits+ frombits a = intercalate "/" $ a urlbits urlbits = map (filesize . escape) $ filter (not . null) $ split "/" fullurl auth = fromMaybe (error $ "bad url " ++ show url) $ uriAuthority url filesize = take 255
@@ -12,7 +12,7 @@ import qualified Option import qualified Command.Watch import Init-import Locations.UserConfig+import Config.Files import System.Environment import System.Posix.Directory
@@ -17,7 +17,7 @@ import Annex.Version def :: [Command]-def = [notBareRepo $ +def = [notBareRepo $ noDaemonRunning $ command "direct" paramNothing seek SectionSetup "switch repository to direct mode"]
@@ -0,0 +1,56 @@+{- git-annex command+ -+ - Copyright 2013 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Command.EnableRemote where++import Common.Annex+import Command+import qualified Logs.Remote+import qualified Types.Remote as R+import qualified Command.InitRemote as InitRemote++import qualified Data.Map as M++def :: [Command]+def = [command "enableremote"+ (paramPair paramName $ paramOptional $ paramRepeating paramKeyValue)+ seek SectionSetup "enables use of an existing special remote"]++seek :: [CommandSeek]+seek = [withWords start]++start :: [String] -> CommandStart+start [] = unknownNameError "Specify the name of the special remote to enable."+start (name:ws) = go =<< InitRemote.findExisting name+ where+ config = Logs.Remote.keyValToConfig ws+ + go Nothing = unknownNameError "Unknown special remote name."+ go (Just (u, c)) = do+ let fullconfig = config `M.union` c + t <- InitRemote.findType fullconfig++ showStart "enableremote" name+ next $ perform t u fullconfig++unknownNameError :: String -> Annex a+unknownNameError prefix = do+ names <- InitRemote.remoteNames+ error $ prefix +++ if null names+ then ""+ else " Known special remotes: " ++ intercalate " " names++perform :: RemoteType -> UUID -> R.RemoteConfig -> CommandPerform+perform t u c = do+ c' <- R.setup t u c+ next $ cleanup u c'++cleanup :: UUID -> R.RemoteConfig -> CommandCleanup+cleanup u c = do+ Logs.Remote.configSet u c+ return True
@@ -24,7 +24,7 @@ def :: [Command] def = [noCommit $ noRepo showGeneralHelp $ dontCheck repoExists $- command "help" paramNothing seek SectionUtility "display help"]+ command "help" paramNothing seek SectionQuery "display help"] seek :: [CommandSeek] seek = [withWords start]@@ -42,7 +42,7 @@ showGeneralHelp :: IO () showGeneralHelp = putStrLn $ unlines- [ "The most commonly used git-annex commands are:"+ [ "The most frequently used git-annex commands are:" , unlines $ map cmdline $ concat [ Command.Init.def , Command.Add.def@@ -54,7 +54,9 @@ , Command.Whereis.def , Command.Fsck.def ]- , "Run git-annex without any options for a complete command list."+ , "Run 'git-annex' for a complete command list."+ , "Run 'git-annex command --help' for help on a specific command."+ , "Run `git annex help options' for a list of common options." ] where cmdline c = "\t" ++ cmdname c ++ "\t" ++ cmddesc c
@@ -18,11 +18,13 @@ import Annex.Content import Annex.CatFile import Annex.Version+import Annex.Perms import Init def :: [Command]-def = [notBareRepo $ command "indirect" paramNothing seek- SectionSetup "switch repository to indirect mode"]+def = [notBareRepo $ noDaemonRunning $+ command "indirect" paramNothing seek+ SectionSetup "switch repository to indirect mode"] seek :: [CommandSeek] seek = [withNothing start]@@ -79,6 +81,7 @@ fromdirect f k = do showStart "indirect" f+ thawContentDir =<< calcRepo (gitAnnexLocation k) cleandirect k -- clean before content directory gets frozen whenM (liftIO $ not . isSymbolicLink <$> getSymbolicLinkStatus f) $ do moveAnnex k f
@@ -1,6 +1,6 @@ {- git-annex command -- - Copyright 2011 Joey Hess <joey@kitenet.net>+ - Copyright 2011,2013 Joey Hess <joey@kitenet.net> - - Licensed under the GNU GPL version 3 or higher. -}@@ -16,30 +16,30 @@ import qualified Types.Remote as R import Annex.UUID import Logs.UUID+import Logs.Trust +import Data.Ord+ def :: [Command] def = [command "initremote" (paramPair paramName $ paramOptional $ paramRepeating paramKeyValue)- seek SectionSetup "sets up a special (non-git) remote"]+ seek SectionSetup "creates a special (non-git) remote"] seek :: [CommandSeek] seek = [withWords start] start :: [String] -> CommandStart-start [] = do- names <- remoteNames- error $ "Specify a name for the remote. " ++- if null names- then ""- else "Either a new name, or one of these existing special remotes: " ++ join " " names-start (name:ws) = do- (u, c) <- findByName name- let fullconfig = config `M.union` c - t <- findType fullconfig-- showStart "initremote" name- next $ perform t u name $ M.union config c+start [] = error "Specify a name for the remote."+start (name:ws) = ifM (isJust <$> findExisting name)+ ( error $ "There is already a special remote named \"" ++ name +++ "\". (Use enableremote to enable an existing special remote.)"+ , do+ (u, c) <- generateNew name+ t <- findType config + showStart "initremote" name+ next $ perform t u name $ M.union config c+ ) where config = Logs.Remote.keyValToConfig ws @@ -54,18 +54,22 @@ 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 <- Logs.Remote.readRemoteLog- maybe generate return $ findByName' name m- where- generate = do- uuid <- liftIO genUUID- return (uuid, M.insert nameKey name M.empty)+{- See if there's an existing special remote with this name. -}+findExisting :: String -> Annex (Maybe (UUID, R.RemoteConfig))+findExisting name = do+ t <- trustMap+ matches <- sortBy (comparing $ \(u, _c) -> M.lookup u t )+ . findByName name+ <$> Logs.Remote.readRemoteLog+ return $ headMaybe matches -findByName' :: String -> M.Map UUID R.RemoteConfig -> Maybe (UUID, R.RemoteConfig)-findByName' n = headMaybe . filter (matching . snd) . M.toList+generateNew :: String -> Annex (UUID, R.RemoteConfig)+generateNew name = do+ uuid <- liftIO genUUID+ return (uuid, M.singleton nameKey name)++findByName :: String -> M.Map UUID R.RemoteConfig -> [(UUID, R.RemoteConfig)]+findByName n = filter (matching . snd) . M.toList where matching c = case M.lookup nameKey c of Nothing -> False
@@ -0,0 +1,30 @@+{- git-annex command+ -+ - Copyright 2013 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Command.RmUrl where++import Common.Annex+import Command+import Logs.Web++def :: [Command]+def = [notBareRepo $+ command "rmurl" (paramPair paramFile paramUrl) seek+ SectionCommon "record file is not available at url"]++seek :: [CommandSeek]+seek = [withPairs start]++start :: (FilePath, String) -> CommandStart+start (file, url) = flip whenAnnexed file $ \_ (key, _) -> do+ showStart "rmurl" file+ next $ next $ cleanup url key++cleanup :: String -> Key -> CommandCleanup+cleanup url key = do+ setUrlMissing key url+ return True
@@ -57,7 +57,7 @@ type StatState = StateT StatInfo Annex def :: [Command]-def = [command "status" (paramOptional paramPaths) seek+def = [command "status" paramPaths seek SectionQuery "shows status information about the annex"] seek :: [CommandSeek]
@@ -73,15 +73,16 @@ return l available = filter (not . Remote.specialRemote) . filter (remoteAnnexSync . Types.Remote.gitconfig)- <$> Remote.enabledRemoteList+ <$> Remote.remoteList good = filterM $ Remote.Git.repoAvail . Types.Remote.repo fastest = fromMaybe [] . headMaybe . Remote.byCost commit :: CommandStart commit = next $ next $ do ifM isDirect- ( ifM stageDirect- ( runcommit [] , return True )+ ( do+ void $ stageDirect+ runcommit [] , runcommit [Param "-a"] ) where@@ -90,8 +91,9 @@ showOutput Annex.Branch.commit "update" -- Commit will fail when the tree is clean, so ignore failure.- _ <- inRepo $ Git.Command.runBool $ (Param "commit") : ps +++ let params = (Param "commit") : ps ++ [Param "-m", Param "git-annex automatic sync"]+ _ <- inRepo $ tryIO . Git.Command.runQuiet params return True mergeLocal :: Git.Ref -> CommandStart
@@ -95,7 +95,7 @@ sendRequest :: Transfer -> AssociatedFile -> Handle -> IO () sendRequest t f h = do- hPutStr h $ join fieldSep+ hPutStr h $ intercalate fieldSep [ serialize (transferDirection t) , serialize (transferUUID t) , serialize (transferKey t)
@@ -27,12 +27,10 @@ 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+ putStrLn $ "supported repository versions: " ++ unwords supportedVersions+ putStrLn $ "upgrade supported from repository versions: " ++ unwords upgradableVersions putStrLn $ "build flags: " ++ unwords buildFlags stop- where- vs = join " " showPackageVersion :: IO () showPackageVersion = putStrLn $ "git-annex version: " ++ SysConfig.packageversion
@@ -15,6 +15,7 @@ import Assistant.Threads.WebApp import Assistant.WebApp import Assistant.Install+import Annex.Environment import Utility.WebApp import Utility.Daemon (checkDaemon) import Init@@ -22,7 +23,7 @@ import qualified Git.Config import qualified Git.CurrentRepo import qualified Annex-import Locations.UserConfig+import Config.Files import qualified Option import System.Posix.Directory@@ -111,6 +112,7 @@ -} firstRun :: Maybe HostName -> IO () firstRun listenhost = do+ checkEnvironmentIO {- Without a repository, we cannot have an Annex monad, so cannot - get a ThreadState. Using undefined is only safe because the - webapp checks its noAnnex field before accessing the@@ -135,6 +137,7 @@ mainthread v url htmlshim | isJust listenhost = do putStrLn url+ hFlush stdout go | otherwise = do browser <- maybe Nothing webBrowser <$> Git.Config.global@@ -150,8 +153,9 @@ sendurlback v _origout _origerr url _htmlshim = putMVar v url openBrowser :: Maybe FilePath -> FilePath -> Maybe Handle -> Maybe Handle -> IO ()-openBrowser cmd htmlshim outh errh = do+openBrowser mcmd htmlshim outh errh = do hPutStrLn (fromMaybe stdout outh) $ "Launching web browser on " ++ url+ hFlush stdout environ <- cleanEnvironment (_, _, _, pid) <- createProcess p { env = environ@@ -163,7 +167,9 @@ hPutStrLn (fromMaybe stderr errh) "failed to start web browser" where url = fileUrl htmlshim- p = proc (fromMaybe browserCommand cmd) [htmlshim]+ p = case mcmd of+ Just cmd -> proc cmd [htmlshim]+ Nothing -> browserProc htmlshim {- web.browser is a generic git config setting for a web browser program -} webBrowser :: Git.Repo -> Maybe FilePath
@@ -2,7 +2,7 @@ module Common (module X) where -import Control.Monad as X hiding (join)+import Control.Monad as X import Control.Monad.IfElse as X import Control.Applicative as X import "mtl" Control.Monad.State.Strict as X (liftIO)@@ -10,7 +10,7 @@ import Data.Maybe as X import Data.List as X hiding (head, tail, init, last)-import Data.String.Utils as X+import Data.String.Utils as X hiding (join) import "MissingH" System.Path as X import System.FilePath as X
@@ -0,0 +1,60 @@+{- git-annex extra config files+ -+ - Copyright 2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Config.Files where++import Common+import Utility.TempFile+import Utility.FreeDesktop++{- ~/.config/git-annex/file -}+userConfigFile :: FilePath -> IO FilePath+userConfigFile file = do+ dir <- userConfigDir+ return $ dir </> "git-annex" </> file++autoStartFile :: IO FilePath+autoStartFile = userConfigFile "autostart"++{- Returns anything listed in the autostart file (which may not exist). -}+readAutoStartFile :: IO [FilePath]+readAutoStartFile = do+ f <- autoStartFile+ nub . map dropTrailingPathSeparator . lines+ <$> catchDefaultIO "" (readFile f)++modifyAutoStartFile :: ([FilePath] -> [FilePath]) -> IO ()+modifyAutoStartFile func = do+ dirs <- readAutoStartFile+ let dirs' = nubBy equalFilePath $ func dirs+ when (dirs' /= dirs) $ do+ f <- autoStartFile+ createDirectoryIfMissing True (parentDir f)+ viaTmp writeFile f $ unlines $ dirs'++{- Adds a directory to the autostart file. -}+addAutoStartFile :: FilePath -> IO ()+addAutoStartFile path = modifyAutoStartFile $ (:) path++{- Removes a directory from the autostart file. -}+removeAutoStartFile :: FilePath -> IO ()+removeAutoStartFile path = modifyAutoStartFile $+ filter (not . equalFilePath path)++{- The path to git-annex is written here; which is useful when cabal+ - has installed it to some aweful non-PATH location. -}+programFile :: IO FilePath+programFile = userConfigFile "program"++{- Returns a command to run for git-annex. -}+readProgramFile :: IO FilePath+readProgramFile = do+ programfile <- programFile+ catchDefaultIO cmd $ + fromMaybe cmd . headMaybe . lines <$> readFile programfile+ where+ cmd = "git-annex"
binary file changed (absent → 13076 bytes)
@@ -130,7 +130,8 @@ remoteNamedFromKey :: String -> IO Repo -> IO Repo remoteNamedFromKey k = remoteNamed basename where- basename = join "." $ reverse $ drop 1 $ reverse $ drop 1 $ split "." k+ basename = intercalate "." $ + reverse $ drop 1 $ reverse $ drop 1 $ split "." k {- Constructs a new Repo for one of a Repo's remotes using a given - location (ie, an url). -}
@@ -20,7 +20,6 @@ import qualified Data.Map as M import System.IO import System.Process-import Data.String.Utils import Utility.SafeCommand import Common@@ -151,7 +150,7 @@ runAction repo action@(CommandAction {}) = withHandle StdinHandle createProcessSuccess p $ \h -> do fileEncoding h- hPutStr h $ join "\0" $ getFiles action+ hPutStr h $ intercalate "\0" $ getFiles action hClose h where p = (proc "xargs" params) { env = gitEnv repo }
@@ -30,6 +30,7 @@ import qualified Command.Init import qualified Command.Describe import qualified Command.InitRemote+import qualified Command.EnableRemote import qualified Command.Fsck import qualified Command.Unused import qualified Command.DropUnused@@ -53,6 +54,7 @@ import qualified Command.Vicfg import qualified Command.Sync import qualified Command.AddUrl+import qualified Command.RmUrl import qualified Command.Import import qualified Command.Map import qualified Command.Direct@@ -85,10 +87,12 @@ , Command.Lock.def , Command.Sync.def , Command.AddUrl.def+ , Command.RmUrl.def , Command.Import.def , Command.Init.def , Command.Describe.def , Command.InitRemote.def+ , Command.EnableRemote.def , Command.Reinject.def , Command.Unannex.def , Command.Uninit.def
@@ -1,6 +1,6 @@ {- user-specified limits on files to act on -- - Copyright 2011,2012 Joey Hess <joey@kitenet.net>+ - Copyright 2011-2013 Joey Hess <joey@kitenet.net> - - Licensed under the GNU GPL version 3 or higher. -}@@ -88,9 +88,9 @@ - once. Also, we use regex-TDFA because it's less buggy in its support - of non-unicode characters. -} matchglob :: String -> Annex.FileInfo -> Bool-matchglob glob (Annex.FileInfo { Annex.matchFile = f }) =+matchglob glob fi = case cregex of- Right r -> case execute r f of+ Right r -> case execute r (Annex.matchFile fi) of Right (Just _) -> True _ -> False Left _ -> error $ "failed to compile regex: " ++ regex@@ -137,6 +137,11 @@ check a = lookupFile >=> handle a handle _ Nothing = return False handle a (Just (key, _)) = a key++{- Limit to content that is in a directory, anywhere in the repository tree -}+limitInDir :: FilePath -> MkLimit+limitInDir dir = const $ Right $ const $ \fi -> return $+ any (== dir) $ splitPath $ takeDirectory $ Annex.matchFile fi {- Adds a limit to skip files not believed to have the specified number - of copies. -}
@@ -1,57 +0,0 @@-{- git-annex user config files- -- - Copyright 2012 Joey Hess <joey@kitenet.net>- -- - Licensed under the GNU GPL version 3 or higher.- -}--module Locations.UserConfig where--import Common-import Utility.TempFile-import Utility.FreeDesktop--{- ~/.config/git-annex/file -}-userConfigFile :: FilePath -> IO FilePath-userConfigFile file = do- dir <- userConfigDir- return $ dir </> "git-annex" </> file--autoStartFile :: IO FilePath-autoStartFile = userConfigFile "autostart"--{- Returns anything listed in the autostart file (which may not exist). -}-readAutoStartFile :: IO [FilePath]-readAutoStartFile = do- f <- autoStartFile- nub . lines <$> catchDefaultIO "" (readFile f)--{- Adds a directory to the autostart file. -}-addAutoStartFile :: FilePath -> IO ()-addAutoStartFile path = do- dirs <- readAutoStartFile- when (path `notElem` dirs) $ do- f <- autoStartFile- createDirectoryIfMissing True (parentDir f)- viaTmp writeFile f $ unlines $ dirs ++ [path]--{- Removes a directory from the autostart file. -}-removeAutoStartFile :: FilePath -> IO ()-removeAutoStartFile path = do- dirs <- readAutoStartFile- when (path `elem` dirs) $ do- f <- autoStartFile- createDirectoryIfMissing True (parentDir f)- viaTmp writeFile f $ unlines $- filter (not . equalFilePath path) dirs--{- The path to git-annex is written here; which is useful when cabal- - has installed it to some aweful non-PATH location. -}-programFile :: IO FilePath-programFile = userConfigFile "program"--{- Returns a command to run for git-annex. -}-readProgramFile :: IO FilePath-readProgramFile = do- programfile <- programFile- catchDefaultIO "git-annex" $ readFile programfile
binary file changed (12856 → absent bytes)
@@ -30,7 +30,9 @@ import Annex.FileMatcher import Annex.UUID import Types.Group+import Types.Remote (RemoteConfig) import Logs.Group+import Logs.Remote import Types.StandardGroups {- Filename of preferred-content.log. -}@@ -65,8 +67,9 @@ preferredContentMapLoad :: Annex Annex.PreferredContentMap preferredContentMapLoad = do groupmap <- groupMap+ configmap <- readRemoteLog m <- simpleMap- . parseLogWithUUID ((Just .) . makeMatcher groupmap)+ . parseLogWithUUID ((Just .) . makeMatcher groupmap configmap) <$> Annex.Branch.get preferredContentLog Annex.changeState $ \s -> s { Annex.preferredcontentmap = Just m } return m@@ -79,30 +82,30 @@ - because the configuration is shared amoung repositories and newer - versions of git-annex may add new features. Instead, parse errors - result in a Matcher that will always succeed. -}-makeMatcher :: GroupMap -> UUID -> String -> FileMatcher-makeMatcher groupmap u s- | s == "standard" = standardMatcher groupmap u+makeMatcher :: GroupMap -> M.Map UUID RemoteConfig -> UUID -> String -> FileMatcher+makeMatcher groupmap configmap u expr+ | expr == "standard" = standardMatcher groupmap configmap u | null (lefts tokens) = Utility.Matcher.generate $ rights tokens | otherwise = matchAll where- tokens = map (parseToken (limitPresent $ Just u) groupmap) (tokenizeMatcher s)+ tokens = exprParser groupmap configmap (Just u) expr {- Standard matchers are pre-defined for some groups. If none is defined, - or a repository is in multiple groups with standard matchers, match all. -}-standardMatcher :: GroupMap -> UUID -> FileMatcher-standardMatcher m u = maybe matchAll (makeMatcher m u . preferredContent) $- getStandardGroup =<< u `M.lookup` groupsByUUID m+standardMatcher :: GroupMap -> M.Map UUID RemoteConfig -> UUID -> FileMatcher+standardMatcher groupmap configmap u = + maybe matchAll (makeMatcher groupmap configmap u . preferredContent) $+ getStandardGroup =<< u `M.lookup` groupsByUUID groupmap {- Checks if an expression can be parsed, if not returns Just error -} checkPreferredContentExpression :: String -> Maybe String-checkPreferredContentExpression s- | s == "standard" = Nothing- | otherwise = case parsedToMatcher vs of+checkPreferredContentExpression expr+ | expr == "standard" = Nothing+ | otherwise = case parsedToMatcher tokens of Left e -> Just e Right _ -> Nothing where- vs = map (parseToken (limitPresent Nothing) emptyGroupMap)- (tokenizeMatcher s)+ tokens = exprParser emptyGroupMap M.empty Nothing expr {- Puts a UUID in a standard group, and sets its preferred content to use - the standard expression for that group, unless something is already set. -}
@@ -9,6 +9,7 @@ trustLog, TrustLevel(..), trustGet,+ trustMap, trustSet, trustPartition, trustExclude,
@@ -54,4 +54,7 @@ logChange key webUUID InfoPresent setUrlMissing :: Key -> URLString -> Annex ()-setUrlMissing key url = addLog (urlLog key) =<< logNow InfoMissing url+setUrlMissing key url = do+ addLog (urlLog key) =<< logNow InfoMissing url+ whenM (null <$> getUrls key) $+ logChange key webUUID InfoMissing
@@ -158,11 +158,12 @@ rm -f tmp/git-annex.dmg.bz2 bzip2 --fast tmp/git-annex.dmg +ANDROID_FLAGS= # Cross compile for Android. # Uses https://github.com/neurocyte/ghc-android android: Build/EvilSplicer echo "Running native build, to get TH splices.."- if [ ! -e dist/setup/setup ]; then $(CABAL) configure -f"-Production" -O0; fi+ if [ ! -e dist/setup/setup ]; then $(CABAL) configure -f"-Production $(ANDROID_FLAGS)" -O0; fi mkdir -p tmp if ! $(CABAL) build --ghc-options=-ddump-splices 2> tmp/dump-splices; then tail tmp/dump-splices >&2; exit 1; fi echo "Setting up Android build tree.."@@ -176,13 +177,13 @@ # Some additional dependencies needed by the expanded splices. sed -i 's/^ Build-Depends: / Build-Depends: yesod-routes, yesod-core, shakespeare-css, shakespeare-js, shakespeare, blaze-markup, file-embed, wai-app-static, /' tmp/androidtree/git-annex.cabal # Avoid warnings due to sometimes unused imports added for the splices.- sed -i 's/-Wall/-Wall -fno-warn-unused-imports/' tmp/androidtree/git-annex.cabal+ sed -i 's/GHC-Options: \\(.*\\)-Wall/GHC-Options: \\1-Wall -fno-warn-unused-imports /i' tmp/androidtree/git-annex.cabal # Cabal cannot cross compile with custom build type, so workaround. sed -i 's/Build-type: Custom/Build-type: Simple/' tmp/androidtree/git-annex.cabal if [ ! -e tmp/androidtree/dist/setup/setup ]; then \ cd tmp/androidtree; \ cabal configure; \- $$HOME/.ghc/android-14/arm-linux-androideabi-4.7/arm-linux-androideabi/bin/cabal configure -f'Android Assistant -Pairing'; \+ $$HOME/.ghc/android-14/arm-linux-androideabi-4.7/arm-linux-androideabi/bin/cabal configure -f"Android $(ANDROID_FLAGS)"; \ fi $(MAKE) -C tmp/androidtree git-annex
@@ -31,7 +31,6 @@ showCustom, showHeader, showRaw,- setupConsole ) where @@ -179,7 +178,7 @@ [ "git-annex:", file, "not found" ] indent :: String -> String-indent = join "\n" . map (\l -> " " ++ l) . lines+indent = intercalate "\n" . map (\l -> " " ++ l) . lines {- Shows a JSON fragment only when in json mode. -} maybeShowJSON :: JSON a => [(String, a)] -> Annex ()
@@ -19,7 +19,6 @@ remoteTypes, remoteList,- enabledRemoteList, specialRemote, remoteMap, uuidDescriptions,@@ -211,7 +210,8 @@ let validtrusteduuids = validuuids `intersect` trusted -- remotes that match uuids that have the key- allremotes <- enabledRemoteList+ allremotes <- filter (not . remoteAnnexIgnore . gitconfig)+ <$> remoteList let validremotes = remotesWithUUID allremotes validuuids return (sortBy (comparing cost) validremotes, validtrusteduuids)@@ -238,7 +238,7 @@ showTriedRemotes [] = noop showTriedRemotes remotes = showLongNote $ "Unable to access these remotes: " ++- join ", " (map name remotes)+ intercalate ", " (map name remotes) forceTrust :: TrustLevel -> String -> Annex () forceTrust level remotename = do
@@ -257,7 +257,7 @@ where bits = split ":" r host = Prelude.head bits- dir = join ":" $ drop 1 bits+ dir = intercalate ":" $ drop 1 bits -- "host:~user/dir" is not supported specially by bup; -- "host:dir" is relative to the home directory; -- "host:" goes in ~/.bup
@@ -136,21 +136,14 @@ - returns the updated repo. -} tryGitConfigRead :: Git.Repo -> Annex Git.Repo tryGitConfigRead r - | not $ M.null $ Git.config r = return r -- already read+ | haveconfig r = return r -- already read | Git.repoIsSsh r = store $ do v <- onRemote r (pipedsshconfig, Left undefined) "configlist" [] []- case (v, Git.remoteName r) of- (Right r', _) -> return r'- (Left _, Just n) -> do- {- Is this remote just not available, or does- - it not have git-annex-shell?- - Find out by trying to fetch from the remote. -}- whenM (inRepo $ Git.Command.runBool [Param "fetch", Param "--quiet", Param n]) $ do- let k = "remote." ++ n ++ ".annex-ignore"- warning $ "Remote " ++ n ++ " does not have git-annex installed; setting " ++ k- inRepo $ Git.Command.run [Param "config", Param k, Param "true"]- return r- _ -> return r+ case v of+ Right r'+ | haveconfig r' -> return r'+ | otherwise -> configlist_failed+ Left _ -> configlist_failed | Git.repoIsHttp r = do headers <- getHttpHeaders store $ safely $ geturlconfig headers@@ -159,6 +152,8 @@ ensureInitialized Annex.getState Annex.repo where+ haveconfig = not . M.null . Git.config+ -- Reading config can fail due to IO error or -- for other reasons; catch all possible exceptions. safely a = either (const $ return r) return@@ -199,6 +194,18 @@ new : exchange ls new | otherwise = old : exchange ls new++ {- Is this remote just not available, or does+ - it not have git-annex-shell?+ - Find out by trying to fetch from the remote. -}+ configlist_failed = case Git.remoteName r of+ Nothing -> return r+ Just n -> do+ whenM (inRepo $ Git.Command.runBool [Param "fetch", Param "--quiet", Param n]) $ do+ let k = "remote." ++ n ++ ".annex-ignore"+ warning $ "Remote " ++ n ++ " does not have git-annex installed; setting " ++ k+ inRepo $ Git.Command.run [Param "config", Param k, Param "true"]+ return r {- Checks if a given remote has the content for a key inAnnex. - If the remote cannot be accessed, or if it cannot determine
@@ -123,7 +123,7 @@ storeCipher c (EncryptedCipher t ks) = M.insert "cipher" (toB64 t) $ M.insert "cipherkeys" (showkeys ks) c where- showkeys (KeyIds l) = join "," l+ showkeys (KeyIds l) = intercalate "," l {- Extracts an StorableCipher from a remote's configuration. -} extractCipher :: RemoteConfig -> Maybe StorableCipher
@@ -98,10 +98,6 @@ Remote.Git.configRead r | otherwise = return r -{- All remotes that are not ignored. -}-enabledRemoteList :: Annex [Remote]-enabledRemoteList = filter (not . remoteAnnexIgnore . gitconfig) <$> remoteList- {- Checks if a remote is a special remote -} specialRemote :: Remote -> Bool specialRemote r = remotetype r /= Remote.Git.remote
@@ -1,20 +1,21 @@-{- Amazon S3 remotes.+{- S3 remotes -- - Copyright 2011 Joey Hess <joey@kitenet.net>+ - Copyright 2011-2013 Joey Hess <joey@kitenet.net> - - Licensed under the GNU GPL version 3 or higher. -} -module Remote.S3 (remote) where+module Remote.S3 (remote, iaHost, isIA, isIAHost, iaItemUrl) where import Network.AWS.AWSConnection-import Network.AWS.S3Object+import Network.AWS.S3Object hiding (getStorageClass) import Network.AWS.S3Bucket hiding (size) import Network.AWS.AWSResult import qualified Data.Text as T import qualified Data.ByteString.Lazy.Char8 as L import qualified Data.Map as M import Data.Char+import Network.Socket (HostName) import Common.Annex import Types.Remote@@ -29,7 +30,10 @@ import Creds import Utility.Metered import Annex.Content+import Logs.Web +type Bucket = String+ remote :: RemoteType remote = RemoteType { typename = "S3",@@ -53,7 +57,7 @@ storeKey = store this, retrieveKeyFile = retrieve this, retrieveKeyFileCheap = retrieveCheap this,- removeKey = remove this,+ removeKey = remove this c, hasKey = checkPresent this, hasKeyCheap = False, whereisKey = Nothing,@@ -67,7 +71,7 @@ } s3Setup :: UUID -> RemoteConfig -> Annex RemoteConfig-s3Setup u c = handlehost $ M.lookup "host" c+s3Setup u c = if isIA c then archiveorg else defaulthost where remotename = fromJust (M.lookup "name" c) defbucket = remotename ++ "-" ++ fromUUID u@@ -79,11 +83,6 @@ , ("bucket", defbucket) ] - handlehost Nothing = defaulthost- handlehost (Just h)- | ".archive.org" `isSuffixOf` map toLower h = archiveorg- | otherwise = defaulthost- use fullconfig = do gitConfigSpecialRemote u fullconfig "s3" "true" setRemoteCredPair fullconfig (AWS.creds u)@@ -97,7 +96,8 @@ archiveorg = do showNote "Internet Archive mode" maybe (error "specify bucket=") (const noop) $- M.lookup "bucket" archiveconfig+ getBucket archiveconfig+ writeUUIDFile archiveconfig u use archiveconfig where archiveconfig =@@ -115,42 +115,39 @@ store :: Remote -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool store r k _f p = s3Action r False $ \(conn, bucket) -> - sendAnnex k (void $ remove r k) $ \src -> do- res <- storeHelper (conn, bucket) r k p src- s3Bool res+ sendAnnex k (void $ remove' r k) $ \src -> do+ ok <- s3Bool =<< storeHelper (conn, bucket) r k p src + -- Store public URL to item in Internet Archive.+ when (ok && isIA (config r)) $+ setUrlPresent k (iaKeyUrl r k)++ return ok+ storeEncrypted :: Remote -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool storeEncrypted r (cipher, enck) k p = s3Action r False $ \(conn, bucket) -> -- 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 -> sendAnnex k (void $ remove r enck) $ \src -> do+ withTmp enck $ \tmp -> sendAnnex k (void $ remove' r enck) $ \src -> do liftIO $ encrypt (getGpgOpts r) cipher (feedFile src) $ readBytes $ L.writeFile tmp- res <- storeHelper (conn, bucket) r enck p tmp- s3Bool res+ s3Bool =<< storeHelper (conn, bucket) r enck p tmp -storeHelper :: (AWSConnection, String) -> Remote -> Key -> MeterUpdate -> FilePath -> Annex (AWSResult ())+storeHelper :: (AWSConnection, Bucket) -> Remote -> Key -> MeterUpdate -> FilePath -> Annex (AWSResult ()) storeHelper (conn, bucket) r k p file = do size <- maybe getsize (return . fromIntegral) $ keySize k meteredBytes (Just p) size $ \meterupdate -> liftIO $ withMeteredFile file meterupdate $ \content -> do -- size is provided to S3 so the whole content -- does not need to be buffered to calculate it- let object = setStorageClass storageclass $ S3Object+ let object = S3Object bucket (bucketFile r k) ""- (("Content-Length", show size) : xheaders)+ (("Content-Length", show size) : getXheaders (config r)) content- sendObject conn object+ sendObject conn $+ setStorageClass (getStorageClass $ config r) object where- storageclass =- case fromJust $ M.lookup "storageclass" $ config r of- "REDUCED_REDUNDANCY" -> REDUCED_REDUNDANCY- _ -> STANDARD- getsize = liftIO $ fromIntegral . fileSize <$> getFileStatus file- - xheaders = filter isxheader $ M.assocs $ config r- isxheader (h, _) = "x-amz-" `isPrefixOf` h retrieve :: Remote -> Key -> AssociatedFile -> FilePath -> MeterUpdate -> Annex Bool retrieve r k _f d p = s3Action r False $ \(conn, bucket) ->@@ -177,11 +174,20 @@ return True Left e -> s3Warning e -remove :: Remote -> Key -> Annex Bool-remove r k = s3Action r False $ \(conn, bucket) -> do- res <- liftIO $ deleteObject conn $ bucketKey r bucket k- s3Bool res+{- Internet Archive doesn't easily allow removing content.+ - While it may remove the file, there are generally other files+ - derived from it that it does not remove. -}+remove :: Remote -> RemoteConfig -> Key -> Annex Bool+remove r c k+ | isIA c = do+ warning "Cannot remove content from the Internet Archive"+ return False+ | otherwise = remove' r k +remove' :: Remote -> Key -> Annex Bool+remove' r k = s3Action r False $ \(conn, bucket) ->+ s3Bool =<< liftIO (deleteObject conn $ bucketKey r bucket k)+ checkPresent :: Remote -> Key -> Annex (Either String Bool) checkPresent r k = s3Action r noconn $ \(conn, bucket) -> do showAction $ "checking " ++ name r@@ -205,7 +211,7 @@ s3Bool (Right _) = return True s3Bool (Left e) = s3Warning e -s3Action :: Remote -> a -> ((AWSConnection, String) -> Annex a) -> Annex a+s3Action :: Remote -> a -> ((AWSConnection, Bucket) -> Annex a) -> Annex a s3Action r noconn action = do let bucket = M.lookup "bucket" $ config r conn <- s3Connection (config r) (uuid r)@@ -217,12 +223,14 @@ bucketFile r = munge . key2file where munge s = case M.lookup "mungekeys" c of- Just "ia" -> iaMunge $ fileprefix ++ s- _ -> fileprefix ++ s- fileprefix = M.findWithDefault "" "fileprefix" c+ Just "ia" -> iaMunge $ filePrefix c ++ s+ _ -> filePrefix c ++ s c = config r -bucketKey :: Remote -> String -> Key -> S3Object+filePrefix :: RemoteConfig -> String+filePrefix = M.findWithDefault "" "fileprefix"++bucketKey :: Remote -> Bucket -> Key -> S3Object bucketKey r bucket k = S3Object bucket (bucketFile r k) "" [] L.empty {- Internet Archive limits filenames to a subset of ascii,@@ -243,18 +251,43 @@ showAction "checking bucket" loc <- liftIO $ getBucketLocation conn bucket case loc of- Right _ -> noop+ Right _ -> writeUUIDFile c u Left err@(NetworkError _) -> s3Error err Left (AWSError _ _) -> do showAction $ "creating bucket in " ++ datacenter res <- liftIO $ createBucketIn conn bucket datacenter case res of- Right _ -> noop+ Right _ -> writeUUIDFile c u Left err -> s3Error err where- bucket = fromJust $ M.lookup "bucket" c+ bucket = fromJust $ getBucket c datacenter = fromJust $ M.lookup "datacenter" c +{- Writes the UUID to an annex-uuid file within the bucket.+ -+ - If the file already exists in the bucket, it must match.+ -+ - Note that IA items do not get created by createBucketIn. + - Rather, they are created the first time a file is stored in them.+ - So this also takes care of that.+ -}+writeUUIDFile :: RemoteConfig -> UUID -> Annex ()+writeUUIDFile c u = do+ conn <- s3ConnectionRequired c u+ go conn =<< liftIO (tryNonAsync $ getObject conn $ mkobject L.empty)+ where+ go _conn (Right (Right o)) = unless (obj_data o == uuidb) $+ error $ "This bucket is already in use by a different S3 special remote, with UUID: " ++ L.unpack (obj_data o)+ go conn _ = do+ let object = setStorageClass (getStorageClass c) (mkobject uuidb)+ either s3Error return =<< liftIO (sendObject conn object)++ file = filePrefix c ++ "annex-uuid"+ uuidb = L.pack $ fromUUID u+ bucket = fromJust $ getBucket c++ mkobject = S3Object bucket file "" (getXheaders c)+ s3ConnectionRequired :: RemoteConfig -> UUID -> Annex AWSConnection s3ConnectionRequired c u = maybe (error "Cannot connect to S3") return =<< s3Connection c u@@ -270,3 +303,34 @@ case reads s of [(p, _)] -> p _ -> error $ "bad S3 port value: " ++ s++getBucket :: RemoteConfig -> Maybe Bucket+getBucket = M.lookup "bucket"++getStorageClass :: RemoteConfig -> StorageClass+getStorageClass c = case fromJust $ M.lookup "storageclass" c of+ "REDUCED_REDUNDANCY" -> REDUCED_REDUNDANCY+ _ -> STANDARD+ +getXheaders :: RemoteConfig -> [(String, String)]+getXheaders = filter isxheader . M.assocs+ where+ isxheader (h, _) = "x-amz-" `isPrefixOf` h++{- Hostname to use for archive.org S3. -}+iaHost :: HostName+iaHost = "s3.us.archive.org"++isIA :: RemoteConfig -> Bool+isIA c = maybe False isIAHost (M.lookup "host" c)++isIAHost :: HostName -> Bool+isIAHost h = ".archive.org" `isSuffixOf` map toLower h++iaItemUrl :: Bucket -> URLString+iaItemUrl bucket = "http://archive.org/details/" ++ bucket++iaKeyUrl :: Remote -> Key -> URLString+iaKeyUrl r k = "http://archive.org/download/" ++ bucket ++ "/" ++ bucketFile r k+ where+ bucket = fromMaybe "" $ getBucket $ config r
@@ -7,7 +7,7 @@ {-# LANGUAGE ScopedTypeVariables, CPP #-} -module Remote.WebDAV (remote, davCreds, setCredsEnv) where+module Remote.WebDAV (remote, davCreds, setCredsEnv, configUrl) where import Network.Protocol.HTTP.DAV import qualified Data.Map as M@@ -203,11 +203,14 @@ davAction :: Remote -> a -> ((DavUrl, DavUser, DavPass) -> Annex a) -> Annex a davAction r unconfigured action = do mcreds <- getCreds (config r) (uuid r)- case (mcreds, M.lookup "url" $ config r) of+ case (mcreds, configUrl r) of (Just (user, pass), Just url) -> action (url, toDavUser user, toDavPass pass) _ -> return unconfigured +configUrl :: Remote -> Maybe DavUrl+configUrl r = M.lookup "url" $ config r+ toDavUser :: String -> DavUser toDavUser = B8.fromString @@ -228,7 +231,7 @@ davUrl baseurl file = baseurl </> file davUrlExists :: DavUrl -> DavUser -> DavPass -> IO (Either String Bool)-davUrlExists url user pass = decode <$> catchHttp (getProps url user pass)+davUrlExists url user pass = decode <$> catchHttp get where decode (Right _) = Right True #if ! MIN_VERSION_http_conduit(1,9,0)@@ -238,6 +241,11 @@ #endif | statusCode status == statusCode notFound404 = Right False decode (Left e) = Left $ showEitherException e+#if ! MIN_VERSION_DAV(0,4,0)+ get = getProps url user pass+#else+ get = getProps url user pass Nothing+#endif davGetUrlContent :: DavUrl -> DavUser -> DavPass -> IO (Maybe L.ByteString) davGetUrlContent url user pass = fmap (snd . snd) <$>
binary file changed (10448 → 10440 bytes)
@@ -730,15 +730,17 @@ Utility.Gpg.testTestHarness @? "test harness self-test failed" Utility.Gpg.testHarness $ do createDirectory "dir"- let initremote = git_annex "initremote"+ let a cmd = git_annex cmd [ "foo" , "type=directory" , "encryption=" ++ Utility.Gpg.testKeyId , "directory=dir" , "highRandomQuality=false" ]- initremote @? "initremote failed"- initremote @? "initremote failed when run twice in a row"+ a "initremote" @? "initremote failed"+ not <$> a "initremote" @? "initremote failed to fail when run twice in a row"+ a "enableremote" @? "enableremote failed"+ a "enableremote" @? "enableremote failed when run twice in a row" git_annex "get" [annexedfile] @? "get of file failed" annexed_present annexedfile git_annex "copy" [annexedfile, "--to", "foo"] @? "copy --to encrypted remote failed"
@@ -7,6 +7,11 @@ module Types.StandardGroups where +import Types.Remote (RemoteConfig)++import qualified Data.Map as M+import Data.Maybe+ data StandardGroup = ClientGroup | TransferGroup@@ -16,6 +21,7 @@ | FullArchiveGroup | SourceGroup | ManualGroup+ | PublicGroup | UnwantedGroup deriving (Eq, Ord, Enum, Bounded, Show) @@ -28,6 +34,7 @@ fromStandardGroup FullArchiveGroup = "archive" fromStandardGroup SourceGroup = "source" fromStandardGroup ManualGroup = "manual"+fromStandardGroup PublicGroup = "public" fromStandardGroup UnwantedGroup = "unwanted" toStandardGroup :: String -> Maybe StandardGroup@@ -39,6 +46,7 @@ toStandardGroup "archive" = Just FullArchiveGroup toStandardGroup "source" = Just SourceGroup toStandardGroup "manual" = Just ManualGroup+toStandardGroup "public" = Just PublicGroup toStandardGroup "unwanted" = Just UnwantedGroup toStandardGroup _ = Nothing @@ -52,7 +60,16 @@ descStandardGroup SourceGroup = "file source: moves files on to other repositories" descStandardGroup ManualGroup = "manual mode: only stores files you manually choose" descStandardGroup UnwantedGroup = "unwanted: remove content from this repository"+descStandardGroup PublicGroup = "public: publishes files located in an associated directory" +associatedDirectory :: Maybe RemoteConfig -> StandardGroup -> Maybe FilePath+associatedDirectory _ SmallArchiveGroup = Just "archive"+associatedDirectory _ FullArchiveGroup = Just "archive"+associatedDirectory (Just c) PublicGroup = Just $+ fromMaybe "public" $ M.lookup "preferreddir" c+associatedDirectory Nothing PublicGroup = Just "public"+associatedDirectory _ _ = Nothing+ {- See doc/preferred_content.mdwn for explanations of these expressions. -} preferredContent :: StandardGroup -> String preferredContent ClientGroup = lastResort $@@ -67,6 +84,7 @@ preferredContent FullArchiveGroup = lastResort notArchived preferredContent SourceGroup = "not (copies=1)" preferredContent ManualGroup = "present and (" ++ preferredContent ClientGroup ++ ")"+preferredContent PublicGroup = "inpreferreddir" preferredContent UnwantedGroup = "exclude=*" notArchived :: String
@@ -143,7 +143,7 @@ -- as the v2 key that it is. readKey1 :: String -> Key readKey1 v- | mixup = fromJust $ file2key $ join ":" $ Prelude.tail bits+ | mixup = fromJust $ file2key $ intercalate ":" $ Prelude.tail bits | otherwise = Key { keyName = n , keyBackendName = b@@ -153,7 +153,7 @@ where bits = split ":" v b = Prelude.head bits- n = join ":" $ drop (if wormy then 3 else 1) bits+ n = intercalate ":" $ drop (if wormy then 3 else 1) bits t = if wormy then Just (Prelude.read (bits !! 1) :: EpochTime) else Nothing@@ -165,7 +165,7 @@ showKey1 :: Key -> String showKey1 Key { keyName = n , keyBackendName = b, keySize = s, keyMtime = t } =- join ":" $ filter (not . null) [b, showifhere t, showifhere s, n]+ intercalate ":" $ filter (not . null) [b, showifhere t, showifhere s, n] where showifhere Nothing = "" showifhere (Just v) = show v
binary file changed (15324 → 15324 bytes)
@@ -32,7 +32,7 @@ when (isAbsolute cmd) $ do path <- getSearchPath let path' = takeDirectory cmd : path- setEnv "PATH" (join [searchPathSeparator] path') True+ setEnv "PATH" (intercalate [searchPathSeparator] path') True {- Checks each of the files in a directory to find open files. - Note that this will find hard links to files elsewhere that are open. -}
@@ -22,7 +22,7 @@ {- rsync requires some weird, non-shell like quoting in - here. A doubled single quote inside the single quoted - string is a single quote. -}- escape s = "'" ++ join "''" (split "'" s) ++ "'"+ escape s = "'" ++ intercalate "''" (split "'" s) ++ "'" {- Runs rsync in server mode to send a file. -} rsyncServerSend :: [CommandParam] -> FilePath -> IO Bool
binary file changed (28136 → 28136 bytes)
@@ -0,0 +1,59 @@+{- Transactional lists+ -+ - Based on DLists, a transactional list can quickly and efficiently+ - have items inserted at either end, or a whole list appended to it.+ -+ - Copyright 2013 Joey Hess <joey@kitenet.net>+ -}++{-# LANGUAGE BangPatterns #-}++module Utility.TList where++import Common++import Control.Concurrent.STM+import qualified Data.DList as D++type TList a = TMVar (D.DList a)++newTList :: STM (TList a)+newTList = newEmptyTMVar++{- Gets the contents of the TList. Blocks when empty.+ - TList is left empty. -}+getTList :: TList a -> STM [a]+getTList tlist = D.toList <$> takeTMVar tlist++{- Takes anything currently in the TList, without blocking.+ - TList is left empty. -}+takeTList :: TList a -> STM [a]+takeTList tlist = maybe [] D.toList <$> tryTakeTMVar tlist++{- Reads anything in the list, without modifying it, or blocking. -}+readTList :: TList a -> STM [a]+readTList tlist = maybe [] D.toList <$> tryReadTMVar tlist++{- Mutates a TList. -}+modifyTList :: TList a -> (D.DList a -> D.DList a) -> STM ()+modifyTList tlist a = do+ dl <- fromMaybe D.empty <$> tryTakeTMVar tlist+ let !dl' = a dl+ {- The TMVar is left empty when the list is empty.+ - Thus attempts to read it automatically block. -}+ unless (emptyDList dl') $+ putTMVar tlist dl'+ where+ emptyDList = D.list True (\_ _ -> False)++consTList :: TList a -> a -> STM ()+consTList tlist v = modifyTList tlist $ \dl -> D.cons v dl++snocTList :: TList a -> a -> STM ()+snocTList tlist v = modifyTList tlist $ \dl -> D.snoc dl v++appendTList :: TList a -> [a] -> STM ()+appendTList tlist l = modifyTList tlist $ \dl -> D.append dl (D.fromList l)++setTList :: TList a -> [a] -> STM ()+setTList tlist l = modifyTList tlist $ const $ D.fromList l
@@ -1,41 +0,0 @@-{- Transactional sets- -- - Copyright 2012, 2013 Joey Hess <joey@kitenet.net>- -}--module Utility.TSet where--import Common--import Control.Concurrent.STM--type TSet = TChan--newTSet :: STM (TSet a)-newTSet = newTChan--{- Gets the contents of the TSet. Blocks until at least one item is- - present. -}-getTSet :: TSet a -> STM [a]-getTSet tset = do- c <- readTChan tset- l <- readTSet tset- return $ c:l--{- Gets anything currently in the TSet, without blocking. -}-readTSet :: TSet a -> STM [a]-readTSet tset = go []- where- go l = do- v <- tryReadTChan tset- case v of- Nothing -> return l- Just c -> go (c:l)--{- Puts items into a TSet. -}-putTSet :: TSet a -> [a] -> STM ()-putTSet tset vs = mapM_ (writeTChan tset) vs--{- Put a single item into a TSet. -}-putTSet1 :: TSet a -> a -> STM ()-putTSet1 tset v = void $ writeTChan tset v
binary file changed (11404 → 11404 bytes)
@@ -40,12 +40,17 @@ localhost :: HostName localhost = "localhost" -{- Command to use to run a web browser. -}-browserCommand :: FilePath+{- Builds a command to use to start or open a web browser showing an url. -}+browserProc :: String -> CreateProcess #ifdef darwin_HOST_OS-browserCommand = "open"+browserProc url = proc "open" [url] #else-browserCommand = "xdg-open"+#ifdef __ANDROID__+browserProc url = proc "am"+ ["start", "-a", "android.intent.action.VIEW", "-d", url]+#else+browserProc url = proc "xdg-open" [url]+#endif #endif {- Binds to a socket on localhost, or possibly a different specified
@@ -10,6 +10,7 @@ #include <fcntl.h> #include <stdlib.h> #include <unistd.h>+#include <sys/types.h> #include <sys/event.h> #include <sys/time.h> #include <errno.h>
@@ -1,3 +1,63 @@+git-annex (4.20130501) unstable; urgency=low++ * sync, assistant: Behavior changes: Sync with remotes that have+ annex-ignore set, so that git remotes on servers without git-annex+ installed can be used to keep clients' git repos in sync.+ * assistant: Work around misfeature in git 1.8.2 that makes+ `git commit --alow-empty -m ""` run an editor.+ * sync: Bug fix, avoid adding to the annex the + dummy symlinks used on crippled filesystems.+ * Add public repository group.+ (And inpreferreddir to preferred content expressions.)+ * webapp: Can now set up Internet Archive repositories.+ * S3: Dropping content from the Internet Archive doesn't work, but+ their API indicates it does. Always refuse to drop from there.+ * Automatically register public urls for files uploaded to the+ Internet Archive.+ * To enable an existing special remote, the new enableremote command+ must be used. The initremote command now is used only to create+ new special remotes.+ * initremote: If two existing remotes have the same name,+ prefer the one with a higher trust level.+ * assistant: Improved XMPP protocol to better support multiple repositories+ using the same XMPP account. Fixes bad behavior when sharing with a friend+ when you or the friend have multiple reposotories on an XMPP account.+ Note that XMPP pairing with your own devices still pairs with all+ repositories using your XMPP account.+ * assistant: Fix bug that could cause incoming pushes to not get+ merged into the local tree. Particularly affected XMPP pushes.+ * webapp: Display some additional information about a repository on+ its edit page.+ * webapp: Install FDO desktop menu file when started in standalone mode.+ * webapp: Don't default to making repository in cwd when started+ from within a directory containing a git-annex file (eg, standalone+ tarball directory).+ * Detect systems that have no user name set in GECOS, and also+ don't have user.name set in git config, and put in a workaround+ so that commits to the git-annex branch (and the assistant)+ will still succeed despite git not liking the system configuration.+ * webapp: When told to add a git repository on a remote server, and+ the repository already exists as a non-bare repository, use it,+ rather than initializing a bare repository in the same directory.+ * direct, indirect: Refuse to do anything when the assistant+ or git-annex watch daemon is running.+ * assistant: When built with git before 1.8.0, use `git remote rm`+ to delete a remote. Newer git uses `git remote remove`.+ * rmurl: New command, removes one of the recorded urls for a file.+ * Detect when the remote is broken like bitbucket is, and exits 0 when+ it fails to run git-annex-shell.+ * assistant: Several improvements to performance and behavior when+ performing bulk adds of a large number of files (tens to hundreds+ of thousands).+ * assistant: Sanitize XMPP presence information logged for debugging.+ * webapp: Now automatically fills in any creds used by an existing remote+ when creating a new remote of the same type. Done for Internet Archive,+ S3, Glacier, and Box.com remotes.+ * Store an annex-uuid file in the bucket when setting up a new S3 remote.+ * Support building with DAV 0.4.++ -- Joey Hess <joeyh@debian.org> Wed, 01 May 2013 01:42:46 -0400+ git-annex (4.20130417) unstable; urgency=low * initremote: Generates encryption keys with high quality entropy.
@@ -17,6 +17,7 @@ libghc-quickcheck2-dev, libghc-monad-control-dev (>= 0.3), libghc-lifted-base-dev,+ libghc-dlist-dev, libghc-uuid-dev, libghc-json-dev, libghc-ifelse-dev,
binary file changed (absent → 34868 bytes)
binary file changed (absent → 12583 bytes)
@@ -1,3 +1,23 @@+## version 4.20130501++This version contains numerous bug fixes, and improvements.++## version 4.20130417++This version contains numerous bug fixes, and improvements.++One bug that was fixed can affect users of gnome-keyring who+have set up remote repositories on ssh servers using the webapp.+The gnome-keyring may load the restricted key that is set up+for that, and make it be used for regular logins to the server;+with the result that you'll get an error message about "git-annex-shell"+when sshing to the server. ++If you experience this problem you can fix it by+moving `.ssh/key.git-annex*` to `.ssh/git-annex/` (creating+that directory first), and edit `.ssh/config` to reflect the new+location of the key. You will also need to restart gnome-keyring.+ ## version 4.20130323, 4.20130405 These versions continue fixing bugs and adding features.
@@ -1,6 +1,18 @@-This is git-annex's bug list. Link bugs to [[bugs/done]] when done.+This is git-annex's bug list. +[[!sidebar content="""+[[!inline feeds=no template=bare pages=sidebar]]++Bugs not show here:++* [[Bugs_affecting_the_assistant|design/assistant/todo]]+* [[Bugs_needing_more_info|moreinfo]]+* [[Closed_bugs|bugs/done]]+"""+]]+ [[!inline pages="./bugs/* and !./bugs/*/* and !./bugs/done and !link(done) +and !./bugs/moreinfo and !link(moreinfo) and !*/Discussion" actions=yes postform=yes show=0 archive=yes]] [[!edittemplate template=templates/bugtemplate match="bugs/*" silent=yes]]
@@ -35,3 +35,7 @@ > So, I'm going to repurpose this bug to track that problem. --[[Joey]] [[!meta title="assistant can fail to make git repository if remote server is lacking GECOS"]]++>> [[done]]; git-annex always checks for missing gecos and enables+>> a workaround. This does mean the server needs to be upgraded in order+>> for the fix to work. --[[Joey]]
@@ -32,3 +32,5 @@ **Please provide any additional information below.** I have observed this problem setting up the repos through the webapp as well, so I don't think it is related to setting up the repos manually. I think the way vim is writing the files seems to be tickling a race condition (both command-line vim and MacVim produce the behavior). I started trying to work around it by switching to emacs to edit those files, and the files haven't disappeared from the edited repo (so far at least).++[[!tag /design/assistant moreinfo]]
@@ -22,3 +22,5 @@ > This affects BSD systems that use Kqueue. It no longer affects OSX, > since we use FSEvents there instead. --[[Joey]] ++[[!tag /design/assistant]]
@@ -22,4 +22,4 @@ Please provide any additional information below. [[!meta title="strange OSX behavior when killed"]]-[[!tag /design/assistant/OSX unreproducible]]+[[!tag /design/assistant/OSX moreinfo]]
@@ -0,0 +1,10 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawkCw26IdxXXPBoLcZsQFslM67OJSJynb1w"+ nickname="Alexander"+ subject="standalone app dmg won't open in OSX 10.8.3"+ date="2013-04-29T18:05:54Z"+ content="""+I downloaded the app build from [http://downloads.kitenet.net/git-annex/OSX/current/10.8.2_Mountain_Lion/](http://downloads.kitenet.net/git-annex/OSX/current/10.8.2_Mountain_Lion/) and unpacked it, but the .dmg won't open. I can run other dmg's successfully. ++Trying to install git-annex via cabal on the same machine led to this issue: [http://git-annex.branchable.com/tips/Building_git-annex_on_Debian_OR___37____164____35____34____164____37____38____34____35___Haskell__33__/#comment-7cc94df1bf9a75a6d03369f3897d6816](http://git-annex.branchable.com/tips/Building_git-annex_on_Debian_OR___37____164____35____34____164____37____38____34____35___Haskell__33__/#comment-7cc94df1bf9a75a6d03369f3897d6816)+"""]]
@@ -76,3 +76,5 @@ Thanks Giovanni++[[!tag moreinfo]]
@@ -0,0 +1,46 @@+**What steps will reproduce the problem?**++On the dashboard, click settings > Delete on the repo you want to remove.+Wait for the dropping to finish.+Start final deletion when the message "The repository "repo" has been emptied, and can now be removed." pops up.++**What is the expected output? What do you see instead?**++The repository should be deleted, but I only see "Internal Server Error: git [Param "remote",Param "remove",Param "repo"] failed".++**What version of git-annex are you using? On what operating system?**++Standalone build, git-annex version 4.20130417-g4bb97d5++**Please provide any additional information below.**++The log shows:++ [2013-04-22 22:17:22 CEST] TransferScanner: The repository "repo" has been emptied, and can now be removed. + error: Unknown subcommand: remove+ usage: git remote [-v | --verbose]+ or: git remote add [-t <branch>] [-m <master>] [-f] [--mirror=<fetch|push>] <name> <url>+ or: git remote rename <old> <new>+ or: git remote rm <name>+ or: git remote set-head <name> (-a | -d | <branch>)+ or: git remote [-v | --verbose] show [-n] <name>+ or: git remote prune [-n | --dry-run] <name>+ or: git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]+ or: git remote set-branches [--add] <name> <branch>...+ or: git remote set-url <name> <newurl> [<oldurl>]+ or: git remote set-url --add <name> <newurl>+ or: git remote set-url --delete <name> <url>++ -v, --verbose be verbose; must be placed before a subcommand++++> Seems that `git remote remove` is new as of git 1.8.0 or so.+> Older gits only support `git remote rm`. Which newer gits+> support as well. but it seems to be in the process+> of being deprecated so I'd rather not use it.+> +> So, I've made the version of git it's+> built for determine which subcommand it uses. [[done]] --[[Joey]]+> +> (You can run `git remote rm repo` by hand to clean up from this BTW.)
@@ -0,0 +1,45 @@+What steps will reproduce the problem?+My annex dir has 23459 files and uses 749MB disk space.+Just create a repository put this dir inside, and git-annex will crash.++What is the expected output? What do you see instead?+I expect git-annex handles large number of files, and does not watch every single file of it.++What version of git-annex are you using? On what operating system?+I'm using git-annex linux build, version 2013.04.17.++Please provide any additional information below.++ [2013-04-17 23:52:35 CEST] Transferrer: Downloaded pappas_hu..di_44.jpg+ git-annex: runInteractiveProcess: pipe: Too many open files+ Committer crashed: lsof: createProcess: resource exhausted (Too many open files)+ [2013-04-17 23:53:52 CEST] Committer: warning Committer crashed: lsof: createProcess: resource exhausted (Too many open files)+ git-annex: runInteractiveProcess: pipe: Too many open files+ git: createProcess: resource exhausted (Too many open files)+ DaemonStatus crashed: /home/user/Desktop/down/annex_test/.git/annex/daemon.status.tmp21215: openFile: resource exhausted (Too many open files)+ [2013-04-17 23:57:24 CEST] DaemonStatus: warning DaemonStatus crashed: /home/user/Desktop/down/annex_test/.git/annex/daemon.status.tmp21215: openFile: resource exhausted (Too many open files)+ git-annex: runInteractiveProcess: pipe: Too many open files+ git: createProcess: resource exhausted (Too many open files)+ git-annex: runInteractiveProcess: pipe: Too many open files+ NetWatcherFallback crashed: git: createProcess: resource exhausted (Too many open files)+ [2013-04-18 00:27:17 CEST] NetWatcherFallback: warning NetWatcherFallback crashed: git: createProcess: resource exhausted (Too many open files)+ git-annex: runInteractiveProcess: pipe: Too many open files+ git-annex: git: createProcess: resource exhausted (Too many open files)+ git-annex: accept: resource exhausted (Too many open files)++Instead of raising system's limit (which is a neverending story), can we make git-annex only watch a directory and not every file of it?++Or could the user specify some directory which he knows it is rarely change, to not be watched only check it once a day?++The best would be if git annex could automatically adapt itself.+Ie. it watches eg. 200 files, and if some of it does not change for three days, then it drops from the watching basket, and those who changed (noticed while sanity checked) it adds to the basket.++I don't really want to raise the ulimit, because my ultimate goal is to have git-annex on multiple raspberry pi with external harddrive (one at my home, one at my mom's home, one at my friends home, etc, etc). And raspberry is fairly low on resource.++I'm interested in your thoughts.++Best, + Laszlo++[[!tag /design/assistant moreinfo]]+[[!meta title="assistant can try to add too many files at once in batch add mode"]]
@@ -0,0 +1,45 @@+What steps will reproduce the problem?++mkdir annex_stress; cd annex_stress, +then execute the following script:++ #! /bin/sh+ + # creating a directory, in which we dump all the files.+ mkdir probes; cd probes+ + for i in `seq -w 1 25769`; do+ mkdir probe$i+ echo "This is an important file, which saved also in backup ('back') directory too.\n Content changes: $i" > probe$i/probe$i.txt+ echo "This is just an identical content file. Saved in each subdir." > probe$i/defaults.txt+ echo "This is a variable ($i) content file, which is not backed up in 'back' directory." > probe$i/probe-nb$i.txt+ mkdir probe$i/back+ cp probe$i/probe$i.txt probe$i/back/probe$i.txt+ done+++It creates about 25000 directory and 3 files in each, two of them are identical.++What is the expected output? What do you see instead?++I expect git annex could import the directory within 12 hours. +Yet, it just crashes the gui (starting webapp, uses the cpu 100% and it does not finish after 28hours.)+++What version of git-annex are you using? On what operating system?++version 2013.04.17++Please provide any additional information below.++I do hope git-annex can be fixed to handle large number of files.+This stress test models well enough my own directory structure, +relatively high number of files relatively low disk space usage +(my own directory structure: 750MB, this test creates 605MB).+++Best, + Laszlo++[[!meta title="assistant Stress test"]]+[[!tag /design/assistant]]
@@ -0,0 +1,17 @@+[[!comment format=mdwn+ username="http://joeyh.name/"+ nickname="joey"+ subject="comment 1"+ date="2013-04-23T20:00:31Z"+ content="""+Is this related or unrelated to the bug you filed at [[Resource_exhausted]]?++I tried this test, and noticed that it was taking the assistant rather a long time to get to the 10 thousand file threshhold where it makes a batch commit. A small change to a better data structure for its queue reduced that time from probably 10 minutes to 2.5. ++I was unable to reproduce any problem with the webapp. Please provide lots of details to back up \"it just crashes the GUI\".++The main problem with this directory tree is that it has more directories than inotify can watch, in the default configuration. +So after it adds the first 8192 directories, it begins failing to watch any more, and printing a message about you needing to increase the inotify limits for each additional directory. I don't think that 51 thousand directories is a particularly realistic amount for any real-world usage of git-annex. (It will also break file manager, dropbox, etc, which all use inotify in the same way.)++The other main time sink is that git-annex needs to run `git hash-object` once per file to stage its symlink. That is a lot of processes to run, and perhaps it could be sped up by using `git fast-import`.+"""]]
@@ -0,0 +1,47 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawm5iosFbL2By7UFeViqkc6v-hoAtqILeDA"+ nickname="Laszlo"+ subject="comment 2"+ date="2013-04-24T06:30:16Z"+ content="""+Hi,++First of all thank you for your time looking into my bug. I try to research more from my side.++The 'Resource exhausted' bugreport +(which lost its title, and could not click on it to add this testcase as a comment)+was tested on real data, my own working directory (a copy of it).+This bugreport is tested on the output of this small shell script.++None of them succeeded to import, and I quickly assumed it is the exact same.++So I will test again, raising the ulimit to 81920, and report.++ The main problem with this directory tree is that it has more directories than inotify can watch, in the default configuration++I would be perfectly fine if I could configure git-annex to sync those directory only once a month or once a week+(ie. check for update once a week). So no need to watch it real time, those are my archived work files.++ I don't think that 51 thousand directories is a particularly realistic amount for any real-world usage of git-annex.++Well, it is not 25000 dir in a single a folder, but rather something like this:++ work_done/2009/workname/back9/back8/back7/back6/back5++Where each 'backX' contains a whole backup the work until it. +So the directory structure is a bit more deep, and no 25000 subdirectory in a single dir. +But the overall numbers are right.++If I could somehow mark this **work_done** dir to not sync real time (or work_done/2008,work_done/2009,work_done/2010,work_done/2011,work_done/2012 subdir in them), +then my whole issue would vanish.++I only want to use git-annex to have a backup of this directory. +In case of laptop theft, or misfunction I could have a backup. +I dont need live sync anywhere, I have directories which I know I will not touch for months.++Best,+ Laszlo++++"""]]
@@ -0,0 +1,20 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawm5iosFbL2By7UFeViqkc6v-hoAtqILeDA"+ nickname="Laszlo"+ subject="comment 3"+ date="2013-04-24T09:10:20Z"+ content="""+ (It will also break file manager, dropbox, etc, which all use inotify in the same way.)++I beg to differ: with dropbox I handle my scrapbook(1) folder, +which means 130 thousand files for over 2 years now without problem between three computers.++ ~/Dropbox/scrapbook$ ls -R -1 |wc -l+ 130263++Don't get me wrong. I'm not complaining, I only give you a completely unrelated usecase, +which requires also high number of files handling. And in that case the 81 thousand ulimit would not help either.++(1): https://addons.mozilla.org/hu/firefox/addon/scrapbook/++"""]]
@@ -0,0 +1,11 @@+[[!comment format=mdwn+ username="http://joeyh.name/"+ nickname="joey"+ subject="comment 4"+ date="2013-04-24T15:05:33Z"+ content="""+You're confusing number of files (inotify doesn't care) with number of directories (inotify does care).++Dropbox is on record about being limited in the number of directories it can watch without adjusting the inotify limit. +<https://www.dropbox.com/help/145>+"""]]
@@ -0,0 +1,15 @@+[[!comment format=mdwn+ username="http://joeyh.name/"+ nickname="joey"+ subject="comment 5"+ date="2013-04-24T15:30:04Z"+ content="""+I found a bug in the webapp thanks to this stress test. When inotify goes over limit, it displays a message about how to fix it..+But it displays that message over and over for each file. The result is a constantly updating very large web page. ++Unless you tell me differently, I'm going to assume that's what the GUI crash you referred to was, since it can make a web browser very slow.++I've fixed this problem. Now when it goes over limit, the webapp will just display this:++[[/assistant/inotify_max_limit_alert.png]]+"""]]
@@ -0,0 +1,14 @@+[[!comment format=mdwn+ username="http://joeyh.name/"+ nickname="joey"+ subject="comment 6"+ date="2013-04-24T17:26:39Z"+ content="""+I put in a further change to reduce the number of alerts shown in the webapp when bulk adding files. This probably quadrupled the speed or more, even when the webapp was not running, as updating an alert every time a file was added was a lot of unnecessary work.++After these changes, it adds the first 10 thousand files in 35 minutes, on my five year old netbook. It should scale linear+(aside from git's own scalability issues with a lot of files, which I don't think are very bad under 1 million files),+so adding all 100 thousand files should take 6 hours or so.++I'm interested to see what results you get, compared with before..+"""]]
@@ -0,0 +1,8 @@+[[!comment format=mdwn+ username="http://joeyh.name/"+ nickname="joey"+ subject="comment 7"+ date="2013-04-24T21:02:55Z"+ content="""+A few more changes got the rate down to 21 minutes per 10 thousand files. Estimate 3.5 hours for all.+"""]]
@@ -48,4 +48,4 @@ Please provide any additional information below. -[[!tag /design/assistant unreproducible]]+[[!tag /design/assistant moreinfo]]
@@ -0,0 +1,5 @@+I'd like to be able to restore my data from S3/Glacier following a catastrophic loss of information.++As I understand it, git-annex doesn't solve this problem for me because it only stores file *contents* in S3/Glacier. A restore-from-nothing requires both the file contents and also the file names and metadata, which git-annex doesn't store in S3.++I'm still feeling my way around git-annex, but I think it will probably be sufficient for my purposes to set up a cron job to push my annex to github. But I think it would be helpful if git-annex could take care of this automatically.
@@ -0,0 +1,54 @@+### Please describe the problem.++I seem to be unable to switch back and forth between git annex direct and git annex indirect mode in one of my repositories. I can in others just fine.++### What steps will reproduce the problem?++In the broken repository I can do:++ cwebber@earlgrey:~/gfx-proj/mediagoblin_vid$ git annex direct+ commit + add audio/part2.aup (checksum...) ok+ ok+ add images/campaign.png (checksum...) ok+ ok+ add images/transifex.png (checksum...) ok+ ok+ add script-lines.txt (checksum...) ok+ ok+ add vid_pitch.blend (checksum...) ok+ ok+ (Recording state in git...)+ [master 9f13dc0] commit before switching to direct mode+ 1 file changed, 145 insertions(+), 1 deletion(-)+ rewrite audio/part2.aup (100%)+ mode change 120000 => 100644+ ok+ direct gavroche-vid-shot.blend + git-annex: /home/cwebber/gfx-proj/mediagoblin_vid/.git/annex/objects/3M/mx/SHA256E-s2935980--3a1c838333a4a0ee1eaa837c3f08a910d3f29fc60baf41affd936fbefe11111f/SHA256E-s2935980--3a1c838333a4a0ee1eaa837c3f08a910d3f29fc60baf41affd936fbefe11111f: rename: permission denied (Permission denied)+ failed+ git-annex: direct: 1 failed++looking at the files:++ cwebber@earlgrey:~/gfx-proj/mediagoblin_vid$ ls -l gavroche-vid-shot.blend+ lrwxrwxrwx 1 cwebber cwebber 190 Apr 28 18:27 gavroche-vid-shot.blend -> .git/annex/objects/3M/mx/SHA256E-s2935980--3a1c838333a4a0ee1eaa837c3f08a910d3f29fc60baf41affd936fbefe11111f/SHA256E-s2935980--3a1c838333a4a0ee1eaa837c3f08a910d3f29fc60baf41affd936fbefe11111f+ cwebber@earlgrey:~/gfx-proj/mediagoblin_vid$ ls -l .git/annex/objects/3M/mx/SHA256E-s2935980--3a1c838333a4a0ee1eaa837c3f08a910d3f29fc60baf41affd936fbefe11111f/SHA256E-s2935980--3a1c838333a4a0ee1eaa837c3f08a910d3f29fc60baf41affd936fbefe11111f+ -rw-r--r-- 1 cwebber cwebber 2935980 Apr 28 18:27 .git/annex/objects/3M/mx/SHA256E-s2935980--3a1c838333a4a0ee1eaa837c3f08a910d3f29fc60baf41affd936fbefe11111f/SHA256E-s2935980--3a1c838333a4a0ee1eaa837c3f08a910d3f29fc60baf41affd936fbefe11111f+ cwebber@earlgrey:~/gfx-proj/mediagoblin_vid$++... it looks like these permissions should be fine!++Some notable things:++* I believe Blender wrote directly to a file that was in "locked" somehow, despite it being in that state. It may have actually followed the symlink and overwritten that file, I'm not sure.+* However, the file that git-annex is now reporting with "permission denied" is not the one it did previously... I did git checkout -- on all the files, switched them over, and it's a different set of broken things now!+* It's actually easy enough to fix... in fact, I did fix it! I just did a fresh clone of the git repository and a git annex get and everything is fine now. However, it seemed like possibly a bug that might hit other people, hence my reporting it.++### What version of git-annex are you using? On what operating system?++git annex version 4.20130417 on debian wheezy++### Please provide any additional information below.++
@@ -0,0 +1,39 @@+What steps will reproduce the problem?++Install 4.20130417 as packaged in Debian unstable.+Using "git annex webapp", setup on first machine adding repository ~/annex. Add a Box.com repository in directory "annex", encrypted. Add a jabber account (apparently successful).+Add test file to ~/annex. Login via website to box.com, notice that the "annex" directory is created and contains encrypted file.+On second (remote machine), follow the same steps (add repository in ~/annex, Add a Box.com repository in directory "annex", encrypted, Add same jabber account).++What is the expected output? What do you see instead?++Expected the file to appear in the second machine's ~/annex. The webapp indicates: Synced with box.com+Log file says:++[2013-04-23 06:50:16 EDT] main: starting assistant version 4.20130417+(scanning...) [2013-04-23 06:50:16 EDT] Watcher: Performing startup scan+(started...) +(encryption setup shared cipher) (testing WebDAV server...)+(gpg) [2013-04-23 06:50:50 EDT] main: Syncing with box.com +[2013-04-23 06:50:50 EDT] main: Share with friends, and keep your devices in sync across the cloud. +[2013-04-23 06:51:03 EDT] main: Share with friends, and keep your devices in sync across the cloud. +warning: Not updating non-default fetch respec+ + Please update the configuration manually if necessary.+Initializing nautilus-gdu extension+Shutting down nautilus-gdu extension+git-annex: Daemon is already running.++What version of git-annex are you using? On what operating system?++4.20130417, debian testing with apt-pinning to unstable on both systems.++Please provide any additional information below.++When editing the Box.com repository, the option to select a directory no longer appears (and the configuration doesn't show the one selected at creation).+There's no indication if the jabber communication is working successfully. It could be that signalling isn't working for some reason, but the user has no information to determine that.+It would be helpful if there were some indication in the Dashboard as to the number of files/directories/objects that git annex believes exists in each location. It could be that it's not accessing the Box.com server successfully, but again, this is difficult to determine.++It's great to see that git annex might make a box.com account useful for automatic upload and sync... looking forward to getting it to work on both sides! Thanks for making this!++[[!tag /design/assistant moreinfo]]
@@ -19,3 +19,6 @@ However, I think it's: * Unfortunate, as fitting Calibre together with git-annex seems like a neat idea. * Useful to make sure that this kind of "doesn't play well together" condition is documented, even if only as a bug report.++> [[done]]; the assistant uses direct mode by default now to avoid+> this kind of thing. --[[Joey]]
@@ -50,3 +50,8 @@ **Edit: Doing a git annex sync in ~/Indirect results in the continuing behaviour to be correct, so there's some issue telling Direct that Indirect is no longer in direct-mode?** This appears to fix it, but I guess shouldn't be necessary.++> AFAICS, the entire problem is that the assistant does not notice when the+> repository it's running in is changed from direct to indirect mode. Since this+> has also been reported to cause problems with the assistant, I have added+> a check to prevent it from being done. [[done]] --[[Joey]]
@@ -22,3 +22,25 @@ Please provide any additional information below. The assistant in this case is being used as nothing more than a way for me to see which files have been added (--verbose, --foreground and --debug with 'watch' outputs nothing..). No remotes or anything like that.++> I have made the assistant re-queue any file that it fails to add,+> so it will retry it later. Typically within a few seconds. [[done]]+> +> I have only been able to think of one scenario in which this could+> happen. It's pretty unusual:+> +> * Something writes to a file, and closes it.+> * Assistant sees file has no writers, and locks it down in preparation+> to add it.+> * Something then re-opens the file to write to it some more.+> Note that it would seem to need to bypass permissions that prevent+> the file from being written to in order to do this. It makes a change+> to the file.+> * Assistant is checksumming file, reaches end, and detects it has been+> tampered with and gives up.+> +> I would still like more information about circumstances that+> cause this to happen, because while a possible scenario, the +> above is too weird to believe anyone could run into it.+> +> --[[Joey]]
@@ -1,5 +1,3 @@-[[!meta title="on Arch Linux, git commit -m tries to run an editor on the commit message"]]- What steps will reproduce the problem? Use git-annex and add file to repo.@@ -25,3 +23,5 @@ Please provide any additional information below. I'm not sure if the git interface has changed, but I do see that --allow-empty-message does still exist. If I run the git commit command (with a '' after the -m), it does indeed start up vim for me. Would we benefit from just making a custom commit message ("Commit from date YYYYMMDDTHHMMSSZ")?++> [[fixed|done]] --[[Joey]]
@@ -0,0 +1,16 @@+**What steps will reproduce the problem?**<br>+Delete the last repository in the current repository (where it says: warning type in "yes please do as i say").+Then try running git annex webapp+<br><br>+**What is the expected output? What do you see instead?**<br>+Webapp starts. git-annex: Not in a git repository.+<br><br>+**What version of git-annex are you using? On what operating system?**<br>+4.20130417. Debian wheezy/testing+<br><br>+**Please provide any additional information below.**<br>+See also+[[bugs/git-annex:_Not_in_a_git_repository._/]]+where the same error message occurs++[[!tag /design/assistant moreinfo]]
@@ -0,0 +1,32 @@+### Please describe the problem.++I setup 3 repositories on my laptop and 3 on my server using the webapp, see the following scheme:++Laptop <- sync with -> Server++ /home/fabian/Dokumente (Client) <-> /mnt/raid/Dokumente (Full-Backup)+ /home/fabian/Bilder (Client) <-> /mnt/raid/Bilder (Full-Backup)+ /mnt/data-common/Audio (Manual) <-> /mnt/raid/Audio (Full-Backup)++As you can see, the Audio folder is in manual mode on the laptop, so it does not get any files automatically.+If I now want to get a folder with 'git-annex get' I get the following error:++ fabian@fabian-thinkpad /mnt/data-common/Audio $ git-annex get Musik+ get Musik/+⁄-/2003 - You Are Here (Bonus Disc)/01 - I've Been Lost.ogg (from eifel.fritz.box__mnt_raid_Audio...) + requested key is not present+ rsync: connection unexpectedly closed (0 bytes received so far) [Receiver]+ rsync error: error in rsync protocol data stream (code 12) at io.c(605) [Receiver=3.0.9]++ Unable to access these remotes: eifel.fritz.box__mnt_raid_Audio+ + Try making some of these repositories available:+ efe13d8c-2b02-455f-9874-b7043caa332f -- eifel.fritz.box__mnt_raid_Audio (fabian@eifel:/mnt/raid/Audio)+ failed++### What steps will reproduce the problem?++I do not really know the minimal setup to reproduce this problem.++### What version of git-annex are you using? On what operating system?++git-annex 4.20130417 on Gentoo Linux using Ebuilds from Haskell overlay
@@ -0,0 +1,39 @@+**What steps will reproduce the problem?**++Run *git annex assistant --autostart*++ git-annex autostart in /home/tobru/annex/repo1+ failed+ git-annex autostart in /home/tobru/annex/repo2+ failed+ git-annex autostart in /home/tobru/annex/repo3+ failed+ git-annex autostart in /home/tobru/annex/repo4+ failed+ git-annex autostart in /home/tobru/annex/repo5+ failed++Running *git annex assistant* in each directory starts the assistant without errors.++What could cause autostart to fail? Is there any log? Or a --debug parameter?+++**What is the expected output? What do you see instead?**++The assistant should start on all known repositories++**What version of git-annex are you using? On what operating system?**++4.20130417-g4bb97d5 on Ubuntu++**Please provide any additional information below.**++The ~/.config/git-annex/autostart file looks like this:++ /home/tobru/annex/repo1+ /home/tobru/annex/repo2+ /home/tobru/annex/repo3+ /home/tobru/annex/repo4+ /home/tobru/annex/repo5++> Closing, seems local misconfiguration. --[[Joey]] [[done]]
@@ -60,3 +60,8 @@ Alright, it didn't fail with an error, that's very strange. What is going on here? [[!meta title="v1 file is ignored"]]++> I don't think I want to make git-annex deal with v1 files, and+> I doubt there are many repos left using them. This seems to be a case+> of an upgrade not being done, for whatever reason. Closing [[done]]+> --[[Joey]]
@@ -0,0 +1,29 @@+What steps will reproduce the problem?++As described in+http://git-annex.branchable.com/backends/#comment-3c1cd45d2a015b4fc412dd813293ad7d+, sha512 is faster. On my 64-bit system, the speed difference is about+1.5times.++What is the expected output? What do you see instead?+++What version of git-annex are you using? On what operating system?+++Please provide any additional information below.++> You are free to change the default in your own annexes. This is very easy+> to do: `echo '* annex.backend=SHA512E' > .gitattributes`+> +> I don't anticipate moving to SHA512, because+> +> 1. It makes `ls -l` really ugly. Each symlink takes like 4 lines+> on an 80 column terminal.+> 2. There are better hashes coming. Particularly SHA3. That should be+> faster and/or more secure. And without adding so much length to the+> hash.+> +> --[[Joey]]++[[done]]
@@ -0,0 +1,6 @@+If your bug report is listed here, it has been flagged as needing more+information. Please respond to the bug and provide the requested+information.++[[!inline pages="./* and link(./moreinfo) and !link(./done) and !*/Discussion" sort=mtime show=10+archive=yes]]
@@ -31,4 +31,4 @@ 3.20130102 on Arch Linux x64 -[!tag /design/assistant]]+[!tag /design/assistant moreinfo]]
@@ -49,3 +49,8 @@ I use a symlink to the repository to change into it. +> Closing this bug as user error. If the `git-annex` branch+> gets merged into master by the user, then that adds all its log files+> to master, and so they're visible as regular files. Solution: Don't do+> that, or if you do that, use `git log --stat` to find the commit that+> adds all those files, and `git revery` the commit. [[done]] --[[Joey]]
@@ -0,0 +1,107 @@+The way [[XMPP pairing|design/assistant/XMPP]] currently works, each+separate repository needs to use a different XMPP account. If two+repositories use the same XMPP account, then they will be combined together+when XMPP pairing takes place.++There are two different UIs for XMPP pairing. While the same protocol+is running behind the scenes, these UIs should be considered separately.++## Share with your other devices++Here, I think it makes sense to require the user to use the same XMPP+account on all their devices (otherwise it's pairing with a friend), and+automatically combine repositories of devices that use the same XMPP+account.++The UI is pretty clear about this:++ If you have multiple devices, all running git-annex, and using #+ your Jabber account #{account}, you can configure them to share #+ your files between themselves.++Doing it this way avoids needing to confirm pair requests coming from the same+XMPP account. Which means that, for example, you can have a device at home,+and one at work, and pair them by simply initiating a pair request from one+to the other. You don't have to travel between home and work to confirm +the request.++(Also, when you have a lot of devices, this avoids a combinatorial explosion+of pair request confirmations.)++The only problem with this is that users who want multiple repositories+need to find multiple XMPP accounts. However, I'm inclined to think this is+a reasonable requirement.++## Share with a friend++Suppose that Alice wants to share with Bob. Bob is using the same XMPP+account for two separate repositories (B1 and B2), that are not+themselves paired.++When Alice chooses to share with Bob, a XMPP pair request is sent.+Both of Bob's repositories see it, and both ask him to confirm.+Bob can choose to only confirm the request in B1, and +not in B2. This should work ok.++* The UI for this only says "Pair request received from #{name}",+ it does not indicate which repository of Alice's is being paired+ with. This could be improved. If Alice has two repositories as well,+ she and Bob will want to coordinate pairing the right ones together.+ Could be fixed by just displaying the description of Alice's repository+ to Bob.++-----++Now, suppose that Alice makes a second, distinct repository (A2),+and chooses to share it with Bob (intending to share with B2). This+sends an XMPP pair request to both of Bob's repositories.++B1 has already paired with Alice, so it assumes this+new pair request is from a different device belonging to Alice, and it+automatically ACKs the pair request. ++The result is that Alice's new A2 repository combines with B1,+which is already combined with A1. Effectively combining all of A1, A2, B1,+and B2, unexpectedly. **This is a bug**.++Some possible fixes:++1. Stop auto-accepting pair requests+ from friends we're already paired with. Require another confirmation.+2. Or, only auto-accept pair requests from friends we're already paired with+ when they come from a repository whose UUID we already know. This+ enhancment to fix #1 makes it easier to build more robust networks of+ repositories. **done**++Hmm, I don't think those fixes are sufficient. Suppose they're in place.+Then when Alice shares A2 with Bob, both his repositories will ask him to+confirm the pair request. He confirms in B2, and pairing proceeds.++But, XMPP git push is broadcast to all clients of an XMPP account.+So when B2 sends a push, A1 sees it, and happily merges away. The+repositories still combine!++So, we need another fix:++* Send UUID in XMPP git push protocol messages. Only respond to git push+ messages from a known UUID, and ignore all others. (XMPP pairing+ already sends the UUID, so it will be known.) **done**++----++* Alternatively, we could say that the problem is that Bob has two + distinct repositories using the same XMPP account, and try to prevent+ him from doing that in the first place. + + One way to do this would be, when configuring the+ XMPP account, scan for other repositories using the same account, and+ don't let it be used unless the user confirms they want to pair them.+ But, this doesn't seem viable because if the other repository is on+ another device, which is turned off, this check wouldn't see it.++ Or there could be a warning about account reuse. Doesn't seem likely to+ be effective.++-----++> [[done]]. I've put in the fixes around pairing with friends. --[[Joey]]
@@ -18,7 +18,8 @@ We are, approximately, here: -* Months 10-11: more user-driven features and polishing+* Month 10: bugfixing, Android webapp+* Month 11: more user-driven features and polishing * Month 12: "Windows purgatory" [[Windows]] ## porting
@@ -18,15 +18,8 @@ The app should be aware of network status, and avoid expensive data transfers when not on wifi. This may need to be configurable. -## Template Haskell for android?--Best lead I have on getting cross compilation of TH working is that GHCJS-does it, and that it involves compiling each file twice, once natively for-TH and once for cross.- ## TODO -* webapp * autostart any configured assistants. Best on boot, but may need to only do it when app is opened for the first time. * Don't make app initially open terminal, but go to a page that@@ -39,9 +32,6 @@ allow git to link to it. * getEnvironment is broken on Android <https://github.com/neurocyte/ghc-android/issues/7> and a few places use it.-* Enable WebDAV support. Currently needs template haskell (could be avoided- by changing the DAV library to not use it), and also networking support,- which seems broken in current ghc-android. * XMPP support. I got all haskell libraries installed, but it fails to find several C libraries at link time. * Get local pairing to work. network-multicast and network-info don't
@@ -0,0 +1,37 @@+Late last night, I successfully built the full webapp for Android!++That was with several manual modifications to the generated code, which I+still need to automate. And I need to set up the autobuilder properly+still. And I need to find a way to make the webapp open Android's web browser+to URL. So it'll be a while yet until a package is available+to try. But what a milestone!++The point I was stuck on all day yesterday was generated code that+looked like this:++[[!format haskell """+(toHtml+ (\ u_a2ehE -> urender_a2ehD u_a2ehE []+ (CloseAlert aid)))));+"""]]++That just couldn't type check at all. Most puzzling. My best guess is that+`u_a2ehE` is the dictionary GHC passes internally to make a typeclass work,+which somehow leaked out and became visible. Although+I can't rule out that I may have messed something up in my build environment.+The EvilSplicer has a hack in it that finds such code and converts it to+something like this:++[[!format haskell """+(toHtml+ (flip urender_a2ehD []+ (CloseAlert aid)))));+"""]]++I wrote some more about the process of the Android port in my personal blog:+[Template Haskell on impossible architectures](http://joeyh.name/blog/entry/Template_Haskell_on_impossible_architectures/)++----++Release day today. The OSX builds are both not available yet for this+release, hopefully will come out soon.
@@ -0,0 +1,14 @@+Finished the last EvilSplicer tweak and other fixes to make the +Android webapp build without any hand-holding.++Currently setting up the Android autobuilder to include the webapp in its+builds. To make this work I had to set up a new chroot with all the right+stuff installed.++Investigated how to make the Android webapp open a web browser when run.+As far as I can tell (without access to an Android device right now), +`am start -a android.intent.action.VIEW -d http://localhost/etc` should do+it.++Seems that git 1.8.2 broke the assistant. I've put in a fix but have not+yet tested it.
@@ -0,0 +1,4 @@+Got WebDAV enabled in the Android build. Had to deal with some system calls+not available in Android's libc.++New poll: [[polls/Android_default_directory]]
@@ -0,0 +1,21 @@+Today was not a work day for me, but I did get a chance to install+git-annex in real life while visiting. Was happy to download the standalone+Linux tarball and see that it could be unpacked, and git-annex webapp+started just by clicking around in the GUI. And in very short order got it+set up.++I was especially pleased to see my laptop noticed this new repository+had appeared on the network via XMPP push, and started immediately+uploading files to my rsync.net transfer repository so the new+repository could get them.++Did notice that the standalone tarball neglected to install a+FDO menu file. Fixed that, and some other minor issues I noticed.+++I also got a brief chance to try the Android webapp. It fails to start;+apparently `getaddrinfo` doesn't like the flags passed to it and is+failing. As failure modes go, this isn't at all bad. I can certainly work+around it with some hardcoded port numbers, but I want to fix it the right+way. Have ordered a replacement battery for my dead phone so I can use it+for Android testing.
@@ -0,0 +1,6 @@+Ported all the C libraries needed for XMPP to Android. (gnutls,+libgcrypt, libgpg-error, nettle, xml2, gsasl, etc). Finally+got it all to link. What a pain.++Bonus: Local pairing support builds for Android now, seems recent changes to+the network library for WebDAV also fixed it.
@@ -0,0 +1,15 @@+Got the OSX autobuilder back running, and finally got a OSX build up for+the 4.20130417 release. Also fixed the OSX app build machinery to handle+rpath.++Made the assistant (and `git annex sync`) sync with git remotes that have+`annex-ignore` set. So, `annex-ignore` is only used to prevent using+the annex of a remote, not syncing with it. The benefit of this change+is that it'll make the assistant sync the local git repository with+a git remote that is on a server that does not have git-annex installed.+It can even sync to github.++Worked around more breakage on misconfigured systems that don't have GECOS+information.++... And other bug fixes and bug triage.
@@ -0,0 +1,18 @@+There seem to be a steady state of enough bug reports coming in that I can+work on them whenever I'm not working on anything else. As I did all day+today.++This doesn't bother me if the bug reports are of real bugs that I can+reproduce and fix, but I'm spending a lot of time currently following up to+messages and asking simple questions like "what version number" and "can I+please see the whole log file". And just trying to guess what a vague+problem report means and read people's minds to get to a definite bug+with a test case that I can then fix.++I've noticed the overall quality of bug reports nosedive over the past+several months. My guess is this means that git-annex has found a less+technical audience. I need to find something to do about this.++With that whining out of the way ...+I fixed a pretty ugly bug on FAT/Android today, and+I am 100% caught up on messages right now!
@@ -0,0 +1,16 @@+Working on assistant's performance when it has to add a whole lot of files+(10k to 100k). ++Improved behavior in several ways, including fixing display+of the alert in the webapp when the default inotify limit of 8192+directories is exceeded.++Created a new TList data type, a transactional DList. Much nicer+implementation than the TChan based thing it was using to keep track of the+changes, although it only improved runtime and memory usage a little bit.+The way that this is internally storing a function in STM and modifying+that function to add items to the list is way cool.++Other tuning seems to have decreased the time it would take to import 100k+files from somewhere in the range of a full day (too long to wait to see),+to around 3.5 hours. I don't know if that's good, but it's certainly better.
@@ -0,0 +1,28 @@+Very productive & long day today, spent adding a new feature to the +webapp: Internet Archive support!++[[!img /assistant/iaitem.png]]++git-annex already supported using archive.org via its S3 special remotes,+so this is just a nice UI around that.++How does it decide which files to publish on archive.org? Well,+the item has a unique name, which is based on the description+field. Any files located in a directory with that name will be uploaded+to that item. (This is done via a new preferred content expression I added.)++So, you can have one repository with multiple IA items attached, and+sort files between them however you like.+I plan to make a screencast eventually demoing that. ++Another interesting use case, once the Android webapp is done, would be add+a repository on the DCIM directory, set the archive.org repository to+prefer all content, and *bam*, you have a phone or tablet that+auto-publishes and archives every picture it takes.++Another nice little feature added today is that whenever a file is uploaded+to the Internet Archive, its public url is automatically recorded, same+as if you'd ran `git annex addurl`. So any users who can clone your+repository can download the files from archive.org, without needing any+login or password info. This makes the Internet Archive a nice way to+publish the large files associated with a public git repository.
@@ -0,0 +1,7 @@+Quiet day. Only did minor things, like adding webapp UI for changing the+directory used by Internet Archive remotes, and splitting out an+`enableremote` command from `initremote`.++My Android development environment is set up and ready to go on my Motorola+Droid. The current Android build of git-annex fails to link at run time, so+my work is cut out for me. Probably broke something while enabling XMPP?
@@ -0,0 +1,23 @@+Turns out my old Droid has such an old version of Android (2.2) that+it doesn't work with any binaries produced by my haskell cross-compiler. I+think it's using a symbol not in its version of libc. Since upgrading this+particular phone is a ugly process and the hardware is dying anyway (bad+USB power connecter), I have given up on using it, and ordered an Android+tablet instead to use for testing. Until that arrives, no Android. Bah.+Wanted to get the Android app working in April.++Instead, today I worked on making the webapp require less redundant+password entry when adding multiple repositories using the same cloud+provider. This is especially needed for the Internet Archive, since users+will often want to have quite a few repositories, for different IA items.+Implemented it for box.com, and Amazon too.++Francois Marier has built an Ubuntu PPA for git-annex, containing the+current version, with the assistant and webapp. It's targeted at Precise,+but I think will probably also work with newer releases.+<https://launchpad.net/~fmarier/+archive/ppa>++Probably while I'm waiting to work on Android again, I will try to+improve the situation with using a single XMPP account for multiple+repositories. Spent a while today thinking through ways to improve the+design, and have some ideas.
@@ -0,0 +1,34 @@+Took 2 days in a row off, because I noticed I have forgotten to do that+since February, or possibly earlier, not counting trips. Whoops!++Also, I was feeling overwhelmed with the complexity of fixing XMPP to not+be buggy when there are multiple separate repos using the same XMPP+account. Let my subconcious work on that, and last night it served up the+solution, in detail. Built it today.++It's only a partial solution, really. If you want to use the same XMPP+account for multiple separate repositories, you cannot use the "Share with+your other devices" option to pair your devices. That's because XMPP+pairing assumes all your devices are using the same XMPP account, in order+to avoid needing to confirm on every device each time you add a new device.+The UI is clear about that, and it avoids complexity, so I'm ok with that.++But, if you want to instead use "Share with a friend", you now can use the+same XMPP account for as many separate repositories as you like. The+assistant now ignores pushes from repositories it doesn't know about.+Before, it would merge them all together without warning.++----++While I was testing that, I think I found out the real reason why XMPP+pushes have seemed a little unrelaible. It turns out to not be an XMPP+issue at all! Instead, the merger was simply not always+noticing when `git receive-pack` updated a ref, and not merging it into+master. That was easily fixed.++----++Adam Spiers has been getting a `.gitignore` query interface suitable for+the assistant to use into `git`, and he tells me it's landed in `next`.+I should soon check that out and get the assistant using it. But first,+Android app!
@@ -10,6 +10,7 @@ shakey; a better method would be to use its API) **done** * Dropbox? That would be ironic.. Via its API, presumably. * [[Amazon Glacier|todo/special_remote_for_amazon_glacier]] **done**+* Internet Archive **done** * [nimbus.io](https://nimbus.io/) Fairly low prices ($0.06/GB); REST API; free software * Mediafire provides 50gb free and has a REST API.
@@ -0,0 +1,7 @@+What directory should the Android webapp default to creating an annex in?++Same as the desktop webapp, users will be able to enter a directory they+want the first time they run it, but to save typing on android, anything+that gets enough votes will be included in a list of choices as well.++[[!poll open=yes expandable=yes 42 "/sdcard/annex" 3 "Whole /sdcard" 4 "DCIM directory (photos and videos only)" 1 "Same as for regular git-annex. ~/annex/"]]
@@ -1,4 +1,4 @@-This is a subset of [[/todo]] for items tagged for the assistant.-Link items to [[todo/done]] when done.+This is a subset of [[/todo]] and [[/bugs]] +for items tagged for the assistant. -[[!inline pages="tagged(design/assistant) and !link(bugs/done)" show=0 archive=yes]]+[[!inline pages="tagged(design/assistant) and !link(bugs/done) and !link(bugs/moreinfo)" show=0 archive=yes]]
@@ -9,11 +9,6 @@ * Do git-annex clients sharing an account with regular clients cause confusing things to happen? See <http://git-annex.branchable.com/design/assistant/blog/day_114__xmpp/#comment-aaba579f92cb452caf26ac53071a6788>-* Support use of a single XMPP account with several separate and- independant git-annex repos. This probably works for the simple- push notification use of XMPP, since unknown UUIDs will just be ignored.- But XMPP pairing and the pushes over XMPP assume that anyone you're- paired with is intending to sync to your repository. ## design goals @@ -64,18 +59,18 @@ For pairing, a chat message is sent to every known git-annex client, containing: - <git-annex xmlns='git-annex' pairing="PairReq|PairAck|PairDone uuid" />+ <git-annex xmlns='git-annex' pairing="PairReq|PairAck|PairDone myuuid" /> ### git push over XMPP To indicate that we could push over XMPP, a chat message is sent, to each known client of each XMPP remote. - <git-annex xmlns='git-annex' canpush="" />+ <git-annex xmlns='git-annex' canpush="myuuid" /> To request that a remote push to us, a chat message can be sent. - <git-annex xmlns='git-annex' pushrequest="" />+ <git-annex xmlns='git-annex' pushrequest="myuuid" /> When replying to an canpush message, this is directed at the specific client that indicated it could push. To solicit pushes from all clients,@@ -83,7 +78,7 @@ When a peer is ready to send a git push, it sends: - <git-annex xmlns='git-annex' startingpush="" />+ <git-annex xmlns='git-annex' startingpush="myuuid" /> The receiver runs `git receive-pack`, and sends back its output in one or more chat messages, directed to the client that is pushing:
@@ -31,10 +31,10 @@ The [[encryption_design|design/encryption]] allows additional encryption keys to be added on to a special remote later. Once a key is added, it is able to access content that has already been stored in the special remote.-To add a new key, just run `git annex initremote` again, specifying the+To add a new key, just run `git annex enableremote` specifying the new encryption key: - git annex initremote myremote encryption=788A3F4C+ git annex enableremote myremote encryption=788A3F4C Note that once a key has been given access to a remote, it's not possible to revoke that access, short of deleting the remote. See
@@ -0,0 +1,14 @@+I would like to use git annex between two locations (work and home) where essentially the two computers are never on at the same time.++I have set up a special remote (S3) to cater for file transfer between the sites, but still need some way of syncronising the git repositories between them.+I have access to a git server, but which doesn't have git-annex on it. So, I think that is all the components I need to get this working.++However, I don't want to have to manually sync my computers with the central server, so I would like the assistant to do it for me, in what is essentially the complement of the special remote.++What is the best way to accomplish this? I guess that this is a general git question, not specific to git annex.+I see some solutions [[http://stackoverflow.com/questions/3583061/automatically-mirror-a-git-repository]], but my git isn't really up to evaluating the options properly.++So, what do other people do in this situation? +++--Walter
@@ -0,0 +1,6 @@+Hi, I have installed latest version from https://launchpad.net/~rubiojr/+archive/git-annex, that is git-annex version: 3.20121017-ubuntu1ppa1~precise+When running git annex webapp I get ++git-annex: unknown command webapp++I only installed git-annex. Are there more packages to be installed to make it work?
@@ -0,0 +1,31 @@+Hi, +first sorry for my poor english it's not my native language.++I have one repository on my laptop and two repository on usb disk. Made with following walkthrough (creating a repository and adding a remote).++Yesterday I have a backup of my repository on usb disk before add some file with++cd /media/usb/annex;git fetch laptop; git merge laptop/master&&git annex get .&&git annex sync++Now the repository on my usb disk is a mess.+Every file before the commit are lost. +For example : + After the sync : file Z.7z +Z.7z: broken symbolic link to `../../../../.git/annex/objects/2K/49/SHA256-s0--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/SHA256-s0--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'++The file ../../../../.git/annex/objects/2K/49/SHA256-s0--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/SHA256-s0--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 doesn't exist.++On the repository before the sync (inside the Backup) :+file Z.7z +Z.7z: symbolic link to `../../../../.git/annex/objects/J1/f4/SHA256-s696365035--d2dcc67bf2f05fcfc7f42723b2d415d4f057a2eeadc282b40f5bc3724534f2f4/SHA256-s696365035--d2dcc67bf2f05fcfc7f42723b2d415d4f057a2eeadc282b40f5bc3724534f2f4'++The file ../../../../.git/annex/objects/J1/f4/SHA256-s696365035--d2dcc67bf2f05fcfc7f42723b2d415d4f057a2eeadc282b40f5bc3724534f2f4/SHA256-s696365035--d2dcc67bf2f05fcfc7f42723b2d415d4f057a2eeadc282b40f5bc3724534f2f4 still exist in the repository after the sync.++3 questions, if somebody could help me :++ - what I do wrong ?+ - why the- symlink for every file had change after "cd /media/usb/annex;git fetch laptop; git merge laptop/master&&git annex get .&&git annex sync"+ - how could I fix my repository ? recover file from the backup ? how ? Copy every file to start my repository from a new clean state ?+ ++
@@ -0,0 +1,31 @@+My situation goes something like this:++I have a machine with an annex and a number of special remotes (s3, box, and an rsync'd nas). I also have a git remote that's doesn't have git annex on it, so it's just got the git branches. That machine has some problems so I start getting the annex set up on a different machine. These are the steps that I went through:++* clone from the git remote+* git annex doesn't think this is an actual annex at this point, so `git annex init` it+* Now I can start the webapp `git annex webapp`+* My rsync'd remote works and the assistant starts downloading the files from there (which is great, it's the local network one) but the box and s3 remotes are disabled (sure, not a huge deal).+* Click enable on the box remote, I have to specify that it's a full backup remote+* Things start copying from box as well, so I disable it until everything from the local network is done+* Click enable on the s3 remote and it wants my AWS creds+* Download those and add it+* Set the remote up the same way, as a full backup.++At this point, all my files have copied from the rsync remote, so I enable the other remotes.++Now I want to make sure that all the remotes are set up and working properly.++I turn off the webapp, turn off direct mode (I think it was indirect mode by default, but I'd been playing with things before then), and `git annex drop <file>` a file that I don't particularly care about. Everything drops successfully.++I'm able to `git annex get -f <rsync remote> <file>` and from `<box remote>` successfully, but when I try to get from the s3 remote, it doesn't give me any output and doesn't download the file.++Having used regular git annex without the assistant before, I try re-initing the remote `git annex initremote <s3>`. It complains that there's no type, so I `git annex initremote <s3> type=S3`, then it complains about encryption. `git annex initremote <s3> type=S3 encryption=shared`. It says it worked, but I `git annex get -f <s3> <file>` still doesn't do anything.++After more looking around, it turns out that I may have created a second remote with the same name as the original s3 remote... (figure that out). I use the webapp to rename the remotes so they're different, but neither of them will `get -f` successfully.++At this point, I turn to you and ask what the heck I did wrong. I tried editing the remote.log and uuid.log files to remove the new s3 remote (and I figured out which one was which) from the git-annex branch. I also marked the new s3 remote as dead, but I still can't get access to s3.++`git annex fsck -f <s3>` doesn't actually seem to hit s3 (I seem to recall that it used to, it calculated checksums), it just checks some git local information.++I don't mind deleting my current checkout and starting from the clone step again if you think I've made too much of a mess. At least I know I can get my files off my rsync remote and box :)
@@ -0,0 +1,8 @@+[[!comment format=mdwn+ username="fmarier"+ ip="121.98.93.240"+ subject="My PPA (Ubuntu Precise) has git-annex 4.20130417"+ date="2013-04-27T11:12:26Z"+ content="""+I just went through the pain of backporting 90+ Haskell packages from Debian unstable: https://launchpad.net/~fmarier/+archive/ppa+"""]]
@@ -0,0 +1,13 @@+Hi, ++I am looking into using git-annex to sync my Music between 3 sources: desktop, laptop, backup, but I'm not sure if this will work ok.++I notice on the wiki that: "Normally, the content of files in the annex is prevented from being modified. That's a good thing, because it might be the only copy, you wouldn't want to lose it in a fumblefingered mistake."++But most music players will download artwork, and some of course will dynamically modify/rename files according to meta data. And I may be downloading and/or playing music on both the desktop and laptop. So obviously I wouldn't want to be manually unlocking individual files here. It would kinda interrupt the music listening experience. So how would this work?++I am assuming that I would always have to do a git annex add *.mp3 *.flac *.jpg before doing a git annex sync. But can I just keep everything unlocked? Any side effects here? Will this work or should I just use rsync?++Regards,++bk
@@ -0,0 +1,30 @@+Hi everyone,++I need some suggestions on how to operate git-annex best in my setup.++I need git-annex mainly for its ability to have directories of all my data on all my nodes but not for the data redundancy it can provide.+I have one node that contains 2 filesystems that I want to merge in one git-annex repository. One filesystem (lets call it SAFE) is on top of a RAID1 between two 1TB hds. The other (BIG) is on top of a 3TB hd. SAFE holds data I do not want to loose (like digital pictures). BIG holds data that I can loose.++I do not have enough disk space on other nodes to get rid of the RAID1.++This is how I mount my filesystems:++SAFE at ~/AllData/++BIG at ~/AllData/bigfiles/++The root of the git repository is at ~/AllData/ however when I do:++git-annex add ~/AllData/bigfiles/file1+It says:+add bigfiles/file1 failed++I assume that is because of file1 being on a different filesystem.++Do I have to create two repositories: one for each filesystem or do you have any ideas on how to use git-annex best in this scenario?+Having two repositories also has the disadvantage that I need two repositories on all other nodes am I right?++Thanks for you suggestions+++
@@ -0,0 +1,3 @@+I tried about 2 weeks ago using the assistant to create on my netbook and my home laptop 3 repositores, for music, pictures and documents. Since I was also going to be away from home, I set up jabber pairing and box.com for a transfer repository, and then watched everything crash as I didn't know yet about jabber only really working for 1 repository at a time.++So my new idea is this: one repository at ~/annex, and changing documents, music and pictures to be symlinks to actual documents, music and pictures folders inside ~/annex, allowing me to have just 1 annex to manage. Is this the best way to put everything together if I want to just jabber pairing (and just 1 jabber account)?
@@ -168,6 +168,10 @@ alternate locations from which the file can be downloaded. In this mode, addurl can be used both to add new files, or to add urls to existing files. +* rmurl file url++ Record that the file is no longer available at the url.+ * import [path ...] Moves files from somewhere outside the git working copy, and adds them to@@ -232,16 +236,41 @@ * initremote name [param=value ...] - Sets up a special remote. The remote's- configuration is specified by the parameters. If a remote- with the specified name has already been configured, its configuration- is modified by any values specified. In either case, the remote will be- added to `.git/config`.+ Creates a new special remote, and adds it to `.git/config`. + + The remote's configuration is specified by the parameters. Different+ types of special remotes need different configuration values. The+ command will prompt for parameters as needed. + All special remotes support encryption. You must either specify+ encryption=none to disable encryption, or use encryption=keyid+ (or encryption=emailaddress) to specify a gpg key that can access+ the encrypted special remote.+ Example Amazon S3 remote: - initremote mys3 type=S3 encryption=none datacenter=EU+ git annex initremote mys3 type=S3 encryption=me@example.com datacenter=EU +* enableremote name [param=value ...]++ Enables use of an existing special remote in the current repository,+ which may be a different repository than the one in which it was+ originally created with the initremote command. + + The name of the remote is the same name used when origianlly + creating that remote with "initremote". Run "git annex enableremote"+ with no parameters to get a list of special remote names.++ Some special remotes may need parameters to be specified every time.+ For example, the directory special remote requires a directory= parameter.+ + This command can also be used to modify the configuration of an existing+ special remote, by specifying new values for parameters that were originally+ set when using initremote. For example, to add a new gpg key to the keys+ that can access an encrypted remote:++ git annex initremote mys3 encryption=friend@example.com+ * trust [repository ...] Records that a repository is trusted to not unexpectedly lose@@ -904,13 +933,16 @@ * `remote.<name>.annex-ignore` If set to `true`, prevents git-annex- from using this remote by default. (You can still request it be used- by the --from and --to options.)+ from storing file contents on this remote by default.+ (You can still request it be used by the --from and --to options.) This is, for example, useful if the remote is located somewhere without git-annex-shell. (For example, if it's on GitHub). Or, it could be used if the network connection between two repositories is too slow to be used normally.++ This does not prevent git-annex sync (or the git-annex assistant) from+ syncing the git repository to the remote. * `remote.<name>.annex-sync`
@@ -1,8 +1,16 @@-If using Debian testing or unstable:+## Debian testing or unstable -* `sudo apt-get install git-annex`+ sudo apt-get install git-annex -If using Debian 6.0 stable:+## Debian 7.0 "wheezy": -* Follow the instructions to [enable backports](http://backports.debian.org/Instructions/).-* `sudo apt-get -t squeeze-backports install git-annex`+ sudo apt-get install git-annex++Note: This version does not include support for the [[assistant]].+The version of git-annex in unstable can be easily installed in wheezy.++## Debian 6.0 "squeeze"++Follow the instructions to [enable backports](http://backports.debian.org/Instructions/).++ sudo apt-get -t squeeze-backports install git-annex
@@ -0,0 +1,8 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawkurjhi0CRJvgm7QNaZDWS9hitBtavqIpc"+ nickname="Bret"+ subject="Can't update"+ date="2013-04-18T00:58:19Z"+ content="""+The laptop is one of the first macbook pro's with a 32 bit chip, which apple dropped support for in 10.7, so the furthest it can update to is 10.6.x. :(+"""]]
@@ -0,0 +1,8 @@+[[!comment format=mdwn+ username="http://edheil.wordpress.com/"+ ip="99.54.57.201"+ subject="comment 19"+ date="2013-04-18T02:05:34Z"+ content="""+sounds like a prime candidate for a nice lightweight linux distro ;)+"""]]
@@ -1,14 +1,29 @@-If using Ubuntu Oneiric or newer:+## Raring sudo apt-get install git-annex -Otherwise, see [[manual_installation_instructions|install]].+## Precise -There is a PPA with a newer version of git-annex in it, maintained by-Sergio Rubio. <https://launchpad.net/~rubiojr/+archive/git-annex>+ sudo apt-get install git-annex ----+Note: This version is too old to include the [[assistant]], but is otherwise+usable. +## Precise PPA++<https://launchpad.net/~fmarier/+archive/ppa>++A newer version of git-annex, including the [[assistant]].+(Maintained by François Marier)++ sudo add-apt-repository ppa:fmarier/ppa+ sudo apt-get update+ sudo apt-get install git-annex++## Oneiric++ sudo apt-get install git-annex+ 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).+had [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.
@@ -13,6 +13,7 @@ * [QuickCheck 2](http://hackage.haskell.org/package/QuickCheck) * [json](http://hackage.haskell.org/package/json) * [IfElse](http://hackage.haskell.org/package/IfElse)+ * [dlist](http://hackage.haskell.org/package/dlist) * [bloomfilter](http://hackage.haskell.org/package/bloomfilter) * [edit-distance](http://hackage.haskell.org/package/edit-distance) * [hS3](http://hackage.haskell.org/package/hS3) (optional)
@@ -1,29 +0,0 @@-git-annex 4.20130227 released with [[!toggle text="these changes"]]-[[!toggleable text="""- * annex.version is now set to 4 for direct mode repositories.- * Should now fully support git repositories with core.symlinks=false;- always using git's pseudosymlink files in such repositories.- * webapp: Allow creating repositories on filesystems that lack support for- symlinks.- * webapp: Can now add a new local repository, and make it sync with- the main local repository.- * Android: Bundle now includes openssh.- * Android: Support ssh connection caching.- * Android: Assistant is fully working. (But no webapp yet.)- * Direct mode: Support filesystems like FAT which can change their inodes- each time they are mounted.- * Direct mode: Fix support for adding a modified file.- * Avoid passing -p to rsync, to interoperate with crippled filesystems.- Closes: #[700282](http://bugs.debian.org/700282)- * Additional GIT\_DIR support bugfixes. May actually work now.- * webapp: Display any error message from git init if it fails to create- a repository.- * Fix a reversion in matching globs introduced in the last release,- where "*" did not match files inside subdirectories. No longer uses- the Glob library.- * copy: Update location log when no copy was performed, if the location- log was out of date.- * Makefile now builds using cabal, taking advantage of cabal's automatic- detection of appropriate build flags.- * test: The test suite is now built into the git-annex binary, and can- be run at any time."""]]
@@ -0,0 +1,57 @@+git-annex 4.20130501 released with [[!toggle text="these changes"]]+[[!toggleable text="""+ * sync, assistant: Behavior changes: Sync with remotes that have+ annex-ignore set, so that git remotes on servers without git-annex+ installed can be used to keep clients' git repos in sync.+ * assistant: Work around misfeature in git 1.8.2 that makes+ `git commit --alow-empty -m ""` run an editor.+ * sync: Bug fix, avoid adding to the annex the+ dummy symlinks used on crippled filesystems.+ * Add public repository group.+ (And inpreferreddir to preferred content expressions.)+ * webapp: Can now set up Internet Archive repositories.+ * S3: Dropping content from the Internet Archive doesn't work, but+ their API indicates it does. Always refuse to drop from there.+ * Automatically register public urls for files uploaded to the+ Internet Archive.+ * To enable an existing special remote, the new enableremote command+ must be used. The initremote command now is used only to create+ new special remotes.+ * initremote: If two existing remotes have the same name,+ prefer the one with a higher trust level.+ * assistant: Improved XMPP protocol to better support multiple repositories+ using the same XMPP account. Fixes bad behavior when sharing with a friend+ when you or the friend have multiple reposotories on an XMPP account.+ Note that XMPP pairing with your own devices still pairs with all+ repositories using your XMPP account.+ * assistant: Fix bug that could cause incoming pushes to not get+ merged into the local tree. Particularly affected XMPP pushes.+ * webapp: Display some additional information about a repository on+ its edit page.+ * webapp: Install FDO desktop menu file when started in standalone mode.+ * webapp: Don't default to making repository in cwd when started+ from within a directory containing a git-annex file (eg, standalone+ tarball directory).+ * Detect systems that have no user name set in GECOS, and also+ don't have user.name set in git config, and put in a workaround+ so that commits to the git-annex branch (and the assistant)+ will still succeed despite git not liking the system configuration.+ * webapp: When told to add a git repository on a remote server, and+ the repository already exists as a non-bare repository, use it,+ rather than initializing a bare repository in the same directory.+ * direct, indirect: Refuse to do anything when the assistant+ or git-annex watch daemon is running.+ * assistant: When built with git before 1.8.0, use `git remote rm`+ to delete a remote. Newer git uses `git remote remove`.+ * rmurl: New command, removes one of the recorded urls for a file.+ * Detect when the remote is broken like bitbucket is, and exits 0 when+ it fails to run git-annex-shell.+ * assistant: Several improvements to performance and behavior when+ performing bulk adds of a large number of files (tens to hundreds+ of thousands).+ * assistant: Sanitize XMPP presence information logged for debugging.+ * webapp: Now automatically fills in any creds used by an existing remote+ when creating a new remote of the same type. Done for Internet Archive,+ S3, Glacier, and Box.com remotes.+ * Store an annex-uuid file in the bucket when setting up a new S3 remote.+ * Support building with DAV 0.4."""]]
@@ -72,6 +72,18 @@ expression. It'll make it prefer to get content that's not present, and drop content that is present! Don't go there.. +### difference: "inpreferreddir"++There's a special "inpreferreddir" keyword you can use in a+preferred content expression of a special remote. This means that the+content is preferred if it's in a directory (located anywhere in the tree)+with a special name.++The name of the directory can be configured using +`git annex initremote $remote preferreddir=$dirname`++(If no directory name is configured, it uses "public" by default.)+ ## standard expressions git-annex comes with some standard preferred content expressions, that can@@ -165,6 +177,18 @@ reached an archive repository. `present and ($client)`++### public++This is used for publishing information to a repository that can be+publically accessed. Only files in a directory with a particular name+will be published. (The directory can be located anywhere in the+repository.)++The name of the directory can be configured using+`git annex initremote $remote preferreddir=$dirname`++`inpreferreddir` ### unwanted
@@ -21,7 +21,7 @@ every clone of the repository to access the encrypted data (use with caution). Note that additional gpg keys can be given access to a remote by- rerunning initremote with the new key id. See [[encryption]].+ running enableremote with the new key id. See [[encryption]]. * `embedcreds` - Optional. Set to "yes" embed the login credentials inside the git repository, which allows other clones to also access them. This is
@@ -26,7 +26,7 @@ every clone of the repository to access the encrypted data (use with caution). Note that additional gpg keys can be given access to a remote by- rerunning initremote with the new key id. See [[encryption]].+ running enableremote with the new key id. See [[encryption]]. * `buprepo` - Required. This is passed to `bup` as the `--remote` to use to store data. To create the repository,`bup init` will be run.
@@ -16,7 +16,7 @@ every clone of the repository to decrypt the encrypted data. Note that additional gpg keys can be given access to a remote by- rerunning initremote with the new key id. See [[encryption]].+ running enableremote with the new key id. See [[encryption]]. * `chunksize` - Avoid storing files larger than the specified size in the directory. For use on directories on mount points that have file size
@@ -27,7 +27,7 @@ every clone of the repository to access the encrypted data (use with caution). Note that additional gpg keys can be given access to a remote by- rerunning initremote with the new key id. See [[encryption]].+ running enableremote with the new key id. See [[encryption]]. * `embedcreds` - Optional. Set to "yes" embed the login credentials inside the git repository, which allows other clones to also access them. This is
@@ -31,7 +31,7 @@ every clone of the repository to access the encrypted data. Note that additional gpg keys can be given access to a remote by- rerunning initremote with the new key id. See [[encryption]].+ running enableremote with the new key id. See [[encryption]]. * `hooktype` - Required. This specifies a collection of hooks to use for this remote.
@@ -21,7 +21,7 @@ every clone of the repository to decrypt the encrypted data. Note that additional gpg keys can be given access to a remote by- rerunning initremote with the new key id. See [[encryption]].+ running enableremote with the new key id. See [[encryption]]. * `rsyncurl` - Required. This is the url or `hostname:/directory` to pass to rsync to tell it where to store content.@@ -31,7 +31,7 @@ setups, but not with some hosting providers that do not expose rsynced filenames to the shell. You'll know you need this option if `git annex get` from the special remote fails with an error message containing a single- quote (`'`) character. If that happens, you can re-run initremote+ quote (`'`) character. If that happens, you can run enableremote setting shellescape=no. The `annex-rsync-options` git configuration setting can be used to pass
@@ -16,7 +16,7 @@ every clone of the repository to access the encrypted data (use with caution). Note that additional gpg keys can be given access to a remote by- rerunning initremote with the new key id. See [[encryption]].+ running enableremote with the new key id. See [[encryption]]. * `embedcreds` - Optional. Set to "yes" embed the login credentials inside the git repository, which allows other clones to also access them. This is
@@ -0,0 +1,10 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawmRFKwny4rArBaz-36xTcsJYqKIgdDaw5Q"+ nickname="Andrew"+ subject="comment 2"+ date="2013-04-17T22:28:39Z"+ content="""+Yeah, I agree, this would be nice. ++For your own domain, you can configure DNS like this: [[http://support.google.com/a/bin/answer.py?hl=en&answer=34143]] to make XMPP find the right server. But for some that's not an option and the \"advanced\" mode would be useful in that case.+"""]]
@@ -1,12 +1,18 @@-What steps will reproduce the problem?+### Please describe the problem. -What is the expected output? What do you see instead?+### What steps will reproduce the problem? -What version of git-annex are you using? On what operating system?+### What version of git-annex are you using? On what operating system? -Please provide any additional information below.+### Please provide any additional information below. +[[!format sh """+# If you can, paste a complete transcript of the problem occurring here.+# If the problem is with the git-annex assistant, paste in .git/annex/debug.log ++# End of transcript or log.+"""]]
@@ -8,6 +8,15 @@ 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). +A nice added feature is that whenever git-annex sends a file to the+Internet Archive, it records its url, the same as if you'd run `git annex+addurl`. So any users who can clone your repository can download the files+from archive.org, without needing any login or password info. This makes+the Internet Archive a nice way to publish the large files associated with+a public git repository.++----+ Sign up for an account, and get your access keys here: <http://www.archive.org/account/s3.php>
@@ -0,0 +1,8 @@+Hi,++I would love to have a page of "history" or "events" in the webapp. Similar to how Dropbox or Box show it.+I've been using git-annex for my personal files for a few months now, and I feel like this is the only feature missing to start using it in a production multi-user environment.++Thanks++[[!tag design/assistant]]
@@ -1,6 +1,6 @@ # François Marier -Free Software and Debian Developer. Lead developer of [Libravatar](http://www.libravatar.org)+Free Software and Debian Developer. Lead developer of [Libravatar](https://www.libravatar.org) -* [Blog](http://feeding.cloud.geek.nz)-* [Identica](http://identi.ca/fmarier) / [Twitter](http://twitter.com/fmarier)+* [Blog](http://feeding.cloud.geek.nz) and [homepage](http://fmarier.org)+* [Identica](http://identi.ca/fmarier) / [Twitter](https://twitter.com/fmarier)
@@ -1,4 +1,6 @@ Normally, the content of files in the annex is prevented from being modified.+(Unless your repository is using [[direct_mode]].)+ That's a good thing, because it might be the only copy, you wouldn't want to lose it in a fumblefingered mistake.
@@ -154,6 +154,9 @@ alternate locations from which the file can be downloaded. In this mode, addurl can be used both to add new files, or to add urls to existing files. .IP+.IP "rmurl file url"+Record that the file is no longer available at the url.+.IP .IP "import [path ...]" Moves files from somewhere outside the git working copy, and adds them to the annex. Individual files to import can be specified. @@ -211,16 +214,40 @@ "here". .IP .IP "initremote name [param=value ...]"-Sets up a special remote. The remote's-configuration is specified by the parameters. If a remote-with the specified name has already been configured, its configuration-is modified by any values specified. In either case, the remote will be-added to .git/config.+Creates a new special remote, and adds it to .git/config. .IP+The remote's configuration is specified by the parameters. Different+types of special remotes need different configuration values. The+command will prompt for parameters as needed.+.IP+All special remotes support encryption. You must either specify+encryption=none to disable encryption, or use encryption=keyid+(or encryption=emailaddress) to specify a gpg key that can access+the encrypted special remote.+.IP Example Amazon S3 remote: .IP- initremote mys3 type=S3 encryption=none datacenter=EU+ git annex initremote mys3 type=S3 encryption=me@example.com datacenter=EU .IP+.IP "enableremote name [param=value ...]"+Enables use of an existing special remote in the current repository,+which may be a different repository than the one in which it was+originally created with the initremote command. +.IP+The name of the remote is the same name used when origianlly +creating that remote with "initremote". Run "git annex enableremote"+with no parameters to get a list of special remote names.+.IP+Some special remotes may need parameters to be specified every time.+For example, the directory special remote requires a directory= parameter.+.IP+This command can also be used to modify the configuration of an existing+special remote, by specifying new values for parameters that were originally+set when using initremote. For example, to add a new gpg key to the keys+that can access an encrypted remote:+.IP+ git annex initremote mys3 encryption=friend@example.com+.IP .IP "trust [repository ...]" Records that a repository is trusted to not unexpectedly lose content. Use with care.@@ -797,13 +824,16 @@ .IP .IP "remote.<name>.annex\-ignore" If set to true, prevents git\-annex-from using this remote by default. (You can still request it be used-by the \-\-from and \-\-to options.)+from storing file contents on this remote by default.+(You can still request it be used by the \-\-from and \-\-to options.) .IP This is, for example, useful if the remote is located somewhere without git\-annex\-shell. (For example, if it's on GitHub). Or, it could be used if the network connection between two repositories is too slow to be used normally.+.IP+This does not prevent git\-annex sync (or the git\-annex assistant) from+syncing the git repository to the remote. .IP .IP "remote.<name>.annex\-sync" If set to false, prevents git\-annex sync (and the git\-annex assistant)
@@ -1,5 +1,5 @@ Name: git-annex-Version: 4.20130417+Version: 4.20130501 Cabal-Version: >= 1.8 License: GPL Maintainer: Joey Hess <joey@kitenet.net>@@ -70,7 +70,7 @@ extensible-exceptions, dataenc, SHA, process, json, base (>= 4.5 && < 4.8), monad-control, transformers-base, lifted-base, IfElse, text, QuickCheck >= 2.1, bloomfilter, edit-distance, process,- SafeSemaphore, uuid, random, regex-tdfa+ SafeSemaphore, uuid, random, regex-tdfa, dlist -- Need to list these because they're generated from .hsc files. Other-Modules: Utility.Touch Utility.Mounts Include-Dirs: Utility@@ -113,7 +113,6 @@ if (! os(windows) && ! os(solaris) && ! os(linux)) CPP-Options: -DWITH_KQUEUE C-Sources: Utility/libkqueue.c- Includes: sys/event.h if os(linux) && flag(Dbus) Build-Depends: dbus (>= 0.10.3)
@@ -18,13 +18,13 @@ GITTREE=$(GIT_ANNEX_ANDROID_SOURCETREE)/git/installed-tree build: start- $(MAKE) $(GIT_ANNEX_ANDROID_SOURCETREE)/openssl- $(MAKE) $(GIT_ANNEX_ANDROID_SOURCETREE)/openssh- $(MAKE) $(GIT_ANNEX_ANDROID_SOURCETREE)/busybox- $(MAKE) $(GIT_ANNEX_ANDROID_SOURCETREE)/rsync- $(MAKE) $(GIT_ANNEX_ANDROID_SOURCETREE)/gnupg- $(MAKE) $(GIT_ANNEX_ANDROID_SOURCETREE)/git- $(MAKE) $(GIT_ANNEX_ANDROID_SOURCETREE)/term+ $(MAKE) $(GIT_ANNEX_ANDROID_SOURCETREE)/openssl/build-stamp+ $(MAKE) $(GIT_ANNEX_ANDROID_SOURCETREE)/openssh/build-stamp+ $(MAKE) $(GIT_ANNEX_ANDROID_SOURCETREE)/busybox/build-stamp+ $(MAKE) $(GIT_ANNEX_ANDROID_SOURCETREE)/rsync/build-stamp+ $(MAKE) $(GIT_ANNEX_ANDROID_SOURCETREE)/gnupg/build-stamp+ $(MAKE) $(GIT_ANNEX_ANDROID_SOURCETREE)/git/build-stamp+ $(MAKE) $(GIT_ANNEX_ANDROID_SOURCETREE)/term/build-stamp # Debug build because it does not need signing keys. cd $(GIT_ANNEX_ANDROID_SOURCETREE)/term && tools/build-debug@@ -79,12 +79,12 @@ mkdir -p ../../tmp cp $(GIT_ANNEX_ANDROID_SOURCETREE)/term/bin/Term-debug.apk ../../tmp/git-annex.apk -$(GIT_ANNEX_ANDROID_SOURCETREE)/openssl:+$(GIT_ANNEX_ANDROID_SOURCETREE)/openssl/build-stamp: cd $(GIT_ANNEX_ANDROID_SOURCETREE)/openssl && CC=$$(which cc) ./Configure android cd $(GIT_ANNEX_ANDROID_SOURCETREE)/openssl && $(MAKE) touch $@ -$(GIT_ANNEX_ANDROID_SOURCETREE)/openssh: openssh.patch openssh.config.h+$(GIT_ANNEX_ANDROID_SOURCETREE)/openssh/build-stamp: openssh.patch openssh.config.h cd $(GIT_ANNEX_ANDROID_SOURCETREE)/openssh && git reset --hard cd $(GIT_ANNEX_ANDROID_SOURCETREE)/openssh && ./configure --host=arm-linux-androideabi --with-ssl-dir=../openssl --without-openssl-header-check cat openssh.patch | (cd $(GIT_ANNEX_ANDROID_SOURCETREE)/openssh && patch -p1)@@ -94,31 +94,31 @@ cd $(GIT_ANNEX_ANDROID_SOURCETREE)/openssh && $(MAKE) ssh ssh-keygen touch $@ -$(GIT_ANNEX_ANDROID_SOURCETREE)/busybox: busybox_config+$(GIT_ANNEX_ANDROID_SOURCETREE)/busybox/build-stamp: busybox_config cp busybox_config $(GIT_ANNEX_ANDROID_SOURCETREE)/busybox/.config cd $(GIT_ANNEX_ANDROID_SOURCETREE)/busybox && yes '' | $(MAKE) oldconfig cd $(GIT_ANNEX_ANDROID_SOURCETREE)/busybox && $(MAKE) touch $@ -$(GIT_ANNEX_ANDROID_SOURCETREE)/git:+$(GIT_ANNEX_ANDROID_SOURCETREE)/git/build-stamp: cd $(GIT_ANNEX_ANDROID_SOURCETREE)/git && $(MAKE) install NO_OPENSSL=1 NO_GETTEXT=1 NO_GECOS_IN_PWENT=1 NO_GETPASS=1 NO_NSEC=1 NO_MKDTEMP=1 NO_PTHREADS=1 NO_PERL=1 NO_CURL=1 NO_EXPAT=1 NO_TCLTK=1 NO_ICONV=1 prefix= DESTDIR=installed-tree touch $@ -$(GIT_ANNEX_ANDROID_SOURCETREE)/rsync: rsync.patch+$(GIT_ANNEX_ANDROID_SOURCETREE)/rsync/build-stamp: rsync.patch cat rsync.patch | (cd $(GIT_ANNEX_ANDROID_SOURCETREE)/rsync && git reset --hard origin/master && git am) cp $(GIT_ANNEX_ANDROID_SOURCETREE)/automake/lib/config.sub $(GIT_ANNEX_ANDROID_SOURCETREE)/automake/lib/config.guess $(GIT_ANNEX_ANDROID_SOURCETREE)/rsync/ cd $(GIT_ANNEX_ANDROID_SOURCETREE)/rsync && ./configure --host=arm-linux-androideabi --disable-locale --disable-iconv-open --disable-iconv --disable-acl-support --disable-xattr-support cd $(GIT_ANNEX_ANDROID_SOURCETREE)/rsync && $(MAKE) touch $@ -$(GIT_ANNEX_ANDROID_SOURCETREE)/gnupg:+$(GIT_ANNEX_ANDROID_SOURCETREE)/gnupg/build-stamp: cd $(GIT_ANNEX_ANDROID_SOURCETREE)/gnupg && git checkout gnupg-1.4.13 cd $(GIT_ANNEX_ANDROID_SOURCETREE)/gnupg && ./autogen.sh cd $(GIT_ANNEX_ANDROID_SOURCETREE)/gnupg && ./configure --host=arm-linux-androideabi --disable-gnupg-iconv --enable-minimal --disable-card-support --disable-agent-support --disable-photo-viewers --disable-keyserver-helpers --disable-nls cd $(GIT_ANNEX_ANDROID_SOURCETREE)/gnupg; $(MAKE) || true # expected failure in doc build touch $@ -$(GIT_ANNEX_ANDROID_SOURCETREE)/term: term.patch icons+$(GIT_ANNEX_ANDROID_SOURCETREE)/term/build-stamp: term.patch icons cd $(GIT_ANNEX_ANDROID_SOURCETREE)/term && git reset --hard cat term.patch | (cd $(GIT_ANNEX_ANDROID_SOURCETREE)/term && patch -p1) (cd icons && tar c .) | (cd $(GIT_ANNEX_ANDROID_SOURCETREE)/term/res && tar x)
@@ -20,6 +20,7 @@ import qualified Yesod.Routes.Dispatch import qualified WaiAppStatic.Storage.Embedded import qualified Data.FileEmbed+import qualified Data.ByteString.Internal {- End EvilSplicer headers. -}
@@ -0,0 +1,306 @@+From d195f807dac2351d29aeff00d2aee3e151eb82e3 Mon Sep 17 00:00:00 2001+From: Joey Hess <joey@kitenet.net>+Date: Thu, 18 Apr 2013 19:37:28 -0400+Subject: [PATCH] build without TH++Used the EvilSplicer to expand the TH++Left off CmdArgs to save time.+---+ DAV.cabal | 20 +----+ Network/Protocol/HTTP/DAV.hs | 53 ++++++++++---+ Network/Protocol/HTTP/DAV/TH.hs | 167 ++++++++++++++++++++++++++++++++++++++-+ 3 files changed, 207 insertions(+), 33 deletions(-)++diff --git a/DAV.cabal b/DAV.cabal+index 774d4e5..8b85133 100644+--- a/DAV.cabal++++ b/DAV.cabal+@@ -38,25 +38,7 @@ library+ , transformers >= 0.3+ , xml-conduit >= 1.0 && <= 1.1+ , xml-hamlet >= 0.4 && <= 0.5+-executable hdav+- main-is: hdav.hs+- ghc-options: -Wall+- build-depends: base >= 4.5 && <= 5+- , bytestring+- , bytestring+- , case-insensitive >= 0.4+- , cmdargs >= 0.9+- , containers+- , http-conduit >= 1.4+- , http-types >= 0.7+- , lens >= 3.0+- , lifted-base >= 0.1+- , mtl >= 2.1+- , network >= 2.3+- , resourcet >= 0.3+- , transformers >= 0.3+- , xml-conduit >= 1.0 && <= 1.1+- , xml-hamlet >= 0.4 && <= 0.5++ , text+ + source-repository head+ type: git+diff --git a/Network/Protocol/HTTP/DAV.hs b/Network/Protocol/HTTP/DAV.hs+index 02e5d15..c0be362 100644+--- a/Network/Protocol/HTTP/DAV.hs++++ b/Network/Protocol/HTTP/DAV.hs+@@ -52,7 +52,8 @@ import Network.HTTP.Types (hContentType, Method, Status, RequestHeaders, unautho+ + import qualified Text.XML as XML+ import Text.XML.Cursor (($/), (&/), element, node, fromDocument, checkName)+-import Text.Hamlet.XML (xml)++import Text.Hamlet.XML++import qualified Data.Text+ + import Data.CaseInsensitive (mk)+ +@@ -246,18 +247,48 @@ makeCollection url username password = withDS url username password $+ propname :: XML.Document+ propname = XML.Document (XML.Prologue [] Nothing []) root []+ where+- root = XML.Element "D:propfind" (Map.fromList [("xmlns:D", "DAV:")]) [xml|+-<D:allprop>+-|]++ root = XML.Element "D:propfind" (Map.fromList [("xmlns:D", "DAV:")]) $ concat++ [[XML.NodeElement++ (XML.Element++ (XML.Name++ (Data.Text.pack "D:allprop") Nothing Nothing)++ Map.empty++ (concat []))]]+++ + locky :: XML.Document+ locky = XML.Document (XML.Prologue [] Nothing []) root []+ where+- root = XML.Element "D:lockinfo" (Map.fromList [("xmlns:D", "DAV:")]) [xml|+-<D:lockscope>+- <D:exclusive>+-<D:locktype>+- <D:write>+-<D:owner>Haskell DAV user+-|]++ root = XML.Element "D:lockinfo" (Map.fromList [("xmlns:D", "DAV:")]) $ concat++ [[XML.NodeElement++ (XML.Element++ (XML.Name++ (Data.Text.pack "D:lockscope") Nothing Nothing)++ Map.empty++ (concat++ [[XML.NodeElement++ (XML.Element++ (XML.Name++ (Data.Text.pack "D:exclusive") Nothing Nothing)++ Map.empty++ (concat []))]]))],++ [XML.NodeElement++ (XML.Element++ (XML.Name++ (Data.Text.pack "D:locktype") Nothing Nothing)++ Map.empty++ (concat++ [[XML.NodeElement++ (XML.Element++ (XML.Name (Data.Text.pack "D:write") Nothing Nothing)++ Map.empty++ (concat []))]]))],++ [XML.NodeElement++ (XML.Element++ (XML.Name (Data.Text.pack "D:owner") Nothing Nothing)++ Map.empty++ (concat++ [[XML.NodeContent++ (Data.Text.pack "Haskell DAV user")]]))]]+++ +diff --git a/Network/Protocol/HTTP/DAV/TH.hs b/Network/Protocol/HTTP/DAV/TH.hs+index 036a2bc..4d3c0f4 100644+--- a/Network/Protocol/HTTP/DAV/TH.hs++++ b/Network/Protocol/HTTP/DAV/TH.hs+@@ -16,11 +16,13 @@+ -- You should have received a copy of the GNU General Public License+ -- along with this program. If not, see <http://www.gnu.org/licenses/>.+ +-{-# LANGUAGE TemplateHaskell #-}++{-# LANGUAGE RankNTypes #-}+ + module Network.Protocol.HTTP.DAV.TH where+ +-import Control.Lens (makeLenses)++import Control.Lens++import qualified Control.Lens.Type++import qualified Data.Functor+ import qualified Data.ByteString as B+ import Network.HTTP.Conduit (Manager, Request)+ +@@ -33,4 +35,163 @@ data DAVContext a = DAVContext {+ , _basicusername :: B.ByteString+ , _basicpassword :: B.ByteString+ }+-makeLenses ''DAVContext++allowedMethods ::++ forall a_a4Oo.++ Control.Lens.Type.Lens' (DAVContext a_a4Oo) [B.ByteString]++allowedMethods++ _f_a5tt++ (DAVContext __allowedMethods'_a5tu++ __baseRequest_a5tw++ __complianceClasses_a5tx++ __httpManager_a5ty++ __lockToken_a5tz++ __basicusername_a5tA++ __basicpassword_a5tB)++ = ((\ __allowedMethods_a5tv++ -> DAVContext++ __allowedMethods_a5tv++ __baseRequest_a5tw++ __complianceClasses_a5tx++ __httpManager_a5ty++ __lockToken_a5tz++ __basicusername_a5tA++ __basicpassword_a5tB)++ Data.Functor.<$> (_f_a5tt __allowedMethods'_a5tu))++{-# INLINE allowedMethods #-}++baseRequest ::++ forall a_a4Oo a_a5tC.++ Control.Lens.Type.Lens (DAVContext a_a4Oo) (DAVContext a_a5tC) (Request a_a4Oo) (Request a_a5tC)++baseRequest++ _f_a5tD++ (DAVContext __allowedMethods_a5tE++ __baseRequest'_a5tF++ __complianceClasses_a5tH++ __httpManager_a5tI++ __lockToken_a5tJ++ __basicusername_a5tK++ __basicpassword_a5tL)++ = ((\ __baseRequest_a5tG++ -> DAVContext++ __allowedMethods_a5tE++ __baseRequest_a5tG++ __complianceClasses_a5tH++ __httpManager_a5tI++ __lockToken_a5tJ++ __basicusername_a5tK++ __basicpassword_a5tL)++ Data.Functor.<$> (_f_a5tD __baseRequest'_a5tF))++{-# INLINE baseRequest #-}++basicpassword ::++ forall a_a4Oo.++ Control.Lens.Type.Lens' (DAVContext a_a4Oo) B.ByteString++basicpassword++ _f_a5tM++ (DAVContext __allowedMethods_a5tN++ __baseRequest_a5tO++ __complianceClasses_a5tP++ __httpManager_a5tQ++ __lockToken_a5tR++ __basicusername_a5tS++ __basicpassword'_a5tT)++ = ((\ __basicpassword_a5tU++ -> DAVContext++ __allowedMethods_a5tN++ __baseRequest_a5tO++ __complianceClasses_a5tP++ __httpManager_a5tQ++ __lockToken_a5tR++ __basicusername_a5tS++ __basicpassword_a5tU)++ Data.Functor.<$> (_f_a5tM __basicpassword'_a5tT))++{-# INLINE basicpassword #-}++basicusername ::++ forall a_a4Oo.++ Control.Lens.Type.Lens' (DAVContext a_a4Oo) B.ByteString++basicusername++ _f_a5tV++ (DAVContext __allowedMethods_a5tW++ __baseRequest_a5tX++ __complianceClasses_a5tY++ __httpManager_a5tZ++ __lockToken_a5u0++ __basicusername'_a5u1++ __basicpassword_a5u3)++ = ((\ __basicusername_a5u2++ -> DAVContext++ __allowedMethods_a5tW++ __baseRequest_a5tX++ __complianceClasses_a5tY++ __httpManager_a5tZ++ __lockToken_a5u0++ __basicusername_a5u2++ __basicpassword_a5u3)++ Data.Functor.<$> (_f_a5tV __basicusername'_a5u1))++{-# INLINE basicusername #-}++complianceClasses ::++ forall a_a4Oo.++ Control.Lens.Type.Lens' (DAVContext a_a4Oo) [B.ByteString]++complianceClasses++ _f_a5u4++ (DAVContext __allowedMethods_a5u5++ __baseRequest_a5u6++ __complianceClasses'_a5u7++ __httpManager_a5u9++ __lockToken_a5ua++ __basicusername_a5ub++ __basicpassword_a5uc)++ = ((\ __complianceClasses_a5u8++ -> DAVContext++ __allowedMethods_a5u5++ __baseRequest_a5u6++ __complianceClasses_a5u8++ __httpManager_a5u9++ __lockToken_a5ua++ __basicusername_a5ub++ __basicpassword_a5uc)++ Data.Functor.<$> (_f_a5u4 __complianceClasses'_a5u7))++{-# INLINE complianceClasses #-}++httpManager ::++ forall a_a4Oo. Control.Lens.Type.Lens' (DAVContext a_a4Oo) Manager++httpManager++ _f_a5ud++ (DAVContext __allowedMethods_a5ue++ __baseRequest_a5uf++ __complianceClasses_a5ug++ __httpManager'_a5uh++ __lockToken_a5uj++ __basicusername_a5uk++ __basicpassword_a5ul)++ = ((\ __httpManager_a5ui++ -> DAVContext++ __allowedMethods_a5ue++ __baseRequest_a5uf++ __complianceClasses_a5ug++ __httpManager_a5ui++ __lockToken_a5uj++ __basicusername_a5uk++ __basicpassword_a5ul)++ Data.Functor.<$> (_f_a5ud __httpManager'_a5uh))++{-# INLINE httpManager #-}++lockToken ::++ forall a_a4Oo.++ Control.Lens.Type.Lens' (DAVContext a_a4Oo) (Maybe B.ByteString)++lockToken++ _f_a5um++ (DAVContext __allowedMethods_a5un++ __baseRequest_a5uo++ __complianceClasses_a5up++ __httpManager_a5uq++ __lockToken'_a5ur++ __basicusername_a5ut++ __basicpassword_a5uu)++ = ((\ __lockToken_a5us++ -> DAVContext++ __allowedMethods_a5un++ __baseRequest_a5uo++ __complianceClasses_a5up++ __httpManager_a5uq++ __lockToken_a5us++ __basicusername_a5ut++ __basicpassword_a5uu)++ Data.Functor.<$> (_f_a5um __lockToken'_a5ur))++{-# INLINE lockToken #-}+-- +1.7.10.4+
@@ -0,0 +1,27 @@+From c7d39a8f91af93203194313195a79b04d80a16a3 Mon Sep 17 00:00:00 2001+From: Joey Hess <joey@kitenet.net>+Date: Sun, 21 Apr 2013 15:22:00 -0400+Subject: [PATCH] link with libgsasl++This requires libgsasl.a (and no .so) be installed in the ugly hardcoded+lib dir. When built this way, the haskell gsasl library will link the+library into executables with no further options.+---+ gsasl.cabal | 1 ++ 1 file changed, 1 insertion(+)++diff --git a/gsasl.cabal b/gsasl.cabal+index c5c2b19..a31cc71 100644+--- a/gsasl.cabal++++ b/gsasl.cabal+@@ -27,6 +27,7 @@ library+ ghc-options: -Wall -O2+ hs-source-dirs: lib+ c-sources: cbits/hsgsasl-shim.c++ LD-Options: -L /home/joey/.ghc/android-14/arm-linux-androideabi-4.7/arm-linux-androideabi/sysroot/usr/lib/+ + build-depends:+ base >= 4.0 && < 5.0+-- +1.7.10.4+
@@ -0,0 +1,293 @@+From bbb49942123f06a36b170966e445692297f71d26 Mon Sep 17 00:00:00 2001+From: Joey Hess <joey@kitenet.net>+Date: Thu, 18 Apr 2013 19:14:30 -0400+Subject: [PATCH] build without TH++---+ lens.cabal | 13 +------------+ src/Control/Exception/Lens.hs | 2 +-+ src/Control/Lens.hs | 6 +++---+ src/Control/Lens/Equality.hs | 4 ++--+ src/Control/Lens/Fold.hs | 6 +++---+ src/Control/Lens/Internal.hs | 2 +-+ src/Control/Lens/Internal/Zipper.hs | 2 +-+ src/Control/Lens/Iso.hs | 2 --+ src/Control/Lens/Lens.hs | 2 +-+ src/Control/Lens/Operators.hs | 2 +-+ src/Control/Lens/Plated.hs | 2 +-+ src/Control/Lens/Setter.hs | 2 --+ src/Control/Lens/TH.hs | 2 +-+ src/Data/Data/Lens.hs | 6 +++---+ 14 files changed, 19 insertions(+), 34 deletions(-)++diff --git a/lens.cabal b/lens.cabal+index a06b3ce..a654b3d 100644+--- a/lens.cabal++++ b/lens.cabal+@@ -10,7 +10,7 @@ stability: provisional+ homepage: http://github.com/ekmett/lens/+ bug-reports: http://github.com/ekmett/lens/issues+ copyright: Copyright (C) 2012-2013 Edward A. Kmett+-build-type: Custom++build-type: Simple+ tested-with: GHC == 7.0.4, GHC == 7.4.1, GHC == 7.4.2, GHC == 7.6.1, GHC == 7.7.20121213, GHC == 7.7.20130117+ synopsis: Lenses, Folds and Traversals+ description:+@@ -171,7 +171,6 @@ library+ containers >= 0.4.0 && < 0.6,+ distributive >= 0.3 && < 1,+ filepath >= 1.2.0.0 && < 1.4,+- generic-deriving == 1.4.*,+ ghc-prim,+ hashable >= 1.1.2.3 && < 1.3,+ MonadCatchIO-transformers >= 0.3 && < 0.4,+@@ -233,14 +232,12 @@ library+ Control.Lens.Review+ Control.Lens.Setter+ Control.Lens.Simple+- Control.Lens.TH+ Control.Lens.Traversal+ Control.Lens.Tuple+ Control.Lens.Type+ Control.Lens.Wrapped+ Control.Lens.Zipper+ Control.Lens.Zoom+- Control.Monad.Error.Lens+ Control.Parallel.Strategies.Lens+ Control.Seq.Lens+ Data.Array.Lens+@@ -264,12 +261,8 @@ library+ Data.Typeable.Lens+ Data.Vector.Lens+ Data.Vector.Generic.Lens+- Generics.Deriving.Lens+- GHC.Generics.Lens+ System.Exit.Lens+ System.FilePath.Lens+- System.IO.Error.Lens+- Language.Haskell.TH.Lens+ Numeric.Lens+ + if flag(safe)+@@ -368,7 +361,6 @@ test-suite doctests+ deepseq,+ doctest >= 0.9.1,+ filepath,+- generic-deriving,+ mtl,+ nats,+ parallel,+@@ -394,7 +386,6 @@ benchmark plated+ comonad,+ criterion,+ deepseq,+- generic-deriving,+ lens,+ transformers+ +@@ -429,7 +420,6 @@ benchmark unsafe+ comonads-fd,+ criterion,+ deepseq,+- generic-deriving,+ lens,+ transformers+ +@@ -446,6 +436,5 @@ benchmark zipper+ comonads-fd,+ criterion,+ deepseq,+- generic-deriving,+ lens,+ transformers+diff --git a/src/Control/Exception/Lens.hs b/src/Control/Exception/Lens.hs+index 5c26d4e..9909132 100644+--- a/src/Control/Exception/Lens.hs++++ b/src/Control/Exception/Lens.hs+@@ -112,7 +112,7 @@ import Prelude+ , Maybe(..), Either(..), Functor(..), String, IO+ )+ +-{-# ANN module "HLint: ignore Use Control.Exception.catch" #-}+++ + -- $setup+ -- >>> :set -XNoOverloadedStrings+diff --git a/src/Control/Lens.hs b/src/Control/Lens.hs+index 8481e44..74700ae 100644+--- a/src/Control/Lens.hs++++ b/src/Control/Lens.hs+@@ -59,7 +59,7 @@ module Control.Lens+ , module Control.Lens.Review+ , module Control.Lens.Setter+ , module Control.Lens.Simple+-#ifndef DISABLE_TEMPLATE_HASKELL++#if 0+ , module Control.Lens.TH+ #endif+ , module Control.Lens.Traversal+@@ -89,7 +89,7 @@ import Control.Lens.Reified+ import Control.Lens.Review+ import Control.Lens.Setter+ import Control.Lens.Simple+-#ifndef DISABLE_TEMPLATE_HASKELL++#if 0+ import Control.Lens.TH+ #endif+ import Control.Lens.Traversal+@@ -99,4 +99,4 @@ import Control.Lens.Wrapped+ import Control.Lens.Zipper+ import Control.Lens.Zoom+ +-{-# ANN module "HLint: ignore Use import/export shortcut" #-}+++diff --git a/src/Control/Lens/Equality.hs b/src/Control/Lens/Equality.hs+index 982c2d7..3a3fe1a 100644+--- a/src/Control/Lens/Equality.hs++++ b/src/Control/Lens/Equality.hs+@@ -28,8 +28,8 @@ module Control.Lens.Equality+ import Control.Lens.Internal.Setter+ import Control.Lens.Type+ +-{-# ANN module "HLint: ignore Use id" #-}+-{-# ANN module "HLint: ignore Eta reduce" #-}+++++ + -- $setup+ -- >>> import Control.Lens+diff --git a/src/Control/Lens/Fold.hs b/src/Control/Lens/Fold.hs+index ae5100d..467eb37 100644+--- a/src/Control/Lens/Fold.hs++++ b/src/Control/Lens/Fold.hs+@@ -161,9 +161,9 @@ import Data.Traversable+ -- >>> let g :: Expr -> Expr; g = Debug.SimpleReflect.Vars.g+ -- >>> let timingOut :: NFData a => a -> IO a; timingOut = fmap (fromMaybe (error "timeout")) . timeout (5*10^6) . evaluate . force+ +-{-# ANN module "HLint: ignore Eta reduce" #-}+-{-# ANN module "HLint: ignore Use camelCase" #-}+-{-# ANN module "HLint: ignore Use curry" #-}+++++++ + infixl 8 ^.., ^?, ^?!, ^@.., ^@?, ^@?!+ +diff --git a/src/Control/Lens/Internal.hs b/src/Control/Lens/Internal.hs+index 295662e..539642d 100644+--- a/src/Control/Lens/Internal.hs++++ b/src/Control/Lens/Internal.hs+@@ -43,4 +43,4 @@ import Control.Lens.Internal.Review+ import Control.Lens.Internal.Setter+ import Control.Lens.Internal.Zoom+ +-{-# ANN module "HLint: ignore Use import/export shortcut" #-}+++diff --git a/src/Control/Lens/Internal/Zipper.hs b/src/Control/Lens/Internal/Zipper.hs+index 95875b7..76060be 100644+--- a/src/Control/Lens/Internal/Zipper.hs++++ b/src/Control/Lens/Internal/Zipper.hs+@@ -53,7 +53,7 @@ import Data.Profunctor.Unsafe+ -- >>> import Control.Lens+ -- >>> import Data.Char+ +-{-# ANN module "HLint: ignore Use foldl" #-}+++ + ------------------------------------------------------------------------------+ -- * Jacket+diff --git a/src/Control/Lens/Iso.hs b/src/Control/Lens/Iso.hs+index 62d40ef..235511a 100644+--- a/src/Control/Lens/Iso.hs++++ b/src/Control/Lens/Iso.hs+@@ -70,8 +70,6 @@ import Data.Profunctor.Unsafe+ import Unsafe.Coerce+ #endif+ +-{-# ANN module "HLint: ignore Use on" #-}+-+ -- $setup+ -- >>> :set -XNoOverloadedStrings+ -- >>> import Control.Lens+diff --git a/src/Control/Lens/Lens.hs b/src/Control/Lens/Lens.hs+index ff2a45f..5401ec4 100644+--- a/src/Control/Lens/Lens.hs++++ b/src/Control/Lens/Lens.hs+@@ -120,7 +120,7 @@ import Data.Profunctor+ import Data.Profunctor.Rep+ import Data.Profunctor.Unsafe+ +-{-# ANN module "HLint: ignore Use ***" #-}+++ + -- $setup+ -- >>> :set -XNoOverloadedStrings+diff --git a/src/Control/Lens/Operators.hs b/src/Control/Lens/Operators.hs+index d88cb49..fa7b37e 100644+--- a/src/Control/Lens/Operators.hs++++ b/src/Control/Lens/Operators.hs+@@ -107,4 +107,4 @@ import Control.Lens.Review+ import Control.Lens.Setter+ import Control.Lens.Zipper+ +-{-# ANN module "HLint: ignore Use import/export shortcut" #-}+++diff --git a/src/Control/Lens/Plated.hs b/src/Control/Lens/Plated.hs+index 07d9212..27070c0 100644+--- a/src/Control/Lens/Plated.hs++++ b/src/Control/Lens/Plated.hs+@@ -95,7 +95,7 @@ import Data.Data.Lens+ import Data.Monoid+ import Data.Tree+ +-{-# ANN module "HLint: ignore Reduce duplication" #-}+++ + -- | A 'Plated' type is one where we know how to extract its immediate self-similar children.+ --+diff --git a/src/Control/Lens/Setter.hs b/src/Control/Lens/Setter.hs+index 2acbfa6..4a12c6b 100644+--- a/src/Control/Lens/Setter.hs++++ b/src/Control/Lens/Setter.hs+@@ -87,8 +87,6 @@ import Data.Profunctor+ import Data.Profunctor.Rep+ import Data.Profunctor.Unsafe+ +-{-# ANN module "HLint: ignore Avoid lambda" #-}+-+ -- $setup+ -- >>> import Control.Lens+ -- >>> import Control.Monad.State+diff --git a/src/Control/Lens/TH.hs b/src/Control/Lens/TH.hs+index fbf4adb..ee723d7 100644+--- a/src/Control/Lens/TH.hs++++ b/src/Control/Lens/TH.hs+@@ -87,7 +87,7 @@ import Language.Haskell.TH+ import Language.Haskell.TH.Syntax+ import Language.Haskell.TH.Lens+ +-{-# ANN module "HLint: ignore Use foldl" #-}+++ + -- | Flags for 'Lens' construction+ data LensFlag+diff --git a/src/Data/Data/Lens.hs b/src/Data/Data/Lens.hs+index cf1e7c9..b39dacf 100644+--- a/src/Data/Data/Lens.hs++++ b/src/Data/Data/Lens.hs+@@ -65,9 +65,9 @@ import Data.Monoid+ import GHC.Exts (realWorld#)+ #endif+ +-{-# ANN module "HLint: ignore Eta reduce" #-}+-{-# ANN module "HLint: ignore Use foldl" #-}+-{-# ANN module "HLint: ignore Reduce duplication" #-}+++++++ + -- $setup+ -- >>> :set -XNoOverloadedStrings+-- +1.8.2.rc3+
binary file changed (59505 → 59934 bytes)
@@ -0,0 +1,32 @@+<div .span9 .hero-unit>+ <h2>+ Adding an Internet Archive item+ <p>+ <a href="http://archive.org/">The Internet Archive</a> allows anyone #+ to publically archive their information, for free. All you need to #+ get started is to #+ <a href="http://archive.org/account/login.createaccount.php">+ Create an account+ <p>+ The Internet Archive stores "items". An item can be a single file, or #+ a related group of files. You can make as many different items as you #+ like.+ <p>+ By default, only files that you place in a special directory #+ will be uploaded to your Internet Archive item. Any other files #+ in your repository will remain private.+ <p>+ <form method="post" .form-horizontal enctype=#{enctype}>+ <fieldset>+ ^{form}+ ^{webAppFormAuthToken}+ <div .form-actions>+ <button .btn .btn-primary type=submit onclick="$('#workingmodal').modal('show');">+ Add Internet Archive item+<div .modal .fade #workingmodal>+ <div .modal-header>+ <h3>+ Making item ...+ <div .modal-body>+ <p>+ Setting up your Internet Archive item. This could take a minute.
@@ -11,4 +11,9 @@ <h2> Store your data in the cloud - ^{makeCloudRepositories False}+ ^{makeCloudRepositories}++ <h2>+ Archive your data++ ^{makeArchiveRepositories}
@@ -0,0 +1,11 @@+<h3>+ <a href="@{AddGlacierR}">+ <i .icon-plus-sign></i> Amazon Glacier+<p>+ Low cost offline data archival.++<h3>+ <a href="@{AddIAR}">+ <i .icon-plus-sign></i> Internet Archive+<p>+ Free archival of public data.
@@ -2,7 +2,7 @@ <a href="@{AddBoxComR}"> <i .icon-plus-sign></i> Box.com <p>- Provides <b>free</b> cloud storage for small amounts of data.+ Provides free cloud storage for small amounts of data. <h3> <a href="@{AddRsyncNetR}">@@ -15,13 +15,6 @@ <i .icon-plus-sign></i> Amazon S3 <p> Good choice for professional quality storage.--$if not onlyTransfer- <h3>- <a href="@{AddGlacierR}">- <i .icon-plus-sign></i> Amazon Glacier- <p>- Low cost offline data archival. <h3> <a href="@{AddSshR}">
@@ -29,3 +29,7 @@ <p> In a hurry? Feel free to skip this step! You can always come back # and configure this repository later.+ <h3>+ Repository information+ <p>+ ^{repoInfo}
@@ -0,0 +1,22 @@+<div .span9 .hero-unit>+ <h2>+ Enabling #{description}+ <p>+ To use this Internet Archive repository, you need an Access Key ID, and a #+ Secret Access Key. These access keys will be stored in a file that #+ only you can access.+ <p>+ <form method="post" .form-horizontal enctype=#{enctype}>+ <fieldset>+ ^{form}+ ^{webAppFormAuthToken}+ <div .form-actions>+ <button .btn .btn-primary type=submit onclick="$('#workingmodal').modal('show');">+ Enable Internet Archive repository+<div .modal .fade #workingmodal>+ <div .modal-header>+ <h3>+ Enabling repository ...+ <div .modal-body>+ <p>+ Enabling this Internet Archive repository. This could take a minute.
@@ -16,14 +16,14 @@ <div .span4> $if xmppconfigured <h3>- <a href="@{XMPPR}">+ <a href="@{XMPPConfigR}"> Re-configure jabber account <p> Your jabber account is set up, and will be used to keep # in touch with remote devices, and with your friends. $else <h3>- <a href="@{XMPPR}">+ <a href="@{XMPPConfigR}"> Configure jabber account <p> Keep in touch with remote devices, and with your friends, #
@@ -29,4 +29,4 @@ ^{cloudRepoList} <h2> Add a cloud repository- ^{makeCloudRepositories True}+ ^{makeCloudRepositories}
@@ -14,4 +14,4 @@ ^{cloudRepoList} <h2> Add a cloud repository- ^{makeCloudRepositories True}+ ^{makeCloudRepositories}
@@ -47,6 +47,11 @@ files until they can be moved to some other repository, like a client # or transfer repository. <p>+ If you configure a repository that can be viwed by the public, #+ but you don't want all your files to show up there, you can #+ configure it to be a <b>public repository</b>. Then only files #+ located in a directory you choose will be sent to it.+ <p> Finally, repositories can be configured to be in <b>manual mode</b>. This # prevents content being automatically synced to the repository, but # you can use command-line tools like `git annex get` and `git annex drop` #
@@ -14,7 +14,11 @@ $nothing $maybe i <- alertIcon alert ^{htmlIcon i} #- #{renderAlertMessage alert}+ $if multiline+ $forall l <- messagelines+ #{l}<br>+ $else+ #{message} $maybe button <- alertButton alert <br> <a .btn .btn-primary href="@{ClickAlert aid}">