darcs-2.18.5: src/Darcs/UI/Commands/Tag.hs
-- 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 <http://producingoss.com/en/development-cycle.html>."
]
, [ "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