-- Copyright (C) 2003-2004 David Roundy -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation; either version 2, or (at your option) -- any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program; see the file COPYING. If not, write to -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. module Darcs.UI.Commands.Tag ( tag ) where import Darcs.Prelude import Control.Monad ( when ) import System.IO ( hPutStr, stderr ) import Darcs.Patch ( RepoPatch ) import Darcs.Patch.Apply ( ApplyState ) import Darcs.Patch.Depends ( getUncovered ) import Darcs.Patch.Info ( patchinfo ) import Darcs.Patch.Named ( adddeps, infopatch ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, n2pia ) import Darcs.Patch.Set ( appendPSFL, emptyPatchSet, patchSet2FL, patchSetTags ) import Darcs.Patch.Witnesses.Ordered ( (:>)(..), FL(..) ) import Darcs.Patch.Witnesses.Sealed ( Sealed(..), mapSeal ) import Darcs.Repository ( AccessType(..) , RepoJob(..) , Repository , finalizeRepositoryChanges , readPatches , tentativelyAddPatch , withRepoLock ) import Darcs.Repository.Flags ( UpdatePending(..) ) import Darcs.UI.Commands ( DarcsCommand(..) , amInHashedRepository , nodefaults , putFinished , withStdOpts ) import Darcs.UI.Completion ( noArgs ) import Darcs.UI.Flags ( DarcsFlag , author , getAuthor , getDate , umask , useCache , verbosity ) import Darcs.UI.Options ( (?), (^) ) import qualified Darcs.UI.Options.All as O import Darcs.UI.PatchHeader ( getLog ) import Darcs.UI.SelectChanges ( SelectionConfig(allowSkipAll) , WhichChanges(..) , runSelection , selectionConfig ) import qualified Darcs.UI.SelectChanges as S import Darcs.Util.Path ( AbsolutePath ) import Darcs.Util.Printer ( Doc, formatWords, vsep ) import Darcs.Util.Tree ( Tree ) tagDescription :: String tagDescription = "Name the current repository state for future reference." tagHelp :: Doc tagHelp = vsep $ map formatWords [ [ "The `darcs tag` command names the current repository state, so that it" , "can easily be referred to later. It does so by recording a special kind" , "of patch that makes no changes and which explicitly depends on all" , "patches currently existing in the repository (except for those which" , "are depended upon by other tags already in the repository). In the" , "common case of a sequential series of tags, this means that the tag" , "depends on all patches since the last tag, plus that tag itself." ] , [ "Every *important* state should be" , "tagged; in particular it is good practice to tag each stable release" , "with a number or codename. Advice on release numbering can be found" , "at ." ] , [ "To reproduce the state of a repository `R` as at tag `t`, use the" , "command `darcs clone --tag t R`. Note however that tags are matched" , "as regular expressions, like with `--patch`. To make sure you get the" , "right tag it may be better to use `darcs clone --tag '^t$'`." , "The command `darcs show tags` lists all tags in the current repository." ] , [ "Tagging also provides significant performance benefits: when Darcs" , "reaches a tag that depends on all preceding patches, it can often" , "stop processing. A tag in such a position is called \"clean\". For" , "instance, operations like push and pull need to examine only patches" , "that come after the latest shared clean tag." ] , [ "Like normal patches, a tag has a name, an author, a timestamp and an" , "optional long description, but it does not change the working tree." , "A tag can have any name, but it is generally best to pick a naming" , "scheme and stick to it." ] , [ "By default a tag names the entire repository state at the time the tag" , "is created. If the --ask-deps option is used, the patches to include" , "as part of the tag can be explicitly selected." ] , [ "The `darcs tag` command accepts the `--pipe` option, which behaves as" , "described in `darcs record`." ] ] tag :: DarcsCommand tag = DarcsCommand { commandProgramName = "darcs" , commandName = "tag" , commandHelp = tagHelp , commandDescription = tagDescription , commandExtraArgs = -1 , commandExtraArgHelp = ["[TAGNAME]"] , commandCommand = tagCmd , commandPrereq = amInHashedRepository , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandOptions = tagOpts } where tagBasicOpts = O.patchname ^ O.author ^ O.pipe ^ O.askLongComment ^ O.askDeps ^ O.repoDir tagAdvancedOpts = O.umask tagOpts = tagBasicOpts `withStdOpts` tagAdvancedOpts tagCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () tagCmd _ opts args = withRepoLock (useCache ? opts) (umask ? opts) $ RepoJob $ \(repository :: Repository 'RW p wU wR) -> do date <- getDate hasPipe the_author <- getAuthor (author ? opts) hasPipe patches <- readPatches repository tags <- return $ patchSetTags patches Sealed chosenPatches <- if O.askDeps ? opts then mapSeal (appendPSFL emptyPatchSet) <$> askAboutTagDepends opts (patchSet2FL patches) else return $ Sealed patches let deps = getUncovered chosenPatches (name, long_comment) <- get_name_log tags myinfo <- patchinfo date name the_author long_comment let mypatch = infopatch myinfo NilFL _ <- tentativelyAddPatch repository YesUpdatePending $ n2pia $ adddeps mypatch deps _ <- finalizeRepositoryChanges repository (O.dryRun ? opts) putFinished opts $ "tagging '"++name++"'" where get_name_log :: [String] -> IO (String, [String]) get_name_log tags = do (name, comment, _) <- getLog (case O.patchname ? opts of Nothing | null args -> Nothing | otherwise -> Just (unwords args) Just s -> Just s) hasPipe (O.logfile ? opts) (O.askLongComment ? opts) Nothing mempty when (length name < 2) $ hPutStr stderr $ "Do you really want to tag '" ++ name ++ "'? If not type: darcs obliterate --last=1\n" when (name `elem` tags) $ putStrLn $ "WARNING: The tag " ++ "\"" ++ name ++ "\"" ++ " already exists." return ("TAG " ++ name, comment) hasPipe = O.pipe ? opts askAboutTagDepends :: forall p wX wY . (RepoPatch p, ApplyState p ~ Tree) => [DarcsFlag] -> FL (PatchInfoAnd p) wX wY -> IO (Sealed (FL (PatchInfoAnd p) wX)) askAboutTagDepends flags ps = do let opts = S.PatchSelectionOptions { S.verbosity = verbosity ? flags , S.matchFlags = [] , S.interactive = True , S.selectDeps = O.PromptDeps , S.withSummary = O.NoSummary } (deps:>_) <- runSelection ps $ ((selectionConfig FirstReversed "depend on" opts Nothing Nothing) { allowSkipAll = False }) return $ Sealed deps