git style quoting for ActionItemOther
Added StringContainingQuotedPath, which is used for ActionItemOther. In the process, checked every ActionItemOther for those containing filenames, and made them use quoting. Sponsored-by: Graham Spencer on Patreon
This commit is contained in:
parent
d689a5b338
commit
2ba1559a8e
41 changed files with 158 additions and 89 deletions
|
@ -503,7 +503,7 @@ importKeys remote importtreeconfig importcontent thirdpartypopulated importablec
|
||||||
in return $ Left $ Just (loc, v)
|
in return $ Left $ Just (loc, v)
|
||||||
[] -> do
|
[] -> do
|
||||||
job <- liftIO $ newEmptyTMVarIO
|
job <- liftIO $ newEmptyTMVarIO
|
||||||
let ai = ActionItemOther (Just (fromRawFilePath (fromImportLocation loc)))
|
let ai = ActionItemOther (Just (QuotedPath (fromImportLocation loc)))
|
||||||
let si = SeekInput []
|
let si = SeekInput []
|
||||||
let importaction = starting ("import " ++ Remote.name remote) ai si $ do
|
let importaction = starting ("import " ++ Remote.name remote) ai si $ do
|
||||||
when oldversion $
|
when oldversion $
|
||||||
|
|
|
@ -25,7 +25,7 @@ import qualified Git
|
||||||
import Annex.Init
|
import Annex.Init
|
||||||
import Utility.Daemon
|
import Utility.Daemon
|
||||||
import Types.Transfer
|
import Types.Transfer
|
||||||
import Types.ActionItem
|
import Types.ActionItem as ReExported
|
||||||
import Types.WorkerPool as ReExported
|
import Types.WorkerPool as ReExported
|
||||||
import Remote.List
|
import Remote.List
|
||||||
|
|
||||||
|
|
|
@ -402,7 +402,7 @@ startingAddUrl si url o p = starting "addurl" ai si $ do
|
||||||
-- available and get added to it. That's ok, this is only
|
-- available and get added to it. That's ok, this is only
|
||||||
-- used to prevent two threads running concurrently when that would
|
-- used to prevent two threads running concurrently when that would
|
||||||
-- likely fail.
|
-- likely fail.
|
||||||
ai = OnlyActionOn urlkey (ActionItemOther (Just url))
|
ai = OnlyActionOn urlkey (ActionItemOther (Just (UnquotedString url)))
|
||||||
urlkey = Backend.URL.fromUrl url Nothing
|
urlkey = Backend.URL.fromUrl url Nothing
|
||||||
|
|
||||||
showDestinationFile :: FilePath -> Annex ()
|
showDestinationFile :: FilePath -> Annex ()
|
||||||
|
|
|
@ -61,7 +61,7 @@ seek (SetConfig ck@(ConfigKey name) val) = checkIsGlobalConfig ck $ commandActio
|
||||||
setConfig ck (fromConfigValue val)
|
setConfig ck (fromConfigValue val)
|
||||||
next $ return True
|
next $ return True
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just (fromConfigValue val))
|
ai = ActionItemOther (Just (UnquotedString (fromConfigValue val)))
|
||||||
si = SeekInput [decodeBS name]
|
si = SeekInput [decodeBS name]
|
||||||
seek (UnsetConfig ck@(ConfigKey name)) = checkIsGlobalConfig ck $ commandAction $
|
seek (UnsetConfig ck@(ConfigKey name)) = checkIsGlobalConfig ck $ commandAction $
|
||||||
startingUsualMessages (decodeBS name) ai si $ do
|
startingUsualMessages (decodeBS name) ai si $ do
|
||||||
|
|
|
@ -26,7 +26,7 @@ start (name:description) | not (null description) = do
|
||||||
starting "describe" ai si $
|
starting "describe" ai si $
|
||||||
perform u $ unwords description
|
perform u $ unwords description
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just name)
|
ai = ActionItemOther (Just (UnquotedString name))
|
||||||
si = SeekInput [name]
|
si = SeekInput [name]
|
||||||
start _ = giveup "Specify a repository and a description."
|
start _ = giveup "Specify a repository and a description."
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ startNormalRemote name restparams r
|
||||||
| otherwise = giveup $
|
| otherwise = giveup $
|
||||||
"That is a normal git remote; passing these parameters does not make sense: " ++ unwords restparams
|
"That is a normal git remote; passing these parameters does not make sense: " ++ unwords restparams
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just name)
|
ai = ActionItemOther (Just (UnquotedString name))
|
||||||
si = SeekInput [name]
|
si = SeekInput [name]
|
||||||
|
|
||||||
startSpecialRemote :: Git.RemoteName -> Remote.RemoteConfig -> [(UUID, Remote.RemoteConfig, Maybe (SpecialRemote.ConfigFrom UUID))] -> CommandStart
|
startSpecialRemote :: Git.RemoteName -> Remote.RemoteConfig -> [(UUID, Remote.RemoteConfig, Maybe (SpecialRemote.ConfigFrom UUID))] -> CommandStart
|
||||||
|
@ -92,7 +92,7 @@ startSpecialRemote name config ((u, c, mcu):[]) =
|
||||||
=<< Remote.byUUID u
|
=<< Remote.byUUID u
|
||||||
performSpecialRemote t u c fullconfig gc mcu
|
performSpecialRemote t u c fullconfig gc mcu
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just name)
|
ai = ActionItemOther (Just (UnquotedString name))
|
||||||
si = SeekInput [name]
|
si = SeekInput [name]
|
||||||
startSpecialRemote _ _ _ =
|
startSpecialRemote _ _ _ =
|
||||||
giveup "Multiple remotes have that name. Either use git-annex renameremote to rename them, or specify the uuid of the remote to enable."
|
giveup "Multiple remotes have that name. Either use git-annex renameremote to rename them, or specify the uuid of the remote to enable."
|
||||||
|
|
|
@ -79,7 +79,7 @@ start (Expire expire) noact actlog descs u =
|
||||||
return $ "last active: " ++ fromDuration d ++ " ago"
|
return $ "last active: " ++ fromDuration d ++ " ago"
|
||||||
_ -> return "no activity"
|
_ -> return "no activity"
|
||||||
desc = fromUUID u ++ " " ++ fromUUIDDesc (fromMaybe mempty (M.lookup u descs))
|
desc = fromUUID u ++ " " ++ fromUUIDDesc (fromMaybe mempty (M.lookup u descs))
|
||||||
ai = ActionItemOther (Just desc)
|
ai = ActionItemOther (Just (UnquotedString desc))
|
||||||
si = SeekInput []
|
si = SeekInput []
|
||||||
notexpired ent = case ent of
|
notexpired ent = case ent of
|
||||||
Unknown -> False
|
Unknown -> False
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- git-annex command
|
{- git-annex command
|
||||||
-
|
-
|
||||||
- Copyright 2017-2019 Joey Hess <id@joeyh.name>
|
- Copyright 2017-2023 Joey Hess <id@joeyh.name>
|
||||||
-
|
-
|
||||||
- Licensed under the GNU AGPL version 3 or higher.
|
- Licensed under the GNU AGPL version 3 or higher.
|
||||||
-}
|
-}
|
||||||
|
@ -413,13 +413,15 @@ startMoveToTempName r db f ek =
|
||||||
loc = mkExportLocation f'
|
loc = mkExportLocation f'
|
||||||
f' = getTopFilePath f
|
f' = getTopFilePath f
|
||||||
tmploc = exportTempName ek
|
tmploc = exportTempName ek
|
||||||
ai = ActionItemOther $ Just $ fromRawFilePath f' ++ " -> " ++ fromRawFilePath (fromExportLocation tmploc)
|
ai = ActionItemOther $ Just $
|
||||||
|
QuotedPath f' <> " -> " <> QuotedPath (fromExportLocation tmploc)
|
||||||
si = SeekInput []
|
si = SeekInput []
|
||||||
|
|
||||||
startMoveFromTempName :: Remote -> ExportHandle -> Key -> TopFilePath -> CommandStart
|
startMoveFromTempName :: Remote -> ExportHandle -> Key -> TopFilePath -> CommandStart
|
||||||
startMoveFromTempName r db ek f = do
|
startMoveFromTempName r db ek f = do
|
||||||
let tmploc = exportTempName ek
|
let tmploc = exportTempName ek
|
||||||
let ai = ActionItemOther (Just (fromRawFilePath (fromExportLocation tmploc) ++ " -> " ++ fromRawFilePath f'))
|
let ai = ActionItemOther $ Just $
|
||||||
|
QuotedPath (fromExportLocation tmploc) <> " -> " <> QuotedPath f'
|
||||||
stopUnless (liftIO $ elem tmploc <$> getExportedLocation db ek) $
|
stopUnless (liftIO $ elem tmploc <$> getExportedLocation db ek) $
|
||||||
starting ("rename " ++ name r) ai si $
|
starting ("rename " ++ name r) ai si $
|
||||||
performRename r db ek tmploc loc
|
performRename r db ek tmploc loc
|
||||||
|
|
|
@ -42,7 +42,7 @@ start o = starting "forget" ai si $ do
|
||||||
else basets
|
else basets
|
||||||
perform ts =<< Annex.getRead Annex.force
|
perform ts =<< Annex.getRead Annex.force
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just (fromRef Branch.name))
|
ai = ActionItemOther (Just (UnquotedString (fromRef Branch.name)))
|
||||||
si = SeekInput []
|
si = SeekInput []
|
||||||
|
|
||||||
perform :: Transitions -> Bool -> CommandPerform
|
perform :: Transitions -> Bool -> CommandPerform
|
||||||
|
|
|
@ -42,7 +42,6 @@ import qualified Database.Keys
|
||||||
import qualified Database.Fsck as FsckDb
|
import qualified Database.Fsck as FsckDb
|
||||||
import Types.CleanupActions
|
import Types.CleanupActions
|
||||||
import Types.Key
|
import Types.Key
|
||||||
import Types.ActionItem
|
|
||||||
import qualified Utility.RawFilePath as R
|
import qualified Utility.RawFilePath as R
|
||||||
|
|
||||||
import Data.Time.Clock.POSIX
|
import Data.Time.Clock.POSIX
|
||||||
|
|
|
@ -27,7 +27,7 @@ start ps@(name:g:[]) = do
|
||||||
startingUsualMessages "group" ai si $
|
startingUsualMessages "group" ai si $
|
||||||
setGroup u (toGroup g)
|
setGroup u (toGroup g)
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just name)
|
ai = ActionItemOther (Just (UnquotedString name))
|
||||||
si = SeekInput ps
|
si = SeekInput ps
|
||||||
start (name:[]) = do
|
start (name:[]) = do
|
||||||
u <- Remote.nameToUUID name
|
u <- Remote.nameToUUID name
|
||||||
|
|
|
@ -27,6 +27,6 @@ start (g:[]) = startingCustomOutput (ActionItemOther Nothing) $
|
||||||
start ps@(g:expr:[]) = startingUsualMessages "groupwanted" ai si $
|
start ps@(g:expr:[]) = startingUsualMessages "groupwanted" ai si $
|
||||||
performSet groupPreferredContentSet expr (toGroup g)
|
performSet groupPreferredContentSet expr (toGroup g)
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just g)
|
ai = ActionItemOther (Just (UnquotedString g))
|
||||||
si = SeekInput ps
|
si = SeekInput ps
|
||||||
start _ = giveup "Specify a group."
|
start _ = giveup "Specify a group."
|
||||||
|
|
|
@ -351,7 +351,7 @@ listContents remote importtreeconfig ci tvar = starting "list" ai si $
|
||||||
liftIO $ atomically $ writeTVar tvar importable
|
liftIO $ atomically $ writeTVar tvar importable
|
||||||
next $ return True
|
next $ return True
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just (Remote.name remote))
|
ai = ActionItemOther (Just (UnquotedString (Remote.name remote)))
|
||||||
si = SeekInput []
|
si = SeekInput []
|
||||||
|
|
||||||
listContents' :: Remote -> ImportTreeConfig -> CheckGitIgnore -> (Maybe (ImportableContentsChunkable Annex (ContentIdentifier, Remote.ByteSize)) -> Annex a) -> Annex a
|
listContents' :: Remote -> ImportTreeConfig -> CheckGitIgnore -> (Maybe (ImportableContentsChunkable Annex (ContentIdentifier, Remote.ByteSize)) -> Annex a) -> Annex a
|
||||||
|
@ -373,7 +373,7 @@ commitRemote remote branch tb trackingcommit importtreeconfig importcommitconfig
|
||||||
importcommit <- buildImportCommit remote importtreeconfig importcommitconfig importable
|
importcommit <- buildImportCommit remote importtreeconfig importcommitconfig importable
|
||||||
next $ updateremotetrackingbranch importcommit
|
next $ updateremotetrackingbranch importcommit
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just $ fromRef $ fromRemoteTrackingBranch tb)
|
ai = ActionItemOther (Just $ UnquotedString $ fromRef $ fromRemoteTrackingBranch tb)
|
||||||
si = SeekInput []
|
si = SeekInput []
|
||||||
-- Update the tracking branch. Done even when there
|
-- Update the tracking branch. Done even when there
|
||||||
-- is nothing new to import, to make sure it exists.
|
-- is nothing new to import, to make sure it exists.
|
||||||
|
|
|
@ -43,7 +43,6 @@ import Logs.Transfer
|
||||||
import Types.Key
|
import Types.Key
|
||||||
import Types.TrustLevel
|
import Types.TrustLevel
|
||||||
import Types.FileMatcher
|
import Types.FileMatcher
|
||||||
import Types.ActionItem
|
|
||||||
import qualified Limit
|
import qualified Limit
|
||||||
import Messages.JSON (DualDisp(..), ObjectMap(..))
|
import Messages.JSON (DualDisp(..), ObjectMap(..))
|
||||||
import Annex.BloomFilter
|
import Annex.BloomFilter
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
- Licensed under the GNU AGPL version 3 or higher.
|
- Licensed under the GNU AGPL version 3 or higher.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module Command.Init where
|
module Command.Init where
|
||||||
|
|
||||||
import Command
|
import Command
|
||||||
|
@ -62,7 +64,7 @@ start os
|
||||||
starting "init" (ActionItemOther (Just "autoenable")) si $
|
starting "init" (ActionItemOther (Just "autoenable")) si $
|
||||||
performAutoEnableOnly
|
performAutoEnableOnly
|
||||||
| otherwise =
|
| otherwise =
|
||||||
starting "init" (ActionItemOther (Just $ initDesc os)) si $
|
starting "init" (ActionItemOther (Just $ UnquotedString $ initDesc os)) si $
|
||||||
perform os
|
perform os
|
||||||
where
|
where
|
||||||
si = SeekInput []
|
si = SeekInput []
|
||||||
|
|
|
@ -81,7 +81,7 @@ start o (name:ws) = ifM (not . null <$> findExisting name)
|
||||||
if whatElse o
|
if whatElse o
|
||||||
then startingCustomOutput (ActionItemOther Nothing) $
|
then startingCustomOutput (ActionItemOther Nothing) $
|
||||||
describeOtherParamsFor c t
|
describeOtherParamsFor c t
|
||||||
else starting "initremote" (ActionItemOther (Just name)) si $
|
else starting "initremote" (ActionItemOther (Just (UnquotedString name))) si $
|
||||||
perform t name c o
|
perform t name c o
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -48,7 +48,7 @@ mergeAnnexBranch = starting "merge" ai si $ do
|
||||||
Annex.Branch.commit =<< Annex.Branch.commitMessage
|
Annex.Branch.commit =<< Annex.Branch.commitMessage
|
||||||
next $ return True
|
next $ return True
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just (fromRef Annex.Branch.name))
|
ai = ActionItemOther (Just (UnquotedString (fromRef Annex.Branch.name)))
|
||||||
si = SeekInput []
|
si = SeekInput []
|
||||||
|
|
||||||
mergeSyncedBranch :: MergeOptions -> CommandStart
|
mergeSyncedBranch :: MergeOptions -> CommandStart
|
||||||
|
@ -63,5 +63,5 @@ mergeBranch o r = starting "merge" ai si $ do
|
||||||
let so = def { notOnlyAnnexOption = True }
|
let so = def { notOnlyAnnexOption = True }
|
||||||
next $ merge currbranch mc so Git.Branch.ManualCommit r
|
next $ merge currbranch mc so Git.Branch.ManualCommit r
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just (Git.fromRef r))
|
ai = ActionItemOther (Just (UnquotedString (Git.fromRef r)))
|
||||||
si = SeekInput []
|
si = SeekInput []
|
||||||
|
|
|
@ -35,5 +35,5 @@ startSet n = startingUsualMessages "mincopies" ai si $ do
|
||||||
setGlobalMinCopies $ configuredMinCopies n
|
setGlobalMinCopies $ configuredMinCopies n
|
||||||
next $ return True
|
next $ return True
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just $ show n)
|
ai = ActionItemOther (Just $ UnquotedString $ show n)
|
||||||
si = SeekInput [show n]
|
si = SeekInput [show n]
|
||||||
|
|
|
@ -49,5 +49,5 @@ startSet n = startingUsualMessages "numcopies" ai si $ do
|
||||||
setGlobalNumCopies $ configuredNumCopies n
|
setGlobalNumCopies $ configuredNumCopies n
|
||||||
next $ return True
|
next $ return True
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just $ show n)
|
ai = ActionItemOther (Just $ UnquotedString $ show n)
|
||||||
si = SeekInput [show n]
|
si = SeekInput [show n]
|
||||||
|
|
|
@ -101,7 +101,7 @@ linkRemote :: RemoteName -> CommandStart
|
||||||
linkRemote remotename = starting "p2p link" ai si $
|
linkRemote remotename = starting "p2p link" ai si $
|
||||||
next promptaddr
|
next promptaddr
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just remotename)
|
ai = ActionItemOther (Just (UnquotedString remotename))
|
||||||
si = SeekInput []
|
si = SeekInput []
|
||||||
promptaddr = do
|
promptaddr = do
|
||||||
liftIO $ putStrLn ""
|
liftIO $ putStrLn ""
|
||||||
|
@ -131,7 +131,7 @@ startPairing remotename addrs = ifM (liftIO Wormhole.isInstalled)
|
||||||
, giveup "Magic Wormhole is not installed, and is needed for pairing. Install it from your distribution or from https://github.com/warner/magic-wormhole/"
|
, giveup "Magic Wormhole is not installed, and is needed for pairing. Install it from your distribution or from https://github.com/warner/magic-wormhole/"
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just remotename)
|
ai = ActionItemOther (Just (UnquotedString remotename))
|
||||||
si = SeekInput []
|
si = SeekInput []
|
||||||
|
|
||||||
performPairing :: RemoteName -> [P2PAddress] -> CommandPerform
|
performPairing :: RemoteName -> [P2PAddress] -> CommandPerform
|
||||||
|
|
|
@ -63,7 +63,7 @@ start' a o (si, (key, url)) =
|
||||||
starting "registerurl" ai si $
|
starting "registerurl" ai si $
|
||||||
perform a o key url
|
perform a o key url
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just url)
|
ai = ActionItemOther (Just (UnquotedString url))
|
||||||
|
|
||||||
perform :: (Remote -> Key -> URLString -> Annex ()) -> RegisterUrlOptions -> Key -> URLString -> CommandPerform
|
perform :: (Remote -> Key -> URLString -> Annex ()) -> RegisterUrlOptions -> Key -> URLString -> CommandPerform
|
||||||
perform a o key url = do
|
perform a o key url = do
|
||||||
|
|
|
@ -24,9 +24,10 @@ seek :: CmdParams -> CommandSeek
|
||||||
seek = withWords (commandAction . start)
|
seek = withWords (commandAction . start)
|
||||||
|
|
||||||
start :: [String] -> CommandStart
|
start :: [String] -> CommandStart
|
||||||
start ws = starting "reinit" (ActionItemOther (Just s)) (SeekInput ws) $
|
start ws = starting "reinit" ai (SeekInput ws) $
|
||||||
perform s
|
perform s
|
||||||
where
|
where
|
||||||
|
ai = ActionItemOther (Just (UnquotedString s))
|
||||||
s = unwords ws
|
s = unwords ws
|
||||||
|
|
||||||
perform :: String -> CommandPerform
|
perform :: String -> CommandPerform
|
||||||
|
|
|
@ -56,7 +56,7 @@ startSrcDest ps@(src:dest:[])
|
||||||
( perform src' key
|
( perform src' key
|
||||||
, giveup $ src ++ " does not have expected content of " ++ dest
|
, giveup $ src ++ " does not have expected content of " ++ dest
|
||||||
)
|
)
|
||||||
ai = ActionItemOther (Just src)
|
ai = ActionItemOther (Just (QuotedPath src'))
|
||||||
si = SeekInput ps
|
si = SeekInput ps
|
||||||
startSrcDest _ = giveup "specify a src file and a dest file"
|
startSrcDest _ = giveup "specify a src file and a dest file"
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ startKnown src = notAnnexed src' $
|
||||||
where
|
where
|
||||||
src' = toRawFilePath src
|
src' = toRawFilePath src
|
||||||
ks = KeySource src' src' Nothing
|
ks = KeySource src' src' Nothing
|
||||||
ai = ActionItemOther (Just src)
|
ai = ActionItemOther (Just (QuotedPath src'))
|
||||||
si = SeekInput [src]
|
si = SeekInput [src]
|
||||||
|
|
||||||
notAnnexed :: RawFilePath -> CommandStart -> CommandStart
|
notAnnexed :: RawFilePath -> CommandStart -> CommandStart
|
||||||
|
|
|
@ -31,7 +31,7 @@ start = parse
|
||||||
performGet u
|
performGet u
|
||||||
parse ps@(name:expr:[]) = do
|
parse ps@(name:expr:[]) = do
|
||||||
u <- Remote.nameToUUID name
|
u <- Remote.nameToUUID name
|
||||||
let ai = ActionItemOther (Just name)
|
let ai = ActionItemOther (Just (UnquotedString name))
|
||||||
let si = SeekInput ps
|
let si = SeekInput ps
|
||||||
startingUsualMessages "schedule" ai si $
|
startingUsualMessages "schedule" ai si $
|
||||||
performSet expr u
|
performSet expr u
|
||||||
|
|
|
@ -21,10 +21,11 @@ seek = withWords (commandAction . start)
|
||||||
|
|
||||||
start :: [String] -> CommandStart
|
start :: [String] -> CommandStart
|
||||||
start ps@(keyname:file:[]) = starting "setkey" ai si $
|
start ps@(keyname:file:[]) = starting "setkey" ai si $
|
||||||
perform (toRawFilePath file) (keyOpt keyname)
|
perform file' (keyOpt keyname)
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just file)
|
ai = ActionItemOther (Just (QuotedPath file'))
|
||||||
si = SeekInput ps
|
si = SeekInput ps
|
||||||
|
file' = toRawFilePath file
|
||||||
start _ = giveup "specify a key and a content file"
|
start _ = giveup "specify a key and a content file"
|
||||||
|
|
||||||
keyOpt :: String -> Key
|
keyOpt :: String -> Key
|
||||||
|
|
|
@ -392,7 +392,7 @@ mergeLocal' mergeconfig o currbranch@(Just branch, _) =
|
||||||
needMerge currbranch branch >>= \case
|
needMerge currbranch branch >>= \case
|
||||||
Nothing -> stop
|
Nothing -> stop
|
||||||
Just syncbranch -> do
|
Just syncbranch -> do
|
||||||
let ai = ActionItemOther (Just $ Git.Ref.describe syncbranch)
|
let ai = ActionItemOther (Just $ UnquotedString $ Git.Ref.describe syncbranch)
|
||||||
let si = SeekInput []
|
let si = SeekInput []
|
||||||
starting "merge" ai si $
|
starting "merge" ai si $
|
||||||
next $ merge currbranch mergeconfig o Git.Branch.ManualCommit syncbranch
|
next $ merge currbranch mergeconfig o Git.Branch.ManualCommit syncbranch
|
||||||
|
@ -400,7 +400,7 @@ mergeLocal' _ _ currbranch@(Nothing, _) = inRepo Git.Branch.currentUnsafe >>= \c
|
||||||
Just branch -> needMerge currbranch branch >>= \case
|
Just branch -> needMerge currbranch branch >>= \case
|
||||||
Nothing -> stop
|
Nothing -> stop
|
||||||
Just syncbranch -> do
|
Just syncbranch -> do
|
||||||
let ai = ActionItemOther (Just $ Git.Ref.describe syncbranch)
|
let ai = ActionItemOther (Just $ UnquotedString $ Git.Ref.describe syncbranch)
|
||||||
let si = SeekInput []
|
let si = SeekInput []
|
||||||
starting "merge" ai si $ do
|
starting "merge" ai si $ do
|
||||||
warning $ "There are no commits yet to branch " ++ Git.fromRef branch ++ ", so cannot merge " ++ Git.fromRef syncbranch ++ " into it."
|
warning $ "There are no commits yet to branch " ++ Git.fromRef branch ++ ", so cannot merge " ++ Git.fromRef syncbranch ++ " into it."
|
||||||
|
@ -513,7 +513,7 @@ pullRemote o mergeconfig remote branch = stopUnless (pure $ pullOption o && want
|
||||||
, Just $ Param $ Remote.name remote
|
, Just $ Param $ Remote.name remote
|
||||||
] ++ map Param bs
|
] ++ map Param bs
|
||||||
wantpull = remoteAnnexPull (Remote.gitconfig remote)
|
wantpull = remoteAnnexPull (Remote.gitconfig remote)
|
||||||
ai = ActionItemOther (Just (Remote.name remote))
|
ai = ActionItemOther (Just (UnquotedString (Remote.name remote)))
|
||||||
si = SeekInput []
|
si = SeekInput []
|
||||||
|
|
||||||
importRemote :: Bool -> SyncOptions -> Remote -> CurrBranch -> CommandSeek
|
importRemote :: Bool -> SyncOptions -> Remote -> CurrBranch -> CommandSeek
|
||||||
|
@ -559,7 +559,7 @@ pullThirdPartyPopulated o remote
|
||||||
Nothing -> next $ return False
|
Nothing -> next $ return False
|
||||||
go Nothing = next $ return True -- unchanged from before
|
go Nothing = next $ return True -- unchanged from before
|
||||||
|
|
||||||
ai = ActionItemOther (Just (Remote.name remote))
|
ai = ActionItemOther (Just (UnquotedString (Remote.name remote)))
|
||||||
si = SeekInput []
|
si = SeekInput []
|
||||||
|
|
||||||
wantpull = remoteAnnexPull (Remote.gitconfig remote)
|
wantpull = remoteAnnexPull (Remote.gitconfig remote)
|
||||||
|
@ -607,7 +607,7 @@ pushRemote o remote (Just branch, _) = do
|
||||||
warning $ unwords [ "Pushing to " ++ Remote.name remote ++ " failed." ]
|
warning $ unwords [ "Pushing to " ++ Remote.name remote ++ " failed." ]
|
||||||
return ok
|
return ok
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just (Remote.name remote))
|
ai = ActionItemOther (Just (UnquotedString (Remote.name remote)))
|
||||||
si = SeekInput []
|
si = SeekInput []
|
||||||
gc = Remote.gitconfig remote
|
gc = Remote.gitconfig remote
|
||||||
needpush mainbranch
|
needpush mainbranch
|
||||||
|
@ -1003,7 +1003,7 @@ cleanupRemote remote (Just b, _) =
|
||||||
Git.Ref.base $ Annex.Branch.name
|
Git.Ref.base $ Annex.Branch.name
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just (Remote.name remote))
|
ai = ActionItemOther (Just (UnquotedString (Remote.name remote)))
|
||||||
si = SeekInput []
|
si = SeekInput []
|
||||||
|
|
||||||
shouldSyncContent :: SyncOptions -> Annex Bool
|
shouldSyncContent :: SyncOptions -> Annex Bool
|
||||||
|
|
|
@ -73,7 +73,7 @@ seek :: TestRemoteOptions -> CommandSeek
|
||||||
seek = commandAction . start
|
seek = commandAction . start
|
||||||
|
|
||||||
start :: TestRemoteOptions -> CommandStart
|
start :: TestRemoteOptions -> CommandStart
|
||||||
start o = starting "testremote" (ActionItemOther (Just (testRemote o))) si $ do
|
start o = starting "testremote" (ActionItemOther (Just (UnquotedString (testRemote o)))) si $ do
|
||||||
fast <- Annex.getRead Annex.fast
|
fast <- Annex.getRead Annex.fast
|
||||||
cache <- liftIO newRemoteVariantCache
|
cache <- liftIO newRemoteVariantCache
|
||||||
r <- either giveup (disableExportTree cache)
|
r <- either giveup (disableExportTree cache)
|
||||||
|
|
|
@ -30,7 +30,7 @@ trustCommand c level ps = withStrings (commandAction . start) ps
|
||||||
start name = do
|
start name = do
|
||||||
u <- Remote.nameToUUID name
|
u <- Remote.nameToUUID name
|
||||||
let si = SeekInput [name]
|
let si = SeekInput [name]
|
||||||
starting c (ActionItemOther (Just name)) si (perform name u)
|
starting c (ActionItemOther (Just (UnquotedString name))) si (perform name u)
|
||||||
perform name uuid = do
|
perform name uuid = do
|
||||||
when (level >= Trusted) $
|
when (level >= Trusted) $
|
||||||
unlessM (Annex.getRead Annex.force) $
|
unlessM (Annex.getRead Annex.force) $
|
||||||
|
|
|
@ -45,7 +45,7 @@ start :: FilePath -> CommandStart
|
||||||
start p = starting "undo" ai si $
|
start p = starting "undo" ai si $
|
||||||
perform p
|
perform p
|
||||||
where
|
where
|
||||||
ai = ActionItemOther (Just p)
|
ai = ActionItemOther (Just (QuotedPath (toRawFilePath p)))
|
||||||
si = SeekInput [p]
|
si = SeekInput [p]
|
||||||
|
|
||||||
perform :: FilePath -> CommandPerform
|
perform :: FilePath -> CommandPerform
|
||||||
|
|
|
@ -24,8 +24,9 @@ seek = withWords (commandAction . start)
|
||||||
start :: [String] -> CommandStart
|
start :: [String] -> CommandStart
|
||||||
start (name:g:[]) = do
|
start (name:g:[]) = do
|
||||||
u <- Remote.nameToUUID name
|
u <- Remote.nameToUUID name
|
||||||
starting "ungroup" (ActionItemOther (Just name)) (SeekInput [name, g]) $
|
starting "ungroup" (ActionItemOther (Just (UnquotedString name)))
|
||||||
perform u (toGroup g)
|
(SeekInput [name, g]) $
|
||||||
|
perform u (toGroup g)
|
||||||
start _ = giveup "Specify a repository and a group."
|
start _ = giveup "Specify a repository and a group."
|
||||||
|
|
||||||
perform :: UUID -> Group -> CommandPerform
|
perform :: UUID -> Group -> CommandPerform
|
||||||
|
|
|
@ -73,7 +73,7 @@ start o = do
|
||||||
Just "." -> (".", checkUnused refspec)
|
Just "." -> (".", checkUnused refspec)
|
||||||
Just "here" -> (".", checkUnused refspec)
|
Just "here" -> (".", checkUnused refspec)
|
||||||
Just n -> (n, checkRemoteUnused n refspec)
|
Just n -> (n, checkRemoteUnused n refspec)
|
||||||
starting "unused" (ActionItemOther (Just name)) (SeekInput []) perform
|
starting "unused" (ActionItemOther (Just (UnquotedString name))) (SeekInput []) perform
|
||||||
|
|
||||||
checkUnused :: RefSpec -> CommandPerform
|
checkUnused :: RefSpec -> CommandPerform
|
||||||
checkUnused refspec = chain 0
|
checkUnused refspec = chain 0
|
||||||
|
@ -337,6 +337,6 @@ startUnused message unused badunused tmpunused maps n = search
|
||||||
case M.lookup n m of
|
case M.lookup n m of
|
||||||
Nothing -> search rest
|
Nothing -> search rest
|
||||||
Just key -> starting message
|
Just key -> starting message
|
||||||
(ActionItemOther $ Just $ show n)
|
(ActionItemOther $ Just $ UnquotedString $ show n)
|
||||||
(SeekInput [])
|
(SeekInput [])
|
||||||
(a key)
|
(a key)
|
||||||
|
|
|
@ -48,6 +48,6 @@ start ps = go =<< currentView
|
||||||
|
|
||||||
num = fromMaybe 1 $ readish =<< headMaybe ps
|
num = fromMaybe 1 $ readish =<< headMaybe ps
|
||||||
|
|
||||||
ai = ActionItemOther (Just $ show num)
|
ai = ActionItemOther (Just $ UnquotedString $ show num)
|
||||||
|
|
||||||
si = SeekInput ps
|
si = SeekInput ps
|
||||||
|
|
|
@ -39,7 +39,7 @@ cmd' name desc getter setter = noMessages $
|
||||||
start ps@(rname:expr:[]) = do
|
start ps@(rname:expr:[]) = do
|
||||||
u <- Remote.nameToUUID rname
|
u <- Remote.nameToUUID rname
|
||||||
let si = SeekInput ps
|
let si = SeekInput ps
|
||||||
let ai = ActionItemOther (Just rname)
|
let ai = ActionItemOther (Just (UnquotedString rname))
|
||||||
startingUsualMessages name ai si $
|
startingUsualMessages name ai si $
|
||||||
performSet setter expr u
|
performSet setter expr u
|
||||||
start _ = giveup "Specify a repository."
|
start _ = giveup "Specify a repository."
|
||||||
|
|
|
@ -133,6 +133,6 @@ parserDiffRaw f = DiffTreeItem
|
||||||
<*> (maybe (fail "bad dstsha") return . extractSha =<< nextword)
|
<*> (maybe (fail "bad dstsha") return . extractSha =<< nextword)
|
||||||
<* A8.char ' '
|
<* A8.char ' '
|
||||||
<*> A.takeByteString
|
<*> A.takeByteString
|
||||||
<*> pure (asTopFilePath $ fromInternalGitPath $ Git.Filename.decode f)
|
<*> pure (asTopFilePath $ fromInternalGitPath $ Git.Filename.unquote f)
|
||||||
where
|
where
|
||||||
nextword = A8.takeTill (== ' ')
|
nextword = A8.takeTill (== ' ')
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
- top of the repository even when run in a subdirectory. Adding some
|
- top of the repository even when run in a subdirectory. Adding some
|
||||||
- types helps keep that straight.
|
- types helps keep that straight.
|
||||||
-
|
-
|
||||||
- Copyright 2012-2019 Joey Hess <id@joeyh.name>
|
- Copyright 2012-2023 Joey Hess <id@joeyh.name>
|
||||||
-
|
-
|
||||||
- Licensed under the GNU AGPL version 3 or higher.
|
- Licensed under the GNU AGPL version 3 or higher.
|
||||||
-}
|
-}
|
||||||
|
@ -51,7 +51,7 @@ data BranchFilePath = BranchFilePath Ref TopFilePath
|
||||||
{- Git uses the branch:file form to refer to a BranchFilePath -}
|
{- Git uses the branch:file form to refer to a BranchFilePath -}
|
||||||
descBranchFilePath :: Filename.QuotePath -> BranchFilePath -> S.ByteString
|
descBranchFilePath :: Filename.QuotePath -> BranchFilePath -> S.ByteString
|
||||||
descBranchFilePath qp (BranchFilePath b f) =
|
descBranchFilePath qp (BranchFilePath b f) =
|
||||||
fromRef' b <> ":" <> Filename.encode qp (getTopFilePath f)
|
fromRef' b <> ":" <> Filename.quote qp (getTopFilePath f)
|
||||||
|
|
||||||
{- Path to a TopFilePath, within the provided git repo. -}
|
{- Path to a TopFilePath, within the provided git repo. -}
|
||||||
fromTopFilePath :: TopFilePath -> Git.Repo -> RawFilePath
|
fromTopFilePath :: TopFilePath -> Git.Repo -> RawFilePath
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{- Some git commands output encoded filenames, in a rather annoyingly complex
|
{- Some git commands output quoted filenames, in a rather annoyingly complex
|
||||||
- C-style encoding.
|
- C-style encoding.
|
||||||
-
|
-
|
||||||
- Copyright 2010-2023 Joey Hess <id@joeyh.name>
|
- Copyright 2010-2023 Joey Hess <id@joeyh.name>
|
||||||
|
@ -6,9 +6,15 @@
|
||||||
- Licensed under the GNU AGPL version 3 or higher.
|
- Licensed under the GNU AGPL version 3 or higher.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings, TypeSynonymInstances #-}
|
||||||
|
|
||||||
module Git.Filename where
|
module Git.Filename (
|
||||||
|
unquote,
|
||||||
|
quote,
|
||||||
|
QuotePath(..),
|
||||||
|
StringContainingQuotedPath(..),
|
||||||
|
prop_quote_unquote_roundtrip,
|
||||||
|
) where
|
||||||
|
|
||||||
import Common
|
import Common
|
||||||
import Utility.Format (decode_c, encode_c, encode_c', isUtf8Byte)
|
import Utility.Format (decode_c, encode_c, encode_c', isUtf8Byte)
|
||||||
|
@ -16,11 +22,13 @@ import Utility.QuickCheck
|
||||||
|
|
||||||
import Data.Char
|
import Data.Char
|
||||||
import Data.Word
|
import Data.Word
|
||||||
|
import Data.String
|
||||||
import qualified Data.ByteString as S
|
import qualified Data.ByteString as S
|
||||||
|
import qualified Data.Semigroup as Sem
|
||||||
|
import Prelude
|
||||||
|
|
||||||
-- encoded filenames will be inside double quotes
|
unquote :: S.ByteString -> RawFilePath
|
||||||
decode :: S.ByteString -> RawFilePath
|
unquote b = case S.uncons b of
|
||||||
decode b = case S.uncons b of
|
|
||||||
Nothing -> b
|
Nothing -> b
|
||||||
Just (h, t)
|
Just (h, t)
|
||||||
| h /= q -> b
|
| h /= q -> b
|
||||||
|
@ -34,24 +42,51 @@ decode b = case S.uncons b of
|
||||||
q = fromIntegral (ord '"')
|
q = fromIntegral (ord '"')
|
||||||
|
|
||||||
-- always encodes and double quotes, even in cases that git does not
|
-- always encodes and double quotes, even in cases that git does not
|
||||||
encodeAlways :: RawFilePath -> S.ByteString
|
quoteAlways :: RawFilePath -> S.ByteString
|
||||||
encodeAlways s = "\"" <> encode_c needencode s <> "\""
|
quoteAlways s = "\"" <> encode_c needencode s <> "\""
|
||||||
where
|
where
|
||||||
needencode c = isUtf8Byte c || c == fromIntegral (ord '"')
|
needencode c = isUtf8Byte c || c == fromIntegral (ord '"')
|
||||||
|
|
||||||
-- git config core.quotePath controls whether to quote unicode characters
|
-- git config core.quotePath controls whether to quote unicode characters
|
||||||
newtype QuotePath = QuotePath Bool
|
newtype QuotePath = QuotePath Bool
|
||||||
|
|
||||||
-- encodes and double quotes when git would
|
class Quoteable t where
|
||||||
encode :: QuotePath -> RawFilePath -> S.ByteString
|
-- double quotes and encodes when git would
|
||||||
encode (QuotePath qp) s = case encode_c' needencode s of
|
quote :: QuotePath -> t -> S.ByteString
|
||||||
Nothing -> s
|
|
||||||
Just s' -> "\"" <> s' <> "\""
|
instance Quoteable RawFilePath where
|
||||||
where
|
quote (QuotePath qp) s = case encode_c' needencode s of
|
||||||
needencode c
|
Nothing -> s
|
||||||
| c == fromIntegral (ord '"') = True
|
Just s' -> "\"" <> s' <> "\""
|
||||||
| qp = isUtf8Byte c
|
where
|
||||||
| otherwise = False
|
needencode c
|
||||||
|
| c == fromIntegral (ord '"') = True
|
||||||
|
| qp = isUtf8Byte c
|
||||||
|
| otherwise = False
|
||||||
|
|
||||||
|
-- Allows building up a string that contains paths, which will get quoted.
|
||||||
|
-- With OverloadedStrings, strings are passed through without quoting.
|
||||||
|
-- Eg: QuotedPath f <> ": not found"
|
||||||
|
data StringContainingQuotedPath
|
||||||
|
= UnquotedString String
|
||||||
|
| QuotedPath RawFilePath
|
||||||
|
| StringContainingQuotedPathMulti [StringContainingQuotedPath]
|
||||||
|
deriving (Show, Eq)
|
||||||
|
|
||||||
|
instance Quoteable StringContainingQuotedPath where
|
||||||
|
quote _ (UnquotedString s) = encodeBS s
|
||||||
|
quote qp (QuotedPath p) = quote qp p
|
||||||
|
quote qp (StringContainingQuotedPathMulti l) = S.concat (map (quote qp) l)
|
||||||
|
|
||||||
|
instance IsString StringContainingQuotedPath where
|
||||||
|
fromString = UnquotedString
|
||||||
|
|
||||||
|
instance Sem.Semigroup StringContainingQuotedPath where
|
||||||
|
UnquotedString a <> UnquotedString b = UnquotedString (a <> b)
|
||||||
|
a <> b = StringContainingQuotedPathMulti [a, b]
|
||||||
|
|
||||||
|
instance Monoid StringContainingQuotedPath where
|
||||||
|
mempty = UnquotedString mempty
|
||||||
|
|
||||||
-- Encoding and then decoding roundtrips only when the string does not
|
-- Encoding and then decoding roundtrips only when the string does not
|
||||||
-- contain high unicode, because eg, both "\12345" and "\227\128\185"
|
-- contain high unicode, because eg, both "\12345" and "\227\128\185"
|
||||||
|
@ -59,8 +94,8 @@ encode (QuotePath qp) s = case encode_c' needencode s of
|
||||||
--
|
--
|
||||||
-- That is not a real-world problem, and using TestableFilePath
|
-- That is not a real-world problem, and using TestableFilePath
|
||||||
-- limits what's tested to ascii, so avoids running into it.
|
-- limits what's tested to ascii, so avoids running into it.
|
||||||
prop_encode_decode_roundtrip :: TestableFilePath -> Bool
|
prop_quote_unquote_roundtrip :: TestableFilePath -> Bool
|
||||||
prop_encode_decode_roundtrip ts =
|
prop_quote_unquote_roundtrip ts =
|
||||||
s == fromRawFilePath (decode (encodeAlways (toRawFilePath s)))
|
s == fromRawFilePath (unquote (quoteAlways (toRawFilePath s)))
|
||||||
where
|
where
|
||||||
s = fromTestableFilePath ts
|
s = fromTestableFilePath ts
|
||||||
|
|
|
@ -137,7 +137,7 @@ parserLsTree long = case long of
|
||||||
-- sha
|
-- sha
|
||||||
<*> (Ref <$> A8.takeTill A8.isSpace)
|
<*> (Ref <$> A8.takeTill A8.isSpace)
|
||||||
|
|
||||||
fileparser = asTopFilePath . Git.Filename.decode <$> A.takeByteString
|
fileparser = asTopFilePath . Git.Filename.unquote <$> A.takeByteString
|
||||||
|
|
||||||
sizeparser = fmap Just A8.decimal
|
sizeparser = fmap Just A8.decimal
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,12 @@ showStartOther command mdesc si = outputMessage json $ encodeBS $
|
||||||
where
|
where
|
||||||
json = JSON.start command Nothing Nothing si
|
json = JSON.start command Nothing Nothing si
|
||||||
|
|
||||||
|
showStartNothing :: String -> SeekInput -> Annex ()
|
||||||
|
showStartNothing command si = outputMessage json $ encodeBS $
|
||||||
|
command ++ " "
|
||||||
|
where
|
||||||
|
json = JSON.start command Nothing Nothing si
|
||||||
|
|
||||||
showStartMessage :: StartMessage -> Annex ()
|
showStartMessage :: StartMessage -> Annex ()
|
||||||
showStartMessage (StartMessage command ai si) = case ai of
|
showStartMessage (StartMessage command ai si) = case ai of
|
||||||
ActionItemAssociatedFile _ _ -> showStartActionItem command ai si
|
ActionItemAssociatedFile _ _ -> showStartActionItem command ai si
|
||||||
|
@ -96,7 +102,8 @@ showStartMessage (StartMessage command ai si) = case ai of
|
||||||
ActionItemBranchFilePath _ _ -> showStartActionItem command ai si
|
ActionItemBranchFilePath _ _ -> showStartActionItem command ai si
|
||||||
ActionItemFailedTransfer _ _ -> showStartActionItem command ai si
|
ActionItemFailedTransfer _ _ -> showStartActionItem command ai si
|
||||||
ActionItemTreeFile _ -> showStartActionItem command ai si
|
ActionItemTreeFile _ -> showStartActionItem command ai si
|
||||||
ActionItemOther msg -> showStartOther command msg si
|
ActionItemOther Nothing -> showStartNothing command si
|
||||||
|
ActionItemOther _ -> showStartActionItem command ai si
|
||||||
OnlyActionOn _ ai' -> showStartMessage (StartMessage command ai' si)
|
OnlyActionOn _ ai' -> showStartMessage (StartMessage command ai' si)
|
||||||
showStartMessage (StartUsualMessages command ai si) = do
|
showStartMessage (StartUsualMessages command ai si) = do
|
||||||
outputType <$> Annex.getState Annex.output >>= \case
|
outputType <$> Annex.getState Annex.output >>= \case
|
||||||
|
|
2
Test.hs
2
Test.hs
|
@ -151,7 +151,7 @@ tests n crippledfilesystem adjustedbranchok opts =
|
||||||
|
|
||||||
properties :: TestTree
|
properties :: TestTree
|
||||||
properties = localOption (QuickCheckTests 1000) $ testGroup "QuickCheck" $
|
properties = localOption (QuickCheckTests 1000) $ testGroup "QuickCheck" $
|
||||||
[ testProperty "prop_encode_decode_roundtrip" Git.Filename.prop_encode_decode_roundtrip
|
[ testProperty "prop_quote_unquote_roundtrip" Git.Filename.prop_quote_unquote_roundtrip
|
||||||
, testProperty "prop_encode_c_decode_c_roundtrip" Utility.Format.prop_encode_c_decode_c_roundtrip
|
, testProperty "prop_encode_c_decode_c_roundtrip" Utility.Format.prop_encode_c_decode_c_roundtrip
|
||||||
, testProperty "prop_isomorphic_key_encode" Key.prop_isomorphic_key_encode
|
, testProperty "prop_isomorphic_key_encode" Key.prop_isomorphic_key_encode
|
||||||
, testProperty "prop_isomorphic_shellEscape" Utility.ShellEscape.prop_isomorphic_shellEscape
|
, testProperty "prop_isomorphic_shellEscape" Utility.ShellEscape.prop_isomorphic_shellEscape
|
||||||
|
|
|
@ -7,7 +7,10 @@
|
||||||
|
|
||||||
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
|
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
|
||||||
|
|
||||||
module Types.ActionItem where
|
module Types.ActionItem (
|
||||||
|
module Types.ActionItem,
|
||||||
|
Git.Filename.StringContainingQuotedPath(..),
|
||||||
|
) where
|
||||||
|
|
||||||
import Key
|
import Key
|
||||||
import Types.Transfer
|
import Types.Transfer
|
||||||
|
@ -15,7 +18,6 @@ import Git.FilePath
|
||||||
import qualified Git.Filename
|
import qualified Git.Filename
|
||||||
import Utility.FileSystemEncoding
|
import Utility.FileSystemEncoding
|
||||||
|
|
||||||
import Data.Maybe
|
|
||||||
import qualified Data.ByteString as S
|
import qualified Data.ByteString as S
|
||||||
|
|
||||||
data ActionItem
|
data ActionItem
|
||||||
|
@ -24,7 +26,7 @@ data ActionItem
|
||||||
| ActionItemBranchFilePath BranchFilePath Key
|
| ActionItemBranchFilePath BranchFilePath Key
|
||||||
| ActionItemFailedTransfer Transfer TransferInfo
|
| ActionItemFailedTransfer Transfer TransferInfo
|
||||||
| ActionItemTreeFile RawFilePath
|
| ActionItemTreeFile RawFilePath
|
||||||
| ActionItemOther (Maybe String)
|
| ActionItemOther (Maybe Git.Filename.StringContainingQuotedPath)
|
||||||
-- Use to avoid more than one thread concurrently processing the
|
-- Use to avoid more than one thread concurrently processing the
|
||||||
-- same Key.
|
-- same Key.
|
||||||
| OnlyActionOn Key ActionItem
|
| OnlyActionOn Key ActionItem
|
||||||
|
@ -59,15 +61,16 @@ instance MkActionItem (Transfer, TransferInfo) where
|
||||||
|
|
||||||
actionItemDesc :: Git.Filename.QuotePath -> ActionItem -> S.ByteString
|
actionItemDesc :: Git.Filename.QuotePath -> ActionItem -> S.ByteString
|
||||||
actionItemDesc qp (ActionItemAssociatedFile (AssociatedFile (Just f)) _) =
|
actionItemDesc qp (ActionItemAssociatedFile (AssociatedFile (Just f)) _) =
|
||||||
Git.Filename.encode qp f
|
Git.Filename.quote qp f
|
||||||
actionItemDesc _ (ActionItemAssociatedFile (AssociatedFile Nothing) k) =
|
actionItemDesc _ (ActionItemAssociatedFile (AssociatedFile Nothing) k) =
|
||||||
serializeKey' k
|
serializeKey' k
|
||||||
actionItemDesc _ (ActionItemKey k) = serializeKey' k
|
actionItemDesc _ (ActionItemKey k) = serializeKey' k
|
||||||
actionItemDesc qp (ActionItemBranchFilePath bfp _) = descBranchFilePath qp bfp
|
actionItemDesc qp (ActionItemBranchFilePath bfp _) = descBranchFilePath qp bfp
|
||||||
actionItemDesc qp (ActionItemFailedTransfer t i) = actionItemDesc qp $
|
actionItemDesc qp (ActionItemFailedTransfer t i) = actionItemDesc qp $
|
||||||
ActionItemAssociatedFile (associatedFile i) (transferKey t)
|
ActionItemAssociatedFile (associatedFile i) (transferKey t)
|
||||||
actionItemDesc qp (ActionItemTreeFile f) = Git.Filename.encode qp f
|
actionItemDesc qp (ActionItemTreeFile f) = Git.Filename.quote qp f
|
||||||
actionItemDesc _ (ActionItemOther s) = encodeBS (fromMaybe "" s)
|
actionItemDesc _ (ActionItemOther Nothing) = mempty
|
||||||
|
actionItemDesc qp (ActionItemOther (Just v)) = Git.Filename.quote qp v
|
||||||
actionItemDesc qp (OnlyActionOn _ ai) = actionItemDesc qp ai
|
actionItemDesc qp (OnlyActionOn _ ai) = actionItemDesc qp ai
|
||||||
|
|
||||||
actionItemKey :: ActionItem -> Maybe Key
|
actionItemKey :: ActionItem -> Maybe Key
|
||||||
|
|
|
@ -12,14 +12,6 @@ working on or in messages.
|
||||||
pipeable, and so should have raw filenames. Note that `find` actually
|
pipeable, and so should have raw filenames. Note that `find` actually
|
||||||
escapes such filenames when outputting to a terminal, but not a pipe.
|
escapes such filenames when outputting to a terminal, but not a pipe.
|
||||||
|
|
||||||
It's possible that keys can also contain an escape sequence, eg in the
|
|
||||||
extension of a SHA-E key. So commands like `git-annex lookupkey`
|
|
||||||
and `git-annex find` that output keys might need to handle
|
|
||||||
that, when outputting to a terminal?
|
|
||||||
|
|
||||||
`git-annex metadata` could also contain an escape sequence. So could
|
|
||||||
`git-annex config --get`.
|
|
||||||
|
|
||||||
git porcelain also accepts the escaped form of files as input, necessary for
|
git porcelain also accepts the escaped form of files as input, necessary for
|
||||||
round-tripping though. git-annex currently does not. (git plumbing doesn't
|
round-tripping though. git-annex currently does not. (git plumbing doesn't
|
||||||
either)
|
either)
|
||||||
|
@ -40,3 +32,30 @@ behave more like git.
|
||||||
> Note that core.quotePath controls whether git quotes unicode characters
|
> Note that core.quotePath controls whether git quotes unicode characters
|
||||||
> (by default it does), so once this gets implemented, some users may want
|
> (by default it does), so once this gets implemented, some users may want
|
||||||
> to set that config to false. --[[Joey]]
|
> to set that config to false. --[[Joey]]
|
||||||
|
|
||||||
|
> Update: Most git-annex commands now quote filenames, due to work on
|
||||||
|
> ActionItem display. `git-annex find`, `git-annex info $file`,
|
||||||
|
> and everywhere filenames get
|
||||||
|
> embedded in error messages, warnings, info messages, still need to be done.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Also:
|
||||||
|
It's possible that keys can also contain an escape sequence, eg in the
|
||||||
|
extension of a SHA-E key. So commands like `git-annex lookupkey`
|
||||||
|
and `git-annex find` that output keys might need to handle
|
||||||
|
that, when outputting to a terminal?
|
||||||
|
|
||||||
|
Also:
|
||||||
|
`git-annex metadata` could also contain an escape sequence. So could
|
||||||
|
`git-annex config --get` and `git-annex schedule` and `git-annex wanted`
|
||||||
|
and `git-annex required` and `git-annex group`. And so could the
|
||||||
|
description of a repository. It seems that git-annex could just filter out
|
||||||
|
control characters from all of these, since they are not filenames, and
|
||||||
|
any control characters in them are surely malicious.
|
||||||
|
|
||||||
|
Also: git-annex importfeed displays urls from the feed, and should filter
|
||||||
|
out control characters. If such an url even can be parsed?
|
||||||
|
|
||||||
|
Also: git-annex initremote with autoenable may be able to cause a remote
|
||||||
|
with a malicious name to be set up?
|
||||||
|
|
Loading…
Reference in a new issue