!. This
file overrides the contents of the environment variables
\verb!$DARCS_EMAIL! and \verb!$EMAIL!.
\paragraph{boring}\label{boring}
The \verb!_darcs/prefs/boring! file may contain a list of regular
expressions describing files, such as object files, that you do not expect
to add to your project. As an example, the boring file that I use with
my darcs repository is:
\begin{verbatim}
\.hi$
\.o$
^\.[^/]
^_
~$
(^|/)CVS($|/)
\end{verbatim}
A newly created repository has a longer boring file that
includes many common source control, backup, temporary, and compiled files.
You may want to have the boring file under version
control. To do this you can use darcs setpref to set the value
``boringfile'' to the name of your desired boring file
(e.g.\ \verb-darcs setpref boringfile .boring-, where \verb-.boring-
is the repository path of a file
that has been
darcs added to your repository). The boringfile preference overrides
\verb!_darcs/prefs/boring!, so be sure to copy that file to the boringfile.
You can also set up a ``boring'' regexps
file in your home directory, named \verb!~/.darcs/boring!,
on MS Windows~\ref{ms_win}, which will be
used with all of your darcs repositories.
Any file not already managed by darcs and whose repository path (such
as \verb!manual/index.html!) matches any of
the boring regular expressions is considered boring. The boring file is
used to filter the files provided to darcs add, to allow you to use a
simple \verb-darcs add newdir newdir/-\verb-*- % cabal haddock barfs on adjacent / *
without accidentally adding a bunch of
object files. It is also used when the \verb!--look-for-adds! flag is
given to whatsnew or record.
Note that once a file has been added to darcs, it is not considered
boring, even if it matches the boring file filter.
\begin{code}
defaultBoring :: [String]
defaultBoring = ["# Boring file regexps:",
"",
"### compiler and interpreter intermediate files",
"# haskell (ghc) interfaces",
"\\.hi$", "\\.hi-boot$", "\\.o-boot$",
"# object files",
"\\.o$","\\.o\\.cmd$",
"# profiling haskell",
"\\.p_hi$", "\\.p_o$",
"# haskell program coverage resp. profiling info",
"\\.tix$", "\\.prof$",
"# fortran module files",
"\\.mod$",
"# linux kernel",
"\\.ko\\.cmd$","\\.mod\\.c$",
"(^|/)\\.tmp_versions($|/)",
"# *.ko files aren't boring by default because they might",
"# be Korean translations rather than kernel modules",
"# \\.ko$",
"# python, emacs, java byte code",
"\\.py[co]$", "\\.elc$","\\.class$",
"# objects and libraries; lo and la are libtool things",
"\\.(obj|a|exe|so|lo|la)$",
"# compiled zsh configuration files",
"\\.zwc$",
"# Common LISP output files for CLISP and CMUCL",
"\\.(fas|fasl|sparcf|x86f)$",
"",
"### build and packaging systems",
"# cabal intermediates",
"\\.installed-pkg-config",
"\\.setup-config",
"# standard cabal build dir, might not be boring for everybody",
"# ^dist(/|$)",
"# autotools",
"(^|/)autom4te\\.cache($|/)", "(^|/)config\\.(log|status)$",
"# microsoft web expression, visual studio metadata directories",
"\\_vti_cnf$",
"\\_vti_pvt$",
"# gentoo tools",
"\\.revdep-rebuild.*",
"# generated dependencies",
"^\\.depend$",
"",
"### version control systems",
"# cvs",
"(^|/)CVS($|/)","\\.cvsignore$",
"# cvs, emacs locks",
"^\\.#",
"# rcs",
"(^|/)RCS($|/)", ",v$",
"# subversion",
"(^|/)\\.svn($|/)",
"# mercurial",
"(^|/)\\.hg($|/)",
"# git",
"(^|/)\\.git($|/)",
"# bzr",
"\\.bzr$",
"# sccs",
"(^|/)SCCS($|/)",
"# darcs",
"(^|/)"++darcsdir++"($|/)", "(^|/)\\.darcsrepo($|/)",
"^\\.darcs-temp-mail$",
"-darcs-backup[[:digit:]]+$",
"# gnu arch",
"(^|/)(\\+|,)",
"(^|/)vssver\\.scc$",
"\\.swp$","(^|/)MT($|/)",
"(^|/)\\{arch\\}($|/)","(^|/).arch-ids($|/)",
"# bitkeeper",
"(^|/)BitKeeper($|/)","(^|/)ChangeSet($|/)",
"",
"### miscellaneous",
"# backup files",
"~$","\\.bak$","\\.BAK$",
"# patch originals and rejects",
"\\.orig$", "\\.rej$",
"# X server",
"\\..serverauth.*",
"# image spam",
"\\#", "(^|/)Thumbs\\.db$",
"# vi, emacs tags",
"(^|/)(tags|TAGS)$",
"#(^|/)\\.[^/]",
"# core dumps",
"(^|/|\\.)core$",
"# partial broken files (KIO copy operations)",
"\\.part$",
"# waf files, see http://code.google.com/p/waf/",
"(^|/)\\.waf-[[:digit:].]+-[[:digit:]]+($|/)",
"(^|/)\\.lock-wscript$",
"# mac os finder",
"(^|/)\\.DS_Store$" ]
darcsdirFilter :: [FilePath] -> [FilePath]
darcsdirFilter = filter (not.isDarcsdir)
isDarcsdir :: FilePath -> Bool
isDarcsdir ('.':'/':f) = isDarcsdir f
isDarcsdir "." = True
isDarcsdir "" = True
isDarcsdir ".." = True
isDarcsdir "../" = True
isDarcsdir "_darcs" = True
isDarcsdir fp = "_darcs/" `isPrefixOf` fp
globalPrefsDir :: IO (Maybe FilePath)
globalPrefsDir = do
env <- getEnvironment
case lookup "DARCS_TESTING_PREFS_DIR" env of
Just d -> return (Just d)
Nothing -> (getAppUserDataDirectory "darcs" >>= return.Just)
`catchall` (return Nothing)
environmentHelpHome :: ([String], [String])
environmentHelpHome = (["HOME", "APPDATA"], [
"Per-user preferences are set in $HOME/.darcs (on Unix) or",
"%APPDATA%/darcs (on Windows). This is also the default location of",
"the cache."])
getGlobal :: String -> IO [String]
getGlobal f = do
dir <- globalPrefsDir
case dir of
(Just d) -> getPreffile $ d </> f
Nothing -> return []
globalCacheDir :: IO (Maybe FilePath)
globalCacheDir = slash_cache `fmap` globalPrefsDir
where slash_cache = fmap (</> "cache")
boringRegexps :: IO [Regex]
boringRegexps = do
borefile <- defPrefval "boringfile" (darcsdir ++ "/prefs/boring")
bores <- getPrefLines borefile `catchall` return []
gbs <- getGlobal "boring"
return $ map mkRegex $ bores ++ gbs
boringFileFilter :: IO ([FilePath] -> [FilePath])
boringFileFilter = boringRegexps >>= return . actualBoringFileFilter
noncomments :: [String] -> [String]
noncomments ss = filter is_ok ss
where is_ok "" = False
is_ok ('#':_) = False
is_ok _ = True
getPrefLines :: ReadableDirectory m => FilePath -> m [String]
getPrefLines f = (notconflicts . noncomments . map stripCr . lines)
`fmap` mReadBinFile (fp2fn f)
where notconflicts = filter nc
startswith [] _ = True
startswith (x:xs) (y:ys) | x == y = startswith xs ys
startswith _ _ = False
nc l | startswith "v v v v v v v" l = False
nc l | startswith "*************" l = False
nc l | startswith "^ ^ ^ ^ ^ ^ ^" l = False
nc _ = True
actualBoringFileFilter :: [Regex] -> [FilePath] -> [FilePath]
actualBoringFileFilter regexps files = filter (not . boring . normalize) files
where boring file = isDarcsdir file ||
any (\regexp -> isJust $ matchRegex regexp file) regexps
normalize :: FilePath -> FilePath
normalize ('.':'/':f) = normalize f
normalize f = normalize_helper $ reverse f
where
normalize_helper ('/':rf) = normalize_helper rf
normalize_helper rf = reverse rf
\end{code}
\paragraph{binaries}
The \verb!_darcs/prefs/binaries! file may contain a list of regular
expressions describing files that should be treated as binary files rather
than text files. Darcs automatically treats files containing
\verb!^Z\! or \verb!'\0'! within the first 4096 bytes as being binary files.
You probably will want to have the binaries file under
version control. To do this you can use darcs setpref to set the value
``binariesfile'' to the name of your desired binaries file
(e.g.\ \verb'darcs setpref binariesfile ./.binaries', where
\verb'.binaries' is a file that has been
darcs added to your repository). As with the boring file, you can also set
up a \verb!~/.darcs/binaries! file if you like, on MS Windows~\ref{ms_win}.
\begin{code}
data FileType = BinaryFile | TextFile
deriving (Eq)
defaultBinaries :: [String]
defaultBinaries = help ++
["\\.(" ++ e ++ "|" ++ map toUpper e ++ ")$" | e <- extensions ]
where extensions = ["a","bmp","bz2","doc","elc","exe","gif","gz","iso",
"jar","jpe?g","mng","mpe?g","p[nbgp]m","pdf","png",
"pyc","so","tar","tgz","tiff?","z","zip"]
help = map ("# "++) binariesFileHelp
binariesFileHelp :: [String]
binariesFileHelp =
["This file contains a list of extended regular expressions, one per",
"line. A file path matching any of these expressions is assumed to",
"contain binary data (not text). The entries in ~/.darcs/binaries (if",
"it exists) supplement those in this file.",
"",
"Blank lines, and lines beginning with an octothorpe (#) are ignored.",
"See regex(7) for a description of extended regular expressions."]
filetypeFunction :: IO (FilePath -> FileType)
filetypeFunction = do
binsfile <- defPrefval "binariesfile" (darcsdir ++ "/prefs/binaries")
bins <- getPrefLines binsfile `catch`
(\e-> if isDoesNotExistError e then return [] else ioError e)
gbs <- getGlobal "binaries"
regexes <- return (map (\r -> mkRegex r) (bins ++ gbs))
let isbin f = or $ map (\r -> isJust $ matchRegex r f) regexes
ftf f = if isbin $ normalize f then BinaryFile else TextFile
in
return ftf
prefsDirectory :: ReadableDirectory m => m String
prefsDirectory =
do darcs <- mDoesDirectoryExist $ fp2fn darcsdir
if darcs
then return $ darcsdir ++ "/prefs/"
else fail $ "Directory " ++ darcsdir ++ "/ does not exist!"
withPrefsDirectory :: ReadableDirectory m => (String -> m ()) -> m ()
withPrefsDirectory j = do prefs <- prefsDirectory `mplus` return "x"
when (prefs /= "x") $ j prefs
addToPreflist :: WriteableDirectory m => String -> String -> m ()
addToPreflist p s = withPrefsDirectory $ \prefs -> do
hasprefs <- mDoesDirectoryExist $ fp2fn prefs
unless hasprefs $ mCreateDirectory $ fp2fn prefs
pl <- getPreflist p
mWriteBinFile (fp2fn $ prefs ++ p) $ unlines $ union [s] pl
getPreflist :: ReadableDirectory m => String -> m [String]
getPreflist p = do prefs <- prefsDirectory `mplus` return "x"
if (prefs /= "x") then getPreffile $ prefs ++ p
else return []
getPreffile :: ReadableDirectory m => FilePath -> m [String]
getPreffile f = do
hasprefs <- mDoesFileExist (fp2fn f)
if hasprefs
then getPrefLines f
else return []
setPreflist :: WriteableDirectory m => String -> [String] -> m ()
setPreflist p ls = withPrefsDirectory $ \prefs -> do
haspref <- mDoesDirectoryExist $ fp2fn prefs
if haspref
then mWriteBinFile (fp2fn $ prefs ++ p) (unlines ls)
else return ()
defPrefval :: String -> String -> IO String
defPrefval p d = do
pv <- getPrefval p
case pv of Nothing -> return d
Just v -> return v
getPrefval :: ReadableDirectory m => String -> m (Maybe String)
getPrefval p =
do pl <- getPreflist "prefs"
case map snd $ filter ((==p).fst) $ map (break (==' ')) pl of
[val] -> case words val of
[] -> return Nothing
_ -> return $ Just $ tail val
_ -> return Nothing
setPrefval :: WriteableDirectory m => String -> String -> m ()
setPrefval p v = do pl <- getPreflist "prefs"
setPreflist "prefs" $
filter ((/=p).fst.(break (==' '))) pl ++ [p++" "++v]
changePrefval :: WriteableDirectory m => String -> String -> String -> m ()
changePrefval p f t =
do pl <- getPreflist "prefs"
ov <- getPrefval p
newval <- case ov of
Nothing -> return t
Just old -> if old == f then return t else return old
setPreflist "prefs" $
filter ((/=p).fst.(break(==' '))) pl ++ [p++" "++newval]
defaultrepo :: [DarcsFlag] -> AbsolutePath -> [String] -> IO [String]
defaultrepo opts _ [] =
do let fixR r | not (isFile r) = return r
| otherwise = do absr <- ioAbsolute r
return $ toFilePath absr
case [r | RemoteRepo r <- opts] of
[] -> do defrepo <- getPreflist "defaultrepo"
case defrepo of
[r] -> (:[]) `fmap` fixR r
_ -> return []
rs -> mapM fixR rs
defaultrepo _ _ r = return r
setDefaultrepo :: String -> [DarcsFlag] -> IO ()
setDefaultrepo r opts = do olddef <- getPreflist "defaultrepo"
let doit = NoSetDefault `notElem` opts && greenLight
greenLight = wetRun
&& not rIsTmp
&& (olddef /= [r] || olddef == [])
if doit
then setPreflist "defaultrepo" [r]
else when greenLight $ putStr . unlines $
[ "Note: if you want to change the default remote repository to"
, r ++ ","
, "quit now and issue the same command with the --set-default flag."
]
addToPreflist "repos" r
`catchall` return ()
where
wetRun = DryRun `notElem` opts
rIsTmp = r `elem` [x | RemoteRepo x <- opts]
\end{code}
\paragraph{email}
The \verb!_darcs/prefs/email! file is used to provide the e-mail address for your
repository that others will use when they \verb!darcs send! a patch back to you.
The contents of the file should simply be an e-mail address.
\paragraph{sources}
The \verb!_darcs/prefs/sources! file is used to indicate alternative
locations from which to download patches when using a ``hashed''
repository. This file contains lines such as:
\begin{verbatim}
cache:/home/droundy/.darcs/cache
readonly:/home/otheruser/.darcs/cache
repo:http://darcs.net
\end{verbatim}
This would indicate that darcs should first look in
\verb!/home/droundy/.darcs/cache! for patches that might be missing, and if
the patch isn't there, it should save a copy there for future use. In that
case, darcs will look in \verb!/home/otheruser/.darcs/cache! to see if that
user might have downloaded a copy, but won't try to save a copy there, of
course. Finally, it will look in \verb!http://darcs.net!. Note that the
\verb!sources! file can also exist in \verb!~/.darcs/!. Also note that the
sources mentioned in your \verb!sources! file will be tried \emph{before}
the repository you are pulling from. This can be useful in avoiding
downloading patches multiple times when you pull from a remote repository
to more than one local repository.
A global cache is enabled by default in your home directory. The cache
allows darcs to avoid re-downloading patches (for example, when doing a
second darcs get of the same repository), and also allows darcs to use hard
links to reduce disk usage.
Note that the cache directory should reside on the same filesystem as your
repositories, so you may need to vary this. You can also use multiple
cache directories on different filesystems, if you have several filesystems
on which you use darcs.
\begin{code}
getCaches :: [DarcsFlag] -> String -> IO Cache
getCaches opts repodir =
do here <- parsehs `fmap` getPreffile (darcsdir ++ "/prefs/sources")
there <- (parsehs . lines . BC.unpack) `fmap`
(gzFetchFilePS (repodir ++ "/" ++ darcsdir ++ "/prefs/sources") Cachable
`catchall` return B.empty)
globalcachedir <- globalCacheDir
let globalcache = case (nocache,globalcachedir) of
(True,_) -> []
(_,Just d) -> [Cache Directory Writable d]
_ -> []
globalsources <- parsehs `fmap` getGlobal "sources"
thisdir <- getCurrentDirectory
let thisrepo = if Ephemeral `elem` opts
then [Cache Repo NotWritable $ toFilePath thisdir]
else [Cache Repo Writable $ toFilePath thisdir]
let tempCache = nub $ thisrepo ++ globalcache ++ globalsources ++
here ++ [Cache Repo NotWritable repodir] ++ there
return $ Ca $ sortBy compareByLocality tempCache
where
parsehs = catMaybes . map readln . noncomments
readln l | take 5 l == "repo:" = Just (Cache Repo NotWritable (drop 5 l))
| take 9 l == "thisrepo:" = Just (Cache Repo Writable (drop 9 l))
| nocache = Nothing
| take 6 l == "cache:" = Just (Cache Directory Writable (drop 6 l))
| take 9 l == "readonly:" = Just (Cache Directory NotWritable (drop 9 l))
| otherwise = Nothing
nocache = NoCache `elem` opts
\end{code}