packages feed

git-annex 8.20200522 → 8.20200617

raw patch · 125 files changed

+1477/−977 lines, 125 filesdep ~process

Dependency ranges changed: process

Files

Annex/AutoMerge.hs view
@@ -120,7 +120,7 @@ 	void $ liftIO cleanup  	unless inoverlay $ do-		(deleted, cleanup2) <- inRepo (LsFiles.deleted [top])+		(deleted, cleanup2) <- inRepo (LsFiles.deleted [] [top]) 		unless (null deleted) $ 			Annex.Queue.addCommand "rm" 				[Param "--quiet", Param "-f", Param "--"]@@ -130,7 +130,8 @@ 	when merged $ do 		Annex.Queue.flush 		unless inoverlay $ do-			unstagedmap <- inodeMap $ inRepo $ LsFiles.notInRepo False [top]+			unstagedmap <- inodeMap $ inRepo $+				LsFiles.notInRepo [] False [top] 			cleanConflictCruft mergedks' mergedfs' unstagedmap 		showLongNote "Merge conflict was automatically resolved; you may want to examine the result." 	return merged
Annex/GitOverlay.hs view
@@ -20,6 +20,11 @@ import Git.Env import qualified Annex import qualified Annex.Queue+import qualified Utility.LockFile.PidLock as PidF+import qualified Utility.LockPool.PidLock as PidP+import Utility.LockPool (dropLock)+import Utility.Env+import Annex.LockPool.PosixOrPid (pidLockFile)  {- Runs an action using a different git index file. -} withIndexFile :: AltIndexFile -> (FilePath -> Annex a) -> Annex a@@ -125,3 +130,57 @@ 		, Annex.repoqueue = Just q 		} 	either E.throw return v++{- Wrap around actions that may run a git-annex child process.+ -+ - When pid locking is in use, this tries to take the pid lock, and if+ - successful, holds it while running the child process. The action+ - is run with the Annex monad modified so git commands are run with+ - an env var set, which prevents child git annex processes from t+ - rying to take the pid lock themselves.+ -+ - This way, any locking the parent does will not get in the way of+ - the child. The child is assumed to not do any locking that conflicts+ - with the parent, but if it did happen to do that, it would be noticed+ - when git-annex is used without pid locking.+ -}+runsGitAnnexChildProcess :: Annex a -> Annex a+runsGitAnnexChildProcess a = pidLockFile >>= \case+	Nothing -> a+	Just pidlock -> bracket (setup pidlock) cleanup (go pidlock)+  where+	setup pidlock = liftIO $ PidP.tryLock pidlock+	+	cleanup (Just h) = liftIO $ dropLock h+	cleanup Nothing = return ()+	+	go _ Nothing = a+	go pidlock (Just _h) = do+		v <- liftIO $ PidF.pidLockEnv pidlock+		let addenv g = do+			g' <- liftIO $ addGitEnv g v "1"+			return (g', ())+		let rmenv oldg g+			| any (\(k, _) -> k == v) (fromMaybe [] (Git.gitEnv oldg)) = g+			| otherwise = +				let e' = case Git.gitEnv g of+					Just e -> Just (delEntry v e)+					Nothing -> Nothing+				in g { Git.gitEnv = e' }+		withAltRepo addenv rmenv (const a)++runsGitAnnexChildProcess' :: Git.Repo -> (Git.Repo -> IO a) -> Annex a+runsGitAnnexChildProcess' r a = pidLockFile >>= \case+	Nothing -> liftIO $ a r+	Just pidlock -> liftIO $ bracket (setup pidlock) cleanup (go pidlock)+  where+	setup pidlock = PidP.tryLock pidlock+	+	cleanup (Just h) = dropLock h+	cleanup Nothing = return ()+	+	go _ Nothing = a r+	go pidlock (Just _h) = do+		v <- PidF.pidLockEnv pidlock+		r' <- addGitEnv r v "1"+		a r'
Annex/Import.hs view
@@ -50,6 +50,7 @@ import qualified Database.Export as Export import qualified Database.ContentIdentifier as CIDDb import qualified Logs.ContentIdentifier as CIDLog+import Backend.Utilities  import Control.Concurrent.STM import qualified Data.Map.Strict as M@@ -404,7 +405,7 @@  - content, before generating its real key. -} importKey :: ContentIdentifier -> Integer -> Key importKey (ContentIdentifier cid) size = mkKey $ \k -> k-	{ keyName = cid+	{ keyName = genKeyName (decodeBS cid) 	, keyVariety = OtherKey "CID" 	, keySize = Just size 	}
Annex/Ingest.hs view
@@ -18,7 +18,6 @@ 	addLink, 	makeLink, 	addUnlocked,-	restoreFile, 	forceParams, 	addAnnexedFile, ) where
Annex/Init.hs view
@@ -1,6 +1,6 @@ {- git-annex repository initialization  -- - Copyright 2011-2019 Joey Hess <id@joeyh.name>+ - Copyright 2011-2020 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU AGPL version 3 or higher.  -}@@ -10,6 +10,7 @@  module Annex.Init ( 	ensureInitialized,+	autoInitialize, 	isInitialized, 	initialize, 	initialize',@@ -35,17 +36,21 @@ import Annex.UUID import Annex.WorkTree import Annex.Fixup+import Annex.Path+import Annex.GitOverlay import Config import Config.Files import Config.Smudge import qualified Upgrade.V5.Direct as Direct import qualified Annex.AdjustedBranch as AdjustedBranch+import Remote.List.Util (remotesChanged) import Annex.Environment import Annex.Hook import Annex.InodeSentinal import Upgrade import Annex.Tmp import Utility.UserInfo+import Utility.ThreadScheduler #ifndef mingw32_HOST_OS import Annex.Perms import Utility.FileMode@@ -55,9 +60,10 @@ #endif  import qualified Data.Map as M+import Control.Concurrent.Async  checkCanInitialize :: Annex a -> Annex a-checkCanInitialize a = inRepo (noAnnexFileContent . fmap fromRawFilePath . Git.repoWorkTree) >>= \case+checkCanInitialize a = canInitialize' >>= \case 	Nothing -> a 	Just noannexmsg -> do 		warning "Initialization prevented by .noannex file (remove the file to override)"@@ -65,6 +71,12 @@ 			warning noannexmsg 		giveup "Not initialized." +canInitialize :: Annex Bool+canInitialize = isNothing <$> canInitialize'++canInitialize' :: Annex (Maybe String)+canInitialize' = inRepo (noAnnexFileContent . fmap fromRawFilePath . Git.repoWorkTree)+ genDescription :: Maybe String -> Annex UUIDDesc genDescription (Just d) = return $ UUIDDesc $ encodeBS d genDescription Nothing = do@@ -150,10 +162,23 @@ ensureInitialized = getVersion >>= maybe needsinit checkUpgrade   where 	needsinit = ifM Annex.Branch.hasSibling-		( initialize Nothing Nothing-		, giveup $ "First run: git-annex init"+		( do+			initialize Nothing Nothing+			autoEnableSpecialRemotes+		, giveup "First run: git-annex init" 		) +{- Initialize if it can do so automatically.+ -+ - Checks repository version and handles upgrades too.+ -}+autoInitialize :: Annex ()+autoInitialize = getVersion >>= maybe needsinit checkUpgrade+  where+	needsinit = whenM (canInitialize <&&> Annex.Branch.hasSibling) $ do+			initialize Nothing Nothing+			autoEnableSpecialRemotes+ {- Checks if a repository is initialized. Does not check version for ugrade. -} isInitialized :: Annex Bool isInitialized = maybe Annex.Branch.hasSibling (const $ return True) =<< getVersion@@ -214,21 +239,28 @@ 			(Git.Config.boolConfig False)  probeLockSupport :: Annex Bool-probeLockSupport = do #ifdef mingw32_HOST_OS-	return True+probeLockSupport = return True #else-	withEventuallyCleanedOtherTmp $ \tmp -> do-		let f = tmp </> "lockprobe"-		mode <- annexFileMode-		liftIO $ do-			nukeFile f-			let locktest = -				Posix.lockExclusive (Just mode) f-					>>= Posix.dropLock-			ok <- isRight <$> tryNonAsync locktest-			nukeFile f-			return ok+probeLockSupport = withEventuallyCleanedOtherTmp $ \tmp -> do+	let f = tmp </> "lockprobe"+	mode <- annexFileMode+	liftIO $ withAsync warnstall (const (go f mode))+  where+	go f mode = do+		nukeFile f+		let locktest = bracket+			(Posix.lockExclusive (Just mode) f)+			Posix.dropLock+			(const noop)+		ok <- isRight <$> tryNonAsync locktest+		nukeFile f+		return ok+	+	warnstall = do+		threadDelaySeconds (Seconds 10)+		warningIO "Probing the filesystem for POSIX fcntl lock support is taking a long time."+		warningIO "(Setting annex.pidlock will avoid this probe.)" #endif  probeFifoSupport :: Annex Bool@@ -252,10 +284,12 @@ #endif  checkLockSupport :: Annex ()-checkLockSupport = unlessM probeLockSupport $ do-	warning "Detected a filesystem without POSIX fcntl lock support."-	warning "Enabling annex.pidlock."-	setConfig (annexConfig "pidlock") (Git.Config.boolConfig True)+checkLockSupport =+	unlessM (annexPidLock <$> Annex.getGitConfig) $+		unlessM probeLockSupport $ do+			warning "Detected a filesystem without POSIX fcntl lock support."+			warning "Enabling annex.pidlock."+			setConfig (annexConfig "pidlock") (Git.Config.boolConfig True)  checkFifoSupport :: Annex () checkFifoSupport = unlessM probeFifoSupport $ do@@ -287,3 +321,24 @@ fixupUnusualReposAfterInit = do 	gc <- Annex.getGitConfig 	void $ inRepo $ \r -> fixupUnusualRepos r gc++{- Try to enable any special remotes that are configured to do so.+ - + - The enabling is done in a child process to avoid it using stdio.+ -}+autoEnableSpecialRemotes :: Annex ()+autoEnableSpecialRemotes = runsGitAnnexChildProcess $ do+	rp <- fromRawFilePath <$> fromRepo Git.repoPath+	cmd <- liftIO programPath+	liftIO $ withNullHandle $ \nullh -> do+		let p = (proc cmd +			[ "init"+			, "--autoenable"+			])+			{ std_out = UseHandle nullh+			, std_err = UseHandle nullh+			, std_in = UseHandle nullh+			, cwd = Just rp+			}+		withCreateProcess p $ \_ _ _ pid -> void $ waitForProcess pid+	remotesChanged
Annex/Link.hs view
@@ -30,6 +30,7 @@ import Git.Config import Annex.HashObject import Annex.InodeSentinal+import Annex.GitOverlay import Utility.FileMode import Utility.InodeCache import Utility.Tmp.Dir@@ -204,23 +205,24 @@ 		    go (Just _) = withTmpDirIn (fromRawFilePath $ Git.localGitDir r) "annexindex" $ \tmpdir -> do 			let tmpindex = tmpdir </> "index" 			let updatetmpindex = do-				r' <- Git.Env.addGitEnv r Git.Index.indexEnv +				r' <- liftIO $ Git.Env.addGitEnv r Git.Index.indexEnv  					=<< Git.Index.indexEnvVal tmpindex 				-- Avoid git warning about CRLF munging. 				let r'' = r' { gitGlobalOpts = gitGlobalOpts r' ++ 					[ Param "-c" 					, Param $ "core.safecrlf=" ++ boolConfig False 					] }-				Git.UpdateIndex.refreshIndex r'' $ \feed ->-					forM_ l $ \(f', checkunmodified) ->-						whenM checkunmodified $-							feed f'+				runsGitAnnexChildProcess' r'' $ \r''' ->+					liftIO $ Git.UpdateIndex.refreshIndex r''' $ \feed ->+						forM_ l $ \(f', checkunmodified) ->+							whenM checkunmodified $+								feed f' 			let replaceindex = catchBoolIO $ do 				moveFile tmpindex realindex 				return True-			ok <- liftIO $ createLinkOrCopy realindex tmpindex+			ok <- liftIO (createLinkOrCopy realindex tmpindex) 				<&&> updatetmpindex-				<&&> replaceindex+				<&&> liftIO replaceindex 			unless ok showwarning 		bracket lockindex unlockindex go 
Annex/LockFile.hs view
@@ -1,6 +1,6 @@ {- git-annex lock files.  -- - Copyright 2012-2019 Joey Hess <id@joeyh.name>+ - Copyright 2012-2020 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU AGPL version 3 or higher.  -}
Annex/LockPool/PosixOrPid.hs view
@@ -18,6 +18,7 @@ 	LockStatus(..), 	getLockStatus, 	checkSaneLock,+	pidLockFile, ) where  import Common@@ -94,4 +95,4 @@ -- avoid complicating any code that might expect to be able to see that -- lock file. But, it's not locked. dummyPosixLock :: Maybe FileMode -> LockFile -> IO ()-dummyPosixLock m f = closeFd =<< openLockFile ReadLock m f+dummyPosixLock m f = bracket (openLockFile ReadLock m f) closeFd (const noop)
Annex/SpecialRemote/Config.hs view
@@ -104,11 +104,11 @@ 	, optionalStringParser sameasUUIDField HiddenField 	, optionalStringParser typeField 		(FieldDesc "type of special remote")-	, trueFalseParser autoEnableField False+	, trueFalseParser autoEnableField (Just False) 		(FieldDesc "automatically enable special remote")-	, yesNoParser exportTreeField False+	, yesNoParser exportTreeField (Just False) 		(FieldDesc "export trees of files to this remote")-	, yesNoParser importTreeField False+	, yesNoParser importTreeField (Just False) 		(FieldDesc "import trees of files from this remote") 	, optionalStringParser preferreddirField 		(FieldDesc "directory whose content is preferred")@@ -232,16 +232,16 @@ 	p (Just v) _c = Right (Just (RemoteConfigValue (fromProposedAccepted v))) 	p Nothing _c = Right Nothing -yesNoParser :: RemoteConfigField -> Bool -> FieldDesc -> RemoteConfigFieldParser-yesNoParser f v fd = genParser yesno f v fd+yesNoParser :: RemoteConfigField -> Maybe Bool -> FieldDesc -> RemoteConfigFieldParser+yesNoParser f mdef fd = genParser yesno f mdef fd 	(Just (ValueDesc "yes or no"))   where 	yesno "yes" = Just True 	yesno "no" = Just False 	yesno _ = Nothing -trueFalseParser :: RemoteConfigField -> Bool -> FieldDesc -> RemoteConfigFieldParser-trueFalseParser f v fd = genParser trueFalseParser' f v fd+trueFalseParser :: RemoteConfigField -> Maybe Bool -> FieldDesc -> RemoteConfigFieldParser+trueFalseParser f mdef fd = genParser trueFalseParser' f mdef fd 	(Just (ValueDesc "true or false"))  -- Not using Git.Config.isTrueFalse because git supports@@ -256,22 +256,22 @@ 	:: Typeable t 	=> (String -> Maybe t) 	-> RemoteConfigField-	-> t -- ^ fallback value+	-> Maybe t -- ^ default if not configured 	-> FieldDesc 	-> Maybe ValueDesc 	-> RemoteConfigFieldParser-genParser parse f fallback fielddesc valuedesc = RemoteConfigFieldParser	+genParser parse f mdef fielddesc valuedesc = RemoteConfigFieldParser	 	{ parserForField = f 	, valueParser = p 	, fieldDesc = fielddesc 	, valueDesc = valuedesc 	}   where-	p Nothing _c = Right (Just (RemoteConfigValue fallback))+	p Nothing _c = Right (fmap RemoteConfigValue mdef) 	p (Just v) _c = case parse (fromProposedAccepted v) of 		Just b -> Right (Just (RemoteConfigValue b)) 		Nothing -> case v of-			Accepted _ -> Right (Just (RemoteConfigValue fallback))+			Accepted _ -> Right (fmap RemoteConfigValue mdef) 			Proposed _ -> Left $ 				"Bad value for " ++ fromProposedAccepted f ++ 				case valuedesc of
Annex/Ssh.hs view
@@ -258,19 +258,19 @@ 	-- (Except there's an unlikely false positive where a forced 	-- ssh command exits 255.) 	tryssh extraps = liftIO $ withNullHandle $ \nullh -> do-		let p = proc "ssh" $ concat+		let p = (proc "ssh" $ concat 			[ extraps 			, toCommand sshparams 			, [fromSshHost sshhost, "true"]-			]-		(Nothing, Nothing, Nothing, pid) <- createProcess $ p+			]) 			{ std_out = UseHandle nullh 			, std_err = UseHandle nullh 			}-		exitcode <- waitForProcess pid-		return $ case exitcode of-			ExitFailure 255 -> False-			_ -> True+		withCreateProcess p $ \_ _ _ pid -> do+			exitcode <- waitForProcess pid+			return $ case exitcode of+				ExitFailure 255 -> False+				_ -> True  {- Find ssh socket files.  -@@ -319,16 +319,19 @@ forceSshCleanup = mapM_ forceStopSsh =<< enumSocketFiles  forceStopSsh :: FilePath -> Annex ()-forceStopSsh socketfile = do+forceStopSsh socketfile = withNullHandle $ \nullh -> do 	let (dir, base) = splitFileName socketfile-	let params = sshConnectionCachingParams base-	-- "ssh -O stop" is noisy on stderr even with -q-	void $ liftIO $ catchMaybeIO $-		withQuietOutput createProcessSuccess $-			(proc "ssh" $ toCommand $-				[ Param "-O", Param "stop" ] ++ -				params ++ [Param "localhost"])-				{ cwd = Just dir }+	let p = (proc "ssh" $ toCommand $+		[ Param "-O", Param "stop" ] ++ +		sshConnectionCachingParams base ++ +		[Param "localhost"])+		{ cwd = Just dir+		-- "ssh -O stop" is noisy on stderr even with -q+		, std_out = UseHandle nullh+		, std_err = UseHandle nullh+		}+	void $ liftIO $ catchMaybeIO $ withCreateProcess p $ \_ _ _ pid ->+		forceSuccessProcess p pid 	liftIO $ nukeFile socketfile  {- This needs to be as short as possible, due to limitations on the length@@ -458,7 +461,8 @@ runSshOptions args s = do 	let args' = toCommand (fromSshOptionsEnv s) ++ args 	let p = proc "ssh" args'-	exitWith =<< waitForProcess . processHandle =<< createProcess p+	exitcode <- withCreateProcess p $ \_ _ _ pid -> waitForProcess pid+	exitWith exitcode  {- When this env var is set, git-annex is being used as a ssh-askpass  - program, and should read the password from the specified location,
Annex/View.hs view
@@ -347,14 +347,13 @@ 	top <- fromRepo Git.repoPath 	(l, clean) <- inRepo $ Git.LsFiles.stagedDetails [top] 	liftIO . nukeFile =<< fromRepo gitAnnexViewIndex-	uh <- withViewIndex $ inRepo Git.UpdateIndex.startUpdateIndex-	forM_ l $ \(f, sha, mode) -> do-		topf <- inRepo (toTopFilePath f)-		go uh topf sha (toTreeItemType =<< mode) =<< lookupFile f-	liftIO $ do-		void $ stopUpdateIndex uh-		void clean-	genViewBranch view+	viewg <- withViewIndex gitRepo+	withUpdateIndex viewg $ \uh -> do+		forM_ l $ \(f, sha, mode) -> do+			topf <- inRepo (toTopFilePath f)+			go uh topf sha (toTreeItemType =<< mode) =<< lookupFile f+		liftIO $ void clean+		genViewBranch view   where 	genviewedfiles = viewedFiles view mkviewedfile -- enables memoization 
Annex/WorkTree.hs view
@@ -1,6 +1,6 @@ {- git-annex worktree files  -- - Copyright 2013-2019 Joey Hess <id@joeyh.name>+ - Copyright 2013-2020 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU AGPL version 3 or higher.  -}@@ -24,6 +24,8 @@ import Config import qualified Utility.RawFilePath as R +import Control.Concurrent+ {- Looks up the key corresponding to an annexed file in the work tree,  - by examining what the file links to.  -@@ -77,12 +79,13 @@  -} scanUnlockedFiles :: Annex () scanUnlockedFiles = whenM (inRepo Git.Ref.headExists <&&> not <$> isBareRepo) $ do-	Database.Keys.runWriter $-		liftIO . Database.Keys.SQL.dropAllAssociatedFiles+	dropold <- liftIO $ newMVar $ +		Database.Keys.runWriter $+			liftIO . Database.Keys.SQL.dropAllAssociatedFiles 	(l, cleanup) <- inRepo $ Git.LsTree.lsTree Git.LsTree.LsTreeRecursive Git.Ref.headRef 	forM_ l $ \i ->  		when (isregfile i) $-			maybe noop (add i)+			maybe noop (add dropold i) 				=<< catKey (Git.LsTree.sha i) 	liftIO $ void cleanup   where@@ -90,7 +93,8 @@ 		Just Git.Types.TreeFile -> True 		Just Git.Types.TreeExecutable -> True 		_ -> False-	add i k = do+	add dropold i k = do+		join $ fromMaybe noop <$> liftIO (tryTakeMVar dropold) 		let tf = Git.LsTree.file i 		Database.Keys.runWriter $ 			liftIO . Database.Keys.SQL.addAssociatedFileFast k tf
Annex/YoutubeDl.hs view
@@ -205,12 +205,14 @@ 			, Param "--no-warnings" 			, Param "--no-playlist" 			]-		(Nothing, Just o, Just e, pid) <- liftIO $ createProcess-			(proc "youtube-dl" (toCommand opts))-				{ std_out = CreatePipe-				, std_err = CreatePipe-				}-		output <- liftIO $ fmap fst $ +		let p = (proc "youtube-dl" (toCommand opts))+			{ std_out = CreatePipe+			, std_err = CreatePipe+			}+		liftIO $ withCreateProcess p waitproc+	+	waitproc Nothing (Just o) (Just e) pid = do+		output <- fmap fst $  			hGetContentsStrict o 				`concurrently` 			hGetContentsStrict e@@ -218,6 +220,8 @@ 		return $ case (ok, lines output) of 			(True, (f:_)) | not (null f) -> Right f 			_ -> nomedia+	waitproc _ _ _ _ = error "internal"+ 	nomedia = Left "no media in url"  youtubeDlOpts :: [CommandParam] -> Annex [CommandParam]
Assistant.hs view
@@ -100,18 +100,20 @@ 		let flag = "GIT_ANNEX_OUTPUT_REDIR" 		createAnnexDirectory (parentDir logfile) 		ifM (liftIO $ isNothing <$> getEnv flag)-			( liftIO $ withFile devNull WriteMode $ \nullh -> do+			( liftIO $ withNullHandle $ \nullh -> do 				loghandle <- openLog logfile 				e <- getEnvironment 				cmd <- programPath 				ps <- getArgs-				(_, _, _, pid) <- createProcess (proc cmd ps)+				let p = (proc cmd ps) 					{ env = Just (addEntry flag "1" e) 					, std_in = UseHandle nullh 					, std_out = UseHandle loghandle 					, std_err = UseHandle loghandle 					}-				exitWith =<< waitForProcess pid+				exitcode <- withCreateProcess p $ \_ _ _ pid ->+					waitForProcess pid+				exitWith exitcode 			, start (Utility.Daemon.foreground (Just pidfile)) $ 				case startbrowser of 					Nothing -> Nothing
Assistant/DeleteRemote.hs view
@@ -16,7 +16,7 @@ import Logs.Location import Assistant.DaemonStatus import qualified Remote-import Remote.List+import Remote.List.Util import qualified Git.Remote.Remove import Logs.Trust import qualified Annex@@ -35,7 +35,7 @@ 		<$> liftAnnex (Remote.remoteFromUUID uuid) 	liftAnnex $ do 		inRepo $ Git.Remote.Remove.remove (Remote.name remote)-		void $ remoteListRefresh+		remotesChanged 	updateSyncRemotes 	return remote 
Assistant/MakeRemote.hs view
@@ -13,7 +13,7 @@ import Assistant.Ssh import qualified Types.Remote as R import qualified Remote-import Remote.List+import Remote.List.Util import qualified Remote.Rsync as Rsync import qualified Remote.GCrypt as GCrypt import qualified Git@@ -46,7 +46,7 @@ addRemote :: Annex RemoteName -> Annex Remote addRemote a = do 	name <- a-	void remoteListRefresh+	remotesChanged 	maybe (error "failed to add remote") return 		=<< Remote.byName (Just name) @@ -174,7 +174,9 @@ 	-> (Remote -> Bool) 	-> Annex (Maybe CredPair) previouslyUsedCredPair getstorage remotetype criteria =-	getM fromstorage =<< filter criteria . filter sametype <$> remoteList+	getM fromstorage+		=<< filter criteria . filter sametype +		<$> Remote.remoteList   where 	sametype r = R.typename (R.remotetype r) == R.typename remotetype 	fromstorage r = do
Assistant/Monad.hs view
@@ -50,6 +50,9 @@ 		Monad, 		MonadIO, 		MonadReader AssistantData,+		MonadCatch,+		MonadThrow,+		MonadMask, 		Fail.MonadFail, 		Functor, 		Applicative
Assistant/Restart.hs view
@@ -113,7 +113,5 @@ startAssistant :: FilePath -> IO () startAssistant repo = void $ forkIO $ do 	program <- programPath-	(_, _, _, pid) <- -		createProcess $-			(proc program ["assistant"]) { cwd = Just repo }-	void $ checkSuccessProcess pid+	let p = (proc program ["assistant"]) { cwd = Just repo }+	withCreateProcess p $ \_ _ _ pid -> void $ checkSuccessProcess pid
Assistant/Sync.hs view
@@ -17,13 +17,12 @@ import Assistant.ScanRemotes import Assistant.RemoteControl import qualified Command.Sync-import Utility.Parallel import qualified Git import qualified Git.Command import qualified Remote import qualified Types.Remote as Remote-import qualified Remote.List as Remote import qualified Annex.Branch+import Remote.List.Util import Annex.UUID import Annex.TaggedPush import Annex.Ssh@@ -43,6 +42,7 @@ import Data.Time.Clock import qualified Data.Map as M import Control.Concurrent+import Control.Concurrent.Async  {- Syncs with remotes that may have been disconnected for a while.  - @@ -177,6 +177,11 @@ 		<*> (Remote.getRepo r >>= \repo -> 			sshOptionsTo repo (Remote.gitconfig r) g) +inParallel :: (v -> IO Bool) -> [v] -> IO ([v], [v])+inParallel a l = (\(t,f) -> (map fst t, map fst f)) +	. partition snd +	. zip l <$> mapConcurrently a l+ {- Displays an alert while running an action that syncs with some remotes,  - and returns any remotes that it failed to sync with.  -@@ -268,7 +273,7 @@ 	repo <- Remote.getRepo r 	let key = Config.remoteAnnexConfig repo "sync" 	Config.setConfig key (boolConfig enabled)-	void Remote.remoteListRefresh+	remotesChanged  updateExportTreeFromLogAll :: Assistant () updateExportTreeFromLogAll = do
Assistant/Threads/ConfigMonitor.hs view
@@ -18,7 +18,7 @@ import Logs.PreferredContent import Logs.Group import Logs.NumCopies-import Remote.List (remoteListRefresh)+import Remote.List.Util import qualified Git.LsTree as LsTree import Git.Types import Git.FilePath@@ -60,7 +60,7 @@ configFilesActions :: [(RawFilePath, Assistant ())] configFilesActions = 	[ (uuidLog, void $ liftAnnex uuidDescMapLoad)-	, (remoteLog, void $ liftAnnex remoteListRefresh)+	, (remoteLog, void $ liftAnnex remotesChanged) 	, (trustLog, void $ liftAnnex trustMapLoad) 	, (groupLog, void $ liftAnnex groupMapLoad) 	, (numcopiesLog, void $ liftAnnex globalNumCopiesLoad)
Assistant/Threads/RemoteControl.hs view
@@ -32,20 +32,27 @@ 	(cmd, params) <- liftIO $ toBatchCommand 		(program, [Param "remotedaemon", Param "--foreground"]) 	let p = proc cmd (toCommand params)-	(Just toh, Just fromh, _, pid) <- liftIO $ createProcess p+	bracket (setup p) cleanup (go p)+  where+	setup p = liftIO $ createProcess $ p 		{ std_in = CreatePipe 		, std_out = CreatePipe 		}-	-	urimap <- liftIO . newMVar =<< liftAnnex getURIMap+	cleanup = liftIO . cleanupProcess -	controller <- asIO $ remoteControllerThread toh-	responder <- asIO $ remoteResponderThread fromh urimap+	go p (Just toh, Just fromh, _, pid) = do+		urimap <- liftIO . newMVar =<< liftAnnex getURIMap -	-- run controller and responder until the remotedaemon dies-	liftIO $ void $ tryNonAsync $ controller `concurrently` responder-	debug ["remotedaemon exited"]-	liftIO $ forceSuccessProcess p pid+		controller <- asIO $ remoteControllerThread toh+		responder <- asIO $ remoteResponderThread fromh urimap++		-- run controller and responder until the remotedaemon dies+		liftIO $ void $ tryNonAsync $+			controller `concurrently` responder+		debug ["remotedaemon exited"]+		liftIO $ forceSuccessProcess p pid	+	go _ _ = error "internal"+		  -- feed from the remoteControl channel into the remotedaemon remoteControllerThread :: Handle -> Assistant ()
Assistant/Threads/SanityChecker.hs view
@@ -152,7 +152,7 @@ 	batchmaker <- liftIO getBatchCommandMaker  	-- Find old unstaged symlinks, and add them to git.-	(unstaged, cleanup) <- liftIO $ Git.LsFiles.notInRepo False ["."] g+	(unstaged, cleanup) <- liftIO $ Git.LsFiles.notInRepo [] False ["."] g 	now <- liftIO getPOSIXTime 	forM_ unstaged $ \file -> do 		let file' = fromRawFilePath file
Assistant/Threads/TransferScanner.hs view
@@ -128,7 +128,7 @@ 		<$> filterM inUnwantedGroup us  	g <- liftAnnex gitRepo-	(files, cleanup) <- liftIO $ LsFiles.inRepo [] g+	(files, cleanup) <- liftIO $ LsFiles.inRepo [] [] g 	removablers <- scan unwantedrs files 	void $ liftIO cleanup 
Assistant/Threads/Watcher.hs view
@@ -136,7 +136,7 @@ 		-- Notice any files that were deleted before 		-- watching was started. 		top <- liftAnnex $ fromRepo Git.repoPath-		(fs, cleanup) <- liftAnnex $ inRepo $ LsFiles.deleted [top]+		(fs, cleanup) <- liftAnnex $ inRepo $ LsFiles.deleted [] [top] 		forM_ fs $ \f -> do 			let f' = fromRawFilePath f 			liftAnnex $ onDel' f'@@ -362,7 +362,7 @@ onDelDir :: Handler onDelDir dir _ = do 	debug ["directory deleted", dir]-	(fs, clean) <- liftAnnex $ inRepo $ LsFiles.deleted [toRawFilePath dir]+	(fs, clean) <- liftAnnex $ inRepo $ LsFiles.deleted [] [toRawFilePath dir] 	let fs' = map fromRawFilePath fs  	liftAnnex $ mapM_ onDel' fs'
Assistant/WebApp/Configurators/AWS.hs view
@@ -43,13 +43,14 @@   where 	needglaciercli = $(widgetFile "configurators/needglaciercli") -data StorageClass = StandardRedundancy | StandardInfrequentAccess | ReducedRedundancy+data StorageClass+	= StandardRedundancy+	| StandardInfrequentAccess 	deriving (Eq, Enum, Bounded)  instance Show StorageClass where 	show StandardRedundancy = "STANDARD"  	show StandardInfrequentAccess = "STANDARD_IA"-	show ReducedRedundancy = "REDUCED_REDUNDANCY"  data AWSInput = AWSInput 	{ accessKeyID :: Text@@ -78,10 +79,7 @@ 	storageclasses :: [(Text, StorageClass)] 	storageclasses = 		[ ("Standard redundancy", StandardRedundancy)-#ifdef WITH_S3 		, ("Infrequent access (cheaper for backups and archives)", StandardInfrequentAccess)-#endif-		, ("Reduced redundancy (costs less)", ReducedRedundancy) 		]  glacierInputAForm :: Maybe CredPair -> MkAForm AWSInput
Assistant/WebApp/Configurators/Edit.hs view
@@ -25,7 +25,7 @@ #endif import qualified Remote import qualified Types.Remote as Remote-import qualified Remote.List as Remote+import Remote.List.Util import Logs.UUID import Logs.Group import Logs.PreferredContent@@ -116,7 +116,7 @@ 				, Param $ T.unpack $ repoName oldc 				, Param name 				]-			void Remote.remoteListRefresh+			remotesChanged 		liftAssistant updateSyncRemotes 	when associatedDirectoryChanged $ case repoAssociatedDirectory newc of 		Nothing -> noop@@ -309,7 +309,7 @@ 			setConfig 				(remoteAnnexConfig repo "ignore") 				(Git.Config.boolConfig False)-		liftAnnex $ void Remote.remoteListRefresh+		liftAnnex remotesChanged 		liftAssistant updateSyncRemotes 		liftAssistant $ syncRemote rmt 		redirect DashboardR
Assistant/WebApp/DashBoard.hs view
@@ -137,8 +137,8 @@ 	ifM (liftIO $ inPath cmd) 		( do 			let run = void $ liftIO $ forkIO $ do-				(Nothing, Nothing, Nothing, pid) <- createProcess p-				void $ waitForProcess pid+				withCreateProcess p $ \_ _ _ pid -> void $+					waitForProcess pid 			run #ifdef mingw32_HOST_OS 			{- On windows, if the file browser is not
Assistant/WebApp/RepoList.hs view
@@ -14,7 +14,7 @@ import Assistant.WebApp.Notifications import qualified Remote import qualified Types.Remote as Remote-import Remote.List (remoteListRefresh)+import Remote.List.Util import Annex.UUID (getUUID) import Logs.Remote import Logs.Trust@@ -242,7 +242,7 @@ 			when (Remote.cost r /= newcost) $ do 				repo <- Remote.getRepo r 				setRemoteCost repo newcost-		void remoteListRefresh+		remotesChanged 	fromjs = fromMaybe (RepoUUID NoUUID) . readish . T.unpack  reorderCosts :: Remote -> [Remote] -> [(Remote, Cost)]
BuildFlags.hs view
@@ -62,6 +62,9 @@ #ifdef WITH_MAGICMIME 	, "MagicMime" #endif+#ifdef DEBUGLOCKS+	, "DebugLocks"+#endif 	-- Always enabled now, but users may be used to seeing these flags 	-- listed. 	, "Feeds"@@ -89,7 +92,7 @@ #endif #ifdef WITH_WEBAPP 	, ("yesod", VERSION_yesod)-#endif +#endif #ifdef TOOL_VERSION_ghc 	, ("ghc", TOOL_VERSION_ghc) #endif
CHANGELOG view
@@ -1,3 +1,66 @@+git-annex (8.20200617) upstream; urgency=medium++  * Added annex.skipunknown git config, that can be set to false to change+    the behavior of commands like `git annex get foo*`, to not skip+    over files/dirs that are not checked into git and are explicitly listed in+    the command line.+  * annex.skipunknown is planned to change to default to false in a+    git-annex release in early 2022. If you prefer the current behavior,+    you can explicitly set it to true.+  * Try to enable special remotes configured with autoenable=yes+    when git-annex auto-initialization happens in a new clone of an+    existing repo. Previously, git-annex init had to be explicitly run to+    enable them. Special remotes cannot display anything when autoenabled+    this way, to avoid interfering with the output of git-annex query+    commands.+  * export: Added options for json output.+  * import: Added --json-progress.+  * addurl: Make --preserve-filename also apply when eg a torrent contains+    multiple files.+  * Fix a crash or potentially not all files being exported when +    sync -J --content is used with an export remote.+  * export: Let concurrent transfers be done with -J or annex.jobs.+  * move --to, copy --to, mirror --to: When concurrency is enabled, run+    cleanup actions in separate job pool from uploads. +  * init: If lock probing stalls for a long time (eg a broken NFS server),+    display a message to let the user know what's taking so long.+  * init: When annex.pidlock is set, skip lock probing.+  * Fix file descriptor leak when importing from a directory special remote+    that is configured with exporttree=yes.+  * Note that external special remote programs should not block SIGINT or+    SIGTERM.+  * Avoid creating the keys database during init when there are no unlocked+    files, to prevent init failing when sqlite does not work in the+    filesystem.+  * import: Avoid using some strange names for temporary keys,+    which broke importing from a directory special remote onto a vfat+    filesystem.+  * S3: The REDUCED_REDUNDANCY storage class is no longer cheaper so+    stop documenting it, and stop offering it as a choice in the assistant.+  * Improve display of problems auto-initializing or upgrading local git+    remotes.+  * When a local git remote cannot be initialized because it has no+    git-annex branch or a .noannex file, avoid displaying a message about it.+  * checkpresentkey: When no remote is specified, try all remotes, not+    only ones that the location log says contain the key. This is what+    the documentation has always said it did.+  * Fix regression in external special remote handling: GETCONFIG did not+    return a value that was set with SETCONFIG immediately before.+    (Regression introduced in version 7.20200202.7)+  * Fix bug that made initremote of extrnal special remotes with+    embedcreds=yes or gpg encryption not store the creds in the git-annex+    branch. git-annex-remote-googledrive one was special remote affected by+    this bug.+    (Regression introduced in version 7.20200202.7)+  * Fix bug that made creds not be stored in git when a special remote+    was initialized with gpg encryption, but without an explicit+    embedcreds=yes.+    (Regression introduced in version 7.20200202.7)+  * Fix a annex.pidlock issue that made eg git-annex get of an unlocked+    file hang until the annex.pidlocktimeout and then fail.++ -- Joey Hess <id@joeyh.name>  Wed, 17 Jun 2020 15:58:59 -0400+ git-annex (8.20200522) upstream; urgency=medium    * Fix bug that made enableremote of S3 and webdav remotes, that
@@ -15,7 +15,7 @@ License: GPL-3+  Files: Remote/Ddar.hs-Copyright: © 2011 Joey Hess <id@joeyh.name>+Copyright: © 2011-2020 Joey Hess <id@joeyh.name>            © 2014 Robie Basak <robie@justgohome.co.uk> License: GPL-3+ 
CmdLine.hs view
@@ -14,7 +14,6 @@  import qualified Options.Applicative as O import qualified Options.Applicative.Help as H-import qualified Control.Exception as E import Control.Exception (throw)  import Annex.Common@@ -31,7 +30,7 @@ dispatch :: Bool -> CmdParams -> [Command] -> [GlobalOption] -> [(String, String)] -> IO Git.Repo -> String -> String -> IO () dispatch fuzzyok allargs allcmds globaloptions fields getgitrepo progname progdesc = do 	setupConsole-	go =<< (E.try getgitrepo :: IO (Either E.SomeException Git.Repo))+	go =<< tryNonAsync getgitrepo   where 	go (Right g) = do 		state <- Annex.new g
CmdLine/Action.hs view
@@ -1,6 +1,6 @@ {- git-annex command-line actions and concurrency  -- - Copyright 2010-2019 Joey Hess <id@joeyh.name>+ - Copyright 2010-2020 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU AGPL version 3 or higher.  -}@@ -135,6 +135,15 @@ 				then return (spareVals pool) 				else retry 		mapM_ mergeState sts++{- Waits for all worker threads that have been started so far to finish. -}+waitForAllRunningCommandActions :: Annex ()+waitForAllRunningCommandActions = Annex.getState Annex.workers >>= \case+	Nothing -> noop+	Just tv -> liftIO $ atomically $ do+		pool <- readTMVar tv+		unless (allIdle pool)+			retry  {- Like commandAction, but without the concurrency. -} includeCommandAction :: CommandStart -> CommandCleanup
CmdLine/Seek.hs view
@@ -4,7 +4,7 @@  - the values a user passes to a command, and prepare actions operating  - on them.  -- - Copyright 2010-2018 Joey Hess <id@joeyh.name>+ - Copyright 2010-2020 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU AGPL version 3 or higher.  -}@@ -35,13 +35,13 @@ import qualified Database.Keys import qualified Utility.RawFilePath as R -withFilesInGit :: (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek-withFilesInGit a l = seekActions $ prepFiltered a $-	seekHelper LsFiles.inRepo l+withFilesInGit :: WarnUnmatchWhen -> (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek+withFilesInGit ww a l = seekActions $ prepFiltered a $+	seekHelper ww LsFiles.inRepo l -withFilesInGitNonRecursive :: String -> (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek-withFilesInGitNonRecursive needforce a l = ifM (Annex.getState Annex.force)-	( withFilesInGit a l+withFilesInGitNonRecursive :: WarnUnmatchWhen -> String -> (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek+withFilesInGitNonRecursive ww needforce a l = ifM (Annex.getState Annex.force)+	( withFilesInGit ww a l 	, if null l 		then giveup needforce 		else seekActions $ prepFiltered a (getfiles [] l)@@ -49,7 +49,8 @@   where 	getfiles c [] = return (reverse c) 	getfiles c ((WorkTreeItem p):ps) = do-		(fs, cleanup) <- inRepo $ LsFiles.inRepo [toRawFilePath p]+		os <- seekOptions ww+		(fs, cleanup) <- inRepo $ LsFiles.inRepo os [toRawFilePath p] 		case fs of 			[f] -> do 				void $ liftIO $ cleanup@@ -66,7 +67,7 @@ 		force <- Annex.getState Annex.force 		g <- gitRepo 		liftIO $ Git.Command.leaveZombie-			<$> LsFiles.notInRepo force (map (\(WorkTreeItem f) -> toRawFilePath f) l) g+			<$> LsFiles.notInRepo [] force (map (\(WorkTreeItem f) -> toRawFilePath f) l) g 	go fs = seekActions $ prepFiltered a $ 		return $ concat $ segmentPaths (map (\(WorkTreeItem f) -> toRawFilePath f) l) fs @@ -104,7 +105,7 @@  withFilesToBeCommitted :: (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek withFilesToBeCommitted a l = seekActions $ prepFiltered a $-	seekHelper LsFiles.stagedNotDeleted l+	seekHelper WarnUnmatchWorkTreeItems (const LsFiles.stagedNotDeleted) l  isOldUnlocked :: RawFilePath -> Annex Bool isOldUnlocked f = liftIO (notSymlink f) <&&> @@ -112,12 +113,12 @@  {- unlocked pointer files that are staged, and whose content has not been  - modified-}-withUnmodifiedUnlockedPointers :: (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek-withUnmodifiedUnlockedPointers a l = seekActions $+withUnmodifiedUnlockedPointers :: WarnUnmatchWhen -> (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek+withUnmodifiedUnlockedPointers ww a l = seekActions $ 	prepFiltered a unlockedfiles   where 	unlockedfiles = filterM isUnmodifiedUnlocked -		=<< seekHelper LsFiles.typeChangedStaged l+		=<< seekHelper ww (const LsFiles.typeChangedStaged) l  isUnmodifiedUnlocked :: RawFilePath -> Annex Bool isUnmodifiedUnlocked f = catKeyFile f >>= \case@@ -125,9 +126,9 @@ 	Just k -> sameInodeCache f =<< Database.Keys.getInodeCaches k  {- Finds files that may be modified. -}-withFilesMaybeModified :: (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek-withFilesMaybeModified a params = seekActions $-	prepFiltered a $ seekHelper LsFiles.modified params+withFilesMaybeModified :: WarnUnmatchWhen -> (RawFilePath -> CommandSeek) -> [WorkTreeItem] -> CommandSeek+withFilesMaybeModified ww a params = seekActions $+	prepFiltered a $ seekHelper ww LsFiles.modified params  withKeys :: (Key -> CommandSeek) -> CmdParams -> CommandSeek withKeys a l = seekActions $ return $ map (a . parse) l@@ -228,13 +229,25 @@ seekActions :: Annex [CommandSeek] -> Annex () seekActions gen = sequence_ =<< gen -seekHelper :: ([RawFilePath] -> Git.Repo -> IO ([RawFilePath], IO Bool)) -> [WorkTreeItem] -> Annex [RawFilePath]-seekHelper a l = inRepo $ \g ->-	concat . concat <$> forM (segmentXargsOrdered l')-		(runSegmentPaths (\fs -> Git.Command.leaveZombie <$> a fs g) . map toRawFilePath)+seekHelper :: WarnUnmatchWhen -> ([LsFiles.Options] -> [RawFilePath] -> Git.Repo -> IO ([RawFilePath], IO Bool)) -> [WorkTreeItem] -> Annex [RawFilePath]+seekHelper ww a l = do+	os <- seekOptions ww+	inRepo $ \g ->+		concat . concat <$> forM (segmentXargsOrdered l')+			(runSegmentPaths (\fs -> Git.Command.leaveZombie <$> a os fs g) . map toRawFilePath)   where 	l' = map (\(WorkTreeItem f) -> f) l +data WarnUnmatchWhen = WarnUnmatchLsFiles | WarnUnmatchWorkTreeItems++seekOptions :: WarnUnmatchWhen -> Annex [LsFiles.Options]+seekOptions WarnUnmatchLsFiles =+	ifM (annexSkipUnknown <$> Annex.getGitConfig)+		( return [] +		, return [LsFiles.ErrorUnmatch]+		)+seekOptions WarnUnmatchWorkTreeItems = return []+ -- An item in the work tree, which may be a file or a directory. newtype WorkTreeItem = WorkTreeItem FilePath @@ -243,30 +256,42 @@ -- seeking for such files. newtype AllowHidden = AllowHidden Bool --- Many git commands like ls-files seek work tree items matching some criteria,--- and silently skip over anything that does not exist. But users expect--- an error message when one of the files they provided as a command-line--- parameter doesn't exist, so this checks that each exists.---+-- git ls-files without --error-unmatch seeks work tree items matching+-- some criteria, and silently skips over anything that does not exist.+ -- Also, when two directories are symlinked, referring to a file--- inside the symlinked directory will be silently skipped by git commands--- like ls-files. But, the user would be surprised for it to be skipped, so--- check if the parent directories are symlinks.-workTreeItems :: CmdParams -> Annex [WorkTreeItem]+-- inside the symlinked directory will be silently skipped by+-- git ls-files without --error-unmatch. +--+-- Sometimes a command needs to use git-lsfiles that way, perhaps repeatedly.+-- But users expect an error message when one of the files they provided+-- as a command-line parameter doesn't exist, so this checks that each+-- exists when run with WarnUnmatchWorkTreeItems.+--+-- Note that, unlike --error-unmatch, using this does not warn+-- about command-line parameters that exist, but are not checked into git.+workTreeItems :: WarnUnmatchWhen -> CmdParams -> Annex [WorkTreeItem] workTreeItems = workTreeItems' (AllowHidden False) -workTreeItems' :: AllowHidden -> CmdParams -> Annex [WorkTreeItem]-workTreeItems' (AllowHidden allowhidden) ps = do-	currbranch <- getCurrentBranch-	forM_ ps $ \p -> do-		relf <- liftIO $ relPathCwdToFile p-		ifM (not <$> (exists p <||> hidden currbranch relf))-			( prob (p ++ " not found")-			, whenM (viasymlink (upFrom relf)) $-				prob (p ++ " is beyond a symbolic link")-			)-	return (map (WorkTreeItem) ps)+workTreeItems' :: AllowHidden -> WarnUnmatchWhen -> CmdParams -> Annex [WorkTreeItem]+workTreeItems' (AllowHidden allowhidden) ww ps = do+	case ww of+		WarnUnmatchWorkTreeItems -> runcheck+		WarnUnmatchLsFiles -> +			whenM (annexSkipUnknown <$> Annex.getGitConfig)+				runcheck+	return (map WorkTreeItem ps)   where+	runcheck = do+		currbranch <- getCurrentBranch+		forM_ ps $ \p -> do+			relf <- liftIO $ relPathCwdToFile p+			ifM (not <$> (exists p <||> hidden currbranch relf))+				( prob (p ++ " not found")+				, whenM (viasymlink (upFrom relf)) $+					prob (p ++ " is beyond a symbolic link")+				)+	 	exists p = isJust <$> liftIO (catchMaybeIO $ getSymbolicLinkStatus p)  	viasymlink Nothing = return False
Command/Add.hs view
@@ -82,10 +82,15 @@ 				giveup "--update --batch is not supported" 			| otherwise -> batchFilesMatching fmt (gofile . toRawFilePath) 		NoBatch -> do-			l <- workTreeItems (addThese o)-			let go a = a (commandAction . gofile) l+			-- Avoid git ls-files complaining about files that+			-- are not known to git yet, since this will add+			-- them. Instead, have workTreeItems warn about other+			-- problems, like files that don't exist.+			let ww = WarnUnmatchWorkTreeItems+			l <- workTreeItems ww (addThese o)+			let go a = a ww (commandAction . gofile) l 			unless (updateOnly o) $-				go withFilesNotInGit+				go (const withFilesNotInGit) 			go withFilesMaybeModified 			go withUnmodifiedUnlockedPointers 
Command/AddUrl.hs view
@@ -144,7 +144,7 @@ 		Nothing -> 			forM_ l $ \(u', sz, f) -> do 				f' <- sanitizeOrPreserveFilePath o f-				let f'' = adjustFile o (deffile </> sanitizeFilePath f')+				let f'' = adjustFile o (deffile </> f') 				void $ commandAction $ startRemote addunlockedmatcher r o f'' u' sz 		Just f -> case l of 			[] -> noop
Command/CheckPresentKey.hs view
@@ -9,6 +9,7 @@  import Command import qualified Remote+import Remote.List  cmd :: Command cmd = noCommit $ noMessages $@@ -46,8 +47,13 @@  check :: String -> Maybe Remote -> Annex Result check ks mr = case mr of-	Nothing -> go Nothing =<< Remote.keyPossibilities k 	Just r -> go Nothing [r]+	Nothing -> do+		mostlikely <- Remote.keyPossibilities k+		otherremotes <- flip Remote.remotesWithoutUUID +			(map Remote.uuid mostlikely)+			<$> remoteList+		go Nothing (mostlikely ++ otherremotes)   where 	k = toKey ks 	go Nothing [] = return NotPresent
Command/Copy.hs view
@@ -51,8 +51,10 @@ 		NoBatch -> withKeyOptions 			(keyOptions o) (autoMode o) 			(commandAction . Command.Move.startKey (fromToOptions o) Command.Move.RemoveNever)-			(withFilesInGit $ commandAction . go)-			=<< workTreeItems (copyFiles o)+			(withFilesInGit ww $ commandAction . go)+			=<< workTreeItems ww (copyFiles o)+  where+	ww = WarnUnmatchLsFiles  {- A copy is just a move that does not delete the source file.  - However, auto mode avoids unnecessary copies, and avoids getting or
Command/Drop.hs view
@@ -52,15 +52,16 @@ 	)  seek :: DropOptions -> CommandSeek-seek o = startConcurrency transferStages $+seek o = startConcurrency commandStages $ 	case batchOption o of 		Batch fmt -> batchFilesMatching fmt (go . toRawFilePath) 		NoBatch -> withKeyOptions (keyOptions o) (autoMode o) 			(commandAction . startKeys o)-			(withFilesInGit (commandAction . go))-			=<< workTreeItems (dropFiles o)+			(withFilesInGit ww (commandAction . go))+			=<< workTreeItems ww (dropFiles o)   where 	go = whenAnnexed $ start o+	ww = WarnUnmatchLsFiles  start :: DropOptions -> RawFilePath -> Key -> CommandStart start o file key = start' o key afile ai
Command/Export.hs view
@@ -44,9 +44,10 @@ import Control.Concurrent  cmd :: Command-cmd = command "export" SectionCommon-	"export content to a remote"-	paramTreeish (seek <$$> optParser)+cmd = withGlobalOptions [jobsOption, jsonOptions, jsonProgressOption] $+	command "export" SectionCommon+		"export content to a remote"+		paramTreeish (seek <$$> optParser)  data ExportOptions = ExportOptions 	{ exportTreeish :: Git.Ref@@ -76,7 +77,7 @@ 	".git-annex-tmp-content-" ++ serializeKey (asKey (ek))  seek :: ExportOptions -> CommandSeek-seek o = do+seek o = startConcurrency commandStages $ do 	r <- getParsed (exportRemote o) 	unlessM (isExportSupported r) $ 		giveup "That remote does not support exports."@@ -135,6 +136,7 @@ 			(Git.DiffTree.file diff) 	forM_ (incompleteExportedTreeishes old) $ \incomplete -> 		mapdiff recover incomplete new+	waitForAllRunningCommandActions  	-- Diff the old and new trees, and delete or rename to new name all 	-- changed files in the export. After this, every file that remains@@ -157,12 +159,14 @@ 					(Just oldf, Nothing) -> 						startUnexport' r db oldf ek 					_ -> stop+			waitForAllRunningCommandActions 			-- Rename from temp to new files. 			seekdiffmap $ \(ek, (moldf, mnewf)) -> 				case (moldf, mnewf) of 					(Just _oldf, Just newf) -> 						startMoveFromTempName r db ek newf 					_ -> stop+			waitForAllRunningCommandActions 		ts -> do 			warning "Resolving export conflict.." 			forM_ ts $ \oldtreesha -> do@@ -180,6 +184,7 @@ 					(\diff -> commandAction $ startUnexport r db (Git.DiffTree.file diff) (unexportboth diff)) 					oldtreesha new 			updateExportTree db emptyTree new+			waitForAllRunningCommandActions 	liftIO $ recordExportTreeCurrent db new  	-- Waiting until now to record the export guarantees that,@@ -238,6 +243,7 @@ 	allfilledvar <- liftIO $ newMVar (AllFilled True) 	commandActions $ map (startExport r db cvar allfilledvar) l 	void $ liftIO $ cleanup+	waitForAllRunningCommandActions  	case mtbcommitsha of 		Nothing -> noop@@ -483,3 +489,4 @@ 					) 			-- Always export non-annexed files. 			Nothing -> return (Just ti)+
Command/Find.hs view
@@ -57,11 +57,12 @@ seek o = case batchOption o of 	NoBatch -> withKeyOptions (keyOptions o) False 		(commandAction . startKeys o)-		(withFilesInGit (commandAction . go))-		=<< workTreeItems (findThese o)+		(withFilesInGit ww (commandAction . go))+		=<< workTreeItems ww (findThese o) 	Batch fmt -> batchFilesMatching fmt (go . toRawFilePath)   where 	go = whenAnnexed $ start o+	ww = WarnUnmatchLsFiles  -- only files inAnnex are shown, unless the user has requested -- others via a limit
Command/Fix.hs view
@@ -32,9 +32,11 @@  seek :: CmdParams -> CommandSeek seek ps = unlessM crippledFileSystem $ do -	withFilesInGit+	withFilesInGit ww 		(commandAction . (whenAnnexed $ start FixAll))-		=<< workTreeItems ps+		=<< workTreeItems ww ps+  where+	ww = WarnUnmatchLsFiles  data FixWhat = FixSymlinks | FixAll 
Command/Fsck.hs view
@@ -94,10 +94,12 @@ 	i <- prepIncremental u (incrementalOpt o) 	withKeyOptions (keyOptions o) False 		(\kai -> commandAction . startKey from i kai =<< getNumCopies)-		(withFilesInGit $ commandAction . (whenAnnexed (start from i)))-		=<< workTreeItems (fsckFiles o)+		(withFilesInGit ww $ commandAction . (whenAnnexed (start from i)))+		=<< workTreeItems ww (fsckFiles o) 	cleanupIncremental i 	void $ tryIO $ recordActivity Fsck u+  where+	ww = WarnUnmatchLsFiles  checkDeadRepo :: UUID -> Annex () checkDeadRepo u =
Command/Get.hs view
@@ -38,15 +38,17 @@ 	<*> parseBatchOption  seek :: GetOptions -> CommandSeek-seek o = startConcurrency transferStages $ do+seek o = startConcurrency downloadStages $ do 	from <- maybe (pure Nothing) (Just <$$> getParsed) (getFrom o) 	let go = whenAnnexed $ start o from 	case batchOption o of 		Batch fmt -> batchFilesMatching fmt (go . toRawFilePath) 		NoBatch -> withKeyOptions (keyOptions o) (autoMode o) 			(commandAction . startKeys from)-			(withFilesInGit (commandAction . go))-			=<< workTreeItems (getFiles o)+			(withFilesInGit ww (commandAction . go))+			=<< workTreeItems ww (getFiles o)+  where+	ww = WarnUnmatchLsFiles  start :: GetOptions -> Maybe Remote -> RawFilePath -> Key -> CommandStart start o from file key = start' expensivecheck from key afile ai
Command/Import.hs view
@@ -38,7 +38,7 @@  cmd :: Command cmd = notBareRepo $-	withGlobalOptions [jobsOption, jsonOptions, fileMatchingOptions] $+	withGlobalOptions [jobsOption, jsonOptions, jsonProgressOption, fileMatchingOptions] $ 		command "import" SectionCommon  			"add a tree of files to the repository" 			(paramPaths ++ "|BRANCH[:SUBDIR]")
Command/Init.hs view
@@ -1,6 +1,6 @@ {- git-annex command  -- - Copyright 2010 Joey Hess <id@joeyh.name>+ - Copyright 2010-2020 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU AGPL version 3 or higher.  -}@@ -24,6 +24,7 @@ data InitOptions = InitOptions 	{ initDesc :: String 	, initVersion :: Maybe RepoVersion+	, autoEnableOnly :: Bool 	}  optParser :: CmdParamsDesc -> Parser InitOptions@@ -33,6 +34,10 @@ 		( long "version" <> metavar paramValue 		<> help "Override default annex.version" 		))+	<*> switch+		( long "autoenable"+		<> help "only enable special remotes configured with autoenable=true"+		)  parseRepoVersion :: MonadFail m => String -> m RepoVersion parseRepoVersion s = case RepoVersion <$> readish s of@@ -47,8 +52,11 @@ seek = commandAction . start  start :: InitOptions -> CommandStart-start os = starting "init" (ActionItemOther (Just $ initDesc os)) $-	perform os+start os+	| autoEnableOnly os = starting "init" (ActionItemOther (Just "autoenable")) $+		performAutoEnableOnly+	| otherwise = starting "init" (ActionItemOther (Just $ initDesc os)) $+		perform os  perform :: InitOptions -> CommandPerform perform os = do@@ -61,5 +69,10 @@ 	initialize 		(if null (initDesc os) then Nothing else Just (initDesc os)) 		(initVersion os)+	Annex.SpecialRemote.autoEnable+	next $ return True++performAutoEnableOnly :: CommandPerform+performAutoEnableOnly = do 	Annex.SpecialRemote.autoEnable 	next $ return True
Command/Inprogress.hs view
@@ -38,9 +38,11 @@ 			| otherwise -> commandAction stop 		_ -> do 			let s = S.fromList ts-			withFilesInGit+			withFilesInGit ww 				(commandAction . (whenAnnexed (start s)))-				=<< workTreeItems (inprogressFiles o)+				=<< workTreeItems ww (inprogressFiles o)+  where+	ww = WarnUnmatchLsFiles  start :: S.Set Key -> RawFilePath -> Key -> CommandStart start s _file k
Command/List.hs view
@@ -44,9 +44,10 @@ seek o = do 	list <- getList o 	printHeader list-	withFilesInGit-		(commandAction . (whenAnnexed $ start list))-		=<< workTreeItems (listThese o)+	withFilesInGit ww (commandAction . (whenAnnexed $ start list))+		=<< workTreeItems ww (listThese o)+  where+	ww = WarnUnmatchLsFiles  getList :: ListOptions -> Annex [(UUID, RemoteName, TrustLevel)] getList o
Command/Lock.hs view
@@ -30,8 +30,10 @@  seek :: CmdParams -> CommandSeek seek ps = do-	l <- workTreeItems ps-	withFilesInGit (commandAction . (whenAnnexed startNew)) l+	l <- workTreeItems ww ps+	withFilesInGit ww (commandAction . (whenAnnexed startNew)) l+  where+	ww = WarnUnmatchLsFiles  startNew :: RawFilePath -> Key -> CommandStart startNew file key = ifM (isJust <$> isAnnexLink file)
Command/Log.hs view
@@ -86,11 +86,13 @@ 	zone <- liftIO getCurrentTimeZone 	let outputter = mkOutputter m zone o 	case (logFiles o, allOption o) of-		(fs, False) -> withFilesInGit+		(fs, False) -> withFilesInGit ww 			(commandAction . (whenAnnexed $ start o outputter)) -			=<< workTreeItems fs+			=<< workTreeItems ww fs 		([], True) -> commandAction (startAll o outputter) 		(_, True) -> giveup "Cannot specify both files and --all"+  where+	ww = WarnUnmatchLsFiles  start :: LogOptions -> (FilePath -> Outputter) -> RawFilePath -> Key -> CommandStart start o outputter file key = do
Command/LookupKey.hs view
@@ -31,7 +31,7 @@ -- But, this plumbing command does not recurse through directories. seekSingleGitFile :: FilePath -> Annex (Maybe RawFilePath) seekSingleGitFile file = do-	(l, cleanup) <- inRepo (Git.LsFiles.inRepo [toRawFilePath file])+	(l, cleanup) <- inRepo (Git.LsFiles.inRepo [] [toRawFilePath file]) 	r <- case l of 		(f:[]) | takeFileName (fromRawFilePath f) == takeFileName file -> 			return (Just f)
Command/Map.hs view
@@ -223,10 +223,16 @@ 	| otherwise = liftIO $ safely $ Git.Config.read r   where 	pipedconfig st pcmd params = liftIO $ safely $-		withHandle StdoutHandle createProcessSuccess p $-			Git.Config.hRead r st+		withCreateProcess p (pipedconfig' st p) 	  where-		p = proc pcmd $ toCommand params+		p = (proc pcmd $ toCommand params)+			{ std_out = CreatePipe }++	pipedconfig' st p _ (Just h) _ pid = +		forceSuccessProcess p pid+			`after`+		Git.Config.hRead r st h+	pipedconfig' _ _ _ _ _ _ = error "internal"  	configlist = Ssh.onRemote NoConsumeStdin r 		(pipedconfig Git.Config.ConfigList, return Nothing) "configlist" [] []
Command/MetaData.hs view
@@ -75,15 +75,16 @@ seek o = case batchOption o of 	NoBatch -> do 		c <- liftIO currentVectorClock+		let ww = WarnUnmatchLsFiles 		let seeker = case getSet o of-			Get _ -> withFilesInGit-			GetAll -> withFilesInGit-			Set _ -> withFilesInGitNonRecursive+			Get _ -> withFilesInGit ww+			GetAll -> withFilesInGit ww+			Set _ -> withFilesInGitNonRecursive ww 				"Not recursively setting metadata. Use --force to do that." 		withKeyOptions (keyOptions o) False 			(commandAction . startKeys c o) 			(seeker (commandAction . (whenAnnexed (start c o))))-			=<< workTreeItems (forFiles o)+			=<< workTreeItems ww (forFiles o) 	Batch fmt -> withMessageState $ \s -> case outputType s of 		JSONOutput _ -> ifM limited 			( giveup "combining --batch with file matching options is not currently supported"
Command/Migrate.hs view
@@ -26,7 +26,10 @@ 		paramPaths (withParams seek)  seek :: CmdParams -> CommandSeek-seek = withFilesInGit (commandAction . (whenAnnexed start)) <=< workTreeItems+seek = withFilesInGit ww (commandAction . (whenAnnexed start))+	<=< workTreeItems ww+  where+	ww = WarnUnmatchLsFiles  start :: RawFilePath -> Key -> CommandStart start file key = do
Command/Mirror.hs view
@@ -41,11 +41,16 @@ 		<*> pure (keyOptions v)  seek :: MirrorOptions -> CommandSeek-seek o = startConcurrency transferStages $ +seek o = startConcurrency stages $  	withKeyOptions (keyOptions o) False 		(commandAction . startKey o (AssociatedFile Nothing))-		(withFilesInGit (commandAction . (whenAnnexed $ start o)))-		=<< workTreeItems (mirrorFiles o)+		(withFilesInGit ww (commandAction . (whenAnnexed $ start o)))+		=<< workTreeItems ww (mirrorFiles o)+  where+	stages = case fromToOptions o of+		FromRemote _ -> downloadStages+		ToRemote _ -> commandStages+	ww = WarnUnmatchLsFiles  start :: MirrorOptions -> RawFilePath -> Key -> CommandStart start o file k = startKey o afile (k, ai)
Command/Move.hs view
@@ -54,14 +54,20 @@ 	deriving (Show, Eq)  seek :: MoveOptions -> CommandSeek-seek o = startConcurrency transferStages $ do+seek o = startConcurrency stages $ do 	let go = whenAnnexed $ start (fromToOptions o) (removeWhen o) 	case batchOption o of 		Batch fmt -> batchFilesMatching fmt (go . toRawFilePath) 		NoBatch -> withKeyOptions (keyOptions o) False 			(commandAction . startKey (fromToOptions o) (removeWhen o))-			(withFilesInGit (commandAction . go))-			=<< workTreeItems (moveFiles o)+			(withFilesInGit ww (commandAction . go))+			=<< workTreeItems ww (moveFiles o)+  where+	stages = case fromToOptions o of+		Right (FromRemote _) -> downloadStages+		Right (ToRemote _) -> commandStages+		Left ToHere -> downloadStages+	ww = WarnUnmatchLsFiles  start :: FromToHereOptions -> RemoveWhen -> RawFilePath -> Key -> CommandStart start fromto removewhen f k = start' fromto removewhen afile k ai
Command/Multicast.hs view
@@ -129,7 +129,9 @@ 	-- expensive. 	starting "sending files" (ActionItemOther Nothing) $ 		withTmpFile "send" $ \t h -> do-			fs' <- seekHelper LsFiles.inRepo =<< workTreeItems fs+			let ww = WarnUnmatchLsFiles+			fs' <- seekHelper ww LsFiles.inRepo+				=<< workTreeItems ww fs 			matcher <- Limit.getMatcher 			let addlist f o = whenM (matcher $ MatchingFile $ FileInfo f f) $ 				liftIO $ hPutStrLn h o
Command/PreCommit.hs view
@@ -32,7 +32,8 @@  seek :: CmdParams -> CommandSeek seek ps = do-	l <- workTreeItems ps+	let ww = WarnUnmatchWorkTreeItems+	l <- workTreeItems ww ps 	-- fix symlinks to files being committed 	flip withFilesToBeCommitted l $ \f -> commandAction $ 		maybe stop (Command.Fix.start Command.Fix.FixSymlinks f)
Command/Sync.hs view
@@ -65,6 +65,7 @@ import Annex.Export import Annex.TaggedPush import Annex.CurrentBranch+import Annex.GitOverlay import qualified Database.Export as Export import Utility.Bloom import Utility.OptParse@@ -194,7 +195,7 @@ seek :: SyncOptions -> CommandSeek seek o = do 	prepMerge-	startConcurrency transferStages (seek' o)+	startConcurrency downloadStages (seek' o) 	 seek' :: SyncOptions -> CommandSeek seek' o = do@@ -513,7 +514,7 @@ 	postpushupdate repo = case Git.repoWorkTree repo of 		Nothing -> return True 		Just wt -> ifM needemulation-			( liftIO $ do+			( runsGitAnnexChildProcess $ liftIO $ do 				p <- programPath 				boolSystem' p [Param "post-receive"] 					(\cp -> cp { cwd = Just (fromRawFilePath wt) })@@ -636,27 +637,29 @@ 		Just WantAllKeys -> Just <$> genBloomFilter (seekworktree mvar []) 		_ -> case currbranch of                 	(Just origbranch, Just adj) | adjustmentHidesFiles adj -> do-				l <- workTreeItems' (AllowHidden True) (contentOfOption o)+				l <- workTreeItems' (AllowHidden True) ww (contentOfOption o) 				seekincludinghidden origbranch mvar l (const noop) 				pure Nothing 			_ -> do-				l <- workTreeItems (contentOfOption o)+				l <- workTreeItems ww (contentOfOption o) 				seekworktree mvar l (const noop) 				pure Nothing 	withKeyOptions' (keyOptions o) False 		(return (gokey mvar bloom)) 		(const noop) 		[]-	finishCommandActions+	waitForAllRunningCommandActions 	liftIO $ not <$> isEmptyMVar mvar   where 	seekworktree mvar l bloomfeeder = -		seekHelper LsFiles.inRepo l+		seekHelper ww LsFiles.inRepo l 			>>= gofiles bloomfeeder mvar  	seekincludinghidden origbranch mvar l bloomfeeder = -		seekHelper (LsFiles.inRepoOrBranch origbranch) l +		seekHelper ww (LsFiles.inRepoOrBranch origbranch) l  			>>= gofiles bloomfeeder mvar++	ww = WarnUnmatchLsFiles  	gofiles bloomfeeder mvar = mapM_ $ \f -> 		ifAnnexed f
Command/Unannex.hs view
@@ -23,7 +23,10 @@ 		paramPaths (withParams seek)  seek :: CmdParams -> CommandSeek-seek ps = (withFilesInGit $ commandAction . whenAnnexed start) =<< workTreeItems ps+seek ps = (withFilesInGit ww $ commandAction . whenAnnexed start)+	=<< workTreeItems ww ps+  where+	ww = WarnUnmatchLsFiles  start :: RawFilePath -> Key -> CommandStart start file key = stopUnless (inAnnex key) $
Command/Undo.hs view
@@ -27,7 +27,7 @@ seek ps = do 	-- Safety first; avoid any undo that would touch files that are not 	-- in the index.-	(fs, cleanup) <- inRepo $ LsFiles.notInRepo False (map toRawFilePath ps)+	(fs, cleanup) <- inRepo $ LsFiles.notInRepo [] False (map toRawFilePath ps) 	unless (null fs) $ 		giveup $ "Cannot undo changes to files that are not checked into git: " ++ unwords (map fromRawFilePath fs) 	void $ liftIO $ cleanup
Command/Uninit.hs view
@@ -41,11 +41,13 @@  seek :: CmdParams -> CommandSeek seek ps = do-	l <- workTreeItems ps+	l <- workTreeItems ww ps 	withFilesNotInGit (commandAction . whenAnnexed (startCheckIncomplete . fromRawFilePath)) l 	Annex.changeState $ \s -> s { Annex.fast = True }-	withFilesInGit (commandAction . whenAnnexed Command.Unannex.start) l+	withFilesInGit ww (commandAction . whenAnnexed Command.Unannex.start) l 	finish+  where+	ww = WarnUnmatchLsFiles  {- git annex symlinks that are not checked into git could be left by an  - interrupted add. -}
Command/Unlock.hs view
@@ -27,7 +27,10 @@ 	command n SectionCommon d paramPaths (withParams seek)  seek :: CmdParams -> CommandSeek-seek ps = withFilesInGit (commandAction . whenAnnexed start) =<< workTreeItems ps+seek ps = withFilesInGit ww (commandAction . whenAnnexed start)+	=<< workTreeItems ww ps+  where+	ww = WarnUnmatchLsFiles  start :: RawFilePath -> Key -> CommandStart start file key = ifM (isJust <$> isAnnexLink file)
Command/Unused.hs view
@@ -210,9 +210,9 @@ 			( return ([], return True) 			, do 				top <- fromRepo Git.repoPath-				inRepo $ LsFiles.allFiles [top]+				inRepo $ LsFiles.allFiles [] [top] 			)-		Just dir -> inRepo $ LsFiles.inRepo [toRawFilePath dir]+		Just dir -> inRepo $ LsFiles.inRepo [] [toRawFilePath dir] 	go v [] = return v 	go v (f:fs) = do 		mk <- lookupFile f
Command/View.hs view
@@ -101,7 +101,7 @@ 		 - removed.) -} 		top <- liftIO . absPath . fromRawFilePath =<< fromRepo Git.repoPath 		(l, cleanup) <- inRepo $-			LsFiles.notInRepoIncludingEmptyDirectories False+			LsFiles.notInRepoIncludingEmptyDirectories [] False 				[toRawFilePath top] 		forM_ l (removeemptydir top) 		liftIO $ void cleanup
Command/WebApp.hs view
@@ -220,14 +220,15 @@ 		hPutStrLn (fromMaybe stdout outh) $ "Launching web browser on " ++ url 		hFlush stdout 		environ <- cleanEnvironment-		(_, _, _, pid) <- createProcess p+		let p' = p 			{ env = environ 			, std_out = maybe Inherit UseHandle outh 			, std_err = maybe Inherit UseHandle errh 			}-		exitcode <- waitForProcess pid-		unless (exitcode == ExitSuccess) $-			hPutStrLn (fromMaybe stderr errh) "failed to start web browser"+		withCreateProcess p' $ \_ _ _ pid -> do+			exitcode <- waitForProcess pid+			unless (exitcode == ExitSuccess) $+				hPutStrLn (fromMaybe stderr errh) "failed to start web browser"  {- web.browser is a generic git config setting for a web browser program -} webBrowser :: Git.Repo -> Maybe FilePath
Command/Whereis.hs view
@@ -57,8 +57,10 @@ 		NoBatch ->  			withKeyOptions (keyOptions o) False 				(commandAction . startKeys o m)-				(withFilesInGit (commandAction . go))-				=<< workTreeItems (whereisFiles o)+				(withFilesInGit ww (commandAction . go))+				=<< workTreeItems ww (whereisFiles o)+  where+	ww = WarnUnmatchLsFiles  start :: WhereisOptions -> M.Map UUID Remote -> RawFilePath -> Key -> CommandStart start o remotemap file key = 
Creds.hs view
@@ -35,6 +35,7 @@ import Utility.Env (getEnv)  import qualified Data.ByteString.Lazy.Char8 as L+import qualified Data.ByteString.Char8 as S import qualified Data.Map as M import Utility.Base64 @@ -56,20 +57,25 @@  - if that's going to be done, so that the creds can be encrypted using the  - cipher. The EncryptionIsSetup is witness to that being the case.  -}-setRemoteCredPair :: EncryptionIsSetup -> ParsedRemoteConfig -> RemoteGitConfig -> CredPairStorage -> Maybe CredPair -> Annex RemoteConfig-setRemoteCredPair encsetup pc = setRemoteCredPair' id (const pc) encsetup (unparsedRemoteConfig pc)+setRemoteCredPair+	:: EncryptionIsSetup+	-> ParsedRemoteConfig+	-> RemoteGitConfig+	-> CredPairStorage+	-> Maybe CredPair+	-> Annex RemoteConfig+setRemoteCredPair encsetup pc gc storage mcreds = unparsedRemoteConfig <$>+	setRemoteCredPair' pc encsetup gc storage mcreds  setRemoteCredPair'-	:: (ProposedAccepted String -> a)-	-> (M.Map RemoteConfigField a -> ParsedRemoteConfig)+	:: ParsedRemoteConfig 	-> EncryptionIsSetup-	-> M.Map RemoteConfigField a 	-> RemoteGitConfig 	-> CredPairStorage 	-> Maybe CredPair-	-> Annex (M.Map RemoteConfigField a)-setRemoteCredPair' mkval parseconfig encsetup c gc storage mcreds = case mcreds of-	Nothing -> maybe (return c) (setRemoteCredPair' mkval parseconfig encsetup c gc storage . Just)+	-> Annex ParsedRemoteConfig+setRemoteCredPair' pc encsetup gc storage mcreds = case mcreds of+	Nothing -> maybe (return pc) (setRemoteCredPair' pc encsetup gc storage . Just) 		=<< getRemoteCredPair pc gc storage 	Just creds 		| embedCreds pc -> do@@ -78,7 +84,7 @@ 			storeconfig creds key =<< remoteCipher pc gc 		| otherwise -> do 			localcache creds-			return c+			return pc   where 	localcache creds = writeCacheCredPair creds storage @@ -86,12 +92,15 @@ 		cmd <- gpgCmd <$> Annex.getGitConfig 		s <- liftIO $ encrypt cmd (pc, gc) cipher 			(feedBytes $ L.pack $ encodeCredPair creds)-			(readBytes $ return . L.unpack)-		return $ M.insert key (mkval (Accepted (toB64 s))) c+			(readBytesStrictly $ return . S.unpack)+		storeconfig' key (Accepted (toB64 s)) 	storeconfig creds key Nothing =-		return $ M.insert key (mkval (Accepted (toB64 $ encodeCredPair creds))) c+		storeconfig' key (Accepted (toB64 $ encodeCredPair creds)) 	-	pc = parseconfig c+	storeconfig' key val = return $ pc+		{ parsedRemoteConfigMap = M.insert key (RemoteConfigValue val) (parsedRemoteConfigMap pc)+		, unparsedRemoteConfig = M.insert key val (unparsedRemoteConfig pc)+		}  {- Gets a remote's credpair, from the environment if set, otherwise  - from the cache in gitAnnexCredsDir, or failing that, from the@@ -104,7 +113,12 @@ 	fromconfig = do 		let key = credPairRemoteField storage 		mcipher <- remoteCipher' c gc-		case (getRemoteConfigValue key c, mcipher) of+		-- The RemoteConfig value may be passed through.+		-- Check for those first, because getRemoteConfigValue+		-- will throw an error if it does not find it.+		let getval = M.lookup key (getRemoteConfigPassedThrough c)+			<|> getRemoteConfigValue key c			+		case (getval, mcipher) of 			(Nothing, _) -> return Nothing 			(Just enccreds, Just (cipher, storablecipher)) -> 				fromenccreds enccreds cipher storablecipher@@ -114,7 +128,7 @@ 		cmd <- gpgCmd <$> Annex.getGitConfig 		mcreds <- liftIO $ catchMaybeIO $ decrypt cmd (c, gc) cipher 			(feedBytes $ L.pack $ fromB64 enccreds)-			(readBytes $ return . L.unpack)+			(readBytesStrictly $ return . S.unpack) 		case mcreds of 			Just creds -> fromcreds creds 			Nothing -> do
Crypto.hs view
@@ -28,6 +28,7 @@ 	feedFile, 	feedBytes, 	readBytes,+	readBytesStrictly, 	encrypt, 	decrypt, 	LensGpgEncParams(..),@@ -187,25 +188,35 @@ readBytes :: (MonadIO m) => (L.ByteString -> m a) -> Reader m a readBytes a h = liftIO (L.hGetContents h) >>= a +readBytesStrictly :: (MonadIO m) => (S.ByteString -> m a) -> Reader m a+readBytesStrictly a h = liftIO (S.hGetContents h) >>= a++ {- Runs a Feeder action, that generates content that is symmetrically  - encrypted with the Cipher (unless it is empty, in which case  - public-key encryption is used) using the given gpg options, and then- - read by the Reader action. -}+ - read by the Reader action. + -+ - Note that the Reader must fully consume gpg's input before returning.+ -} encrypt :: (MonadIO m, MonadMask m, LensGpgEncParams c) => Gpg.GpgCmd -> c -> Cipher -> Feeder -> Reader m a -> m a encrypt cmd c cipher = case cipher of 	Cipher{} -> Gpg.feedRead cmd (params ++ Gpg.stdEncryptionParams True) $ 			cipherPassphrase cipher-	MacOnlyCipher{} -> Gpg.pipeLazy cmd $ params ++ Gpg.stdEncryptionParams False+	MacOnlyCipher{} -> Gpg.feedRead' cmd $ params ++ Gpg.stdEncryptionParams False   where 	params = getGpgEncParams c  {- Runs a Feeder action, that generates content that is decrypted with the  - Cipher (or using a private key if the Cipher is empty), and read by the- - Reader action. -}+ - Reader action.+ -+ - Note that the Reader must fully consume gpg's input before returning.+ - -} decrypt :: (MonadIO m, MonadMask m, LensGpgEncParams c) => Gpg.GpgCmd -> c -> Cipher -> Feeder -> Reader m a -> m a decrypt cmd c cipher = case cipher of 	Cipher{} -> Gpg.feedRead cmd params $ cipherPassphrase cipher-	MacOnlyCipher{} -> Gpg.pipeLazy cmd params+	MacOnlyCipher{} -> Gpg.feedRead' cmd params   where 	params = Param "--decrypt" : getGpgDecParams c 
Git/CatFile.hs view
@@ -180,14 +180,16 @@ 			, std_in = Inherit 			, std_out = CreatePipe 			}-		pid <- createProcess p'-		let h = stdoutHandle pid-		output <- reader h-		hClose h-		ifM (checkSuccessProcess (processHandle pid))+		withCreateProcess p' go+  where+	go _ (Just outh) _ pid = do+		output <- reader outh+		hClose outh+		ifM (checkSuccessProcess pid) 			( return (Just output) 			, return Nothing 			)+	go _ _ _ _ = error "internal"  querySize :: Ref -> Repo -> IO (Maybe FileSize) querySize r repo = maybe Nothing (readMaybe . takeWhile (/= '\n'))
Git/Command.hs view
@@ -1,6 +1,6 @@ {- running git commands  -- - Copyright 2010-2013 Joey Hess <id@joeyh.name>+ - Copyright 2010-2020 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU AGPL version 3 or higher.  -}@@ -43,9 +43,13 @@  {- Runs git and forces it to be quiet, throwing an error if it fails. -} runQuiet :: [CommandParam] -> Repo -> IO ()-runQuiet params repo = withQuietOutput createProcessSuccess $-	(proc "git" $ toCommand $ gitCommandLine (params) repo)-		{ env = gitEnv repo }+runQuiet params repo = withNullHandle $ \nullh ->+	let p = (proc "git" $ toCommand $ gitCommandLine (params) repo)+		{ env = gitEnv repo+		, std_out = UseHandle nullh+		, std_err = UseHandle nullh+		}+	in withCreateProcess p $ \_ _ _ -> forceSuccessProcess p  {- Runs a git command and returns its output, lazily.  -@@ -70,14 +74,18 @@  {- The reader action must be strict. -} pipeReadStrict' :: (Handle -> IO a) -> [CommandParam] -> Repo -> IO a-pipeReadStrict' reader params repo = assertLocal repo $-	withHandle StdoutHandle (createProcessChecked ignoreFailureProcess) p $ \h -> do-		output <- reader h-		hClose h-		return output+pipeReadStrict' reader params repo = assertLocal repo $ withCreateProcess p go   where-	p  = gitCreateProcess params repo+	p  = (gitCreateProcess params repo)+		{ std_out = CreatePipe } +	go _ (Just outh) _ pid = do+		output <- reader outh+		hClose outh+		void $ waitForProcess pid+		return output+	go _ _ _ _ = error "internal"+ {- Runs a git command, feeding it an input, and returning its output,  - which is expected to be fairly small, since it's all read into memory  - strictly. -}@@ -95,9 +103,16 @@  {- Runs a git command, feeding it input on a handle with an action. -} pipeWrite :: [CommandParam] -> Repo -> (Handle -> IO ()) -> IO ()-pipeWrite params repo = assertLocal repo $ -	withHandle StdinHandle createProcessSuccess $-		gitCreateProcess params repo+pipeWrite params repo feeder = assertLocal repo $+	let p = (gitCreateProcess params repo)+		{ std_in = CreatePipe }+	in withCreateProcess p (go p)+  where+	go p (Just hin) _ _ pid = do+		feeder hin+		hClose hin+		forceSuccessProcess p pid+	go _ _ _ _ _ = error "internal"  {- Reads null terminated output of a git command (as enabled by the -z   - parameter), and splits it. -}
Git/Config.hs view
@@ -58,29 +58,37 @@ 	go Repo { location = Local { gitdir = d } } = git_config d 	go Repo { location = LocalUnknown d } = git_config d 	go _ = assertLocal repo $ error "internal"-	git_config d = withHandle StdoutHandle createProcessSuccess p $-		hRead repo ConfigNullList+	git_config d = withCreateProcess p (git_config' p) 	  where 		params = ["config", "--null", "--list"] 		p = (proc "git" params) 			{ cwd = Just (fromRawFilePath d) 			, env = gitEnv repo+			, std_out = CreatePipe  			}+	git_config' p _ (Just hout) _ pid = +		forceSuccessProcess p pid+			`after`+		hRead repo ConfigNullList hout+	git_config' _ _ _ _ _ = error "internal"  {- Gets the global git config, returning a dummy Repo containing it. -} global :: IO (Maybe Repo) global = do 	home <- myHomeDir 	ifM (doesFileExist $ home </> ".gitconfig")-		( do-			repo <- withHandle StdoutHandle createProcessSuccess p $-				hRead (Git.Construct.fromUnknown) ConfigNullList-			return $ Just repo+		( Just <$> withCreateProcess p go 		, return Nothing 		)   where 	params = ["config", "--null", "--list", "--global"] 	p = (proc "git" params)+		{ std_out = CreatePipe }+	go _ (Just hout) _ pid = +		forceSuccessProcess p pid+			`after`+		hRead (Git.Construct.fromUnknown) ConfigNullList hout+	go _ _ _ _ = error "internal"  {- Reads git config from a handle and populates a repo with it. -} hRead :: Repo -> ConfigStyle -> Handle -> IO Repo@@ -200,16 +208,20 @@  - and returns a repo populated with the configuration, as well as the raw  - output and any standard output of the command. -} fromPipe :: Repo -> String -> [CommandParam] -> ConfigStyle -> IO (Either SomeException (Repo, S.ByteString, S.ByteString))-fromPipe r cmd params st = try $-	withOEHandles createProcessSuccess p $ \(hout, herr) -> do-		geterr <- async $ S.hGetContents herr-		getval <- async $ S.hGetContents hout-		val <- wait getval-		err <- wait geterr+fromPipe r cmd params st = tryNonAsync $ withCreateProcess p go+  where+	p = (proc cmd $ toCommand params)+		{ std_out = CreatePipe+		, std_err = CreatePipe+		}+	go _ (Just hout) (Just herr) pid = do+		(val, err) <- concurrently +			(S.hGetContents hout)+			(S.hGetContents herr)+		forceSuccessProcess p pid 		r' <- store val st r 		return (r', val, err)-  where-	p = proc cmd $ toCommand params+	go _ _ _ _ = error "internal"  {- Reads git config from a specified file and returns the repo populated  - with the configuration. -}
Git/Fsck.hs view
@@ -77,27 +77,31 @@ 		then toBatchCommand (command, params) 		else return (command, params) 	-	p@(_, _, _, pid) <- createProcess $-		(proc command' (toCommand params'))-			{ std_out = CreatePipe-			, std_err = CreatePipe-			}-	(o1, o2) <- concurrently-		(parseFsckOutput maxobjs r (stdoutHandle p))-		(parseFsckOutput maxobjs r (stderrHandle p))-	fsckok <- checkSuccessProcess pid-	case mappend o1 o2 of-		FsckOutput badobjs truncated-			| S.null badobjs && not fsckok -> return FsckFailed-			| otherwise -> return $ FsckFoundMissing badobjs truncated-		NoFsckOutput-			| not fsckok -> return FsckFailed-			| otherwise -> return noproblem-		-- If all fsck output was duplicateEntries warnings,-		-- the repository is not broken, it just has some unusual-		-- tree objects in it. So ignore nonzero exit status.-		AllDuplicateEntriesWarning -> return noproblem+	let p = (proc command' (toCommand params'))+		{ std_out = CreatePipe+		, std_err = CreatePipe+		}+	withCreateProcess p go   where+	go _ (Just outh) (Just errh) pid = do+		(o1, o2) <- concurrently+			(parseFsckOutput maxobjs r outh)+			(parseFsckOutput maxobjs r errh)+		fsckok <- checkSuccessProcess pid+		case mappend o1 o2 of+			FsckOutput badobjs truncated+				| S.null badobjs && not fsckok -> return FsckFailed+				| otherwise -> return $ FsckFoundMissing badobjs truncated+			NoFsckOutput+				| not fsckok -> return FsckFailed+				| otherwise -> return noproblem+			-- If all fsck output was duplicateEntries warnings,+			-- the repository is not broken, it just has some+			-- unusual tree objects in it. So ignore nonzero+			-- exit status.+			AllDuplicateEntriesWarning -> return noproblem+	go _ _ _ _ = error "internal"+	 	maxobjs = 10000 	noproblem = FsckFoundMissing S.empty False 
Git/GCrypt.hs view
@@ -66,12 +66,12 @@ 		, Param "--check" 		, Param loc 		] baserepo-	(_, _, _, pid) <- createProcess p-	code <- waitForProcess pid-	return $ case code of-		ExitSuccess -> Decryptable-		ExitFailure 1 -> NotDecryptable-		ExitFailure _ -> NotEncrypted+	withCreateProcess p $ \_ _ _ pid -> do+		code <- waitForProcess pid+		return $ case code of+			ExitSuccess -> Decryptable+			ExitFailure 1 -> NotDecryptable+			ExitFailure _ -> NotEncrypted  type GCryptId = String 
Git/History.hs view
@@ -50,19 +50,22 @@ {- Gets a History starting with the provided commit, and down to the  - requested depth. -} getHistoryToDepth :: Integer -> Ref -> Repo -> IO (Maybe (History HistoryCommit))-getHistoryToDepth n commit r = do-	(_, Just inh, _, pid) <- createProcess (gitCreateProcess params r)-		{ std_out = CreatePipe }-	!h <- fmap (truncateHistoryToDepth n) -		. build Nothing -		. map parsehistorycommit-		. map L.toStrict-		. L8.lines-		<$> L.hGetContents inh-	hClose inh-	void $ waitForProcess pid-	return h+getHistoryToDepth n commit r = withCreateProcess p go   where+	p = (gitCreateProcess params r)+		{ std_out = CreatePipe }+	go _ (Just inh) _ pid = do+		!h <- fmap (truncateHistoryToDepth n) +			. build Nothing +			. map parsehistorycommit+			. map L.toStrict+			. L8.lines+			<$> L.hGetContents inh+		hClose inh+		void $ waitForProcess pid+		return h+	go _ _ _ _ = error "internal"+ 	build h [] = fmap (mapHistory fst) h 	build _ (Nothing:_) = Nothing 	build Nothing (Just v:rest) =
Git/LsFiles.hs view
@@ -1,11 +1,12 @@ {- git ls-files interface  -- - Copyright 2010-2019 Joey Hess <id@joeyh.name>+ - Copyright 2010-2020 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU AGPL version 3 or higher.  -}  module Git.LsFiles (+	Options(..), 	inRepo, 	inRepoOrBranch, 	notInRepo,@@ -13,10 +14,8 @@ 	allFiles, 	deleted, 	modified,-	modifiedOthers, 	staged, 	stagedNotDeleted,-	stagedOthersDetails, 	stagedDetails, 	typeChanged, 	typeChangedStaged,@@ -63,101 +62,63 @@ 	| safeForLsFiles r = a 	| otherwise = error $ "git ls-files is unsafe to run on repository " ++ repoDescribe r +data Options = ErrorUnmatch+ {- Lists files that are checked into git's index at the specified paths.  - With no paths, all files are listed.  -}-inRepo :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)-inRepo = inRepo' [] +inRepo :: [Options] -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+inRepo = inRepo' [Param "--cached"]  -inRepo' :: [CommandParam] -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)-inRepo' ps l repo = guardSafeForLsFiles repo $ pipeNullSplit' params repo+inRepo' :: [CommandParam] -> [Options] -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+inRepo' ps os l repo = guardSafeForLsFiles repo $ pipeNullSplit' params repo   where 	params =  		Param "ls-files" :-		Param "--cached" : 		Param "-z" :-		ps +++		map op os ++ ps ++ 		(Param "--" : map (File . fromRawFilePath) l)+	op ErrorUnmatch = Param "--error-unmatch"   {- Files that are checked into the index or have been committed to a  - branch. -}-inRepoOrBranch :: Branch -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)-inRepoOrBranch b = inRepo' [Param $ "--with-tree=" ++ fromRef b]+inRepoOrBranch :: Branch -> [Options] -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+inRepoOrBranch b = inRepo'+	[ Param "--cached"+	, Param ("--with-tree=" ++ fromRef b)+	]  {- Scans for files at the specified locations that are not checked into git. -}-notInRepo :: Bool -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+notInRepo :: [Options] -> Bool -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool) notInRepo = notInRepo' [] -notInRepo' :: [CommandParam] -> Bool -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)-notInRepo' ps include_ignored l repo = guardSafeForLsFiles repo $-	pipeNullSplit' params repo+notInRepo' :: [CommandParam] -> [Options] -> Bool -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+notInRepo' ps os include_ignored = +	inRepo' (Param "--others" : ps ++ exclude) os   where-	params = concat-		[ [ Param "ls-files", Param "--others"]-		, ps-		, exclude-		, [ Param "-z", Param "--" ]-		, map (File . fromRawFilePath) l-		] 	exclude 		| include_ignored = [] 		| otherwise = [Param "--exclude-standard"]  {- Scans for files at the specified locations that are not checked into  - git. Empty directories are included in the result. -}-notInRepoIncludingEmptyDirectories :: Bool -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+notInRepoIncludingEmptyDirectories :: [Options] -> Bool -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool) notInRepoIncludingEmptyDirectories = notInRepo' [Param "--directory"]  {- Finds all files in the specified locations, whether checked into git or  - not. -}-allFiles :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)-allFiles l repo = guardSafeForLsFiles repo $ pipeNullSplit' params repo-  where-	params =-		Param "ls-files" :-		Param "--cached" :-		Param "--others" :-		Param "-z" :-		Param "--" :-		map (File . fromRawFilePath) l+allFiles :: [Options] -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+allFiles = inRepo' [Param "--cached", Param "--others"]  {- Returns a list of files in the specified locations that have been  - deleted. -}-deleted :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)-deleted l repo = guardSafeForLsFiles repo $ pipeNullSplit' params repo-  where-	params =-		Param "ls-files" :-		Param "--deleted" :-		Param "-z" :-		Param "--" :-		map (File . fromRawFilePath) l+deleted :: [Options] -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+deleted = inRepo' [Param "--deleted"]  {- Returns a list of files in the specified locations that have been  - modified. -}-modified :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)-modified l repo = guardSafeForLsFiles repo $ pipeNullSplit' params repo-  where-	params = -		Param "ls-files" :-		Param "--modified" :-		Param "-z" :-		Param "--" :-		map (File . fromRawFilePath) l--{- Files that have been modified or are not checked into git (and are not- - ignored). -}-modifiedOthers :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)-modifiedOthers l repo = guardSafeForLsFiles repo $ pipeNullSplit' params repo-  where-	params = -		Param "ls-files" :-		Param "--modified" :-		Param "--others" :-		Param "--exclude-standard" :-		Param "-z" :-		Param "--" :-		map (File . fromRawFilePath) l+modified :: [Options] -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+modified = inRepo' [Param "--modified"]  {- Returns a list of all files that are staged for commit. -} staged :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)@@ -176,11 +137,6 @@ 	suffix = Param "--" : map (File . fromRawFilePath) l  type StagedDetails = (RawFilePath, Maybe Sha, Maybe FileMode)--{- Returns details about files that are staged in the index,- - as well as files not yet in git. Skips ignored files. -}-stagedOthersDetails :: [RawFilePath] -> Repo -> IO ([StagedDetails], IO Bool)-stagedOthersDetails = stagedDetails' [Param "--others", Param "--exclude-standard"]  {- Returns details about all files that are staged in the index. -} stagedDetails :: [RawFilePath] -> Repo -> IO ([StagedDetails], IO Bool)
Git/Queue.hs view
@@ -191,10 +191,11 @@ 	liftIO $ Git.UpdateIndex.streamUpdateIndex repo $ reverse streamers runAction repo action@(CommandAction {}) = liftIO $ do #ifndef mingw32_HOST_OS-	let p = (proc "xargs" $ "-0":"git":toCommand gitparams) { env = gitEnv repo }-	withHandle StdinHandle createProcessSuccess p $ \h -> do-		hPutStr h $ intercalate "\0" $ toCommand $ getFiles action-		hClose h+	let p = (proc "xargs" $ "-0":"git":toCommand gitparams)+		{ env = gitEnv repo+		, std_in = CreatePipe+		}+	withCreateProcess p (go p) #else 	-- Using xargs on Windows is problematic, so just run the command 	-- once per file (not as efficient.)@@ -206,6 +207,11 @@   where 	gitparams = gitCommandLine 		(Param (getSubcommand action):getParams action) repo+	go p (Just h) _ _ pid = do+		hPutStr h $ intercalate "\0" $ toCommand $ getFiles action+		hClose h+		forceSuccessProcess p pid+	go _ _ _ _ _ = error "internal" runAction repo action@(InternalAction {}) = 	let InternalActionRunner _ runner = getRunner action 	in runner repo (getInternalFiles action)
Git/UpdateIndex.hs view
@@ -1,6 +1,6 @@ {- git-update-index library  -- - Copyright 2011-2019 Joey Hess <id@joeyh.name>+ - Copyright 2011-2020 Joey Hess <id@joeyh.name>  -  - Licensed under the GNU AGPL version 3 or higher.  -}@@ -12,8 +12,7 @@ 	pureStreamer, 	streamUpdateIndex, 	streamUpdateIndex',-	startUpdateIndex,-	stopUpdateIndex,+	withUpdateIndex, 	lsTree, 	lsSubTree, 	updateIndexLine,@@ -33,6 +32,7 @@ import qualified Git.DiffTreeItem as Diff  import qualified Data.ByteString.Lazy as L+import Control.Monad.IO.Class  {- Streamers are passed a callback and should feed it lines in the form  - read by update-index, and generated by ls-tree. -}@@ -44,28 +44,32 @@  {- Streams content into update-index from a list of Streamers. -} streamUpdateIndex :: Repo -> [Streamer] -> IO ()-streamUpdateIndex repo as = bracket (startUpdateIndex repo) stopUpdateIndex $-	(\h -> forM_ as $ streamUpdateIndex' h)+streamUpdateIndex repo as = withUpdateIndex repo $ \h ->+	forM_ as $ streamUpdateIndex' h -data UpdateIndexHandle = UpdateIndexHandle ProcessHandle Handle+data UpdateIndexHandle = UpdateIndexHandle Handle  streamUpdateIndex' :: UpdateIndexHandle -> Streamer -> IO ()-streamUpdateIndex' (UpdateIndexHandle _ h) a = a $ \s -> do+streamUpdateIndex' (UpdateIndexHandle h) a = a $ \s -> do 	L.hPutStr h s 	L.hPutStr h "\0" -startUpdateIndex :: Repo -> IO UpdateIndexHandle-startUpdateIndex repo = do-	(Just h, _, _, p) <- createProcess (gitCreateProcess params repo)-		{ std_in = CreatePipe }-	return $ UpdateIndexHandle p h+withUpdateIndex :: (MonadIO m, MonadMask m) => Repo -> (UpdateIndexHandle -> m a) -> m a+withUpdateIndex repo a = bracket setup cleanup go   where 	params = map Param ["update-index", "-z", "--index-info"]--stopUpdateIndex :: UpdateIndexHandle -> IO Bool-stopUpdateIndex (UpdateIndexHandle p h) = do-	hClose h-	checkSuccessProcess p+	+	setup = liftIO $ createProcess $ +		(gitCreateProcess params repo)+			{ std_in = CreatePipe }+	go p = do+		r <- a (UpdateIndexHandle (stdinHandle p))+		liftIO $ do+			hClose (stdinHandle p)+			void $ checkSuccessProcess (processHandle p)+		return r+	+	cleanup = liftIO . cleanupProcess  {- A streamer that adds the current tree for a ref. Useful for eg, copying  - and modifying branches. -}@@ -132,15 +136,7 @@  {- Refreshes the index, by checking file stat information.  -} refreshIndex :: Repo -> ((FilePath -> IO ()) -> IO ()) -> IO Bool-refreshIndex repo feeder = do-	(Just h, _, _, p) <- createProcess (gitCreateProcess params repo)-		{ std_in = CreatePipe }-	feeder $ \f -> do-		hPutStr h f-		hPutStr h "\0"-	hFlush h-	hClose h-	checkSuccessProcess p+refreshIndex repo feeder = withCreateProcess p go   where 	params =  		[ Param "update-index"@@ -149,3 +145,15 @@ 		, Param "-z" 		, Param "--stdin" 		]+	+	p = (gitCreateProcess params repo)+		{ std_in = CreatePipe }++	go (Just h) _ _ pid = do+		feeder $ \f -> do+			hPutStr h f+			hPutStr h "\0"+		hFlush h+		hClose h+		checkSuccessProcess pid+	go _ _ _ _ = error "internal"
NEWS view
@@ -1,3 +1,14 @@+git-annex (8.20200523) UNRELEASED; urgency=medium++  Transition notice: Commands like `git-annex get foo*` silently skip over+  files that are not checked into git. Since that can be surprising in many+  cases, the behavior will change to erroring out when one of the listed+  files is not checked into git. This change is planned for a git-annex+  release in early 2022. If you would like to keep the current+  behavior, use git config to set annex.skipunknown to true.++ -- Joey Hess <id@joeyh.name>  Thu, 28 May 2020 13:23:40 -0400+ git-annex (8.20200226) upstream; urgency=medium      This version of git-annex uses repository version 8 for all repositories.
P2P/IO.hs view
@@ -272,19 +272,21 @@   where 	setup = do 		v <- newEmptyMVar-		void $ async $ relayFeeder runner v hin-		void $ async $ relayReader v hout-		return v+		t1 <- async $ relayFeeder runner v hin+		t2 <- async $ relayReader v hout+		return (v, t1, t2) 	-	cleanup _ = do+	cleanup (_, t1, t2) = do 		hClose hin 		hClose hout+		cancel t1+		cancel t2 	-	go v = relayHelper runner v+	go (v, _, _) = relayHelper runner v  runRelayService :: P2PConnection -> RunProto IO -> Service -> IO (Either ProtoFailure ())-runRelayService conn runner service = -	bracket setup cleanup go+runRelayService conn runner service =+	withCreateProcess serviceproc' go 		`catchNonAsync` (return . Left . ProtoFailureException)   where 	cmd = case service of@@ -295,30 +297,29 @@ 		[ Param cmd 		, File (fromRawFilePath (repoPath (connRepo conn))) 		] (connRepo conn)+	serviceproc' = serviceproc +		{ std_out = CreatePipe+		, std_in = CreatePipe+		} -	setup = do-		(Just hin, Just hout, _, pid) <- createProcess serviceproc-			{ std_out = CreatePipe-			, std_in = CreatePipe-			}+	go (Just hin) (Just hout) _ pid = do 		v <- newEmptyMVar-		void $ async $ relayFeeder runner v hin-		void $ async $ relayReader v hout-		waiter <- async $ waitexit v pid-		return (v, waiter, hin, hout, pid)--	cleanup (_, waiter, hin, hout, pid) = do-		hClose hin-		hClose hout-		cancel waiter+		r <- withAsync (relayFeeder runner v hin) $ \_ ->+			withAsync (relayReader v hout) $ \_ ->+				withAsync (waitexit v pid) $ \_ -> do+					r <- runrelay v+					hClose hin+					hClose hout+					return r 		void $ waitForProcess pid--	go (v, _, _, _, _) = do-		r <- relayHelper runner v-		case r of-			Left e -> return $ Left e-			Right exitcode -> runner $ net $ relayToPeer (RelayDone exitcode)+		return r+	go _ _ _ _ = error "internal" 	+	runrelay v = relayHelper runner v >>= \case+		Left e -> return $ Left e+		Right exitcode -> runner $+			net $ relayToPeer (RelayDone exitcode)+ 	waitexit v pid = putMVar v . RelayDone =<< waitForProcess pid  -- Processes RelayData as it is put into the MVar.
Remote.hs view
@@ -74,6 +74,7 @@ import Logs.Location hiding (logStatus) import Logs.Web import Remote.List+import Remote.List.Util import Config import Config.DynamicConfig import Git.Types (RemoteName, ConfigKey(..), fromConfigValue)@@ -286,7 +287,7 @@ 	findinmap = M.lookup u <$> remoteMap id 	{- Re-read remote list in case a new remote has popped up. -} 	tryharder = do-		void remoteListRefresh+		remotesChanged 		findinmap  {- Filters a list of remotes to ones that have the listed uuids. -}
Remote/Bup.hs view
@@ -152,22 +152,42 @@  store :: Remote -> BupRepo -> Storer store r buprepo = byteStorer $ \k b p -> do-	let params = bupSplitParams r buprepo k [] 	showOutput -- make way for bup output-	let cmd = proc "bup" (toCommand params) 	quiet <- commandProgressDisabled-	let feeder = \h -> meteredWrite p h b-	liftIO $ if quiet-		then feedWithQuietOutput createProcessSuccess cmd feeder-		else withHandle StdinHandle createProcessSuccess cmd feeder+	liftIO $ withNullHandle $ \nullh ->+		let params = bupSplitParams r buprepo k []+		    cmd = (proc "bup" (toCommand params))+			{ std_in = CreatePipe }+		    cmd' = if quiet+			then cmd +				{ std_out = UseHandle nullh+				, std_err = UseHandle nullh+				}+			else cmd+		    feeder = \h -> do+			meteredWrite p h b+			hClose h+		in withCreateProcess cmd' (go feeder cmd')+  where+	go feeder p (Just h) _ _ pid =+		forceSuccessProcess p pid+			`after`+		feeder h+	go _ _ _ _ _ _ = error "internal"  retrieve :: BupRepo -> Retriever retrieve buprepo = byteRetriever $ \k sink -> do 	let params = bupParams "join" buprepo [Param $ bupRef k]-	let p = proc "bup" (toCommand params)-	(_, Just h, _, pid) <- liftIO $ createProcess $ p { std_out = CreatePipe }-	liftIO (hClose h >> forceSuccessProcess p pid)-		`after` (sink =<< liftIO (L.hGetContents h))+	let p = (proc "bup" (toCommand params))+		{ std_out = CreatePipe }+	bracketIO (createProcess p) cleanupProcess (go sink p)+  where+	go sink p (_, Just h, _, pid) = do+		() <- sink =<< liftIO (L.hGetContents h)+		liftIO $ do+			hClose h+			forceSuccessProcess p pid+	go _ _ _ = error "internal"  {- Cannot revert having stored a key in bup, but at least the data for the  - key will be used for deltaing data of other keys stored later.@@ -177,7 +197,7 @@ remove :: BupRepo -> Remover remove buprepo k = do 	go =<< liftIO (bup2GitRemote buprepo)-	giveup "content cannot be completely removed from bup remote"+	warning "content cannot be completely removed from bup remote"   where 	go r 		| Git.repoIsUrl r = void $ onBupRemote r boolSystem "git" params
Remote/Ddar.hs view
@@ -1,11 +1,13 @@ {- Using ddar as a remote. Based on bup and rsync remotes.  -- - Copyright 2011 Joey Hess <id@joeyh.name>+ - Copyright 2011-2020 Joey Hess <id@joeyh.name>  - Copyright 2014 Robie Basak <robie@justgohome.co.uk>  -  - Licensed under the GNU GPL version 3 or higher.  -} +{-# LANGUAGE BangPatterns #-}+ module Remote.Ddar (remote) where  import qualified Data.Map as M@@ -157,10 +159,16 @@ retrieve :: DdarRepo -> Retriever retrieve ddarrepo = byteRetriever $ \k sink -> do 	(cmd, params) <- ddarExtractRemoteCall NoConsumeStdin ddarrepo k-	let p = (proc cmd $ toCommand params) { std_out = CreatePipe }-	(_, Just h, _, pid) <- liftIO $ createProcess p-	liftIO (hClose h >> forceSuccessProcess p pid)-		`after` (sink =<< liftIO (L.hGetContents h))+	let p = (proc cmd $ toCommand params)+		{ std_out = CreatePipe }+	bracketIO (createProcess p) cleanupProcess (go sink p)+  where+	go sink p (_, Just h, _, pid) = do+		() <- sink =<< liftIO (L.hGetContents h)+		liftIO $ do+			hClose h+			forceSuccessProcess p pid+	go _ _ _ = error "internal"  remove :: DdarRepo -> Remover remove ddarrepo key = do@@ -195,12 +203,18 @@ inDdarManifest :: DdarRepo -> Key -> Annex (Either String Bool) inDdarManifest ddarrepo k = do 	(cmd, params) <- ddarRemoteCall NoConsumeStdin ddarrepo 't' []-	let p = proc cmd $ toCommand params-	liftIO $ catchMsgIO $ withHandle StdoutHandle createProcessSuccess p $ \h -> do-		contents <- hGetContents h-		return $ elem k' $ lines contents+	let p = (proc cmd $ toCommand params)+		{ std_out = CreatePipe }+	liftIO $ catchMsgIO $ withCreateProcess p (go p)   where 	k' = serializeKey k+	+	go p _ (Just hout) _ pid = do+		contents <- hGetContents hout+		let !r = elem k' (lines contents)+		forceSuccessProcess p pid+		return r+	go _ _ _ _ _ = error "internal"  checkKey :: DdarRepo -> CheckPresent checkKey ddarrepo key = do
Remote/Directory.hs view
@@ -350,20 +350,28 @@  	docopy cont = do #ifndef mingw32_HOST_OS-		-- Need a duplicate fd for the post check, since-		-- hGetContentsMetered closes its handle.-		fd <- liftIO $ openFd f ReadOnly Nothing defaultFileFlags-		dupfd <- liftIO $ dup fd-		h <- liftIO $ fdToHandle fd+		let open = do+			-- Need a duplicate fd for the post check, since+			-- hGetContentsMetered closes its handle.+			fd <- openFd f ReadOnly Nothing defaultFileFlags+			dupfd <- dup fd+			h <- fdToHandle fd+			return (h, dupfd)+		let close (h, dupfd) = do+			hClose h+			closeFd dupfd+		bracketIO open close $ \(h, dupfd) -> do #else-		h <- liftIO $ openBinaryFile f ReadMode+		let open = openBinaryFile f ReadMode+		let close = hClose+		bracketIO setup close $ \h -> do #endif-		liftIO $ hGetContentsMetered h p >>= L.writeFile dest-		k <- mkkey+			liftIO $ hGetContentsMetered h p >>= L.writeFile dest+			k <- mkkey #ifndef mingw32_HOST_OS-		cont dupfd (return k)+			cont dupfd (return k) #else-		cont (return k)+			cont (return k) #endif 	 	-- Check before copy, to avoid expensive copy of wrong file
Remote/External.hs view
@@ -414,9 +414,13 @@ 					(Accepted setting) 					(RemoteConfigValue (PassedThrough value)) 					m-				in ParsedRemoteConfig m' c+				    c' = M.insert+				    	(Accepted setting)+					(Accepted value)+					c+				in ParsedRemoteConfig m' c' 			modifyTVar' (externalConfigChanges st) $ \f ->-				f . M.insert (Accepted setting) (Accepted value)+				M.insert (Accepted setting) (Accepted value) . f 	handleRemoteRequest (GETCONFIG setting) = do 		value <- maybe "" fromProposedAccepted 			. (M.lookup (Accepted setting))@@ -426,11 +430,18 @@ 	handleRemoteRequest (SETCREDS setting login password) = case (externalUUID external, externalGitConfig external) of 		(Just u, Just gc) -> do 			let v = externalConfig st-			(ParsedRemoteConfig m c) <- liftIO $ atomically $ readTVar v-			m' <- setRemoteCredPair' RemoteConfigValue (\m' -> ParsedRemoteConfig m' c) encryptionAlreadySetup m gc+			pc <- liftIO $ atomically $ readTVar v+			pc' <- setRemoteCredPair' pc encryptionAlreadySetup gc 				(credstorage setting u) 				(Just (login, password))-			void $ liftIO $ atomically $ swapTVar v (ParsedRemoteConfig m' c)+			let configchanges = M.differenceWithKey+				(\_k a b -> if a == b then Nothing else Just a)+				(unparsedRemoteConfig pc')+				(unparsedRemoteConfig pc)+			void $ liftIO $ atomically $ do+				_ <- swapTVar v pc'+				modifyTVar' (externalConfigChanges st) $ \f ->+					M.union configchanges . f 		_ -> senderror "cannot send SETCREDS here" 	handleRemoteRequest (GETCREDS setting) = case (externalUUID external, externalGitConfig external) of 		(Just u, Just gc) -> do@@ -560,13 +571,24 @@ {- While the action is running, the ExternalState provided to it will not  - be available to any other calls.  -- - Starts up a new process if no ExternalStates are available. -}+ - Starts up a new process if no ExternalStates are available.+ -+ - If the action is interrupted by an async exception, the external process+ - is in an unknown state, and may eg be still performing a transfer. So it+ - is killed. The action should not normally throw any exception itself,+ - unless perhaps there's a problem communicating with the external+ - process.+ -} withExternalState :: External -> (ExternalState -> Annex a) -> Annex a-withExternalState external = bracket alloc dealloc+withExternalState external a = do+	st <- get+	r <- a st `onException` liftIO (externalShutdown st True)+	put st -- only when no exception is thrown+	return r   where 	v = externalState external -	alloc = do+	get = do 		ms <- liftIO $ atomically $ do 			l <- readTVar v 			case l of@@ -576,7 +598,7 @@ 					return (Just st) 		maybe (startExternal external) return ms 	-	dealloc st = liftIO $ atomically $ modifyTVar' v (st:)+	put st = liftIO $ atomically $ modifyTVar' v (st:)  {- Starts an external remote process running, and checks VERSION and  - exchanges EXTENSIONS. -}@@ -611,7 +633,7 @@ 			, std_err = CreatePipe 			} 		p <- propgit g basep-		(Just hin, Just hout, Just herr, ph) <- +		pall@(Just hin, Just hout, Just herr, ph) <-  			createProcess p `catchNonAsync` runerr cmdpath 		stderrelay <- async $ errrelayer herr 		cv <- newTVarIO $ externalDefaultConfig external@@ -621,13 +643,20 @@ 			n <- succ <$> readTVar (externalLastPid external) 			writeTVar (externalLastPid external) n 			return n+		let shutdown forcestop = do+			cancel stderrelay+			if forcestop+				then cleanupProcess pall+				else flip onException (cleanupProcess pall) $ do+					hClose herr+					hClose hin+					hClose hout+					void $ waitForProcess ph 		return $ ExternalState 			{ externalSend = hin 			, externalReceive = hout 			, externalPid = pid-			, externalShutdown = do-				cancel stderrelay-				void $ waitForProcess ph+			, externalShutdown = shutdown 			, externalPrepared = pv 			, externalConfig = cv 			, externalConfigChanges = ccv@@ -657,12 +686,7 @@ stopExternal :: External -> Annex () stopExternal external = liftIO $ do 	l <- atomically $ swapTVar (externalState external) []-	mapM_ stop l-  where-	stop st = do-		hClose $ externalSend st-		hClose $ externalReceive st-		externalShutdown st+	mapM_ (flip externalShutdown False) l  externalRemoteProgram :: ExternalType -> String externalRemoteProgram externaltype = "git-annex-remote-" ++ externaltype@@ -811,7 +835,7 @@ 	{ remoteConfigFieldParsers = 		[ optionalStringParser externaltypeField 			(FieldDesc "type of external special remote to use")-		, trueFalseParser readonlyField False+		, trueFalseParser readonlyField (Just False) 			(FieldDesc "enable readonly mode") 		] 	, remoteConfigRestPassthrough = Just
Remote/External/Types.hs view
@@ -77,7 +77,7 @@ data ExternalState = ExternalState 	{ externalSend :: Handle 	, externalReceive :: Handle-	, externalShutdown :: IO ()+	, externalShutdown :: Bool -> IO () 	, externalPid :: PID 	, externalPrepared :: TVar PrepareStatus 	, externalConfig :: TVar ParsedRemoteConfig
Remote/Git.hs view
@@ -337,7 +337,9 @@ 	readlocalannexconfig = do 		let check = do 			Annex.BranchState.disableUpdate-			catchNonAsync ensureInitialized (warning . show)+			catchNonAsync autoInitialize $ \e ->+				warning $ "remote " ++ Git.repoDescribe r +++					" :"  ++ show e 			Annex.getState Annex.repo 		s <- Annex.new r 		Annex.eval s $ check `finally` stopCoProcesses@@ -469,18 +471,18 @@ 		P2PHelper.lock withconn Ssh.runProtoConn (uuid r) key callback 	| otherwise = failedlock   where-	fallback = do+	fallback = withNullHandle $ \nullh -> do 		Just (cmd, params) <- Ssh.git_annex_shell ConsumeStdin 			repo "lockcontent" 			[Param $ serializeKey key] []-		(Just hin, Just hout, Nothing, p) <- liftIO $ -			withFile devNull WriteMode $ \nullh ->-				createProcess $-					 (proc cmd (toCommand params))-						{ std_in = CreatePipe-						, std_out = CreatePipe-						, std_err = UseHandle nullh-						}+		let p = (proc cmd (toCommand params))+			{ std_in = CreatePipe+			, std_out = CreatePipe+			, std_err = UseHandle nullh+			}+		bracketIO (createProcess p) cleanupProcess fallback'++	fallback' (Just hin, Just hout, Nothing, p) = do 		v <- liftIO $ tryIO $ getProtocolLine hout 		let signaldone = void $ tryNonAsync $ liftIO $ mapM_ tryNonAsync 			[ hPutStrLn hout ""@@ -507,6 +509,8 @@ 					showNote "lockcontent failed" 					signaldone 					failedlock+	fallback' _ = error "internal"+ 	failedlock = giveup "can't lock content"  {- Tries to copy a key's content from a remote's annex to a file. -}@@ -587,7 +591,7 @@ 			repo "transferinfo"  			[Param $ serializeKey key] fields 		v <- liftIO (newEmptySV :: IO (MSampleVar Integer))-		pidv <- liftIO $ newEmptyMVar+		pv <- liftIO $ newEmptyMVar 		tid <- liftIO $ forkIO $ void $ tryIO $ do 			bytes <- readSV v 			p <- createProcess $@@ -595,7 +599,7 @@ 					{ std_in = CreatePipe 					, std_err = CreatePipe 					}-			putMVar pidv (processHandle p)+			putMVar pv p 			hClose $ stderrHandle p 			let h = stdinHandle p 			let send b = do@@ -614,10 +618,18 @@ 		-- do it in the background. 		let cleanup = forkIO $ do 			void $ tryIO $ killThread tid-			void $ tryNonAsync $-				maybe noop (void . waitForProcess)-					=<< tryTakeMVar pidv+			void $ tryNonAsync $ +				maybe noop (void . waitForProcess . processHandle)+					=<< tryTakeMVar pv++		let forcestop = do+			void $ tryIO $ killThread tid+			void $ tryNonAsync $ +				maybe noop cleanupProcess+					=<< tryTakeMVar pv+ 		bracketIO noop (const cleanup) (const $ a feeder)+			`onException` liftIO forcestop  copyFromRemoteCheap :: Remote -> State -> Git.Repo -> Maybe (Key -> AssociatedFile -> FilePath -> Annex ()) copyFromRemoteCheap r st repo@@ -824,7 +836,7 @@ 		| not $ Git.repoIsUrl repo = onLocalFast st $ 			doQuietSideAction $ 				Annex.Branch.commit =<< Annex.Branch.commitMessage-		| otherwise = void $ do+		| otherwise = do 			Just (shellcmd, shellparams) <- 				Ssh.git_annex_shell NoConsumeStdin 					repo "commit" [] []@@ -832,10 +844,13 @@ 			-- Throw away stderr, since the remote may not 			-- have a new enough git-annex shell to 			-- support committing.-			liftIO $ catchMaybeIO $-				withQuietOutput createProcessSuccess $-					proc shellcmd $-						toCommand shellparams+			liftIO $ void $ catchMaybeIO $ withNullHandle $ \nullh ->+				let p = (proc shellcmd (toCommand shellparams))+					{ std_out = UseHandle nullh+					, std_err = UseHandle nullh+					}+				in withCreateProcess p $ \_ _ _ ->+					forceSuccessProcess p  wantHardLink :: Annex Bool wantHardLink = (annexHardLink <$> Annex.getGitConfig)
Remote/Glacier.hs view
@@ -162,10 +162,15 @@ 		, Param "-" 		] 	go Nothing = giveup "Glacier not usable."-	go (Just e) = liftIO $ do+	go (Just e) = 		let cmd = (proc "glacier" (toCommand params)) { env = Just e }-		withHandle StdinHandle createProcessSuccess cmd $ \h ->-			meteredWrite p h b+			{ std_in = CreatePipe }+		in liftIO $ withCreateProcess cmd (go' cmd)+	go' cmd (Just hin) _ _ pid = do+		meteredWrite p hin b+		hClose hin+		forceSuccessProcess cmd pid+	go' _ _ _ _ _ = error "internal"  retrieve :: Remote -> Retriever retrieve = byteRetriever . retrieve'@@ -185,14 +190,15 @@ 		] 	go Nothing = giveup "cannot retrieve from glacier" 	go (Just environ) = do-		let cmd = (proc "glacier" (toCommand params))+		let p = (proc "glacier" (toCommand params)) 			{ env = Just environ 			, std_out = CreatePipe 			}-		(_, Just h, _, pid) <- liftIO $ createProcess cmd+		bracketIO (createProcess p) cleanupProcess (go' p)+	go' p (_, Just h, _, pid) = do 		let cleanup = liftIO $ do 			hClose h-			forceSuccessProcess cmd pid+			forceSuccessProcess p pid 		flip finally cleanup $ do 			-- Glacier cannot store empty files, so if 			-- the output is empty, the content is not@@ -200,6 +206,7 @@ 			whenM (liftIO $ hIsEOF h) $ 				giveup "Content is not available from glacier yet. Recommend you wait up to 4 hours, and then run this command again." 			sink =<< liftIO (L.hGetContents h)+	go' _ _ = error "internal"  remove :: Remote -> Remover remove r k = unlessM go $@@ -351,5 +358,10 @@ 		giveup wrongcmd   where 	test = proc "glacier" ["--compatibility-test-git-annex"]-	shouldfail = withQuietOutput createProcessSuccess test+	shouldfail = withNullHandle $ \nullh ->+		let p = test+			{ std_out = UseHandle nullh+			, std_err = UseHandle nullh+			}+		in withCreateProcess p $ \_ _ _ -> forceSuccessProcess p 	wrongcmd = "The glacier program in PATH seems to be from boto, not glacier-cli. Cannot use this program."
Remote/Helper/Chunked/Legacy.hs view
@@ -12,7 +12,6 @@ import Utility.Metered  import qualified Data.ByteString.Lazy as L-import qualified Control.Exception as E  {- This is an extension that's added to the usual file (or whatever)  - where the remote stores a key. -}@@ -87,8 +86,8 @@  - writes a whole L.ByteString at a time.  -} storeChunked :: ChunkSize -> [FilePath] -> (FilePath -> L.ByteString -> IO ()) -> L.ByteString -> IO [FilePath]-storeChunked chunksize dests storer content = either onerr return-	=<< (E.try (go (Just chunksize) dests) :: IO (Either E.SomeException [FilePath]))+storeChunked chunksize dests storer content = +	either onerr return =<< tryNonAsync (go (Just chunksize) dests)   where 	go _ [] = return [] -- no dests!? 	go Nothing (d:_) = do
Remote/Helper/Encryptable.hs view
@@ -59,7 +59,7 @@ 	, optionalStringParser cipherField HiddenField 	, optionalStringParser cipherkeysField HiddenField 	, optionalStringParser pubkeysField HiddenField-	, yesNoParser embedCredsField False+	, yesNoParser embedCredsField Nothing 		(FieldDesc "embed credentials into git repository") 	, macFieldParser 	, optionalStringParser (Accepted "keyid")@@ -242,7 +242,7 @@ embedCreds c = case getRemoteConfigValue embedCredsField c of 	Just v -> v 	Nothing -> case (getRemoteConfigValue cipherkeysField c, getRemoteConfigValue cipherField c) of-		(Just (_ :: ProposedAccepted String), Just (_ :: ProposedAccepted String)) -> True+		(Just (_ :: String), Just (_ :: String)) -> True 		_ -> False  {- Gets encryption Cipher, and key encryptor. -}
Remote/Helper/Ssh.hs view
@@ -29,7 +29,6 @@ import Control.Concurrent.STM import Control.Concurrent.Async import qualified Data.ByteString as B-import Data.Unique  toRepo :: ConsumeStdin -> Git.Repo -> RemoteGitConfig -> SshCommand -> Annex (FilePath, [CommandParam]) toRepo cs r gc remotecmd = do@@ -199,11 +198,13 @@  closeP2PSshConnection :: P2PSshConnection -> IO (P2PSshConnection, Maybe ExitCode) closeP2PSshConnection P2P.ClosedConnection = return (P2P.ClosedConnection, Nothing)-closeP2PSshConnection (P2P.OpenConnection (_st, conn, pid, stderrhandlerst)) = do-	P2P.closeConnection conn-	atomically $ writeTVar stderrhandlerst EndStderrHandler-	exitcode <- waitForProcess pid-	return (P2P.ClosedConnection, Just exitcode)+closeP2PSshConnection (P2P.OpenConnection (_st, conn, pid, stderrhandlerst)) =+	-- mask async exceptions, avoid cleanup being interrupted+	uninterruptibleMask_ $ do+		P2P.closeConnection conn+		atomically $ writeTVar stderrhandlerst EndStderrHandler+		exitcode <- waitForProcess pid+		return (P2P.ClosedConnection, Just exitcode)  -- Pool of connections over ssh to git-annex-shell p2pstdio. type P2PSshConnectionPool = TVar (Maybe P2PSshConnectionPoolState)@@ -262,16 +263,14 @@ 				, std_out = CreatePipe 				, std_err = CreatePipe 				}-		-- Could use getPid, but need to build with older versions-		-- of process, so instead a unique connection number.-		connnum <- hashUnique <$> newUnique+		pidnum <- getPid pid 		let conn = P2P.P2PConnection 			{ P2P.connRepo = repo 			, P2P.connCheckAuth = const False 			, P2P.connIhdl = to 			, P2P.connOhdl = from 			, P2P.connIdent = P2P.ConnIdent $-				Just $ "ssh connection " ++ show connnum+				Just $ "ssh connection " ++ show pidnum 			} 		stderrhandlerst <- newStderrHandler err 		runst <- P2P.mkRunState P2P.Client
Remote/List.hs view
@@ -21,7 +21,6 @@ import Remote.Helper.ReadOnly import Remote.Helper.ExportImport import qualified Git-import qualified Git.Config  import qualified Remote.Git import qualified Remote.GCrypt@@ -89,17 +88,6 @@ 	process m t = enumerate t autoinit  		>>= mapM (remoteGen m t)  		>>= return . catMaybes--{- Forces the remoteList to be re-generated, re-reading the git config. -}-remoteListRefresh :: Annex [Remote]-remoteListRefresh = do-	newg <- inRepo Git.Config.reRead-	Annex.changeState $ \s -> s -		{ Annex.remotes = []-		, Annex.gitremotes = Nothing-		, Annex.repo = newg-		}-	remoteList  {- Generates a Remote. -} remoteGen :: M.Map UUID RemoteConfig -> RemoteType -> Git.Repo -> Annex (Maybe Remote)
+ Remote/List/Util.hs view
@@ -0,0 +1,24 @@+{- git-annex remote list utils+ -+ - Copyright 2011-2020 Joey Hess <id@joeyh.name>+ -+ - Licensed under the GNU AGPL version 3 or higher.+ -}++module Remote.List.Util where++import Annex.Common+import qualified Annex+import qualified Git.Config++{- Call when remotes have changed. Re-reads the git config, and+ - invalidates the cache so the remoteList will be re-generated next time+ - it's used. -}+remotesChanged :: Annex ()+remotesChanged = do+	newg <- inRepo Git.Config.reRead+	Annex.changeState $ \s -> s +		{ Annex.remotes = []+		, Annex.gitremotes = Nothing+		, Annex.repo = newg+		}
Remote/Rsync.hs view
@@ -128,8 +128,8 @@ -- Things used by genRsyncOpts rsyncRemoteConfigs :: [RemoteConfigFieldParser] rsyncRemoteConfigs = -	[ yesNoParser shellEscapeField True-		(FieldDesc "avoid usual shell escaping (not recommended)")+	[ yesNoParser shellEscapeField (Just True)+		(FieldDesc "set to no to avoid usual shell escaping (not recommended)") 	]  genRsyncOpts :: ParsedRemoteConfig -> RemoteGitConfig -> Annex [CommandParam] -> RsyncUrl -> RsyncOpts@@ -288,10 +288,12 @@ 	-- note: Does not currently differentiate between rsync failing 	-- to connect, and the file not being present. 	untilTrue rsyncurls $ \u -> -		liftIO $ catchBoolIO $ do-			withQuietOutput createProcessSuccess $-				proc "rsync" $ toCommand $ opts ++ [Param u]-			return True+		liftIO $ catchBoolIO $ withNullHandle $ \nullh ->+			let p = (proc "rsync" $ toCommand $ opts ++ [Param u])+				{ std_out = UseHandle nullh+				, std_err = UseHandle nullh+				}+			in withCreateProcess p $ \_ _ _ -> checkSuccessProcess  storeExportM :: RsyncOpts -> FilePath -> Key -> ExportLocation -> MeterUpdate -> Annex () storeExportM o src _k loc meterupdate =
Remote/S3.hs view
@@ -84,13 +84,13 @@ 			, optionalStringParser partsizeField 				(FieldDesc "part size for multipart upload (eg 1GiB)") 			, optionalStringParser storageclassField-				(FieldDesc "storage class, eg STANDARD or REDUCED_REDUNDANCY")+				(FieldDesc "storage class, eg STANDARD or STANDARD_IA or ONEZONE_IA") 			, optionalStringParser fileprefixField 				(FieldDesc "prefix to add to filenames in the bucket")-			, yesNoParser versioningField False+			, yesNoParser versioningField (Just False) 				(FieldDesc "enable versioning of bucket content")-			, yesNoParser publicField False-				(FieldDesc "allow public read access to the buckey")+			, yesNoParser publicField (Just False)+				(FieldDesc "allow public read access to the bucket") 			, optionalStringParser publicurlField 				(FieldDesc "url that can be used by public to download files") 			, optionalStringParser protocolField@@ -157,7 +157,7 @@  signatureVersionParser :: RemoteConfigField -> FieldDesc -> RemoteConfigFieldParser signatureVersionParser f fd =-	genParser go f defver fd+	genParser go f (Just defver) fd 		(Just (ValueDesc "v2 or v4"))   where 	go "v2" = Just (SignatureVersion 2)@@ -939,7 +939,6 @@  getStorageClass :: ParsedRemoteConfig -> S3.StorageClass getStorageClass c = case getRemoteConfigValue storageclassField c of-	Just "REDUCED_REDUNDANCY" -> S3.ReducedRedundancy 	Just s -> S3.OtherStorageClass (T.pack s) 	_ -> S3.Standard 
RemoteDaemon/Transport/Ssh.hs view
@@ -37,14 +37,15 @@  transportUsingCmd' :: FilePath -> [CommandParam] -> Transport transportUsingCmd' cmd params (RemoteRepo r gc) url transporthandle ichan ochan =-	robustConnection 1 $ do-		(Just toh, Just fromh, Just errh, pid) <--			createProcess (proc cmd (toCommand params))-			{ std_in = CreatePipe-			, std_out = CreatePipe-			, std_err = CreatePipe-			}-		+	robustConnection 1 $ withCreateProcess p go+  where+	p = (proc cmd (toCommand params))+		{ std_in = CreatePipe+		, std_out = CreatePipe+		, std_err = CreatePipe+		}++	go (Just toh) (Just fromh) (Just errh) pid = do 		-- Run all threads until one finishes and get the status 		-- of the first to finish. Cancel the rest. 		status <- catchDefaultIO (Right ConnectionClosed) $@@ -58,7 +59,8 @@ 		void $ waitForProcess pid  		return $ either (either id id) id status-  where+	go _ _ _ _ = error "internal"+ 	send msg = atomically $ writeTChan ochan msg  	fetch = do
Test.hs view
@@ -135,8 +135,8 @@ 		pp <- Annex.Path.programPath 		Utility.Env.Set.setEnv subenv "1" True 		ps <- getArgs-		(Nothing, Nothing, Nothing, pid) <- createProcess (proc pp ps)-		exitcode <- waitForProcess pid+		exitcode <- withCreateProcess (proc pp ps) $+			\_ _ _ pid -> waitForProcess pid 		unless (keepFailuresOption opts) finalCleanup 		exitWith exitcode 	runsubprocesstests (Just _) = isolateGitConfig $ do
Test/Framework.hs view
@@ -458,9 +458,9 @@ 		] runFakeSsh :: [String] -> IO () runFakeSsh ("-n":ps) = runFakeSsh ps-runFakeSsh (_host:cmd:[]) = do-	(_, _, _, pid) <- createProcess (shell cmd)-	exitWith =<< waitForProcess pid+runFakeSsh (_host:cmd:[]) =+	withCreateProcess (shell cmd) $+		\_ _ _ pid -> exitWith =<< waitForProcess pid runFakeSsh ps = error $ "fake ssh option parse error: " ++ show ps  getTestMode :: IO TestMode
Types/Crypto.hs view
@@ -36,6 +36,7 @@  -- XXX ideally, this would be a locked memory region data Cipher = Cipher String | MacOnlyCipher String+	deriving (Show) -- XXXDO NOT COMMIT  data StorableCipher 	= EncryptedCipher String EncryptedCipherVariant KeyIds
Types/GitConfig.hs view
@@ -123,6 +123,7 @@ 	, annexCacheCreds :: Bool 	, annexAutoUpgradeRepository :: Bool 	, annexCommitMode :: CommitMode+	, annexSkipUnknown :: Bool 	, coreSymlinks :: Bool 	, coreSharedRepository :: SharedRepository 	, receiveDenyCurrentBranch :: DenyCurrentBranch@@ -214,6 +215,7 @@ 	, annexCommitMode = if getbool (annexConfig "allowsign") False 		then ManualCommit 		else AutomaticCommit+	, annexSkipUnknown = getbool (annexConfig "skipunknown") True 	, coreSymlinks = getbool "core.symlinks" True 	, coreSharedRepository = getSharedRepository r 	, receiveDenyCurrentBranch = getDenyCurrentBranch r
Types/WorkerPool.hs view
@@ -82,12 +82,13 @@ 	, stageSet = S.fromList [PerformStage, CleanupStage] 	} --- | When a command is transferring content, it can use this instead.--- Transfers are often bottlenecked on the network another disk than the one--- containing the repository, while verification bottlenecks on--- the disk containing the repository or on the CPU.-transferStages :: UsedStages-transferStages = UsedStages+-- | When a command is downloading content, it can use this instead.+-- Downloads are often bottlenecked on the network or another disk+-- than the one containing the repository, while verification bottlenecks+-- on the disk containing the repository or on the CPU. So, run the+-- transfer and verify stage separately.+downloadStages :: UsedStages+downloadStages = UsedStages 	{ initialStage = TransferStage 	, stageSet = S.fromList [TransferStage, VerifyStage] 	}
Upgrade.hs view
@@ -15,6 +15,7 @@ import Config import Annex.Path import Annex.Version+import Annex.GitOverlay import Types.RepoVersion #ifndef mingw32_HOST_OS import qualified Upgrade.V0@@ -104,7 +105,7 @@ 	upgraderemote = do 		rp <- fromRawFilePath <$> fromRepo Git.repoPath 		cmd <- liftIO programPath-		liftIO $ boolSystem' cmd+		runsGitAnnexChildProcess $ liftIO $ boolSystem' cmd 			[ Param "upgrade" 			, Param "--quiet" 			, Param "--autoonly"
Upgrade/V1.hs view
@@ -85,7 +85,7 @@ updateSymlinks = do 	showAction "updating symlinks" 	top <- fromRepo Git.repoPath-	(files, cleanup) <- inRepo $ LsFiles.inRepo [top]+	(files, cleanup) <- inRepo $ LsFiles.inRepo [] [top] 	forM_ files (fixlink . fromRawFilePath) 	void $ liftIO cleanup   where
Utility/Batch.hs view
@@ -1,6 +1,6 @@ {- Running a long or expensive batch operation niced.  -- - Copyright 2013 Joey Hess <id@joeyh.name>+ - Copyright 2013-2020 Joey Hess <id@joeyh.name>  -  - License: BSD-2-clause  -}@@ -22,7 +22,6 @@ import Control.Concurrent.Async import System.Posix.Process #endif-import qualified Control.Exception as E  {- Runs an operation, at batch priority.  -@@ -75,11 +74,7 @@ 	return $ batchmaker v  {- Runs a command in a way that's suitable for batch jobs that can be- - interrupted.- -- - If the calling thread receives an async exception, it sends the- - command a SIGTERM, and after the command finishes shuttting down,- - it re-raises the async exception. -}+ - interrupted. -} batchCommand :: String -> [CommandParam] -> IO Bool batchCommand command params = batchCommandEnv command params Nothing @@ -87,13 +82,4 @@ batchCommandEnv command params environ = do 	batchmaker <- getBatchCommandMaker 	let (command', params') = batchmaker (command, params)-	let p = proc command' $ toCommand params'-	(_, _, _, pid) <- createProcess $ p { env = environ }-	r <- E.try (waitForProcess pid) :: IO (Either E.SomeException ExitCode)-	case r of-		Right ExitSuccess -> return True-		Right _ -> return False-		Left asyncexception -> do-			terminateProcess pid-			void $ waitForProcess pid-			E.throwIO asyncexception+	boolSystemEnv command' params' environ
Utility/CoProcess.hs view
@@ -1,7 +1,7 @@ {- Interface for running a shell command as a coprocess,  - sending it queries and getting back results.  -- - Copyright 2012-2013 Joey Hess <id@joeyh.name>+ - Copyright 2012-2020 Joey Hess <id@joeyh.name>  -  - License: BSD-2-clause  -}@@ -62,28 +62,46 @@ 	let p = proc (coProcessCmd $ coProcessSpec s) (coProcessParams $ coProcessSpec s) 	forceSuccessProcess p (coProcessPid s) -{- To handle a restartable process, any IO exception thrown by the send and+{- Note that concurrent queries are not safe to perform; caller should+ - serialize calls to query.+ -+ - To handle a restartable process, any IO exception thrown by the send and  - receive actions are assumed to mean communication with the process- - failed, and the failed action is re-run with a new process. -}+ - failed, and the query is re-run with a new process.+ -+ - If an async exception is received during a query, the state of+ - communication with the process is unknown, so it is killed, and a new+ - one started so the CoProcessHandle can continue to be used by other+ - threads.+ -} query :: CoProcessHandle -> (Handle -> IO a) -> (Handle -> IO b) -> IO b-query ch send receive = do-	s <- readMVar ch-	restartable s (send $ coProcessTo s) $ const $-		restartable s (hFlush $ coProcessTo s) $ const $-			restartable s (receive $ coProcessFrom s)-				return+query ch send receive = uninterruptibleMask $ \unmask ->+	unmask (readMVar ch >>= restartable)+		`catchAsync` forcerestart   where-	restartable s a cont+	go s = do+		void $ send $ coProcessTo s+		hFlush $ coProcessTo s+		receive $ coProcessFrom s+	+	restartable s 		| coProcessNumRestarts (coProcessSpec s) > 0 =-			maybe restart cont =<< catchMaybeIO a-		| otherwise = cont =<< a-	restart = do-		s <- takeMVar ch-		void $ catchMaybeIO $ do+			catchMaybeIO (go s)+				>>= maybe (restart s increstarts restartable) return+		| otherwise = go s+	+	increstarts s = s { coProcessNumRestarts = coProcessNumRestarts s - 1 }++	restart s f cont = do+		void $ tryNonAsync $ do 			hClose $ coProcessTo s 			hClose $ coProcessFrom s 		void $ waitForProcess $ coProcessPid s-		s' <- start' $ (coProcessSpec s)-			{ coProcessNumRestarts = coProcessNumRestarts (coProcessSpec s) - 1 }-		putMVar ch s'-		query ch send receive+		s' <- withMVarMasked ch $ \_ -> start' (f (coProcessSpec s))+		cont s'++	forcerestart ex = do+		s <- readMVar ch+		terminateProcess (coProcessPid s)+		restart s id $ \s' -> void $ swapMVar ch s'+		either throwM throwM ex
Utility/CopyFile.hs view
@@ -57,11 +57,12 @@ 		void $ tryIO $ removeFile dest 		-- When CoW is not supported, cp will complain to stderr, 		-- so have to discard its stderr.-		ok <- catchBoolIO $ do-			withQuietOutput createProcessSuccess $-				proc "cp" $ toCommand $-					params ++ [File src, File dest]-			return True+		ok <- catchBoolIO $ withNullHandle $ \nullh ->+			let p = (proc "cp" $ toCommand $ params ++ [File src, File dest])+				{ std_out = UseHandle nullh+				, std_err = UseHandle nullh+				}+			in withCreateProcess p $ \_ _ _ -> checkSuccessProcess 		-- When CoW is not supported, cp creates the destination 		-- file but leaves it empty. 		unless ok $
Utility/Env/Set.hs view
@@ -10,6 +10,7 @@ module Utility.Env.Set ( 	setEnv, 	unsetEnv,+	legalInEnvVar, ) where  #ifdef mingw32_HOST_OS@@ -18,6 +19,7 @@ #else import qualified System.Posix.Env as PE #endif+import Data.Char  {- Sets an environment variable. To overwrite an existing variable,  - overwrite must be True.@@ -41,3 +43,7 @@ #else unsetEnv = System.SetEnv.unsetEnv #endif++legalInEnvVar :: Char -> Bool+legalInEnvVar '_' = True+legalInEnvVar c = isAsciiLower c || isAsciiUpper c || (isNumber c && isAscii c)
Utility/Exception.hs view
@@ -1,6 +1,6 @@ {- Simple IO exception handling (and some more)  -- - Copyright 2011-2016 Joey Hess <id@joeyh.name>+ - Copyright 2011-2020 Joey Hess <id@joeyh.name>  -  - License: BSD-2-clause  -}@@ -20,6 +20,7 @@ 	bracketIO, 	catchNonAsync, 	tryNonAsync,+	catchAsync, 	tryWhenExists, 	catchIOErrorType, 	IOErrorType(..),@@ -85,6 +86,14 @@ 	[ M.Handler (\ (e :: AsyncException) -> throwM e) 	, M.Handler (\ (e :: SomeAsyncException) -> throwM e) 	, M.Handler (\ (e :: SomeException) -> onerr e)+	]++{- Catches only async exceptions. -}+catchAsync :: MonadCatch m => m a -> (Either AsyncException SomeAsyncException -> m a) -> m a+catchAsync a onerr = a `catches`+	[ M.Handler (\ (e :: AsyncException) -> onerr (Left e))+	, M.Handler (\ (e :: SomeAsyncException) -> onerr (Right e))+	, M.Handler (\ (e :: SomeException) -> throwM e) 	]  tryNonAsync :: MonadCatch m => m a -> m (Either SomeException a)
Utility/Gpg.hs view
@@ -17,7 +17,7 @@ 	stdEncryptionParams, 	pipeStrict, 	feedRead,-	pipeLazy,+	feedRead', 	findPubKeys, 	UserId, 	secretKeys,@@ -47,7 +47,7 @@ #endif import Utility.Format (decode_c) -import Control.Concurrent+import Control.Concurrent.Async import Control.Monad.IO.Class import qualified Data.Map as M import Data.Char@@ -112,21 +112,33 @@ readStrict :: GpgCmd -> [CommandParam] -> IO String readStrict (GpgCmd cmd) params = do 	params' <- stdParams params-	withHandle StdoutHandle createProcessSuccess (proc cmd params') $ \h -> do-		hSetBinaryMode h True-		hGetContentsStrict h+	let p = (proc cmd params')+		{ std_out = CreatePipe }+	withCreateProcess p (go p)+  where+	go p _ (Just hout) _ pid = do+		hSetBinaryMode hout True+		forceSuccessProcess p pid `after` hGetContentsStrict hout+	go _ _ _ _ _ = error "internal"  {- Runs gpg, piping an input value to it, and returning its stdout,  - strictly. -} pipeStrict :: GpgCmd -> [CommandParam] -> String -> IO String pipeStrict (GpgCmd cmd) params input = do 	params' <- stdParams params-	withIOHandles createProcessSuccess (proc cmd params') $ \(to, from) -> do+	let p = (proc cmd params')+		{ std_in = CreatePipe+		, std_out = CreatePipe+		}+	withCreateProcess p (go p)+  where+	go p (Just to) (Just from) _ pid = do 		hSetBinaryMode to True 		hSetBinaryMode from True 		hPutStr to input 		hClose to-		hGetContentsStrict from+		forceSuccessProcess p pid `after` hGetContentsStrict from+	go _ _ _ _ _ = error "internal"  {- Runs gpg with some parameters. First sends it a passphrase (unless it  - is empty) via '--passphrase-fd'. Then runs a feeder action that is@@ -137,20 +149,26 @@  - Runs gpg in batch mode; this is necessary to avoid gpg 2.x prompting for  - the passphrase.  -- - Note that to avoid deadlock with the cleanup stage,- - the reader must fully consume gpg's input before returning. -}+ - Note that the reader must fully consume gpg's input before returning. -} feedRead :: (MonadIO m, MonadMask m) => GpgCmd -> [CommandParam] -> String -> (Handle -> IO ()) -> (Handle -> m a) -> m a feedRead cmd params passphrase feeder reader = do #ifndef mingw32_HOST_OS-	-- pipe the passphrase into gpg on a fd-	(frompipe, topipe) <- liftIO System.Posix.IO.createPipe-	liftIO $ void $ forkIO $ do+	let setup = liftIO $ do+		-- pipe the passphrase into gpg on a fd+		(frompipe, topipe) <- System.Posix.IO.createPipe 		toh <- fdToHandle topipe-		hPutStrLn toh passphrase+		t <- async $ do+			hPutStrLn toh passphrase+			hClose toh+		let Fd pfd = frompipe+		let passphrasefd = [Param "--passphrase-fd", Param $ show pfd]+		return (passphrasefd, frompipe, toh, t)+	let cleanup (_, frompipe, toh, t) = liftIO $ do+		closeFd frompipe 		hClose toh-	let Fd pfd = frompipe-	let passphrasefd = [Param "--passphrase-fd", Param $ show pfd]-	liftIO (closeFd frompipe) `after` go (passphrasefd ++ params)+		cancel t+	bracket setup cleanup $ \(passphrasefd, _, _, _) -> +		go (passphrasefd ++ params) #else 	-- store the passphrase in a temp file for gpg 	withTmpFile "gpg" $ \tmpfile h -> do@@ -160,27 +178,31 @@ 		go $ passphrasefile ++ params #endif   where-	go params' = pipeLazy cmd params' feeder reader+	go params' = feedRead' cmd params' feeder reader  {- Like feedRead, but without passphrase. -}-pipeLazy :: (MonadIO m, MonadMask m) => GpgCmd -> [CommandParam] -> (Handle -> IO ()) -> (Handle -> m a) -> m a-pipeLazy (GpgCmd cmd) params feeder reader = do+feedRead' :: (MonadIO m, MonadMask m) => GpgCmd -> [CommandParam] -> (Handle -> IO ()) -> (Handle -> m a) -> m a+feedRead' (GpgCmd cmd) params feeder reader = do 	params' <- liftIO $ stdParams $ Param "--batch" : params 	let p = (proc cmd params') 		{ std_in = CreatePipe 		, std_out = CreatePipe 		, std_err = Inherit 		}-	bracket (setup p) (cleanup p) go+	bracket (setup p) cleanup (go p)   where 	setup = liftIO . createProcess-	cleanup p (_, _, _, pid) = liftIO $ forceSuccessProcess p pid-	go p = do-		let (to, from) = ioHandles p-		liftIO $ void $ forkIO $ do+	cleanup = liftIO . cleanupProcess++	go p (Just to, Just from, _, pid) =+		let runfeeder = do 			feeder to 			hClose to-		reader from+		in bracketIO (async runfeeder) cancel $ const $ do+			r <- reader from+			liftIO $ forceSuccessProcess p pid+			return r+	go _ _ = error "internal"  {- Finds gpg public keys matching some string. (Could be an email address,  - a key id, or a name; See the section 'HOW TO SPECIFY A USER ID' of@@ -241,10 +263,13 @@  -} genSecretKey :: GpgCmd -> KeyType -> Passphrase -> UserId -> Size -> IO () genSecretKey (GpgCmd cmd) keytype passphrase userid keysize =-	withHandle StdinHandle createProcessSuccess (proc cmd params) feeder+	let p = (proc cmd params)+		{ std_in = CreatePipe }+	in withCreateProcess p (go p)   where 	params = ["--batch", "--gen-key"]-	feeder h = do++	go p (Just h) _ _ pid = do 		hPutStr h $ unlines $ catMaybes 			[ Just $  "Key-Type: " ++  				case keytype of@@ -259,6 +284,8 @@ 				else Just $ "Passphrase: " ++ passphrase 			] 		hClose h+		forceSuccessProcess p pid+	go _ _ _ _ _ = error "internal"  {- Creates a block of high-quality random data suitable to use as a cipher.  - It is armored, to avoid newlines, since gpg only reads ciphers up to the
Utility/LockFile/PidLock.hs view
@@ -1,6 +1,6 @@ {- pid-based lock files  -- - Copyright 2015 Joey Hess <id@joeyh.name>+ - Copyright 2015-2020 Joey Hess <id@joeyh.name>  -  - License: BSD-2-clause  -}@@ -14,6 +14,7 @@ 	getLockStatus, 	checkLocked, 	checkSaneLock,+	pidLockEnv, ) where  import Utility.PartialPrelude@@ -27,10 +28,15 @@ import Utility.ThreadScheduler import Utility.Hash import Utility.FileSystemEncoding+import Utility.Env+import Utility.Env.Set import qualified Utility.LockFile.Posix as Posix  import System.IO-import System.Posix+import System.Posix.IO+import System.Posix.Types+import System.Posix.Files+import System.Posix.Process import Data.Maybe import Data.List import Network.BSD@@ -40,7 +46,9 @@  type LockFile = FilePath -data LockHandle = LockHandle LockFile FileStatus SideLockHandle+data LockHandle+	= LockHandle LockFile FileStatus SideLockHandle+	| ParentLocked  type SideLockHandle = Maybe (LockFile, Posix.LockHandle) @@ -115,40 +123,49 @@ -- However, if the lock file is on a networked file system, and was -- created on a different host than the current host (determined by hostname), -- this can't be done and stale locks may persist.+--+-- If a parent process is holding the lock, determined by a+-- "PIDLOCK_lockfile" environment variable, does not block either. tryLock :: LockFile -> IO (Maybe LockHandle)-tryLock lockfile = trySideLock lockfile $ \sidelock -> do-	lockfile' <- absPath lockfile-	(tmp, h) <- openTempFile (takeDirectory lockfile') "locktmp"-	setFileMode tmp (combineModes readModes)-	hPutStr h . show =<< mkPidLock-	hClose h-	let failedlock st = do-		dropLock $ LockHandle tmp st sidelock-		nukeFile tmp-		return Nothing-	let tooklock st = return $ Just $ LockHandle lockfile' st sidelock-	ifM (linkToLock sidelock tmp lockfile')-		( do+tryLock lockfile = do+	abslockfile <- absPath lockfile+	lockenv <- pidLockEnv abslockfile+	getEnv lockenv >>= \case+		Nothing -> trySideLock lockfile (go abslockfile)+		_ -> return (Just ParentLocked)+  where+	go abslockfile sidelock = do+		(tmp, h) <- openTempFile (takeDirectory abslockfile) "locktmp"+		setFileMode tmp (combineModes readModes)+		hPutStr h . show =<< mkPidLock+		hClose h+		let failedlock st = do+			dropLock $ LockHandle tmp st sidelock 			nukeFile tmp-			-- May not have made a hard link, so stat-			-- the lockfile-			lckst <- getFileStatus lockfile'-			tooklock lckst-		, do-			v <- readPidLock lockfile'-			hn <- getHostName-			tmpst <- getFileStatus tmp-			case v of-				Just pl | isJust sidelock && hn == lockingHost pl -> do-					-- Since we have the sidelock,-					-- and are on the same host that-					-- the pidlock was taken on,-					-- we know that the pidlock is-					-- stale, and can take it over.-					rename tmp lockfile'-					tooklock tmpst-				_ -> failedlock tmpst-		)+			return Nothing+		let tooklock st = return $ Just $ LockHandle abslockfile st sidelock+		ifM (linkToLock sidelock tmp abslockfile)+			( do+				nukeFile tmp+				-- May not have made a hard link, so stat+				-- the lockfile+				lckst <- getFileStatus abslockfile+				tooklock lckst+			, do+				v <- readPidLock abslockfile+				hn <- getHostName+				tmpst <- getFileStatus tmp+				case v of+					Just pl | isJust sidelock && hn == lockingHost pl -> do+						-- Since we have the sidelock,+						-- and are on the same host that+						-- the pidlock was taken on,+						-- we know that the pidlock is+						-- stale, and can take it over.+						rename tmp abslockfile+						tooklock tmpst+					_ -> failedlock tmpst+			)  -- Linux's open(2) man page recommends linking a pid lock into place, -- as the most portable atomic operation that will fail if@@ -174,12 +191,13 @@ 				, return False 				) 		Left _ -> catchBoolIO $ do-			fd <- openFd dest WriteOnly-				(Just $ combineModes readModes)-				(defaultFileFlags {exclusive = True})-			h <- fdToHandle fd-			readFile src >>= hPutStr h-			hClose h+			let setup = do+				fd <- openFd dest WriteOnly+					(Just $ combineModes readModes)+					(defaultFileFlags {exclusive = True})+				fdToHandle fd+			let cleanup = hClose+			bracket setup cleanup (\h -> readFile src >>= hPutStr h) 			return True   where 	checklinked = do@@ -241,6 +259,7 @@ 	-- considered stale. 	dropSideLock sidelock 	nukeFile lockfile+dropLock ParentLocked = return ()  getLockStatus :: LockFile -> IO LockStatus getLockStatus = maybe StatusUnLocked (StatusLockedBy . lockingPid) <$$> readPidLock@@ -260,3 +279,17 @@ 	go Nothing = return False 	go (Just st') = return $ 		deviceID st == deviceID st' && fileID st == fileID st'+checkSaneLock _ ParentLocked = return True++-- | A parent process that is using pid locking can set this to 1 before+-- starting a child, to communicate to the child that it's holding the pid+-- lock and that the child can skip trying to take it, and not block+-- on the pid lock its parent is holding.+--+-- The parent process should keep running as long as the child+-- process is running, since the child inherits the environment and will+-- not see unsetLockEnv.+pidLockEnv :: FilePath -> IO String+pidLockEnv lockfile = do+	abslockfile <- absPath lockfile+	return $ "PIDLOCK_" ++ filter legalInEnvVar abslockfile
Utility/LockFile/Posix.hs view
@@ -58,7 +58,7 @@  -- Tries to take an lock, but does not block. tryLock :: LockRequest -> Maybe FileMode -> LockFile -> IO (Maybe LockHandle)-tryLock lockreq mode lockfile = do+tryLock lockreq mode lockfile = uninterruptibleMask_ $ do 	l <- openLockFile lockreq mode lockfile 	v <- tryIO $ setLock l (lockreq, AbsoluteSeek, 0, 0) 	case v of@@ -92,14 +92,17 @@ 		Just (Just pid) -> StatusLockedBy pid  getLockStatus' :: LockFile -> IO (Maybe (Maybe ProcessID))-getLockStatus' lockfile = go =<< catchMaybeIO open+getLockStatus' lockfile = bracket open close go   where-	open = openLockFile ReadLock Nothing lockfile-	go Nothing = return Nothing+	open = catchMaybeIO $ openLockFile ReadLock Nothing lockfile++	close (Just h) = closeFd h+	close Nothing = return ()+ 	go (Just h) = do 		v <- getLock h (ReadLock, AbsoluteSeek, 0, 0)-		closeFd h 		return (Just (fmap fst v))+	go Nothing = return Nothing  dropLock :: LockHandle -> IO () dropLock (LockHandle fd) = closeFd fd
Utility/Lsof.hs view
@@ -49,11 +49,16 @@  - Note: If lsof is not available, this always returns [] !  -} query :: [String] -> IO [(FilePath, LsofOpenMode, ProcessInfo)]-query opts =-	withHandle StdoutHandle (createProcessChecked checkSuccessProcess) p $-		parse <$$> hGetContentsStrict+query opts = withCreateProcess p go   where-	p = proc "lsof" ("-F0can" : opts)+	p = (proc "lsof" ("-F0can" : opts))+		{ std_out = CreatePipe }+	+	go _ (Just outh) _ pid = do+		r <- parse <$> hGetContentsStrict outh+		void $ waitForProcess pid+		return r+	go _ _ _ _ = error "internal"  type LsofParser = String -> [(FilePath, LsofOpenMode, ProcessInfo)] 
Utility/MagicWormhole.hs view
@@ -30,12 +30,12 @@ import Utility.Misc import Utility.Env import Utility.Path+import Utility.Exception  import System.IO import System.Exit import Control.Concurrent import Control.Concurrent.Async-import Control.Exception import Data.Char import Data.List import Control.Applicative@@ -146,26 +146,22 @@ wormHoleProcess = proc "wormhole" . toCommand  runWormHoleProcess :: CreateProcess -> (Handle -> Handle -> Handle -> IO Bool) ->  IO Bool-runWormHoleProcess p consumer = -	bracketOnError setup (\v -> cleanup v <&&> return False) go+runWormHoleProcess p consumer =+	withCreateProcess p' go `catchNonAsync` const (return False)   where-	setup = do-		(Just hin, Just hout, Just herr, pid)-			<- createProcess p-				{ std_in = CreatePipe-				, std_out = CreatePipe-				, std_err = CreatePipe-				}-		return (hin, hout, herr, pid)-	cleanup (hin, hout, herr, pid) = do+	p' = p +		{ std_in = CreatePipe+		, std_out = CreatePipe+		, std_err = CreatePipe+		}+	go (Just hin) (Just hout) (Just herr) pid =+		consumer hin hout herr <&&> waitbool pid+	go _ _ _ _ = error "internal"+	waitbool pid = do 		r <- waitForProcess pid-		hClose hin-		hClose hout-		hClose herr 		return $ case r of 			ExitSuccess -> True 			ExitFailure _ -> False-	go h@(hin, hout, herr, _) = consumer hin hout herr <&&> cleanup h  isInstalled :: IO Bool isInstalled = inPath "wormhole"
Utility/Metered.hs view
@@ -310,17 +310,21 @@ 	-> (Handle -> IO ()) 	-> (Handle -> IO ()) 	-> IO (Maybe ExitCode)-outputFilter cmd params environ outfilter errfilter = catchMaybeIO $ do-	(_, Just outh, Just errh, pid) <- createProcess p-		{ std_out = CreatePipe-		, std_err = CreatePipe-		}-	void $ async $ tryIO (outfilter outh) >> hClose outh-	void $ async $ tryIO (errfilter errh) >> hClose errh-	waitForProcess pid+outputFilter cmd params environ outfilter errfilter = +	catchMaybeIO $ withCreateProcess p go   where+	go _ (Just outh) (Just errh) pid = do+		void $ concurrently +			(tryIO (outfilter outh) >> hClose outh)+			(tryIO (errfilter errh) >> hClose errh)+		waitForProcess pid+	go _ _ _ _ = error "internal"+	 	p = (proc cmd (toCommand params))-		{ env = environ }+		{ env = environ+		, std_out = CreatePipe+		, std_err = CreatePipe+		}  -- | Limit a meter to only update once per unit of time. --
− Utility/Parallel.hs
@@ -1,34 +0,0 @@-{- parallel processing via threads- -- - Copyright 2012 Joey Hess <id@joeyh.name>- -- - License: BSD-2-clause- -}--module Utility.Parallel (inParallel) where--import Common--import Control.Concurrent--{- Runs an action in parallel with a set of values, in a set of threads.- - In order for the actions to truely run in parallel, requires GHC's- - threaded runtime, - -- - Returns the values partitioned into ones with which the action succeeded,- - and ones with which it failed. -}-inParallel :: (v -> IO Bool) -> [v] -> IO ([v], [v])-inParallel a l = do-	mvars <- mapM thread l-	statuses <- mapM takeMVar mvars-	return $ reduce $ partition snd $ zip l statuses-  where-	reduce (x,y) = (map fst x, map fst y)-	thread v = do-		mvar <- newEmptyMVar-		_ <- forkIO $ do-			r <- try (a v) :: IO (Either SomeException Bool)-			case r of-				Left _ -> putMVar mvar False-				Right b -> putMVar mvar b-		return mvar
Utility/Process.hs view
@@ -20,64 +20,55 @@ 	forceSuccessProcess, 	forceSuccessProcess', 	checkSuccessProcess,-	ignoreFailureProcess,-	createProcessSuccess,-	createProcessChecked,-	createBackgroundProcess,-	withHandle,-	withIOHandles,-	withOEHandles, 	withNullHandle,-	withQuietOutput,-	feedWithQuietOutput, 	createProcess,+	withCreateProcess, 	waitForProcess,+	cleanupProcess, 	startInteractiveProcess, 	stdinHandle, 	stdoutHandle, 	stderrHandle,-	ioHandles, 	processHandle, 	devNull, ) where  import qualified Utility.Process.Shim-import qualified Utility.Process.Shim as X hiding (CreateProcess(..), createProcess, runInteractiveProcess, readProcess, readProcessWithExitCode, system, rawSystem, runInteractiveCommand, runProcess)-import Utility.Process.Shim hiding (createProcess, readProcess, waitForProcess)+import qualified Utility.Process.Shim as X hiding (CreateProcess(..), createProcess, withCreateProcess, runInteractiveProcess, readProcess, readProcessWithExitCode, system, rawSystem, runInteractiveCommand, runProcess, cleanupProcess)+import Utility.Process.Shim hiding (createProcess, withCreateProcess, readProcess, waitForProcess, cleanupProcess) import Utility.Misc import Utility.Exception+import Utility.Monad  import System.Exit import System.IO import System.Log.Logger-import Control.Concurrent-import qualified Control.Exception as E-import Control.Monad+import Control.Monad.IO.Class+import Control.Concurrent.Async import qualified Data.ByteString as S -type CreateProcessRunner = forall a. CreateProcess -> ((Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> IO a) -> IO a- data StdHandle = StdinHandle | StdoutHandle | StderrHandle 	deriving (Eq)  -- | Normally, when reading from a process, it does not need to be fed any -- standard input. readProcess :: FilePath	-> [String] -> IO String-readProcess cmd args = readProcessEnv cmd args Nothing+readProcess cmd args = readProcess' (proc cmd args)  readProcessEnv :: FilePath -> [String] -> Maybe [(String, String)] -> IO String-readProcessEnv cmd args environ = readProcess' p-  where-	p = (proc cmd args)-		{ std_out = CreatePipe-		, env = environ-		}+readProcessEnv cmd args environ = +	readProcess' $ (proc cmd args) { env = environ }  readProcess' :: CreateProcess -> IO String-readProcess' p = withHandle StdoutHandle createProcessSuccess p $ \h -> do-	output  <- hGetContentsStrict h-	hClose h-	return output+readProcess' p = withCreateProcess p' go+  where+	p' = p { std_out = CreatePipe }+	go _ (Just h) _ pid = do+		output  <- hGetContentsStrict h+		hClose h+		forceSuccessProcess p' pid+		return output+	go _ _ _ _ = error "internal"  -- | Runs an action to write to a process on its stdin,  -- returns its output, and also allows specifying the environment.@@ -87,26 +78,7 @@ 	-> Maybe [(String, String)] 	-> (Maybe (Handle -> IO ())) 	-> IO S.ByteString-writeReadProcessEnv cmd args environ writestdin = do-	(Just inh, Just outh, _, pid) <- createProcess p--	-- fork off a thread to start consuming the output-	outMVar <- newEmptyMVar-	_ <- forkIO $ putMVar outMVar =<< S.hGetContents outh--	-- now write and flush any input-	maybe (return ()) (\a -> a inh >> hFlush inh) writestdin-	hClose inh -- done with stdin--	-- wait on the output-	output <- takeMVar outMVar-	hClose outh--	-- wait on the process-	forceSuccessProcess p pid--	return output-+writeReadProcessEnv cmd args environ writestdin = withCreateProcess p go   where 	p = (proc cmd args) 		{ std_in = CreatePipe@@ -114,7 +86,19 @@ 		, std_err = Inherit 		, env = environ 		}+	+	go (Just inh) (Just outh) _ pid = do+		let reader = hClose outh `after` S.hGetContents outh+		let writer = do+			maybe (return ()) (\a -> a inh >> hFlush inh) writestdin+			hClose inh+		(output, ()) <- concurrently reader writer +		forceSuccessProcess p pid++		return output+	go _ _ _ _ = error "internal"+ -- | Waits for a ProcessHandle, and throws an IOError if the process -- did not exit successfully. forceSuccessProcess :: CreateProcess -> ProcessHandle -> IO ()@@ -126,117 +110,15 @@ 	showCmd p ++ " exited " ++ show n  -- | Waits for a ProcessHandle and returns True if it exited successfully.--- Note that using this with createProcessChecked will throw away--- the Bool, and is only useful to ignore the exit code of a process,--- while still waiting for it. -} checkSuccessProcess :: ProcessHandle -> IO Bool checkSuccessProcess pid = do 	code <- waitForProcess pid 	return $ code == ExitSuccess -ignoreFailureProcess :: ProcessHandle -> IO Bool-ignoreFailureProcess pid = do-	void $ waitForProcess pid-	return True---- | Runs createProcess, then an action on its handles, and then--- forceSuccessProcess.-createProcessSuccess :: CreateProcessRunner-createProcessSuccess p a = createProcessChecked (forceSuccessProcess p) p a---- | Runs createProcess, then an action on its handles, and then--- a checker action on its exit code, which must wait for the process.-createProcessChecked :: (ProcessHandle -> IO b) -> CreateProcessRunner-createProcessChecked checker p a = do-	t@(_, _, _, pid) <- createProcess p-	r <- tryNonAsync $ a t-	_ <- checker pid-	either E.throw return r---- | Leaves the process running, suitable for lazy streaming.--- Note: Zombies will result, and must be waited on.-createBackgroundProcess :: CreateProcessRunner-createBackgroundProcess p a = a =<< createProcess p---- | Runs a CreateProcessRunner, on a CreateProcess structure, that--- is adjusted to pipe only from/to a single StdHandle, and passes--- the resulting Handle to an action.-withHandle-	:: StdHandle-	-> CreateProcessRunner-	-> CreateProcess-	-> (Handle -> IO a)-	-> IO a-withHandle h creator p a = creator p' $ a . select-  where-	base = p-		{ std_in = Inherit-		, std_out = Inherit-		, std_err = Inherit-		}-	(select, p') = case h of-		StdinHandle -> (stdinHandle, base { std_in = CreatePipe })-		StdoutHandle -> (stdoutHandle, base { std_out = CreatePipe })-		StderrHandle -> (stderrHandle, base { std_err = CreatePipe })---- | Like withHandle, but passes (stdin, stdout) handles to the action.-withIOHandles-	:: CreateProcessRunner-	-> CreateProcess-	-> ((Handle, Handle) -> IO a)-	-> IO a-withIOHandles creator p a = creator p' $ a . ioHandles-  where-	p' = p-		{ std_in = CreatePipe-		, std_out = CreatePipe-		, std_err = Inherit-		}---- | Like withHandle, but passes (stdout, stderr) handles to the action.-withOEHandles-	:: CreateProcessRunner-	-> CreateProcess-	-> ((Handle, Handle) -> IO a)-	-> IO a-withOEHandles creator p a = creator p' $ a . oeHandles-  where-	p' = p-		{ std_in = Inherit-		, std_out = CreatePipe-		, std_err = CreatePipe-		}--withNullHandle :: (Handle -> IO a) -> IO a-withNullHandle = withFile devNull WriteMode---- | Forces the CreateProcessRunner to run quietly;--- both stdout and stderr are discarded.-withQuietOutput-	:: CreateProcessRunner-	-> CreateProcess-	-> IO ()-withQuietOutput creator p = withNullHandle $ \nullh -> do-	let p' = p-		{ std_out = UseHandle nullh-		, std_err = UseHandle nullh-		}-	creator p' $ const $ return ()---- | Stdout and stderr are discarded, while the process is fed stdin--- from the handle.-feedWithQuietOutput-	:: CreateProcessRunner-	-> CreateProcess-	-> (Handle -> IO a)-	-> IO a-feedWithQuietOutput creator p a = withFile devNull WriteMode $ \nullh -> do-	let p' = p-		{ std_in = CreatePipe-		, std_out = UseHandle nullh-		, std_err = UseHandle nullh-		}-	creator p' $ a . stdinHandle+withNullHandle :: (MonadIO m, MonadMask m) => (Handle -> m a) -> m a+withNullHandle = bracket+	(liftIO $ openFile devNull WriteMode)+	(liftIO . hClose)  devNull :: FilePath #ifndef mingw32_HOST_OS@@ -252,6 +134,7 @@ -- Get it wrong and the runtime crash will always happen, so should be -- easily noticed. type HandleExtractor = (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> Handle+ stdinHandle :: HandleExtractor stdinHandle (Just h, _, _, _) = h stdinHandle _ = error "expected stdinHandle"@@ -261,12 +144,6 @@ stderrHandle :: HandleExtractor stderrHandle (_, _, Just h, _) = h stderrHandle _ = error "expected stderrHandle"-ioHandles :: (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> (Handle, Handle)-ioHandles (Just hin, Just hout, _, _) = (hin, hout)-ioHandles _ = error "expected ioHandles"-oeHandles :: (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> (Handle, Handle)-oeHandles (_, Just hout, Just herr, _) = (hout, herr)-oeHandles _ = error "expected oeHandles"  processHandle :: (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> ProcessHandle processHandle (_, _, _, pid) = pid@@ -301,6 +178,11 @@ 	debugProcess p 	Utility.Process.Shim.createProcess p +-- | Wrapper around 'System.Process.withCreateProcess' that does debug logging.+withCreateProcess :: CreateProcess -> (Maybe Handle -> Maybe Handle -> Maybe Handle -> ProcessHandle -> IO a) -> IO a+withCreateProcess p action = bracket (createProcess p) cleanupProcess+	(\(m_in, m_out, m_err, ph) -> action m_in m_out m_err ph)+ -- | Debugging trace for a CreateProcess. debugProcess :: CreateProcess -> IO () debugProcess p = debugM "Utility.Process" $ unwords@@ -322,3 +204,21 @@ 	r <- Utility.Process.Shim.waitForProcess h 	debugM "Utility.Process" ("process done " ++ show r) 	return r++cleanupProcess :: (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> IO () +#if MIN_VERSION_process(1,6,4)+cleanupProcess = Utility.Process.Shim.cleanupProcess+#else+#warning building with process-1.6.3; some timeout features may not work well+cleanupProcess (mb_stdin, mb_stdout, mb_stderr, pid) = do+	-- Unlike the real cleanupProcess, this does not wait+	-- for the process to finish in the background, so if+	-- the process ignores SIGTERM, this can block until the process+	-- gets around the exiting.+	terminateProcess pid+	let void _ = return ()+	maybe (return ()) (void . tryNonAsync . hClose) mb_stdin+	maybe (return ()) hClose mb_stdout+	maybe (return ()) hClose mb_stderr+	void $ waitForProcess pid+#endif
Utility/Process/Transcript.hs view
@@ -1,6 +1,6 @@ {- Process transcript  -- - Copyright 2012-2018 Joey Hess <id@joeyh.name>+ - Copyright 2012-2020 Joey Hess <id@joeyh.name>  -  - License: BSD-2-clause  -}@@ -21,6 +21,7 @@ import System.Exit import Control.Concurrent.Async import Control.Monad+import Control.Exception #ifndef mingw32_HOST_OS import qualified System.Posix.IO #else@@ -45,36 +46,44 @@ #ifndef mingw32_HOST_OS {- This implementation interleves stdout and stderr in exactly the order  - the process writes them. -}-	(readf, writef) <- System.Posix.IO.createPipe-	System.Posix.IO.setFdOption readf System.Posix.IO.CloseOnExec True-	System.Posix.IO.setFdOption writef System.Posix.IO.CloseOnExec True-	readh <- System.Posix.IO.fdToHandle readf-	writeh <- System.Posix.IO.fdToHandle writef-	p@(_, _, _, pid) <- createProcess $ cp-		{ std_in = if isJust input then CreatePipe else Inherit-		, std_out = UseHandle writeh-		, std_err = UseHandle writeh-		}-	hClose writeh--	get <- asyncreader readh-	writeinput input p-	transcript <- wait get+ 	let setup = do+		(readf, writef) <- System.Posix.IO.createPipe+		System.Posix.IO.setFdOption readf System.Posix.IO.CloseOnExec True+		System.Posix.IO.setFdOption writef System.Posix.IO.CloseOnExec True+		readh <- System.Posix.IO.fdToHandle readf+		writeh <- System.Posix.IO.fdToHandle writef+		return (readh, writeh)+	let cleanup (readh, writeh) = do+		hClose readh+		hClose writeh+	bracket setup cleanup $ \(readh, writeh) -> do+		let cp' = cp+			{ std_in = if isJust input then CreatePipe else Inherit+			, std_out = UseHandle writeh+			, std_err = UseHandle writeh+			}+		withCreateProcess cp' $ \hin hout herr pid -> do+			get <- asyncreader readh+			writeinput input (hin, hout, herr, pid)+			transcript <- wait get+			code <- waitForProcess pid+			return (transcript, code) #else {- This implementation for Windows puts stderr after stdout. -}-	p@(_, _, _, pid) <- createProcess $ cp+	let cp' = cp  		{ std_in = if isJust input then CreatePipe else Inherit 		, std_out = CreatePipe 		, std_err = CreatePipe 		}--	getout <- asyncreader (stdoutHandle p)-	geterr <- asyncreader (stderrHandle p)-	writeinput input p-	transcript <- (++) <$> wait getout <*> wait geterr+	withCreateProcess cp' \hin hout herr pid -> do+		let p = (hin, hout, herr, pid)+		getout <- asyncreader (stdoutHandle p)+		geterr <- asyncreader (stderrHandle p)+		writeinput input p+		transcript <- (++) <$> wait getout <*> wait geterr+		code <- waitForProcess pid+		return (transcript, code) #endif-	code <- waitForProcess pid-	return (transcript, code)   where 	asyncreader = async . hGetContentsStrict 
Utility/SafeCommand.hs view
@@ -81,9 +81,9 @@ safeSystem command params = safeSystem' command params id  safeSystem' :: FilePath -> [CommandParam] -> (CreateProcess -> CreateProcess) -> IO ExitCode-safeSystem' command params mkprocess = do-	(_, _, _, pid) <- createProcess p-	waitForProcess pid+safeSystem' command params mkprocess = +	withCreateProcess p $ \_ _ _ pid ->+		waitForProcess pid   where 	p = mkprocess $ proc command (toCommand params) 
doc/git-annex-checkpresentkey.mdwn view
@@ -12,11 +12,11 @@ is present in the specified remote.  When no remote is specified, it verifies if the key's content is present-somewhere, checking accessible remotes until it finds the content.+in any accessible remotes. -Exits 0 if the content is verified present, or 1 if it is verified to not-be present. If there is a problem, the special exit code 100 is used,-and an error message is output to stderr.+Exits 0 if the content is verified present in the remote, or 1 if it is+verified to not be present in the remote. If there is a problem, +the special exit code 100 is used, and an error message is output to stderr.  # OPTIONS 
doc/git-annex-export.mdwn view
@@ -86,6 +86,27 @@   the remote. You can later run `git annex sync --content` to upload   the files to the export. +* `--jobs=N` `-JN`++  Exports multiple files in parallel. This may be faster.+  For example: `-J4`  ++  Setting this to "cpus" will run one job per CPU core.++* `--json`++  Enable JSON output. This is intended to be parsed by programs that use+  git-annex. Each line of output is a JSON object.++* `--json-progress`++  Include progress objects in JSON output.++* `--json-error-messages`++  Messages that would normally be output to standard error are included in+  the json instead.+ # EXAMPLE  	git annex initremote myremote type=directory directory=/mnt/myremote \
doc/git-annex-import.mdwn view
@@ -171,6 +171,10 @@   Enable JSON output. This is intended to be parsed by programs that use   git-annex. Each line of output is a JSON object. +* `--json-progress`++  Include progress objects in JSON output.+ * `--json-error-messages`    Messages that would normally be output to standard error are included in
doc/git-annex-init.mdwn view
@@ -18,7 +18,7 @@  If any special remotes were configured with autoenable=true, this will also attempt to enable them. See [[git-annex-initremote]](1).-To disable this, re-enable a remote with "autoenable=false", or+To prevent that, re-enable a remote with "autoenable=false", or mark it as dead (see [[git-annex-dead]](1)).  This command is entirely safe, although usually pointless, to run inside an@@ -47,6 +47,11 @@    When the version given is one that automatically upgrades to a newer   version, it will automatically use the newer version instead.++* --autoenable++  Only enable any special remotes that were configured with+  autoenable=true, do not otherwise initialize anything.  # SEE ALSO 
doc/git-annex-initremote.mdwn view
@@ -93,10 +93,10 @@ * `autoenable`    To avoid `git annex enableremote` needing to be run,-  you can pass "autoenable=true". Then when [[git-annex-init]](1)-  is run in a new clone, it will attempt to enable the special remote. Of-  course, this works best when the special remote does not need anything-  special to be done to get it enabled.+  you can pass "autoenable=true". Then when git-annex is run in a new clone,+  it will attempt to enable the special remote. Of course, this works best+  when the special remote does not need anything special to be done to get+  it enabled.  * `uuid` 
doc/git-annex.mdwn view
@@ -880,6 +880,28 @@    The default reserve is 1 megabyte. +* `annex.skipunknown`++  Set to true to make commands like "git-annex get" silently skip over+  items that are listed in the command line, but are not checked into git.++  Set to false to make it an error for commands like "git-annex get"+  to be asked to operate on files that are not checked into git.++  The default is currently true, but is planned to change to false in a+  release in 2022.++  Note that, when annex.skipunknown is false, a command like "git-annex get ."+  will fail if no files in the current directory are checked into git,+  but a command like "git-annex get" will not fail, because the current+  directory is not listed, but is implicit. Commands like "git-annex get foo/"+  will fail if no files in the directory are checked into git, but if+  at least one file is, it will ignore other files that are not. This is+  all the same as the behavior of "git-ls files --error-unmatch".+  +  Also note that git-annex skips files that are checked into git, but are+  not annexed files, this setting does not affect that.+ * `annex.largefiles`    Used to configure which files are large enough to be added to the annex.
git-annex.cabal view
@@ -1,5 +1,5 @@ Name: git-annex-Version: 8.20200522+Version: 8.20200617 Cabal-Version: >= 1.10 License: AGPL-3 Maintainer: Joey Hess <id@joeyh.name>@@ -294,10 +294,11 @@   location: git://git-annex.branchable.com/  custom-setup-  Setup-Depends: base (>= 4.11.1.0), hslogger, split, unix-compat, process,+  Setup-Depends: base (>= 4.11.1.0), hslogger, split, unix-compat,      filepath, exceptions, bytestring, directory, IfElse, data-default,     filepath-bytestring (>= 1.4.2.1.1),-    utf8-string, transformers, Cabal+    process (>= 1.6.3),+    async, utf8-string, transformers, Cabal  Executable git-annex   Main-Is: git-annex.hs@@ -310,7 +311,7 @@    stm (>= 2.3),    mtl (>= 2),    uuid (>= 1.2.6),-   process (>= 1.4.2),+   process (>= 1.6.3),    data-default,    case-insensitive,    random,@@ -956,6 +957,7 @@     Remote.Helper.Ssh     Remote.Hook     Remote.List+    Remote.List.Util     Remote.P2P     Remote.Rsync     Remote.Rsync.RsyncUrl@@ -1083,7 +1085,6 @@     Utility.NotificationBroadcaster     Utility.OptParse     Utility.PID-    Utility.Parallel     Utility.PartialPrelude     Utility.Path     Utility.Path.Max