sync --quiet

* sync: When --quiet is used, run git commit, push, and pull without
  their ususual output.
* merge: When --quiet is used, run git merge without its usual output.

This might also make --quiet work better for some other commands
that make commits, like git-annex adjust.

Sponsored-by: Kevin Mueller on Patreon
This commit is contained in:
Joey Hess 2021-07-19 11:28:31 -04:00
parent f84bd8e921
commit 33a80d083a
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
13 changed files with 124 additions and 65 deletions

View file

@ -362,9 +362,9 @@ adjustToCrippledFileSystem = do
commitForAdjustedBranch :: [CommandParam] -> Annex () commitForAdjustedBranch :: [CommandParam] -> Annex ()
commitForAdjustedBranch ps = do commitForAdjustedBranch ps = do
cmode <- annexCommitMode <$> Annex.getGitConfig cmode <- annexCommitMode <$> Annex.getGitConfig
void $ inRepo $ Git.Branch.commitCommand cmode $ let cquiet = Git.Branch.CommitQuiet True
[ Param "--quiet" void $ inRepo $ Git.Branch.commitCommand cmode cquiet $
, Param "--allow-empty" [ Param "--allow-empty"
, Param "-m" , Param "-m"
, Param "commit before entering adjusted branch" , Param "commit before entering adjusted branch"
] ++ ps ] ++ ps

View file

@ -352,11 +352,13 @@ reuseOldFile srcmap key origfile destfile = do
| otherwise = go fs | otherwise = go fs
commitResolvedMerge :: Git.Branch.CommitMode -> Annex Bool commitResolvedMerge :: Git.Branch.CommitMode -> Annex Bool
commitResolvedMerge commitmode = inRepo $ Git.Branch.commitCommand commitmode commitResolvedMerge commitmode = do
[ Param "--no-verify" commitquiet <- Git.Branch.CommitQuiet <$> commandProgressDisabled
, Param "-m" inRepo $ Git.Branch.commitCommand commitmode commitquiet
, Param "git-annex automatic merge conflict fix" [ Param "--no-verify"
] , Param "-m"
, Param "git-annex automatic merge conflict fix"
]
type InodeMap = M.Map (Either FilePath InodeCacheKey) FilePath type InodeMap = M.Map (Either FilePath InodeCacheKey) FilePath

View file

@ -60,8 +60,8 @@ initRepo True primary_assistant_repo dir desc mgroup = inDir dir $ do
unlessM (Git.Config.isBare <$> gitRepo) $ do unlessM (Git.Config.isBare <$> gitRepo) $ do
cmode <- annexCommitMode <$> Annex.getGitConfig cmode <- annexCommitMode <$> Annex.getGitConfig
void $ inRepo $ Git.Branch.commitCommand cmode void $ inRepo $ Git.Branch.commitCommand cmode
[ Param "--quiet" (Git.Branch.CommitQuiet True)
, Param "--allow-empty" [ Param "--allow-empty"
, Param "-m" , Param "-m"
, Param "created repository" , Param "created repository"
] ]

View file

@ -21,6 +21,7 @@ import qualified Git
import qualified Git.Command import qualified Git.Command
import qualified Remote import qualified Remote
import qualified Types.Remote as Remote import qualified Types.Remote as Remote
import qualified Annex
import qualified Annex.Branch import qualified Annex.Branch
import Remote.List.Util import Remote.List.Util
import Annex.UUID import Annex.UUID
@ -125,25 +126,26 @@ pushToRemotes remotes = do
pushToRemotes' :: UTCTime -> [Remote] -> Assistant [Remote] pushToRemotes' :: UTCTime -> [Remote] -> Assistant [Remote]
pushToRemotes' now remotes = do pushToRemotes' now remotes = do
(g, branch, u) <- liftAnnex $ do (g, branch, u, ms) <- liftAnnex $ do
Annex.Branch.commit =<< Annex.Branch.commitMessage Annex.Branch.commit =<< Annex.Branch.commitMessage
(,,) (,,,)
<$> gitRepo <$> gitRepo
<*> getCurrentBranch <*> getCurrentBranch
<*> getUUID <*> getUUID
ret <- go True branch g u remotes <*> Annex.getState Annex.output
ret <- go ms True branch g u remotes
return ret return ret
where where
go _ (Nothing, _) _ _ _ = return [] -- no branch, so nothing to do go _ _ (Nothing, _) _ _ _ = return [] -- no branch, so nothing to do
go _ _ _ _ [] = return [] -- no remotes, so nothing to do go _ _ _ _ _ [] = return [] -- no remotes, so nothing to do
go shouldretry currbranch@(Just branch, _) g u rs = do go ms shouldretry currbranch@(Just branch, _) g u rs = do
debug ["pushing to", show rs] debug ["pushing to", show rs]
(succeeded, failed) <- parallelPush g rs (push branch) (succeeded, failed) <- parallelPush g rs (push ms branch)
updatemap succeeded [] updatemap succeeded []
if null failed if null failed
then return [] then return []
else if shouldretry else if shouldretry
then retry currbranch g u failed then retry ms currbranch g u failed
else fallback branch g u failed else fallback branch g u failed
updatemap succeeded failed = do updatemap succeeded failed = do
@ -153,10 +155,10 @@ pushToRemotes' now remotes = do
M.difference m (makemap succeeded) M.difference m (makemap succeeded)
makemap l = M.fromList $ zip l (repeat now) makemap l = M.fromList $ zip l (repeat now)
retry currbranch g u rs = do retry ms currbranch g u rs = do
debug ["trying manual pull to resolve failed pushes"] debug ["trying manual pull to resolve failed pushes"]
void $ manualPull currbranch rs void $ manualPull currbranch rs
go False currbranch g u rs go ms False currbranch g u rs
fallback branch g u rs = do fallback branch g u rs = do
debug ["fallback pushing to", show rs] debug ["fallback pushing to", show rs]
@ -164,7 +166,7 @@ pushToRemotes' now remotes = do
updatemap succeeded failed updatemap succeeded failed
return failed return failed
push branch remote = Command.Sync.pushBranch remote (Just branch) push ms branch remote = Command.Sync.pushBranch remote (Just branch) ms
parallelPush :: Git.Repo -> [Remote] -> (Remote -> Git.Repo -> IO Bool)-> Assistant ([Remote], [Remote]) parallelPush :: Git.Repo -> [Remote] -> (Remote -> Git.Repo -> IO Bool)-> Assistant ([Remote], [Remote])
parallelPush g rs a = do parallelPush g rs a = do
@ -211,6 +213,7 @@ syncAction rs a
manualPull :: Command.Sync.CurrBranch -> [Remote] -> Assistant ([Remote], Bool) manualPull :: Command.Sync.CurrBranch -> [Remote] -> Assistant ([Remote], Bool)
manualPull currentbranch remotes = do manualPull currentbranch remotes = do
g <- liftAnnex gitRepo g <- liftAnnex gitRepo
mc <- liftAnnex Command.Sync.mergeConfig
failed <- forM remotes $ \r -> if wantpull $ Remote.gitconfig r failed <- forM remotes $ \r -> if wantpull $ Remote.gitconfig r
then do then do
g' <- liftAnnex $ do g' <- liftAnnex $ do
@ -225,7 +228,7 @@ manualPull currentbranch remotes = do
<$> liftAnnex Annex.Branch.forceUpdate <$> liftAnnex Annex.Branch.forceUpdate
forM_ remotes $ \r -> forM_ remotes $ \r ->
liftAnnex $ Command.Sync.mergeRemote r liftAnnex $ Command.Sync.mergeRemote r
currentbranch Command.Sync.mergeConfig def currentbranch mc def
when haddiverged $ when haddiverged $
updateExportTreeFromLogAll updateExportTreeFromLogAll
return (catMaybes failed, haddiverged) return (catMaybes failed, haddiverged)

View file

@ -90,8 +90,10 @@ onChange file
] ]
void $ liftAnnex $ do void $ liftAnnex $ do
cmode <- annexCommitMode <$> Annex.getGitConfig cmode <- annexCommitMode <$> Annex.getGitConfig
mc <- Command.Sync.mergeConfig
Command.Sync.merge Command.Sync.merge
currbranch Command.Sync.mergeConfig currbranch
mc
def def
cmode cmode
changedbranch changedbranch

View file

@ -6,6 +6,9 @@ git-annex (8.20210715) UNRELEASED; urgency=medium
started out as a bare repository, or had annex.crippledfilesystem started out as a bare repository, or had annex.crippledfilesystem
set, and was converted to a non-bare repository. set, and was converted to a non-bare repository.
* Fix retrieval of content from borg repos accessed over ssh. * Fix retrieval of content from borg repos accessed over ssh.
* sync: When --quiet is used, run git commit, push, and pull without
their ususual output.
* merge: When --quiet is used, run git merge without its usual output.
-- Joey Hess <id@joeyh.name> Wed, 14 Jul 2021 14:26:36 -0400 -- Joey Hess <id@joeyh.name> Wed, 14 Jul 2021 14:26:36 -0400

View file

@ -40,13 +40,16 @@ mergeAnnexBranch = starting "merge" ai si $ do
si = SeekInput [] si = SeekInput []
mergeSyncedBranch :: CommandStart mergeSyncedBranch :: CommandStart
mergeSyncedBranch = mergeLocal mergeConfig def =<< getCurrentBranch mergeSyncedBranch = do
mc <- mergeConfig
mergeLocal mc def =<< getCurrentBranch
mergeBranch :: Git.Ref -> CommandStart mergeBranch :: Git.Ref -> CommandStart
mergeBranch r = starting "merge" ai si $ do mergeBranch r = starting "merge" ai si $ do
currbranch <- getCurrentBranch currbranch <- getCurrentBranch
let o = def { notOnlyAnnexOption = True } let o = def { notOnlyAnnexOption = True }
next $ merge currbranch mergeConfig o Git.Branch.ManualCommit r mc <- mergeConfig
next $ merge currbranch mc o Git.Branch.ManualCommit r
where where
ai = ActionItemOther (Just (Git.fromRef r)) ai = ActionItemOther (Just (Git.fromRef r))
si = SeekInput [] si = SeekInput []

View file

@ -52,4 +52,5 @@ updateInsteadEmulation :: CommandStart
updateInsteadEmulation = do updateInsteadEmulation = do
prepMerge prepMerge
let o = def { notOnlyAnnexOption = True } let o = def { notOnlyAnnexOption = True }
mergeLocal mergeConfig o =<< getCurrentBranch mc <- mergeConfig
mergeLocal mc o =<< getCurrentBranch

View file

@ -218,21 +218,23 @@ seek' o = do
commandAction (withbranch cleanupLocal) commandAction (withbranch cleanupLocal)
mapM_ (commandAction . withbranch . cleanupRemote) gitremotes mapM_ (commandAction . withbranch . cleanupRemote) gitremotes
else do else do
mc <- mergeConfig
-- Syncing involves many actions, any of which -- Syncing involves many actions, any of which
-- can independently fail, without preventing -- can independently fail, without preventing
-- the others from running. -- the others from running.
-- These actions cannot be run concurrently. -- These actions cannot be run concurrently.
mapM_ includeCommandAction $ concat mapM_ includeCommandAction $ concat
[ [ commit o ] [ [ commit o ]
, [ withbranch (mergeLocal mergeConfig o) ] , [ withbranch (mergeLocal mc o) ]
, map (withbranch . pullRemote o mergeConfig) gitremotes , map (withbranch . pullRemote o mc) gitremotes
, [ mergeAnnex ] , [ mergeAnnex ]
] ]
content <- shouldSyncContent o content <- shouldSyncContent o
forM_ (filter isImport contentremotes) $ forM_ (filter isImport contentremotes) $
withbranch . importRemote content o mergeConfig withbranch . importRemote content o mc
forM_ (filter isThirdPartyPopulated contentremotes) $ forM_ (filter isThirdPartyPopulated contentremotes) $
pullThirdPartyPopulated o pullThirdPartyPopulated o
@ -259,7 +261,7 @@ seek' o = do
-- avoid our push overwriting those changes. -- avoid our push overwriting those changes.
when (syncedcontent || exportedcontent) $ do when (syncedcontent || exportedcontent) $ do
mapM_ includeCommandAction $ concat mapM_ includeCommandAction $ concat
[ map (withbranch . pullRemote o mergeConfig) gitremotes [ map (withbranch . pullRemote o mc) gitremotes
, [ commitAnnex, mergeAnnex ] , [ commitAnnex, mergeAnnex ]
] ]
@ -273,17 +275,21 @@ seek' o = do
prepMerge :: Annex () prepMerge :: Annex ()
prepMerge = Annex.changeDirectory . fromRawFilePath =<< fromRepo Git.repoPath prepMerge = Annex.changeDirectory . fromRawFilePath =<< fromRepo Git.repoPath
mergeConfig :: [Git.Merge.MergeConfig] mergeConfig :: Annex [Git.Merge.MergeConfig]
mergeConfig = mergeConfig = do
[ Git.Merge.MergeNonInteractive quiet <- commandProgressDisabled
-- In several situations, unrelated histories should be merged return $ catMaybes
-- together. This includes pairing in the assistant, merging [ Just Git.Merge.MergeNonInteractive
-- from a remote into a newly created direct mode repo, -- In several situations, unrelated histories should be
-- and an initial merge from an import from a special remote. -- merged together. This includes pairing in the assistant,
-- (Once direct mode is removed, this could be changed, so only -- merging from a remote into a newly created direct mode
-- the assistant and import from special remotes use it.) -- repo, and an initial merge from an import from a special
, Git.Merge.MergeUnrelatedHistories -- remote. (Once direct mode is removed, this could be
] -- changed, so only the assistant and import from special
-- remotes use it.)
, Just Git.Merge.MergeUnrelatedHistories
, if quiet then Just Git.Merge.MergeQuiet else Nothing
]
merge :: CurrBranch -> [Git.Merge.MergeConfig] -> SyncOptions -> Git.Branch.CommitMode -> Git.Branch -> Annex Bool merge :: CurrBranch -> [Git.Merge.MergeConfig] -> SyncOptions -> Git.Branch.CommitMode -> Git.Branch -> Annex Bool
merge currbranch mergeconfig o commitmode tomerge = do merge currbranch mergeconfig o commitmode tomerge = do
@ -331,7 +337,9 @@ commit o = stopUnless shouldcommit $ starting "commit" ai si $ do
Annex.Branch.commit =<< Annex.Branch.commitMessage Annex.Branch.commit =<< Annex.Branch.commitMessage
next $ do next $ do
showOutput showOutput
void $ inRepo $ Git.Branch.commitCommand Git.Branch.ManualCommit let cmode = Git.Branch.ManualCommit
cquiet <- Git.Branch.CommitQuiet <$> commandProgressDisabled
void $ inRepo $ Git.Branch.commitCommand cmode cquiet
[ Param "-a" [ Param "-a"
, Param "-m" , Param "-m"
, Param commitmessage , Param commitmessage
@ -462,10 +470,15 @@ pullRemote o mergeconfig remote branch = stopUnless (pure $ pullOption o && want
where where
fetch bs = do fetch bs = do
repo <- Remote.getRepo remote repo <- Remote.getRepo remote
ms <- Annex.getState Annex.output
inRepoWithSshOptionsTo repo (Remote.gitconfig remote) $ inRepoWithSshOptionsTo repo (Remote.gitconfig remote) $
Git.Command.runBool $ Git.Command.runBool $ catMaybes
[Param "fetch", Param $ Remote.name remote] [ Just $ Param "fetch"
++ map Param bs , if commandProgressDisabled' ms
then Just $ Param "--quiet"
else Nothing
, Just $ Param $ Remote.name remote
] ++ map Param bs
wantpull = remoteAnnexPull (Remote.gitconfig remote) wantpull = remoteAnnexPull (Remote.gitconfig remote)
ai = ActionItemOther (Just (Remote.name remote)) ai = ActionItemOther (Just (Remote.name remote))
si = SeekInput [] si = SeekInput []
@ -548,8 +561,9 @@ pushRemote o remote (Just branch, _) = do
starting "push" ai si $ next $ do starting "push" ai si $ next $ do
repo <- Remote.getRepo remote repo <- Remote.getRepo remote
showOutput showOutput
ms <- Annex.getState Annex.output
ok <- inRepoWithSshOptionsTo repo gc $ ok <- inRepoWithSshOptionsTo repo gc $
pushBranch remote mainbranch pushBranch remote mainbranch ms
if ok if ok
then postpushupdate repo then postpushupdate repo
else do else do
@ -621,8 +635,8 @@ pushRemote o remote (Just branch, _) = do
- The only difference caused by using a forced push in that case is that - The only difference caused by using a forced push in that case is that
- the last repository to push wins the race, rather than the first to push. - the last repository to push wins the race, rather than the first to push.
-} -}
pushBranch :: Remote -> Maybe Git.Branch -> Git.Repo -> IO Bool pushBranch :: Remote -> Maybe Git.Branch -> MessageState -> Git.Repo -> IO Bool
pushBranch remote mbranch g = directpush `after` annexpush `after` syncpush pushBranch remote mbranch ms g = directpush `after` annexpush `after` syncpush
where where
syncpush = flip Git.Command.runBool g $ pushparams $ catMaybes syncpush = flip Git.Command.runBool g $ pushparams $ catMaybes
[ (refspec . fromAdjustedBranch) <$> mbranch [ (refspec . fromAdjustedBranch) <$> mbranch
@ -648,9 +662,12 @@ pushBranch remote mbranch g = directpush `after` annexpush `after` syncpush
(transcript, ok) <- processTranscript' p Nothing (transcript, ok) <- processTranscript' p Nothing
when (not ok && not ("denyCurrentBranch" `isInfixOf` transcript)) $ when (not ok && not ("denyCurrentBranch" `isInfixOf` transcript)) $
hPutStr stderr transcript hPutStr stderr transcript
pushparams branches = pushparams branches = catMaybes
[ Param "push" [ Just $ Param "push"
, Param $ Remote.name remote , if commandProgressDisabled' ms
then Just $ Param "--quiet"
else Nothing
, Just $ Param $ Remote.name remote
] ++ map Param branches ] ++ map Param branches
refspec b = concat refspec b = concat
[ Git.fromRef $ Git.Ref.base b [ Git.fromRef $ Git.Ref.base b

View file

@ -121,6 +121,13 @@ fastForward branch (first:rest) repo =
(False, True) -> findbest c rs -- worse (False, True) -> findbest c rs -- worse
(False, False) -> findbest c rs -- same (False, False) -> findbest c rs -- same
{- Should the commit avoid the usual summary output? -}
newtype CommitQuiet = CommitQuiet Bool
applyCommitQuiet :: CommitQuiet -> [CommandParam] -> [CommandParam]
applyCommitQuiet (CommitQuiet True) ps = Param "--quiet" : ps
applyCommitQuiet (CommitQuiet False) ps = ps
{- The user may have set commit.gpgsign, intending all their manual {- The user may have set commit.gpgsign, intending all their manual
- commits to be signed. But signing automatic/background commits could - commits to be signed. But signing automatic/background commits could
- easily lead to unwanted gpg prompts or failures. - easily lead to unwanted gpg prompts or failures.
@ -148,12 +155,14 @@ applyCommitModeForCommitTree commitmode ps r
ps' = applyCommitMode commitmode ps ps' = applyCommitMode commitmode ps
{- Commit via the usual git command. -} {- Commit via the usual git command. -}
commitCommand :: CommitMode -> [CommandParam] -> Repo -> IO Bool commitCommand :: CommitMode -> CommitQuiet -> [CommandParam] -> Repo -> IO Bool
commitCommand = commitCommand' runBool commitCommand = commitCommand' runBool
commitCommand' :: ([CommandParam] -> Repo -> IO a) -> CommitMode -> [CommandParam] -> Repo -> IO a commitCommand' :: ([CommandParam] -> Repo -> IO a) -> CommitMode -> CommitQuiet -> [CommandParam] -> Repo -> IO a
commitCommand' runner commitmode ps = runner $ commitCommand' runner commitmode commitquiet ps =
Param "commit" : applyCommitMode commitmode ps runner $ Param "commit" : ps'
where
ps' = applyCommitMode commitmode (applyCommitQuiet commitquiet ps)
{- Commits the index into the specified branch (or other ref), {- Commits the index into the specified branch (or other ref),
- with the specified parent refs, and returns the committed sha. - with the specified parent refs, and returns the committed sha.
@ -162,7 +171,7 @@ commitCommand' runner commitmode ps = runner $
- one parent, and it has the same tree that would be committed. - one parent, and it has the same tree that would be committed.
- -
- Unlike git-commit, does not run any hooks, or examine the work tree - Unlike git-commit, does not run any hooks, or examine the work tree
- in any way. - in any way, or output a summary.
-} -}
commit :: CommitMode -> Bool -> String -> Branch -> [Ref] -> Repo -> IO (Maybe Sha) commit :: CommitMode -> Bool -> String -> Branch -> [Ref] -> Repo -> IO (Maybe Sha)
commit commitmode allowempty message branch parentrefs repo = do commit commitmode allowempty message branch parentrefs repo = do

View file

@ -1,6 +1,6 @@
{- git merging {- git merging
- -
- Copyright 2012-2016 Joey Hess <id@joeyh.name> - Copyright 2012-2021 Joey Hess <id@joeyh.name>
- -
- Licensed under the GNU AGPL version 3 or higher. - Licensed under the GNU AGPL version 3 or higher.
-} -}
@ -22,9 +22,12 @@ import Git.Branch (CommitMode(..))
data MergeConfig data MergeConfig
= MergeNonInteractive = MergeNonInteractive
-- ^ avoids interactive merge -- ^ avoids interactive merge with commit message edit
| MergeUnrelatedHistories | MergeUnrelatedHistories
-- ^ avoids git's prevention of merging unrelated histories -- ^ avoids git's prevention of merging unrelated histories
| MergeQuiet
-- ^ avoids usual output when merging, but errors will still be
-- displayed
deriving (Eq) deriving (Eq)
merge :: Ref -> [MergeConfig] -> CommitMode -> Repo -> IO Bool merge :: Ref -> [MergeConfig] -> CommitMode -> Repo -> IO Bool
@ -36,11 +39,14 @@ merge' extraparams branch mergeconfig commitmode r
go [Param $ fromRef branch] go [Param $ fromRef branch]
| otherwise = go [Param "--no-edit", Param $ fromRef branch] | otherwise = go [Param "--no-edit", Param $ fromRef branch]
where where
go ps = merge'' (sp ++ [Param "merge"] ++ ps ++ extraparams) mergeconfig r go ps = merge'' (sp ++ [Param "merge"] ++ qp ++ ps ++ extraparams) mergeconfig r
sp sp
| commitmode == AutomaticCommit = | commitmode == AutomaticCommit =
[Param "-c", Param "commit.gpgsign=false"] [Param "-c", Param "commit.gpgsign=false"]
| otherwise = [] | otherwise = []
qp
| MergeQuiet `notElem` mergeconfig = []
| otherwise = [Param "--quiet"]
merge'' :: [CommandParam] -> [MergeConfig] -> Repo -> IO Bool merge'' :: [CommandParam] -> [MergeConfig] -> Repo -> IO Bool
merge'' ps mergeconfig r merge'' ps mergeconfig r

View file

@ -43,9 +43,11 @@ module Messages (
setupConsole, setupConsole,
enableDebugOutput, enableDebugOutput,
commandProgressDisabled, commandProgressDisabled,
commandProgressDisabled',
jsonOutputEnabled, jsonOutputEnabled,
outputMessage, outputMessage,
withMessageState, withMessageState,
MessageState,
prompt, prompt,
mkPrompter, mkPrompter,
) where ) where
@ -276,12 +278,14 @@ debugDisplayer = do
{- Should commands that normally output progress messages have that {- Should commands that normally output progress messages have that
- output disabled? -} - output disabled? -}
commandProgressDisabled :: Annex Bool commandProgressDisabled :: Annex Bool
commandProgressDisabled = withMessageState $ \s -> return $ commandProgressDisabled = withMessageState $ return . commandProgressDisabled'
case outputType s of
NormalOutput -> concurrentOutputEnabled s commandProgressDisabled' :: MessageState -> Bool
QuietOutput -> True commandProgressDisabled' s = case outputType s of
JSONOutput _ -> True NormalOutput -> concurrentOutputEnabled s
SerializedOutput _ _ -> True QuietOutput -> True
JSONOutput _ -> True
SerializedOutput _ _ -> True
jsonOutputEnabled :: Annex Bool jsonOutputEnabled :: Annex Bool
jsonOutputEnabled = withMessageState $ \s -> return $ jsonOutputEnabled = withMessageState $ \s -> return $

View file

@ -0,0 +1,9 @@
[[!comment format=mdwn
username="joey"
subject="""comment 1"""
date="2021-07-19T15:24:55Z"
content="""
It's perfectly fine to file a bug report if you find something like this.
Nobody seems to have wanted that before.. I've implemented it now.
"""]]