darcs-beta 2.7.98.3 → 2.7.99.1
raw patch · 96 files changed
+4538/−2747 lines, 96 filesdep ~basedep ~test-frameworkPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: base, test-framework
API changes (from Hackage documentation)
- Darcs.Patch: applyToFilepaths :: (Apply p, ApplyState p ~ Tree) => p x y -> [FilePath] -> [FilePath]
- Darcs.Patch.Apply: applyToFilepaths :: (Apply p, ApplyState p ~ Tree) => p x y -> [FilePath] -> [FilePath]
- Darcs.Patch.ApplyMonad: withFilePaths :: [FileName] -> FilePathMonad a -> [FileName]
- Darcs.Patch.V2.Non: add :: (WL l, Patchy p, ToFromPrim p) => l (PrimOf p) x y -> Non p y -> Non p x
- Darcs.Patch.V2.Non: addP :: (Patchy p, ToFromPrim p) => p x y -> Non p y -> Non p x
- Darcs.Patch.V2.Non: addPs :: (Patchy p, ToFromPrim p) => RL p x y -> Non p y -> Non p x
- Darcs.Patch.V2.Non: propAdjustTwice :: (Patchy p, ToFromPrim p, MyEq (PrimOf p)) => p x y -> Non p y -> Maybe Doc
- Darcs.Patch.V2.Non: remP :: (Patchy p, ToFromPrim p) => p x y -> Non p x -> Maybe (Non p y)
- Darcs.Patch.V2.Non: remPs :: (Patchy p, ToFromPrim p) => FL p x y -> Non p x -> Maybe (Non p y)
- URL.Curl: cachableToInt :: Cachable -> CInt
- URL.Curl: curl_enable_debug :: IO ()
- URL.Curl: curl_last_url :: IO CString
- URL.Curl: curl_pipelining_enabled :: IO CInt
- URL.Curl: curl_request_url :: CString -> CString -> CInt -> IO CString
- URL.Curl: curl_wait_next_url :: Ptr CInt -> Ptr CLong -> IO CString
- URL.Curl: pipeliningEnabled :: IO Bool
- URL.Curl: requestUrl :: String -> FilePath -> Cachable -> IO String
- URL.Curl: setDebugHTTP :: IO ()
- URL.Curl: waitNextUrl :: IO (String, String, Maybe ConnectionError)
+ Darcs.Arguments: NoPauseForGui :: DarcsFlag
+ Darcs.Arguments: PauseForGui :: DarcsFlag
+ Darcs.Commands.AmendRecord: amendunrecord :: DarcsCommand
+ Darcs.Commands.Move: instance Eq FileKind
+ Darcs.Commands.Move: instance Show FileKind
+ Darcs.Commands.Move: instance Show FileStatus
+ Darcs.Commands.Test: test :: DarcsCommand
+ Darcs.Flags: NoPauseForGui :: DarcsFlag
+ Darcs.Flags: PauseForGui :: DarcsFlag
+ Darcs.Flags: wantGuiPause :: [DarcsFlag] -> Bool
+ Darcs.Patch: applyToFilePaths :: (Apply p, ApplyState p ~ Tree) => p x y -> Maybe [(FilePath, FilePath)] -> [FilePath] -> ([FilePath], [FilePath], [(FilePath, FilePath)])
+ Darcs.Patch: effectOnFilePaths :: (Apply p, ApplyState p ~ Tree) => p x y -> [FilePath] -> [FilePath]
+ Darcs.Patch.Apply: applyToFilePaths :: (Apply p, ApplyState p ~ Tree) => p x y -> Maybe [(FilePath, FilePath)] -> [FilePath] -> ([FilePath], [FilePath], [(FilePath, FilePath)])
+ Darcs.Patch.Apply: effectOnFilePaths :: (Apply p, ApplyState p ~ Tree) => p x y -> [FilePath] -> [FilePath]
+ Darcs.Patch.ApplyMonad: withFileNames :: (Maybe [OrigFileNameOf]) -> [FileName] -> FilePathMonad a -> FilePathMonadState
+ Darcs.Patch.FileName: isParentOrEqOf :: FileName -> FileName -> Bool
+ Darcs.Patch.V2.Non: commuteOrAddToCtx :: (Patchy p, ToFromPrim p) => p x y -> Non p y -> Non p x
+ Darcs.Patch.V2.Non: commuteOrAddToCtxRL :: (Patchy p, ToFromPrim p) => RL p x y -> Non p y -> Non p x
+ Darcs.Patch.V2.Non: commuteOrRemFromCtx :: (Patchy p, ToFromPrim p) => p x y -> Non p x -> Maybe (Non p y)
+ Darcs.Patch.V2.Non: commuteOrRemFromCtxFL :: (Patchy p, ToFromPrim p) => FL p x y -> Non p x -> Maybe (Non p y)
+ Darcs.Patch.V2.Non: commutePrimsOrAddToCtx :: (WL l, Patchy p, ToFromPrim p) => l (PrimOf p) x y -> Non p y -> Non p x
+ Darcs.Utils: askEnter :: String -> IO ()
+ Darcs.Utils: setDarcsEncodings :: IO ()
- Darcs.Arguments: pipeInteractive, recordRollback, fromOpt, leaveTestDir, rmlogfile, logfile, sendmailCmd, matchSeveral, matchRange, matchOne, allowUnrelatedRepos, forceReplace, help, notest, test, ignoretimes, lookforadds, askdeps, author, matchSeveralOrLast, matchSeveralOrRange, nocompress, inReplyTo, charset, subject, uncompressNocompress, summary, unified, repoCombinator, unidiff, outputAutoName, output, editDescription, patchnameOption, distnameOption, xmloutput, reply, applyConflictOptions, ccApply, ccSend, target, pullConflictOptions, useExternalMerge, storeInMemory, diffCmdFlag, sendToContext, happyForwarding, matchOneContext, changesFormat, creatorhash, changesReverse, matchOneNontag, askLongComment, noskipBoring, allowProblematicFilenames, diffflags, humanReadable, allInteractive, allPipeInteractive :: DarcsOption
+ Darcs.Arguments: pipeInteractive, recordRollback, fromOpt, leaveTestDir, rmlogfile, logfile, sendmailCmd, matchSeveral, matchRange, matchOne, allowUnrelatedRepos, forceReplace, help, notest, test, ignoretimes, lookforadds, askdeps, author, matchSeveralOrLast, matchSeveralOrRange, nocompress, inReplyTo, charset, subject, uncompressNocompress, summary, unified, repoCombinator, unidiff, outputAutoName, output, editDescription, patchnameOption, distnameOption, xmloutput, reply, applyConflictOptions, ccApply, ccSend, target, pullConflictOptions, pauseForGui, useExternalMerge, storeInMemory, diffCmdFlag, sendToContext, happyForwarding, matchOneContext, changesFormat, creatorhash, changesReverse, matchOneNontag, askLongComment, noskipBoring, allowProblematicFilenames, diffflags, humanReadable, allInteractive, allPipeInteractive :: DarcsOption
- Darcs.Patch: showContextPatch :: (ShowPatch p, Monad m, ApplyMonadTrans m (ApplyState p), ApplyMonad m (ApplyState p), Monad m) => p x y -> m Doc
+ Darcs.Patch: showContextPatch :: (ShowPatch p, Monad m, ApplyMonadTrans m (ApplyState p), ApplyMonad m (ApplyState p)) => p x y -> m Doc
- Darcs.Patch.Patchy: showContextPatch :: (ShowPatch p, Monad m, ApplyMonadTrans m (ApplyState p), ApplyMonad m (ApplyState p), Monad m) => p x y -> m Doc
+ Darcs.Patch.Patchy: showContextPatch :: (ShowPatch p, Monad m, ApplyMonadTrans m (ApplyState p), ApplyMonad m (ApplyState p)) => p x y -> m Doc
- Darcs.Patch.Prim.V3.Show: showHunk :: String -> UUID -> Hunk t t1 -> Doc
+ Darcs.Patch.Prim.V3.Show: showHunk :: String -> UUID -> Hunk x y -> Doc
- Darcs.Patch.Set: PatchSet :: RL (PatchInfoAnd p) y x -> RL (Tagged p) start y -> PatchSet p start x
+ Darcs.Patch.Set: PatchSet :: RL (PatchInfoAnd p) x y -> RL (Tagged p) start x -> PatchSet p start y
- Darcs.Patch.Set: Tagged :: PatchInfoAnd p y z -> Maybe String -> RL (PatchInfoAnd p) x3 y -> Tagged p x3 z
+ Darcs.Patch.Set: Tagged :: PatchInfoAnd p y z -> Maybe String -> RL (PatchInfoAnd p) x y -> Tagged p x z
- Darcs.Patch.Set: data PatchSet p start x
+ Darcs.Patch.Set: data PatchSet p start y
- Darcs.Patch.Set: data Tagged p x2 z
+ Darcs.Patch.Set: data Tagged p x z
- Darcs.Patch.Set: newset2FL :: PatchSet p start x6 -> FL (PatchInfoAnd p) start x6
+ Darcs.Patch.Set: newset2FL :: PatchSet p start x -> FL (PatchInfoAnd p) start x
- Darcs.Patch.Set: newset2RL :: PatchSet p start x4 -> RL (PatchInfoAnd p) start x4
+ Darcs.Patch.Set: newset2RL :: PatchSet p start x -> RL (PatchInfoAnd p) start x
- Darcs.Patch.Set: progressPatchSet :: String -> PatchSet p start x7 -> PatchSet p start x7
+ Darcs.Patch.Set: progressPatchSet :: String -> PatchSet p start x -> PatchSet p start x
- Darcs.Patch.Set: tags :: PatchSet p start x13 -> [PatchInfo]
+ Darcs.Patch.Set: tags :: PatchSet p start x -> [PatchInfo]
- Darcs.Patch.Show: showContextPatch :: (ShowPatch p, Monad m, ApplyMonadTrans m (ApplyState p), ApplyMonad m (ApplyState p), Monad m) => p x y -> m Doc
+ Darcs.Patch.Show: showContextPatch :: (ShowPatch p, Monad m, ApplyMonadTrans m (ApplyState p), ApplyMonad m (ApplyState p)) => p x y -> m Doc
- Darcs.Patch.TouchesFiles: lookTouch :: (Patchy p, ApplyState p ~ Tree) => [FilePath] -> p x y -> (Bool, [FilePath])
+ Darcs.Patch.TouchesFiles: lookTouch :: (Patchy p, ApplyState p ~ Tree) => Maybe [(FilePath, FilePath)] -> [FilePath] -> p x y -> (Bool, [FilePath], [FilePath], [(FilePath, FilePath)])
- Darcs.Patch.V2.Non: Non :: FL p a x -> PrimOf p x y -> Non p a
+ Darcs.Patch.V2.Non: Non :: FL p x y -> PrimOf p y z -> Non p x
- Darcs.PrintPatch: printFriendly :: Patchy p => [DarcsFlag] -> p x y -> IO ()
+ Darcs.PrintPatch: printFriendly :: (Patchy p, ApplyState p ~ Tree) => (Maybe (Tree IO)) -> [DarcsFlag] -> p x y -> IO ()
- Darcs.Repository: data PatchSet p start x
+ Darcs.Repository: data PatchSet p start y
- Darcs.Resolution: externalResolution :: (RepoPatch p, ApplyState p ~ Tree) => Tree IO -> String -> FL (PrimOf p) x y -> FL (PrimOf p) x z -> FL p y a -> IO (Sealed (FL (PrimOf p) a))
+ Darcs.Resolution: externalResolution :: (RepoPatch p, ApplyState p ~ Tree) => Tree IO -> String -> [DarcsFlag] -> FL (PrimOf p) x y -> FL (PrimOf p) x z -> FL p y a -> IO (Sealed (FL (PrimOf p) a))
- Darcs.SelectChanges: selectionContextPrim :: PrimPatch prim => String -> [DarcsFlag] -> Maybe (Splitter prim) -> Maybe [FilePath] -> PatchSelectionContext prim
+ Darcs.SelectChanges: selectionContextPrim :: PrimPatch prim => String -> [DarcsFlag] -> Maybe (Splitter prim) -> Maybe [FilePath] -> Maybe (Tree IO) -> PatchSelectionContext prim
- Darcs.SelectChanges: viewChanges :: Patchy p => [DarcsFlag] -> [Sealed2 p] -> IO ()
+ Darcs.SelectChanges: viewChanges :: (Patchy p, ApplyState p ~ Tree) => [DarcsFlag] -> [Sealed2 p] -> IO ()
Files
- contrib/darcs_completion +3/−3
- darcs-beta.cabal +39/−18
- doc/src/darcs.tex +1/−1
- release/distributed-context +1/−1
- src/CommandLine.hs +46/−28
- src/Darcs/Annotate.hs +190/−99
- src/Darcs/Arguments.hs +22/−5
- src/Darcs/ColorPrinter.hs +1/−1
- src/Darcs/Commands/Add.hs +223/−153
- src/Darcs/Commands/AmendRecord.hs +221/−155
- src/Darcs/Commands/Annotate.hs +4/−3
- src/Darcs/Commands/Apply.hs +49/−21
- src/Darcs/Commands/Changes.hs +88/−55
- src/Darcs/Commands/Diff.hs +30/−24
- src/Darcs/Commands/Dist.hs +98/−76
- src/Darcs/Commands/Help.hs +1/−2
- src/Darcs/Commands/Move.hs +60/−6
- src/Darcs/Commands/Pull.hs +57/−28
- src/Darcs/Commands/Record.hs +14/−6
- src/Darcs/Commands/Replace.hs +3/−3
- src/Darcs/Commands/Revert.hs +5/−5
- src/Darcs/Commands/Rollback.hs +1/−1
- src/Darcs/Commands/Test.hs +67/−0
- src/Darcs/Commands/Unrevert.hs +4/−3
- src/Darcs/Commands/WhatsNew.hs +120/−94
- src/Darcs/Diff.hs +112/−62
- src/Darcs/External.hs +7/−7
- src/Darcs/Flags.hs +82/−27
- src/Darcs/Global.hs +126/−61
- src/Darcs/Match.hs +1/−1
- src/Darcs/Patch.hs +88/−46
- src/Darcs/Patch/Apply.hs +51/−16
- src/Darcs/Patch/ApplyMonad.hs +36/−11
- src/Darcs/Patch/FileName.hs +16/−9
- src/Darcs/Patch/Format.hs +15/−14
- src/Darcs/Patch/Info.hs +11/−0
- src/Darcs/Patch/Merge.hs +32/−15
- src/Darcs/Patch/Named.hs +5/−4
- src/Darcs/Patch/Prim/V3/Apply.hs +0/−1
- src/Darcs/Patch/Prim/V3/Core.hs +9/−5
- src/Darcs/Patch/Prim/V3/Details.hs +0/−6
- src/Darcs/Patch/Prim/V3/ObjectMap.hs +0/−2
- src/Darcs/Patch/Prim/V3/Read.hs +6/−10
- src/Darcs/Patch/Prim/V3/Show.hs +12/−17
- src/Darcs/Patch/Set.hs +22/−11
- src/Darcs/Patch/Show.hs +1/−1
- src/Darcs/Patch/Split.hs +10/−6
- src/Darcs/Patch/TokenReplace.hs +6/−3
- src/Darcs/Patch/TouchesFiles.hs +29/−18
- src/Darcs/Patch/V2/Non.hs +162/−123
- src/Darcs/Patch/V2/Real.hs +875/−782
- src/Darcs/PrintPatch.hs +5/−2
- src/Darcs/Repository.hs +3/−7
- src/Darcs/Repository/Cache.hs +2/−2
- src/Darcs/Repository/HashedRepo.hs +4/−0
- src/Darcs/Repository/Internal.hs +3/−3
- src/Darcs/Repository/Merge.hs +1/−1
- src/Darcs/Repository/State.hs +2/−2
- src/Darcs/Resolution.hs +28/−18
- src/Darcs/RunCommand.hs +30/−16
- src/Darcs/SelectChanges.hs +96/−76
- src/Darcs/Ssh.hs +120/−78
- src/Darcs/TheCommands.hs +4/−1
- src/Darcs/Utils.hs +269/−120
- src/DateMatcher.hs +98/−80
- src/English.hs +25/−15
- src/Exec.hs +75/−44
- src/IsoDate.hs +11/−3
- src/Lcs.hs +11/−1
- src/Progress.hs +210/−108
- src/Ratified.hs +7/−1
- src/SHA1.hs +10/−0
- src/URL.hs +8/−0
- src/Workaround.hs +87/−36
- src/darcs.hs +54/−39
- src/test.hs +3/−0
- tests/amend-unrecord.sh +5/−2
- tests/failing-issue1522_trailing_slash_borkage.sh +35/−0
- tests/failing-issue2100-add-failures.sh +39/−0
- tests/issue1645-ignore-symlinks-case-fold.sh +1/−1
- tests/issue1645-ignore-symlinks.sh +11/−11
- tests/issue1749-rmdir.sh +3/−3
- tests/issue1763-pull-fails-on-non-ascii-filenames.sh +4/−1
- tests/issue194.sh +1/−1
- tests/issue2013_send_to_context.sh +2/−2
- tests/issue2136-changes_created_as_for_multiple_files.sh +86/−0
- tests/issue2139-mv-to-dir.sh +54/−0
- tests/match-date.sh +0/−1
- tests/mv-formerly-pl.sh +1/−1
- tests/network/issue2090-transfer-mode.sh +2/−0
- tests/network/ssh.sh +2/−0
- tests/pending_has_conflicts.sh +1/−3
- tests/rename_shouldnt_affect_prefixes.sh +50/−0
- tests/rmdir.sh +0/−6
- tests/rollback.sh +1/−1
- tests/workingdir.sh +12/−12
contrib/darcs_completion view
@@ -2,8 +2,8 @@ # darcs command line completion. # Copyright 2002 "David Roundy" <droundy@abridgegame.org>-#-have darcs &&+# This archive should be copied in the directory /etc/bash_completion.d/+ _darcs() { local cur@@ -48,5 +48,5 @@ return 0 }-[ "$have" ] && complete -F _darcs -o default darcs+complete -F _darcs -o default darcs
darcs-beta.cabal view
@@ -1,5 +1,5 @@ Name: darcs-beta-version: 2.7.98.3+version: 2.7.99.1 License: GPL License-file: COPYING Author: David Roundy <droundy@darcs.net>, <darcs-users@darcs.net>@@ -122,13 +122,13 @@ description: Build with warnings-as-errors -- If base >= 4.4 is used, the standard directory functions--- do Unicode translation. Use an explicit flag so we can+-- do Unicode translation. Use a flag so we can -- detect this situation and use a workaround.-flag base44+flag force-char8-encoding default: False- -- once we have a workaround remove the manual: line- manual: True- description: Allow base 4.4 or above: note, there are known problems with Unicode filenames (http://bugs.darcs.net/issue2095)+ description: Allow base 4.5 or above on Unix. base 4.4 introduces a change in+ the behaviour of filenames that requires API calls in+ base 4.5 to workaround. So base 4.4 is not supported at all. -- ---------------------------------------------------------------------- -- darcs library@@ -191,6 +191,7 @@ Darcs.Commands.ShowRepo Darcs.Commands.ShowTags Darcs.Commands.Tag+ Darcs.Commands.Test Darcs.Commands.TrackDown Darcs.Commands.TransferMode Darcs.Commands.Util@@ -323,11 +324,11 @@ SHA1 URL URL.Request- URL.Curl URL.HTTP Workaround other-modules: Version+ URL.Curl c-sources: src/atomic_create.c src/fpstring.c@@ -353,10 +354,14 @@ if os(solaris) cc-options: -DHAVE_SIGINFO_H - if flag(base44)- build-depends: base >= 4 && < 4.6+ if os(windows)+ build-depends: base >= 4 && < 4.6 else- build-depends: base >= 4 && < 4.4+ if flag(force-char8-encoding)+ build-depends: base >= 4.5 && < 4.6+ cpp-options: -DFORCE_CHAR8_ENCODING+ else+ build-depends: base >= 4 && < 4.4 build-depends: extensible-exceptions >= 0.1 && < 0.2, regex-compat >= 0.95.1,@@ -510,10 +515,14 @@ if os(solaris) cc-options: -DHAVE_SIGINFO_H - if flag(base44)- build-depends: base >= 4 && < 4.6+ if os(windows)+ build-depends: base >= 4 && < 4.6 else- build-depends: base >= 4 && < 4.4+ if flag(force-char8-encoding)+ build-depends: base >= 4.5 && < 4.6+ cpp-options: -DFORCE_CHAR8_ENCODING+ else+ build-depends: base >= 4 && < 4.4 build-depends: extensible-exceptions >= 0.1 && < 0.2, regex-compat >= 0.95.1,@@ -594,7 +603,7 @@ Executable darcs-test main-is: test.hs - build-tools: ghc >= 6.10 && < 7.2+ build-tools: ghc >= 6.10 && < 7.6 if !flag(test)@@ -612,7 +621,7 @@ HUnit >= 1.0, cmdlib >= 0.2.1 && < 0.4, shellish >= 0.1.3 && < 0.2,- test-framework >= 0.2.2,+ test-framework >= 0.4.0, test-framework-hunit >= 0.2.2, test-framework-quickcheck2 >= 0.2.8 @@ -669,6 +678,14 @@ if flag(threaded) ghc-options: -threaded + if flag(curl)+ extra-libraries: curl+ includes: curl/curl.h+ cpp-options: -DHAVE_CURL+ c-sources: src/hscurl.c+ cc-options: -DHAVE_CURL++ if flag(hpc) ghc-prof-options: -fhpc -hpcdir dist/hpc/darcs-test @@ -691,10 +708,14 @@ if os(solaris) cc-options: -DHAVE_SIGINFO_H - if flag(base44)- build-depends: base >= 4 && < 4.6+ if os(windows)+ build-depends: base >= 4 && < 4.6 else- build-depends: base >= 4 && < 4.4+ if flag(force-char8-encoding)+ build-depends: base >= 4.5 && < 4.6+ cpp-options: -DFORCE_CHAR8_ENCODING+ else+ build-depends: base >= 4 && < 4.4 if !os(windows) build-depends: unix >= 1.0 && < 2.6
doc/src/darcs.tex view
@@ -1236,7 +1236,7 @@ \darcsCommand{tag} \darcsCommand{setpref}-\darcsCommand{check}+\darcsCommand{test} If you like, you can configure your repository to be able to run a test suite of some sort. You can do this by using ``setpref'' to set the
release/distributed-context view
@@ -1,1 +1,1 @@-Just "\nContext:\n\n[TAG 2.7.98.3\nGanesh Sittampalam <ganesh@earth.li>**20120119060551\n Ignore-this: bc156878e2f54804226a43035ea5da28\n] \n"+Just "\nContext:\n\n[TAG 2.7.99.1\nFlorent Becker <florent.becker@ens-lyon.org>**20120401092449\n Ignore-this: 23fef1226d1278f70993402dc13ea30d\n] \n"
src/CommandLine.hs view
@@ -15,6 +15,14 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. +-- |+-- Module : CommandLine+-- Copyright : 2005 Benedikt Schmidt+-- License : GPL+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable+-- -- |A parser for commandlines, returns an arg list and expands -- format strings given in a translation table. Additionally -- the commandline can end with "%<" specifying that the command@@ -42,46 +50,57 @@ -- > ("a %s %<",(["a","<insert subject here>"], True))] -- > -- > runTests = map (uncurry $ testParser (commandline formatTable)) testCases-module CommandLine ( parseCmd, addUrlencoded ) where-import Text.ParserCombinators.Parsec++module CommandLine+ ( parseCmd+ , addUrlencoded+ ) where++import Control.Arrow ( (***) ) import Data.Char ( ord, intToDigit, toUpper ) import Data.List ( find )+import Text.ParserCombinators.Parsec -- | assoc list mapping characters to strings -- eg (c,s) means that %c is replaced by s type FTable = [(Char,String)]+ commandline :: FTable -> Parser ([String], Bool)-commandline ftable = consumeAll (do l <- sepEndBy1 (arg ftable)- (try separator)- redir <- formatRedir- spaces- return (l,redir))+commandline ftable = consumeAll $ do+ l <- sepEndBy1 (arg ftable) (try separator)+ redir <- formatRedir+ spaces+ return (l,redir) -escape:: Parser String-arg, format, quotedArg, unquotedArg, quoteContent :: FTable -> Parser String-arg ftable = (quotedArg ftable <|> unquotedArg ftable)+arg :: FTable -> Parser String+arg ftable = quotedArg ftable <|> unquotedArg ftable -unquotedArg ftable = do (try $ format ftable)- <|> (many1 $ noneOf [' ', '\t', '"', '%'])+unquotedArg :: FTable -> Parser String+unquotedArg ftable = try (format ftable) <|> many1 (noneOf " \t\"%") -quotedArg ftable = between (char '"') (char '"') $ quoteContent ftable+quotedArg :: FTable -> Parser String+quotedArg ftable = between quoteChar quoteChar $ quoteContent ftable+ where+ quoteChar = char '"' +quoteContent :: FTable -> Parser String quoteContent ftable = do s1 <- escape- <|> (try $ format ftable)- <|> (many1 (noneOf ['"', '\\', '%']))+ <|> try (format ftable)+ <|> many1 (noneOf "\"\\%") s2 <- quoteContent ftable return $ s1 ++ s2 <|> return "" formatRedir :: Parser Bool-formatRedir = do _ <- string "%<"- return True- <|> return False+formatRedir = (string "%<" >> return True)+ <|> return False +format :: FTable -> Parser String format ftable = do _ <- char '%'- c <- oneOf (map fst ftable)+ c <- oneOf (map fst ftable) return $ expandFormat ftable c +escape :: Parser String escape = do _ <- char '\\' c <- anyChar return [c]@@ -92,12 +111,12 @@ return r separator :: Parser ()-separator = do skipMany1 space+separator = skipMany1 space expandFormat :: FTable -> Char -> String expandFormat ftable c = case find ((==c) . fst) ftable of- Just (_,s) -> s- Nothing -> error "impossible"+ Just (_,s) -> s+ Nothing -> error "impossible" -- | parse a commandline returning a list of strings -- (intended to be used as argv) and a bool value which@@ -106,17 +125,16 @@ -- and replaced by the given strings. E.g. if the ftable is -- [('s',"Some subject")], then "%s" is replaced by "Some subject" parseCmd :: FTable -> String -> Either ParseError ([String],Bool)-parseCmd ftable s = parse (commandline ftable) "" s+parseCmd ftable = parse (commandline ftable) "" urlEncode :: String -> String urlEncode s = concatMap escapeC s- where escapeC x = if allowed x then [x] else '%':(intToHex $ ord x)+ where escapeC x = if allowed x then [x] else '%' : intToHex (ord x) intToHex i = map intToDigit [i `div` 16, i `mod` 16]- allowed x = x `elem` ['a' .. 'z'] ++ ['A' .. 'Z'] ++ ['0' .. '9']- ++ "!'()*-.~"+ allowed x = x `elem` allowedChars+ allowedChars = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "!'()*-.~" -- | for every mapping (c,s), add a mapping with uppercase c -- and the urlencoded string s addUrlencoded :: FTable -> FTable-addUrlencoded ftable =- ftable ++(map (\ (c,x) -> (toUpper c, urlEncode x)) ftable)+addUrlencoded ftable = ftable ++ map (toUpper *** urlEncode) ftable
src/Darcs/Annotate.hs view
@@ -22,120 +22,195 @@ -- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -- SOFTWARE. -module Darcs.Annotate ( annotate, annotateDirectory, format, machineFormat ) where-import Darcs.Patch.ApplyMonad( ApplyMonad(..) )-import Data.Maybe( isJust, catMaybes )-import Data.List( nub, groupBy )+-- |+-- Module : Darcs.Annotate+-- Copyright : 2010 Petr Rockai+-- License : MIT+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable++module Darcs.Annotate+ (+ annotate+ , annotateDirectory+ , format+ , machineFormat+ ) where++import Prelude hiding ( pi )++import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC+import qualified Data.Map as M import qualified Data.Vector as V++import Data.List( nub, groupBy )+import Data.Maybe( isJust, catMaybes, isNothing )++import Control.Monad.State ( modify, when, gets, State, execState )+import Control.Applicative( (<$>) )++import Darcs.Patch.ApplyMonad( ApplyMonad(..) ) import Darcs.Patch.FileName( FileName, movedirfilename, fn2ps, ps2fn ) import Darcs.Patch.Apply ( Apply, apply, ApplyState ) import Darcs.Patch.Info ( PatchInfo(..), humanFriendly, piAuthor, makeFilename ) import Darcs.Patch.PatchInfoAnd( info, PatchInfoAnd )+import Darcs.Witnesses.Ordered++import Storage.Hashed.Tree( Tree ) import Lcs( getChanges ) import Printer( renderString )-import Darcs.Witnesses.Ordered import ByteStringUtils ( linesPS, unlinesPS )-import Control.Monad.State ( modify, when, gets, State, execState )-import Control.Applicative( (<$>) )-import qualified Data.Map as M-import qualified Data.ByteString as B-import qualified Data.ByteString.Char8 as BC-import Storage.Hashed.Tree( Tree ) #include "gadts.h" #include "impossible.h" -data FileOrDirectory = File | Directory deriving (Show, Eq)-data Annotated = Annotated { annotated :: V.Vector (Maybe PatchInfo, B.ByteString)- , current :: [(Int, B.ByteString)]- , path :: Maybe FileName- , what :: FileOrDirectory- , currentInfo :: PatchInfo }- deriving Show +data FileOrDirectory = File+ | Directory+ deriving (Show, Eq)++data Annotated = Annotated+ { annotated :: V.Vector (Maybe PatchInfo, B.ByteString)+ , current :: [(Int, B.ByteString)]+ , path :: Maybe FileName+ , what :: FileOrDirectory+ , currentInfo :: PatchInfo+ } deriving Show++ type AnnotatedM = State Annotated +-- XXX: No explicit method nor default method for 'editFile', 'editDirectory' instance ApplyMonad AnnotatedM Tree where type ApplyMonadBase AnnotatedM = AnnotatedM++ nestedApply _ _ = undefinedFun "nestedApply"+ liftApply _ _ = undefinedFun "liftApply"+ getApplyState = undefinedFun "getApplyState"+ putApplyState _ = undefinedFun "putApplyState"+ mReadFilePS = undefinedFun "mReadFilePS"++ mDoesFileExist _ = return True mDoesDirectoryExist _ = return True- mReadFilePS = fail "mReadFilePS undefined for Annotated"- mCreateDirectory _ = return ()- mCreateFile _ = return ()- mRemoveFile f = do p <- gets path- when (p == Just f) $ modify (\x -> x { path = Nothing })- updateDirectory f+ mCreateDirectory _ = return ()+ mCreateFile _ = return ()++ mRemoveFile f = do+ p <- gets path+ when (p == Just f) $ modify (\x -> x { path = Nothing })+ updateDirectory f+ mRemoveDirectory = mRemoveFile- mRename a b =- do p <- gets path- w <- gets what- when (isJust p) $- modify $ \st -> st { path = Just $ movedirfilename a b (fromJust p) }- when (w == Directory) $ do- let fix (i, x) = (i, fn2ps $ movedirfilename a b (ps2fn x))- modify $ \st -> st { current = map fix $ current st }- mModifyFilePS f job = do p <- gets path- when (p == Just f) $ updateFile (fmap linesPS . job . unlinesPS)- mModifyFilePSs f job = do p <- gets path- when (p == Just f) $ updateFile job -updateFile :: ([B.ByteString] -> AnnotatedM [B.ByteString]) -> AnnotatedM ()+ mRename a b = do+ p <- gets path+ w <- gets what+ when (isJust p) $+ modify $ \st -> st { path = Just $ movedirfilename a b (fromJust p) }+ when (w == Directory) $ do+ let fix (i, x) = (i, fn2ps $ movedirfilename a b (ps2fn x))+ modify $ \st -> st { current = map fix $ current st }++ mModifyFilePS f job = do+ p <- gets path+ when (p == Just f) $ updateFile (fmap linesPS . job . unlinesPS)++ mModifyFilePSs f job = do+ p <- gets path+ when (p == Just f) $ updateFile job++undefinedFun :: Monad m+ => String+ -> m a+undefinedFun name = fail $ name ++ " undefined for Annotated"+++updateFile :: ([B.ByteString]+ -> AnnotatedM [B.ByteString])+ -> AnnotatedM () updateFile job = (==File) <$> gets what >>= flip when go- where go = do before <- map snd `fmap` gets current- after <- job before- reannotate $ getChanges before after- reannotate [] = return ()- reannotate ((off, remove, add):rest) = do- i <- gets currentInfo- c <- gets current- a <- gets annotated- modify $ \s -> s { current = take off c ++ [ (-1, x) | x <- add ] ++- drop (off + length remove) c- , annotated = merge i a $ take (length remove) $ drop off c }- reannotate rest- merge i a l = a V.// [ (line, (Just i, B.empty))- | (line, _) <- l, line >= 0 && line < V.length a]+ where+ go = do+ before <- map snd `fmap` gets current+ after <- job before+ reannotate $ getChanges before after + reannotate [] = return ()+ reannotate ((off, remove, add):rest) = do+ i <- gets currentInfo+ c <- gets current+ a <- gets annotated+ modify $ \s -> s { current = take off c ++ [ (-1, x) | x <- add ] +++ drop (off + length remove) c+ , annotated = merge i a $ take (length remove) $ drop off c+ }+ reannotate rest++ merge i a l = a V.// [ (line, (Just i, B.empty))+ | (line, _) <- l, line >= 0 && line < V.length a]++ updateDirectory :: FileName -> AnnotatedM () updateDirectory p = (==Directory) <$> gets what >>= flip when go- where go = do let line = fn2ps p- files <- gets current- case filter ((==line) . snd) files of- [match@(ident, _)] -> reannotate ident match line- _ -> return ()- reannotate ident match line =- modify $ \x -> x { annotated = annotated x V.// [ (ident, update line $ currentInfo x) ]- , current = filter (/= match) $ current x }- update line inf = (Just inf, BC.concat [ " -- created as: ", line ])+ where+ go = do let line = fn2ps p+ files <- gets current+ case filter ((==line) . snd) files of+ [match@(ident, _)] -> reannotate ident match line+ _ -> return ()+ reannotate ident match line =+ modify $ \x -> x { annotated = annotated x V.// [ (ident, update line $ currentInfo x) ]+ , current = filter (/= match) $ current x }+ update line inf = (Just inf, BC.concat [ " -- created as: ", line ]) + complete :: Annotated -> Bool-complete x = V.all (isJust . fst) $ annotated x+complete x = (V.all (isJust . fst) $ annotated x) || (isNothing $ path x) -annotate' :: (Apply p, ApplyState p ~ Tree) =>- FL (PatchInfoAnd p) C(x y) -> Annotated -> Annotated++annotate' :: (Apply p, ApplyState p ~ Tree)+ => FL (PatchInfoAnd p) C(x y)+ -> Annotated+ -> Annotated annotate' NilFL ann = ann annotate' (p :>: ps) ann- | complete ann = ann- | otherwise = annotate' ps $ execState (apply p) (ann { currentInfo = info p })+ | complete ann = ann+ | otherwise = annotate' ps $ execState (apply p) (ann { currentInfo = info p }) + annotate :: (Apply p, ApplyState p ~ Tree)- => FL (PatchInfoAnd p) C(x y) -> FileName -> B.ByteString -> Annotated+ => FL (PatchInfoAnd p) C(x y)+ -> FileName+ -> B.ByteString+ -> Annotated annotate patches inipath inicontent = annotate' patches initial- where initial = Annotated { path = Just inipath- , currentInfo = error "There is no currentInfo."- , current = zip [0..] (linesPS inicontent)- , what = File- , annotated = V.replicate (length $ breakLines inicontent)- (Nothing, B.empty) }+ where+ initial = Annotated { path = Just inipath+ , currentInfo = error "There is no currentInfo."+ , current = zip [0..] (linesPS inicontent)+ , what = File+ , annotated = V.replicate (length $ breakLines inicontent)+ (Nothing, B.empty)+ } + annotateDirectory :: (Apply p, ApplyState p ~ Tree)- => FL (PatchInfoAnd p) C(x y) -> FileName -> [FileName] -> Annotated+ => FL (PatchInfoAnd p) C(x y)+ -> FileName+ -> [FileName]+ -> Annotated annotateDirectory patches inipath inicontent = annotate' patches initial- where initial = Annotated { path = Just inipath- , currentInfo = error "There is no currentInfo."- , current = zip [0..] (map fn2ps inicontent)- , what = Directory- , annotated = V.replicate (length inicontent) (Nothing, B.empty) }+ where+ initial = Annotated { path = Just inipath+ , currentInfo = error "There is no currentInfo."+ , current = zip [0..] (map fn2ps inicontent)+ , what = Directory+ , annotated = V.replicate (length inicontent) (Nothing, B.empty)+ } + machineFormat :: B.ByteString -> Annotated -> String machineFormat d a = unlines [ case i of Just inf -> makeFilename inf@@ -144,28 +219,44 @@ ++ " | " ++ BC.unpack line ++ " " ++ BC.unpack add | ((i, add), line) <- zip (V.toList $ annotated a) (breakLines d) ] -format :: B.ByteString -> Annotated -> String++format :: B.ByteString+ -> Annotated+ -> String format d a = pi_list ++ "\n" ++ file- where pis = nub $ catMaybes . map fst $ V.toList (annotated a)- pi_list = unlines $ [ show n ++ ": " ++ renderString (humanFriendly i)- | (n :: Int, i) <- zip [1..] pis ]- pi_map = M.fromList (zip pis [1 :: Int ..])- file_ann = groupBy (\a b -> fst a == fst b) $ zip (V.toList $ annotated a) (breakLines d)- file = concat [ annotation (fst $ head chunk) ++ " | " ++ line (head chunk) ++- "\n" ++ unlines [ indent 25 (" | " ++ line l) | l <- tail chunk ]- | chunk <- file_ann ]- line ((_, add), l) = BC.unpack $ BC.concat [l, " ", add]- annotation (Just i, _) | Just n <- M.lookup i pi_map =- pad 20 (piMail i) ++ " " ++ pad 4 ("#" ++ show n)- annotation _ = pad 25 "unknown"- pad n str = replicate (n - length str) ' ' ++ (take n str)- indent n str = replicate n ' ' ++ str- piMail pi- | '<' `elem` piAuthor pi = takeWhile (/= '>') . drop 1 . dropWhile (/= '<') $ piAuthor pi- | otherwise = piAuthor pi+ where+ pi_list = unlines $ [ show n ++ ": " ++ renderString (humanFriendly i)+ | (n :: Int, i) <- zip [1..] pis ] + file = concat [ annotation (fst $ head chunk) ++ " | " ++ line (head chunk) +++ "\n" ++ unlines [ indent 25 (" | " ++ line l) | l <- tail chunk ]+ | chunk <- file_ann ]++ pis = nub $ catMaybes . map fst $ V.toList (annotated a)++ pi_map = M.fromList (zip pis [1 :: Int ..])++ file_ann = groupBy (\x y -> fst x == fst y) $ zip (V.toList $ annotated a) (breakLines d)++ line ((_, add), l) = BC.unpack $ BC.concat [l, " ", add]++ annotation (Just i, _) | Just n <- M.lookup i pi_map =+ pad 20 (piMail i) ++ " " ++ pad 4 ("#" ++ show n)+ annotation _ = pad 25 "unknown"++ pad n str = replicate (n - length str) ' ' ++ (take n str)++ indent n str = replicate n ' ' ++ str++ piMail pi+ | '<' `elem` piAuthor pi = takeWhile (/= '>') . drop 1 . dropWhile (/= '<') $ piAuthor pi+ | otherwise = piAuthor pi+++breakLines :: BC.ByteString+ -> [BC.ByteString] breakLines s = case BC.split '\n' s of- [] -> []- split | BC.null (last split) -> init split- | otherwise -> split+ [] -> []+ split | BC.null (last split) -> init split+ | otherwise -> split
src/Darcs/Arguments.hs view
@@ -65,6 +65,7 @@ summary, unified, tokens, partial, diffCmdFlag, diffflags, unidiff, xmloutput,+ pauseForGui, forceReplace, dryRun, dryRunNoxml, printDryRunMessageAndExit, showFriendly, matchOne, matchSeveral, matchRange,@@ -257,6 +258,8 @@ getContent (ApplyAs s) = StringContent s getContent (DiffCmd s) = StringContent s getContent (ExternalMerge s) = StringContent s+getContent PauseForGui = NoContent+getContent NoPauseForGui = NoContent getContent (DiffFlags s) = StringContent s getContent (OnePattern _) = NoContent -- FIXME!!! getContent (SeveralPattern _) = NoContent -- FIXME!!!@@ -555,6 +558,7 @@ askLongComment, matchOneNontag, changesReverse, creatorhash, changesFormat, matchOneContext, happyForwarding, sendToContext, diffCmdFlag, storeInMemory, useExternalMerge,+ pauseForGui, pullConflictOptions, target, ccSend, ccApply, applyConflictOptions, reply, xmloutput, distnameOption, patchnameOption, editDescription, output, outputAutoName, unidiff, repoCombinator,@@ -738,7 +742,7 @@ [DarcsNoArgOption [] ["add"] NoAmendUnrecord "add the changes to patch (default)", DarcsNoArgOption [] ["unrecord"]- AmendUnrecord "substract the changes from patch"]+ AmendUnrecord "subtract the changes from patch"] ignoretimes = DarcsMultipleChoiceOption@@ -818,7 +822,7 @@ -- from @_darcs/prefs@, and if it's not possible, ask the user. getAuthor :: [DarcsFlag] -> IO String getAuthor (Author a:_) = return a-getAuthor (Pipe:_) = do askUser "Who is the author? "+getAuthor (Pipe:_) = askUser "Who is the author? " getAuthor (_:flags) = getAuthor flags getAuthor [] = do easy_author <- getEasyAuthor@@ -890,9 +894,9 @@ DarcsNoArgOption [] ["no-summary"] NoSummary "don't summarize changes"] unified = DarcsMultipleChoiceOption [DarcsNoArgOption ['u'] ["unified"] Unified- "output patch in a darcs-specific format similar to diff -u",+ "output changes in a darcs-specific format similar to diff -u", DarcsNoArgOption [] ["no-unified"] NonUnified- "output patch in darcs' usual format"]+ "output changes in darcs' usual format"] unidiff = DarcsMultipleChoiceOption [DarcsNoArgOption ['u'] ["unified"] Unified@@ -903,6 +907,19 @@ diffCmdFlag = DarcsSingleOption $ DarcsArgOption [] ["diff-command"] DiffCmd "COMMAND" "specify diff command (ignores --diff-opts)" +pauseForGui = DarcsMultipleChoiceOption+ [ DarcsNoArgOption+ []+ ["pause-for-gui"]+ PauseForGui+ "pause for an external diff or merge command to finish [DEFAULT]"+ , DarcsNoArgOption+ []+ ["no-pause-for-gui"]+ NoPauseForGui+ "return immediately after external diff or merge command finishes"+ ]+ storeInMemory = DarcsMultipleChoiceOption [DarcsNoArgOption [] ["store-in-memory"] StoreInMemory "do patch application in memory rather than on disk",@@ -1110,7 +1127,7 @@ DarcsNoArgOption [] ["allow-conflicts"] AllowConflicts "allow conflicts, but don't mark them", DarcsNoArgOption [] ["dont-allow-conflicts","no-allow-conflicts"]- NoAllowConflicts "fail if there patches that would create conflicts",+ NoAllowConflicts "fail if there are patches that would create conflicts", DarcsNoArgOption [] ["skip-conflicts"] SkipConflicts "filter out any patches that would create conflicts" ]
src/Darcs/ColorPrinter.hs view
@@ -6,7 +6,7 @@ import Darcs.External (getTermNColors) import Printer (Printer, Printers, Printers'(..), Printable(..), Color(..), invisiblePrinter, (<>), (<?>), Doc(Doc,unDoc), unsafeBothText, simplePrinter, hcat,- unsafeText, unsafeChar, space, unsafePackedString,+ unsafeText, unsafePackedString, renderStringWith, prefix ) import Data.Char ( isAscii, isPrint, isSpace, isControl, ord, chr ) import Data.Bits ( bit, xor )
src/Darcs/Commands/Add.hs view
@@ -15,88 +15,115 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -module Darcs.Commands.Add ( add, expandDirs ) where+-- |+-- Module : Darcs.Commands.Add+-- Copyright : 2002-2004 David Roundy+-- License : GPL+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable -import Data.List ( (\\), nub)-import Data.Maybe( isNothing )+module Darcs.Commands.Add+ (+ add+ , expandDirs+ ) where+++#include "gadts.h"+#include "impossible.h"++ import Control.Monad ( when, unless, liftM )-import Storage.Hashed.Tree( Tree, findTree, expand )-import Storage.Hashed.AnchoredPath( floatPath, anchorPath, parents )+import Data.List ( (\\), nub )+import Data.Maybe ( isNothing, maybeToList )+import Printer ( text )+import Storage.Hashed.Tree ( Tree, findTree, expand )+import Storage.Hashed.AnchoredPath ( floatPath, anchorPath, parents )+import System.FilePath.Posix ( takeDirectory, (</>) )+import System.Posix.Files ( isRegularFile, isDirectory, isSymbolicLink )+import System.Directory ( getPermissions, readable ) -import Darcs.Commands(DarcsCommand(..), putVerbose, putWarning, nodefaults)-import Darcs.Arguments (noskipBoring, allowProblematicFilenames,- recursive, workingRepoDir, dryRunNoxml, umaskOption,- listFiles, listUnregisteredFiles,- DarcsFlag ( Recursive, DryRun, Verbose, Quiet),- fixSubPaths,+import qualified System.FilePath.Windows as WindowsFilePath++import Darcs.Arguments ( noskipBoring, allowProblematicFilenames,+ recursive, workingRepoDir, dryRunNoxml, umaskOption,+ listFiles, listUnregisteredFiles,+ DarcsFlag ( Recursive, DryRun, Verbose, Quiet ),+ fixSubPaths, )-import Darcs.Flags( includeBoring, doAllowCaseOnly, doAllowWindowsReserved,)-import Darcs.Utils ( withCurrentDirectory, nubsort )-import Darcs.Repository.State( readRecordedAndPending )-import Darcs.Repository ( amInHashedRepository, withRepoLock, RepoJob(..), addToPending )+import Darcs.Commands ( DarcsCommand(..), putVerbose, putWarning, nodefaults)+import Darcs.Flags ( includeBoring, doAllowCaseOnly, doAllowWindowsReserved ) import Darcs.Patch ( Patchy, PrimPatch, applyToTree, addfile, adddir ) import Darcs.Patch.Apply ( ApplyState )-import Darcs.Witnesses.Ordered ( FL(..), (+>+), nullFL )-import Darcs.Witnesses.Sealed ( Sealed(..), Gap(..), FreeLeft, unFreeLeft )-import Darcs.Utils ( treeHas, treeHasDir, treeHasAnycase, getFileStatus ) import Darcs.RepoPath ( SubPath, toFilePath, simpleSubPath, toPath )+import Darcs.Repository.State ( readRecordedAndPending )+import Darcs.Repository ( amInHashedRepository, withRepoLock, RepoJob(..), addToPending ) import Darcs.Repository.Prefs ( darcsdirFilter, boringFileFilter )-import Data.Maybe ( maybeToList )-import System.FilePath.Posix ( takeDirectory, (</>) )-import qualified System.FilePath.Windows as WindowsFilePath-import System.Posix.Files( isRegularFile, isDirectory, isSymbolicLink )-import System.Directory ( getPermissions, readable )-import Printer( text )+import Darcs.Utils ( treeHas, treeHasDir, treeHasAnycase, getFileStatus, withCurrentDirectory, nubsort )+import Darcs.Witnesses.Ordered ( FL(..), (+>+), nullFL )+import Darcs.Witnesses.Sealed ( Sealed(..), Gap(..), FreeLeft, unFreeLeft ) -#include "gadts.h"-#include "impossible.h" addDescription :: String addDescription = "Add one or more new files or directories." + addHelp :: String addHelp =- "Generally a repository contains both files that should be version\n" ++- "controlled (such as source code) and files that Darcs should ignore\n" ++- "(such as executables compiled from the source code). The `darcs add'\n" ++- "command is used to tell Darcs which files to version control.\n" ++- "\n" ++- "When an existing project is first imported into a Darcs repository, it\n" ++- "is common to run `darcs add -r *' or `darcs record -l' to add all\n" ++- "initial source files into darcs.\n"++- "\n" ++- "Adding symbolic links (symlinks) is not supported.\n\n"+ "Generally a repository contains both files that should be version\n" +++ "controlled (such as source code) and files that Darcs should ignore\n" +++ "(such as executables compiled from the source code). The `darcs add'\n" +++ "command is used to tell Darcs which files to version control.\n" +++ "\n" +++ "When an existing project is first imported into a Darcs repository, it\n" +++ "is common to run `darcs add -r *' or `darcs record -l' to add all\n" +++ "initial source files into darcs.\n"+++ "\n" +++ "Adding symbolic links (symlinks) is not supported.\n\n" -add :: DarcsCommand-add = DarcsCommand {commandProgramName = "darcs",- commandName = "add",- commandHelp = addHelp ++ addHelp',- commandDescription = addDescription,- commandExtraArgs = -1,- commandExtraArgHelp = ["<FILE or DIRECTORY> ..."],- commandCommand = addCmd,- commandPrereq = amInHashedRepository,- commandGetArgPossibilities = listUnregisteredFiles,- commandArgdefaults = nodefaults,- commandAdvancedOptions = [umaskOption],- commandBasicOptions =- [noskipBoring, allowProblematicFilenames,- recursive "add contents of subdirectories",- workingRepoDir, dryRunNoxml]} addHelp' :: String addHelp' =- "Darcs will ignore all files and folders that look `boring'. The\n" ++- "--boring option overrides this behaviour.\n" ++- "\n" ++- "Darcs will not add file if another file in the same folder has the\n" ++- "same name, except for case. The --case-ok option overrides this\n" ++- "behaviour. Windows and OS X usually use filesystems that do not allow\n" ++- "files a folder to have the same name except for case (for example,\n" ++- "`ReadMe' and `README'). If --case-ok is used, the repository might be\n" ++- "unusable on those systems!\n\n"+ "Darcs will ignore all files and folders that look `boring'. The\n" +++ "--boring option overrides this behaviour.\n" +++ "\n" +++ "Darcs will not add file if another file in the same folder has the\n" +++ "same name, except for case. The --case-ok option overrides this\n" +++ "behaviour. Windows and OS X usually use filesystems that do not allow\n" +++ "files a folder to have the same name except for case (for example,\n" +++ "`ReadMe' and `README'). If --case-ok is used, the repository might be\n" +++ "unusable on those systems!\n\n" -addCmd :: [DarcsFlag] -> [String] -> IO ()++add :: DarcsCommand+add = DarcsCommand+ {+ commandProgramName = "darcs"+ , commandName = "add"+ , commandHelp = addHelp ++ addHelp'+ , commandDescription = addDescription+ , commandExtraArgs = -1+ , commandExtraArgHelp = [ "<FILE or DIRECTORY> ..." ]+ , commandCommand = addCmd+ , commandPrereq = amInHashedRepository+ , commandGetArgPossibilities = listUnregisteredFiles+ , commandArgdefaults = nodefaults+ , commandAdvancedOptions = [umaskOption]+ , commandBasicOptions =+ [+ noskipBoring+ , allowProblematicFilenames+ , recursive "add contents of subdirectories"+ , workingRepoDir+ , dryRunNoxml+ ]+ }+++addCmd :: [DarcsFlag]+ -> [String]+ -> IO () addCmd opts args | null args = putStrLn $ "Nothing specified, nothing added." ++ "Maybe you wanted to say `darcs add --recursive .'?"@@ -106,9 +133,12 @@ [] -> fail "No valid arguments were given" _ -> addFiles opts fs -addFiles :: [DarcsFlag] -> [SubPath] -> IO ()-addFiles opts origfiles = withRepoLock opts $ RepoJob $ \repository ->- do -- TODO do not expand here, and use findM/findIO or such later++addFiles :: [DarcsFlag] -- ^ Command options+ -> [SubPath]+ -> IO ()+addFiles opts origfiles = withRepoLock opts $ RepoJob $ \repository -> do+ -- TODO do not expand here, and use findM/findIO or such later -- (needs adding to hashed-storage first though) cur <- expand =<< readRecordedAndPending repository let parlist = getParents cur (map toFilePath origfiles)@@ -122,18 +152,24 @@ else boringFileFilter let fixedOpts = if DryRun `elem` opts then Verbose:opts else opts mapM_ (putWarning fixedOpts . text . ((msgSkipping msgs ++ " boring file ")++)) $- flist \\ nboring flist+ flist \\ nboring flist Sealed ps <- fmap unFreeLeft $ addp msgs fixedOpts cur $ nboring flist when (nullFL ps && not (null origfiles) && not (Quiet `elem` opts)) $ fail "No files were added" unless gotDryRun $ addToPending repository ps where gotDryRun = DryRun `elem` opts- msgs | gotDryRun = dryRunMessages- | otherwise = normalMessages+ msgs+ | gotDryRun = dryRunMessages+ | otherwise = normalMessages + addp :: forall prim . (Patchy prim, PrimPatch prim, ApplyState prim ~ Tree)- => AddMessages -> [DarcsFlag] -> Tree IO -> [FilePath] -> IO (FreeLeft (FL prim))+ => AddMessages+ -> [DarcsFlag]+ -> Tree IO+ -> [FilePath]+ -> IO (FreeLeft (FL prim)) addp msgs opts cur0 files = do (ps, dups) <- foldr@@ -154,116 +190,150 @@ unless (null dups) $ do dupMsg <- case uniq_dups of- [f] ->- do+ [f] -> do isDir <- doesDirectoryReallyExist f if isDir- then return $- "The following directory "++msgIs msgs++" already in the repository"- else return $- "The following file "++msgIs msgs++" already in the repository"- fs ->- do+ then return $+ "The following directory " +++ msgIs msgs ++ " already in the repository"+ else return $+ "The following file " +++ msgIs msgs ++ " already in the repository"+ fs -> do areDirs <- mapM doesDirectoryReallyExist fs if and areDirs- then return $- "The following directories "++msgAre msgs++" already in the repository"- else- (if or areDirs- then return $- "The following files and directories " ++- msgAre msgs ++ " already in the repository"- else return $- "The following files " ++ msgAre msgs ++ " already in the repository")+ then return $+ "The following directories " +++ msgAre msgs ++ " already in the repository"+ else+ (if or areDirs+ then return $+ "The following files and directories " +++ msgAre msgs ++ " already in the repository"+ else return $+ "The following files " +++ msgAre msgs ++ " already in the repository") putWarning opts . text $ dupMsg ++ caseMsg mapM_ (putWarning opts . text) uniq_dups return $ foldr (joinGap (+>+)) (emptyGap NilFL) ps- where- addp' :: Tree IO -> FilePath -> IO (Tree IO, Maybe (FreeLeft (FL prim)), Maybe FilePath)- addp' cur f = do- already_has <- (if gotAllowCaseOnly then treeHas else treeHasAnycase) cur f- mstatus <- getFileStatus f- case (already_has, is_badfilename, mstatus) of- (True, _, _) -> return (cur, Nothing, Just f)- (_, True, _) ->- do putWarning opts . text $- "The filename " ++ f ++ " is invalid under Windows.\nUse --reserved-ok to allow it."- return add_failure- (_, _, Just s) | isDirectory s -> trypatch $ freeGap (adddir f :>: NilFL)- | isRegularFile s -> trypatch $ freeGap (addfile f :>: NilFL)- | isSymbolicLink s -> do- putWarning opts . text $ "Sorry, file " ++ f ++- " is a symbolic link, which is unsupported by darcs."- return add_failure- _ -> do putWarning opts . text $ "File "++ f ++" does not exist!"- return add_failure- where is_badfilename = not (gotAllowWindowsReserved || WindowsFilePath.isValid f)- add_failure = (cur, Nothing, Nothing)- trypatch :: FreeLeft (FL prim) -> IO (Tree IO, Maybe (FreeLeft (FL prim)), Maybe FilePath)- trypatch p = do perms <- getPermissions f- if not $ readable perms- then do- putWarning opts . text $- msgSkipping msgs ++ " '" ++ f ++ "': permission denied "- return (cur, Nothing, Nothing)- else trypatch' p- trypatch' p = do Sealed p' <- return $ unFreeLeft p- ok <- treeHasDir cur parentdir- if ok- then do- tree <- applyToTree p' cur- putVerbose opts . text $ msgAdding msgs++" '"++f++"'"- return (tree, Just p, Nothing)- else do- putWarning opts . text $ msgSkipping msgs ++ " '" ++ f ++ "' ... couldn't add parent directory '" ++ parentdir ++ "' to repository"- return (cur, Nothing, Nothing)- `catch` \e -> do- putWarning opts . text $ msgSkipping msgs ++ " '" ++ f ++ "' ... " ++ show e- return (cur, Nothing, Nothing)- parentdir = takeDirectory f- gotAllowCaseOnly = doAllowCaseOnly opts- gotAllowWindowsReserved = doAllowWindowsReserved opts+ where+ addp' :: Tree IO+ -> FilePath+ -> IO (Tree IO, Maybe (FreeLeft (FL prim)), Maybe FilePath)+ addp' cur f = do+ already_has <- (if gotAllowCaseOnly then treeHas else treeHasAnycase) cur f+ mstatus <- getFileStatus f+ case (already_has, is_badfilename, mstatus) of+ (True, _, _) -> return (cur, Nothing, Just f)+ (_, True, _) -> do+ putWarning opts . text $+ "The filename " ++ f ++ " is invalid under Windows.\n" +++ "Use --reserved-ok to allow it."+ return add_failure+ (_, _, Just s)+ | isDirectory s -> trypatch $ freeGap (adddir f :>: NilFL)+ | isRegularFile s -> trypatch $ freeGap (addfile f :>: NilFL)+ | isSymbolicLink s -> do+ putWarning opts . text $+ "Sorry, file " ++ f +++ " is a symbolic link, which is unsupported by darcs."+ return add_failure+ _ -> do+ putWarning opts . text $ "File "++ f ++" does not exist!"+ return add_failure+ where+ is_badfilename = not (gotAllowWindowsReserved || WindowsFilePath.isValid f)+ add_failure = (cur, Nothing, Nothing)+ trypatch :: FreeLeft (FL prim)+ -> IO (Tree IO, Maybe (FreeLeft (FL prim)), Maybe FilePath)+ trypatch p = do+ perms <- getPermissions f+ if not $ readable perms+ then do+ putWarning opts . text $+ msgSkipping msgs ++ " '" ++ f ++ "': permission denied "+ return (cur, Nothing, Nothing)+ else trypatch' p+ trypatch' p = do+ Sealed p' <- return $ unFreeLeft p+ ok <- treeHasDir cur parentdir+ if ok+ then do+ tree <- applyToTree p' cur+ putVerbose opts . text $+ msgAdding msgs ++ " '" ++ f ++ "'"+ return (tree, Just p, Nothing)+ else do+ putWarning opts . text $+ msgSkipping msgs ++ " '" ++ f +++ "' ... couldn't add parent directory '" +++ parentdir ++ "' to repository"+ return (cur, Nothing, Nothing)+ `catch` \e -> do+ putWarning opts . text $+ msgSkipping msgs ++ " '" ++ f ++ "' ... " ++ show e+ return (cur, Nothing, Nothing)+ parentdir = takeDirectory f+ gotAllowCaseOnly = doAllowCaseOnly opts+ gotAllowWindowsReserved = doAllowWindowsReserved opts -doesDirectoryReallyExist :: FilePath -> IO Bool-doesDirectoryReallyExist f =- maybe False isDirectory `fmap` getFileStatus f -data AddMessages =- AddMessages- { msgSkipping :: String+data AddMessages = AddMessages+ {+ msgSkipping :: String , msgAdding :: String , msgIs :: String , msgAre :: String } -normalMessages, dryRunMessages :: AddMessages-normalMessages =- AddMessages- { msgSkipping = "Skipping"++normalMessages :: AddMessages+normalMessages = AddMessages+ {+ msgSkipping = "Skipping" , msgAdding = "Adding" , msgIs = "is" , msgAre = "are" }-dryRunMessages =- AddMessages- { msgSkipping = "Would skip"+++dryRunMessages :: AddMessages+dryRunMessages = AddMessages+ {+ msgSkipping = "Would skip" , msgAdding = "Would add" , msgIs = "would be" , msgAre = "would be" } -expandDirs :: [SubPath] -> IO [SubPath]-expandDirs fs = liftM (map (fromJust . simpleSubPath)) (concat `fmap` mapM (expandOne . toPath) fs) -expandOne :: FilePath -> IO [FilePath]+doesDirectoryReallyExist :: FilePath -> IO Bool+doesDirectoryReallyExist f = maybe False isDirectory `fmap` getFileStatus f+++expandDirs :: [SubPath]+ -> IO [SubPath]+expandDirs fs =+ liftM (map (fromJust . simpleSubPath)) $+ concat `fmap` mapM (expandOne . toPath) fs+++expandOne :: FilePath+ -> IO [FilePath] expandOne "" = listFiles expandOne f = do- isdir <- doesDirectoryReallyExist f- if not isdir then return [f]- else do fs <- withCurrentDirectory f listFiles- return $ f: map (f </>) fs+ isdir <- doesDirectoryReallyExist f+ if not isdir+ then return [f]+ else do+ fs <- withCurrentDirectory f listFiles+ return $ f: map (f </>) fs -getParents :: Tree IO -> [FilePath] -> [FilePath]++getParents :: Tree IO+ -> [FilePath]+ -> [FilePath] getParents cur = map (anchorPath "") . go . map floatPath- where go fs = filter (isNothing . findTree cur) $ concatMap parents fs+ where+ go fs = filter (isNothing . findTree cur) $ concatMap parents fs
src/Darcs/Commands/AmendRecord.hs view
@@ -15,142 +15,193 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -module Darcs.Commands.AmendRecord ( amendrecord ) where+-- |+-- Module : Darcs.Commands.AmendRecord+-- Copyright : 2004, 2007 David Roundy+-- License : GPL+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable++module Darcs.Commands.AmendRecord+ (+ amendrecord+ , amendunrecord+ ) where+++#include "gadts.h"+ import Data.Maybe ( isJust, isNothing )-import System.Directory ( removeFile )-import System.Exit ( ExitCode(..), exitWith )+import Data.List ( intersect ) import Control.Applicative ( (<$>) ) import Control.Monad ( when, unless )+import System.Directory ( removeFile )+import System.Exit ( ExitCode(..), exitWith ) +import Darcs.Arguments ( DarcsFlag ( All, AmendUnrecord, Unified ),+ fixSubPaths, setEnvDarcsFiles,+ allInteractive, ignoretimes,+ askLongComment, askdeps, keepDate, author, patchnameOption,+ leaveTestDir, nocompress, lookforadds,+ workingRepoDir,+ matchOneNontag, umaskOption,+ test, listRegisteredFiles,+ getEasyAuthor, setScriptsExecutableOption, amendUnrecord+ , unified+ )+import Darcs.Commands ( DarcsCommand(..), commandAlias, nodefaults )+import Darcs.Commands.Record ( getDate, getLog, askAboutDepends )+import Darcs.Commands.Util ( announceFiles ) import Darcs.Flags ( DarcsFlag(Author, LogFile, PatchName, AskDeps, EditLongComment, PromptLongComment, KeepDate) , isInteractive , diffingOpts, compression, removeFromAmended ) import Darcs.Lock ( worldReadableTemp )-import Darcs.RepoPath ( toFilePath )+import Darcs.Patch ( RepoPatch, description, PrimOf, fromPrims,+ infopatch, getdeps, adddeps, effect, invertFL+ )+import Darcs.Patch.Apply ( ApplyState )+import Darcs.Patch.Info ( piAuthor, piName, piLog, piDateString,+ PatchInfo, patchinfo, isInverted, isTag, invertName,+ )+import Darcs.Patch.Prim ( canonizeFL )+import Darcs.Patch.Split ( primSplitter ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, n2pia, hopefully, info, patchDesc )+import Darcs.RepoPath ( toFilePath, SubPath() ) import Darcs.Repository ( Repository, withRepoLock, RepoJob(..), withGutsOf, tentativelyRemovePatches, tentativelyAddPatch, finalizeRepositoryChanges, amInHashedRepository , invalidateIndex, unrecordedChanges , testTentative+ , readRecorded ) import Darcs.Repository.Prefs ( globalPrefsDirDoc )-import Darcs.Patch ( RepoPatch, description, PrimOf, fromPrims,- infopatch, getdeps, adddeps, effect, invertFL- )-import Darcs.Patch.Apply( ApplyState )-import Darcs.Patch.Prim ( canonizeFL )-import Darcs.Patch.Info ( piAuthor, piName, piLog, piDateString,- PatchInfo, patchinfo, isInverted, isTag, invertName,- )-import Darcs.Patch.Split ( primSplitter )-import Darcs.Witnesses.Ordered ( FL(..), (:>)(..), (+>+), nullFL, reverseRL ) import Darcs.SelectChanges ( selectChanges, WhichChanges(..), selectionContextPrim, runSelection, withSelectedPatchFromRepo )-import Darcs.Commands ( DarcsCommand(..), nodefaults )-import Darcs.Commands.Record ( getDate, getLog, askAboutDepends )-import Darcs.Commands.Util ( announceFiles )-import Darcs.Arguments ( DarcsFlag ( All ),- fixSubPaths, setEnvDarcsFiles,- allInteractive, ignoretimes,- askLongComment, askdeps, keepDate, author, patchnameOption,- leaveTestDir, nocompress, lookforadds,- workingRepoDir,- matchOneNontag, umaskOption,- test, listRegisteredFiles,- getEasyAuthor, setScriptsExecutableOption, amendUnrecord- ) import Darcs.Utils ( askUser, clarifyErrors, PromptConfig(..), promptChar )-import Darcs.RepoPath ( SubPath() )+import Darcs.Witnesses.Ordered ( FL(..), (:>)(..), (+>+), nullFL, reverseRL )+ import Printer ( putDocLn ) import Storage.Hashed.Tree( Tree )-#include "gadts.h" + amendrecordDescription :: String-amendrecordDescription =- "Improve a patch before it leaves your repository."+amendrecordDescription = "Improve a patch before it leaves your repository." amendrecordHelp :: String amendrecordHelp =- "Amend-record updates a `draft' patch with additions or improvements,\n" ++- "resulting in a single `finished' patch. This is better than recording\n" ++- "the additions and improvements as separate patches, because then\n" ++- "whenever the `draft' patch is copied between repositories, you would\n" ++- "need to make sure all the extra patches are copied, too.\n" ++- "\n" ++- "Do not copy draft patches between repositories, because a finished\n" ++- "patch cannot be copied into a repository that contains a draft of the\n" ++- "same patch. If this has already happened, `darcs obliterate' can be\n" ++- "used to remove the draft patch.\n" ++- "\n" ++- -- FIXME: is the following still true in Darcs 2.1? --twb- "Do not run amend-record in repository that other developers can pull\n" ++- "from, because if they pull while an amend-record is in progress, their\n" ++- "repository may be corrupted.\n" ++- "\n" ++- "When recording a draft patch, it is a good idea to start the name with\n" ++- "`DRAFT:' so that other developers know it is not finished. When\n" ++- "finished, remove it with `darcs amend-record --edit-long-comment'.\n" ++- "To change the patch name without starting an editor, use --patch-name.\n" ++- "\n" ++- "Like `darcs record', if you call amend-record with files as arguments,\n" ++- "you will only be asked about changes to those files. So to amend a\n" ++- "patch to foo.c with improvements in bar.c, you would run:\n" ++- "\n" ++- " darcs amend-record --match 'touch foo.c' bar.c\n" ++- "\n" ++- "It is usually a bad idea to amend another developer's patch. To make\n" ++- "amend-record only ask about your own patches by default, you can add\n" ++- "something like `amend-record match David Roundy' to " ++ globalPrefsDirDoc ++ "defaults, \n" ++- "where `David Roundy' is your name.\n" + "Amend-record updates a `draft' patch with additions or improvements,\n" +++ "resulting in a single `finished' patch. This is better than recording\n" +++ "the additions and improvements as separate patches, because then\n" +++ "whenever the `draft' patch is copied between repositories, you would\n" +++ "need to make sure all the extra patches are copied, too.\n" +++ "\n" +++ "Do not copy draft patches between repositories, because a finished\n" +++ "patch cannot be copied into a repository that contains a draft of the\n" +++ "same patch. If this has already happened, `darcs obliterate' can be\n" +++ "used to remove the draft patch.\n" +++ "\n" +++ -- FIXME: is the following still true in Darcs 2.1? --twb+ "Do not run amend-record in repository that other developers can pull\n" +++ "from, because if they pull while an amend-record is in progress, their\n" +++ "repository may be corrupted.\n" +++ "\n" +++ "When recording a draft patch, it is a good idea to start the name with\n" +++ "`DRAFT:' so that other developers know it is not finished. When\n" +++ "finished, remove it with `darcs amend-record --edit-long-comment'.\n" +++ "To change the patch name without starting an editor, use --patch-name.\n" +++ "\n" +++ "Like `darcs record', if you call amend-record with files as arguments,\n" +++ "you will only be asked about changes to those files. So to amend a\n" +++ "patch to foo.c with improvements in bar.c, you would run:\n" +++ "\n" +++ " darcs amend-record --match 'touch foo.c' bar.c\n" +++ "\n" +++ "It is usually a bad idea to amend another developer's patch. To make\n" +++ "amend-record only ask about your own patches by default, you can add\n" +++ "something like `amend-record match David Roundy' to " ++ globalPrefsDirDoc +++ "defaults, \n" +++ "where `David Roundy' is your name.\n" + amendrecord :: DarcsCommand-amendrecord = DarcsCommand {commandProgramName = "darcs",- commandName = "amend-record",- commandHelp = amendrecordHelp,- commandDescription = amendrecordDescription,- commandExtraArgs = -1,- commandExtraArgHelp = ["[FILE or DIRECTORY]..."],- commandCommand = amendrecordCmd,- commandPrereq = amInHashedRepository,- commandGetArgPossibilities = listRegisteredFiles,- commandArgdefaults = nodefaults,- commandAdvancedOptions = [nocompress, ignoretimes, umaskOption,- setScriptsExecutableOption],- commandBasicOptions = [matchOneNontag,- test,- leaveTestDir,- allInteractive,- author, patchnameOption, askdeps, askLongComment, keepDate,- lookforadds,- workingRepoDir, amendUnrecord]}+amendrecord = DarcsCommand+ {+ commandProgramName = "darcs"+ , commandName = "amend-record"+ , commandHelp = amendrecordHelp+ , commandDescription = amendrecordDescription+ , commandExtraArgs = -1+ , commandExtraArgHelp = ["[FILE or DIRECTORY]..."]+ , commandCommand = amendrecordCmd+ , commandPrereq = amInHashedRepository+ , commandGetArgPossibilities = listRegisteredFiles+ , commandArgdefaults = nodefaults+ , commandAdvancedOptions =+ [+ nocompress+ , ignoretimes+ , umaskOption+ , setScriptsExecutableOption+ ]+ , commandBasicOptions =+ [+ matchOneNontag+ , test+ , leaveTestDir+ , allInteractive+ , author+ , patchnameOption+ , askdeps+ , askLongComment+ , keepDate+ , lookforadds+ , workingRepoDir+ , amendUnrecord+ , unified+ ]+ } -amendrecordCmd :: [DarcsFlag] -> [String] -> IO ()-amendrecordCmd opts args = if null args- then doAmendRecord opts Nothing- else do++amendunrecord :: DarcsCommand+amendunrecord = (commandAlias "amend-unrecord" Nothing amendrecord)+ { commandCommand = \fs -> commandCommand amendrecord (AmendUnrecord : fs)+ , commandDescription = "Alias for `darcs " ++ commandName amendrecord +++ " --unrecord '.\n" +++ "This allows changes already recorded in the patch to be removed."+ }+++amendrecordCmd :: [DarcsFlag]+ -> [String]+ -> IO ()+amendrecordCmd opts [] = doAmendRecord opts Nothing+amendrecordCmd opts args = do files <- fixSubPaths opts args if null files then fail "No valid arguments were given, nothing to do." else doAmendRecord opts $ Just files + doAmendRecord :: [DarcsFlag] -> Maybe [SubPath] -> IO () doAmendRecord opts files = withRepoLock opts $ RepoJob $ \(repository :: Repository p C(r u r)) -> do withSelectedPatchFromRepo "amend" repository opts $ \ (_ :> oldp) -> do announceFiles files "Amending changes in" -- auxiliary function needed because the witness types differ for the isTag case+ pristine <- readRecorded repository let go :: FORALL(u1) FL (PrimOf p) C(r u1) -> IO () go NilFL | not (hasEditMetadata opts) = putStrLn "No changes!" go ch = do let context = selectionContextPrim "add"- (filter (==All) opts)+ (intersect [All,Unified] opts) (Just primSplitter) (map toFilePath <$> files)+ (Just pristine) (chosenPatches :> _) <- runSelection (selectChanges First ch) context addChangesToPatch opts repository oldp chosenPatches if not (isTag (info oldp))@@ -158,9 +209,10 @@ then if removeFromAmended opts then do let sel = selectChanges Last (effect oldp) context = selectionContextPrim "unrecord"- (filter (==All) opts)+ (intersect [All,Unified] opts) (Just primSplitter) (map toFilePath <$> files)+ (Just pristine) (_ :> chosenPrims) <- runSelection sel context let invPrims = reverseRL (invertFL chosenPrims) addChangesToPatch opts repository oldp invPrims@@ -179,88 +231,102 @@ else putStrLn "You cannot add new changes to a tag, but you are allowed to edit tag's metadata (see darcs help amend-record)." go NilFL + addChangesToPatch :: forall p C(r u t x y) . (RepoPatch p, ApplyState p ~ Tree)- => [DarcsFlag] -> Repository p C(r u t) -> PatchInfoAnd p C(x t)- -> FL (PrimOf p) C(t y) -> IO ()+ => [DarcsFlag]+ -> Repository p C(r u t)+ -> PatchInfoAnd p C(x t)+ -> FL (PrimOf p) C(t y)+ -> IO () addChangesToPatch opts repository oldp chs =- if (nullFL chs && not (hasEditMetadata opts))- then putStrLn "You don't want to record anything!"- else do- invalidateIndex repository- withGutsOf repository $ do- repository' <- tentativelyRemovePatches repository (compression opts)- (oldp :>: NilFL)- (mlogf, newp) <- updatePatchHeader opts repository' oldp chs- setEnvDarcsFiles newp- repository'' <- tentativelyAddPatch repository' (compression opts) newp- let failmsg = maybe "" (\lf -> "\nLogfile left in "++lf++".") mlogf- rc <- testTentative repository- when (rc /= ExitSuccess) $ do- when (not $ isInteractive opts) $ exitWith rc `clarifyErrors` failmsg- putStrLn $ "Looks like you have a bad patch: '" ++ patchDesc newp ++ "'"- let prompt = "Shall I amend it anyway?"- yn <- promptChar (PromptConfig prompt "yn" [] (Just 'n') [])- case yn of- 'y' -> return ()- _ -> exitWith rc `clarifyErrors` failmsg- finalizeRepositoryChanges repository'' `clarifyErrors` failmsg- maybe (return ()) removeFile mlogf- putStrLn "Finished amending patch:"- putDocLn $ description newp+ if (nullFL chs && not (hasEditMetadata opts))+ then putStrLn "You don't want to record anything!"+ else do+ invalidateIndex repository+ withGutsOf repository $ do+ repository' <- tentativelyRemovePatches repository (compression opts)+ (oldp :>: NilFL)+ (mlogf, newp) <- updatePatchHeader opts repository' oldp chs+ setEnvDarcsFiles newp+ repository'' <- tentativelyAddPatch repository' (compression opts) newp+ let failmsg = maybe "" (\lf -> "\nLogfile left in "++lf++".") mlogf+ rc <- testTentative repository+ when (rc /= ExitSuccess) $ do+ when (not $ isInteractive opts) $ exitWith rc `clarifyErrors` failmsg+ putStrLn $ "Looks like you have a bad patch: '" ++ patchDesc newp ++ "'"+ let prompt = "Shall I amend it anyway?"+ yn <- promptChar (PromptConfig prompt "yn" [] (Just 'n') [])+ case yn of+ 'y' -> return ()+ _ -> exitWith rc `clarifyErrors` failmsg+ finalizeRepositoryChanges repository'' `clarifyErrors` failmsg+ maybe (return ()) removeFile mlogf+ putStrLn "Finished amending patch:"+ putDocLn $ description newp + updatePatchHeader :: forall p C(x y r u t) . (RepoPatch p, ApplyState p ~ Tree)- => [DarcsFlag] -> Repository p C(r u t)- -> PatchInfoAnd p C(t x) -> FL (PrimOf p) C(x y)+ => [DarcsFlag]+ -> Repository p C(r u t)+ -> PatchInfoAnd p C(t x)+ -> FL (PrimOf p) C(x y) -> IO (Maybe String, PatchInfoAnd p C(t y)) updatePatchHeader opts repository oldp chs = do - let newchs = canonizeFL (effect oldp +>+ chs)+ let newchs = canonizeFL (effect oldp +>+ chs) - let old_pdeps = getdeps $ hopefully oldp- newdeps <- if AskDeps `elem` opts- then askAboutDepends repository newchs opts old_pdeps- else return old_pdeps+ let old_pdeps = getdeps $ hopefully oldp+ newdeps <- if AskDeps `elem` opts+ then askAboutDepends repository newchs opts old_pdeps+ else return old_pdeps - let old_pinf = info oldp- prior = (piName old_pinf, piLog old_pinf)- make_log = worldReadableTemp "darcs-amend-record"- old_author = piAuthor old_pinf- date <- if KeepDate `elem` opts then return (piDateString old_pinf) else getDate opts- warnIfHijacking opts old_author- (new_name, new_log, mlogf) <- getLog opts (Just prior) make_log chs- let new_author = case getAuthor opts of- Just a -> a- Nothing -> piAuthor old_pinf- maybe_invert = if isInverted old_pinf then invertName else id- new_pinf <- maybe_invert `fmap` patchinfo date new_name- new_author new_log+ let old_pinf = info oldp+ prior = (piName old_pinf, piLog old_pinf)+ make_log = worldReadableTemp "darcs-amend-record"+ old_author = piAuthor old_pinf+ date <- if KeepDate `elem` opts then return (piDateString old_pinf) else getDate opts+ warnIfHijacking opts old_author+ (new_name, new_log, mlogf) <- getLog opts (Just prior) make_log chs+ let new_author = case getAuthor opts of+ Just a -> a+ Nothing -> piAuthor old_pinf+ maybe_invert = if isInverted old_pinf then invertName else id+ new_pinf <- maybe_invert `fmap` patchinfo date new_name+ new_author new_log - let newp = n2pia (adddeps (infopatch new_pinf (fromPrims newchs)) newdeps)+ let newp = n2pia (adddeps (infopatch new_pinf (fromPrims newchs)) newdeps) - return (mlogf, newp)+ return (mlogf, newp) -warnIfHijacking :: [DarcsFlag] -> String -> IO ()++warnIfHijacking :: [DarcsFlag]+ -> String+ -> IO () warnIfHijacking opts old_author = do- authors_here <- getEasyAuthor- let edit_author = isJust (getAuthor opts)- unless (edit_author || any (== old_author) authors_here) $- do yorn <- askUser $- "You're not "++old_author ++"! Amend anyway? "- case yorn of ('y':_) -> return ()- _ -> exitWith ExitSuccess+ authors_here <- getEasyAuthor+ let edit_author = isJust (getAuthor opts)+ unless (edit_author || any (== old_author) authors_here) $ do+ yorn <- askUser $+ "You're not " ++ old_author ++"! Amend anyway? "+ case yorn of ('y':_) -> return ()+ _ -> exitWith ExitSuccess -hasEditMetadata :: [DarcsFlag] -> Bool-hasEditMetadata (Author _:_) = True-hasEditMetadata (LogFile _:_) = True-hasEditMetadata (PatchName _:_) = True-hasEditMetadata (EditLongComment:_) = True++hasEditMetadata :: [DarcsFlag]+ -> Bool+hasEditMetadata [] = False+hasEditMetadata (Author _:_) = True+hasEditMetadata (LogFile _:_) = True+hasEditMetadata (PatchName _:_) = True+hasEditMetadata (EditLongComment:_) = True hasEditMetadata (PromptLongComment:_) = True-hasEditMetadata (AskDeps:_) = True-hasEditMetadata (_:fs) = hasEditMetadata fs-hasEditMetadata [] = False+hasEditMetadata (AskDeps:_) = True+hasEditMetadata (_:fs) = hasEditMetadata fs -getAuthor :: [DarcsFlag] -> Maybe String-getAuthor (Author a:_) = Just a-getAuthor (_:as) = getAuthor as-getAuthor [] = Nothing++getAuthor :: [DarcsFlag]+ -> Maybe String+getAuthor (Author a:_) = Just a+getAuthor (_:as) = getAuthor as+getAuthor [] = Nothing
src/Darcs/Commands/Annotate.hs view
@@ -40,7 +40,7 @@ import qualified Data.ByteString.Char8 as BC ( pack, concat, intercalate ) import Data.ByteString.Lazy ( toChunks ) import Darcs.PrintPatch ( printPatch, contextualPrintPatch )-import Darcs.Patch.ApplyMonad( withFilePaths )+import Darcs.Patch.ApplyMonad( withFileNames ) import Darcs.Patch.FileName( fp2fn ) import System.FilePath( (</>) ) import Darcs.RepoPath( toFilePath )@@ -128,8 +128,9 @@ (Sealed patches, initial, path) <- if haveNonrangeMatch opts then do Sealed x <- getOnePatchset repository opts- [path] <- return $ withFilePaths [fp2fn $ toFilePath origpath]- (getNonrangeMatchS opts r)+ let fn = [fp2fn $ toFilePath origpath]+ nonRangeMatch = getNonrangeMatchS opts r+ (_, [path], _) = withFileNames Nothing fn nonRangeMatch initial <- snd `fmap` virtualTreeIO (getNonrangeMatchS opts r) recorded return $ (seal $ newset2RL x, initial, toFilePath path) else return $ (seal $ newset2RL r, recorded, toFilePath origpath)
src/Darcs/Commands/Apply.hs view
@@ -29,21 +29,35 @@ import Darcs.SignalHandler ( withSignalsBlocked ) import Darcs.Commands ( DarcsCommand(..), putVerbose ) import Darcs.CommandsAux ( checkPaths )-import Darcs.Arguments ( DarcsFlag( Reply, Interactive, All),- matchSeveral,- setEnvDarcsPatches,- getCc, workingRepoDir,- notest, nocompress, applyConflictOptions,- useExternalMerge,- ignoretimes, getSendmailCmd,- reply, verify, listFiles,- fixFilePathOrStd, umaskOption,- allInteractive, sendmailCmd,- leaveTestDir, happyForwarding,- dryRun, printDryRunMessageAndExit,- setScriptsExecutableOption, restrictPaths,- changesReverse, makeScriptsExecutable- )+import Darcs.Arguments+ ( DarcsFlag( All, Interactive, Reply )+ , allInteractive+ , applyConflictOptions+ , changesReverse+ , dryRun+ , fixFilePathOrStd+ , getCc+ , getSendmailCmd+ , happyForwarding+ , ignoretimes+ , leaveTestDir+ , listFiles+ , makeScriptsExecutable+ , matchSeveral+ , nocompress+ , notest+ , pauseForGui+ , printDryRunMessageAndExit+ , reply+ , restrictPaths+ , sendmailCmd+ , setEnvDarcsPatches+ , setScriptsExecutableOption+ , umaskOption+ , useExternalMerge+ , verify+ , workingRepoDir+ ) import Darcs.Flags(doHappyForwarding, doReverse, isInteractive) import qualified Darcs.Arguments as DarcsArguments ( ccApply )@@ -133,12 +147,19 @@ commandPrereq = amInHashedRepository, commandGetArgPossibilities = listFiles, commandArgdefaults = const stdindefault,- commandAdvancedOptions = [reply, DarcsArguments.ccApply,- happyForwarding,- sendmailCmd,- ignoretimes, nocompress,- setScriptsExecutableOption, umaskOption,- restrictPaths, changesReverse],+ commandAdvancedOptions =+ [ reply+ , DarcsArguments.ccApply+ , happyForwarding+ , sendmailCmd+ , ignoretimes+ , nocompress+ , setScriptsExecutableOption+ , umaskOption+ , restrictPaths+ , changesReverse+ , pauseForGui+ ], commandBasicOptions = [verify, allInteractive]++dryRun++ [matchSeveral,@@ -264,6 +285,13 @@ "A patch bundle may introduce unresolved conflicts with existing\n" ++ "patches or with the working tree. By default, Darcs will add conflict\n" ++ "markers (see `darcs mark-conflicts').\n" +++ "\n" +++ "The --external-merge option lets you resolve these conflicts\n" +++ "using an external merge tool. In the option, '%a' is replaced with\n" +++ "the common ancestor (merge base), '%1' with the first version, '%2'\n" +++ "with the second version, and '%o' with the path where your resolved\n" +++ "content should go. For example, to use the xxdiff visual merge tool\n" +++ "you'd specify: --external-merge='xxdiff -m -O -M %o %1 %a %2'\n" ++ "\n" ++ "The --allow-conflicts option will skip conflict marking; this is\n" ++ "useful when you want to treat a repository as just a bunch of patches,\n" ++
src/Darcs/Commands/Changes.hs view
@@ -23,9 +23,10 @@ import Prelude hiding ( log ) import Unsafe.Coerce (unsafeCoerce) -import Data.List ( intersect, sort, nub )+import Data.List ( intersect, sort, nub, find ) import Data.Maybe ( fromMaybe, fromJust, isJust )-import Control.Monad ( when, unless )+import Control.Arrow ( second )+import Control.Monad.State.Strict import Control.Applicative ((<$>)) import Darcs.Patch.PatchInfoAnd ( hopefullyM, info )@@ -55,8 +56,8 @@ import Darcs.Patch.Bundle( contextPatches ) import Darcs.Patch.TouchesFiles ( lookTouch ) import Darcs.Patch.Apply( ApplyState )-import Darcs.Patch ( RepoPatch, invert, xmlSummary, description, applyToFilepaths,- listTouchedFiles, effect )+import Darcs.Patch ( RepoPatch, invert, xmlSummary, description,+ effectOnFilePaths, listTouchedFiles, effect ) import Darcs.Witnesses.Eq ( EqCheck(..) ) import Darcs.Witnesses.Ordered ( FL(NilFL), RL(..), filterFLFL, filterRL, reverseFL, (:>)(..), mapRL )@@ -64,9 +65,8 @@ matchAPatchread, haveNonrangeMatch, matchFirstPatchset, matchSecondPatchset, )-import Printer ( Doc, putDocLnWith, simplePrinters, (<+>),- renderString, prefix, text, vcat, vsep, (<>),- ($$), empty, errorDoc, insertBeforeLastline )+import Printer ( Doc, putDocLnWith, simplePrinters, (<+>), prefix, text, vcat,+ vsep, (<>), ($$), errorDoc, insertBeforeLastline ) import Darcs.ColorPrinter ( fancyPrinters ) import Progress ( setProgressMode, debugMessage ) import Darcs.SelectChanges ( viewChanges )@@ -127,8 +127,10 @@ Nothing -> return $ Sealed NilFL Just _ -> Sealed `fmap` unrecordedChanges (UseIndex, ScanKnown) repository files `catch` \_ -> return (Sealed NilFL) -- this is triggered when repository is remote- let filez = map (fn2fp . normPath . fp2fn) . applyToFilepaths (invert unrec) . map toFilePath <$> files- filtered_changes p = maybe_reverse $ getChangesInfo opts filez p+ let normfp = fn2fp . normPath . fp2fn+ undoUnrecordedOnFPs = effectOnFilePaths (invert unrec)+ recFiles = map normfp . undoUnrecordedOnFPs . map toFilePath <$> files+ filtered_changes p = maybe_reverse $ getChangesInfo opts recFiles p debugMessage "About to read the repository..." patches <- readRepo repository debugMessage "Done reading the repository."@@ -137,7 +139,7 @@ fp = map fst fp_and_fs viewChanges opts fp else do when (isJust files && not (XMLOutput `elem` opts)) $- putStrLn $ "Changes to "++unwords (fromJust filez)++":\n"+ putStrLn $ "Changes to "++unwords (fromJust recFiles)++":\n" debugMessage "About to print the changes..." let printers = if XMLOutput `elem` opts then simplePrinters else fancyPrinters ps <- readRepo repository -- read repo again to prevent holding onto@@ -169,7 +171,9 @@ getChangesInfo :: (RepoPatch p, ApplyState p ~ Tree) => [DarcsFlag] -> Maybe [FilePath] -> PatchSet p C(x y)- -> ([(Sealed2 (PatchInfoAnd p), [FilePath])], [FilePath], Doc)+ -> ( [(Sealed2 (PatchInfoAnd p), [FilePath])]+ , [(FilePath, FilePath)]+ , Maybe Doc ) getChangesInfo opts plain_fs ps = case (sp1s, sp2s) of (Sealed p1s, Sealed p2s) ->@@ -177,7 +181,7 @@ _ :> us -> let ps' = filterRL pf (reverseFL us) in case plain_fs of- Nothing -> foldr (\x xs -> (x, []) -:- xs) ([], [], empty) $+ Nothing -> foldr (\x xs -> (x, []) -:- xs) ([], [], Nothing) $ maybe id take (maxCount opts) ps' Just fs -> let fs' = map (\x -> "./" ++ x) fs in filterPatchesByNames (maxCount opts) fs' ps'@@ -191,31 +195,50 @@ then matchAPatchread opts else \_ -> True --- | Take a list of filenames and patches and produce a list of--- patches that actually touch the given files with list of touched--- file names, a new file list that represents the same set of files--- as in input, before the returned patches would have been applied,--- and possibly an error. Additionaly, the function takes a "depth--- limit" -- maxcount, that could be Nothing (return everything) or--- "Just n" -- returns at most n patches touching the file (starting--- from the beginning of the patch list).+-- | Take a list of filenames and patches and produce a list of patches that+-- actually touch the given files with a list of touched file names, a list of+-- original-to-current filepath mappings, indicating the original names of the+-- affected files and possibly an error. Additionaly, the function takes a+-- "depth limit" -- maxcount, that could be Nothing (return everything) or+-- "Just n" -- returns at most n patches touching the file (starting from the+-- beginning of the patch list). filterPatchesByNames :: (RepoPatch p, ApplyState p ~ Tree) => Maybe Int -- ^ maxcount -> [FilePath] -- ^ filenames -> [Sealed2 (PatchInfoAnd p)] -- ^ patchlist- -> ([(Sealed2 (PatchInfoAnd p),[FilePath])], [FilePath], Doc)-filterPatchesByNames (Just 0) _ _ = ([], [], empty)-filterPatchesByNames _ [] _ = ([], [], empty)-filterPatchesByNames _ _ [] = ([], [], empty)-filterPatchesByNames maxcount fs ((Sealed2 hp):ps)- | Just p <- hopefullyM hp =- case lookTouch fs (invert p) of- (True, []) -> ([(Sealed2 hp, fs)], fs, empty)- (True, fs') -> (Sealed2 hp, fs) -:- filterPatchesByNames- (subtract 1 `fmap` maxcount) fs' ps- (False, fs') -> filterPatchesByNames maxcount fs' ps-filterPatchesByNames _ _ ((Sealed2 hp):_) =- ([], [], text "Can't find changes prior to:" $$ description hp)+ -> ([(Sealed2 (PatchInfoAnd p),[FilePath])], [(FilePath, FilePath)], Maybe Doc)+filterPatchesByNames maxcount fns patches = removeNonRenames $+ evalState (filterPatchesByNames' fns patches) (maxcount, initRenames) where+ removeNonRenames (ps, renames, doc) = (ps, removeIds renames, doc)+ removeIds = filter (\(a, b) -> a /= b)+ initRenames = map (\x -> (x, x)) fns+ returnFinal = (\renames -> ([], renames, Nothing)) <$> gets snd+ filterPatchesByNames' [] _ = returnFinal+ filterPatchesByNames' _ [] = returnFinal+ filterPatchesByNames' fs (s2hp@(Sealed2 hp) : ps) = do+ (count, renames) <- get+ let stopNow = case count of+ Nothing -> False+ Just c -> c <= 0+ if stopNow+ then returnFinal+ else case hopefullyM hp of+ Nothing -> do+ let err = text "Can't find changes prior to:"+ $$ description hp+ return ([], renames, Just err)+ Just p ->+ case lookTouch (Just renames) fs (invert p) of+ (True, affected, [], renames') -> do+ return ([(s2hp, affected)], renames', Nothing)+ (True, affected, fs', renames') -> do+ let sub1Mb c = (subtract 1) <$> c+ modify $ \(c, _) -> (sub1Mb c, renames')+ rest <- filterPatchesByNames' fs' ps+ return $ (s2hp, affected) -:- rest+ (False, _, fs', renames') -> do+ modify $ second (const renames')+ filterPatchesByNames' fs' ps -- | Note, lazy pattern matching is required to make functions like -- filterPatchesByNames lazy in case you are only not interested in@@ -228,25 +251,22 @@ changelog :: forall p C(start x) . (RepoPatch p, ApplyState p ~ Tree) => [DarcsFlag] -> PatchSet p C(start x)- -> ([(Sealed2 (PatchInfoAnd p), [FilePath])], [FilePath], Doc)+ -> ([(Sealed2 (PatchInfoAnd p), [FilePath])], [(FilePath, FilePath)], Maybe Doc) -> Doc-changelog opts patchset (pis_and_fs, orig_fs, errstring)+changelog opts patchset (pis_and_fs, createdAsFs, mbErr) | Count `elem` opts = text $ show $ length pis_and_fs | MachineReadable `elem` opts =- if renderString errstring == ""- then vsep $ map (unseal2 (showPatchInfo.info)) pis- else errorDoc errstring+ maybe (vsep $ map (unseal2 (showPatchInfo.info)) pis) errorDoc mbErr | XMLOutput `elem` opts = text "<changelog>"- $$ vcat xml_file_names+ $$ vcat created_as_xml $$ vcat actual_xml_changes $$ text "</changelog>" | Summary `elem` opts || Verbose `elem` opts =- vsep (map (number_patch change_with_summary) pis_and_fs)- $$ errstring- | otherwise = vsep (map (number_patch description') pis_and_fs)- $$ errstring- where change_with_summary :: (Sealed2 (PatchInfoAnd p), [FilePath]) -> Doc+ mbAppendErr $ vsep (map (number_patch change_with_summary) pis_and_fs)+ | otherwise = mbAppendErr $ vsep (map (number_patch description') pis_and_fs)+ where mbAppendErr = maybe id (\err -> ($$ err)) mbErr+ change_with_summary :: (Sealed2 (PatchInfoAnd p), [FilePath]) -> Doc change_with_summary (Sealed2 hp, fs) | Just p <- hopefullyM hp = if showChangesOnlyToFiles opts then description hp $$ text "" $$@@ -268,10 +288,20 @@ actual_xml_changes = if Summary `elem` opts then map xml_with_summary pis else map (toXml . (unseal2 info)) pis- xml_file_names = map (createdAsXml first_change) orig_fs- first_change = if doReverse opts- then unseal2 info $ head pis- else unseal2 info $ last pis++ created_as_xml = map create createdAsFs where+ create rename@(_, as) = createdAsXml (first_change_of as) rename+ -- We need to reorder the patches when they haven't been reversed+ -- already, so that we find the *first* patch that modifies a given+ -- file, not the last (by default, the list is oldest->newest).+ reorderer = if not (doReverse opts) then reverse else id+ oldest_first_pis_and_fs = reorderer pis_and_fs+ couldnt_find fn = error $ "Couldn't find first patch affecting " +++ fn ++ " in pis_and_fs"+ mb_first_change_of fn = find ((fn `elem`) . snd) oldest_first_pis_and_fs+ find_first_change_of fn = fromMaybe (couldnt_find fn)+ (mb_first_change_of fn)+ first_change_of = unseal2 info . fst . find_first_change_of number_patch f x = if NumberPatches `elem` opts then case get_number (fst x) of Just n -> text (show n++":") <+> f x@@ -312,7 +342,7 @@ (_ :> ps') <- contextPatches `fmap` readRepo repository let ps = mapRL (\p -> (seal2 p, [])) ps' unless fancy $ putStrLn "\nContext:\n"- putDocLnWith simplePrinters $ changelog opts' emptyset (ps, [], empty)+ putDocLnWith simplePrinters $ changelog opts' emptyset (ps, [], Nothing) where opts' = if fancy then opts else MachineReadable : opts fancy = HumanReadable `elem` opts || XMLOutput `elem` opts emptyset = PatchSet NilRL NilRL@@ -320,9 +350,12 @@ log :: DarcsCommand log = commandAlias "log" Nothing changes -createdAsXml :: PatchInfo -> String -> Doc-createdAsXml pinfo as = text "<created_as original_name='"- <> escapeXML as- <> text "'>"- $$ toXml pinfo- $$ text "</created_as>"+createdAsXml :: PatchInfo -> (String, String) -> Doc+createdAsXml pinfo (current, createdAs) =+ text "<created_as current_name='"+ <> escapeXML current+ <> text "' original_name='"+ <> escapeXML createdAs+ <> text "'>"+ $$ toXml pinfo+ $$ text "</created_as>"
src/Darcs/Commands/Diff.hs view
@@ -21,7 +21,7 @@ import System.FilePath.Posix ( takeFileName ) import Workaround ( getCurrentDirectory )-import Darcs.Utils ( askUser, withCurrentDirectory )+import Darcs.Utils ( askEnter, withCurrentDirectory ) import Control.Monad ( when ) import Data.List ( (\\) ) @@ -30,13 +30,18 @@ import Darcs.External( diffProgram ) import CommandLine ( parseCmd ) import Darcs.Commands ( DarcsCommand(..), nodefaults )-import Darcs.Arguments ( DarcsFlag(DiffFlags, DiffCmd,- LastN, AfterPatch),- matchRange, storeInMemory,- diffCmdFlag, diffflags, unidiff,- workingRepoDir, fixSubPaths,- )-import Darcs.Flags ( isNotUnified )+import Darcs.Arguments+ ( DarcsFlag(AfterPatch, DiffCmd, DiffFlags, LastN)+ , diffCmdFlag+ , diffflags+ , fixSubPaths+ , matchRange+ , pauseForGui+ , storeInMemory+ , unidiff+ , workingRepoDir+ )+import Darcs.Flags ( isNotUnified, wantGuiPause ) import Darcs.Patch.PatchInfoAnd ( info ) import Darcs.RepoPath ( AbsolutePath, SubPath, toFilePath, sp2fn ) import Darcs.Match ( getPartialFirstMatch, getPartialSecondMatch,@@ -70,6 +75,7 @@ "the `darcs whatsnew' command, since it outputs a line-by-line diff,\n" ++ "and it is also slower. As with `darcs whatsnew', if you specify\n" ++ "files or directories, changes to other files are not listed.\n" +++ "The command always uses an external diff utility.\n" ++ "\n" ++ "With the --patch option, the comparison will be made between working\n" ++ "trees with and without that patch. Patches `after' the selected patch\n" ++@@ -105,11 +111,18 @@ commandPrereq = amInHashedRepository, commandGetArgPossibilities = return [], commandArgdefaults = nodefaults,- commandAdvancedOptions = [],- commandBasicOptions = [matchRange,- diffCmdFlag,- diffflags, unidiff,- workingRepoDir, storeInMemory]}+ commandAdvancedOptions =+ [ pauseForGui+ ],+ commandBasicOptions =+ [ matchRange+ , diffCmdFlag+ , diffflags+ , unidiff+ , workingRepoDir+ , storeInMemory+ ]+ } getDiffOpts :: [DarcsFlag] -> [String] getDiffOpts opts | isNotUnified opts = get_nonU_diff_opts opts@@ -118,11 +131,6 @@ get_nonU_diff_opts (_:fs) = get_nonU_diff_opts fs get_nonU_diff_opts [] = [] -hasDiffCmdFlag :: [DarcsFlag] -> Bool-hasDiffCmdFlag (DiffCmd _:_) = True-hasDiffCmdFlag (_:t) = hasDiffCmdFlag t-hasDiffCmdFlag [] = False- -- | Returns the command we should use for diff as a tuple (command, arguments). -- This will either be whatever the user specified via --diff-command or the -- default 'diffProgram'. Note that this potentially involves parsing the@@ -180,14 +188,12 @@ case getDiffCmdAndArgs cmd opts f1 f2 of Left err -> fail err Right (d_cmd, d_args) ->- let other_diff = hasDiffCmdFlag opts in- do when other_diff $ putStrLn $+ let pausingForGui = wantGuiPause opts in+ do when pausingForGui $ putStrLn $ "Running command '" ++ unwords (d_cmd:d_args) ++ "'" output <- execPipeIgnoreError d_cmd d_args empty- when other_diff $ do--- Warning: A do-notation statement discarded a result of type String.- _ <- askUser "Hit return to move on..."- return ()+ when pausingForGui $+ askEnter "Hit return to move on..." return output withTempDirs :: String -> (AbsolutePath -> AbsolutePath -> IO a) -> IO a
src/Darcs/Commands/Dist.hs view
@@ -15,98 +15,126 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -module Darcs.Commands.Dist ( dist ) where+-- |+-- Module : Darcs.Commands.Dist+-- Copyright : 2003 David Roundy+-- License : GPL+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable++module Darcs.Commands.Dist+ (+ dist+ ) where++import Prelude hiding ( writeFile )++import Data.ByteString.Lazy ( writeFile )+import Data.Char ( isAlphaNum )+import Control.Monad ( when ) import System.Directory ( setCurrentDirectory )-import Workaround ( getCurrentDirectory )-import System.Exit ( ExitCode(..), exitWith ) import System.Cmd ( system )+import System.Exit ( ExitCode(..), exitWith ) import System.FilePath.Posix ( takeFileName, (</>) )-import Data.Char ( isAlphaNum )-import Control.Monad ( when )++import Workaround ( getCurrentDirectory ) import Codec.Archive.Tar ( pack, write ) import Codec.Archive.Tar.Entry ( entryPath ) import Codec.Compression.GZip ( compress )-import Prelude hiding ( writeFile )-import Data.ByteString.Lazy ( writeFile ) -import Darcs.Commands ( DarcsCommand(..),- nodefaults ) import Darcs.Arguments ( DarcsFlag(Verbose, Quiet, DistName, SetScriptsExecutable), distnameOption, workingRepoDir, matchOne, storeInMemory, setScriptsExecutableOption )-import Darcs.Match ( getNonrangeMatch, haveNonrangeMatch )-import Darcs.Repository ( amInHashedRepository, withRepoReadLock, RepoJob(..), --withRecorded,+import Darcs.Commands ( DarcsCommand(..), nodefaults )+import Darcs.Lock ( withTempDir )+import Darcs.Match ( getNonrangeMatch, haveNonrangeMatch, firstMatch, getFirstMatch )+import Darcs.Repository ( amInHashedRepository, withRepoReadLock, RepoJob(..), setScriptsExecutable, createPartialsPristineDirectoryTree ) import Darcs.Repository.Prefs ( getPrefval )-import Darcs.Lock ( withTempDir ) import Darcs.RepoPath ( AbsolutePath, toFilePath ) import Darcs.Utils ( withCurrentDirectory ) + distDescription :: String distDescription = "Create a distribution tarball." + distHelp :: String distHelp =- "The `darcs dist' command creates a compressed archive (a `tarball') in\n" ++- "the repository's root directory, containing the recorded state of the\n" ++- "working tree (unrecorded changes and the _darcs directory are\n" ++- "excluded).\n" ++- "\n" ++- "If a predist command is set (see `darcs setpref'), that command will\n" ++- "be run on the tarball contents prior to archiving. For example,\n" ++- "autotools projects would set it to `autoconf && automake'.\n" ++- "\n" ++- "By default, the tarball (and the top-level directory within the\n" ++- "tarball) has the same name as the repository, but this can be\n" ++- "overridden with the --dist-name option.\n"+ "The `darcs dist' command creates a compressed archive (a `tarball') in\n" +++ "the repository's root directory, containing the recorded state of the\n" +++ "working tree (unrecorded changes and the _darcs directory are\n" +++ "excluded).\n" +++ "\n" +++ "If a predist command is set (see `darcs setpref'), that command will\n" +++ "be run on the tarball contents prior to archiving. For example,\n" +++ "autotools projects would set it to `autoconf && automake'.\n" +++ "\n" +++ "By default, the tarball (and the top-level directory within the\n" +++ "tarball) has the same name as the repository, but this can be\n" +++ "overridden with the --dist-name option.\n" - -- FIXME: this is tedious and ugly.- {-- ++ "\n" ++- "Suppose you use a version numbering scheme `major.minor.patch', and\n" ++- "you tag each release `major.minor'. You can then calculate the\n" ++- "version number by taking the newest tag and appending a dot and the\n" ++- "number of patches since that tag. If you use the directory name as\n" ++- "the project name, you can make tarballs of the form name-version.tgz\n" ++- "using the following shell script:\n" ++- "\n" ++- " major_minor=$(darcs show tags | head -1) &&\n" ++- " patch_level=$(($(darcs changes --count --from-tag .) - 1)) &&\n" ++- " version=$major_minor.$patch_level &&\n" ++- " project=${PWD##*/} &&\n" ++- " darcs dist --dist-name \"$project\"-\"$version\".tar.gz\n"- -}+ -- FIXME: this is tedious and ugly.+ {-+ ++ "\n" +++ "Suppose you use a version numbering scheme `major.minor.patch', and\n" +++ "you tag each release `major.minor'. You can then calculate the\n" +++ "version number by taking the newest tag and appending a dot and the\n" +++ "number of patches since that tag. If you use the directory name as\n" +++ "the project name, you can make tarballs of the form name-version.tgz\n" +++ "using the following shell script:\n" +++ "\n" +++ " major_minor=$(darcs show tags | head -1) &&\n" +++ " patch_level=$(($(darcs changes --count --from-tag .) - 1)) &&\n" +++ " version=$major_minor.$patch_level &&\n" +++ " project=${PWD##*/} &&\n" +++ " darcs dist --dist-name \"$project\"-\"$version\".tar.gz\n"+ -} + dist :: DarcsCommand-dist = DarcsCommand {commandProgramName = "darcs",- commandName = "dist",- commandHelp = distHelp,- commandDescription = distDescription,- commandExtraArgs = 0,- commandExtraArgHelp = [],- commandCommand = distCmd,- commandPrereq = amInHashedRepository,- commandGetArgPossibilities = return [],- commandArgdefaults = nodefaults,- commandAdvancedOptions = [],- commandBasicOptions = [distnameOption,- workingRepoDir,- matchOne,- setScriptsExecutableOption,- storeInMemory]}+dist = DarcsCommand+ {+ commandProgramName = "darcs"+ , commandName = "dist"+ , commandHelp = distHelp+ , commandDescription = distDescription+ , commandExtraArgs = 0+ , commandExtraArgHelp = []+ , commandCommand = distCmd+ , commandPrereq = amInHashedRepository+ , commandGetArgPossibilities = return []+ , commandArgdefaults = nodefaults+ , commandAdvancedOptions = []+ , commandBasicOptions =+ [+ distnameOption+ , workingRepoDir+ , matchOne+ , setScriptsExecutableOption+ , storeInMemory+ ]+ } -distCmd :: [DarcsFlag] -> [String] -> IO ()++distCmd :: [DarcsFlag]+ -> [String]+ -> IO () distCmd opts _ = withRepoReadLock opts $ RepoJob $ \repository -> do- distname <- getDistName opts- predist <- getPrefval "predist" formerdir <- getCurrentDirectory+ let distname = getDistName formerdir [x | DistName x <- opts]+ predist <- getPrefval "predist" let resultfile = formerdir </> distname ++ ".tar.gz" withTempDir "darcsdist" $ \tempdir -> do setCurrentDirectory formerdir withTempDir (toFilePath tempdir </> takeFileName distname) $ \ddir -> do if haveNonrangeMatch opts- then withCurrentDirectory ddir $ getNonrangeMatch repository opts+ then+ if firstMatch opts+ then withCurrentDirectory ddir $ getFirstMatch repository opts+ else withCurrentDirectory ddir $ getNonrangeMatch repository opts else createPartialsPristineDirectoryTree repository [""] (toFilePath ddir) ec <- case predist of Nothing -> return ExitSuccess Just pd -> system pd@@ -121,30 +149,24 @@ putStrLn "Dist aborted due to predist failure" exitWith ec + -- | This function performs the actual distribution action itself. -- NB - it does /not/ perform the pre-dist, that should already -- have completed successfully before this is invoked. doDist :: [DarcsFlag] -> AbsolutePath -> AbsolutePath -> FilePath -> IO () doDist opts tempdir ddir resultfile = do- setCurrentDirectory (toFilePath tempdir)- let safeddir = safename $ takeFileName $ toFilePath ddir- entries <- pack "." [safeddir]- when (Verbose `elem` opts) $ putStr $ unlines $ map entryPath entries- writeFile resultfile $ compress $ write entries- when (Quiet `notElem` opts) $ putStrLn $ "Created dist as " ++ resultfile+ setCurrentDirectory (toFilePath tempdir)+ let safeddir = safename $ takeFileName $ toFilePath ddir+ entries <- pack "." [safeddir]+ when (Verbose `elem` opts) $ putStr $ unlines $ map entryPath entries+ writeFile resultfile $ compress $ write entries+ when (Quiet `notElem` opts) $ putStrLn $ "Created dist as " ++ resultfile where safename n@(c:_) | isAlphaNum c = n safename n = "./" ++ n -guessRepoName :: IO String-guessRepoName = do- pwd <- getCurrentDirectory- if '/' `elem` pwd- then return $ reverse $ takeWhile (/='/') $ reverse pwd- else return "cantguessreponame" -getDistName :: [DarcsFlag] -> IO String-getDistName (DistName dn:_) = return dn-getDistName (_:fs) = getDistName fs-getDistName _ = guessRepoName+getDistName :: FilePath -> [String] -> FilePath+getDistName _ (dn:_) = dn+getDistName currentDirectory _ = takeFileName currentDirectory
src/Darcs/Commands/Help.hs view
@@ -32,8 +32,7 @@ import Darcs.Ssh ( environmentHelpSsh, environmentHelpScp, environmentHelpSshPort ) import Darcs.Utils ( withCurrentDirectory, environmentHelpEditor, environmentHelpPager ) import Data.Char ( isAlphaNum, toLower, toUpper )-import Data.List ( groupBy, isPrefixOf, partition, intercalate, nub )-import Data.Maybe ( isNothing, mapMaybe )+import Data.List ( groupBy, isPrefixOf, intercalate, nub ) import English ( andClauses ) import Printer (text, vcat, vsep, ($$)) import System.Exit ( ExitCode(..), exitWith )
src/Darcs/Commands/Move.hs view
@@ -33,7 +33,7 @@ import System.FilePath.Posix ( (</>), takeFileName ) import System.Directory ( renameDirectory ) import Workaround ( renameFile )-import Darcs.Repository.State ( readRecordedAndPending )+import Darcs.Repository.State ( readRecordedAndPending, readRecorded ) import Darcs.Repository ( Repository, withRepoLock, RepoJob(..), amInHashedRepository, addToPending ) import Darcs.Witnesses.Ordered ( FL(..), toFL ) import Darcs.Witnesses.Sealed ( Sealed(..), unseal, freeGap, FreeLeft, unFreeLeft )@@ -109,17 +109,71 @@ [] -> fail "Nothing to move." froms' -> moveFilesToDir opts froms' to' +data FileKind = Dir | File+ deriving (Show, Eq)++data FileStatus =+ Nonexistant+ | Unadded FileKind+ | Shadow FileKind -- ^ known to darcs, but absent in working copy+ | Known FileKind+ deriving Show++fileStatus :: Tree IO -> Tree IO -> Tree IO ->+ FilePath -> IO FileStatus+fileStatus work cur rec fp = do+ exists_in_cur <- treeHas cur fp+ exists_in_rec <- treeHas rec fp+ exists_in_work <- treeHas work fp+ case (exists_in_rec, exists_in_cur, exists_in_work) of+ (_, True, True) -> do+ isDirCur <- treeHasDir cur fp+ isDirWork <- treeHasDir work fp+ unless (isDirCur == isDirWork) . fail $ "don't know what to do with " ++ fp+ return . Known $ if isDirCur then Dir else File++ (_, False, True) -> do+ isDir <- treeHasDir work fp+ if isDir+ then return $ Unadded Dir+ else return $ Unadded File+ (False, False, False) -> return Nonexistant+ (_, _, False) -> do+ isDir <- treeHasDir cur fp+ if isDir+ then return $ Shadow Dir+ else return $ Shadow File++-- | Takes two filenames (as 'Subpath'), and tries to move the first+-- into/onto the second. Needs to guess what that means: renaming or moving+-- into a directory, and whether it is a post-hoc move. moveFile :: [DarcsFlag] -> SubPath -> SubPath -> IO () moveFile opts old new = withRepoLock opts $ RepoJob $ \repository -> do work <- readPlainTree "." cur <- readRecordedAndPending repository+ rec <- readRecorded repository let old_fp = toFilePath old new_fp = toFilePath new- new_is_a_dir <- treeHasDir work new_fp- old_is_a_dir <- treeHasDir cur old_fp- if new_is_a_dir && not old_is_a_dir- then moveToDir repository opts [old_fp] new_fp- else do+ new_fs <- fileStatus work cur rec new_fp+ old_fs <- fileStatus work cur rec old_fp+ case (old_fs, new_fs) of+ (Nonexistant, _) -> fail $ old_fp ++ " is inexistant."+ (Unadded k, _) -> fail $ show k ++ " " ++ old_fp ++ " is unadded."+ (Known _, Nonexistant) -> simpleMove repository opts cur work old_fp new_fp+ (Known _, Shadow _) -> simpleMove repository opts cur work old_fp new_fp+ (_, Nonexistant) -> fail $ old_fp ++ " is not in the repository."+ (Known _, Known Dir) -> moveToDir repository opts [old_fp] new_fp+ (Known _, Unadded Dir) -> fail $ new_fp ++ " is not known to darcs; please add it to the repository."+ (Known _, _) -> fail $ new_fp ++ " already exists."+ (Shadow k, Unadded k') | k == k' -> simpleMove repository opts cur work old_fp new_fp+ (Shadow _, Known Dir) -> moveToDir repository opts [old_fp] new_fp+ (Shadow k, _) -> fail $ "cannot move " ++ show k ++ " " ++ old_fp ++ " into " ++ new_fp ++ " : " +++ "did you already move it elsewhere?"++simpleMove :: (RepoPatch p, ApplyState p ~ Tree) => Repository p C(r u t)+ -> [DarcsFlag] -> Tree IO -> Tree IO -> FilePath -> FilePath+ -> IO ()+simpleMove repository opts cur work old_fp new_fp = do addpatch <- checkNewAndOldFilenames opts cur work (old_fp,new_fp) withSignalsBlocked $ do case unFreeLeft <$> addpatch of
src/Darcs/Commands/Pull.hs view
@@ -26,20 +26,45 @@ import Darcs.Commands ( DarcsCommand(..), putVerbose, putInfo ) import Darcs.CommandsAux ( checkPaths )-import Darcs.Arguments ( DarcsFlag( Verbose, DryRun, MarkConflicts,- Intersection, Complement, AllowConflicts,- NoAllowConflicts, XMLOutput ),- nocompress, ignoretimes, setEnvDarcsPatches,- depsSel, pullConflictOptions, useExternalMerge,- matchSeveral, fixUrl,- allInteractive, repoCombinator,- printDryRunMessageAndExit,- test, dryRun,- setDefault, summary, workingRepoDir, remoteRepo,- setScriptsExecutableOption,- networkOptions, umaskOption, allowUnrelatedRepos, restrictPaths, changesReverse,- getOutput, output, makeScriptsExecutable- )+import Darcs.Arguments+ ( DarcsFlag+ ( AllowConflicts+ , Complement+ , DryRun+ , Intersection+ , MarkConflicts+ , NoAllowConflicts+ , Verbose+ , XMLOutput+ )+ , allInteractive+ , allowUnrelatedRepos+ , changesReverse+ , depsSel+ , dryRun+ , fixUrl+ , getOutput+ , ignoretimes+ , makeScriptsExecutable+ , matchSeveral+ , networkOptions+ , nocompress+ , output+ , pauseForGui+ , printDryRunMessageAndExit+ , pullConflictOptions+ , remoteRepo+ , repoCombinator+ , restrictPaths+ , setDefault+ , setEnvDarcsPatches+ , setScriptsExecutableOption+ , summary+ , test+ , umaskOption+ , useExternalMerge+ , workingRepoDir+ ) import Darcs.Flags( doReverse, isInteractive ) import Darcs.Repository ( Repository, identifyRepositoryFor, withGutsOf, amInHashedRepository, withRepoLock, RepoJob(..),@@ -96,7 +121,9 @@ "some of the patches that are in that repository but not in this one. Pull\n"++ "accepts arguments, which are URLs from which to pull, and when called\n"++ "without an argument, pull will use the repository from which you have most\n"++- "recently either pushed or pulled.\n"+ "recently either pushed or pulled.\n" +++ "\n" +++ "See 'darcs help apply' for detailed description of many options.\n" fetchHelp :: String fetchHelp =@@ -123,9 +150,10 @@ commandPrereq = amInHashedRepository, commandGetArgPossibilities = getPreflist "repos", commandArgdefaults = defaultrepo,- commandAdvancedOptions = [repoCombinator,- remoteRepo] ++- networkOptions,+ commandAdvancedOptions =+ [ repoCombinator+ , remoteRepo+ ] ++ networkOptions, commandBasicOptions = [matchSeveral, allInteractive] ++dryRun++@@ -147,16 +175,17 @@ commandPrereq = amInHashedRepository, commandGetArgPossibilities = getPreflist "repos", commandArgdefaults = defaultrepo,- commandAdvancedOptions = [repoCombinator,- nocompress,- ignoretimes,- remoteRepo,- setScriptsExecutableOption,- umaskOption,- restrictPaths,- changesReverse- ] ++- networkOptions,+ commandAdvancedOptions =+ [ repoCombinator+ , nocompress+ , ignoretimes+ , remoteRepo+ , setScriptsExecutableOption+ , umaskOption+ , restrictPaths+ , changesReverse+ , pauseForGui+ ] ++ networkOptions, commandBasicOptions = [matchSeveral, allInteractive, pullConflictOptions,
src/Darcs/Commands/Record.hs view
@@ -39,7 +39,7 @@ readTentativeRepo, tentativelyAddPatch, finalizeRepositoryChanges , testTentative- , invalidateIndex, unrecordedChanges )+ , invalidateIndex, unrecordedChanges, readRecorded ) import Darcs.Patch ( RepoPatch, Patchy, PrimOf, PrimPatch, namepatch, summaryFL, anonymous, adddeps, fromPrims ) import Darcs.Patch.Set ( PatchSet(..) )@@ -60,7 +60,7 @@ import Darcs.Commands ( DarcsCommand(..), nodefaults, commandStub ) import Darcs.Commands.Util ( announceFiles, filterExistingFiles ) import Darcs.Arguments ( DarcsFlag( PromptLongComment, NoEditLongComment,- EditLongComment, LogFile, Pipe,+ EditLongComment, LogFile, Pipe, Quiet, PatchName, AskDeps, All ), fileHelpAuthor, getAuthor, workingRepoDir, lookforadds,@@ -69,7 +69,8 @@ allPipeInteractive, leaveTestDir, test, author, patchnameOption, umaskOption, ignoretimes, nocompress, rmlogfile, logfile, listRegisteredFiles,- setScriptsExecutableOption )+ setScriptsExecutableOption+ , unified ) import Darcs.Flags (willRemoveLogFile, diffingOpts, compression, isInteractive) import Darcs.Utils ( askUser, promptYorn, PromptConfig(..), promptChar, editFile, clarifyErrors ) import Progress ( debugMessage)@@ -115,7 +116,7 @@ askdeps, askLongComment, lookforadds,- workingRepoDir]}+ workingRepoDir, unified]} commitDescription :: String commitDescription = "Redirect the user to record, push or send."@@ -150,6 +151,7 @@ Nothing -> do when (Pipe `elem` opts) $ do _ <- getDate opts return () putStrLn "No changes!"+ exitFailure Just ch -> doRecord repository opts existing_files ch where allow_empty_with_askdeps :: FL p C(x y) -> Maybe (FL p C(x y)) allow_empty_with_askdeps NilFL@@ -180,9 +182,11 @@ my_author <- getAuthor opts debugMessage "I'm slurping the repository." debugMessage "About to select changes..."+ pristine <- readRecorded repository (chs :> _ ) <- runSelection (selectChanges First ps) $ selectionContextPrim "record" opts (Just primSplitter) (map toFilePath <$> files)+ (Just pristine) when (is_empty_but_not_askdeps chs) $ do putStrLn "Ok, if you don't want to record anything, that's fine!" exitWith ExitSuccess@@ -228,7 +232,8 @@ `clarifyErrors` failuremessage debugMessage "Syncing timestamps..." when (isJust logf) $ removeFile (fromJust logf)- putStrLn $ "Finished recording patch '"++name++"'"+ when (Quiet `notElem` opts) $+ putStrLn $ "Finished recording patch '"++name++"'" where failuremessage = "Failed to record patch '"++name++"'" ++ case logf of Just lf -> "\nLogfile left in "++lf++"." Nothing -> ""@@ -415,5 +420,8 @@ "\n" ++ "If your test command is tediously slow (e.g. `make all') and you are\n" ++ "recording several patches in a row, you may wish to use --no-test to\n" ++- "skip all but the final test.\n"+ "skip all but the final test.\n" +++ "\n" +++ "To see some context (unchanged lines) around each change, use the\n" +++ "--unified option.\n"
src/Darcs/Commands/Replace.hs view
@@ -34,11 +34,11 @@ applyToWorking, readUnrecorded, readRecordedAndPending )-import Darcs.Patch ( Patchy, PrimPatch, tokreplace, forceTokReplace, applyToTree, fromPrim )+import Darcs.Patch ( Patchy, PrimPatch, tokreplace, forceTokReplace, applyToTree ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.FileName( fn2fp ) import Darcs.Patch.Patchy ( Apply )-import Darcs.Witnesses.Ordered ( FL(..), (+>+), concatFL, toFL, mapFL_FL )+import Darcs.Witnesses.Ordered ( FL(..), (+>+), concatFL, toFL ) import Darcs.Witnesses.Sealed ( Sealed(..), mapSeal, FreeLeft, Gap(..) ) import Darcs.Patch.RegChars ( regChars ) import Data.Char ( isSpace )@@ -202,7 +202,7 @@ -- a caret (@^@) is treated as a complementary set. isTok :: String -> String -> Bool isTok _ "" = False-isTok toks s = and $ map (regChars toks) s+isTok toks s = all (regChars toks) s -- | This function checks for @--token-chars@ on the command-line. If -- found, it validates the argument and returns it, without the
src/Darcs/Commands/Revert.hs view
@@ -29,7 +29,7 @@ ignoretimes, workingRepoDir, allInteractive, fixSubPaths,- listRegisteredFiles, umaskOption,+ listRegisteredFiles, umaskOption, unified ) import Darcs.Utils ( askUser ) import Darcs.RepoPath ( toFilePath )@@ -39,7 +39,7 @@ amInHashedRepository, readRecorded, unrecordedChanges )-import Darcs.Patch ( invert, applyToFilepaths, commute )+import Darcs.Patch ( invert, effectOnFilePaths, commute ) import Darcs.Patch.Split ( reversePrimSplitter ) import Darcs.Witnesses.Ordered ( FL(..), (:>)(..), lengthFL, nullFL, (+>+) ) import Darcs.SelectChanges ( selectChanges, WhichChanges(Last), selectionContextPrim, runSelection )@@ -78,7 +78,7 @@ commandGetArgPossibilities = listRegisteredFiles, commandArgdefaults = nodefaults, commandAdvancedOptions = [ignoretimes, umaskOption],- commandBasicOptions = [allInteractive,+ commandBasicOptions = [allInteractive, unified, workingRepoDir]} revertCmd :: [DarcsFlag] -> [String] -> IO ()@@ -87,13 +87,13 @@ else Just . sort <$> fixSubPaths opts args announceFiles files "Reverting changes in" changes <- unrecordedChanges (diffingOpts opts {- always ScanKnown here -}) repository files- let pre_changed_files = applyToFilepaths (invert changes) . map toFilePath <$> files+ let pre_changed_files = effectOnFilePaths (invert changes) . map toFilePath <$> files rec <- readRecorded repository Sealed touching_changes <- return (chooseTouching pre_changed_files changes) (case touching_changes of NilFL -> putStrLn "There are no changes to revert!" _ -> do- let context = selectionContextPrim "revert" opts (Just reversePrimSplitter) pre_changed_files+ let context = selectionContextPrim "revert" opts (Just reversePrimSplitter) pre_changed_files (Just rec) (norevert:>p) <- runSelection (selectChanges Last changes) context if nullFL p then putStrLn $ "If you don't want to revert after all," ++
src/Darcs/Commands/Rollback.hs view
@@ -121,7 +121,7 @@ when (nullFL ps) $ do putStrLn "No patches selected!" exitWith ExitSuccess setEnvDarcsPatches ps- let hunks_context = selectionContextPrim "rollback" opts (Just reversePrimSplitter) files_fp+ let hunks_context = selectionContextPrim "rollback" opts (Just reversePrimSplitter) files_fp Nothing hunks = (concatFL $ mapFL_FL canonize $ sortCoalesceFL $ effect ps) runSelection (selectChanges Last hunks) hunks_context >>= if (rollbackInWorkingDir opts)
+ src/Darcs/Commands/Test.hs view
@@ -0,0 +1,67 @@+-- Copyright (C) 2002-2005 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.Commands.Test ( test ) where+import System.Exit ( exitWith )++import Darcs.Commands ( DarcsCommand(..), nodefaults, putInfo )+import Darcs.Arguments ( DarcsFlag(Test)+ , leaveTestDir+ , workingRepoDir+ )+import Darcs.Repository ( Repository, amInHashedRepository, withRepository,+ testRecorded, RepoJob(..) )+import Darcs.Patch ( RepoPatch )+import Darcs.Patch.Apply( ApplyState )+import Printer ( text )+import Storage.Hashed.Tree( Tree )++#include "gadts.h"++testDescription :: String+testDescription = "Run regression test."++testHelp :: String+testHelp =+ "If a regression test is defined (see `darcs setpref') it will be run.\n"++test :: DarcsCommand+test = DarcsCommand {commandProgramName = "darcs",+ commandName = "test",+ commandHelp = testHelp,+ commandDescription = testDescription,+ commandExtraArgs = 0,+ commandExtraArgHelp = [],+ commandCommand = testCmd,+ commandPrereq = amInHashedRepository,+ commandGetArgPossibilities = return [],+ commandArgdefaults = nodefaults,+ commandAdvancedOptions = [],+ commandBasicOptions = [ leaveTestDir,+ workingRepoDir+ ]}++testCmd :: [DarcsFlag] -> [String] -> IO ()+testCmd opts _ = withRepository (Test:opts) (RepoJob (test' opts))++test'+ :: forall p C(r u t) . (RepoPatch p, ApplyState p ~ Tree)+ => [DarcsFlag] -> Repository p C(r u t) -> IO ()+test' opts repository = do+ putInfo opts $ text "Running test on current repository state."+ rc <- testRecorded repository+ exitWith rc
src/Darcs/Commands/Unrevert.hs view
@@ -27,7 +27,7 @@ import Darcs.Flags( diffingOpts ) import Darcs.Arguments ( DarcsFlag( MarkConflicts ), ignoretimes, workingRepoDir,- allInteractive, umaskOption,+ allInteractive, umaskOption, unified ) import Darcs.Repository ( SealedPatchSet, Repository, withRepoLock, RepoJob(..), unrevertUrl, considerMergeToWorking,@@ -82,7 +82,8 @@ commandAdvancedOptions = [umaskOption], commandBasicOptions = [ignoretimes, allInteractive,- workingRepoDir]}+ workingRepoDir,+ unified]} unrevertCmd :: [DarcsFlag] -> [String] -> IO () unrevertCmd opts [] = withRepoLock opts $ RepoJob $ \repository -> do@@ -92,7 +93,7 @@ unrec <- unrecordedChanges (diffingOpts opts {- always ScanKnown here -}) repository Nothing Sealed h_them <- return $ mergeThem us them Sealed pw <- considerMergeToWorking repository "pull" (MarkConflicts:opts) NilFL h_them- let context = selectionContextPrim "unrevert" opts Nothing Nothing+ let context = selectionContextPrim "unrevert" opts Nothing Nothing (Just rec) (p :> skipped) <- runSelection (selectChanges First pw) context tentativelyAddToPending repository opts p withSignalsBlocked $
src/Darcs/Commands/WhatsNew.hs view
@@ -19,39 +19,63 @@ #include "gadts.h" -module Darcs.Commands.WhatsNew ( whatsnew, status ) where-import System.Exit ( ExitCode(..), exitWith )-import Data.List ( sort, (\\) )-import Control.Monad ( when )+module Darcs.Commands.WhatsNew+ (+ whatsnew+ , status+ ) where+ import Control.Applicative ( (<$>) )+import Data.List ( delete )+import System.Exit ( ExitCode(..), exitWith ) +import Storage.Hashed.Tree( Tree )++import Darcs.Arguments ( DarcsFlag(..), workingRepoDir, lookforadds,+ ignoretimes, noskipBoring, unified, summary,+ fixSubPaths, listRegisteredFiles ) import Darcs.Commands ( DarcsCommand(..), nodefaults, commandAlias ) import Darcs.Commands.Util ( announceFiles )-import Darcs.Arguments ( DarcsFlag(..), workingRepoDir, lookforadds,- ignoretimes, noskipBoring,- unified, summary,- fixSubPaths,- listRegisteredFiles,- )+import Darcs.Diff( treeDiff ) import Darcs.Flags( isUnified, diffingOpts )-+import Darcs.Patch ( RepoPatch, PrimPatch, PrimOf, plainSummaryPrims,+ primIsHunk, applyToTree )+import Darcs.Patch.Apply( ApplyState )+import Darcs.Patch.Format ( PatchListFormat(..) )+import Darcs.Patch.FileHunk ( IsHunk(..) )+import Darcs.Patch.Patchy ( Patchy )+import Darcs.Patch.Permutations ( partitionRL )+import Darcs.Patch.Prim.Class ( PrimDetails(..) )+import Darcs.Patch.TouchesFiles( choosePreTouching )+import Darcs.RepoPath( SubPath, toFilePath ) import Darcs.Repository ( Repository, withRepository, RepoJob(..) , amInRepository , unrecordedChanges, readRecorded ) import Darcs.Repository.Prefs ( filetypeFunction )-import Darcs.Patch ( RepoPatch, PrimPatch, PrimOf, plainSummaryPrims, primIsHunk, applyToTree )-import Darcs.Patch.Apply( ApplyState )-import Darcs.Patch.TouchesFiles( choosePreTouching )-import Darcs.Patch.Permutations ( partitionRL )-import Darcs.RepoPath( SubPath, toFilePath ) import Darcs.PrintPatch ( printPatch, contextualPrintPatch )-import Darcs.Witnesses.Ordered ( FL(..), reverseRL, reverseFL, (:>)(..), nullFL )+import Darcs.Witnesses.Ordered ( FL(..), reverseRL, reverseFL, (:>)(..) ) import Darcs.Witnesses.Sealed ( Sealed(..), unFreeLeft )-import Darcs.Diff( treeDiff )-import Storage.Hashed.Tree( Tree )- import Printer ( putDocLn, renderString, vcat, text ) +whatsnew :: DarcsCommand+whatsnew = DarcsCommand { commandProgramName = "darcs"+ , commandName = "whatsnew"+ , commandHelp = whatsnewHelp+ , commandDescription = whatsnewDescription+ , commandExtraArgs = -1+ , commandExtraArgHelp = ["[FILE or DIRECTORY]..."]+ , commandCommand = whatsnewCmd+ , commandPrereq = amInRepository+ , commandGetArgPossibilities = listRegisteredFiles+ , commandArgdefaults = nodefaults+ , commandAdvancedOptions = [ignoretimes, noskipBoring]+ , commandBasicOptions = [ summary+ , unified+ , lookforadds+ , workingRepoDir+ ]+ }+ whatsnewDescription :: String whatsnewDescription = "List unrecorded changes in the working tree." @@ -71,6 +95,8 @@ " `M f -N +M rP' means a modified file, with N lines deleted, M\n" ++ " lines added, and P lexical replacements.\n" ++ " `f -> g' means a moved file or directory.\n" +++ " `a f' and `a d/' respectively mean a new, but unadded, file or\n" +++ " directory, when using --look-for-adds.\n" ++ "\n" ++ " An exclamation mark (!) as in `R! foo.c', means the hunk is known to\n" ++ " conflict with a hunk in another patch. The phrase `duplicated'\n" ++@@ -84,84 +110,84 @@ "This command exits unsuccessfully (returns a non-zero exit status) if\n" ++ "there are no unrecorded changes.\n" -whatsnew :: DarcsCommand-whatsnew = DarcsCommand {commandProgramName = "darcs",- commandName = "whatsnew",- commandHelp = whatsnewHelp,- commandDescription = whatsnewDescription,- commandExtraArgs = -1,- commandExtraArgHelp = ["[FILE or DIRECTORY]..."],- commandCommand = whatsnewCmd,- commandPrereq = amInRepository,- commandGetArgPossibilities = listRegisteredFiles,- commandArgdefaults = nodefaults,- commandAdvancedOptions = [ignoretimes, noskipBoring],- commandBasicOptions = [summary, unified,- lookforadds,- workingRepoDir]}--filteredChanges :: (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)- => [DarcsFlag] -> Repository p C(r u t)- -> Maybe [SubPath] -> IO (Sealed (FL (PrimOf p) C(t)))-filteredChanges opts repo files =- choosePreTouching (map toFilePath <$> files) `fmap` unrecordedChanges (diffingOpts opts) repo files- whatsnewCmd :: [DarcsFlag] -> [String] -> IO ()-whatsnewCmd opts' args- | LookForAdds `elem` opts' && NoSummary `notElem` opts' =- -- add Summary to the opts since 'darcs whatsnew --look-for-adds'- -- implies summary- withRepository (Summary:opts')- $ RepoJob $ \(repository :: Repository p C(r u r)) -> do- files <- if null args then return Nothing- else Just <$> fixSubPaths opts' args+whatsnewCmd opts args =+ withRepository opts $ RepoJob $ \(repo :: Repository p C(r u t)) -> do+ files <- if null args+ then return Nothing+ else Just <$> fixSubPaths opts args+ let isLookForAdds = LookForAdds `elem` opts && NoSummary `notElem` opts+ -- LookForAdds implies Summary, unless it's explcitly disabled.+ optsModifier = if isLookForAdds+ then (Summary :) . (LookForAdds `delete`)+ else id+ opts' = optsModifier opts+ Sealed noLookChanges <- filteredUnrecordedChanges opts' repo files+ pristine <- readRecorded repo+ -- If we are looking for adds, return the corresponding FL of changes.+ Sealed unaddedNewPathsPs <- if isLookForAdds+ then do+ -- Use opts not opts', here, since we *do* want to look for adds.+ Sealed lookChanges <- filteredUnrecordedChanges opts repo files+ noLookAddsTree <- applyAddPatchesToPristine noLookChanges pristine+ lookAddsTree <- applyAddPatchesToPristine lookChanges pristine+ ftf <- filetypeFunction+ -- Return the patches that create files/dirs that aren't yet added.+ unFreeLeft <$> treeDiff ftf noLookAddsTree lookAddsTree+ else return (Sealed NilFL) announceFiles files "What's new in"- Sealed all_changes <- filteredChanges opts' repository files- Sealed chold <- filteredChanges (opts' \\ [LookForAdds]) repository files- pristine <- readRecorded repository- ftf <- filetypeFunction- cho_adds :> _ <- return $ partitionRL primIsHunk $ reverseFL chold- cha :> _ <- return $ partitionRL primIsHunk $ reverseFL all_changes+ exitOnNoChanges (unaddedNewPathsPs, noLookChanges)+ printChanges opts' pristine noLookChanges+ printUnaddedPaths unaddedNewPathsPs+ where+ -- |Filter out hunk patches (leaving add patches) and return the tree+ -- resulting from applying the filtered patches to the pristine tree.+ applyAddPatchesToPristine ps pristine = do+ adds :> _ <- return $ partitionRL primIsHunk $ reverseFL ps+ applyToTree (reverseRL adds) pristine - cho_adds_t <- applyToTree (reverseRL cho_adds) pristine- cha_t <- applyToTree (reverseRL cha) pristine- Sealed chn <- unFreeLeft `fmap` treeDiff ftf cho_adds_t cha_t :: IO (Sealed (FL (PrimOf p) C(r)))+ exitOnNoChanges :: (FL p C(x y), FL p C(u v)) -> IO ()+ exitOnNoChanges (NilFL, NilFL) = do putStrLn "No changes!"+ exitWith $ ExitFailure 1+ exitOnNoChanges _ = return () - exitOnNoChanges (chn, chold)- putDocLn $ plainSummaryPrims chold- printSummary chn- where lower_as x = vcat $ map (text . l_as) $ lines x- l_as ('A':x) = 'a':x- l_as x = x- exitOnNoChanges :: (FL prim C(x y), FL p C(u v)) -> IO ()- exitOnNoChanges (NilFL, NilFL) = do putStrLn "No changes!"- exitWith $ ExitFailure 1- exitOnNoChanges _ = return ()- printSummary :: PrimPatch prim => FL prim C(x y) -> IO ()- printSummary NilFL = return ()- printSummary new = putDocLn $ lower_as $ renderString $ plainSummaryPrims new+ printUnaddedPaths :: PrimPatch p => FL p C(x y) -> IO ()+ printUnaddedPaths NilFL = return ()+ printUnaddedPaths ps =+ putDocLn . lowercaseAs . renderString . plainSummaryPrims $ ps -whatsnewCmd opts args- | otherwise =- withRepository opts $ RepoJob $ \repository -> do- files <- if null args then return Nothing- else Just . sort <$> fixSubPaths opts args- announceFiles files "What's new in"- Sealed changes <- filteredChanges opts repository files- when (nullFL changes) $ putStrLn "No changes!" >> (exitWith $ ExitFailure 1)- printSummary repository changes- where printSummary :: (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)- => Repository p C(r u t) -> FL (PrimOf p) C(r y) -> IO ()- printSummary _ NilFL = do putStrLn "No changes!"- exitWith $ ExitFailure 1- printSummary r ch | Summary `elem` opts = putDocLn $ plainSummaryPrims ch- | isUnified opts = do pristine <- readRecorded r- contextualPrintPatch pristine ch- | otherwise = printPatch ch+ -- Make any add markers lowercase, to distinguish new-but-unadded files+ -- from those that are unrecorded, but added.+ lowercaseAs x = vcat $ map (text . lowercaseA) $ lines x+ lowercaseA ('A' : x) = 'a' : x+ lowercaseA x = x -status :: DarcsCommand-status = (commandAlias "status" Nothing whatsnew)- { commandCommand = \fs -> commandCommand whatsnew (Summary : LookForAdds : fs)- , commandDescription = "Alias for `darcs " ++ commandName whatsnew ++ " -ls '."- }+ -- |Appropriately print changes, according to the passed flags.+ printChanges :: (PatchListFormat p, IsHunk p, Patchy p, PrimDetails p,+ ApplyState p ~ Tree) => [DarcsFlag] -> Tree IO -> FL p C(x y)+ -> IO ()+ printChanges opts' pristine changes+ | Summary `elem` opts' = putDocLn $ plainSummaryPrims changes+ | isUnified opts' = contextualPrintPatch pristine changes+ | otherwise = printPatch changes + -- |return the unrecorded changes that affect an optional list of paths.+ filteredUnrecordedChanges :: (RepoPatch p, ApplyState p ~ Tree,+ ApplyState (PrimOf p) ~ Tree) => [DarcsFlag]+ -> Repository p C(r u t) -> Maybe [SubPath]+ -> IO (Sealed (FL (PrimOf p) C(t)))+ filteredUnrecordedChanges opts' repo files =+ let filePaths = map toFilePath <$> files in+ let diffOpts = diffingOpts opts' in+ choosePreTouching filePaths <$> unrecordedChanges diffOpts repo files++-- |status is an alias for whatsnew, with implicit Summary and LookForAdds+-- flags. We override the default description, to include the implicit flags.+status :: DarcsCommand+status = statusAlias { commandCommand = statusCmd+ , commandDescription = statusDesc+ }+ where+ statusAlias = commandAlias "status" Nothing whatsnew+ statusCmd fs = commandCommand whatsnew (Summary : LookForAdds : fs)+ statusDesc = "Alias for `darcs " ++ commandName whatsnew ++ " -ls '."
src/Darcs/Diff.hs view
@@ -20,87 +20,137 @@ -- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -- SOFTWARE. -module Darcs.Diff( treeDiff ) where-import Darcs.Witnesses.Ordered ( FL(..), (+>+) )-import Darcs.Witnesses.Sealed ( Gap(..) )-import Darcs.Repository.Prefs ( FileType(..) )-import Darcs.Patch ( PrimPatch, hunk, canonize, binary- , addfile, rmfile, adddir, rmdir, invert)+-- |+-- Module : Darcs.Diff+-- Copyright : 2009 Petr Rockai+-- License : MIT+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable -import Storage.Hashed.Tree( diffTrees, zipTrees, TreeItem(..), Tree- , readBlob, emptyBlob )-import Storage.Hashed.AnchoredPath( AnchoredPath, anchorPath )+module Darcs.Diff+ (+ treeDiff+ ) where + import qualified Data.ByteString.Lazy.Char8 as BLC import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BL+ import Data.List ( sortBy )-import ByteStringUtils( isFunky ) +import Storage.Hashed.Tree ( diffTrees+ , zipTrees+ , TreeItem(..)+ , Tree+ , readBlob+ , emptyBlob+ )+import Storage.Hashed.AnchoredPath( AnchoredPath, anchorPath )+++import ByteStringUtils ( isFunky )+import Darcs.Patch ( PrimPatch+ , hunk+ , canonize+ , binary+ , addfile+ , rmfile+ , adddir+ , rmdir+ , invert+ )+import Darcs.Repository.Prefs ( FileType(..) )+import Darcs.Witnesses.Ordered ( FL(..)+ , (+>+)+ )+import Darcs.Witnesses.Sealed ( Gap(..) )++ #include "gadts.h" #include "impossible.h" -data Diff m = Added (TreeItem m) | Removed (TreeItem m) | Changed (TreeItem m) (TreeItem m) -getDiff :: AnchoredPath -> Maybe (TreeItem m) -> Maybe (TreeItem m) -> (AnchoredPath, Diff m)+data Diff m = Added (TreeItem m)+ | Removed (TreeItem m)+ | Changed (TreeItem m) (TreeItem m)+++getDiff :: AnchoredPath+ -> Maybe (TreeItem m)+ -> Maybe (TreeItem m)+ -> (AnchoredPath, Diff m) getDiff p Nothing (Just t) = (p, Added t) getDiff p (Just from) (Just to) = (p, Changed from to) getDiff p (Just t) Nothing = (p, Removed t) getDiff _ Nothing Nothing = impossible -- zipTrees should never return this -treeDiff :: forall m w prim . (Functor m, Monad m, Gap w, PrimPatch prim) => (FilePath -> FileType) -> Tree m -> Tree m -> m (w (FL prim))++treeDiff :: forall m w prim . (Functor m, Monad m, Gap w, PrimPatch prim)+ => (FilePath -> FileType)+ -> Tree m+ -> Tree m+ -> m (w (FL prim)) treeDiff ft t1 t2 = do- (from, to) <- diffTrees t1 t2- diffs <- mapM (uncurry diff) $ sortBy organise $ zipTrees getDiff from to- return $ foldr (joinGap (+>+)) (emptyGap NilFL) diffs- where- -- sort into removes, changes, adds, with removes in reverse-path order- -- and everything else in forward order- organise :: (AnchoredPath, Diff m) -> (AnchoredPath, Diff m) -> Ordering+ (from, to) <- diffTrees t1 t2+ diffs <- mapM (uncurry diff) $ sortBy organise $ zipTrees getDiff from to+ return $ foldr (joinGap (+>+)) (emptyGap NilFL) diffs+ where+ -- sort into removes, changes, adds, with removes in reverse-path order+ -- and everything else in forward order+ organise :: (AnchoredPath, Diff m) -> (AnchoredPath, Diff m) -> Ordering - organise (p1, Changed _ _ ) (p2, Changed _ _) = compare p1 p2- organise (p1, Added _) (p2, Added _) = compare p1 p2- organise (p1, Removed _) (p2, Removed _) = compare p2 p1+ organise (p1, Changed _ _ ) (p2, Changed _ _) = compare p1 p2+ organise (p1, Added _) (p2, Added _) = compare p1 p2+ organise (p1, Removed _) (p2, Removed _) = compare p2 p1 - organise (_, Removed _) _ = LT- organise _ (_, Removed _) = GT+ organise (_, Removed _) _ = LT+ organise _ (_, Removed _) = GT - organise (_, Changed _ _) _ = LT- organise _ (_, Changed _ _) = GT+ organise (_, Changed _ _) _ = LT+ organise _ (_, Changed _ _) = GT - diff :: AnchoredPath -> Diff m -> m (w (FL prim))- diff _ (Changed (SubTree _) (SubTree _)) = return (emptyGap NilFL)- diff p (Removed (SubTree _)) =- return $ freeGap (rmdir (anchorPath "" p) :>: NilFL)- diff p (Added (SubTree _)) =- return $ freeGap (adddir (anchorPath "" p) :>: NilFL)- diff p (Added b'@(File _)) =- do diff' <- diff p (Changed (File emptyBlob) b')- return $ joinGap (:>:) (freeGap (addfile (anchorPath "" p))) diff'- diff p (Removed a'@(File _)) =- do diff' <- diff p (Changed a' (File emptyBlob))- return $ joinGap (+>+) diff' (freeGap (rmfile (anchorPath "" p) :>: NilFL))- diff p (Changed (File a') (File b')) =- do a <- readBlob a'- b <- readBlob b'- let path = anchorPath "" p- case ft path of- TextFile | no_bin a && no_bin b ->- return $ text_diff path a b- _ -> return $ if a /= b- then freeGap (binary path (strict a) (strict b) :>: NilFL)- else emptyGap NilFL- diff p _ = fail $ "Missing case at path " ++ show p- text_diff p a b- | BL.null a && BL.null b = emptyGap NilFL- | BL.null a = freeGap (diff_from_empty p b)- | BL.null b = freeGap (diff_to_empty p a)- | otherwise = freeGap (line_diff p (linesB a) (linesB b))- line_diff p a b = canonize (hunk p 1 a b)- diff_to_empty p x | BLC.last x == '\n' = line_diff p (init $ linesB x) []- | otherwise = line_diff p (linesB x) [BS.empty]- diff_from_empty p x = invert (diff_to_empty p x)- no_bin = not . isFunky . strict . BL.take 4096- linesB = map strict . BLC.split '\n'- strict = BS.concat . BL.toChunks+ diff :: AnchoredPath -> Diff m -> m (w (FL prim))+ diff _ (Changed (SubTree _) (SubTree _)) = return (emptyGap NilFL)+ diff p (Removed (SubTree _)) =+ return $ freeGap (rmdir (anchorPath "" p) :>: NilFL)+ diff p (Added (SubTree _)) =+ return $ freeGap (adddir (anchorPath "" p) :>: NilFL)+ diff p (Added b'@(File _)) =+ do diff' <- diff p (Changed (File emptyBlob) b')+ return $ joinGap (:>:) (freeGap (addfile (anchorPath "" p))) diff'+ diff p (Removed a'@(File _)) =+ do diff' <- diff p (Changed a' (File emptyBlob))+ return $ joinGap (+>+) diff' (freeGap (rmfile (anchorPath "" p) :>: NilFL))+ diff p (Changed (File a') (File b')) =+ do a <- readBlob a'+ b <- readBlob b'+ let path = anchorPath "" p+ case ft path of+ TextFile | no_bin a && no_bin b ->+ return $ text_diff path a b+ _ -> return $ if a /= b+ then freeGap (binary path (strict a) (strict b) :>: NilFL)+ else emptyGap NilFL+ diff p _ = fail $ "Missing case at path " ++ show p++ text_diff p a b+ | BL.null a && BL.null b = emptyGap NilFL+ | BL.null a = freeGap (diff_from_empty p b)+ | BL.null b = freeGap (diff_to_empty p a)+ | otherwise = freeGap (line_diff p (linesB a) (linesB b))++ line_diff p a b = canonize (hunk p 1 a b)++ diff_to_empty p x | BLC.last x == '\n' = line_diff p (init $ linesB x) []+ | otherwise = line_diff p (linesB x) [BS.empty]++ diff_from_empty p x = invert (diff_to_empty p x)++ no_bin = not . isFunky . strict . BL.take 4096++ linesB = map strict . BLC.split '\n'++ strict = BS.concat . BL.toChunks
src/Darcs/External.hs view
@@ -47,8 +47,7 @@ import System.Console.Terminfo( tiGetNum, setupTermFromEnv, getCapability ) #endif import System.Posix.Files ( createLink )-import System.Directory ( createDirectoryIfMissing )-import System.FilePath.Posix ( (</>), takeDirectory, normalise )+import System.FilePath.Posix ( (</>), normalise ) import Darcs.Flags ( DarcsFlag( SignAs, Sign, SignSSL, Verify, VerifySSL )@@ -124,8 +123,9 @@ backupBy backup f = do hasBF <- doesFileExist f hasBD <- doesDirectoryExist f- when (hasBF || hasBD) $ helper (0::Int)+ when (hasBF || hasBD) $ helper 0 where+ helper :: Int -> IO () helper i = do existsF <- doesFileExist next existsD <- doesDirectoryExist next if (existsF || existsD)@@ -133,7 +133,7 @@ else do putStrLn $ "Backing up " ++ f ++ "(" ++ suffix ++ ")" backup f next where next = f ++ suffix- suffix = "-darcs-backup" ++ show i+ suffix = ".~" ++ show i ++ "~" copyAndReadFile :: (FilePath -> IO a) -> String -> Cachable -> IO a copyAndReadFile readfn fou _ | isFile fou = readfn fou@@ -212,7 +212,7 @@ mk_source fp = source </> fp mk_dest fp = dest </> fp zipWithM_ cloneSubTree (map mk_source fps') (map mk_dest fps')- else if isRegularFile fs then do+ else if isRegularFile fs then cloneFile source dest else fail ("cloneSubTree: Bad source "++ source) `catch` (\e -> if isDoesNotExistError e@@ -336,7 +336,7 @@ sendEmailDoc f cc s "" scmd mbundle body sendEmailDoc f t s cc scmd mbundle body = do use_sendmail <- haveSendmail- if use_sendmail || scmd /= "" then do+ if use_sendmail || scmd /= "" then withOpenTemp $ \(h,fn) -> do generateEmail h f t s cc body hClose h@@ -383,7 +383,7 @@ resendEmail t scmd body = do use_sendmail <- haveSendmail if use_sendmail || scmd /= ""- then do+ then withOpenTemp $ \(h,fn) -> do hPutStrLn h $ "To: "++ t hPutStrLn h $ find_from (linesPS body)
src/Darcs/Flags.hs view
@@ -15,21 +15,42 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -module Darcs.Flags ( DarcsFlag( .. ),- Compression( .. ), UseIndex(..), ScanKnown(..), RemoteDarcs(..),- compression, remoteDarcs, diffingOpts,- wantExternalMerge, isInteractive,- maxCount, willIgnoreTimes, willRemoveLogFile, isUnified,- isNotUnified, doHappyForwarding, includeBoring,- doAllowCaseOnly, doAllowWindowsReserved, doReverse,- usePacks,- showChangesOnlyToFiles, rollbackInWorkingDir, removeFromAmended,- defaultFlag- ) where-import Data.Maybe( fromMaybe )+module Darcs.Flags+ ( DarcsFlag( .. )+ , Compression( .. )+ , UseIndex(..)+ , ScanKnown(..)+ , RemoteDarcs(..)+ , compression+ , remoteDarcs+ , diffingOpts+ , wantExternalMerge+ , wantGuiPause+ , isInteractive+ , maxCount+ , willIgnoreTimes+ , willRemoveLogFile+ , isUnified+ , isNotUnified+ , doHappyForwarding+ , includeBoring+ , doAllowCaseOnly+ , doAllowWindowsReserved+ , doReverse+ , usePacks+ , showChangesOnlyToFiles+ , rollbackInWorkingDir+ , removeFromAmended+ , defaultFlag+ ) where+++import Data.List ( find )+import Data.Maybe( fromMaybe, isJust ) import Darcs.Patch.MatchData ( PatchMatch ) import Darcs.RepoPath ( AbsolutePath, AbsolutePathOrStd ) + -- | The 'DarcsFlag' type is a list of all flags that can ever be -- passed to darcs, or to one of its commands. data DarcsFlag = Help | ListOptions | NoTest | Test@@ -77,6 +98,7 @@ | Pipe | Interactive | DiffCmd String | ExternalMerge String | Summary | NoSummary+ | PauseForGui | NoPauseForGui | Unified | NonUnified | Reverse | Forward | Complete | Lazy | FixFilePath AbsolutePath AbsolutePath | DiffFlags String@@ -120,33 +142,66 @@ -- ADTs for selecting specific behaviour... FIXME These should be eventually -- moved out from this module, closer to where they are actually used -data Compression = NoCompression | GzipCompression-compression :: [DarcsFlag] -> Compression-compression f | NoCompress `elem` f = NoCompression- | otherwise = GzipCompression+data Compression = NoCompression+ | GzipCompression -remoteDarcs :: [DarcsFlag] -> RemoteDarcs-remoteDarcs f | (x:_) <- [ c | RemoteDarcsOpt c <- f ] = RemoteDarcs x- | otherwise = DefaultRemoteDarcs+data UseIndex = UseIndex+ | IgnoreIndex -data UseIndex = UseIndex | IgnoreIndex data ScanKnown = ScanKnown -- ^Just files already known to darcs | ScanAll -- ^All files, i.e. look for new ones | ScanBoring -- ^All files, even boring ones-diffingOpts :: [DarcsFlag] -> (UseIndex, ScanKnown)-diffingOpts opts = (if willIgnoreTimes opts then IgnoreIndex else UseIndex,- if LookForAdds `elem` opts then - if Boring `elem` opts then ScanBoring else ScanAll- else ScanKnown) -data RemoteDarcs = RemoteDarcs String | DefaultRemoteDarcs+data RemoteDarcs = RemoteDarcs String+ | DefaultRemoteDarcs ----------------------- +compression :: [DarcsFlag]+ -> Compression+compression f+ | NoCompress `elem` f = NoCompression+ | otherwise = GzipCompression+++remoteDarcs :: [DarcsFlag]+ -> RemoteDarcs+remoteDarcs f+ | (x:_) <- [ c | RemoteDarcsOpt c <- f ] = RemoteDarcs x+ | otherwise = DefaultRemoteDarcs+++diffingOpts :: [DarcsFlag]+ -> (UseIndex, ScanKnown)+diffingOpts opts = (index, scan)+ where+ index = if willIgnoreTimes opts+ then IgnoreIndex+ else UseIndex+ scan =+ if LookForAdds `elem` opts+ then+ if Boring `elem` opts+ then ScanBoring+ else ScanAll+ else ScanKnown++ wantExternalMerge :: [DarcsFlag] -> Maybe String wantExternalMerge [] = Nothing wantExternalMerge (ExternalMerge c:_) = Just c wantExternalMerge (_:fs) = wantExternalMerge fs+++wantGuiPause :: [DarcsFlag] -> Bool+wantGuiPause fs = (hasDiffCmd || hasExternalMerge) && hasPause+ where+ hasDiffCmd = any isDiffCmd fs+ hasExternalMerge = isJust $ wantExternalMerge fs+ isDiffCmd (DiffCmd _) = True+ isDiffCmd _ = False+ hasPause = maybe True (==PauseForGui) $ find isPauseFlag $ reverse fs+ isPauseFlag f = (f==PauseForGui) || (f==NoPauseForGui)+ isInteractive :: [DarcsFlag] -> Bool isInteractive = isInteractive_ True
src/Darcs/Global.hs view
@@ -16,21 +16,47 @@ -- Boston, MA 02110-1301, USA. {-# LANGUAGE CPP #-}--- | This was originally Tomasz Zielonka's AtExit module, slightly generalised++-- |+-- Module : Darcs.Global+-- Copyright : 2005 Tomasz Zielonka+-- License : GPL+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable+--+-- This was originally Tomasz Zielonka's AtExit module, slightly generalised -- to include global variables. Here, we attempt to cover broad, global -- features, such as exit handlers. These features slightly break the Haskellian -- purity of darcs, in favour of programming convenience.-module Darcs.Global ( atexit, withAtexit,- SshSettings(..), defaultSsh,- timingsMode, setTimingsMode,- whenDebugMode, withDebugMode, setDebugMode,- debugMessage, debugFail, putTiming,- addCRCWarning, getCRCWarnings, resetCRCWarnings,- addBadSource, getBadSourcesList, isBadSource, darcsdir,- isReachableSource, addReachableSource,- windows++module Darcs.Global+ (+ atexit+ , withAtexit+ , SshSettings(..)+ , defaultSsh+ , timingsMode+ , setTimingsMode+ , whenDebugMode+ , withDebugMode+ , setDebugMode+ , debugMessage+ , debugFail+ , putTiming+ , addCRCWarning+ , getCRCWarnings+ , resetCRCWarnings+ , addBadSource+ , getBadSourcesList+ , isBadSource+ , darcsdir+ , isReachableSource+ , addReachableSource+ , windows ) where + import Control.Applicative ( (<$>), (<*>) ) import Control.Monad ( when ) import Control.Concurrent.MVar@@ -50,27 +76,32 @@ import System.Exit ( ExitCode(..) ) import Prelude hiding (catch) + windows :: Bool windows = "mingw" `isPrefixOf` os -{-# NOINLINE atexitActions #-}+ atexitActions :: MVar (Maybe [IO ()]) atexitActions = unsafePerformIO (newMVar (Just []))+{-# NOINLINE atexitActions #-} --- | Registers an IO action to run just before darcs exits. Useful--- for removing temporary files and directories, for example.-atexit :: IO () -> IO ()-atexit action = do- modifyMVar_ atexitActions $ \ml -> do++-- | Registers an IO action to run just before darcs exits. Useful for removing+-- temporary files and directories, for example. Referenced in Issue1914.+atexit :: IO ()+ -> IO ()+atexit action =+ modifyMVar_ atexitActions $ \ml -> case ml of- Just l -> do+ Just l -> return (Just (action : l)) Nothing -> do hPutStrLn stderr "It's too late to use atexit" return Nothing + withAtexit :: IO a -> IO a-withAtexit prog = do+withAtexit prog = bracket_ (return ()) exit@@ -80,27 +111,31 @@ Just actions <- swapMVar atexitActions Nothing -- from now on atexit will not register new actions mapM_ runAction actions- runAction action = do+ runAction action = catch (unblock action) $ \(exn :: SomeException) -> do hPutStrLn stderr $ "Exception thrown by an atexit registered action:" hPutStrLn stderr $ show exn -- Write-once-read-many global variables make it easier to implement flags, such--- as --no-ssh-cm. Using global variables reduces the number of parameters--- that we have to pass around, but it is rather unsafe and should be used sparingly.+-- as --no-ssh-cm. Using global variables reduces the number of parameters that+-- we have to pass around, but it is rather unsafe and should be used sparingly. -{-# NOINLINE _debugMode #-}+ _debugMode :: IORef Bool _debugMode = unsafePerformIO $ newIORef False+{-# NOINLINE _debugMode #-} + setDebugMode :: IO () setDebugMode = writeIORef _debugMode True + whenDebugMode :: IO () -> IO () whenDebugMode j = do b <- readIORef _debugMode when b j + withDebugMode :: (Bool -> IO a) -> IO a withDebugMode j = readIORef _debugMode >>= j @@ -108,33 +143,43 @@ debugMessage :: String -> IO () debugMessage m = whenDebugMode $ do putTiming; hPutStrLn stderr m + debugFail :: String -> IO a debugFail m = debugMessage m >> fail m + putTiming :: IO ()-putTiming = when timingsMode $ do t <- getClockTime >>= toCalendarTime- hPutStr stderr (calendarTimeToString t++": ")+putTiming = when timingsMode $ do+ t <- getClockTime >>= toCalendarTime+ hPutStr stderr (calendarTimeToString t++": ") -{-# NOINLINE _timingsMode #-}+ _timingsMode :: IORef Bool _timingsMode = unsafePerformIO $ newIORef False+{-# NOINLINE _timingsMode #-} + setTimingsMode :: IO () setTimingsMode = writeIORef _timingsMode True -{-# NOINLINE timingsMode #-}+ timingsMode :: Bool timingsMode = unsafePerformIO $ readIORef _timingsMode+{-# NOINLINE timingsMode #-} -data SshSettings = SshSettings { ssh :: String- , scp :: String- , sftp :: String }- deriving (Show, Eq) +data SshSettings = SshSettings+ { ssh :: String+ , scp :: String+ , sftp :: String+ } deriving (Show, Eq)++ _defaultSsh :: IORef SshSettings _defaultSsh = unsafePerformIO $ newIORef =<< detectSsh --- expected properties:++-- | Expected properties: -- -- * only ever runs once in the lifetime of the program -- * environment variables override all@@ -142,74 +187,94 @@ -- * falls back to plain old ssh detectSsh :: IO SshSettings detectSsh = do- whenDebugMode (putStrLn "Detecting SSH settings")- vanilla <- if windows- then do- plinkStr <- (snd3 <$> readProcessWithExitCode "plink" [] "")- `catch` \(e :: SomeException) -> return (show e)- whenDebugMode $ putStrLn $ "SSH settings (plink): " ++ (concat . take 1 . lines $ plinkStr)- if "PuTTY" `isPrefixOf` plinkStr- then return (SshSettings "plink" "pscp -q" "psftp")- else return rawVanilla- else return rawVanilla- settings <- SshSettings <$> fromEnv (ssh vanilla) "DARCS_SSH"- <*> fromEnv (scp vanilla) "DARCS_SCP"- <*> fromEnv (sftp vanilla) "DARCS_SFTP"- whenDebugMode (putStrLn $ "SSH settings: " ++ show settings)- return settings- where- snd3 (_, x, _) = x- rawVanilla = SshSettings "ssh" "scp -q" "sftp"- fromEnv :: String -> String -> IO String- fromEnv d v = catchJust notFound- (getEnv v)- (const (return d))- notFound e = if isDoesNotExistErrorType (ioeGetErrorType e) then Just () else Nothing+ whenDebugMode (putStrLn "Detecting SSH settings")+ vanilla <- if windows+ then do+ plinkStr <- (snd3 <$> readProcessWithExitCode "plink" [] "")+ `catch` \(e :: SomeException) -> return (show e)+ whenDebugMode $ putStrLn $+ "SSH settings (plink): " +++ (concat . take 1 . lines $ plinkStr)+ if "PuTTY" `isPrefixOf` plinkStr+ then return (SshSettings "plink" "pscp -q" "psftp")+ else return rawVanilla+ else return rawVanilla+ settings <- SshSettings <$> fromEnv (ssh vanilla) "DARCS_SSH"+ <*> fromEnv (scp vanilla) "DARCS_SCP"+ <*> fromEnv (sftp vanilla) "DARCS_SFTP"+ whenDebugMode (putStrLn $ "SSH settings: " ++ show settings)+ return settings+ where+ snd3 (_, x, _) = x+ rawVanilla = SshSettings "ssh" "scp -q" "sftp"+ fromEnv :: String -> String -> IO String+ fromEnv d v = catchJust notFound+ (getEnv v)+ (const (return d))+ notFound e = if isDoesNotExistErrorType (ioeGetErrorType e)+ then Just ()+ else Nothing + defaultSsh :: SshSettings defaultSsh = unsafePerformIO $ readIORef _defaultSsh + type CRCWarningList = [FilePath]-{-# NOINLINE _crcWarningList #-} _crcWarningList :: IORef CRCWarningList _crcWarningList = unsafePerformIO $ newIORef []+{-# NOINLINE _crcWarningList #-} + addCRCWarning :: FilePath -> IO () addCRCWarning fp = modifyIORef _crcWarningList (fp:) + getCRCWarnings :: IO [FilePath] getCRCWarnings = readIORef _crcWarningList + resetCRCWarnings :: IO () resetCRCWarnings = writeIORef _crcWarningList [] -{- NOINLINE _badSourcesList -}+ _badSourcesList :: IORef [String] _badSourcesList = unsafePerformIO $ newIORef []+{- NOINLINE _badSourcesList -} + addBadSource :: String -> IO () addBadSource cache = modifyIORef _badSourcesList (cache:) + getBadSourcesList :: IO [String] getBadSourcesList = readIORef _badSourcesList + isBadSource :: IO (String -> Bool)-isBadSource = do badSources <- getBadSourcesList- return (`elem` badSources)+isBadSource = do+ badSources <- getBadSourcesList+ return (`elem` badSources) -{- NOINLINE _reachableSourcesList -}+ _reachableSourcesList :: IORef [String] _reachableSourcesList = unsafePerformIO $ newIORef []+{- NOINLINE _reachableSourcesList -} + addReachableSource :: String -> IO () addReachableSource src = modifyIORef _reachableSourcesList (src:) + getReachableSources :: IO [String] getReachableSources = readIORef _reachableSourcesList + isReachableSource :: IO (String -> Bool)-isReachableSource = do reachableSources <- getReachableSources- return (`elem` reachableSources)+isReachableSource = do+ reachableSources <- getReachableSources+ return (`elem` reachableSources)+ darcsdir :: String darcsdir = "_darcs"
src/Darcs/Match.hs view
@@ -187,7 +187,7 @@ unpullLastN repo n = applyInvRL `unsealFlipped` (safetake n $ newset2RL repo) checkMatchSyntax :: [DarcsFlag] -> IO ()-checkMatchSyntax opts = do+checkMatchSyntax opts = case getMatchPattern opts of Nothing -> return () Just p -> either fail (const $ return ()) $ (parseMatch p::Either String (MatchFun DummyPatch))
src/Darcs/Patch.hs view
@@ -17,42 +17,88 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE CPP, UndecidableInstances #-} -- XXX Undecidable only in GHC < 7+ #include "gadts.h"-module Darcs.Patch ( RepoPatch,- PrimOf, Named, Patchy,- joinPatches,- fromPrim, fromPrims,- rmfile, addfile, rmdir, adddir, move,- hunk, tokreplace, namepatch, anonymous,- binary,- description,- showContextPatch, showPatch, showNicely,- infopatch, changepref,- thing, things,- primIsAddfile, primIsHunk, primIsSetpref,- merge,- commute, listTouchedFiles, hunkMatches,- forceTokReplace,- PrimPatch,- -- for PatchTest- resolveConflicts,- Effect, effect,- primIsBinary, gzWritePatch, writePatch, primIsAdddir,- invert, invertFL, invertRL,- commuteFLorComplain, commuteRL,- readPatch, readPatchPartial,- canonize, sortCoalesceFL,- tryToShrink,- patchname, patchcontents,- applyToFilepaths, apply,- applyToTree,- patch2patchinfo,- summary, summaryFL, plainSummary, xmlSummary, plainSummaryPrims,- adddeps, getdeps,- listConflictedFiles,- isInconsistent,- ) where++module Darcs.Patch+ ( RepoPatch+ , PrimOf+ , Named+ , Patchy+ , joinPatches+ , fromPrim+ , fromPrims+ , rmfile+ , addfile+ , rmdir+ , adddir+ , move+ , hunk+ , tokreplace+ , namepatch+ , anonymous+ , binary+ , description+ , showContextPatch+ , showPatch+ , showNicely+ , infopatch+ , changepref+ , thing+ , things+ , primIsAddfile+ , primIsHunk+ , primIsSetpref+ , merge+ , commute+ , listTouchedFiles+ , hunkMatches+ , forceTokReplace+ , PrimPatch+ -- * for PatchTest+ , resolveConflicts+ , Effect+ , effect+ , primIsBinary+ , gzWritePatch+ , writePatch+ , primIsAdddir+ , invert+ , invertFL+ , invertRL+ , commuteFLorComplain+ , commuteRL+ , readPatch+ , readPatchPartial+ , canonize+ , sortCoalesceFL+ , tryToShrink+ , patchname+ , patchcontents+ , applyToFilePaths+ , apply+ , applyToTree+ , effectOnFilePaths+ , patch2patchinfo+ , summary+ , summaryFL+ , plainSummary+ , xmlSummary+ , plainSummaryPrims+ , adddeps+ , getdeps+ , listConflictedFiles+ , isInconsistent+ ) where+++import Darcs.Patch.Apply ( applyToFilePaths, effectOnFilePaths, applyToTree )+import Darcs.Patch.Apply ( ApplyState ) import Darcs.Patch.Commute ( commuteFLorComplain, commuteRL )+import Darcs.Patch.Conflict ( Conflict, CommuteNoConflicts, listConflictedFiles, resolveConflicts )+import Darcs.Patch.Effect ( Effect(effect) )+import Darcs.Patch.FileHunk ( IsHunk )+import Darcs.Patch.Format ( PatchListFormat ) import Darcs.Patch.Invert ( invertRL, invertFL ) import Darcs.Patch.Named ( Named, adddeps, namepatch,@@ -60,7 +106,6 @@ getdeps, infopatch, patch2patchinfo, patchname, patchcontents )-import Darcs.Patch.Read ( readPatch, readPatchPartial ) import Darcs.Patch.Patchy ( Patchy, showPatch, showNicely, showContextPatch, invert,@@ -69,15 +114,6 @@ description, summary, summaryFL, commute, listTouchedFiles, hunkMatches )-import Darcs.Patch.RepoPatch ( RepoPatch )-import Darcs.Patch.Show ( writePatch, gzWritePatch )-import Darcs.Patch.Summary ( xmlSummary, plainSummary, plainSummaryPrims )-import Darcs.Patch.Apply ( applyToFilepaths, applyToTree )-import Darcs.Patch.V1.Commute ( merge )-import Darcs.Patch.Conflict ( Conflict, CommuteNoConflicts, listConflictedFiles, resolveConflicts )-import Darcs.Patch.Effect ( Effect(effect) )-import Darcs.Patch.FileHunk ( IsHunk )-import Darcs.Patch.Format ( PatchListFormat ) import Darcs.Patch.Prim ( FromPrims, fromPrims, joinPatches, FromPrim, fromPrim, canonize, sortCoalesceFL,@@ -87,10 +123,16 @@ primIsHunk, primIsBinary, primIsSetpref, tryToShrink, PrimPatch, PrimPatchBase(..) )-import Darcs.Patch.TokenReplace ( forceTokReplace )+import Darcs.Patch.Read ( readPatch, readPatchPartial ) import Darcs.Patch.Repair ( isInconsistent )-import Darcs.Patch.Apply ( ApplyState )+import Darcs.Patch.RepoPatch ( RepoPatch )+import Darcs.Patch.Show ( writePatch, gzWritePatch )+import Darcs.Patch.Summary ( xmlSummary, plainSummary, plainSummaryPrims )+import Darcs.Patch.TokenReplace ( forceTokReplace )+import Darcs.Patch.V1.Commute ( merge )+ import Storage.Hashed.Tree( Tree )+ instance (CommuteNoConflicts p, Conflict p, IsHunk p, PatchListFormat p, PrimPatchBase p, Patchy p, ApplyState p ~ Tree) => Patchy (Named p)
src/Darcs/Patch/Apply.hs view
@@ -20,24 +20,36 @@ #include "gadts.h" -module Darcs.Patch.Apply ( Apply(..),- applyToFilepaths,- applyToTree,- applyToState,- )- where+-- |+-- Module : Darcs.Patch.Apply+-- Copyright : 2002-2005 David Roundy+-- License : GPL+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable +module Darcs.Patch.Apply+ (+ Apply(..)+ , applyToFilePaths+ , applyToTree+ , applyToState+ , effectOnFilePaths+ ) where+ import Prelude hiding ( catch, pi ) -import Darcs.Witnesses.Ordered ( FL(..), RL(..) )+import Control.Applicative ( (<$>) )+import Control.Arrow ( (***) ) -import Darcs.Patch.ApplyMonad ( ApplyMonad(..), withFilePaths, ApplyMonadTrans(..) )-import Darcs.Patch.FileName( fn2fp, fp2fn ) import Storage.Hashed.Tree( Tree ) import Storage.Hashed.Monad( virtualTreeMonad ) -import Control.Monad ( MonadPlus )+import Darcs.Patch.ApplyMonad ( ApplyMonad(..), withFileNames, ApplyMonadTrans(..) )+import Darcs.Patch.FileName( fn2fp, fp2fn )+import Darcs.Witnesses.Ordered ( FL(..), RL(..) ) + class Apply p where type ApplyState p :: (* -> *) -> * apply :: ApplyMonad m (ApplyState p) => p C(x y) -> m ()@@ -52,14 +64,37 @@ apply NilRL = return () apply (p:<:ps) = apply ps >> apply p -applyToFilepaths :: (Apply p, ApplyState p ~ Tree) => p C(x y) -> [FilePath] -> [FilePath]-applyToFilepaths pa fs = map fn2fp $ withFilePaths (map fp2fn fs) (apply pa) +effectOnFilePaths :: (Apply p, ApplyState p ~ Tree)+ => p C(x y)+ -> [FilePath]+ -> [FilePath]+effectOnFilePaths p fps = fps' where+ (_, fps', _) = applyToFilePaths p Nothing fps+++applyToFilePaths :: (Apply p, ApplyState p ~ Tree)+ => p C(x y)+ -> Maybe [(FilePath, FilePath)]+ -> [FilePath]+ -> ([FilePath], [FilePath], [(FilePath, FilePath)])+applyToFilePaths pa ofpos fs = toFPs $ withFileNames ofnos fns (apply pa) where+ fns = map fp2fn fs+ ofnos = map (fp2fn *** fp2fn) <$> ofpos+ toFPs (affected, new, renames) =+ (map fn2fp affected, map fn2fp new, map (fn2fp *** fn2fp) renames)++ -- | Apply a patch to a 'Tree', yielding a new 'Tree'. applyToTree :: (Apply p, Functor m, Monad m, ApplyState p ~ Tree)- => p C(x y) -> Tree m -> m (Tree m)-applyToTree patch t = snd `fmap` virtualTreeMonad (apply patch) t+ => p C(x y)+ -> Tree m+ -> m (Tree m)+applyToTree patch t = snd <$> virtualTreeMonad (apply patch) t + applyToState :: forall p m C(x y). (Apply p, ApplyMonadTrans m (ApplyState p))- => p C(x y) -> (ApplyState p) m -> m ((ApplyState p) m)-applyToState patch t = snd `fmap` runApplyMonad (apply patch) t+ => p C(x y)+ -> (ApplyState p) m+ -> m ((ApplyState p) m)+applyToState patch t = snd <$> runApplyMonad (apply patch) t
src/Darcs/Patch/ApplyMonad.hs view
@@ -20,7 +20,7 @@ -- ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -- SOFTWARE.-module Darcs.Patch.ApplyMonad( ApplyMonad(..), ApplyMonadTrans(..), withFilePaths, withFiles, ToTree(..) ) where+module Darcs.Patch.ApplyMonad( ApplyMonad(..), ApplyMonadTrans(..), withFileNames, withFiles, ToTree(..) ) where import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL@@ -28,7 +28,7 @@ import qualified Storage.Hashed.Monad as HSM import Storage.Hashed.Tree ( Tree ) import ByteStringUtils( linesPS, unlinesPS )-import Darcs.Patch.FileName( FileName, movedirfilename, fn2fp )+import Darcs.Patch.FileName( FileName, movedirfilename, fn2fp, isParentOrEqOf ) import Storage.Hashed.AnchoredPath( floatPath, AnchoredPath ) import Control.Monad.State.Strict import Control.Monad.Identity( Identity )@@ -111,20 +111,45 @@ mRemoveDirectory = HSM.unlink . fn2ap mRemoveFile = HSM.unlink . fn2ap -type FilePathMonad = State [FileName]+-- Latest name, current original name.+type OrigFileNameOf = (FileName, FileName)+-- Touched files, new file list (after removes etc.) and rename details+type FilePathMonadState = ([FileName], [FileName], [OrigFileNameOf])+type FilePathMonad = State FilePathMonadState -withFilePaths :: [FileName] -> FilePathMonad a -> [FileName]-withFilePaths fps x = execState x fps+-- |trackOrigRename takes an old and new name and attempts to apply the mapping+-- to the OrigFileNameOf pair. If the old name is the most up-to-date name of+-- the file in question, the first element of the OFNO will match, otherwise if+-- the up-to-date name was originally old, the second element will match.+trackOrigRename :: FileName -> FileName -> OrigFileNameOf -> OrigFileNameOf+trackOrigRename old new pair@(latest, from)+ | old `isParentOrEqOf` latest = (latest, movedirfilename old new latest)+ | old `isParentOrEqOf` from = (latest, movedirfilename old new from)+ | otherwise = pair +-- |withFileNames takes a maybe list of existing rename-pairs, a list of+-- filenames and an action, and returns the resulting triple of affected files,+-- updated filename list and new rename details. If the rename-pairs are not+-- present, a new list is generated from the filesnames.+withFileNames :: (Maybe [OrigFileNameOf]) -> [FileName] -> FilePathMonad a+ -> FilePathMonadState+withFileNames mbofnos fps x = execState x ([], fps, ofnos) where+ ofnos = maybe (map (\x -> (x, x)) fps) id mbofnos+ instance ApplyMonad FilePathMonad Tree where type ApplyMonadBase FilePathMonad = Identity -- We can't check it actually is a directory here- mDoesDirectoryExist d = gets (d `elem`)- mCreateDirectory _ = return ()- mRemoveFile f = modify $ filter (/= f)- mRemoveDirectory f = modify $ filter (/= f)- mRename a b = modify $ map (movedirfilename a b)- mModifyFilePS _ _ = return ()+ mDoesDirectoryExist d = gets $ \(_, fs, _) -> d `elem` fs++ mCreateDirectory = mCreateFile+ mCreateFile f = modify $ \(ms, fs, rns) -> (f : ms, fs, rns)+ mRemoveFile f = modify $ \(ms, fs, rns) -> (f : ms, filter (/= f) fs, rns)+ mRemoveDirectory = mRemoveFile+ mRename a b =+ modify $ \(ms, fs, rns) -> ( a : b : ms+ , map (movedirfilename a b) fs+ , map (trackOrigRename a b) rns)+ mModifyFilePS f _ = mCreateFile f instance MonadProgress FilePathMonad where runProgressActions = silentlyRunProgressActions
src/Darcs/Patch/FileName.hs view
@@ -25,10 +25,11 @@ movedirfilename, encodeWhite, decodeWhite, (///),- breakup+ breakup, isParentOrEqOf ) where import Data.Char ( isSpace, chr, ord )+import Data.List ( stripPrefix ) import ByteStringUtils ( packStringToUTF8, unpackPSFromUTF8 ) import qualified Data.ByteString.Char8 as BC (unpack, pack) import qualified Data.ByteString as B (ByteString)@@ -142,13 +143,19 @@ (FN "")///b = normPath b a///b = normPath $ fp2fn $ fn2fp a ++ "/" ++ fn2fp b +isParentOrEqOf :: FileName -> FileName -> Bool+isParentOrEqOf fn1 fn2 = case stripPrefix (fn2fp fn1) (fn2fp fn2) of+ Just ('/' : _) -> True+ Just [] -> True+ _ -> False+ movedirfilename :: FileName -> FileName -> FileName -> FileName movedirfilename old new name =- if name' == old' then new- else if length name' > length old' &&- take (length old'+1) name' == old'++"/"- then fp2fn ("./"++new'++drop (length old') name')- else name- where old' = fn2fp $ normPath old- new' = fn2fp $ normPath new- name' = fn2fp $ normPath name+ if name' == old'+ then new+ else case stripPrefix old' name' of+ Just rest@('/':_) -> fp2fn $ "./" ++ new' ++ rest+ _ -> name+ where old' = fn2fp $ normPath old+ new' = fn2fp $ normPath new+ name' = fn2fp $ normPath name
src/Darcs/Patch/Format.hs view
@@ -5,27 +5,28 @@ #include "gadts.h" --- |Showing and reading lists of patches This class allows us to control how--- lists of patches are formatted on disk. For legacy reasons V1 patches--- have their own special treatment (see 'ListFormat'). Other patch types--- use the default format which just puts them in a sequence without separators--- or any prelude/epilogue.+-- | Showing and reading lists of patches This class allows us to control how+-- lists of patches are formatted on disk. For legacy reasons V1 patches have+-- their own special treatment (see 'ListFormat'). Other patch types use the+-- default format which just puts them in a sequence without separators or any+-- prelude/epilogue. ----- This means that 'FL (FL p)' etc would be ambiguous, so there are--- no instances for 'FL p' or other list types.+-- This means that 'FL (FL p)' etc would be ambiguous, so there are no instances+-- for 'FL p' or other list types. class PatchListFormat p where patchListFormat :: ListFormat p patchListFormat = ListFormatDefault --- | This type is used to tweak the way that lists of 'p' are shown for a--- given 'Patch' type 'p'. It is needed to maintain backwards compatibility--- for V1 and V2 patches.+-- | This type is used to tweak the way that lists of 'p' are shown for a given+-- 'Patch' type 'p'. It is needed to maintain backwards compatibility for V1 and+-- V2 patches. data ListFormat (p :: PATCHKIND)- = ListFormatDefault -- ^Show and read lists without braces.- | ListFormatV1 -- ^Show lists with a single layer of braces around the outside,+ = ListFormatDefault -- ^ Show and read lists without braces.+ | ListFormatV1 -- ^ Show lists with a single layer of braces around the outside, -- except for singletons which have no braces. -- Read with arbitrary nested braces and parens and flatten them out.- | ListFormatV2 -- ^Show lists without braces+ | ListFormatV2 -- ^ Show lists without braces -- Read with arbitrary nested parens and flatten them out. -data FileNameFormat = OldFormat | NewFormat+data FileNameFormat = OldFormat+ | NewFormat
src/Darcs/Patch/Info.hs view
@@ -333,6 +333,7 @@ -- > ] -- -- note that below I assume the name has no newline in it.+-- See 'readPatchInfo' for the inverse operation. showPatchInfo :: PatchInfo -> Doc showPatchInfo pi = blueText "[" <> packedString (_piName pi)@@ -344,6 +345,16 @@ where mul [] = text "\n" mul (s:ss) = text "\n " <> packedString s <> mul ss +-- |Parser for 'PatchInfo' as stored in patch bundles and inventory files,+-- for example:+--+-- > [Document the foo interface+-- > John Doe <john.doe@example.com>**20110615084241+-- > Ignore-this: 85b94f67d377c4ab671101266ef9c229+-- > Nobody knows what a 'foo' is, so describe it.+-- > ]+--+-- See 'showPatchInfo' for the inverse operation. readPatchInfo :: ParserM m => m (PatchInfo) readPatchInfo = do skipSpace
src/Darcs/Patch/Merge.hs view
@@ -1,36 +1,53 @@+#include "gadts.h"++-- |+-- Module : Darcs.Patch.Merge+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable+ module Darcs.Patch.Merge- ( Merge(..), mergeFL )- where+ (+ Merge(..)+ , mergeFL+ ) where -import Darcs.Patch.Commute ( Commute ) +import Data.Maybe ( fromJust )++import Darcs.Patch.Commute ( Commute ) import Darcs.Witnesses.Ordered ( (:\/:)(..), (:/\:)(..), FL(..), RL, reverseFL, reverseRL ) -import Data.Maybe ( fromJust ) -#include "gadts.h"- -- | Things that can always be merged class Commute p => Merge p where- merge :: (p :\/: p) C(x y) -> (p :/\: p) C(x y)+ merge :: (p :\/: p) C(x y)+ -> (p :/\: p) C(x y) + instance Merge p => Merge (FL p) where merge (NilFL :\/: x) = x :/\: NilFL merge (x :\/: NilFL) = NilFL :/\: x- merge ((x:>:xs) :\/: ys) = fromJust $ do ys' :/\: x' <- return $ mergeFL (x :\/: ys)- xs' :/\: ys'' <- return $ merge (ys' :\/: xs)- return (ys'' :/\: (x' :>: xs'))+ merge ((x:>:xs) :\/: ys) = fromJust $ do+ ys' :/\: x' <- return $ mergeFL (x :\/: ys)+ xs' :/\: ys'' <- return $ merge (ys' :\/: xs)+ return (ys'' :/\: (x' :>: xs')) -mergeFL :: Merge p => (p :\/: FL p) C(x y) -> (FL p :/\: p) C(x y)-mergeFL (p :\/: NilFL) = NilFL :/\: p-mergeFL (p :\/: (x :>: xs)) = fromJust $ do x' :/\: p' <- return $ merge (p :\/: x)- xs' :/\: p'' <- return $ mergeFL (p' :\/: xs)- return ((x' :>: xs') :/\: p'') instance Merge p => Merge (RL p) where merge (x :\/: y) = case merge (reverseRL x :\/: reverseRL y) of (ry' :/\: rx') -> reverseFL ry' :/\: reverseFL rx'+++mergeFL :: Merge p+ => (p :\/: FL p) C(x y)+ -> (FL p :/\: p) C(x y)+mergeFL (p :\/: NilFL) = NilFL :/\: p+mergeFL (p :\/: (x :>: xs)) = fromJust $ do+ x' :/\: p' <- return $ merge (p :\/: x)+ xs' :/\: p'' <- return $ mergeFL (p' :\/: xs)+ return ((x' :>: xs') :/\: p'')
src/Darcs/Patch/Named.hs view
@@ -107,10 +107,6 @@ instance RepairToFL p => Repair (Named p) where applyAndTryToFix (NamedP n d p) = mapMaybeSnd (NamedP n d) `fmap` applyAndTryToFix p -infopatch :: Patchy p => PatchInfo -> FL p C(x y) -> Named p C(x y)-adddeps :: Named p C(x y) -> [PatchInfo] -> Named p C(x y)-getdeps :: Named p C(x y) -> [PatchInfo]- namepatch :: Patchy p => String -> String -> String -> [String] -> FL p C(x y) -> IO (Named p C(x y)) namepatch date name author desc p | '\n' `elem` name = error "Patch names cannot contain newlines."@@ -120,8 +116,13 @@ anonymous :: Patchy p => FL p C(x y) -> IO (Named p C(x y)) anonymous p = namepatch "today" "anonymous" "unknown" ["anonymous"] p +infopatch :: Patchy p => PatchInfo -> FL p C(x y) -> Named p C(x y) infopatch pi p = NamedP pi [] p++adddeps :: Named p C(x y) -> [PatchInfo] -> Named p C(x y) adddeps (NamedP pi _ p) ds = NamedP pi ds p++getdeps :: Named p C(x y) -> [PatchInfo] getdeps (NamedP _ ds _) = ds patch2patchinfo :: Named p C(x y) -> PatchInfo
src/Darcs/Patch/Prim/V3/Apply.hs view
@@ -12,7 +12,6 @@ import Control.Monad.State( StateT, runStateT, gets, lift, put ) import qualified Data.Map as M-import Data.List( (\\), sort ) -- import Darcs.Patch.ApplyMonad ( ApplyMonad(..) )
src/Darcs/Patch/Prim/V3/Core.hs view
@@ -30,8 +30,7 @@ import qualified Data.ByteString as BS -import Darcs.Witnesses.Eq ( MyEq(..), EqCheck(..) )-import Darcs.Witnesses.Unsafe ( unsafeCoerceP )+import Darcs.Witnesses.Eq ( MyEq(..) ) import Darcs.Patch.FileHunk( IsHunk(..) ) import Darcs.Patch.Invert ( Invert(..) ) import Darcs.Patch.Inspect ( PatchInspect(..) )@@ -47,11 +46,11 @@ invertHunk (Hunk off old new) = Hunk off new old hunkEdit :: Hunk C(x y) -> BS.ByteString -> BS.ByteString-hunkEdit hunk@(Hunk off old new) bs = case splice bs (off) (off + BS.length old) of+hunkEdit (Hunk off old new) bs = case splice bs (off) (off + BS.length old) of x | x == old -> BS.concat [ BS.take off bs, new, BS.drop (off + BS.length old) bs ] | otherwise -> error $ "error applying hunk: " ++ show off ++ " " ++ show old ++ " " ++ show new ++ " to " ++ show bs- where splice bs x y = BS.drop x $ BS.take y bs+ where splice bs' x y = BS.drop x $ BS.take y bs' instance MyEq Hunk where unsafeCompare (Hunk i x y) (Hunk i' x' y') = i == i' && x == x' && y == y'@@ -79,9 +78,13 @@ -- TODO: PrimClassify doesn't make sense for V3 prims instance PrimClassify Prim where primIsAddfile _ = False+ primIsRmfile _ = False primIsAdddir _ = False+ primIsRmdir _ = False primIsHunk _ = False+ primIsMove _ = False primIsBinary _ = False+ primIsTokReplace _ = False primIsSetpref _ = False is_filepatch _ = Nothing @@ -107,6 +110,7 @@ invert (TextHunk x h) = TextHunk x $ invertHunk h invert (Manifest x y) = Demanifest x y invert (Demanifest x y) = Manifest x y+ invert (Move x y z) = Move x z y invert Identity = Identity instance PatchInspect Prim where@@ -115,7 +119,7 @@ listTouchedFiles _ = [] -- TODO (used for --match 'hunk ...', presumably)- hunkMatches f _ = False+ hunkMatches _ _ = False instance MyEq Prim where unsafeCompare (BinaryHunk a b) (BinaryHunk c d) = a == c && b `unsafeCompare` d
src/Darcs/Patch/Prim/V3/Details.hs view
@@ -3,14 +3,8 @@ () where -import Prelude hiding ( pi ) import Darcs.Patch.Prim.Class ( PrimDetails(..) ) import Darcs.Patch.Prim.V3.Core ( Prim(..) )-import Darcs.Patch.SummaryData ( SummDetail(..), SummOp(..) )--import qualified Data.ByteString as B ( ByteString, concat )-import qualified Data.ByteString.Char8 as BC ( pack, split )-import Data.Maybe ( catMaybes ) #include "gadts.h"
src/Darcs/Patch/Prim/V3/ObjectMap.hs view
@@ -34,8 +34,6 @@ data Object (m :: * -> *) = Directory DirContent | Blob (m BS.ByteString) !Hash -data ObjectType = TText | TBinary | TDirectory- data ObjectMap (m :: * -> *) = ObjectMap { getObject :: UUID -> m (Maybe (Object m)) , putObject :: UUID -> Object m -> m (ObjectMap m) , listObjects :: m [UUID]
src/Darcs/Patch/Prim/V3/Read.hs view
@@ -9,10 +9,10 @@ import Darcs.Witnesses.Sealed( seal ) import Control.Applicative ( (<$>) )-import Control.Monad ( liftM )+import Control.Monad ( liftM, liftM2 ) import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as BC-import Data.Char ( isSpace, ord, chr )+import Data.Char ( chr ) instance PrimRead Prim where readPrim _ = do skipSpace@@ -23,23 +23,19 @@ manifest "manifest" Manifest, manifest "demanifest" Demanifest ] - where manifest kind ctor = do id <- patch kind- loc <- location- return $ ctor id loc+ where manifest kind ctor = liftM2 ctor (patch kind) location identity = lexString "identity" >> return Identity patch x = string x >> uuid uuid = UUID <$> myLex' filename = encoded encoded = decodeWhite <$> myLex' hunktext = skipSpace >> choice [ string "." >> encoded, string "!" >> return B.empty ]- location = do id <- uuid- name <- filename- return (id, name)- hunk kind ctor = do id <- patch kind+ location = liftM2 (,) uuid filename+ hunk kind ctor = do uid <- patch kind offset <- int old <- hunktext new <- hunktext- return $ ctor id (Hunk offset old new)+ return $ ctor uid (Hunk offset old new) instance ReadPatch Prim where readPatch' = readPrim undefined
src/Darcs/Patch/Prim/V3/Show.hs view
@@ -6,25 +6,18 @@ import Prelude hiding ( pi ) -import ByteStringUtils ( fromPS2Hex )-import Data.Char ( isSpace, ord, chr )+import Data.Char ( isSpace, ord ) import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as BC -import Darcs.Patch.FileHunk ( FileHunk(..), IsHunk(..), showFileHunk )-import Darcs.Patch.FileName ( FileName ) import Darcs.Patch.Format ( PatchListFormat, FileNameFormat(..) )-import Darcs.Patch.Show ( ShowPatchBasic(..), ShowPatch(..), formatFileName )+import Darcs.Patch.Show ( ShowPatchBasic(..), ShowPatch(..) ) import Darcs.Patch.Summary ( plainSummaryPrim, plainSummaryPrims )-import Darcs.Patch.Viewing ( showContextHunk ) import Darcs.Patch.Prim.Class ( PrimShow(..) ) import Darcs.Patch.Prim.V3.Core ( Prim(..), Hunk(..), UUID(..) ) import Darcs.Patch.Prim.V3.Details ()-import Darcs.Witnesses.Show ( appPrec, Show1(..), Show2(..), ShowDict(..) )-import Printer ( Doc, renderString, vcat,- text, userchunk, packedString, blueText,- ($$), (<+>), (<>),- )+import Darcs.Witnesses.Show ( Show1(..), Show2(..), ShowDict(..) )+import Printer ( renderString, text, packedString, blueText, (<+>), (<>), Doc ) #include "gadts.h" @@ -56,21 +49,23 @@ showPrim _ (Demanifest f (d,p)) = showManifest "demanifest" d f p showPrim _ Identity = blueText "identity" +showManifest :: String -> UUID -> UUID -> BC.ByteString -> Doc showManifest txt dir file path = blueText txt <+> formatUUID file <+> formatUUID dir <+> packedString (encodeWhite path) -showHunk txt id (Hunk off old new) = blueText txt <+>- formatUUID id <+>- text (show off) <+>- hunktext old <+>- hunktext new+showHunk :: String -> UUID -> Hunk C(x y) -> Doc+showHunk txt uid (Hunk off old new) = blueText txt <+>+ formatUUID uid <+>+ text (show off) <+>+ hunktext old <+>+ hunktext new where hunktext bit | B.null bit = text "!" | otherwise = text "." <> packedString (encodeWhite bit) +formatUUID :: UUID -> Doc formatUUID (UUID x) = packedString x- -- XXX a bytestring version of encodeWhite from Darcs.FileName encodeWhite :: B.ByteString -> B.ByteString
src/Darcs/Patch/Set.hs view
@@ -34,32 +34,43 @@ type SealedPatchSet p C(start) = Sealed ((PatchSet p) C(start)) -data PatchSet p C(start x) where- PatchSet :: RL (PatchInfoAnd p) C(y x) -> RL (Tagged p) C(start y) -> PatchSet p C(start x)+-- A PatchSet is a list of patches since the last tag, and a list of tagged+-- patch lists that form a repo's history.+data PatchSet p C(start y) where+ PatchSet :: RL (PatchInfoAnd p) C(x y) -> RL (Tagged p) C(start x) -> PatchSet p C(start y) -data Tagged p C(x2 z) where+-- A Tagged is a Tag, the hash of the 'previous' inventory (if it exists) and+-- the list of patches since that previous inventory.+data Tagged p C(x z) where Tagged :: PatchInfoAnd p C(y z) -> Maybe String- -> RL (PatchInfoAnd p) C(x3 y) -> Tagged p C(x3 z)+ -> RL (PatchInfoAnd p) C(x y) -> Tagged p C(x z) -newset2RL :: PatchSet p C(start x4) -> RL (PatchInfoAnd p) C(start x4)+-- |newset2RL takes a PatchSet and returns an equivalent, linear RL of patches.+newset2RL :: PatchSet p C(start x) -> RL (PatchInfoAnd p) C(start x) newset2RL (PatchSet ps ts) = ps +<+ concatRL (mapRL_RL ts2rl ts)- where ts2rl :: Tagged p C(x5 y) -> RL (PatchInfoAnd p) C(x5 y)+ where ts2rl :: Tagged p C(y z) -> RL (PatchInfoAnd p) C(y z) ts2rl (Tagged t _ ps2) = t :<: ps2 -newset2FL :: PatchSet p C(start x6) -> FL (PatchInfoAnd p) C(start x6)+-- |newset2FL takes a PatchSet and returns an equivalent, linear FL of patches.+newset2FL :: PatchSet p C(start x) -> FL (PatchInfoAnd p) C(start x) newset2FL = reverseRL . newset2RL +-- |appendPSFL takes a PatchSet and a FL of patches that 'follow' the PatchSet,+-- and concatenates the patches into the PatchSet. appendPSFL :: PatchSet p C(start x) -> FL (PatchInfoAnd p) C(x y) -> PatchSet p C(start y) appendPSFL (PatchSet ps ts) newps = PatchSet (reverseFL newps +<+ ps) ts -progressPatchSet :: String -> PatchSet p C(start x7) -> PatchSet p C(start x7)+-- |Runs a progress action for each tag and patch in a given PatchSet, using+-- the passed progress message. Does not alter the PatchSet.+progressPatchSet :: String -> PatchSet p C(start x) -> PatchSet p C(start x) progressPatchSet k (PatchSet ps0 ts0) = PatchSet (mapRL_RL prog ps0) $ mapRL_RL pts ts0 where prog = progress k- pts :: Tagged p C(x8 y) -> Tagged p C(x8 y)+ pts :: Tagged p C(y z) -> Tagged p C(y z) pts (Tagged t h ps) = Tagged (prog t) h (mapRL_RL prog ps) -tags :: PatchSet p C(start x13) -> [PatchInfo]+-- |tags returns the PatchInfos corresponding to the tags of a given PatchSet.+tags :: PatchSet p C(start x) -> [PatchInfo] tags (PatchSet _ ts) = mapRL f ts- where f :: Tagged p C(x14 y) -> PatchInfo+ where f :: Tagged p C(y z) -> PatchInfo f (Tagged t _ _) = info t
src/Darcs/Patch/Show.hs view
@@ -59,7 +59,7 @@ -- unified context is not included in patch representation, this -- requires access to the tree. showContextPatch :: (Monad m, ApplyMonadTrans m (ApplyState p),- ApplyMonad m (ApplyState p), Monad m)+ ApplyMonad m (ApplyState p)) => p C(x y) -> m Doc showContextPatch p = return $ showPatch p description :: p C(x y) -> Doc
src/Darcs/Patch/Split.hs view
@@ -111,7 +111,7 @@ doPrimSplit :: PrimPatch prim => prim C(x y) -> Maybe (B.ByteString, B.ByteString -> Maybe (FL prim C(x y)))-doPrimSplit = doPrimSplit_ explanation+doPrimSplit = doPrimSplit_ True explanation where explanation = map BC.pack [ "Interactive hunk edit:"@@ -124,7 +124,7 @@ , "" ] -doPrimSplit_ helptext (isHunk -> Just (FileHunk fn n before after))+doPrimSplit_ edit_before_part helptext (isHunk -> Just (FileHunk fn n before after)) = Just (B.concat $ intersperse (BC.pack "\n") $ concat [ helptext , [mkSep " BEFORE (reference) =========================="]@@ -136,8 +136,11 @@ \bs -> do let ls = BC.split '\n' bs (_, ls2) <- breakSep ls -- before (before', ls3) <- breakSep ls2 -- after 1- (after', _) <- breakSep ls3 -- after 2- return (hunk before before' +>+ hunk before' after' +>+ hunk after' after))+ (after', _) <- breakSep ls3 -- after+ return $+ if edit_before_part+ then hunk before before' +>+ hunk before' after' +>+ hunk after' after+ else hunk before after' +>+ hunk after' after) where sep = BC.pack "==========================" hunk :: PrimPatch prim => [B.ByteString] -> [B.ByteString] -> FL prim C(a b) hunk b a = canonize (primFromHunk (FileHunk fn n b a))@@ -145,7 +148,7 @@ breakSep xs = case break (sep `BC.isPrefixOf`) xs of (_, []) -> Nothing (ys, _:zs) -> Just (ys, zs)-doPrimSplit_ _ _ = Nothing+doPrimSplit_ _ _ _ = Nothing -- |Split a primitive hunk patch up -- by allowing the user to edit both the before and after lines, then insert fixup patches@@ -156,7 +159,7 @@ doReversePrimSplit :: PrimPatch prim => prim C(x y) -> Maybe (B.ByteString, B.ByteString -> Maybe (FL prim C(x y))) doReversePrimSplit prim = do- (text, parser) <- doPrimSplit_ reverseExplanation (invert prim)+ (text, parser) <- doPrimSplit_ False reverseExplanation (invert prim) let parser' p = do patch <- parser p return . reverseRL $ invertFL patch@@ -167,6 +170,7 @@ , " - Edit the section marked 'AFTER' (representing the state to which you'll revert)" , " - Arbitrary editing is supported" , " - Your working copy will be returned to the 'AFTER' state"+ , " - Do not touch the 'BEFORE' section" , " - Hints:" , " - To revert only a part of a text addition, delete the part you want to get rid of" , " - To revert only a part of a removal, copy back the part you want to retain"
src/Darcs/Patch/TokenReplace.hs view
@@ -1,6 +1,9 @@-module Darcs.Patch.TokenReplace - ( tryTokInternal, forceTokReplace ) - where +module Darcs.Patch.TokenReplace+ (+ tryTokInternal+ , forceTokReplace+ )+ where import Darcs.Patch.RegChars ( regChars )
src/Darcs/Patch/TouchesFiles.hs view
@@ -24,14 +24,14 @@ deselectNotTouching, selectNotTouching, ) where import Control.Applicative ( (<$>) )-import Data.List ( sort, isSuffixOf )+import Data.List ( isSuffixOf, nub ) import Darcs.Patch.Choices ( PatchChoices, Tag, TaggedPatch, patchChoices, tag, getChoices, forceFirsts, forceLasts, tpPatch, )-import Darcs.Patch ( Patchy, listTouchedFiles, invert )-import Darcs.Patch.Apply ( ApplyState, applyToFilepaths )+import Darcs.Patch ( Patchy, invert )+import Darcs.Patch.Apply ( ApplyState, applyToFilePaths, effectOnFilePaths ) import Darcs.Witnesses.Ordered ( FL(..), (:>)(..), mapFL_FL, (+>+) ) import Darcs.Witnesses.Sealed ( Sealed, seal ) import Storage.Hashed.Tree( Tree )@@ -42,7 +42,7 @@ selectTouching (Just files) pc = forceFirsts xs pc where ct :: (Patchy p, ApplyState p ~ Tree) => [FilePath] -> FL (TaggedPatch p) C(x y) -> [Tag] ct _ NilFL = []- ct fs (tp:>:tps) = case lookTouch fs (tpPatch tp) of+ ct fs (tp:>:tps) = case lookTouchOnlyEffect fs (tpPatch tp) of (True, fs') -> tag tp:ct fs' tps (False, fs') -> ct fs' tps xs = case getChoices pc of@@ -54,7 +54,7 @@ deselectNotTouching (Just files) pc = forceLasts xs pc where ct :: (Patchy p, ApplyState p ~ Tree) => [FilePath] -> FL (TaggedPatch p) C(x y) -> [Tag] ct _ NilFL = []- ct fs (tp:>:tps) = case lookTouch fs (tpPatch tp) of+ ct fs (tp:>:tps) = case lookTouchOnlyEffect fs (tpPatch tp) of (True, fs') -> ct fs' tps (False, fs') -> tag tp:ct fs' tps xs = case getChoices pc of@@ -66,7 +66,7 @@ selectNotTouching (Just files) pc = forceFirsts xs pc where ct :: (Patchy p, ApplyState p ~ Tree) => [FilePath] -> FL (TaggedPatch p) C(x y) -> [Tag] ct _ NilFL = []- ct fs (tp:>:tps) = case lookTouch fs (tpPatch tp) of+ ct fs (tp:>:tps) = case lookTouchOnlyEffect fs (tpPatch tp) of (True, fs') -> ct fs' tps (False, fs') -> tag tp:ct fs' tps xs = case getChoices pc of@@ -86,18 +86,29 @@ choosePreTouching :: (Patchy p, ApplyState p ~ Tree) => Maybe [FilePath] -> FL p C(x y) -> Sealed (FL p C(x))-choosePreTouching files patch = chooseTouching (applyToFilepaths- (invert patch) <$> files) patch+choosePreTouching files patch = chooseTouching filesBeforePatch patch where+ filesBeforePatch = effectOnFilePaths (invert patch) <$> files -lookTouch :: (Patchy p, ApplyState p ~ Tree) => [FilePath] -> p C(x y) -> (Bool, [FilePath])-lookTouch fs p = (any (\tf -> any (affects tf) fs) (listTouchedFiles p)- || fs' /= fs, fs')- where affects :: FilePath -> FilePath -> Bool- affects touched f = touched == f- || touched `isSubPathOf` f- || f `isSubPathOf` touched+lookTouchOnlyEffect :: (Patchy p, ApplyState p ~ Tree) => [FilePath] -> p C(x y)+ -> (Bool, [FilePath])+lookTouchOnlyEffect fs p = (wasTouched, fs') where+ (wasTouched, _, fs', _) = lookTouch Nothing fs p+++lookTouch :: (Patchy p, ApplyState p ~ Tree) => Maybe [(FilePath, FilePath)]+ -> [FilePath] -> p C(x y)+ -> (Bool, [FilePath], [FilePath], [(FilePath, FilePath)])+lookTouch renames fs p = (anyTouched, touchedFs, fs', renames')+ where+ touchedFs = nub . concatMap fsAffectedBy $ affected+ fsAffectedBy af = filter (affectedBy af) fs+ anyTouched = length touchedFs > 0+ affectedBy :: FilePath -> FilePath -> Bool+ touched `affectedBy` f = touched == f+ || touched `isSubPathOf` f+ || f `isSubPathOf` touched isSubPathOf :: FilePath -> FilePath -> Bool- isSubPathOf sub path = case splitAt (length sub) path of- (path', '/':_) -> path' == sub+ path `isSubPathOf` parent = case splitAt (length parent) path of+ (path', '/':_) -> path' == parent _ -> False- fs' = sort $ applyToFilepaths p fs+ (affected, fs', renames') = applyToFilePaths p renames fs
src/Darcs/Patch/V2/Non.hs view
@@ -20,19 +20,30 @@ #include "gadts.h" --- |'NonPatch' and 'Non' patches are patches that store a context as a--- sequence of patches. See "Darcs.Patch.Real" for example usage. module Darcs.Patch.V2.Non- ( Non(..), Nonable(..), unNon,- showNon, readNon, showNons, readNons,- add, addP, remP, addPs, remPs, remNons,- (*>), (>*), (*>>), (>>*),- propAdjustTwice ) where+ ( Non(..)+ , Nonable(..)+ , unNon+ , showNon+ , showNons+ , readNon+ , readNons+ , commutePrimsOrAddToCtx+ , commuteOrAddToCtx+ , commuteOrRemFromCtx+ , commuteOrAddToCtxRL+ , commuteOrRemFromCtxFL+ , remNons+ , (*>)+ , (>*)+ , (*>>)+ , (>>*)+ ) where import Prelude hiding ( rem ) import Data.List ( delete ) import Control.Monad ( liftM, mzero )-import Darcs.Patch.Commute ( commuteFLorComplain )+import Darcs.Patch.Commute ( commuteFL ) import Darcs.Patch.Effect ( Effect(..) ) import Darcs.Patch.Format ( PatchListFormat, FileNameFormat(..) ) import Darcs.Patch.Invert ( Invert, invertFL, invertRL )@@ -56,41 +67,57 @@ import Printer ( Doc, empty, vcat, hiddenPrefix, blueText, redText, ($$) ) import qualified Data.ByteString.Char8 as BC ( pack, singleton, ByteString ) ---import Darcs.ColorPrinter ( traceDoc )---import Printer ( greenText )+-- |A 'Non' stores a context with a 'Prim' patch. It is a patch whose effect+-- isn't visible - a Non-affecting patch.+data Non p C(x) where+ Non :: FL p C(x y) -> PrimOf p C(y z) -> Non p C(x) -showNons :: (ShowPatchBasic p, PatchListFormat p, PrimPatchBase p) => [Non p C(x)] -> Doc-showNons [] = empty-showNons xs = blueText "{{" $$ vcat (map showNon xs) $$ blueText "}}"+-- |unNon converts a Non into a FL of its context followed by the primitive+-- patch.+unNon :: FromPrim p => Non p C(x) -> Sealed (FL p C(x))+unNon (Non c x) = Sealed (c +>+ fromPrim x :>: NilFL) -oBracket :: BC.ByteString-oBracket = BC.pack "{{"+instance (Show2 p, Show2 (PrimOf p)) => Show (Non p C(x)) where+ showsPrec d (Non cs p) = showParen (d > appPrec) $ showString "Non " .+ showsPrec2 (appPrec + 1) cs . showString " " .+ showsPrec2 (appPrec + 1) p -cBracket :: BC.ByteString-cBracket = BC.pack "}}"+instance (Show2 p, Show2 (PrimOf p)) => Show1 (Non p) where+ showDict1 = ShowDictClass -showNon :: (ShowPatchBasic p, PatchListFormat p, PrimPatchBase p) => Non p C(x) -> Doc+-- |showNons creates a Doc representing a list of Nons.+showNons :: (ShowPatchBasic p, PatchListFormat p, PrimPatchBase p)+ => [Non p C(x)] -> Doc+showNons [] = empty+showNons xs = blueText "{{" $$ vcat (map showNon xs) $$ blueText "}}"++-- |showNon creates a Doc representing a Non.+showNon :: (ShowPatchBasic p, PatchListFormat p, PrimPatchBase p) => Non p C(x)+ -> Doc showNon (Non c p) = hiddenPrefix "|" (showPatch c) $$ hiddenPrefix "|" (blueText ":") $$ showPrim NewFormat p -readNons :: (ReadPatch p, PatchListFormat p, PrimPatchBase p, ParserM m) => m [Non p C(x)]-readNons = peekfor oBracket rns (return [])- where rns = peekfor cBracket (return []) $- do Sealed ps <- readPatch'- lexChar ':'- Sealed p <- readPrim NewFormat- (Non ps p :) `liftM` rns+-- |readNons is a parser that attempts to read a list of Nons.+readNons :: (ReadPatch p, PatchListFormat p, PrimPatchBase p, ParserM m)+ => m [Non p C(x)]+readNons = peekfor (BC.pack "{{") rns (return [])+ where rns = peekfor (BC.pack "}}") (return []) $+ do Sealed ps <- readPatch'+ lexChar ':'+ Sealed p <- readPrim NewFormat+ (Non ps p :) `liftM` rns -readNon :: (ReadPatch p, PatchListFormat p, PrimPatchBase p, ParserM m) => m (Non p C(x))+-- |readNon is a parser that attempts to read a single Non.+readNon :: (ReadPatch p, PatchListFormat p, PrimPatchBase p, ParserM m)+ => m (Non p C(x)) readNon = do Sealed ps <- readPatch'- peekfor colon (do Sealed p <- readPrim NewFormat- return $ Non ps p)- mzero--colon :: BC.ByteString-colon = BC.singleton ':'+ let doReadPrim = do Sealed p <- readPrim NewFormat+ return $ Non ps p+ peekfor (BC.singleton ':') doReadPrim mzero +-- |Nons are equal if their context patches are equal, and they have an equal+-- prim patch. instance (Commute p, MyEq p, MyEq (PrimOf p)) => Eq (Non p C(x)) where Non (cx :: FL p C(x y1)) (x :: PrimOf p C(y1 z1)) == Non (cy :: FL p C(x y2)) (y :: PrimOf p C(y2 z2)) =@@ -100,51 +127,35 @@ NotEq -> False NotEq -> False --- | 'Non' stores a context with a 'Prim' patch.-data Non p C(x) where- Non :: FL p C(a x) -> PrimOf p C(x y) -> Non p C(a)---- | Return as a list the context followed by the primitive patch.-unNon :: FromPrim p => Non p C(x) -> Sealed (FL p C(x))-unNon (Non c x) = Sealed (c +>+ fromPrim x :>: NilFL)-+-- |Nonable represents the class of patches that can be turned into a Non. class Nonable p where non :: p C(x y) -> Non p C(x) --- | 'addP' @x cy@ tries to commute @x@ past @cy@ and always returns some--- variant @cy'@. -- commutation suceeds, the variant is just--- straightforwardly the commuted versian. If commutation fails, the variant+-- |'commuteOrAddToCtx' @x cy@ tries to commute @x@ past @cy@ and always+-- returns some variant @cy'@. If commutation suceeds, the variant is just+-- straightforwardly the commuted version. If commutation fails, the variant -- consists of @x@ prepended to the context of @cy@.-addP :: (Patchy p, ToFromPrim p) => p C(x y) -> Non p C(y) -> Non p C(x)-addP p n | Just n' <- p >* n = n'-addP p (Non c x) = Non (p:>:c) x+commuteOrAddToCtx :: (Patchy p, ToFromPrim p) => p C(x y) -> Non p C(y)+ -> Non p C(x)+commuteOrAddToCtx p n | Just n' <- p >* n = n'+commuteOrAddToCtx p (Non c x) = Non (p:>:c) x --- | 'addPs' @xs cy@ commutes as many patches of @xs@ past @cy@ as--- possible, stopping at the first patch that fails to commute.--- Note the fact @xs@ is a 'RL'------ Suppose we have+-- | 'commuteOrAddToCtxRL' @xs cy@ commutes as many patches of @xs@ past @cy@+-- as possible, adding any that don't commute to the context of cy. Suppose we+-- have ----- > x1 x2 x3 [c1 c2 y]+-- > x1 x2 x3 [c1 c2 y] ----- and that in our example @c1@ fails to commute past @x1@, this--- function would commute down to+-- and that in our example @x1@ fails to commute past @c1@, this function+-- would commute down to ----- > x1 [c1'' c2'' y''] x2' x3'+-- > x1 [c1'' c2'' y''] x2' x3' ----- and return @[x1 c1'' c2'' y'']@-addPs :: (Patchy p, ToFromPrim p) => RL p C(x y) -> Non p C(y) -> Non p C(x)-addPs NilRL n = n-addPs (p:<:ps) n = addPs ps $ addP p n--add :: (WL l, Patchy p, ToFromPrim p) => l (PrimOf p) C(x y) -> Non p C(y) -> Non p C(x)-add q = addPs (mapRL_RL fromPrim $ toRL q)---- remNons really only works right if the relevant nons are conflicting...-remNons :: (Nonable p, Effect p, Patchy p, ToFromPrim p, PrimPatchBase p, MyEq (PrimOf p)) => [Non p C(x)] -> Non p C(x) -> Non p C(x)-remNons ns (Non c x) = case remNonHelper ns c of- NilFL :> c' -> Non c' x- _ -> Non c x+-- and return @[x1 c1'' c2'' y'']@+commuteOrAddToCtxRL :: (Patchy p, ToFromPrim p) => RL p C(x y) -> Non p C(y)+ -> Non p C(x)+commuteOrAddToCtxRL NilRL n = n+commuteOrAddToCtxRL (p:<:ps) n = commuteOrAddToCtxRL ps $ commuteOrAddToCtx p n -- |abstract over 'FL'/'RL' class WL l where@@ -162,66 +173,94 @@ toRL = id invertWL = reverseFL . invertRL -remNonHelper :: (Nonable p, Effect p, Patchy p, ToFromPrim p, PrimPatchBase p, MyEq (PrimOf p)) => [Non p C(x)] -> FL p C(x y)- -> (FL (PrimOf p) :> FL p) C(x y)-remNonHelper [] x = NilFL :> x-remNonHelper ns (c:>:cs)- | non c `elem` ns = case remNonHelper (map (addP $ invert c) $ delete (non c) ns) cs of- a :> z -> sortCoalesceFL (effect c+>+a) :> z- | otherwise = case commuteWhatWeCanFL (c :> cs) of- b :> c' :> d ->- case remNonHelper ns b of- a :> b' -> a :> (b'+>+c':>:d)-remNonHelper _ NilFL = NilFL :> NilFL--remP :: (Patchy p, ToFromPrim p) => p C(x y) -> Non p C(x) -> Maybe (Non p C(y))-remP p n | Just n' <- n *> p = Just n'-remP p (Non pc x) = do c <- removeFL p pc- return (Non c x)+-- |commutePrimsOrAddToCtx takes a WL of prims and attempts to commute them+-- past a Non.+commutePrimsOrAddToCtx :: (WL l, Patchy p, ToFromPrim p) => l (PrimOf p) C(x y)+ -> Non p C(y) -> Non p C(x)+commutePrimsOrAddToCtx q = commuteOrAddToCtxRL (mapRL_RL fromPrim $ toRL q) -remPs :: (Patchy p, ToFromPrim p) => FL p C(x y) -> Non p C(x) -> Maybe (Non p C(y))-remPs NilFL n = Just n-remPs (p:>:ps) n = remP p n >>= remPs ps+-- TODO: Figure out what remNons is for; it's is only used in one place - when+-- commuting two Conflictors:+--+-- > commute (Conflictor a1 n1 p1 :> Conflictor a2 n2 p2)+-- > ...+-- > a1' = map (commutePrimsOrAddToCtx n2) a1+-- > p2ooo = remNons a1' p2+-- > n2n1 = n2 +>+ n1+-- > n1' :> n2' <- return $ filterConflictsFL p2ooo n2n1+--+-- which appears to be munging the not-yet-undone FLs in the Conflictors. a1'+-- will be the list of Nons with n2 commuted in/past them. So we then want to+-- modify p2, so that it doesn't have any of a1' in its context. -(*>) :: (Patchy p, ToFromPrim p) => Non p C(x) -> p C(x y) -> Maybe (Non p C(y))-n *> p = invert p >* n+-- remNons really only works right if the relevant nons are conflicting...+remNons :: (Nonable p, Effect p, Patchy p, ToFromPrim p, PrimPatchBase p,+ MyEq (PrimOf p)) => [Non p C(x)] -> Non p C(x) -> Non p C(x)+remNons ns n@(Non c x) = case remNonHelper ns c of+ NilFL :> c' -> Non c' x+ _ -> n+ where+ remNonHelper :: (Nonable p, Effect p, Patchy p, ToFromPrim p,+ PrimPatchBase p, MyEq (PrimOf p)) => [Non p C(x)]+ -> FL p C(x y) -> (FL (PrimOf p) :> FL p) C(x y)+ remNonHelper [] x = NilFL :> x+ remNonHelper _ NilFL = NilFL :> NilFL+ remNonHelper ns (c:>:cs)+ | non c `elem` ns =+ let nsWithoutC = delete (non c) ns in+ let commuteOrAddInvC = commuteOrAddToCtx $ invert c in+ case remNonHelper (map commuteOrAddInvC $ nsWithoutC) cs of+ a :> z -> sortCoalesceFL (effect c +>+ a) :> z+ | otherwise = case commuteWhatWeCanFL (c :> cs) of+ b :> c' :> d -> case remNonHelper ns b of+ a :> b' -> a :> (b' +>+ c' :>: d) -(>*) :: (Patchy p, ToFromPrim p) => p C(x y) -> Non p C(y) -> Maybe (Non p C(x))-y >* (Non c x) = case commuteFLorComplain (y :> c) of- Right (c' :> y') -> do- px' :> _ <- commute (y' :> fromPrim x)- x' <- toPrim px'- return (Non c' x')- _ -> Nothing+-- |commuteOrRemFromCtx attempts to remove a given patch from a Non. If the+-- patch was not in the Non, then the commute will succeed and the modified Non+-- will be returned. If the commute fails then the patch is either in the Non+-- context, or the Non patch itself; we attempt to remove the patch from the+-- context and then return the non with the updated context.+--+-- TODO: understand if there is any case where p is equal to the prim patch of+-- the Non, in which case, we return the original Non, is that right?+commuteOrRemFromCtx :: (Patchy p, ToFromPrim p) => p C(x y) -> Non p C(x)+ -> Maybe (Non p C(y))+commuteOrRemFromCtx p n | n'@(Just _) <- n *> p = n'+commuteOrRemFromCtx p (Non pc x) = removeFL p pc >>= \c -> return (Non c x) -(*>>) :: (WL l, Patchy p, ToFromPrim p, PrimPatchBase p) => Non p C(x) -> l (PrimOf p) C(x y) -> Maybe (Non p C(y))-n *>> p = invertWL p >>* n+-- |commuteOrRemFromCtxFL attempts to remove a FL of patches from a Non,+-- returning Nothing if any of the individual removes fail.+commuteOrRemFromCtxFL :: (Patchy p, ToFromPrim p) => FL p C(x y) -> Non p C(x)+ -> Maybe (Non p C(y))+commuteOrRemFromCtxFL NilFL n = Just n+commuteOrRemFromCtxFL (p:>:ps) n = do n' <- commuteOrRemFromCtx p n+ commuteOrRemFromCtxFL ps n' -(>>*) :: (WL l, Patchy p, ToFromPrim p) => l (PrimOf p) C(x y) -> Non p C(y) -> Maybe (Non p C(x))-q >>* nn = adj (toRL q) nn- where adj :: (Patchy p, ToFromPrim p) => RL (PrimOf p) C(x y) -> Non p C(y) -> Maybe (Non p C(x))- adj NilRL n = Just n- adj (x:<:xs) n = fromPrim x >* n >>= adj xs+-- |(*>) attemts to modify a Non by commuting it past a given patch.+(*>) :: (Patchy p, ToFromPrim p) => Non p C(x) -> p C(x y)+ -> Maybe (Non p C(y))+n *> p = invert p >* n --- TODO why don't any tests run this?-propAdjustTwice :: (Patchy p, ToFromPrim p, MyEq (PrimOf p)) => p C(x y) -> Non p C(y) -> Maybe Doc-propAdjustTwice p n =- do n' <- p >* n- case n' *> p of- Nothing -> Just (redText "prop_adjust_inverse 1")- Just n'' | n'' /= n -> Just (redText "prop_adjust_inverse 2")- _ -> case n *> invert p of- Nothing -> Just (redText "prop_adjust_inverse 3")- Just n'' | n'' /= n' -> Just (redText "prop_adjust_inverse 4")- _ -> case invert p >* n' of- Nothing -> Just (redText "prop_adjust_inverse 5")- Just n'' | n'' /= n -> Just (redText "prop_adjust_inverse 6")- _ -> Nothing+-- |(>*) attempts to modify a Non, by commuting a given patch past it.+(>*) :: (Patchy p, ToFromPrim p) => p C(x y) -> Non p C(y)+ -> Maybe (Non p C(x))+y >* (Non c x) = do+ c' :> y' <- commuteFL (y :> c)+ px' :> _ <- commute (y' :> fromPrim x)+ x' <- toPrim px'+ return (Non c' x') -instance (Show2 p, Show2 (PrimOf p)) => Show (Non p C(x)) where- showsPrec d (Non cs p) = showParen (d > appPrec) $ showString "Non " .- showsPrec2 (appPrec + 1) cs . showString " " .- showsPrec2 (appPrec + 1) p+-- |(*>>) attempts to modify a Non by commuting it past a given WL of patches.+(*>>) :: (WL l, Patchy p, ToFromPrim p, PrimPatchBase p) => Non p C(x)+ -> l (PrimOf p) C(x y) -> Maybe (Non p C(y))+n *>> p = invertWL p >>* n -instance (Show2 p, Show2 (PrimOf p)) => Show1 (Non p) where- showDict1 = ShowDictClass+-- |(>>*) attempts to modify a Non by commuting a given WL of patches past it.+(>>*) :: (WL l, Patchy p, ToFromPrim p) => l (PrimOf p) C(x y) -> Non p C(y)+ -> Maybe (Non p C(x))+ps >>* n = commuteRLPastNon (toRL ps) n+ where+ commuteRLPastNon :: (Patchy p, ToFromPrim p) => RL (PrimOf p) C(x y)+ -> Non p C(y) -> Maybe (Non p C(x))+ commuteRLPastNon NilRL n = Just n+ commuteRLPastNon (x:<:xs) n = fromPrim x >* n >>= commuteRLPastNon xs
src/Darcs/Patch/V2/Real.hs view
@@ -20,788 +20,881 @@ #include "gadts.h" --- | Conflictor patches-module Darcs.Patch.V2.Real- ( RealPatch(..), prim2real, isConsistent, isForward, isDuplicate,- mergeUnravelled ) where--import Control.Monad ( mplus, liftM )-import Data.List ( partition, nub )-import Darcs.Patch.Commute ( commuteFLorComplain, commuteRL, commuteRLFL )-import Darcs.Patch.Conflict ( Conflict(..), CommuteNoConflicts(..), IsConflictedPrim(..), ConflictState(..) )-import Darcs.Patch.ConflictMarking ( mangleUnravelled )-import Darcs.Patch.Effect ( Effect(..) )-import Darcs.Patch.FileHunk ( IsHunk(..) )-import Darcs.Patch.Format ( PatchListFormat(..), ListFormat(..), FileNameFormat(NewFormat) )-import Darcs.Patch.Invert ( invertFL, invertRL )-import Darcs.Patch.Merge ( Merge(..) )-import Darcs.Patch.Prim ( FromPrim(..), ToFromPrim(..),- showPrim, showPrimFL,- readPrim,- PrimOf, PrimPatchBase, PrimPatch- )-import Darcs.Patch.Read ( bracketedFL )-import Darcs.Patch.Repair ( mapMaybeSnd, RepairToFL(..), Check(..) )-import Darcs.Patch.Patchy ( Patchy, Apply(..), Commute(..)- , PatchInspect(..)- , ReadPatch(..), ShowPatch(..)- , Invert(..) )-import Darcs.Patch.Show ( ShowPatchBasic(..) )-import Darcs.Patch.Summary ( plainSummary )-import Darcs.Witnesses.Ordered ( FL(..), RL(..), (:>)(..), (+>+)- , (+<+), mapFL_FL, reverseFL, (:\/:)(..)- , (:/\:)(..), reverseRL, lengthFL- , lengthRL )-import Darcs.Patch.V2.Non ( Non(..), Nonable(..), unNon,- showNons, showNon, readNons, readNon,- add, addP, addPs, remP, remPs, remNons,- (*>), (>*), (*>>), (>>*) )-import Darcs.Patch.Permutations ( commuteWhatWeCanFL, commuteWhatWeCanRL,- genCommuteWhatWeCanRL,- removeRL, removeFL, removeSubsequenceFL )-import Darcs.Patch.RepoPatch ()-import qualified Data.ByteString.Char8 as BC ( ByteString, pack )-import Darcs.Patch.ReadMonads ( skipSpace, string, choice )-import Darcs.Utils ( nubsort )-import Darcs.Witnesses.Unsafe ( unsafeCoerceP )-import Darcs.Witnesses.Eq ( MyEq(..), EqCheck(..) )-import Darcs.Witnesses.Sealed ( FlippedSeal(..), Sealed(Sealed), mapSeal )-import Darcs.Witnesses.Show ( Show2(..), ShowDict(..) )-import Printer ( Doc, renderString, blueText, redText, (<+>), ($$) )-import Darcs.ColorPrinter ( errorDoc, assertDoc )---import Printer ( greenText )---import Darcs.ColorPrinter ( traceDoc )-#include "impossible.h"---- |--- @Duplicate x@: This patch has no effect since @x@ is already present in the repository------ @Etacilpud x: invert (Duplicate x)@------ @Normal prim@: A primitive patch------ @Conflictor ix xx x@:--- @ix@ is the set of patches:------ * that conflict with @x@ and also conflict with another patch in the repository------ * that conflict with a patch that conflict with @x@------ @xx@ is the sequence of patches that conflict *only* with @x@------ @x@ is the current patch------ @ix@ and @x@ are stored as @Non@ objects, which include any necessary--- context to uniquely define the patch that is referred to.------ @InvConflictor ix xx x@: like @invert (Conflictor ix xx x)@-data RealPatch prim C(x y) where- Duplicate :: Non (RealPatch prim) C(x) -> RealPatch prim C(x x)- Etacilpud :: Non (RealPatch prim) C(x) -> RealPatch prim C(x x)- Normal :: prim C(x y) -> RealPatch prim C(x y)- Conflictor :: [Non (RealPatch prim) C(x)] -> FL prim C(x y) -> Non (RealPatch prim) C(x) -> RealPatch prim C(y x)- InvConflictor :: [Non (RealPatch prim) C(x)] -> FL prim C(x y) -> Non (RealPatch prim) C(x) -> RealPatch prim C(x y)--instance PrimPatch prim => PrimPatchBase (RealPatch prim) where- type PrimOf (RealPatch prim) = prim---- | 'isDuplicate' @p@ is ' @True@ if @p@ is either a 'Duplicate' or 'Etacilpud' patch-isDuplicate :: RealPatch prim C(s y) -> Bool-isDuplicate (Duplicate _) = True-isDuplicate (Etacilpud _) = True-isDuplicate _ = False---- | This is only used for unit testing-isForward :: PrimPatch prim => RealPatch prim C(s y) -> Maybe Doc-isForward p@(InvConflictor _ _ _) =- Just $ redText "An inverse conflictor" $$ showPatch p-isForward p@(Etacilpud _) =- Just $ redText "An inverse duplicate" $$ showPatch p-isForward _ = Nothing--mergeUnravelled :: PrimPatch prim => [Sealed ((FL prim) C(x))] -> Maybe (FlippedSeal (RealPatch prim) C(x))-mergeUnravelled [] = Nothing-mergeUnravelled [_] = Nothing-mergeUnravelled ws = case mergeUnravelled_private ws of- Nothing -> Nothing- Just NilRL -> bug "found no patches in mergeUnravelled"- Just (z:<:_) -> Just $ FlippedSeal z- where notNullS :: PrimPatch prim => Sealed ((FL prim) C(x)) -> Bool- notNullS (Sealed NilFL) = False- notNullS _ = True- mergeUnravelled_private :: PrimPatch prim => [Sealed (FL prim C(x))] -> Maybe (RL (RealPatch prim) C(x x))- mergeUnravelled_private xs = reverseFL `fmap` mergeConflictingNons- (map sealed2non $ filter notNullS xs)---- | 'sealed2non' @(Sealed xs)@ converts @xs@ to a 'Non'.--- @xs@ must be non-empty since we split this list at the last patch-sealed2non :: Sealed ((FL prim) C(x)) -> Non (RealPatch prim) C(x)-sealed2non (Sealed xs) = case reverseFL xs of- y:<:ys -> Non (mapFL_FL fromPrim $ reverseRL ys) y- NilRL -> bug "NilFL encountered in sealed2non"--mergeConflictingNons :: PrimPatch prim => [Non (RealPatch prim) C(x)] -> Maybe (FL (RealPatch prim) C(x x))-mergeConflictingNons ns = mcn $ map unNon ns- where mcn :: PrimPatch prim => [Sealed (FL (RealPatch prim) C(x))] -> Maybe (FL (RealPatch prim) C(x x))- mcn [] = Just NilFL- mcn [Sealed p] = case joinEffects p of -- this is just a safety check, and could- NilFL -> Just p -- be removed when we're sure of the code.- _ -> Nothing- mcn (Sealed p1:Sealed p2:zs) = case pullCommon p1 p2 of- Common c ps qs ->- case merge (ps :\/: qs) of- qs' :/\: _ -> mcn (Sealed (c +>+ ps +>+ qs'):zs)--joinEffects :: forall p C(x y)- . (Effect p, Invert (PrimOf p), Commute (PrimOf p), MyEq (PrimOf p))- => p C(x y) -> FL (PrimOf p) C(x y)-joinEffects = joinInverses . effect- where joinInverses :: FL (PrimOf p) C(a b) -> FL (PrimOf p) C(a b)- joinInverses NilFL = NilFL- joinInverses (p:>:ps) = case removeFL (invert p) ps' of- Just ps'' -> ps''- Nothing -> p :>: ps'- where ps' = joinInverses ps--assertConsistent :: PrimPatch prim => RealPatch prim C(x y) -> RealPatch prim C(x y)-assertConsistent x = assertDoc (do e <- isConsistent x- Just (redText "Inconsistent patch:" $$ showPatch x $$ e)) x---- | @mergeAfterConflicting@ takes as input a sequence of conflicting--- patches @xxx@ (which therefore have no effect) and a sequence of--- primitive patches @yyy@ that follow said sequence of conflicting--- patches, and may depend upon some of the conflicting patches (as a--- resolution).---- The output is two sequences of patches the first consisting of a--- set of mutually-conflicting patches, and the second having the same--- effect as the original primitive patch sequence in the input.---- So far as I can tell, the second output is always identical to--- @mapFL Normal yyy@---- The first output is the set of patches from @xxx@ that are depended--- upon by @yyy@.--mergeAfterConflicting :: PrimPatch prim- => FL (RealPatch prim) C(x x) -> FL prim C(x y)- -> Maybe (FL (RealPatch prim) C(x x), FL (RealPatch prim) C(x y))-mergeAfterConflicting xxx yyy = --traceDoc (greenText "mergeAfterConflicting xxx" $$ showPatch xxx $$- -- greenText "and yyy" $$ showPatch yyy) $- mac (reverseFL xxx) yyy NilFL- where mac :: PrimPatch prim- => RL (RealPatch prim) C(x y) -> FL prim C(y z) -> FL (RealPatch prim) C(z a)- -> Maybe (FL (RealPatch prim) C(x x), FL (RealPatch prim) C(x a))- mac NilRL xs goneby = case joinEffects goneby of- NilFL -> Just (NilFL, mapFL_FL Normal xs)- _z -> --traceDoc (greenText "mac1 z" $$ showPatch _z) $- Nothing- mac (p:<:ps) xs goneby = --traceDoc (greenText "mac ps" $$ showPatch ps $$- -- greenText "p" $$ showPatch p $$- -- greenText "xs" $$ showPatch xs $$- -- greenText "goneby" $$ showPatch goneby) $- case commuteFLorComplain (p :> mapFL_FL Normal xs) of- Left _ -> case genCommuteWhatWeCanRL commuteNoConflicts (ps :> p) of- a:>p':>b ->- do (b',xs') <- mac b xs goneby- let pa = joinEffects $ p':<:a- --traceDoc (greenText "foo1" $$- -- showPatch pa) $ Just ()- NilFL <- return pa- return (reverseRL (p':<:a)+>+b', xs')- `mplus` do NilFL <- return goneby- NilFL <- return $ joinEffects (p:<:ps)- return (reverseRL (p:<:ps),- mapFL_FL Normal xs)- Right (l:>p'') ->- case allNormal l of- Just xs'' -> mac ps xs'' (p'':>:goneby)- Nothing ->- case genCommuteWhatWeCanRL commuteNoConflicts (ps :> p) of- a:>p':>b ->- do (b',xs') <- mac b xs goneby- let pa = joinEffects $ p':<:a- --traceDoc (greenText "foo2" $$- -- showPatch pa) $ Just ()- NilFL <- return pa- return $ (reverseRL (p':<:a)+>+b', xs')--geteff :: PrimPatch prim => [Non (RealPatch prim) C(x)] -> FL prim C(x y) -> ([Non (RealPatch prim) C(x)], FL (RealPatch prim) C(x y))-geteff _ NilFL = ([],NilFL)-geteff ix (x:>:xs) | Just ix' <- mapM (remP (Normal x)) ix- = --traceDoc (greenText "I got rid of x" $$ showPatch x) $- case geteff ix' xs of- (ns,xs') -> (non (Normal x) : map (addP (Normal x)) ns,- Normal x :>: xs')-geteff ix xx = case mergeConflictingNons ix of- Nothing -> errorDoc $ redText "mergeConflictingNons failed in geteff with ix" $$- showNons ix $$ redText "xx" $$ showPatch xx- Just rix -> case mergeAfterConflicting rix xx of- Just (a,x) -> (map (addPs (reverseFL a)) $ toNons x,- a +>+ x)- Nothing -> errorDoc $ redText "mergeAfterConflicting failed in geteff"$$- redText "where ix" $$ showNons ix $$- redText "and xx" $$ showPatch xx $$- redText "and rix" $$ showPatch rix--xx2nons :: PrimPatch prim => [Non (RealPatch prim) C(x)] -> FL prim C(x y) -> [Non (RealPatch prim) C(x)]-xx2nons ix xx = fst $ geteff ix xx--xx2patches :: PrimPatch prim => [Non (RealPatch prim) C(x)] -> FL prim C(x y) -> FL (RealPatch prim) C(x y)-xx2patches ix xx = snd $ geteff ix xx---- | If @xs@ consists only of 'Normal' patches, 'allNormal' @xs@ returns--- @Just pxs@ those patches (so @lengthFL pxs == lengthFL xs@).--- Otherwise, it returns 'Nothing'.-allNormal :: FL (RealPatch prim) C(x y) -> Maybe (FL prim C(x y))-allNormal (Normal x:>:xs) = (x :>:) `fmap` allNormal xs-allNormal NilFL = Just NilFL-allNormal _ = Nothing---- | This is used for unit-testing and for internal sanity checks-isConsistent :: PrimPatch prim => RealPatch prim C(x y) -> Maybe Doc-isConsistent (Normal _) = Nothing-isConsistent (Duplicate _) = Nothing-isConsistent (Etacilpud _) = Nothing-isConsistent (Conflictor im mm m@(Non deps _))- | not $ everyoneConflicts im = Just $ redText "Someone doesn't conflict in im in isConsistent"- | Just _ <- remPs rmm m, _:>:_ <- mm = Just $ redText "m doesn't conflict with mm in isConsistent"- | any (\x -> any (x `conflictsWith`) nmm) im- = Just $ redText "mm conflicts with im in isConsistent where nmm is" $$- showNons nmm- | Nothing <- (nmm ++ im) `minus` toNons deps = Just $ redText "dependencies not in conflict:" $$- showNons (toNons deps) $$- redText "compared with deps itself:" $$- showPatch deps- | otherwise = case allConflictsWith m im of- (im1,[]) | im1 `eqSet` im -> Nothing- (_,imnc) -> Just $ redText "m doesn't conflict with im in isConsistent. unconflicting:"- $$ showNons imnc- where (nmm, rmm) = geteff im mm-isConsistent c@(InvConflictor _ _ _) = isConsistent (invert c)--everyoneConflicts :: PrimPatch prim => [Non (RealPatch prim) C(x)] -> Bool-everyoneConflicts [] = True-everyoneConflicts (x:xs) = case allConflictsWith x xs of- ([],_) -> False- (_,xs') -> everyoneConflicts xs'--prim2real :: prim C(x y) -> RealPatch prim C(x y)-prim2real = Normal--instance PrimPatch prim => Patchy (RealPatch prim)--mergeWith :: PrimPatch prim => Non (RealPatch prim) C(x) -> [Non (RealPatch prim) C(x)] -> Sealed (FL prim C(x))-mergeWith p [] = effect `mapSeal` unNon p-mergeWith p xs = mergeall $ map unNon $ (p:) $ unconflicting_of $- filter (\x -> not (p `dependsUpon` x) && not (p `conflictsWith` x)) xs- where mergeall :: PrimPatch prim => [Sealed (FL (RealPatch prim) C(x))] -> Sealed (FL prim C(x))- mergeall [Sealed x] = Sealed $ effect x- mergeall [] = Sealed NilFL- mergeall (Sealed x:Sealed y:rest) = case merge (x :\/: y) of- y' :/\: _ -> mergeall (Sealed (x+>+y'):rest)- unconflicting_of [] = []- unconflicting_of (q:qs) = case allConflictsWith q qs of- ([],_) -> q:qs- (_,nc) -> unconflicting_of nc--instance PrimPatch prim => Conflict (RealPatch prim) where- conflictedEffect (Duplicate (Non _ x)) = [IsC Duplicated x]- conflictedEffect (Etacilpud _) = impossible- conflictedEffect (Conflictor _ _ (Non _ x)) = [IsC Conflicted x]- conflictedEffect (InvConflictor _ _ _) = impossible- conflictedEffect (Normal x) = [IsC Okay x]- resolveConflicts (Conflictor ix xx x) = [mangleUnravelled unravelled : unravelled]- where unravelled = nub $ filter isn $ map (`mergeWith` (x:ix++nonxx)) (x:ix++nonxx)- nonxx = nonxx_ (nonxx_aux ix xx)- nonxx_aux :: [Non (RealPatch prim) C(x)] -> FL prim C(x y) -> RL (RealPatch prim) C(x y)- nonxx_aux a b = reverseFL $ xx2patches a b- nonxx_ :: RL (RealPatch prim) C(x y) -> [Non (RealPatch prim) C(x)]- nonxx_ NilRL = []- nonxx_ ((Normal q) :<: qs) = [Non (reverseRL qs) q]- nonxx_ _ = []- isn :: Sealed (FL p C(x)) -> Bool- isn (Sealed NilFL) = False- isn _ = True- resolveConflicts _ = []--instance PrimPatch prim => CommuteNoConflicts (RealPatch prim) where- -- cA- commuteNoConflicts (Duplicate x :> Duplicate y) = Just (Duplicate y :> Duplicate x)- commuteNoConflicts (Etacilpud x :> Duplicate y) = Just (Duplicate y :> Etacilpud x)- commuteNoConflicts (Duplicate x :> Etacilpud y) = Just (Etacilpud y :> Duplicate x)- commuteNoConflicts (Etacilpud x :> Etacilpud y) = Just (Etacilpud y :> Etacilpud x)- -- cB- commuteNoConflicts (x :> Duplicate d) = if d == addP (invert x) (non x)- then Just (x :> Duplicate d)- else do d' <- remP (invert x) d- return (Duplicate d' :> x)- commuteNoConflicts (Duplicate d' :> x) = Just (x :> Duplicate (addP (invert x) d'))- commuteNoConflicts c@(Etacilpud _ :> _) = invertCommuteNC c- commuteNoConflicts c@(_ :> Etacilpud _) = invertCommuteNC c- -- cE- commuteNoConflicts (Normal x :> Normal y) = do y' :> x' <- commute (x :> y)- return (Normal y' :> Normal x')- -- cF -- involves a conflict- -- cG- commuteNoConflicts (Normal x :> Conflictor iy yy y) =- case commuteFLorComplain (x :> invert yy) of- Right (iyy' :> x') -> do- y':iy' <- mapM (Normal x' >*) (y:iy)- return (Conflictor iy' (invert iyy') y' :> Normal x')- _ -> Nothing- -- cFi+cGi -- handle with previous two pattern matches- commuteNoConflicts c@(InvConflictor _ _ _ :> Normal _) = invertCommuteNC c- -- icG FIXME: where is icF?- commuteNoConflicts (Conflictor iy' yy' y' :> Normal x') =- do x :> iyy <- commuteRL (invertFL yy' :> x')- y:iy <- mapM (*> Normal x') (y':iy')- return (Normal x :> Conflictor iy (invertRL iyy) y)- -- icGi -- handle with previous pattern match- commuteNoConflicts c@(Normal _ :> InvConflictor _ _ _) = invertCommuteNC c- -- cH -- this involves a conflict commute- -- cI- commuteNoConflicts (Conflictor ix xx x :> Conflictor iy yy y) =- do xx' :> yy' <- commute (yy :> xx)- x':ix' <- mapM (yy >>*) (x:ix)- y':iy' <- mapM (*>> xx') (y:iy)- False <- return $ any (conflictsWith y) (x':ix')- False <- return $ any (conflictsWith x') iy- return (Conflictor iy' yy' y' :> Conflictor ix' xx' x')- -- cHi+cIi uses previous two matches- commuteNoConflicts c@(InvConflictor _ _ _ :> InvConflictor _ _ _) = invertCommuteNC c- -- cJ- commuteNoConflicts (InvConflictor ix xx x :> Conflictor iy yy y) =- do iyy' :> xx' <- commute (xx :> invert yy)- y':iy' <- mapM (xx' >>*) (y:iy)- x':ix' <- mapM (invertFL iyy' >>*) (x:ix)- False <- return $ any (conflictsWith y') (x':ix')- False <- return $ any (conflictsWith x') iy'- return (Conflictor iy' (invert iyy') y' :> InvConflictor ix' xx' x')- -- icJ- commuteNoConflicts (Conflictor iy' yy' y' :> InvConflictor ix' xx' x') =- do xx :> iyy <- commute (invert yy' :> xx')- y:iy <- mapM (*>> xx') (y':iy')- x:ix <- mapM (*>> yy') (x':ix')- False <- return $ any (conflictsWith y') (x':ix')- False <- return $ any (conflictsWith x') iy'- return (InvConflictor ix xx x :> Conflictor iy (invert iyy) y)--instance PrimPatch prim => Check (RealPatch prim) where- isInconsistent = isConsistent--instance FromPrim (RealPatch prim) where- fromPrim = prim2real-instance ToFromPrim (RealPatch prim) where- toPrim (Normal p) = Just p- toPrim _ = Nothing--instance PrimPatch prim => MyEq (RealPatch prim) where- (Duplicate x) =\/= (Duplicate y) | x == y = IsEq- (Etacilpud x) =\/= (Etacilpud y) | x == y = IsEq- (Normal x) =\/= (Normal y) = x =\/= y- (Conflictor cx xx x) =\/= (Conflictor cy yy y)- | map (add $ invertFL xx) cx `eqSet`- map (add $ invertFL yy) cy &&- add (invert xx) x == add (invert yy) y = xx =/\= yy- (InvConflictor cx xx x) =\/= (InvConflictor cy yy y)- | cx `eqSet` cy && x == y = xx =\/= yy- _ =\/= _ = NotEq--eqSet :: Eq a => [a] -> [a] -> Bool-eqSet [] [] = True-eqSet (x:xs) xys | Just ys <- remove1 x xys = eqSet xs ys-eqSet _ _ = False--remove1 :: Eq a => a -> [a] -> Maybe [a]-remove1 x (y:ys) | x == y = Just ys- | otherwise = (y :) `fmap` remove1 x ys-remove1 _ [] = Nothing--minus :: Eq a => [a] -> [a] -> Maybe [a]-minus xs [] = Just xs-minus xs (y:ys) = do xs' <- remove1 y xs- xs' `minus` ys--invertNon :: PrimPatch prim => Non (RealPatch prim) C(x) -> Non (RealPatch prim) C(x)-invertNon (Non c x)- | Just rc' <- removeRL nix (reverseFL c) = Non (reverseRL rc') (invert x)- | otherwise = addPs (Normal x :<: reverseFL c) $ non nix- where nix = Normal $ invert x--nonTouches :: PatchInspect prim => Non (RealPatch prim) C(x) -> [FilePath]-nonTouches (Non c x) = listTouchedFiles (c +>+ fromPrim x :>: NilFL)--nonHunkMatches :: PatchInspect prim => (BC.ByteString -> Bool) -> Non (RealPatch prim) C(x) -> Bool-nonHunkMatches f (Non c x) = hunkMatches f c || hunkMatches f x--toNons :: forall p C(x y)- . (Conflict p, Patchy p, PatchListFormat p, ToFromPrim p,- Nonable p, ShowPatchBasic (PrimOf p))- => FL p C(x y) -> [Non p C(x)]-toNons xs = map lastNon $ initsFL xs- where lastNon :: Sealed ((p :> FL p) C(x)) -> Non p C(x)- lastNon (Sealed xxx) = case lastNon_aux xxx of- deps :> p :> _ -> case non p of- Non NilFL pp -> Non (reverseRL deps) pp- Non ds pp -> errorDoc $ redText "Weird case in toNons" $$- redText "please report this bug!" $$- (case xxx of- z:>zs -> showPatch (z:>:zs)) $$- redText "ds are" $$ showPatch ds $$- redText "pp is" $$ showPatch pp- reverseFoo :: (p :> FL p) C(x z) -> (RL p :> p) C(x z)- reverseFoo (p :> ps) = rf NilRL p ps- where rf :: RL p C(a b) -> p C(b c) -> FL p C(c d) -> (RL p :> p) C(a d)- rf rs l NilFL = rs :> l- rf rs x (y:>:ys) = rf (x:<:rs) y ys- lastNon_aux :: (p :> FL p) C(x z) -> (RL p :> p :> RL p) C(x z)- lastNon_aux = commuteWhatWeCanRL . reverseFoo--initsFL :: Patchy p => FL p C(x y) -> [Sealed ((p :> FL p) C(x))]-initsFL NilFL = []-initsFL (x:>:xs) = Sealed (x:>NilFL) : map (\ (Sealed (y:>xs')) -> Sealed (x:>y:>:xs')) (initsFL xs)--filterConflictsFL :: PrimPatch prim => Non (RealPatch prim) C(x) -> FL prim C(x y) -> (FL prim :> FL prim) C(x y)-filterConflictsFL _ NilFL = NilFL :> NilFL-filterConflictsFL n (p:>:ps)- | Just n' <- remP (fromPrim p) n = case filterConflictsFL n' ps of- p1 :> p2 -> p:>:p1 :> p2- | otherwise = case commuteWhatWeCanFL (p :> ps) of- p1 :> p' :> p2 -> case filterConflictsFL n p1 of- p1a :> p1b -> p1a :> p1b +>+ p' :>: p2--instance Invert prim => Invert (RealPatch prim) where- invert (Duplicate d) = Etacilpud d- invert (Etacilpud d) = Duplicate d- invert (Normal p) = Normal (invert p)- invert (Conflictor x c p) = InvConflictor x c p- invert (InvConflictor x c p) = Conflictor x c p--instance PrimPatch prim => Commute (RealPatch prim) where--- commute (x :> y) | traceDoc (greenText "commuting x" $$ showPatch x $$--- greenText "with y" $$ showPatch y) False = undefined- commute (x :> y) | Just (y' :> x') <- commuteNoConflicts (assertConsistent x :> assertConsistent y) = Just (y' :> x')- -- cF- commute (Normal x :> Conflictor a1'nop2 n1'x p1') -- these patches conflicted- | Just rn1' <- removeRL x (reverseFL n1'x) =- do let p2:n1nons = reverse $ xx2nons a1'nop2 $ reverseRL (x:<:rn1')- a2 = p1':a1'nop2++n1nons- case (a1'nop2, reverseRL rn1', p1') of- ([], NilFL, Non c y) | NilFL <- joinEffects c ->- Just (Normal y :> Conflictor a1'nop2 (y:>:NilFL) p2)- (a1,n1,_) -> Just (Conflictor a1 n1 p1' :> Conflictor a2 NilFL p2)- -- cFi -- handle with previous pattern match- commute c@(InvConflictor _ _ _ :> Normal _) = invertCommute c- -- cH- commute (Conflictor a1 n1 p1 :> Conflictor a2 n2 p2)- | Just a2_minus_p1 <- remove1 p1' a2,- not (p2 `dependsUpon` p1') =- do let n1nons = map (add n2) $ xx2nons a1 n1- n2nons = xx2nons a2 n2- Just a2_minus_p1n1 = a2_minus_p1 `minus` n1nons- n2n1 = n2 +>+ n1- a1' = map (add n2) a1- p2ooo = remNons a1' p2- n1' :> n2' <- return $ filterConflictsFL p2ooo n2n1- let n1'n2'nons = xx2nons a2_minus_p1n1 (n1'+>+n2')- n1'nons = take (lengthFL n1') n1'n2'nons- n2'nons = drop (lengthFL n1') n1'n2'nons- Just a1'nop2 = (a2++n2nons) `minus` (p1':n1'nons)- Just a2'o = --traceDoc (greenText "\n\nConflictor a1 n1 p1 is" $$- -- showPatch (assertConsistent $ Conflictor a1 n1 p1) $$- -- greenText "and Conflictor a2 n2 p2 is" $$- -- showPatch (assertConsistent $ Conflictor a2 n2 p2) $$- -- greenText "where n2'nons is" $$ showNons n2'nons $$- -- greenText "and others are" $$- -- showNons (fst $ allConflictsWith p2 $ a2_minus_p1++n2nons) $$- -- greenText "These came from" $$- -- showNons (a2_minus_p1++n2nons) $$- -- greenText "n1'n2'nons" $$ showNons n1'n2'nons $$- -- greenText "from n1' :> n2'" $$- -- showPatch n1' $$ greenText ":>" $$ showPatch n2' $$- -- greenText "p2" $$ showNon p2 $$- -- greenText "p2 fixed" $$ showNon p2ooo $$- -- -- greenText "pren1" $$ showPatch pren1 $$- -- greenText "n1'" $$ showPatch n1' $$- -- greenText "p2" $$ showNon p2- -- )- (fst $ allConflictsWith p2 $ a2_minus_p1++n2nons) `minus` n2'nons- Just a2' = mapM (remPs (xx2patches a1'nop2 n1')) $- a2'o- Just p2' = remPs (xx2patches a1'nop2 n1') p2- case (a2', n2', p2') of- ([], NilFL, Non c x) | NilFL <- joinEffects c ->- Just (Normal x :> Conflictor a1'nop2 (n1'+>+x:>:NilFL) p1')- | otherwise -> impossible- _ -> Just (Conflictor a2' n2' p2' :> Conflictor (p2:a1'nop2) n1' p1')- where (_,rpn2) = geteff a2 n2- p1' = addPs (reverseFL rpn2) p1- -- cHi -- uses previous match- commute c@(InvConflictor _ _ _ :> InvConflictor _ _ _) = invertCommute c- commute _ = Nothing--instance PrimPatch prim => Merge (RealPatch prim) where- merge (InvConflictor _ _ _ :\/: _) = impossible- merge (_ :\/: InvConflictor _ _ _) = impossible- merge (Etacilpud _ :\/: _) = impossible- merge (_ :\/: Etacilpud _) = impossible--- merge (x :\/: y) | traceDoc (greenText "merging x" $$ showPatch x $$--- greenText "with y" $$ showPatch y) False = impossible- -- mA- merge (Duplicate a :\/: Duplicate b) = Duplicate b :/\: Duplicate a- -- mB- merge (Duplicate a :\/: b) = b :/\: Duplicate (addP (invert b) a) -- FIXME ???- -- smB- merge m@(_ :\/: Duplicate _) = swapMerge m- -- mC--- merge _ | traceDoc (greenText "about to look for conflictingness") False = impossible- merge (x :\/: y) | Just (y' :> ix') <- commute (invert (assertConsistent x) :> assertConsistent y),- Just (y'' :> _) <- commute (x :> y'),- IsEq <- y'' =\/= y = --traceDoc (greenText "These didn't conflict") $- assertConsistent y' :/\: invert (assertConsistent ix')- | IsEq <- x =\/= y,- n <- addP (invert x) $ non x =- --traceDoc (greenText "Found duplicate") $- Duplicate n :/\: Duplicate n--- merge (x :\/: y) | traceDoc (greenText "trying to merging x" $$ showPatch x $$--- greenText "which conflicts with y" $$ showPatch y) False = impossible- -- mD- merge (Normal x :\/: Normal y) =- Conflictor [] (x:>:NilFL) (non $ Normal y) :/\: Conflictor [] (y:>:NilFL) (non $ Normal x)- -- mG- merge (Normal x :\/: Conflictor iy yy y) =- --traceDoc (greenText "merging Normal x" $$ showPatch x $$- -- greenText "and Conflictor iy yy y" $$ showPatch (Conflictor iy yy y)) $- Conflictor iy yyx y :/\: Conflictor (y:iy++nyy) NilFL x'- where yyx = yy +>+ x:>:NilFL- (x':nyy) = reverse $ xx2nons iy yyx- -- smE+smG- merge m@(Conflictor _ _ _ :\/: Normal _) = swapMerge m--- merge (x :\/: y) | traceDoc (greenText "still trying to merge x" $$ showPatch x $$--- greenText "with y" $$ showPatch y) False = impossible- -- mH see also cH- merge (Conflictor ix xx x :\/: Conflictor iy yy y) =- case pullCommonRL (reverseFL xx) (reverseFL yy) of- CommonRL rxx1 ryy1 c ->- case commuteRLFL (ryy1 :> invertRL rxx1) of- Just (ixx' :> ryy') ->- let xx' = invert ixx'- yy' = reverseRL ryy'- y':iy' = map (add $ invertFL ixx') (y:iy)- x':ix' = map (add ryy') (x:ix)- nyy' = xx2nons iy' yy'- nxx' = xx2nons ix' xx'- icx = drop (lengthRL rxx1) $ xx2nons ix (reverseRL $ c+<+rxx1)- ic' = map (add ryy') icx- ixy' = ic' ++ (iy'+++ix')- -- +++ above is a more efficient version of nub- -- (iy'++ix') given that we know each element shows up- -- only once in either list.- in --traceDoc (greenText "here I am! and so is ixy'" $$ showNons ixy' $$- -- greenText "and iy" $$ showNons iy $$ greenText (show $ length iy) $$- -- greenText "and ix" $$ showNons ix $$- -- greenText "and iy'" $$ showNons iy' $$- -- greenText "and ix'" $$ showNons ix' $$- -- greenText "and ic'" $$ showNons ic'- -- ) $- Conflictor (x':ixy'++nxx') yy' y' :/\: Conflictor (y':ixy'++nyy') xx' x'- Nothing -> impossible--- merge _ = error "haven't finished fixing merge"--instance PatchInspect prim => PatchInspect (RealPatch prim) where- listTouchedFiles (Duplicate p) = nonTouches p- listTouchedFiles (Etacilpud p) = nonTouches p- listTouchedFiles (Normal p) = listTouchedFiles p- listTouchedFiles (Conflictor x c p) =- nubsort $ concatMap nonTouches x ++ listTouchedFiles c ++ nonTouches p- listTouchedFiles (InvConflictor x c p) =- nubsort $ concatMap nonTouches x ++ listTouchedFiles c ++ nonTouches p-- hunkMatches f (Duplicate p) = nonHunkMatches f p- hunkMatches f (Etacilpud p) = nonHunkMatches f p- hunkMatches f (Normal p) = hunkMatches f p- hunkMatches f (Conflictor x c p) = or [or $ map (nonHunkMatches f) x, hunkMatches f c, nonHunkMatches f p]- hunkMatches f (InvConflictor x c p) = or [or $ map (nonHunkMatches f) x, hunkMatches f c, nonHunkMatches f p]--{--allConflictsWithFL :: FL prim C(x y) -> [Non (RealPatch prim) C(x)]- -> ([Non (RealPatch prim) C(x)], [Non (RealPatch prim) C(x)])-allConflictsWithFL xx ns = case partition f ns of- ([],nc) -> ([],nc)- (c,nc) -> case acw c nc of- (c',nc') -> (c++c',nc')- where acw (y:ys) zs = case allConflictsWith y zs of- (c,nc) -> case acw ys nc of- (c',nc') -> (c++c',nc')- acw [] zs = ([],zs)- f (Non c p) = case commuteRLFL (invertFL c :> mapFL_FL Normal xx) of- Nothing -> True- Just (xx' :> _) -> case commuteFLorComplain (Normal (invert p) :> xx') of- Nothing -> True- Just _ -> False--}-allConflictsWith :: PrimPatch prim- => Non (RealPatch prim) C(x) -> [Non (RealPatch prim) C(x)]- -> ([Non (RealPatch prim) C(x)], [Non (RealPatch prim) C(x)])-allConflictsWith x ys = acw $ partition (conflictsWith x) ys- where acw ([],nc) = ([],nc)- acw (c:cs, nc) = case allConflictsWith c nc of- (c1,nc1) -> case acw (cs, nc1) of- (xs',nc') -> (c:c1++xs',nc')--conflictsWith :: PrimPatch prim => Non (RealPatch prim) C(x) -> Non (RealPatch prim) C(x) -> Bool-conflictsWith x y | x `dependsUpon` y || y `dependsUpon` x = False-conflictsWith x (Non cy y) =- case remPs cy x of- Just (Non cx' x') -> case commuteFLorComplain (fromPrim (invert y) :> cx' +>+ fromPrim x' :>: NilFL) of- Right _ -> False- Left _ -> True- Nothing -> True--dependsUpon :: PrimPatch prim => Non (RealPatch prim) C(x) -> Non (RealPatch prim) C(x) -> Bool-dependsUpon (Non xs _) (Non ys y) =- case removeSubsequenceFL (ys +>+ fromPrim y :>: NilFL) xs of- Just _ -> True- Nothing -> False--(+++) :: Eq a => [a] -> [a] -> [a]-[] +++ x = x-x +++ [] = x-(x:xs) +++ xys | Just ys <- remove1 x xys = x : (xs +++ ys)- | otherwise = x : (xs +++ xys)--swapMerge :: PrimPatch prim => (RealPatch prim :\/: RealPatch prim) C(x y) -> (RealPatch prim :/\: RealPatch prim) C(x y)-swapMerge (x :\/: y) = case merge (y :\/: x) of x' :/\: y' -> y' :/\: x'--invertCommute :: PrimPatch prim => (RealPatch prim :> RealPatch prim) C(x y) -> Maybe ((RealPatch prim :> RealPatch prim) C(x y))-invertCommute (x :> y) = do ix' :> iy' <- commute (invert y :> invert x)- return (invert iy' :> invert ix')--invertCommuteNC :: PrimPatch prim => (RealPatch prim :> RealPatch prim) C(x y) -> Maybe ((RealPatch prim :> RealPatch prim) C(x y))-invertCommuteNC (x :> y) = do ix' :> iy' <- commuteNoConflicts (invert y :> invert x)- return (invert iy' :> invert ix')---- | 'pullCommon' @xs ys@ returns the set of patches that can be commuted--- out of both @xs@ and @ys@ along with the remnants of both lists-pullCommon :: Patchy p => FL p C(o x) -> FL p C(o y) -> Common p C(o x y)-pullCommon NilFL ys = Common NilFL NilFL ys-pullCommon xs NilFL = Common NilFL xs NilFL-pullCommon (x:>:xs) xys | Just ys <- removeFL x xys = case pullCommon xs ys of- Common c xs' ys' -> Common (x:>:c) xs' ys'-pullCommon (x:>:xs) ys = case commuteWhatWeCanFL (x :> xs) of- xs1:>x':>xs2 -> case pullCommon xs1 ys of- Common c xs1' ys' -> Common c (xs1'+>+x':>:xs2) ys'---- | 'Common' @cs xs ys@ represents two sequences of patches that have @cs@ in common,--- in other words @cs +>+ xs@ and @cs +>+ ys@-data Common p C(o x y) where- Common :: FL p C(o i) -> FL p C(i x) -> FL p C(i y) -> Common p C(o x y)---- | 'pullCommonRL' @xs ys@ returns the set of patches that can be commuted--- out of both @xs@ and @ys@ along with the remnants of both lists-pullCommonRL :: Patchy p => RL p C(x o) -> RL p C(y o) -> CommonRL p C(x y o)-pullCommonRL NilRL ys = CommonRL NilRL ys NilRL-pullCommonRL xs NilRL = CommonRL xs NilRL NilRL-pullCommonRL (x:<:xs) xys- | Just ys <- removeRL x xys = case pullCommonRL xs ys of- CommonRL xs' ys' c -> CommonRL xs' ys' (x:<:c)-pullCommonRL (x:<:xs) ys =- case commuteWhatWeCanRL (xs :> x) of- xs1:>x':>xs2 -> case pullCommonRL xs2 ys of- CommonRL xs2' ys' c -> CommonRL (xs2'+<+x':<:xs1) ys' c---- | 'CommonRL' @xs ys cs@' represents two sequences of patches that have @cs@ in common,--- in other words @xs +<+ cs@ and @ys +<+ cs@-data CommonRL p C(x y f) where- CommonRL :: RL p C(x i) -> RL p C(y i) -> RL p C(i f) -> CommonRL p C(x y f)--instance PrimPatch prim => Apply (RealPatch prim) where- type ApplyState (RealPatch prim) = ApplyState prim- apply p = apply (effect p)--instance PrimPatch prim => RepairToFL (RealPatch prim) where- applyAndTryToFixFL (Normal p) = mapMaybeSnd (mapFL_FL Normal) `liftM` applyAndTryToFixFL p- applyAndTryToFixFL x = do apply x; return Nothing--instance PatchListFormat (RealPatch prim) where- -- In principle we could use ListFormatDefault when prim /= V1 Prim patches,- -- as those are the only case where we need to support a legacy on-disk- -- format. In practice we don't expect RealPatch to be used with any other argument- -- anyway, so it doesn't matter.- patchListFormat = ListFormatV2--instance PrimPatch prim => ShowPatchBasic (RealPatch prim) where- showPatch (Duplicate d) = blueText "duplicate" $$ showNon d- showPatch (Etacilpud d) = blueText "etacilpud" $$ showNon d- showPatch (Normal p) = showPrim NewFormat p- showPatch (Conflictor i NilFL p) =- blueText "conflictor" <+> showNons i <+> blueText "[]" $$ showNon p- showPatch (Conflictor i cs p) =- blueText "conflictor" <+> showNons i <+> blueText "[" $$- showPrimFL NewFormat cs $$- blueText "]" $$- showNon p- showPatch (InvConflictor i NilFL p) =- blueText "rotcilfnoc" <+> showNons i <+> blueText "[]" $$ showNon p- showPatch (InvConflictor i cs p) =- blueText "rotcilfnoc" <+> showNons i <+> blueText "[" $$- showPrimFL NewFormat cs $$- blueText "]" $$- showNon p--instance PrimPatch prim => ShowPatch (RealPatch prim) where- showContextPatch (Normal p) = showContextPatch p- showContextPatch c = return $ showPatch c- summary = plainSummary- summaryFL = plainSummary- thing _ = "change"--instance PrimPatch prim => ReadPatch (RealPatch prim) where- readPatch' = skipSpace >> choice- [ do string duplicate- p <- readNon- return $ Sealed $ Duplicate p- , do string etacilpud- p <- readNon- return $ Sealed $ Etacilpud p- , do string conflictor- i <- readNons- Sealed ps <- bracketedFL (readPrim NewFormat) '[' ']'- p <- readNon- return $ Sealed $ Conflictor i (unsafeCoerceP ps) p- , do string rotcilfnoc- i <- readNons- Sealed ps <- bracketedFL (readPrim NewFormat) '[' ']'- p <- readNon- return $ Sealed $ InvConflictor i ps p- , do Sealed p <- readPrim NewFormat- return $ Sealed $ Normal p- ]--duplicate :: BC.ByteString-duplicate = BC.pack "duplicate"--etacilpud :: BC.ByteString-etacilpud = BC.pack "etacilpud"--conflictor :: BC.ByteString-conflictor = BC.pack "conflictor"--rotcilfnoc :: BC.ByteString-rotcilfnoc = BC.pack "rotcilfnoc"--instance PrimPatch prim => Show (RealPatch prim C(x y)) where- show p = renderString $ showPatch p--instance PrimPatch prim => Show2 (RealPatch prim) where- showDict2 = ShowDictClass--instance PrimPatch prim => Nonable (RealPatch prim) where- non (Duplicate d) = d- non (Etacilpud d) = invertNon d -- FIXME !!! ???- non (Normal p) = Non NilFL p- non (Conflictor _ xx x) = add (invertFL xx) x+module Darcs.Patch.V2.Real+ ( RealPatch(..)+ , prim2real+ , isConsistent+ , isForward+ , isDuplicate+ , mergeUnravelled+ ) where++import Control.Monad ( mplus, liftM )+import qualified Data.ByteString.Char8 as BC ( ByteString, pack )+import Data.Maybe ( fromMaybe )+import Data.List ( partition, nub )++import Darcs.ColorPrinter ( errorDoc, assertDoc )+import Darcs.Patch.Commute ( commuteFL, commuteFLorComplain, commuteRL+ , commuteRLFL )+import Darcs.Patch.Conflict ( Conflict(..), CommuteNoConflicts(..)+ , IsConflictedPrim(..), ConflictState(..) )+import Darcs.Patch.ConflictMarking ( mangleUnravelled )+import Darcs.Patch.Effect ( Effect(..) )+import Darcs.Patch.FileHunk ( IsHunk(..) )+import Darcs.Patch.Format ( PatchListFormat(..), ListFormat(..)+ , FileNameFormat(NewFormat) )+import Darcs.Patch.Invert ( invertFL, invertRL )+import Darcs.Patch.Merge ( Merge(..) )+import Darcs.Patch.Prim ( FromPrim(..), ToFromPrim(..), showPrim, showPrimFL+ , readPrim, PrimOf, PrimPatchBase, PrimPatch )+import Darcs.Patch.Read ( bracketedFL )+import Darcs.Patch.ReadMonads ( skipSpace, string, choice )+import Darcs.Patch.Repair ( mapMaybeSnd, RepairToFL(..), Check(..) )+import Darcs.Patch.Patchy ( Patchy, Apply(..), Commute(..), PatchInspect(..)+ , ReadPatch(..), ShowPatch(..), Invert(..) )+import Darcs.Patch.Permutations ( commuteWhatWeCanFL, commuteWhatWeCanRL+ , genCommuteWhatWeCanRL, removeRL, removeFL+ , removeSubsequenceFL )+import Darcs.Patch.Show ( ShowPatchBasic(..) )+import Darcs.Patch.Summary ( plainSummary )+import Darcs.Patch.V2.Non ( Non(..), Nonable(..), unNon, showNons, showNon+ , readNons, readNon, commutePrimsOrAddToCtx+ , commuteOrAddToCtx, commuteOrAddToCtxRL+ , commuteOrRemFromCtx, commuteOrRemFromCtxFL+ , remNons, (*>), (>*), (*>>), (>>*) )+import Darcs.Utils ( nubsort )+import Darcs.Witnesses.Unsafe ( unsafeCoerceP )+import Darcs.Witnesses.Eq ( MyEq(..), EqCheck(..) )+import Darcs.Witnesses.Ordered ( FL(..), RL(..), (:>)(..), (+>+), (+<+)+ , mapFL_FL, reverseFL, (:\/:)(..), (:/\:)(..)+ , reverseRL, lengthFL, lengthRL, nullFL )+import Darcs.Witnesses.Sealed ( FlippedSeal(..), Sealed(Sealed), mapSeal+ , unseal )+import Darcs.Witnesses.Show ( Show2(..), ShowDict(..) )++import Printer ( Doc, renderString, blueText, redText, (<+>), ($$) )++#include "impossible.h"++-- |'RealPatch' is used to represents prim patches that are duplicates of, or+-- conflict with, another prim patch in the repository.+--+-- @Normal prim@: A primitive patch+--+-- @Duplicate x@: This patch has no effect since @x@ is already present in the+-- repository.+--+-- @Etacilpud x: invert (Duplicate x)@+--+-- @Conflictor ix xx x@:+-- @ix@ is the set of patches:+-- * that conflict with @x@ and also conflict with another patch in the+-- repository.+-- * that conflict with a patch that conflict with @x@+--+-- @xx@ is the sequence of patches that conflict *only* with @x@+--+-- @x@ is the original, conflicting patch.+--+-- @ix@ and @x@ are stored as @Non@ objects, which include any necessary+-- context to uniquely define the patch that is referred to.+--+-- The intuition is that a Conflictor should have the effect of inverting any+-- patches that 'x' conflicts with, that haven't already been undone by another+-- Conflictor in the repository.+-- Therefore, the effect of a Conflictor is @invert xx@.+--+-- @InvConflictor ix xx x@: like @invert (Conflictor ix xx x)@+data RealPatch prim C(x y) where+ Duplicate :: Non (RealPatch prim) C(x) -> RealPatch prim C(x x)+ Etacilpud :: Non (RealPatch prim) C(x) -> RealPatch prim C(x x)+ Normal :: prim C(x y) -> RealPatch prim C(x y)+ Conflictor :: [Non (RealPatch prim) C(x)] -> FL prim C(x y)+ -> Non (RealPatch prim) C(x) -> RealPatch prim C(y x)+ InvConflictor :: [Non (RealPatch prim) C(x)] -> FL prim C(x y)+ -> Non (RealPatch prim) C(x) -> RealPatch prim C(x y)++instance PrimPatch prim => PrimPatchBase (RealPatch prim) where+ type PrimOf (RealPatch prim) = prim++-- | 'isDuplicate' @p@ is @True@ if @p@ is either a 'Duplicate' or 'Etacilpud'+-- patch.+isDuplicate :: RealPatch prim C(s y) -> Bool+isDuplicate (Duplicate _) = True+isDuplicate (Etacilpud _) = True+isDuplicate _ = False++-- | 'isForward' @p@ is @True@ if @p@ is either an 'InvConflictor' or+-- 'Etacilpud'.+isForward :: PrimPatch prim => RealPatch prim C(s y) -> Maybe Doc+isForward = isForward'+ where+ isForward' p@(InvConflictor _ _ _) = justRedP "An inverse conflictor" p+ isForward' p@(Etacilpud _) = justRedP "An inverse duplicate" p+ isForward' _ = Nothing+ justRedP msg p = Just $ redText msg $$ showPatch p++-- |'mergeUnravelled' is used when converting from Darcs V1 patches (Mergers)+-- to Darcs V2 patches (Conflictors).+mergeUnravelled :: PrimPatch prim => [Sealed ((FL prim) C(x))]+ -> Maybe (FlippedSeal (RealPatch prim) C(x))+mergeUnravelled [] = Nothing+mergeUnravelled [_] = Nothing+mergeUnravelled ws =+ case mergeUnravelled_private ws of+ Nothing -> Nothing+ Just NilRL -> bug "found no patches in mergeUnravelled"+ Just (z :<: _) -> Just $ FlippedSeal z+ where+ notNullS :: PrimPatch prim => Sealed ((FL prim) C(x)) -> Bool+ notNullS (Sealed NilFL) = False+ notNullS _ = True++ mergeUnravelled_private :: PrimPatch prim => [Sealed (FL prim C(x))]+ -> Maybe (RL (RealPatch prim) C(x x))+ mergeUnravelled_private xs = let nonNullXs = filter notNullS xs in+ reverseFL `fmap` mergeConflictingNons (map sealed2non nonNullXs)++ -- | 'sealed2non' @(Sealed xs)@ converts @xs@ to a 'Non'.+ -- @xs@ must be non-empty since we split this list at the last patch,+ -- taking @init xs@ as the context of @last xs@.+ sealed2non :: Sealed ((FL prim) C(x)) -> Non (RealPatch prim) C(x)+ sealed2non (Sealed xs) =+ case reverseFL xs of+ y :<: ys -> Non (mapFL_FL fromPrim $ reverseRL ys) y+ NilRL -> bug "NilFL encountered in sealed2non"++mergeConflictingNons :: PrimPatch prim => [Non (RealPatch prim) C(x)]+ -> Maybe (FL (RealPatch prim) C(x x))+mergeConflictingNons ns = mcn $ map unNon ns+ where mcn :: PrimPatch prim => [Sealed (FL (RealPatch prim) C(x))]+ -> Maybe (FL (RealPatch prim) C(x x))+ mcn [] = Just NilFL+ -- Apparently, the joinEffects call is a safety check "and could be+ -- removed when we're sure of the code"!+ mcn [Sealed p] = case joinEffects p of+ NilFL -> Just p+ _ -> Nothing+ mcn (Sealed p1:Sealed p2:zs) =+ case pullCommon p1 p2 of+ Common c ps qs ->+ case merge (ps :\/: qs) of+ qs' :/\: _ -> mcn (Sealed (c +>+ ps +>+ qs'):zs)++joinEffects :: forall p C(x y) . (Effect p, Invert (PrimOf p),+ Commute (PrimOf p), MyEq (PrimOf p)) => p C(x y)+ -> FL (PrimOf p) C(x y)+joinEffects = joinInverses . effect+ where joinInverses :: FL (PrimOf p) C(a b) -> FL (PrimOf p) C(a b)+ joinInverses NilFL = NilFL+ joinInverses (p :>: ps) =+ let ps' = joinInverses ps in+ fromMaybe (p :>: ps') $ removeFL (invert p) ps'++assertConsistent :: PrimPatch prim => RealPatch prim C(x y)+ -> RealPatch prim C(x y)+assertConsistent x = flip assertDoc x $ do+ e <- isConsistent x+ Just (redText "Inconsistent patch:" $$ showPatch x $$ e)++-- | @mergeAfterConflicting@ takes as input a sequence of conflicting patches+-- @xxx@ (which therefore have no effect) and a sequence of primitive patches+-- @yyy@ that follow said sequence of conflicting patches, and may depend upon+-- some of the conflicting patches (as a resolution).++-- The output is two sequences of patches the first consisting of a set of+-- mutually-conflicting patches, and the second having the same effect as the+-- original primitive patch sequence in the input.++-- So far as I can tell, the second output is always identical to @mapFL Normal+-- yyy@++-- The first output is the set of patches from @xxx@ that are depended upon by+-- @yyy@.+mergeAfterConflicting :: PrimPatch prim => FL (RealPatch prim) C(x x)+ -> FL prim C(x y) -> Maybe ( FL (RealPatch prim) C(x x)+ , FL (RealPatch prim) C(x y))+mergeAfterConflicting xxx yyy = mac (reverseFL xxx) yyy NilFL+ where+ mac :: PrimPatch prim+ => RL (RealPatch prim) C(x y) -> FL prim C(y z)+ -> FL (RealPatch prim) C(z a)+ -> Maybe (FL (RealPatch prim) C(x x), FL (RealPatch prim) C(x a))+ mac NilRL xs goneby = case joinEffects goneby of+ NilFL -> Just (NilFL, mapFL_FL Normal xs)+ _ -> Nothing+ mac (p :<: ps) xs goneby =+ case commuteFLorComplain (p :> mapFL_FL Normal xs) of+ Left _ ->+ case genCommuteWhatWeCanRL commuteNoConflicts (ps :> p) of+ a :> p' :> b ->+ do (b', xs') <- mac b xs goneby+ let pa = joinEffects $ p' :<: a+ NilFL <- return pa+ return (reverseRL (p' :<: a) +>+ b', xs')+ `mplus`+ do NilFL <- return goneby+ NilFL <- return $ joinEffects (p :<: ps)+ return (reverseRL (p :<: ps), mapFL_FL Normal xs)+ Right (l :> p'') ->+ case allNormal l of+ Just xs'' -> mac ps xs'' (p'' :>: goneby)+ Nothing ->+ case genCommuteWhatWeCanRL commuteNoConflicts (ps :> p) of+ a :> p' :> b ->+ do (b', xs') <- mac b xs goneby+ let pa = joinEffects $ p' :<: a+ NilFL <- return pa+ return (reverseRL (p' :<: a) +>+ b', xs')++geteff :: PrimPatch prim => [Non (RealPatch prim) C(x)] -> FL prim C(x y)+ -> ([Non (RealPatch prim) C(x)], FL (RealPatch prim) C(x y))+geteff _ NilFL = ([], NilFL)+geteff ix (x :>: xs) | Just ix' <- mapM (commuteOrRemFromCtx (Normal x)) ix =+ case geteff ix' xs of+ (ns, xs') -> ( non (Normal x) : map (commuteOrAddToCtx (Normal x)) ns+ , Normal x :>: xs')+geteff ix xx =+ case mergeConflictingNons ix of+ Nothing -> errorDoc $+ redText "mergeConflictingNons failed in geteff: ix" $$+ showNons ix $$ redText "xx" $$ showPatch xx+ Just rix ->+ case mergeAfterConflicting rix xx of+ Just (a, x) ->+ ( map (commuteOrAddToCtxRL (reverseFL a)) $ toNons x+ , a +>+ x)+ Nothing ->+ errorDoc $+ redText "mergeAfterConflicting failed in geteff" $$+ redText "where ix" $$ showNons ix $$+ redText "and xx" $$ showPatch xx $$+ redText "and rix" $$ showPatch rix++xx2nons :: PrimPatch prim => [Non (RealPatch prim) C(x)] -> FL prim C(x y)+ -> [Non (RealPatch prim) C(x)]+xx2nons ix xx = fst $ geteff ix xx++xx2patches :: PrimPatch prim => [Non (RealPatch prim) C(x)] -> FL prim C(x y)+ -> FL (RealPatch prim) C(x y)+xx2patches ix xx = snd $ geteff ix xx++-- | If @xs@ consists only of 'Normal' patches, 'allNormal' @xs@ returns+-- @Just pxs@ those patches (so @lengthFL pxs == lengthFL xs@).+-- Otherwise, it returns 'Nothing'.+allNormal :: FL (RealPatch prim) C(x y) -> Maybe (FL prim C(x y))+allNormal (Normal x :>: xs) = (x :>: ) `fmap` allNormal xs+allNormal NilFL = Just NilFL+allNormal _ = Nothing++-- | This is used for unit-testing and for internal sanity checks+isConsistent :: PrimPatch prim => RealPatch prim C(x y) -> Maybe Doc+isConsistent (Normal _) = Nothing+isConsistent (Duplicate _) = Nothing+isConsistent (Etacilpud _) = Nothing+isConsistent c@(InvConflictor _ _ _) = isConsistent (invert c)+isConsistent (Conflictor im mm m@(Non deps _))+ | not $ everyoneConflicts im =+ Just $ redText "Someone doesn't conflict in im in isConsistent"+ | Just _ <- commuteOrRemFromCtxFL rmm m, _ :>: _ <- mm =+ Just $ redText "m doesn't conflict with mm in isConsistent"+ | any (\x -> any (x `conflictsWith`) nmm) im =+ Just $ redText "mm conflicts with im in isConsistent where nmm is" $$+ showNons nmm+ | Nothing <- (nmm ++ im) `minus` toNons deps =+ Just $ redText "dependencies not in conflict:" $$+ showNons (toNons deps) $$+ redText "compared with deps itself:" $$+ showPatch deps+ | otherwise =+ case allConflictsWith m im of+ (im1, []) | im1 `eqSet` im -> Nothing+ (_, imnc) -> Just $ redText ("m doesn't conflict with im in "+ ++ "isConsistent. unconflicting:") $$+ showNons imnc+ where (nmm, rmm) = geteff im mm++everyoneConflicts :: PrimPatch prim => [Non (RealPatch prim) C(x)] -> Bool+everyoneConflicts [] = True+everyoneConflicts (x : xs) = case allConflictsWith x xs of+ ([], _) -> False+ (_, xs') -> everyoneConflicts xs'++prim2real :: prim C(x y) -> RealPatch prim C(x y)+prim2real = Normal++instance PrimPatch prim => Patchy (RealPatch prim)++mergeWith :: PrimPatch prim => Non (RealPatch prim) C(x)+ -> [Non (RealPatch prim) C(x)] -> Sealed (FL prim C(x))+mergeWith p [] = effect `mapSeal` unNon p+mergeWith p xs =+ mergeall . map unNon . (p :) . unconflicting_of $ nonDependsOrConflictsP xs+ where+ nonDependsOrConflictsP =+ filter (\x -> not ((p `dependsUpon` x) || (p `conflictsWith` x)))+ mergeall :: PrimPatch prim => [Sealed (FL (RealPatch prim) C(x))]+ -> Sealed (FL prim C(x))+ mergeall [Sealed x] = Sealed $ effect x+ mergeall [] = Sealed NilFL+ mergeall (Sealed x : Sealed y : rest) =+ case merge (x :\/: y) of+ y' :/\: _ -> mergeall (Sealed (x +>+ y') : rest)+ unconflicting_of [] = []+ unconflicting_of (q : qs) = case allConflictsWith q qs of+ ([], _) -> q : qs+ (_, nc) -> unconflicting_of nc++instance PrimPatch prim => Conflict (RealPatch prim) where+ conflictedEffect (Duplicate (Non _ x)) = [IsC Duplicated x]+ conflictedEffect (Etacilpud _) = impossible+ conflictedEffect (Conflictor _ _ (Non _ x)) = [IsC Conflicted x]+ conflictedEffect (InvConflictor _ _ _) = impossible+ conflictedEffect (Normal x) = [IsC Okay x]+ resolveConflicts (Conflictor ix xx x) = [mangledUnravelled : unravelled]+ where+ mangledUnravelled = mangleUnravelled unravelled+ unravelled = nub $ filter isCons $ map (`mergeWith` xIxNonXX) xIxNonXX+ xIxNonXX = x : ix ++ nonxx+ nonxx = nonxx_ (reverseFL $ xx2patches ix xx)+ -- |nonxx_ takes an RL of patches, and returns a singleton list+ -- containing a Non, in the case where we have a Normal patch at the+ -- end of the list (using the rest of the RL as context), and an empty+ -- list otherwise.+ nonxx_ :: RL (RealPatch prim) C(x y) -> [Non (RealPatch prim) C(x)]+ nonxx_ (Normal q :<: qs) = [Non (reverseRL qs) q]+ nonxx_ _ = []+ isCons = unseal (not . nullFL)+ resolveConflicts _ = []++instance PrimPatch prim => CommuteNoConflicts (RealPatch prim) where+ commuteNoConflicts (d1@(Duplicate _) :> d2@(Duplicate _)) = Just (d2 :> d1)+ commuteNoConflicts (e@(Etacilpud _) :> d@(Duplicate _)) = Just (d :> e)+ commuteNoConflicts (d@(Duplicate _) :> e@(Etacilpud _)) = Just (e :> d)+ commuteNoConflicts (e1@(Etacilpud _) :> e2@(Etacilpud _)) = Just (e2 :> e1)++ -- If the duplicate is @x@, as a 'Non', with @invert x@ as the context,+ -- then it is the patch the duplicate @d@ represents, so commuting results+ -- in the same two patches (since we'd make one a duplicate, and the other+ -- would become @x@ as it would no longer be duplicated).+ -- Otherwise, we commute past, or remove @invert x@ from the context of @d@+ -- to obtain a new Duplicate.+ commuteNoConflicts orig@(x :> Duplicate d) =+ if d == commuteOrAddToCtx (invert x) (non x)+ then Just orig+ else do d' <- commuteOrRemFromCtx (invert x) d+ return (Duplicate d' :> x)++ -- Commuting a Duplicate and any other patch simply places @invert x@ into+ -- the context of the non @d@, by commuting past, or adding to the context.+ commuteNoConflicts (Duplicate d :> x) =+ Just (x :> Duplicate (commuteOrAddToCtx (invert x) d))++ -- handle Etacilpud cases by first inverting, then using the previous+ -- definitions.+ commuteNoConflicts c@(Etacilpud _ :> _) = invertCommuteNC c+ commuteNoConflicts c@(_ :> Etacilpud _) = invertCommuteNC c++ -- Two normal patches should be simply commuted (assuming the can).+ commuteNoConflicts (Normal x :> Normal y) = do+ y' :> x' <- commute (x :> y)+ return (Normal y' :> Normal x')++ -- Commuting a Normal patch past a Conflictor first commutes @x@ past the+ -- effect of the Conflictor, then commutes the resulting @x'@ past the+ -- conflicting patch and the already-undone patches. The commuting must be+ -- done in this order to make the contexts match up (@iy@ and @y@ are made+ -- in the context before @yy@ have their effect, so we need to commute past+ -- the effect of @yy@ first).+ commuteNoConflicts (Normal x :> Conflictor iy yy y) = do+ iyy' :> x' <- commuteFL (x :> invert yy)+ y' : iy' <- mapM (Normal x' >*) (y : iy)+ return (Conflictor iy' (invert iyy') y' :> Normal x')++ -- Handle via the previous case, using the inverting commuter.+ commuteNoConflicts c@(InvConflictor _ _ _ :> Normal _) = invertCommuteNC c++ -- Commuting a Conflictor past a Normal patch is the dual operation to+ -- commuting a Normal patch past a Conflictor.+ commuteNoConflicts (Conflictor iy yy y :> Normal x) = do+ y' : iy' <- mapM (*> Normal x) (y : iy)+ x' :> iyy' <- commuteRL (invertFL yy :> x)+ return (Normal x' :> Conflictor iy' (invertRL iyy') y')++ -- Handle via the previous case, using the inverting commuter.+ commuteNoConflicts c@(Normal _ :> InvConflictor _ _ _) = invertCommuteNC c++ -- Commuting two Conflictors, c1 and c2, first commutes the Conflictors'+ -- effects, then commutes the effect of c1 and c2 and the other's+ -- already-undone, and conflicting patch, to bring the already-undone and+ -- conflicting patch into the context of the commuted effects.+ commuteNoConflicts (Conflictor ix xx x :> Conflictor iy yy y) = do+ xx' :> yy' <- commute (yy :> xx)+ x':ix' <- mapM (yy >>*) (x:ix)+ y':iy' <- mapM (*>> xx') (y:iy)+ False <- return $ any (conflictsWith y) (x':ix')+ False <- return $ any (conflictsWith x') iy+ return (Conflictor iy' yy' y' :> Conflictor ix' xx' x')++ -- Handle via the previous case, using the inverting commuter.+ commuteNoConflicts c@(InvConflictor _ _ _ :> InvConflictor _ _ _) =+ invertCommuteNC c++ commuteNoConflicts (InvConflictor ix xx x :> Conflictor iy yy y) = do+ iyy' :> xx' <- commute (xx :> invert yy)+ y':iy' <- mapM (xx' >>*) (y:iy)+ x':ix' <- mapM (invertFL iyy' >>*) (x:ix)+ False <- return $ any (conflictsWith y') (x':ix')+ False <- return $ any (conflictsWith x') iy'+ return (Conflictor iy' (invert iyy') y' :> InvConflictor ix' xx' x')++ commuteNoConflicts (Conflictor iy' yy' y' :> InvConflictor ix' xx' x') = do+ xx :> iyy <- commute (invert yy' :> xx')+ y:iy <- mapM (*>> xx') (y':iy')+ x:ix <- mapM (*>> yy') (x':ix')+ False <- return $ any (conflictsWith y') (x':ix')+ False <- return $ any (conflictsWith x') iy'+ return (InvConflictor ix xx x :> Conflictor iy (invert iyy) y)++instance PrimPatch prim => Check (RealPatch prim) where+ isInconsistent = isConsistent++instance FromPrim (RealPatch prim) where+ fromPrim = prim2real++instance ToFromPrim (RealPatch prim) where+ toPrim (Normal p) = Just p+ toPrim _ = Nothing++instance PrimPatch prim => MyEq (RealPatch prim) where+ (Duplicate x) =\/= (Duplicate y) | x == y = IsEq+ (Etacilpud x) =\/= (Etacilpud y) | x == y = IsEq+ (Normal x) =\/= (Normal y) = x =\/= y+ (Conflictor cx xx x) =\/= (Conflictor cy yy y)+ | map commuteOrAddIXX cx `eqSet` map commuteOrAddIYY cy+ && commuteOrAddIXX x == commuteOrAddIYY y = xx =/\= yy+ where+ commuteOrAddIXX = commutePrimsOrAddToCtx (invertFL xx)+ commuteOrAddIYY = commutePrimsOrAddToCtx (invertFL yy)+ (InvConflictor cx xx x) =\/= (InvConflictor cy yy y)+ | cx `eqSet` cy && x == y = xx =\/= yy+ _ =\/= _ = NotEq++eqSet :: Eq a => [a] -> [a] -> Bool+eqSet [] [] = True+eqSet (x:xs) xys | Just ys <- remove1 x xys = eqSet xs ys+eqSet _ _ = False++remove1 :: Eq a => a -> [a] -> Maybe [a]+remove1 x (y : ys) = if x == y then Just ys else (y :) `fmap` remove1 x ys+remove1 _ [] = Nothing++minus :: Eq a => [a] -> [a] -> Maybe [a]+minus xs [] = Just xs+minus xs (y:ys) = do xs' <- remove1 y xs+ xs' `minus` ys++invertNon :: PrimPatch prim => Non (RealPatch prim) C(x)+ -> Non (RealPatch prim) C(x)+invertNon (Non c x)+ | Just rc' <- removeRL nix (reverseFL c) = Non (reverseRL rc') (invert x)+ | otherwise = commuteOrAddToCtxRL (Normal x :<: reverseFL c) $ non nix+ where+ nix = Normal $ invert x++nonTouches :: PatchInspect prim => Non (RealPatch prim) C(x) -> [FilePath]+nonTouches (Non c x) = listTouchedFiles (c +>+ fromPrim x :>: NilFL)++nonHunkMatches :: PatchInspect prim => (BC.ByteString -> Bool)+ -> Non (RealPatch prim) C(x) -> Bool+nonHunkMatches f (Non c x) = hunkMatches f c || hunkMatches f x++toNons :: forall p C(x y) . (Conflict p, Patchy p, PatchListFormat p,+ ToFromPrim p, Nonable p, ShowPatchBasic (PrimOf p)) => FL p C(x y)+ -> [Non p C(x)]+toNons xs = map lastNon $ initsFL xs+ where lastNon :: Sealed ((p :> FL p) C(x)) -> Non p C(x)+ lastNon (Sealed xxx) =+ case lastNon_aux xxx of+ deps :> p :> _ ->+ case non p of+ Non NilFL pp -> Non (reverseRL deps) pp+ Non ds pp ->+ errorDoc $ redText "Weird case in toNons" $$+ redText "please report this bug!" $$+ (case xxx of+ z :> zs -> showPatch (z :>: zs)) $$+ redText "ds are" $$ showPatch ds $$+ redText "pp is" $$ showPatch pp++ reverseFoo :: (p :> FL p) C(x z) -> (RL p :> p) C(x z)+ reverseFoo (p :> ps) = rf NilRL p ps+ where+ rf :: RL p C(a b) -> p C(b c) -> FL p C(c d)+ -> (RL p :> p) C(a d)+ rf rs l NilFL = rs :> l+ rf rs x (y :>: ys) = rf (x :<: rs) y ys++ lastNon_aux :: (p :> FL p) C(x z) -> (RL p :> p :> RL p) C(x z)+ lastNon_aux = commuteWhatWeCanRL . reverseFoo++initsFL :: Patchy p => FL p C(x y) -> [Sealed ((p :> FL p) C(x))]+initsFL NilFL = []+initsFL (x :>: xs) =+ Sealed (x :> NilFL) :+ map (\(Sealed (y :> xs')) -> Sealed (x :> y :>: xs')) (initsFL xs)++filterConflictsFL :: PrimPatch prim => Non (RealPatch prim) C(x)+ -> FL prim C(x y) -> (FL prim :> FL prim) C(x y)+filterConflictsFL _ NilFL = NilFL :> NilFL+filterConflictsFL n (p :>: ps)+ | Just n' <- commuteOrRemFromCtx (fromPrim p) n =+ case filterConflictsFL n' ps of+ p1 :> p2 -> p :>: p1 :> p2+ | otherwise = case commuteWhatWeCanFL (p :> ps) of+ p1 :> p' :> p2 ->+ case filterConflictsFL n p1 of+ p1a :> p1b -> p1a :> p1b +>+ p' :>: p2++instance Invert prim => Invert (RealPatch prim) where+ invert (Duplicate d) = Etacilpud d+ invert (Etacilpud d) = Duplicate d+ invert (Normal p) = Normal (invert p)+ invert (Conflictor x c p) = InvConflictor x c p+ invert (InvConflictor x c p) = Conflictor x c p++instance PrimPatch prim => Commute (RealPatch prim) where+ commute (x :> y) | Just (y' :> x') <-+ commuteNoConflicts (assertConsistent x :> assertConsistent y) =+ Just (y' :> x')++ -- These patches conflicted, since we failed to commuteNoConflicts in the+ -- case above.+ commute (Normal x :> Conflictor a1'nop2 n1'x p1')+ | Just rn1' <- removeRL x (reverseFL n1'x) = do+ let p2 : n1nons = reverse $ xx2nons a1'nop2 $ reverseRL (x :<: rn1')+ a2 = p1' : a1'nop2 ++ n1nons+ case (a1'nop2, reverseRL rn1', p1') of+ ([], NilFL, Non c y) | NilFL <- joinEffects c ->+ Just (Normal y :> Conflictor a1'nop2 (y :>: NilFL) p2)+ (a1, n1, _) ->+ Just (Conflictor a1 n1 p1' :> Conflictor a2 NilFL p2)++ -- Handle using the inverting commuter, and the previous case. N.b. this+ -- is innefficient, since we'll have to also try commuteNoConflicts again+ -- (which we know will fail, since we got here).+ commute c@(InvConflictor _ _ _ :> Normal _) = invertCommute c++ commute (Conflictor a1 n1 p1 :> Conflictor a2 n2 p2)+ | Just a2_minus_p1 <- remove1 p1' a2+ , not (p2 `dependsUpon` p1') = do+ let n1nons = map (commutePrimsOrAddToCtx n2) $ xx2nons a1 n1+ n2nons = xx2nons a2 n2+ Just a2_minus_p1n1 = a2_minus_p1 `minus` n1nons+ n2n1 = n2 +>+ n1+ a1' = map (commutePrimsOrAddToCtx n2) a1+ p2ooo = remNons a1' p2+ n1' :> n2' <- return $ filterConflictsFL p2ooo n2n1+ let n1'n2'nons = xx2nons a2_minus_p1n1 (n1' +>+ n2')+ n1'nons = take (lengthFL n1') n1'n2'nons+ n2'nons = drop (lengthFL n1') n1'n2'nons+ Just a1'nop2 = (a2 ++ n2nons) `minus` (p1' : n1'nons)+ Just a2'o =+ fst (allConflictsWith p2 $ a2_minus_p1 ++ n2nons)+ `minus` n2'nons+ Just a2' =+ mapM (commuteOrRemFromCtxFL (xx2patches a1'nop2 n1')) a2'o+ Just p2' = commuteOrRemFromCtxFL (xx2patches a1'nop2 n1') p2+ case (a2', n2', p2') of+ ([], NilFL, Non c x) ->+ case joinEffects c of+ NilFL -> let n1'x = n1' +>+ x :>: NilFL in+ Just (Normal x :> Conflictor a1'nop2 n1'x p1')+ _ -> impossible+ _ -> Just (c1 :> c2)+ where+ c1 = Conflictor a2' n2' p2'+ c2 = Conflictor (p2 : a1'nop2) n1' p1'++ where (_, rpn2) = geteff a2 n2+ p1' = commuteOrAddToCtxRL (reverseFL rpn2) p1++ -- Handle using the inverting commuter, and the previous case. This is also+ -- innefficient, since we'll have to also try commuteNoConflicts again+ -- (which we know will fail, since we got here).+ commute c@(InvConflictor _ _ _ :> InvConflictor _ _ _) = invertCommute c++ commute _ = Nothing++instance PrimPatch prim => Merge (RealPatch prim) where+ merge (InvConflictor _ _ _ :\/: _) = impossible+ merge (_ :\/: InvConflictor _ _ _) = impossible+ merge (Etacilpud _ :\/: _) = impossible+ merge (_ :\/: Etacilpud _) = impossible+++ merge (Duplicate a :\/: Duplicate b) = Duplicate b :/\: Duplicate a+ -- We had a FIXME comment on this case, why?+ merge (Duplicate a :\/: b) =+ b :/\: Duplicate (commuteOrAddToCtx (invert b) a)++ -- Handle using the swap merge and the previous case.+ merge m@(_ :\/: Duplicate _) = swapMerge m++ -- When merging x and y, we do a bunch of what look like "consistency"+ -- check merges. If the resulting y'' and y are equal, then we succeed.+ -- If the first case fails, we check for equal patches (which wouldn't+ -- commute) and return a Duplicate on both sides of the merge, in that+ -- case.+ merge (x :\/: y)+ | Just (y' :> ix') <-+ commute (invert (assertConsistent x) :> assertConsistent y)+ , Just (y'' :> _) <- commute (x :> y')+ , IsEq <- y'' =\/= y =+ assertConsistent y' :/\: invert (assertConsistent ix')+ -- If we detect equal patches, we have a duplicate.+ | IsEq <- x =\/= y+ , n <- commuteOrAddToCtx (invert x) $ non x =+ Duplicate n :/\: Duplicate n++ -- We know that these two patches conflict, and aren't Duplicates, since we+ -- failed the previous case. We therefore create basic Conflictors, which+ -- undo the other patch.+ merge (nx@(Normal x) :\/: ny@(Normal y)) = cy :/\: cx+ where+ cy = Conflictor [] (x :>: NilFL) (non ny)+ cx = Conflictor [] (y :>: NilFL) (non nx)++ -- If a Normal patch @x@ and a Conflictor @cy@ conflict, we add @x@ to the+ -- effect of @cy@ on one side, and create a Conflictor that has no effect,+ -- but has the already-undone and conflicted patch of @cy@ and some foos as+ -- the already-undone on the other side.+ --+ -- TODO: what is foo?+ -- Why do we need nyy? I think @x'@ is @x@ in the context of @yy@.+ merge (Normal x :\/: Conflictor iy yy y) =+ Conflictor iy yyx y :/\: Conflictor (y : iy ++ nyy) NilFL x'+ where yyx = yy +>+ x :>: NilFL+ (x' : nyy) = reverse $ xx2nons iy yyx++ -- Handle using the swap merge and the previous case.+ merge m@(Conflictor _ _ _ :\/: Normal _) = swapMerge m++ -- mH see also cH+ merge (Conflictor ix xx x :\/: Conflictor iy yy y) =+ case pullCommonRL (reverseFL xx) (reverseFL yy) of+ CommonRL rxx1 ryy1 c ->+ case commuteRLFL (ryy1 :> invertRL rxx1) of+ Just (ixx' :> ryy') ->+ let xx' = invert ixx'+ yy' = reverseRL ryy'+ y' : iy' =+ map (commutePrimsOrAddToCtx xx') (y : iy)+ x' : ix' =+ map (commutePrimsOrAddToCtx ryy') (x : ix)+ nyy' = xx2nons iy' yy'+ nxx' = xx2nons ix' xx'+ icx = drop (lengthRL rxx1) $+ xx2nons ix (reverseRL $ c +<+ rxx1)+ ic' = map (commutePrimsOrAddToCtx ryy') icx+ -- +++ is a more efficient version of nub (iy' +++ -- ix') given that we know each element shows up+ -- only once in either list.+ ixy' = ic' ++ (iy' +++ ix')+ c1 = Conflictor (x' : ixy' ++ nxx') yy' y'+ c2 = Conflictor (y' : ixy' ++ nyy') xx' x' in+ c1 :/\: c2+ Nothing -> impossible++instance PatchInspect prim => PatchInspect (RealPatch prim) where+ listTouchedFiles (Duplicate p) = nonTouches p+ listTouchedFiles (Etacilpud p) = nonTouches p+ listTouchedFiles (Normal p) = listTouchedFiles p+ listTouchedFiles (Conflictor x c p) =+ nubsort $ concatMap nonTouches x ++ listTouchedFiles c ++ nonTouches p+ listTouchedFiles (InvConflictor x c p) =+ nubsort $ concatMap nonTouches x ++ listTouchedFiles c ++ nonTouches p++ hunkMatches f (Duplicate p) = nonHunkMatches f p+ hunkMatches f (Etacilpud p) = nonHunkMatches f p+ hunkMatches f (Normal p) = hunkMatches f p+ hunkMatches f (Conflictor x c p) =+ any (nonHunkMatches f) x || hunkMatches f c || nonHunkMatches f p+ hunkMatches f (InvConflictor x c p) =+ any (nonHunkMatches f) x || hunkMatches f c || nonHunkMatches f p++allConflictsWith :: PrimPatch prim => Non (RealPatch prim) C(x)+ -> [Non (RealPatch prim) C(x)]+ -> ([Non (RealPatch prim) C(x)], [Non (RealPatch prim) C(x)])+allConflictsWith x ys = acw $ partition (conflictsWith x) ys+ where+ acw ([], nc) = ([], nc)+ acw (c:cs, nc) = case allConflictsWith c nc of+ (c1, nc1) -> case acw (cs, nc1) of+ (xs', nc') -> (c : c1 ++ xs', nc')++conflictsWith :: PrimPatch prim => Non (RealPatch prim) C(x)+ -> Non (RealPatch prim) C(x) -> Bool+conflictsWith x y | x `dependsUpon` y || y `dependsUpon` x = False+conflictsWith x (Non cy y) =+ case commuteOrRemFromCtxFL cy x of+ Just (Non cx' x') ->+ let iy = fromPrim $ invert y in+ case commuteFLorComplain (iy :> cx' +>+ fromPrim x' :>: NilFL) of+ Right _ -> False+ Left _ -> True+ Nothing -> True++dependsUpon :: PrimPatch prim => Non (RealPatch prim) C(x)+ -> Non (RealPatch prim) C(x) -> Bool+dependsUpon (Non xs _) (Non ys y) =+ case removeSubsequenceFL (ys +>+ fromPrim y :>: NilFL) xs of+ Just _ -> True+ Nothing -> False++(+++) :: Eq a => [a] -> [a] -> [a]+[] +++ x = x+x +++ [] = x+(x:xs) +++ xys | Just ys <- remove1 x xys = x : (xs +++ ys)+ | otherwise = x : (xs +++ xys)++swapMerge :: PrimPatch prim => (RealPatch prim :\/: RealPatch prim) C(x y)+ -> (RealPatch prim :/\: RealPatch prim) C(x y)+swapMerge (x :\/: y) = case merge (y :\/: x) of x' :/\: y' -> y' :/\: x'++invertCommute :: PrimPatch prim => (RealPatch prim :> RealPatch prim) C(x y)+ -> Maybe ((RealPatch prim :> RealPatch prim) C(x y))+invertCommute (x :> y) = do ix' :> iy' <- commute (invert y :> invert x)+ return (invert iy' :> invert ix')++invertCommuteNC :: PrimPatch prim => (RealPatch prim :> RealPatch prim) C(x y)+ -> Maybe ((RealPatch prim :> RealPatch prim) C(x y))+invertCommuteNC (x :> y) = do+ ix' :> iy' <- commuteNoConflicts (invert y :> invert x)+ return (invert iy' :> invert ix')++-- | 'pullCommon' @xs ys@ returns the set of patches that can be commuted out+-- of both @xs@ and @ys@ along with the remnants of both lists+pullCommon :: Patchy p => FL p C(o x) -> FL p C(o y) -> Common p C(o x y)+pullCommon NilFL ys = Common NilFL NilFL ys+pullCommon xs NilFL = Common NilFL xs NilFL+pullCommon (x :>: xs) xys | Just ys <- removeFL x xys =+ case pullCommon xs ys of+ Common c xs' ys' -> Common (x :>: c) xs' ys'+pullCommon (x :>: xs) ys =+ case commuteWhatWeCanFL (x :> xs) of+ xs1 :> x' :> xs2 -> case pullCommon xs1 ys of+ Common c xs1' ys' -> Common c (xs1' +>+ x' :>: xs2) ys'++-- | 'Common' @cs xs ys@ represents two sequences of patches that have @cs@ in+-- common, in other words @cs +>+ xs@ and @cs +>+ ys@+data Common p C(o x y) where+ Common :: FL p C(o i) -> FL p C(i x) -> FL p C(i y) -> Common p C(o x y)++-- | 'pullCommonRL' @xs ys@ returns the set of patches that can be commuted+-- out of both @xs@ and @ys@ along with the remnants of both lists+pullCommonRL :: Patchy p => RL p C(x o) -> RL p C(y o) -> CommonRL p C(x y o)+pullCommonRL NilRL ys = CommonRL NilRL ys NilRL+pullCommonRL xs NilRL = CommonRL xs NilRL NilRL+pullCommonRL (x :<: xs) xys | Just ys <- removeRL x xys =+ case pullCommonRL xs ys of+ CommonRL xs' ys' c -> CommonRL xs' ys' (x :<: c)+pullCommonRL (x :<: xs) ys =+ case commuteWhatWeCanRL (xs :> x) of+ xs1 :> x' :> xs2 ->+ case pullCommonRL xs2 ys of+ CommonRL xs2' ys' c -> CommonRL (xs2' +<+ x' :<: xs1) ys' c++-- | 'CommonRL' @xs ys cs@' represents two sequences of patches that have @cs@+-- in common, in other words @xs +<+ cs@ and @ys +<+ cs@+data CommonRL p C(x y f) where+ CommonRL :: RL p C(x i) -> RL p C(y i) -> RL p C(i f) -> CommonRL p C(x y f)++instance PrimPatch prim => Apply (RealPatch prim) where+ type ApplyState (RealPatch prim) = ApplyState prim+ apply p = apply (effect p)++instance PrimPatch prim => RepairToFL (RealPatch prim) where+ applyAndTryToFixFL (Normal p) =+ mapMaybeSnd (mapFL_FL Normal) `liftM` applyAndTryToFixFL p+ applyAndTryToFixFL x = do apply x; return Nothing++instance PatchListFormat (RealPatch prim) where+ -- In principle we could use ListFormatDefault when prim /= V1 Prim patches,+ -- as those are the only case where we need to support a legacy on-disk+ -- format. In practice we don't expect RealPatch to be used with any other+ -- argument anyway, so it doesn't matter.+ patchListFormat = ListFormatV2++duplicate, etacilpud, conflictor, rotcilfnoc :: String+duplicate = "duplicate"+etacilpud = "etacilpud"+conflictor = "conflictor"+rotcilfnoc = "rotcilfnoc"++instance PrimPatch prim => ShowPatchBasic (RealPatch prim) where+ showPatch (Duplicate d) = blueText duplicate $$ showNon d+ showPatch (Etacilpud d) = blueText etacilpud $$ showNon d+ showPatch (Normal p) = showPrim NewFormat p+ showPatch (Conflictor i NilFL p) =+ blueText conflictor <+> showNons i <+> blueText "[]" $$ showNon p+ showPatch (Conflictor i cs p) =+ blueText conflictor <+> showNons i <+> blueText "[" $$+ showPrimFL NewFormat cs $$+ blueText "]" $$+ showNon p+ showPatch (InvConflictor i NilFL p) =+ blueText rotcilfnoc <+> showNons i <+> blueText "[]" $$ showNon p+ showPatch (InvConflictor i cs p) =+ blueText rotcilfnoc <+> showNons i <+> blueText "[" $$+ showPrimFL NewFormat cs $$+ blueText "]" $$+ showNon p++instance PrimPatch prim => ShowPatch (RealPatch prim) where+ showContextPatch (Normal p) = showContextPatch p+ showContextPatch c = return $ showPatch c+ summary = plainSummary+ summaryFL = plainSummary+ thing _ = "change"++instance PrimPatch prim => ReadPatch (RealPatch prim) where+ readPatch' = do+ skipSpace+ let str = string . BC.pack+ readConflictorPs = do+ i <- readNons+ ps <- bracketedFL (readPrim NewFormat) '[' ']'+ p <- readNon+ return (i, ps, p)+ choice [ do str duplicate+ p <- readNon+ return $ Sealed $ Duplicate p+ , do str etacilpud+ p <- readNon+ return $ Sealed $ Etacilpud p+ , do str conflictor+ (i, Sealed ps, p) <- readConflictorPs+ return $ Sealed $ Conflictor i (unsafeCoerceP ps) p+ , do str rotcilfnoc+ (i, Sealed ps, p) <- readConflictorPs+ return $ Sealed $ InvConflictor i ps p+ , do Sealed p <- readPrim NewFormat+ return $ Sealed $ Normal p+ ]++instance PrimPatch prim => Show (RealPatch prim C(x y)) where+ show p = renderString $ showPatch p++instance PrimPatch prim => Show2 (RealPatch prim) where+ showDict2 = ShowDictClass++instance PrimPatch prim => Nonable (RealPatch prim) where+ non (Duplicate d) = d+ non (Etacilpud d) = invertNon d -- FIXME !!! ???+ non (Normal p) = Non NilFL p+ non (Conflictor _ xx x) = commutePrimsOrAddToCtx (invertFL xx) x non (InvConflictor _ _ n) = invertNon n instance PrimPatch prim => Effect (RealPatch prim) where
src/Darcs/PrintPatch.hs view
@@ -27,6 +27,7 @@ import Storage.Hashed.Tree( Tree ) import Storage.Hashed.Monad( virtualTreeIO ) import Darcs.Arguments ( DarcsFlag, showFriendly )+import Darcs.Flags ( isUnified ) import Printer ( putDocLnWith ) import Darcs.ColorPrinter ( fancyPrinters ) import Darcs.External ( viewDocWith )@@ -34,8 +35,10 @@ -- | @'printFriendly' opts patch@ prints @patch@ in accordance with the -- flags in opts, ie, whether @--verbose@ or @--summary@ were passed at -- the command-line.-printFriendly :: Patchy p => [DarcsFlag] -> p C(x y) -> IO ()-printFriendly opts p = putDocLnWith fancyPrinters $ showFriendly opts p+printFriendly :: (Patchy p, ApplyState p ~ Tree) => (Maybe (Tree IO)) -> [DarcsFlag] -> p C(x y) -> IO ()+printFriendly (Just pristine) opts p+ | isUnified opts = virtualTreeIO (showContextPatch p) pristine >>= putDocLnWith fancyPrinters . fst+printFriendly _ opts p = putDocLnWith fancyPrinters $ showFriendly opts p -- | 'printPatch' prints a patch on standard output. printPatch :: Patchy p => p C(x y) -> IO ()
src/Darcs/Repository.hs view
@@ -46,8 +46,8 @@ ) where import System.Exit ( ExitCode(..), exitWith )-import Data.List ( isPrefixOf )-import Data.Maybe( catMaybes, isJust )+import Data.List ( isPrefixOf)+import Data.Maybe( catMaybes, isJust, listToMaybe ) import Darcs.Repository.State( readRecorded, readUnrecorded, readWorking, unrecordedChanges , readPending, readIndex, invalidateIndex@@ -369,13 +369,9 @@ return () cacheDir :: Cache -> Maybe String-cacheDir (Ca cs) = safeHead . catMaybes .flip map cs $ \x -> case x of+cacheDir (Ca cs) = listToMaybe . catMaybes .flip map cs $ \x -> case x of Cache Directory Writable x' -> Just x' _ -> Nothing--safeHead :: [a] -> Maybe a-safeHead [] = Nothing-safeHead (x:_) = Just x hashedPatchFileName :: PatchInfoAnd p C(a b) -> String hashedPatchFileName x = case extractHash x of
src/Darcs/Repository/Cache.hs view
@@ -233,7 +233,7 @@ copyFilesUsingCache :: OrOnlySpeculate -> Cache -> HashedDir -> [String] -> IO () copyFilesUsingCache oos cache subdir hs =- do forM_ hs $ copyFileUsingCache oos cache subdir+ forM_ hs $ copyFileUsingCache oos cache subdir data FromWhere = LocalOnly | Anywhere deriving ( Eq )@@ -411,7 +411,7 @@ reportBadSources = do sources <- getBadSourcesList let size = length sources- unless (null sources) $ do+ unless (null sources) $ hPutStrLn stderr $ concat [ "\nHINT: I could not reach the following ", englishNum size (Noun "repository") ":" , "\n", (intercalate "\n" (map (" " ++) sources))
src/Darcs/Repository/HashedRepo.hs view
@@ -413,6 +413,10 @@ x <- fst <$> readInventoryPrivate (Ca []) (darcsdir </> "inventories") i (i :) <$> maybe (return []) f x +-- | 'readPatchIds inventory' parses the content of a hashed_inventory file+-- after the "pristine:" and "Starting with inventory:" header lines have+-- been removed. The second value in the resulting tuples is the file hash+-- of the associated patch (the "hash:" line). readPatchIds :: B.ByteString -> [(PatchInfo, String)] readPatchIds inv | B.null inv = [] readPatchIds inv = case parseStrictly readPatchInfo inv of
src/Darcs/Repository/Internal.hs view
@@ -49,7 +49,7 @@ makeNewPending, seekRepo ) where -import Printer ( putDocLn, (<+>), text, ($$), redText, putDocWith, (<>), ($$))+import Printer ( putDocLn, (<+>), text, ($$), redText, putDocLnWith, (<>), ($$)) import Darcs.ColorPrinter (fancyPrinters) import Darcs.Repository.Prefs ( getPrefval )@@ -473,10 +473,10 @@ [] -> return False cfs -> if MarkConflicts `elem` opts || AllowConflicts `elem` opts || wantExternalMerge opts /= Nothing- then do putDocWith fancyPrinters $ + then do putDocLnWith fancyPrinters $ redText "We have conflicts in the following files:" $$ text (unwords cfs) return True- else do putDocWith fancyPrinters $+ else do putDocLnWith fancyPrinters $ redText "There are conflicts in the following files:" $$ text (unwords cfs) fail $ "Refusing to "++cmd++" patches leading to conflicts.\n"++ "If you would rather apply the patch and mark the conflicts,\n"++
src/Darcs/Repository/Merge.hs view
@@ -80,7 +80,7 @@ then seal NilFL else seal standard_resolved_pw (_,False) -> return $ seal standard_resolved_pw- (Just c, True) -> externalResolution working c+ (Just c, True) -> externalResolution working c opts (effect us +>+ pend) (effect them) pwprim debugMessage "Applying patches to the local directories..."
src/Darcs/Repository/State.hs view
@@ -45,7 +45,7 @@ import qualified Data.ByteString.Char8 as BSC import Darcs.Patch ( RepoPatch, PrimOf, sortCoalesceFL, fromPrim, effect )-import Darcs.Patch.Apply ( ApplyState, applyToTree, applyToFilepaths )+import Darcs.Patch.Apply ( ApplyState, applyToTree, effectOnFilePaths ) import Darcs.Witnesses.Ordered ( FL(..), (+>+), mapFL_FL ) import Darcs.Witnesses.Eq ( EqCheck(IsEq, NotEq) ) import Darcs.Witnesses.Unsafe ( unsafeCoerceP )@@ -93,7 +93,7 @@ restrictSubpaths repo subpaths = do Sealed pending <- readPendingLL repo let paths = map (fn2fp . sp2fn) subpaths- paths' = paths `union` applyToFilepaths pending paths+ paths' = paths `union` (effectOnFilePaths pending paths) anchored = map floatPath paths' restrictPaths :: FilterTree tree m => tree m -> tree m restrictPaths = filter (filterPaths anchored)
src/Darcs/Resolution.hs view
@@ -32,7 +32,7 @@ import Darcs.Diff( treeDiff ) import Darcs.Patch ( PrimOf, PrimPatch, RepoPatch, joinPatches, resolveConflicts,- applyToFilepaths, patchcontents,+ effectOnFilePaths, patchcontents, invert, listConflictedFiles, commute, applyToTree, fromPrim ) import Darcs.Patch.Apply( ApplyState ) import Darcs.RepoPath ( toFilePath )@@ -41,7 +41,7 @@ import CommandLine ( parseCmd ) import Darcs.Patch.PatchInfoAnd ( hopefully )-import Darcs.Utils ( askUser, filterFilePaths )+import Darcs.Utils ( askEnter, filterFilePaths ) import Darcs.Patch.Set ( PatchSet(..) ) #ifdef GADT_WITNESSES import Darcs.Patch.Set ( Origin )@@ -51,6 +51,7 @@ import Exec ( exec, Redirect(..) ) import Darcs.Lock ( withTempDir ) import Darcs.External ( cloneTree )+import Darcs.Flags ( DarcsFlag, wantGuiPause ) import qualified Storage.Hashed.Tree as Tree import Storage.Hashed ( writePlainTree, readPlainTree )@@ -72,20 +73,25 @@ doml mp [] = Sealed mp externalResolution :: forall p C(x y z a). (RepoPatch p, ApplyState p ~ Tree.Tree)- => Tree.Tree IO -> String -> FL (PrimOf p) C(x y) -> FL (PrimOf p) C(x z)- -> FL p C(y a)- -> IO (Sealed (FL (PrimOf p) C(a)))-externalResolution s1 c p1_prim p2_prim pmerged = do+ => Tree.Tree IO+ -> String -- ^ external merge tool command+ -> [DarcsFlag] -- ^ command line arguments+ -> FL (PrimOf p) C(x y)+ -> FL (PrimOf p) C(x z)+ -> FL p C(y a)+ -> IO (Sealed (FL (PrimOf p) C(a)))+externalResolution s1 c opts p1_prim p2_prim pmerged = do -- TODO: remove the following two once we can rely on GHC 7.2 / superclass equality let p1 :: FL p C(x y) = mapFL_FL fromPrim p1_prim p2 :: FL p C(x z) = mapFL_FL fromPrim p2_prim sa <- applyToTree (invert p1) s1 sm <- applyToTree pmerged s1 s2 <- applyToTree p2 sa- let nms = listConflictedFiles pmerged- nas = applyToFilepaths (invert pmerged) nms- n1s = applyToFilepaths p1 nas- n2s = applyToFilepaths p2 nas+ let effectOnFPs ps fps = effectOnFilePaths ps fps+ nms = listConflictedFiles pmerged+ nas = effectOnFPs (invert pmerged) nms+ n1s = effectOnFPs p1 nas+ n2s = effectOnFPs p2 nas ns = zip4 nas n1s n2s nms write_files tree fs = writePlainTree (Tree.filter (filterFilePaths fs) tree) "." in do@@ -109,23 +115,27 @@ withTempDir "version2" $ \absd2 -> do let d2 = toFilePath absd2 write_files s2 n2s- mapM_ (externallyResolveFile c da d1 d2 dm) ns+ mapM_ (externallyResolveFile c opts da d1 d2 dm) ns sc <- readPlainTree dc sfixed <- readPlainTree dm ftf <- filetypeFunction unFreeLeft `fmap` treeDiff ftf sc sfixed -externallyResolveFile :: String -> String -> String -> String -> String- -> (FilePath, FilePath, FilePath, FilePath)- -> IO ()-externallyResolveFile c da d1 d2 dm (fa, f1, f2, fm) = do+externallyResolveFile :: String -- ^ external merge tool command+ -> [DarcsFlag] -- ^ command line arguments+ -> String -- ^ path to merge base+ -> String -- ^ path to side 1 of the merge+ -> String -- ^ path to side 2 of the merge+ -> String -- ^ path where resolved content should go+ -> (FilePath, FilePath, FilePath, FilePath)+ -> IO ()+externallyResolveFile c opts da d1 d2 dm (fa, f1, f2, fm) = do putStrLn $ "Merging file "++fm++" by hand." ec <- run c [('1', d1</>f1), ('2', d2</>f2), ('a', da</>fa), ('o', dm</>fm), ('%', "%")] when (ec /= ExitSuccess) $ putStrLn $ "External merge command exited with " ++ show ec--- Warning: A do-notation statement discarded a result of type String.- _ <- askUser "Hit return to move on, ^C to abort the whole operation..."- return ()+ when (wantGuiPause opts) $+ askEnter "Hit return to move on, ^C to abort the whole operation..." run :: String -> [(Char,String)] -> IO ExitCode run c replacements =
src/Darcs/RunCommand.hs view
@@ -91,7 +91,7 @@ file_args <- commandGetArgPossibilities cmd putStrLn $ unlines $ getOptionsOptions (opts1++opts2) : file_args | otherwise -> considerRunning msuper cmd (addVerboseIfDebug opts) extra- (_,_,ermsgs) -> do fail $ chompNewline(unlines ermsgs)+ (_,_,ermsgs) -> fail $ chompNewline(unlines ermsgs) where addVerboseIfDebug opts | DebugVerbose `elem` opts = Debug:Verbose:opts | otherwise = opts @@ -109,21 +109,10 @@ extra <- (commandArgdefaults cmd) specops cwd old_extra when (Disable `elem` specops) $ fail $ "Command "++commandName cmd++" disabled with --disable option!"- if commandExtraArgs cmd < 0- then runWithHooks specops extra- else if length extra > commandExtraArgs cmd- then fail $ "Bad argument: `"++unwords extra++"'\n"++- getCommandMiniHelp msuper cmd- else if length extra < commandExtraArgs cmd- then fail $ "Missing argument: " ++- nth_arg (length extra + 1) ++- "\n" ++ getCommandMiniHelp msuper cmd- else runWithHooks specops extra+ case extraArgumentsError extra cmd msuper of+ Nothing -> runWithHooks specops extra+ Just msg -> fail msg where nubopts = nubOptions (uncurry (++) $ commandAlloptions cmd)- nth_arg n = nth_of n (commandExtraArgHelp cmd)- nth_of 1 (h:_) = h- nth_of n (_:hs) = nth_of (n-1) hs- nth_of _ [] = "UNDOCUMENTED" runWithHooks os ex = do here <- getCurrentDirectory checkMatchSyntax os@@ -144,6 +133,31 @@ postHookExitCode <- runPosthook os here exitWith postHookExitCode +-- Checks if the number of extra arguments matches the number of extra+-- arguments supported by the command as specified in `commandExtraArgs`.+-- Extra arguments are arguments that follow the command but aren't+-- considered a flag. In `darcs push xyz`, xyz would be an extra argument.+extraArgumentsError :: [String] -- extra commands provided by user+ -> DarcsCommand+ -> Maybe DarcsCommand+ -> Maybe String+extraArgumentsError extra cmd msuper+ | extraArgsCmd < 0 = Nothing+ | extraArgsInput > extraArgsCmd = Just badArg+ | extraArgsInput < extraArgsCmd = Just missingArg+ | otherwise = Nothing+ where+ extraArgsInput = length extra+ extraArgsCmd = commandExtraArgs cmd+ badArg = "Bad argument: `" ++ unwords extra +++ "'\n" ++ getCommandMiniHelp msuper cmd+ missingArg = "Missing argument: " ++ nthArg (length extra + 1) +++ "\n" ++ getCommandMiniHelp msuper cmd+ nthArg n = nthOf n (commandExtraArgHelp cmd)+ nthOf 1 (h:_) = h+ nthOf n (_:hs) = nthOf (n-1) hs+ nthOf _ [] = "UNDOCUMENTED"+ addCommandDefaults :: DarcsCommand -> [DarcsFlag] -> IO [DarcsFlag] addCommandDefaults cmd already = do let (opts1, opts2) = commandAlloptions cmd@@ -175,4 +189,4 @@ then fail $ "Command " ++ (commandName super) ++ " disabled with --disable option!" else fail $ "Invalid subcommand!\n\n" ++ subusage super- (_,_,ermsgs) -> do fail $ chompNewline(unlines ermsgs)+ (_,_,ermsgs) -> fail $ chompNewline(unlines ermsgs)
src/Darcs/SelectChanges.hs view
@@ -48,7 +48,7 @@ (+>+), lengthFL, mapFL_FL, spanFL, spanFL_M, reverseFL, (+<+), mapFL, filterFL ) import Darcs.Witnesses.WZipper( FZipper(..), left, right- , rightmost+ , rightmost, nullFZ , toEnd, toStart) import Darcs.Patch.Choices ( PatchChoices, patchChoices, patchChoicesTpsSub,@@ -112,17 +112,19 @@ , splitter :: Maybe (Splitter p) , files :: Maybe [FilePath] , matchCriterion :: MatchCriterion p- , jobname :: String }+ , jobname :: String+ , pristine :: Maybe (Tree IO)} -- | A 'PatchSelectionContext' for selecting 'Prim' patches. selectionContextPrim :: PrimPatch prim => String -> [DarcsFlag] -> Maybe (Splitter prim)- -> Maybe [FilePath] -> PatchSelectionContext prim-selectionContextPrim jn o spl fs =+ -> Maybe [FilePath] -> Maybe (Tree IO) -> PatchSelectionContext prim+selectionContextPrim jn o spl fs p = PSC { opts = o , splitter = spl , files = fs , matchCriterion = triv- , jobname = jn }+ , jobname = jn+ , pristine = p } -- | A 'PatchSelectionContext' for selecting full patches ('PatchInfoAnd' patches) selectionContext :: (RepoPatch p) => String -> [DarcsFlag] -> Maybe (Splitter (PatchInfoAnd p))@@ -132,7 +134,8 @@ , splitter = spl , files = fs , matchCriterion = iswanted- , jobname = jn }+ , jobname = jn+ , pristine = Nothing } -- | The dynamic parameters for interactive selection of patches. data InteractiveSelectionContext p C(x y) = ISC { total :: Int@@ -215,7 +218,7 @@ repr FirstReversed (Sealed2 p) = Sealed2 (invert p) -- | The equivalent of 'selectChanges' for the @darcs changes@ command-viewChanges :: Patchy p => [DarcsFlag] -> [Sealed2 p] -> IO ()+viewChanges :: (Patchy p, ApplyState p ~ Tree) => [DarcsFlag] -> [Sealed2 p] -> IO () viewChanges opts' ps = textView opts' Nothing 0 [] ps -- | The type of the answers to a "shall I [wiggle] that [foo]?" question@@ -242,67 +245,77 @@ keysFor = concatMap (map kp) -- | The function for selecting a patch to amend record. Read at your own risks.-withSelectedPatchFromRepo :: forall p C(r u t). (RepoPatch p, ApplyState p ~ Tree)- => String -> Repository p C(r u t) -> [DarcsFlag]- -> (FORALL(a) (FL (PatchInfoAnd p) :> PatchInfoAnd p) C(a r) -> IO ()) -> IO ()+withSelectedPatchFromRepo ::+ forall p C(r u t). (RepoPatch p, ApplyState p ~ Tree)+ => String -- name of calling command (always "amend" as of now)+ -> Repository p C(r u t)+ -> [DarcsFlag]+ -> (FORALL(a) (FL (PatchInfoAnd p) :> PatchInfoAnd p) C(a r) -> IO ())+ -> IO () withSelectedPatchFromRepo jn repository o job = do- p_s <- readRepo repository- sp <- wspfr jn (matchAPatchread o)- (newset2RL p_s) NilFL+ patchSet <- readRepo repository+ sp <- wspfr jn (matchAPatchread o) (newset2RL patchSet) NilFL case sp of- Just (FlippedSeal (skipped :> selected')) -> job (skipped :> selected')- Nothing -> do putStrLn $ "Cancelling "++jn++" since no patch was selected."+ Just (FlippedSeal (skipped :> selected')) -> job (skipped :> selected')+ Nothing ->+ putStrLn $ "Cancelling " ++ jn ++ " since no patch was selected." --- | This ensures that the selected patch commutes freely with the skipped patches, including pending--- and also that the skipped sequences has an ending context that matches the recorded state, z,--- of the repository.+-- | This ensures that the selected patch commutes freely with the skipped+-- patches, including pending and also that the skipped sequences has an+-- ending context that matches the recorded state, z, of the repository. wspfr :: (RepoPatch p, ApplyState p ~ Tree)- => String -> (FORALL(a b) (PatchInfoAnd p) C(a b) -> Bool)- -> RL (PatchInfoAnd p) C(x y) -> FL (PatchInfoAnd p) C(y u)+ => String+ -> (FORALL(a b) (PatchInfoAnd p) C(a b) -> Bool)+ -> RL (PatchInfoAnd p) C(x y)+ -> FL (PatchInfoAnd p) C(y u) -> IO (Maybe (FlippedSeal (FL (PatchInfoAnd p) :> (PatchInfoAnd p)) C(u))) wspfr _ _ NilRL _ = return Nothing-wspfr jn matches (p:<:pps) skipped+wspfr jn matches remaining@(p:<:pps) skipped | not $ matches p = wspfr jn matches pps (p:>:skipped) | otherwise = case commuteFLorComplain (p :> skipped) of Left _ -> do putStrLn "\nSkipping depended-upon patch:"- printFriendly [] p+ printFriendly Nothing [] p wspfr jn matches pps (p:>:skipped) Right (skipped' :> p') -> do- printFriendly [] p- let repeat_this = wspfr jn matches (p:<:pps) skipped- basic_options =- [[ KeyPress 'y' (jn++" this patch")- , KeyPress 'n' ("don't "++jn++" it")+ printFriendly Nothing [] p+ yorn <- promptChar $+ PromptConfig { pPrompt = prompt'+ , pBasicCharacters = keysFor basicOptions+ , pAdvancedCharacters = keysFor advancedOptions+ , pDefault = Just 'n'+ , pHelp = "?h" }+ case yorn of+ 'y' -> return $ Just $ flipSeal $ skipped' :> p'+ 'n' -> nextPatch+ 'j' -> nextPatch+ 'k' -> case skipped of+ NilFL -> repeatThis+ (prev :>: skipped') -> wspfr jn matches (prev :<: remaining) skipped'+ 'v' -> printPatch p >> repeatThis+ 'p' -> printPatchPager p >> repeatThis+ 'x' -> do putDocLn $ prefix " " $ summary p+ repeatThis+ 'q' -> do putStrLn $ jnCapital ++ " cancelled."+ exitWith $ ExitSuccess+ _ -> do putStrLn $ helpFor jn basicOptions advancedOptions+ repeatThis+ where jnCapital = (toUpper $ head jn) : tail jn+ repeatThis = wspfr jn matches (p:<:pps) skipped+ prompt' = "Shall I " ++ jn ++ " this patch?"+ nextPatch = wspfr jn matches pps (p:>:skipped)+ basicOptions =+ [[ KeyPress 'y' (jn ++ " this patch")+ , KeyPress 'n' ("don't " ++ jn ++ " it")+ , KeyPress 'j' "skip to next patch" , KeyPress 'k' "back up to previous patch" ]]- advanced_options =+ advancedOptions = [[ KeyPress 'v' "view this patch in full" , KeyPress 'p' "view this patch in full with pager" , KeyPress 'x' "view a summary of this patch"- , KeyPress 'q' ("cancel "++jn)+ , KeyPress 'q' ("cancel " ++ jn) ]]- let prompt' = "Shall I "++jn++" this patch?"- yorn <- promptChar $ PromptConfig { pPrompt = prompt'- , pBasicCharacters = keysFor basic_options- , pAdvancedCharacters = keysFor advanced_options- , pDefault = Just 'n'- , pHelp = "?h" }- case yorn of- 'y' -> return $ Just $ flipSeal $ skipped' :> p'- 'n' -> wspfr jn matches pps (p:>:skipped)- 'k' -> case skipped of- NilFL -> repeat_this- (prev :>: skipped') -> wspfr jn matches (prev :<: p :<: pps) skipped'- 'v' -> printPatch p >> repeat_this- 'p' -> printPatchPager p >> repeat_this- 'x' -> do putDocLn $ prefix " " $ summary p- repeat_this- 'q' -> do putStrLn $ jn_cap++" cancelled."- exitWith $ ExitSuccess- _ -> do putStrLn $ helpFor jn basic_options advanced_options- repeat_this- where jn_cap = (toUpper $ head jn) : tail jn -- After selecting with a splitter, the results may not be canonical canonizeAfterSplitter :: (FL p :> FL p) C(x y) -> Reader (PatchSelectionContext p) ((FL p :> FL p) C(x y))@@ -311,7 +324,7 @@ let canonizeIfNeeded = maybe id canonizeSplit mspl return $ canonizeIfNeeded x :> canonizeIfNeeded y -realSelectChanges :: forall p C(x y). Patchy p+realSelectChanges :: forall p C(x y). (Patchy p, ApplyState p ~ Tree) => WhichChanges -> PatchChoices p C(x y) -> PatchSelectionM p IO (PatchChoices p C(x y))@@ -410,20 +423,25 @@ justDone n = modify $ \isc -> isc{ current = current isc + n} -- | The actual interactive selection process.-textSelect :: forall p C(x y) . Patchy p => WhichChanges ->+textSelect :: forall p C(x y) . (Patchy p, ApplyState p ~ Tree) => WhichChanges -> FL (TaggedPatch p) C(x y) -> PatchChoices p C(x y) -> PatchSelectionM p IO (PatchChoices p C(x y)) textSelect whch tps' pcs = do userSelection <- execStateT (skipMundane whch >> showCur whch >>- textSelect' whch) $+ textSelectIfAny) $ ISC { total = lengthFL tps' , current = 0 , tps = FZipper NilRL tps' , choices = pcs } return $ choices userSelection+ where textSelectIfAny = do+ z <- gets tps+ if rightmost z+ then return ()+ else textSelect' whch -textSelect' :: Patchy p => WhichChanges ->+textSelect' :: (Patchy p, ApplyState p ~ Tree) => WhichChanges -> InteractiveSelectionM p C(x y) () textSelect' whch = do z <- gets tps@@ -461,9 +479,9 @@ optionsNav :: String -> [KeyPress] optionsNav aThing =- [ KeyPress 'j' ("skip to next "++ aThing)- , KeyPress 'k' ("back up to previous "++ aThing)- , KeyPress 'o' ("start over from first " ++ aThing)]+ [ KeyPress 'j' ("skip to next "++ aThing)+ , KeyPress 'k' ("back up to previous "++ aThing)+ , KeyPress 'g' ("start over from the first "++aThing)] optionsSplit :: Maybe (Splitter a) -> String -> [KeyPress] optionsSplit split aThing@@ -686,7 +704,7 @@ } -- | Ask the user what to do with the next patch.-textSelectOne :: forall p C(x y). Patchy p => WhichChanges+textSelectOne :: forall p C(x y). (Patchy p, ApplyState p ~ Tree) => WhichChanges -> InteractiveSelectionM p C(x y) () textSelectOne whichch = do c <- currentPatch@@ -702,7 +720,7 @@ theSlot <- liftChoices $ patchSlot' tp let the_default = getDefault (whichch == Last || whichch == FirstReversed) theSlot- jn_cap = (toUpper $ head jn) : tail jn+ jnCapital = (toUpper $ head jn) : tail jn yorn <- promptUser singleFile the_default let nextPatch = skipMundane whichch >> showCur whichch case yorn of@@ -721,44 +739,46 @@ 'l' -> printSelected whichch >> showCur whichch 'x' -> liftIO $ unseal2 printSummary reprCur 'd' -> skipAll- 'o' -> backAll >> showCur whichch+ 'g' -> backAll >> showCur whichch 'a' -> do askConfirmation modChoices $ selectAllMiddles (whichch == Last || whichch == FirstReversed) skipAll 'q' -> liftIO $- do putStrLn $ jn_cap++" cancelled."+ do putStrLn $ jnCapital++" cancelled." exitWith $ ExitSuccess 'j' -> skipOne >> showCur whichch 'k' -> backOne >> showCur whichch- _ -> do liftIO . putStrLn $ helpFor jn basicOptions advancedOptions+ _ -> do+ liftIO . putStrLn $ helpFor jn basicOptions advancedOptions -- | Shows the current patch as it should be seen by the user.-showCur :: forall p C(x y) . Patchy p => WhichChanges+showCur :: forall p C(x y) . (Patchy p, ApplyState p ~ Tree) => WhichChanges -> InteractiveSelectionM p C(x y) () showCur whichch = do o <- asks opts+ p <- asks pristine c <- currentPatch case c of Nothing -> return () Just (Sealed2 tp) -> do let reprCur = repr whichch (Sealed2 (tpPatch tp))- liftIO . (unseal2 (printFriendly o)) $ reprCur+ liftIO . (unseal2 (printFriendly p o)) $ reprCur -- | The interactive part of @darcs changes@-textView :: forall p . Patchy p => [DarcsFlag] -> Maybe Int -> Int+textView :: forall p . (Patchy p, ApplyState p ~ Tree) => [DarcsFlag] -> Maybe Int -> Int -> [Sealed2 p] -> [Sealed2 p] -> IO () textView _ _ _ _ [] = return () textView o n_max n ps_done ps_todo@(p:ps_todo') = do- unseal2 (printFriendly o) p- repeat_this -- prompt the user+ unseal2 (printFriendly Nothing o) p+ repeatThis -- prompt the user where prev_patch :: IO () prev_patch = case ps_done of- [] -> repeat_this+ [] -> repeatThis (p':ps_done') -> textView o n_max (n-1) ps_done' (p':ps_todo)@@ -783,7 +803,7 @@ [ KeyPress 'q' "quit view changes" , KeyPress 'k' "back up to previous patch" , KeyPress 'j' "skip to next patch"- , KeyPress 'o' "start over from the first patch"+ , KeyPress 'g' "start over from the first patch" , KeyPress 'c' "count total patch number" ] basicOptions = [ options_yn ] advancedOptions =@@ -792,24 +812,24 @@ ++ [ optionsNav' ] prompt' = "Shall I view this patch? " ++ "(" ++ show (n+1) ++ "/" ++ maybe "?" show n_max ++ ")"- repeat_this :: IO ()- repeat_this = do+ repeatThis :: IO ()+ repeatThis = do yorn <- promptChar (PromptConfig prompt' (keysFor basicOptions) (keysFor advancedOptions) (Just 'n') "?h") case yorn of 'y' -> unseal2 printPatch p >> next_patch 'n' -> next_patch- 'v' -> unseal2 printPatch p >> repeat_this- 'p' -> unseal2 printPatchPager p >> repeat_this+ 'v' -> unseal2 printPatch p >> repeatThis+ 'p' -> unseal2 printPatchPager p >> repeatThis 'x' -> do putDocLn $ prefix " " $ unseal2 summary p- repeat_this+ repeatThis 'q' -> exitWith ExitSuccess 'k' -> prev_patch 'j' -> next_patch- 'o' -> first_patch+ 'g' -> first_patch 'c' -> textView o count_n_max n ps_done ps_todo _ -> do putStrLn $ helpFor "view changes" basicOptions advancedOptions- repeat_this+ repeatThis count_n_max | isJust n_max = n_max | otherwise = Just $ length ps_done + length ps_todo
src/Darcs/Ssh.hs view
@@ -1,56 +1,79 @@ {-# LANGUAGE CPP, ForeignFunctionInterface #-} -module Darcs.Ssh (- copySSH, SSHCmd(..), getSSH,- environmentHelpSsh, environmentHelpScp, environmentHelpSshPort,- remoteDarcs+-- |+-- Module : Darcs.Ssh+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable++module Darcs.Ssh+ (+ copySSH+ , SSHCmd(..)+ , getSSH+ , environmentHelpSsh+ , environmentHelpScp+ , environmentHelpSshPort+ , remoteDarcs ) where + import Prelude hiding ( lookup, catch )-import qualified Ratified( hGetContents ) -import System.Exit ( ExitCode(..) )+import qualified Ratified ( hGetContents )+ import System.Environment ( getEnv )-#ifndef WIN32-import System.Posix.Process ( getProcessID )-#else-import Darcs.Utils ( showHexLen )+import System.Exit ( ExitCode(..) )++#ifdef WIN32 import Data.Bits ( (.&.) ) import System.Random ( randomIO )+import Darcs.Utils ( showHexLen ) #endif-import System.IO ( Handle, hSetBinaryMode, hPutStrLn, hGetLine, hFlush )-import System.IO.Unsafe ( unsafePerformIO )-import System.Directory ( doesFileExist, createDirectoryIfMissing )+ import Control.Monad ( when )-import System.Process ( runInteractiveProcess ) +import qualified Data.ByteString as B (ByteString, hGet, writeFile )+ import Data.Map ( Map, empty, insert, lookup ) import Data.IORef ( IORef, newIORef, readIORef, modifyIORef ) +import System.IO ( Handle, hSetBinaryMode, hPutStrLn, hGetLine, hFlush )+import System.IO.Unsafe ( unsafePerformIO )+import System.Process ( runInteractiveProcess )+ import Darcs.SignalHandler ( catchNonSignal )-import Darcs.Utils ( breakCommand, prettyException, catchall )-import Darcs.Global ( atexit, withDebugMode- , defaultSsh, SshSettings- )-import Darcs.Lock ( tempdirLoc, removeFileMayNotExist )+import Darcs.Flags( RemoteDarcs(..) )+import Darcs.Global ( defaultSsh, SshSettings) import Darcs.URL (SshFilePath(..), urlOf)-import Exec ( exec, Redirects, Redirect(..), )+import Darcs.Utils ( breakCommand, prettyException, catchall )+import Exec ( exec, Redirect(..), ) import Progress ( withoutProgress, debugMessage, debugFail )-import Darcs.Flags( RemoteDarcs(..) ) -import qualified Data.ByteString as B (ByteString, hGet, writeFile ) import qualified Darcs.Global as Settings -{-# NOINLINE sshConnections #-}+ sshConnections :: IORef (Map String (Maybe Connection)) sshConnections = unsafePerformIO $ newIORef empty+{-# NOINLINE sshConnections #-} -data Connection = C { inp :: !Handle, out :: !Handle, err :: !Handle, deb :: String -> IO () } --- | @withSSHConnection rdarcs destination withconnection withoutconnection@--- performs an action on a remote host. If we are already connected to @destination@,+data Connection = C+ { inp :: !Handle+ , out :: !Handle+ , err :: !Handle+ , deb :: String -> IO ()+ }+++-- | @withSSHConnection rdarcs repoid withconnection withoutconnection@+-- performs an action on a remote host. If we are already connected to @repoid @, -- then it does @withconnection@, else @withoutconnection@.-withSSHConnection :: String -> SshFilePath -> (Connection -> IO a) -> IO a -> IO a+withSSHConnection :: String -- ^ rdarcs+ -> SshFilePath -- ^ Destination repo id+ -> (Connection -> IO a) -- ^ withconnection+ -> IO a -- ^ withoutconnection+ -> IO a withSSHConnection rdarcs repoid withconnection withoutconnection = withoutProgress $ do cs <- readIORef sshConnections@@ -85,10 +108,13 @@ return Nothing maybe withoutconnection withconnection mc -severSSHConnection :: SshFilePath -> IO ()-severSSHConnection x = do debugMessage $ "Severing ssh failed connection to "++(sshUhost x)- modifyIORef sshConnections (insert (urlOf x) Nothing)+severSSHConnection :: SshFilePath+ -> IO ()+severSSHConnection x = do+ debugMessage $ "Severing ssh failed connection to " ++ (sshUhost x)+ modifyIORef sshConnections (insert (urlOf x) Nothing) + grabSSH :: SshFilePath -> Connection -> IO B.ByteString grabSSH dest c = do debugMessage $ "grabSSH dest=" ++ urlOf dest@@ -116,80 +142,96 @@ [] -> failwith "An error occurred" else failwith "Error" -remoteDarcs :: RemoteDarcs -> String+remoteDarcs :: RemoteDarcs+ -> String remoteDarcs DefaultRemoteDarcs = "darcs" remoteDarcs (RemoteDarcs x) = x -copySSH :: RemoteDarcs -> SshFilePath -> FilePath -> IO ()++copySSH :: RemoteDarcs+ -> SshFilePath+ -> FilePath+ -> IO () copySSH remote dest to | rdarcs <- remoteDarcs remote = do- debugMessage $ "copySSH file: " ++ urlOf dest- withSSHConnection rdarcs dest (\c -> grabSSH dest c >>= B.writeFile to) $- do let u = escape_dollar $ urlOf dest- (scp, args) <- getSSH SCP- r <- exec scp (args ++ [u, to]) (AsIs,AsIs,AsIs)- when (r /= ExitSuccess) $- debugFail $ "(scp) failed to fetch: " ++ u- where {- '$' in filenames is troublesome for scp, for some reason.. -}- escape_dollar :: String -> String- escape_dollar = concatMap tr- where tr '$' = "\\$"- tr c = [c]+ debugMessage $ "copySSH file: " ++ urlOf dest+ withSSHConnection rdarcs dest (\c -> grabSSH dest c >>= B.writeFile to) $ do+ let u = escape_dollar $ urlOf dest+ (scp, args) <- getSSH SCP+ r <- exec scp (args ++ [u, to]) (AsIs,AsIs,AsIs)+ when (r /= ExitSuccess) $+ debugFail $ "(scp) failed to fetch: " ++ u+ where+ -- '$' in filenames is troublesome for scp, for some reason.+ escape_dollar :: String -> String+ escape_dollar = concatMap tr+ where+ tr '$' = "\\$"+ tr c = [c] + -- --------------------------------------------------------------------- -- older ssh helper functions -- --------------------------------------------------------------------- -data SSHCmd = SSH | SCP | SFTP+data SSHCmd = SSH+ | SCP+ | SFTP -fromSshCmd :: SshSettings -> SSHCmd -> String++fromSshCmd :: SshSettings+ -> SSHCmd+ -> String fromSshCmd s SSH = Settings.ssh s fromSshCmd s SCP = Settings.scp s fromSshCmd s SFTP = Settings.sftp s + -- | Return the command and arguments needed to run an ssh command -- First try the appropriate darcs environment variable and SSH_PORT -- defaulting to "ssh" and no specified port.-getSSH :: SSHCmd -> IO (String, [String])-getSSH cmd =- do -- port+getSSH :: SSHCmd+ -> IO (String, [String])+getSSH cmd = do port <- (portFlag cmd `fmap` getEnv "SSH_PORT") `catchall` return [] let (ssh, ssh_args) = breakCommand command- -- return (ssh, ssh_args ++ port)- where- command = fromSshCmd defaultSsh cmd- portFlag SSH x = ["-p", x]- portFlag SCP x = ["-P", x]- portFlag SFTP x = ["-oPort="++x]+ where+ command = fromSshCmd defaultSsh cmd+ portFlag SSH x = ["-p", x]+ portFlag SCP x = ["-P", x]+ portFlag SFTP x = ["-oPort=" ++ x] + environmentHelpSsh :: ([String], [String]) environmentHelpSsh = (["DARCS_SSH"], [- "Repositories of the form [user@]host:[dir] are taken to be remote",- "repositories, which Darcs accesses with the external program ssh(1).",- "",- "The environment variable $DARCS_SSH can be used to specify an",- "alternative SSH client. Arguments may be included, separated by",- "whitespace. The value is not interpreted by a shell, so shell",- "constructs cannot be used; in particular, it is not possible for the",- "program name to contain whitespace by using quoting or escaping."])+ "Repositories of the form [user@]host:[dir] are taken to be remote",+ "repositories, which Darcs accesses with the external program ssh(1).",+ "",+ "The environment variable $DARCS_SSH can be used to specify an",+ "alternative SSH client. Arguments may be included, separated by",+ "whitespace. The value is not interpreted by a shell, so shell",+ "constructs cannot be used; in particular, it is not possible for the",+ "program name to contain whitespace by using quoting or escaping."]) + environmentHelpScp :: ([String], [String]) environmentHelpScp = (["DARCS_SCP", "DARCS_SFTP"], [- "When reading from a remote repository, Darcs will attempt to run",- "`darcs transfer-mode' on the remote host. This will fail if the",- "remote host only has Darcs 1 installed, doesn't have Darcs installed",- "at all, or only allows SFTP.",- "",- "If transfer-mode fails, Darcs will fall back on scp(1) and sftp(1).",- "The commands invoked can be customized with the environment variables",- "$DARCS_SCP and $DARCS_SFTP respectively, which behave like $DARCS_SSH.",- "If the remote end allows only sftp, try setting DARCS_SCP=sftp."])+ "When reading from a remote repository, Darcs will attempt to run",+ "`darcs transfer-mode' on the remote host. This will fail if the",+ "remote host only has Darcs 1 installed, doesn't have Darcs installed",+ "at all, or only allows SFTP.",+ "",+ "If transfer-mode fails, Darcs will fall back on scp(1) and sftp(1).",+ "The commands invoked can be customized with the environment variables",+ "$DARCS_SCP and $DARCS_SFTP respectively, which behave like $DARCS_SSH.",+ "If the remote end allows only sftp, try setting DARCS_SCP=sftp."]) + environmentHelpSshPort :: ([String], [String]) environmentHelpSshPort = (["SSH_PORT"], [- "If this environment variable is set, it will be used as the port",- "number for all SSH calls made by Darcs (when accessing remote",- "repositories over SSH). This is useful if your SSH server does not",- "run on the default port, and your SSH client does not support",- "ssh_config(5). OpenSSH users will probably prefer to put something",- "like `Host *.example.net Port 443' into their ~/.ssh/config file."])+ "If this environment variable is set, it will be used as the port",+ "number for all SSH calls made by Darcs (when accessing remote",+ "repositories over SSH). This is useful if your SSH server does not",+ "run on the default port, and your SSH client does not support",+ "ssh_config(5). OpenSSH users will probably prefer to put something",+ "like `Host *.example.net Port 443' into their ~/.ssh/config file."])
src/Darcs/TheCommands.hs view
@@ -20,7 +20,7 @@ import Prelude () import Darcs.Commands.Add ( add )-import Darcs.Commands.AmendRecord ( amendrecord )+import Darcs.Commands.AmendRecord ( amendrecord, amendunrecord ) import Darcs.Commands.Annotate ( annotate ) import Darcs.Commands.Apply ( apply ) import Darcs.Commands.Changes ( changes, log )@@ -46,6 +46,7 @@ import Darcs.Commands.Send ( send ) import Darcs.Commands.SetPref ( setpref ) import Darcs.Commands.Tag ( tag )+import Darcs.Commands.Test ( test ) import Darcs.Commands.TrackDown ( trackdown ) import Darcs.Commands.TransferMode ( transferMode ) import Darcs.Commands.Unrecord ( unrecord, unpull, obliterate )@@ -69,6 +70,7 @@ CommandData record, HiddenCommand commit, CommandData unrecord, CommandData amendrecord,+ HiddenCommand amendunrecord, CommandData markconflicts, GroupName "Direct modification of the repository:", CommandData tag,@@ -78,6 +80,7 @@ CommandData changes, HiddenCommand log, CommandData annotate, CommandData dist,+ CommandData test, CommandData trackdown, CommandData showCommand, HiddenCommand list, HiddenCommand query, HiddenCommand transferMode,
src/Darcs/Utils.hs view
@@ -1,114 +1,168 @@+{-# LANGUAGE CPP, ForeignFunctionInterface #-}++-- |+-- Module : Exec+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable+-- -- Various utility functions that do not belong anywhere else. -{-# LANGUAGE CPP, ForeignFunctionInterface #-}+module Darcs.Utils+ (+ ortryrunning+ , nubsort+ , breakCommand+ , showHexLen+ , maybeGetEnv+ , formatPath+ -- * Monads+ , firstJustIO+ -- * User prompts+ , askEnter+ , askUser+ , askUserListItem+ , PromptConfig(..)+ , promptYorn+ , promptChar+ -- * Text+ , getViewer+ , editFile+ , runEditor+ , stripCr+ -- * Help+ , environmentHelpEditor+ , environmentHelpPager+ -- * Errors and exceptions+ , catchall+ , clarifyErrors+ , prettyException+ , prettyError+ , addToErrorLoc+ -- * Files and directories+ , getFileStatus+ , withCurrentDirectory+ , withUMask+ -- * Locales+ , setDarcsEncodings+ , getSystemEncoding+ , isUTF8Locale+ -- * Tree filtering.+ , filterFilePaths+ , filterPaths+ -- * Tree lookup.+ , treeHas+ , treeHasDir+ , treeHasFile+ , treeHasAnycase+ ) where -module Darcs.Utils ( ortryrunning, nubsort, breakCommand- , showHexLen- , maybeGetEnv- , formatPath- -- * Monads- , firstJustIO- -- * User prompts- , askUser, askUserListItem- , PromptConfig(..), promptYorn, promptChar- -- * Text- , getViewer, editFile, runEditor- , stripCr- -- * Help- , environmentHelpEditor, environmentHelpPager- -- * Errors and exceptions- , catchall- , clarifyErrors, prettyException, prettyError- , addToErrorLoc- -- * Files and directories- , getFileStatus- , withCurrentDirectory- , withUMask- -- * Locales- , getSystemEncoding- , isUTF8Locale- -- * Tree filtering.- , filterFilePaths, filterPaths- -- * Tree lookup.- , treeHas, treeHasDir, treeHasFile, treeHasAnycase- ) where import Prelude hiding ( catch )+ import Control.Exception.Extensible ( bracket, bracket_, catch, try, IOException, SomeException, Exception(fromException) )-import System.IO.Error ( annotateIOError, isUserError, ioeGetErrorString- , isDoesNotExistError, ioeGetFileName )--import Darcs.SignalHandler ( catchNonSignal )-import Numeric ( showHex )-import System.Directory ( doesFileExist )-import System.Exit ( ExitCode(..) )-import System.Environment ( getEnv )-import Data.Char ( toUpper, toLower, isSpace )-import Darcs.RepoPath ( FilePathLike, getCurrentDirectory, setCurrentDirectory, toFilePath )-import Data.Maybe ( isJust )-import Data.List ( group, sort ) import Control.Monad ( when, forM ) import Control.Monad.Error( MonadError )-import Exec ( execInteractive )-import Foreign.C.String ( CString, withCString, peekCString )-import Foreign.C.Error ( throwErrno )-import Foreign.C.Types ( CInt )-import Text.Regex+import Control.Monad.State.Strict( gets ) +import qualified Data.ByteString as B ( readFile ) import qualified Data.ByteString.Char8 as BSC -import System.Posix.Files( getSymbolicLinkStatus, FileStatus )+import Data.Char ( toUpper, toLower, isSpace )+import Data.List ( group, sort )+import Data.Maybe ( isJust ) -import Progress ( withoutProgress )+import Foreign.C.String ( CString, withCString, peekCString )+import Foreign.C.Error ( throwErrno )+import Foreign.C.Types ( CInt ) -import System.Console.Haskeline ( runInputT, defaultSettings, getInputLine,- getInputChar, outputStr, outputStrLn )-import qualified Data.ByteString as B ( readFile )+#ifdef FORCE_CHAR8_ENCODING+import GHC.IO.Encoding ( setFileSystemEncoding, setForeignEncoding, char8 )+#endif -import Control.Monad.State.Strict( gets ) import Storage.Hashed.AnchoredPath( AnchoredPath(..), Name(..), isPrefix, floatPath ) import Storage.Hashed.Monad( withDirectory, fileExists, directoryExists , virtualTreeMonad, currentDirectory , TreeMonad ) import qualified Storage.Hashed.Monad as HS ( exists, tree )+ import Storage.Hashed.Tree( Tree, listImmediate, findTree ) -showHexLen :: (Integral a, Show a) => Int -> a -> String+import System.Console.Haskeline ( runInputT, defaultSettings, getInputLine,+ getInputChar, outputStr, outputStrLn )+import System.Directory ( doesFileExist )+import System.Environment ( getEnv )+import System.Exit ( ExitCode(..) )+import System.IO.Error ( annotateIOError, isUserError, ioeGetErrorString+ , isDoesNotExistError, ioeGetFileName )+import System.Posix.Files( getSymbolicLinkStatus, FileStatus )++import Text.Regex++import Darcs.RepoPath ( FilePathLike, getCurrentDirectory, setCurrentDirectory, toFilePath )+import Darcs.SignalHandler ( catchNonSignal )++import Exec ( execInteractive )+import Numeric ( showHex )+import Progress ( withoutProgress )+++showHexLen :: (Integral a, Show a)+ => Int+ -> a+ -> String showHexLen n x = let s = showHex x "" in replicate (n - length s) ' ' ++ s -addToErrorLoc :: IOException -> String -> IOException++addToErrorLoc :: IOException+ -> String+ -> IOException addToErrorLoc ioe s = annotateIOError ioe s Nothing Nothing -catchall :: IO a -> IO a -> IO a++catchall :: IO a+ -> IO a+ -> IO a a `catchall` b = a `catchNonSignal` (\_ -> b) -maybeGetEnv :: String -> IO (Maybe String)++maybeGetEnv :: String+ -> IO (Maybe String) maybeGetEnv s = fmap Just (getEnv s) `catchall` return Nothing -- err can only be isDoesNotExist --- |The firstJustM returns the first Just entry in a list of monadic operations. This is close to--- `listToMaybe `fmap` sequence`, but the sequence operator evaluates all monadic members of the--- list before passing it along (i.e. sequence is strict). The firstJustM is lazy in that list--- member monads are only evaluated up to the point where the first Just entry is obtained.-firstJustM :: Monad m => [m (Maybe a)] -> m (Maybe a)+-- | The firstJustM returns the first Just entry in a list of monadic+-- operations. This is close to `listToMaybe `fmap` sequence`, but the sequence+-- operator evaluates all monadic members of the list before passing it along+-- (i.e. sequence is strict). The firstJustM is lazy in that list member monads+-- are only evaluated up to the point where the first Just entry is obtained.+firstJustM :: Monad m+ => [m (Maybe a)]+ -> m (Maybe a) firstJustM [] = return Nothing firstJustM (e:es) = e >>= (\v -> if isJust v then return v else firstJustM es) --- |The firstJustIO is a slight modification to firstJustM: the--- entries in the list must be IO monad operations and the--- firstJustIO will silently turn any monad call that throws an--- exception into Nothing, basically causing it to be ignored.-firstJustIO :: [IO (Maybe a)] -> IO (Maybe a)++-- | The firstJustIO is a slight modification to firstJustM: the entries in the+-- list must be IO monad operations and the firstJustIO will silently turn any+-- monad call that throws an exception into Nothing, basically causing it to be+-- ignored.+firstJustIO :: [IO (Maybe a)]+ -> IO (Maybe a) firstJustIO = firstJustM . map (`catchall` return Nothing) -clarifyErrors :: IO a -> String -> IO a+clarifyErrors :: IO a+ -> String+ -> IO a clarifyErrors a e = a `catch` (\x -> fail $ unlines [prettyException x,e]) -prettyException :: SomeException -> String++prettyException :: SomeException+ -> String prettyException e | Just ioe <- fromException e, isUserError ioe = ioeGetErrorString ioe prettyException e | Just ioe <- fromException e, isDoesNotExistError ioe = case ioeGetFileName ioe of@@ -116,25 +170,36 @@ Nothing -> show e prettyException e = show e + prettyError :: IOError -> String prettyError e | isUserError e = ioeGetErrorString e | otherwise = show e + -- | Given two shell commands as arguments, execute the former. The -- latter is then executed if the former failed because the executable -- wasn't found (code 127), wasn't executable (code 126) or some other -- exception occurred. Other failures (such as the user holding ^C) -- do not cause the second command to be tried.-ortryrunning :: IO ExitCode -> IO ExitCode -> IO ExitCode+ortryrunning :: IO ExitCode+ -> IO ExitCode+ -> IO ExitCode a `ortryrunning` b = do ret <- try a case ret of (Right (ExitFailure 126)) -> b -- command not executable (Right (ExitFailure 127)) -> b -- command not found+#ifdef WIN32+ (Right (ExitFailure 9009)) -> b -- command not found by cmd.exe on Windows+#endif (Right x) -> return x -- legitimate success/failure (Left (_ :: SomeException)) -> b -- an exception -withCurrentDirectory :: FilePathLike p => p -> IO a -> IO a++withCurrentDirectory :: FilePathLike p+ => p+ -> IO a+ -> IO a withCurrentDirectory name m = bracket (do cwd <- getCurrentDirectory@@ -143,12 +208,16 @@ (\oldwd -> setCurrentDirectory oldwd `catchall` return ()) (const m) + foreign import ccall unsafe "umask.h set_umask" set_umask :: CString -> IO CInt foreign import ccall unsafe "umask.h reset_umask" reset_umask :: CInt -> IO CInt -withUMask :: String -> IO a -> IO a++withUMask :: String+ -> IO a+ -> IO a withUMask umask job = do rc <- withCString umask set_umask when (rc < 0) (throwErrno "Couldn't set umask")@@ -157,6 +226,7 @@ (reset_umask rc) job + -- | Ask the user for a line of input. askUser :: String -- ^ The prompt to display -> IO String -- ^ The string the user entered.@@ -164,83 +234,106 @@ getInputLine prompt >>= maybe (error "askUser: unexpected end of input") return +-- | Ask the user to press Enter+askEnter :: String -- ^ The prompt to display+ -> IO ()+askEnter prompt = askUser prompt >> return ()+ -- | @askUserListItem prompt xs@ enumerates @xs@ on the screen, allowing -- the user to choose one of the items-askUserListItem :: String -> [String] -> IO String+askUserListItem :: String+ -> [String]+ -> IO String askUserListItem prompt xs = withoutProgress $ runInputT defaultSettings $ do- outputStr . unlines $ zipWith (\n x -> show n ++ ". " ++ x) [1::Int ..] xs- loop- where- loop = do- answer <- getInputLine prompt- >>= maybe (error "askUser: unexpected end of input") return- case maybeRead answer of- Just n | n > 0 && n <= length xs -> return (xs !! (n-1))- _ -> outputStrLn "Invalid response, try again!" >> loop+ outputStr . unlines $ zipWith (\n x -> show n ++ ". " ++ x) [1::Int ..] xs+ loop+ where+ loop = do+ answer <- getInputLine prompt+ >>= maybe (error "askUser: unexpected end of input") return+ case maybeRead answer of+ Just n | n > 0 && n <= length xs -> return (xs !! (n-1))+ _ -> outputStrLn "Invalid response, try again!" >> loop -maybeRead :: Read a => String -> Maybe a++maybeRead :: Read a+ => String+ -> Maybe a maybeRead s = case reads s of- [(x, rest)] | all isSpace rest -> Just x- _ -> Nothing+ [(x, rest)] | all isSpace rest -> Just x+ _ -> Nothing -stripCr :: String -> String++stripCr :: String+ -> String stripCr "" = "" stripCr "\r" = "" stripCr (c:cs) = c : stripCr cs --- Format a path for screen output,--- so that the user sees where the path begins and ends.--- Could (should?) also warn about unprintable characters here.-formatPath :: String -> String+-- | Format a path for screen output, so that the user sees where the path+-- begins and ends. Could (should?) also warn about unprintable characters here.+formatPath :: String+ -> String formatPath path = "\"" ++ quote path ++ "\"" where quote "" = "" quote (c:cs) = if c `elem` ['\\', '"'] then '\\':c:quote cs else c:quote cs + breakCommand :: String -> (String, [String]) breakCommand s = case words s of (arg0:args) -> (arg0,args) [] -> (s,[]) -nubsort :: Ord a => [a] -> [a]++nubsort :: Ord a+ => [a]+ -> [a] nubsort = map head . group . sort --- | @editFile f@ lets the user edit a file which could but does not need--- to already exist. This function returns the exit code from the text--- editor and a flag indicating if the user made any changes.-editFile :: FilePathLike p => p -> IO (ExitCode, Bool)+-- | @editFile f@ lets the user edit a file which could but does not need to+-- already exist. This function returns the exit code from the text editor and a+-- flag indicating if the user made any changes.+editFile :: FilePathLike p+ => p+ -> IO (ExitCode, Bool) editFile ff = do- old_content <- file_content- ec <- runEditor f- new_content <- file_content- return (ec, new_content /= old_content)- where f = toFilePath ff- file_content = do- exists <- doesFileExist f- if exists then do content <- B.readFile f- return $ Just content- else return Nothing+ old_content <- file_content+ ec <- runEditor f+ new_content <- file_content+ return (ec, new_content /= old_content)+ where+ f = toFilePath ff+ file_content = do+ exists <- doesFileExist f+ if exists then do content <- B.readFile f+ return $ Just content+ else return Nothing -runEditor :: FilePath -> IO ExitCode++runEditor :: FilePath+ -> IO ExitCode runEditor f = do- ed <- getEditor- execInteractive ed f- `ortryrunning` execInteractive "emacs" f- `ortryrunning` execInteractive "emacs -nw" f- `ortryrunning` execInteractive "nano" f+ ed <- getEditor+ execInteractive ed f+ `ortryrunning` execInteractive "emacs" f+ `ortryrunning` execInteractive "emacs -nw" f+ `ortryrunning` execInteractive "nano" f #ifdef WIN32- `ortryrunning` execInteractive "edit" f+ `ortryrunning` execInteractive "edit" f #endif + getEditor :: IO String getEditor = getEnv "DARCS_EDITOR" `catchall` getEnv "DARCSEDITOR" `catchall` getEnv "VISUAL" `catchall` getEnv "EDITOR" `catchall` return "vi" + environmentHelpEditor :: ([String], [String]) environmentHelpEditor = (["DARCS_EDITOR", "DARCSEDITOR", "VISUAL", "EDITOR"],[ "To edit a patch description of email comment, Darcs will invoke an",@@ -250,6 +343,7 @@ "found in your PATH, emacs, emacs -nw, nano and (on Windows) edit are", "each tried in turn."]) + getViewer :: IO String getViewer = getEnv "DARCS_PAGER" `catchall` getEnv "PAGER" `catchall` return "less"@@ -267,10 +361,12 @@ , pHelp :: [Char] } + -- | Prompt the user for a yes or no promptYorn :: [Char] -> IO Bool promptYorn p = (== 'y') `fmap` promptChar (PromptConfig p "yn" [] Nothing []) + promptChar :: PromptConfig -> IO Char promptChar (PromptConfig p basic_chs adv_chs md help_chs) = withoutProgress $ runInputT defaultSettings loopChar@@ -296,27 +392,45 @@ Just d -> map (setUpper d) s setUpper d c = if d == c then toUpper c else c + -- | Construct a filter from a list of AnchoredPaths, that will accept any path -- that is either a parent or a child of any of the listed paths, and discard -- everything else.-filterPaths :: [AnchoredPath] -> AnchoredPath -> t -> Bool+filterPaths :: [AnchoredPath]+ -> AnchoredPath+ -> t+ -> Bool filterPaths files p _ = any (\x -> x `isPrefix` p || p `isPrefix` x) files + -- | Same as 'filterPath', but for ordinary 'FilePath's (as opposed to -- AnchoredPath).-filterFilePaths :: [FilePath] -> AnchoredPath -> t -> Bool+filterFilePaths :: [FilePath]+ -> AnchoredPath+ -> t+ -> Bool filterFilePaths = filterPaths . map floatPath -getFileStatus :: FilePath -> IO (Maybe FileStatus)++getFileStatus :: FilePath+ -> IO (Maybe FileStatus) getFileStatus f = Just `fmap` getSymbolicLinkStatus f `catchall` return Nothing -treeHasAnycase :: (MonadError e m, Functor m, Monad m) => Tree m -> FilePath -> m Bool-treeHasAnycase tree path = fst `fmap` virtualTreeMonad (existsAnycase $ floatPath path) tree -existsAnycase :: (MonadError e m, Functor m, Monad m) => AnchoredPath -> TreeMonad m Bool+treeHasAnycase :: (MonadError e m, Functor m, Monad m)+ => Tree m+ -> FilePath+ -> m Bool+treeHasAnycase tree path =+ fst `fmap` virtualTreeMonad (existsAnycase $ floatPath path) tree+++existsAnycase :: (MonadError e m, Functor m, Monad m)+ => AnchoredPath+ -> TreeMonad m Bool existsAnycase (AnchoredPath []) = return True-existsAnycase (AnchoredPath (Name x:xs)) =+existsAnycase (AnchoredPath (Name x:xs)) = do do wd <- currentDirectory Just tree <- gets (flip findTree wd . HS.tree) let subs = [ AnchoredPath [Name n] | (Name n, _) <- listImmediate tree,@@ -326,6 +440,7 @@ if file then return True else withDirectory path (existsAnycase $ AnchoredPath xs)) + treeHas :: (MonadError e m, Functor m, Monad m) => Tree m -> FilePath -> m Bool treeHas tree path = fst `fmap` virtualTreeMonad (HS.exists $ floatPath path) tree @@ -335,6 +450,38 @@ treeHasFile :: (MonadError e m, Functor m, Monad m) => Tree m -> FilePath -> m Bool treeHasFile tree path = fst `fmap` virtualTreeMonad (fileExists $ floatPath path) tree +-- | In some environments, darcs requires that certain global GHC library variables that+-- control the encoding used in internal translations are set to specific values.+--+-- @setDarcsEncoding@ enforces those settings, and should be called before the+-- first time any darcs operation is run, and again if anything else might have+-- set those encodings to different values.+--+-- Note that it isn't thread-safe and has a global effect on your program.+--+-- The current behaviour of this function is as follows, though this may+-- change in future:+--+-- Encodings are only set on GHC 7.4 and up, on any non-Windows platform.+--+-- Two encodings are set, both to @GHC.IO.Encoding.char8@:+-- @GHC.IO.Encoding.setFileSystemEncoding@ and @GHC.IO.Encoding.setForeignEncoding@.+--+setDarcsEncodings :: IO ()+setDarcsEncodings = do+#ifdef FORCE_CHAR8_ENCODING++-- This is needed for appropriate behaviour from getArgs and from general+-- filesystem calls (e.g. getDirectoryContents, readFile, ...)+ setFileSystemEncoding char8++-- This ensures that foreign calls made by hashed-storage to stat+-- filenames returned from getDirectoryContents are translated appropriately+ setForeignEncoding char8++#endif+ return ()+ -- The following functions are copied from the encoding package (BSD3 -- licence, by Henning Günther). @@ -342,10 +489,12 @@ foreign import ccall "system_encoding.h get_system_encoding" get_system_encoding :: IO CString + getSystemEncoding :: IO String getSystemEncoding = do enc <- get_system_encoding peekCString enc+ -- | @isUTF8@ checks if an encoding is UTF-8 (or ascii, since it is a -- subset of UTF-8).
src/DateMatcher.hs view
@@ -15,24 +15,36 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. - {-# LANGUAGE ExistentialQuantification #-} -module DateMatcher ( parseDateMatcher- -- for debugging only- , DateMatcher(..), getMatchers ) where+-- |+-- Module : DateMatcher+-- Copyright : 2004 David Roundy+-- License : GPL+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable -import Prelude hiding ( catch )+module DateMatcher (+ parseDateMatcher+ -- for debugging only+ , DateMatcher(..)+ , getMatchers+ ) where+ import Control.Exception.Extensible ( catch, throw )-import System.IO.Error ( isUserError, ioeGetErrorString ) import Data.Maybe ( isJust )+import Prelude hiding ( catch )++import System.IO.Error ( isUserError, ioeGetErrorString ) import System.Time-import IsoDate ( parseDate, englishDateTime, englishInterval, englishLast, iso8601Interval,- resetCalendar, subtractFromMCal, getLocalTz,+import Text.ParserCombinators.Parsec ( eof, parse, ParseError )++import IsoDate ( parseDate, englishDateTime, englishInterval, englishLast,+ iso8601Interval, resetCalendar, subtractFromMCal, getLocalTz, MCalendarTime(..), toMCalendarTime, unsafeToCalendarTime,- unsetTime,+ unsetTime )-import Text.ParserCombinators.Parsec ( eof, parse, ParseError ) -- | 'withinDay' @x y@ is true if @x <= y < (x + one_day)@ -- Note that this converts the two dates to @ClockTime@ to avoid@@ -41,14 +53,15 @@ withinDay a b = within (Just $ toClockTime a) (Just (addToClockTime day $ toClockTime a)) (toClockTime b)- where day = TimeDiff 0 0 1 0 0 0 0+ where+ day = TimeDiff 0 0 1 0 0 0 0 -- | 'dateRange' @x1 x2 y@ is true if @x1 <= y < x2@ -- Since @x1@ and @x2@ can be underspecified, we simply assume the -- first date that they could stand for. dateRange :: Maybe MCalendarTime -> Maybe MCalendarTime -> CalendarTime -> Bool-dateRange a b c = cDateRange (fmap unsafeToCalendarTime a)- (fmap unsafeToCalendarTime b) c+dateRange a b = cDateRange (fmap unsafeToCalendarTime a)+ (fmap unsafeToCalendarTime b) -- | 'cDateRange' @x1 x2 y@ is true if @x1 <= y < x2@ cDateRange :: Maybe CalendarTime -> Maybe CalendarTime -> CalendarTime -> Bool@@ -72,99 +85,104 @@ -- second. samePartialDate :: MCalendarTime -> CalendarTime -> Bool samePartialDate a b_ =- within (Just clockA)- (Just $ addToClockTime interval clockA)- (toClockTime calB)- where interval- | isJust (mctSec a) = second- | isJust (mctMin a) = minute- | isJust (mctHour a) = hour- | isJust (mctYDay a) = day- | mctWeek a = maybe week (const day) (mctWDay a)- | isJust (mctDay a) = day- | isJust (mctMonth a) = month- | otherwise = year- year = TimeDiff 1 0 0 0 0 0 0- month = TimeDiff 0 1 0 0 0 0 0- week = TimeDiff 0 0 7 0 0 0 0- day = TimeDiff 0 0 1 0 0 0 0- hour = TimeDiff 0 0 0 1 0 0 0- minute = TimeDiff 0 0 0 0 1 0 0- second = TimeDiff 0 0 0 0 0 1 0- --- clockA = toClockTime $ unsafeToCalendarTime a- calB = resetCalendar b_+ within (Just clockA)+ (Just $ addToClockTime interval clockA)+ (toClockTime calB)+ where+ interval+ | isJust (mctSec a) = second+ | isJust (mctMin a) = minute+ | isJust (mctHour a) = hour+ | isJust (mctYDay a) = day+ | mctWeek a = maybe week (const day) (mctWDay a)+ | isJust (mctDay a) = day+ | isJust (mctMonth a) = month+ | otherwise = year+ year = TimeDiff 1 0 0 0 0 0 0+ month = TimeDiff 0 1 0 0 0 0 0+ week = TimeDiff 0 0 7 0 0 0 0+ day = TimeDiff 0 0 1 0 0 0 0+ hour = TimeDiff 0 0 0 1 0 0 0+ minute = TimeDiff 0 0 0 0 1 0 0+ second = TimeDiff 0 0 0 0 0 1 0+ clockA = toClockTime $ unsafeToCalendarTime a+ calB = resetCalendar b_ -- | A 'DateMatcher' combines a potential parse for a date string -- with a "matcher" function that operates on a given date. -- We use an existential type on the matcher to allow -- the date string to either be interpreted as a point in time -- or as an interval.-data DateMatcher = forall d . (Show d) =>- DM String -- name- (Either ParseError d) -- parser- (d -> CalendarTime -> Bool) -- matcher+data DateMatcher = forall d . (Show d) => DM+ String -- name+ (Either ParseError d) -- parser+ (d -> CalendarTime -> Bool) -- matcher -- | 'parseDateMatcher' @s@ return the first matcher in -- 'getMatchers' that can parse 's' parseDateMatcher :: String -> IO (CalendarTime -> Bool)-parseDateMatcher d =- do matcher <- tryMatchers `fmap` getMatchers d- -- Hack: test the matcher against the current date and discard the results.- -- We just want to make sure it won't throw any exceptions when we use it for real.- matcher `fmap` now >>= (`seq` return matcher)- `catchUserError`+parseDateMatcher d = testDateMatcher `catchUserError` handleError+ where+ catchUserError comp handler = catch comp $ \e ->+ if isUserError e then handler (ioeGetErrorString e) else throw e+ -- If the user enters a date > maxint seconds ago, the toClockTime -- function cannot work.- \e -> if e == "Time.toClockTime: invalid input"- then error "Can't handle dates that far back!"- else error e- where- catchUserError comp handler- = catch comp (\e -> if isUserError e then handler (ioeGetErrorString e) else throw e)+ handleError e = if e == "Time.toClockTime: invalid input"+ then error "Can't handle dates that far back!"+ else error e + -- Hack: test the matcher against the current date and discard the results.+ -- We just want to make sure it won't throw any exceptions when we use it+ -- for real.+ testDateMatcher = do+ matcher <- tryMatchers `fmap` getMatchers d+ matcher `fmap` now >>= (`seq` return matcher)+ -- | 'getMatchers' @d@ returns the list of matchers that will be -- applied on @d@. If you wish to extend the date parsing code, -- this will likely be the function that you modify to do so. getMatchers :: String -> IO [DateMatcher]-getMatchers d =- do rightNow <- now+getMatchers d = do+ rightNow <- now let midnightToday = unsetTime rightNow mRightNow = toMCalendarTime rightNow- matchIsoInterval (Left dur) = dateRange (Just $ dur `subtractFromMCal` mRightNow) (Just mRightNow)+ matchIsoInterval (Left dur) =+ let durAgo = dur `subtractFromMCal` mRightNow in+ dateRange (Just durAgo) (Just mRightNow) matchIsoInterval (Right (a,b)) = dateRange (Just a) (Just b) tzNow <- getLocalTz- return -- note that the order of these is quite important as some matchers- -- can match the same date.- [ DM "from English date"- (parseDateWith $ englishLast midnightToday)- (\(a,_) -> cDateRange (Just a) Nothing)- , DM "specific English date"- (parseDateWith $ englishDateTime midnightToday)- withinDay- , DM "English interval"- (parseDateWith $ englishInterval rightNow)- (uncurry cDateRange)- , DM "ISO 8601 interval"- (parseDateWith $ iso8601Interval tzNow)- matchIsoInterval- , DM "CVS, ISO 8601, or old style date"- (parseDate tzNow d)- samePartialDate ]- where- tillEof p = do { x <- p; eof; return x }- parseDateWith p = parse (tillEof p) "" d+ return+ -- note that the order of these is quite important as some matchers can+ -- match the same date.+ [ DM "from English date"+ (parseDateWith $ englishLast midnightToday)+ (\(a,_) -> cDateRange (Just a) Nothing)+ , DM "specific English date"+ (parseDateWith $ englishDateTime midnightToday)+ withinDay+ , DM "English interval"+ (parseDateWith $ englishInterval rightNow)+ (uncurry cDateRange)+ , DM "ISO 8601 interval"+ (parseDateWith $ iso8601Interval tzNow)+ matchIsoInterval+ , DM "CVS, ISO 8601, or old style date"+ (parseDate tzNow d)+ samePartialDate+ ]+ where+ tillEof p = do { x <- p; eof; return x }+ parseDateWith p = parse (tillEof p) "" d -- | 'tryMatchers' @ms@ returns the first successful match in @ms@ -- It is an error if there are no matches-tryMatchers :: [DateMatcher] -> (CalendarTime -> Bool)+tryMatchers :: [DateMatcher] -> CalendarTime -> Bool tryMatchers (DM _ parsed matcher : ms) =- case parsed of- Left _ -> tryMatchers ms- Right d -> matcher d+ case parsed of+ Left _ -> tryMatchers ms+ Right d -> matcher d tryMatchers [] = error "Can't support fancy dates." --- darcs-doc: self-explanatory now :: IO CalendarTime now = getClockTime >>= toCalendarTime-
src/English.hs view
@@ -15,13 +15,22 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. --- | This modules provides rudimentary natural language generation+-- |+-- Module : English+-- Copyright : 2008 Eric Kow+-- License : GPL+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable+--+-- This modules provides rudimentary natural language generation -- (NLG) utilities. That is, generating natural language from a -- machine representation. Initially, only English is supported at -- all. Representations are implemented for: -- -- * countable nouns (plurality); and -- * lists of clauses (foo, bar and/or baz).+ module English where import Data.List (isSuffixOf, intercalate)@@ -34,8 +43,8 @@ -- | Things that have a plural and singular spelling class Countable a where- plural :: a -> ShowS- singular :: a -> ShowS+ plural :: a -> ShowS+ singular :: a -> ShowS -- | This only distinguishes between nouns with a final -ch, -- and nouns which do not.@@ -48,32 +57,33 @@ data Pronoun = It instance Countable Noun where- -- more irregular nouns will just need to have their own type- plural (Noun s) | "ch" `isSuffixOf` s = showString s . showString "es"- plural (Noun s) | "y" `isSuffixOf` s- && length s > 1- && last (init s) `notElem` "aeiou" = showString (init s) . showString "ies"- plural (Noun s) = showString s . showChar 's'- singular (Noun s) = showString s+ -- more irregular nouns will just need to have their own type+ plural (Noun s) | "ch" `isSuffixOf` s = showString s . showString "es"+ plural (Noun s) | "y" `isSuffixOf` s+ && length s > 1+ && last (init s) `notElem` "aeiou" =+ showString (init s) . showString "ies"+ plural (Noun s) = showString s . showChar 's'+ singular (Noun s) = showString s instance Countable Pronoun where- plural It = showString "them"- singular It = showString "it"+ plural It = showString "them"+ singular It = showString "it" -- | > singular This (Noun "batch") "" == "this batch" -- > plural This (Noun "batch") "" == "these batches" data This = This Noun instance Countable This where- plural (This s) = showString "these " . plural s- singular (This s) = showString "this " . singular s+ plural (This s) = showString "these " . plural s+ singular (This s) = showString "this " . singular s -- | Given a list of things, combine them thusly: -- -- > orClauses ["foo", "bar", "baz"] == "foo, bar or baz" andClauses, orClauses :: [String] -> String andClauses = intersperseLast ", " " and "-orClauses = intersperseLast ", " " or "+orClauses = intersperseLast ", " " or " -- | As 'intersperse', with a different separator for the last -- | interspersal.
src/Exec.hs view
@@ -15,16 +15,24 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. - {-# LANGUAGE CPP, ForeignFunctionInterface, DeriveDataTypeable #-} -module Exec ( exec, execInteractive,- withoutNonBlock,- Redirects, Redirect(..),- ExecException(..)- ) where+-- |+-- Module : Exec+-- Copyright : 2003 David Roundy+-- License : GPL+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable -import Data.Typeable ( Typeable, cast )+module Exec (+ exec+ , execInteractive+ , withoutNonBlock+ , Redirects+ , Redirect(..)+ , ExecException(..)+ ) where #ifndef WIN32 import Control.Exception.Extensible ( bracket )@@ -36,17 +44,20 @@ import Data.List ( isInfixOf ) #endif -import System.Exit ( ExitCode (..) )-import System.Cmd ( system )-import System.IO ( IOMode(..), openBinaryFile, stdout )-import System.Process ( runProcess, terminateProcess, waitForProcess ) #if __GLASGOW_HASKELL__ >= 612 import GHC.IO.Handle ( hDuplicate ) #else import GHC.Handle ( hDuplicate ) #endif-import Control.Exception.Extensible ( bracketOnError, Exception(..), SomeException(..) ) +import Control.Exception.Extensible ( bracketOnError, Exception(..),+ SomeException(..) )+import Data.Typeable ( Typeable, cast )+import System.Cmd ( system )+import System.Exit ( ExitCode (..) )+import System.IO ( IOMode(..), openBinaryFile, stdout )+import System.Process ( runProcess, terminateProcess, waitForProcess )+ import Darcs.Global ( whenDebugMode ) import Progress ( withoutProgress ) @@ -69,7 +80,10 @@ -} type Redirects = (Redirect, Redirect, Redirect)-data Redirect = AsIs | Null | File FilePath++data Redirect = AsIs+ | Null+ | File FilePath | Stdout deriving Show @@ -79,12 +93,16 @@ doesn't exist. -} -- ExecException cmd args redirecs errorDesc-data ExecException = ExecException String [String] Redirects String+data ExecException = ExecException+ String -- cmd+ [String] -- args+ Redirects -- redirects+ String -- errorDesc deriving (Typeable,Show) instance Exception ExecException where- toException e = SomeException e- fromException (SomeException e) = cast e+ toException e = SomeException e+ fromException (SomeException e) = cast e _devNull :: FilePath #ifdef WIN32@@ -97,29 +115,28 @@ We use System.Process, which does the necessary quoting and redirection for us behind the scenes. -}- exec :: String -> [String] -> Redirects -> IO ExitCode exec cmd args (inp,out,err) = withoutProgress $ do- h_stdin <- redirect inp ReadMode- h_stdout <- redirect out WriteMode- h_stderr <- redirect err WriteMode--- putStrLn (unwords (cmd:args ++ map show [inp,out,err]))- withExit127 $ bracketOnError- (do whenDebugMode $ putStrLn $ unwords $ cmd:args ++ ["; #"] ++ map show [inp,out,err]- runProcess cmd args Nothing Nothing h_stdin h_stdout h_stderr)- (terminateProcess)- (waitForProcess)+ h_stdin <- redirect inp ReadMode+ h_stdout <- redirect out WriteMode+ h_stderr <- redirect err WriteMode+ withExit127 $ bracketOnError+ (do doOptionalDebug+ runProcess cmd args Nothing Nothing h_stdin h_stdout h_stderr)+ (terminateProcess)+ (waitForProcess) where+ doOptionalDebug = whenDebugMode . putStrLn . unwords $+ cmd : args ++ ["; #"] ++ map show [inp, out, err] redirect AsIs _ = return Nothing redirect Null mode = Just `fmap` openBinaryFile _devNull mode redirect (File "/dev/null") mode = redirect Null mode redirect (File f) mode = Just `fmap` openBinaryFile f mode+ -- hDuplicate stdout rather than passing stdout itself,+ -- because runProcess closes the Handles we pass it. redirect Stdout _ = Just `fmap` hDuplicate stdout- -- hDuplicate stdout rather than passing stdout itself,- -- because runProcess closes the Handles we pass it. execInteractive :: String -> String -> IO ExitCode- #ifndef WIN32 {- This should handle arbitrary commands interpreted by the shell on Unix since@@ -128,26 +145,40 @@ cmd "$DARCS_ARGUMENT" -} execInteractive cmd arg = withoutProgress $ do- let var = "DARCS_ARGUMENT"- stdin `seq` return ()- withoutNonBlock $ bracket- (do oldval <- getEnv var- setEnv var arg True- return oldval)- (\oldval ->- do case oldval of- Nothing -> unsetEnv var- Just val -> setEnv var val True)- (\_ -> withExit127 $ system $ cmd++" \"$"++var++"\"")+ let var = "DARCS_ARGUMENT"+ stdin `seq` return ()+ withoutNonBlock $ bracket+ (do oldval <- getEnv var+ setEnv var arg True+ return oldval)+ (\oldval ->+ case oldval of+ Nothing -> unsetEnv var+ Just val -> setEnv var val True)+ (\_ -> withExit127 $ system $ cmd++" \"$"++var++"\"") #else-+-- The `system' function passes commands to execute via cmd.exe (or+-- command.com) it's return value is equivalent to the one returned by the+-- shell. For regular applications - this works correctly resulting in the+-- exit code of the program. However in case of a command/file which can't be+-- found - cmd.exe will return 1 instead of propagating the ExitFailure 9009+-- which on windows is equivalent to ExitFailure 127 from *nix machines.+--+-- Here we force return the exit code of the last cmd.exe action by appending+-- & exit !errorlevel! to the command being executed that way chaining with+-- ortryrunning works correctly.+--+-- SETLOCAL EnableDelayedExpansion makes sure that !variable! expansion is done+-- correctly on systems where that function is not enabled by default.+-- execInteractive cmd arg = withoutProgress $ do- system $ cmd ++ " " ++ arg+ withExit127 $ system $ "SETLOCAL EnableDelayedExpansion & " +++ cmd ++ " " ++ arg +++ " & exit !errorlevel!" #endif withoutNonBlock :: IO a -> IO a- #ifndef WIN32 {- Do IO without NonBlockingRead on stdInput.@@ -160,7 +191,7 @@ do nb <- queryFdOption stdInput NonBlockingRead if nb then bracket- (do setFdOption stdInput NonBlockingRead False)+ (setFdOption stdInput NonBlockingRead False) (\_ -> setFdOption stdInput NonBlockingRead True) (\_ -> x) else x
src/IsoDate.hs view
@@ -16,6 +16,14 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. +-- |+-- Module : IsoDate+-- Copyright : 2003 Peter Simons+-- 2003 David Roundy+-- License : GPL+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable module IsoDate ( getIsoDateTime, readLocalDate, readUTCDate, parseDate, getLocalTz,@@ -303,8 +311,8 @@ | otherwise = 10 ^ (picoExp - digsExp) * (read digs) return $ \c -> c { mctPicosec = Just $ frac } zulu = do { _ <- char 'Z'; return (\c -> c { mctTZ = Just 0 }) }- offset = do sign <- choice [ do { char '+' >> return 1 }- , do { char '-' >> return (-1) } ]+ offset = do sign <- choice [ char '+' >> return 1+ , char '-' >> return (-1) ] h <- twoDigits m <- option 0 $ do { optional colon; twoDigits } return $ \c -> c { mctTZ = Just $ sign * 60 * ((h*60)+m) }@@ -604,7 +612,7 @@ englishAgo :: CalendarTime -> CharParser a CalendarTime englishAgo now = try $ do p <- englishDuration- _ <- try $ do space+ _ <- try space (m,ref) <- (try $ caseString "ago" >> return ((-1), now)) <|> do m <- beforeMod <|> afterMod _ <- space
src/Lcs.hs view
@@ -18,7 +18,16 @@ {-# LANGUAGE CPP #-} --- | LCS stands for Longest Common Subsequence, and it is a relatively+-- |+-- Module : Lcs+-- Copyright : 2003 David Roundy+-- 2005 Benedikt Schmidt+-- License : GPL+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable+--+-- LCS stands for Longest Common Subsequence, and it is a relatively -- challenging problem to find an LCS efficiently. This module implements -- the algorithm described in: --@@ -41,6 +50,7 @@ -- The last step tries to create longer changed regions and line up deletions -- in the first file to insertions in the second by shifting changed lines -- forward and backward.+ module Lcs ( getChanges, shiftBoundaries ) where
src/Progress.hs view
@@ -1,203 +1,305 @@--- Utility functions for tracking progress of long-running actions.- {-# LANGUAGE CPP #-} #include "gadts.h" -module Progress ( beginTedious, endTedious, tediousSize,- debugMessage, debugFail, withoutProgress,- progress, progressKeepLatest, finishedOne,- finishedOneIO, progressList, minlist,- setProgressMode) where+-- |+-- Module : Progress+-- Copyright : 2008 David Roundy+-- License : GPL+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable+--+-- Utility functions for tracking progress of long-running actions. +module Progress+ (+ beginTedious+ , endTedious+ , tediousSize+ , debugMessage+ , debugFail+ , withoutProgress+ , progress+ , progressKeepLatest+ , finishedOne+ , finishedOneIO+ , progressList+ , minlist+ , setProgressMode+ ) where++ import Prelude hiding (lookup) import Control.Exception.Extensible ( onException ) import Control.Monad ( when )-import System.IO ( stdout, stderr, hFlush, hPutStr, hPutStrLn,- hSetBuffering, hIsTerminalDevice,- Handle, BufferMode(LineBuffering) )-import System.IO.Unsafe ( unsafePerformIO )+import Control.Concurrent ( forkIO, threadDelay )+ import Data.Char ( toLower ) import Data.Map ( Map, empty, adjust, insert, delete, lookup ) import Data.Maybe ( isJust )-import Control.Concurrent ( forkIO, threadDelay ) import Data.IORef ( IORef, newIORef, readIORef, writeIORef, modifyIORef ) +import System.IO ( stdout, stderr, hFlush, hPutStr, hPutStrLn,+ hSetBuffering, hIsTerminalDevice,+ Handle, BufferMode(LineBuffering) )+import System.IO.Unsafe ( unsafePerformIO )+ import Darcs.Global ( withDebugMode, debugMessage, putTiming, debugFail ) ++data ProgressData = ProgressData+ { sofar :: !Int+ , latest :: !(Maybe String)+ , total :: !(Maybe Int)+ }+ handleProgress :: IO ()-handleProgress = do threadDelay 1000000- handleMoreProgress "" 0+handleProgress = do+ threadDelay 1000000+ handleMoreProgress "" 0 + handleMoreProgress :: String -> Int -> IO () handleMoreProgress k n = withProgressMode $ \m -> if m then do s <- getProgressLast mp <- getProgressData s case mp of- Nothing -> do threadDelay 1000000- handleMoreProgress k n- Just p -> do when (k /= s || n < sofar p) $ whenProgressMode $ printProgress s p- threadDelay 1000000- handleMoreProgress s (sofar p)+ Nothing -> do+ threadDelay 1000000+ handleMoreProgress k n+ Just p -> do+ when (k /= s || n < sofar p) $ whenProgressMode $ printProgress s p+ threadDelay 1000000+ handleMoreProgress s (sofar p) else do threadDelay 1000000 handleMoreProgress k n -printProgress :: String -> ProgressData -> IO ()++printProgress :: String+ -> ProgressData+ -> IO () printProgress k (ProgressData {sofar=s, total=Just t, latest=Just l}) = myput output output- where output = (k++" "++show s++" done, "++show (t - s)++" queued. "++l)+ where+ output = k ++ " " ++ show s ++ " done, " ++ show (t - s) ++ " queued. " ++ l printProgress k (ProgressData {latest=Just l}) =- myput (k++" "++l) k+ myput (k ++ " " ++ l) k printProgress k (ProgressData {sofar=s, total=Just t}) | t >= s =- myput (k++" "++show s++" done, "++show (t - s)++" queued") (k++" "++show s)+ myput (k ++ " " ++ show s ++ " done, " ++ show (t - s) ++ " queued")+ (k ++ " " ++ show s) printProgress k (ProgressData {sofar=s}) =- myput (k++" "++show s) k+ myput (k ++ " " ++ show s) k + myput :: String -> String -> IO () myput l s = withDebugMode $ \debugMode ->- if debugMode- then putTiming >> hPutStrLn stderr l- else if '\n' `elem` l- then myput (takeWhile (/= '\n') l) s- else putTiming >> if length l < 80 then simpleput l- else simpleput (take 80 s)+ if debugMode+ then putTiming >> hPutStrLn stderr l+ else+ if '\n' `elem` l+ then myput (takeWhile (/= '\n') l) s+ else putTiming >> if length l < 80+ then simpleput l+ else simpleput (take 80 s) -{-# NOINLINE simpleput #-}+ simpleput :: String -> IO () simpleput = unsafePerformIO $ mkhPutCr stderr+{-# NOINLINE simpleput #-} + -- | @beginTedious k@ starts a tedious process and registers it in--- '_progressData' with the key @k@. A tedious process is one for which we--- want a progress indicator.+-- '_progressData' with the key @k@. A tedious process is one for which we want+-- a progress indicator. ----- Wouldn't it be safer if it had type String -> IO--- ProgressDataKey, so that we can ensure there is no collision?--- What happens if you call beginTedious twice with the same string, without--- calling endTedious in the meantime?+-- Wouldn't it be safer if it had type String -> IO ProgressDataKey, so that we+-- can ensure there is no collision? What happens if you call beginTedious twice+-- with the same string, without calling endTedious in the meantime? beginTedious :: String -> IO ()-beginTedious k = do debugMessage $ "Beginning " ++ map toLower k- setProgressData k $ ProgressData { sofar = 0,- latest = Nothing,- total = Nothing }+beginTedious k = do+ debugMessage $ "Beginning " ++ map toLower k+ setProgressData k $ ProgressData+ { sofar = 0+ , latest = Nothing+ , total = Nothing+ } --- | @endTedious k@ unregisters the tedious process with key @k@, printing "Done" if such--- a tedious process exists.++-- | @endTedious k@ unregisters the tedious process with key @k@, printing+-- "Done" if such a tedious process exists. endTedious :: String -> IO ()-endTedious k = whenProgressMode $ do p <- getProgressData k- modifyIORef _progressData (\(a,m) -> (a,delete k m))- when (isJust p) $ debugMessage $ "Done "++- (map toLower k)+endTedious k = whenProgressMode $ do+ p <- getProgressData k+ modifyIORef _progressData (\(a,m) -> (a,delete k m))+ when (isJust p) $ debugMessage $ "Done " +++ (map toLower k) -tediousSize :: String -> Int -> IO ()++tediousSize :: String+ -> Int+ -> IO () tediousSize k s = updateProgressData k uptot- where uptot p = case total p of Just t -> seq ts $ p { total = Just ts }- where ts = t + s- Nothing -> p { total = Just s }+ where+ uptot p = case total p of+ Just t -> seq ts $ p { total = Just ts }+ where ts = t + s+ Nothing -> p { total = Just s } ++-- | XXX: document this constant minlist :: Int minlist = 4 -progressList :: String -> [a] -> [a]++progressList :: String+ -> [a]+ -> [a] progressList _ [] = []-progressList k (x:xs) = if l < minlist then x:xs- else startit x : pl xs- where l = length (x:xs)- startit y = unsafePerformIO $ do beginTedious k- tediousSize k l- return y- pl [] = []- pl [y] = unsafePerformIO $ do endTedious k- return [y]- pl (y:ys) = progress k y : pl ys+progressList k (x:xs) = if l < minlist+ then x:xs+ else startit x : pl xs+ where+ l = length (x:xs) + startit y = unsafePerformIO $ do+ beginTedious k+ tediousSize k l+ return y -progress :: String -> a -> a+ pl [] = []+ pl [y] = unsafePerformIO $ do+ endTedious k+ return [y]+ pl (y:ys) = progress k y : pl ys+++progress :: String+ -> a+ -> a progress k a = unsafePerformIO $ progressIO k >> return a + progressIO :: String -> IO () progressIO "" = return ()-progressIO k = do updateProgressData k (\p -> p { sofar = sofar p + 1,- latest = Nothing })- putDebug k ""+progressIO k = do+ updateProgressData k $ \p ->+ p { sofar = sofar p + 1, latest = Nothing }+ putDebug k "" -progressKeepLatest :: String -> a -> a++progressKeepLatest :: String+ -> a+ -> a progressKeepLatest k a = unsafePerformIO $ progressKeepLatestIO k >> return a + progressKeepLatestIO :: String -> IO () progressKeepLatestIO "" = return ()-progressKeepLatestIO k = do updateProgressData k (\p -> p {sofar = sofar p + 1})- putDebug k ""+progressKeepLatestIO k = do+ updateProgressData k (\p -> p {sofar = sofar p + 1})+ putDebug k "" + finishedOne :: String -> String -> a -> a finishedOne k l a = unsafePerformIO $ finishedOneIO k l >> return a + finishedOneIO :: String -> String -> IO () finishedOneIO "" _ = return ()-finishedOneIO k l = do updateProgressData k (\p -> p { sofar = sofar p + 1,- latest = Just l })- putDebug k l+finishedOneIO k l = do+ updateProgressData k (\p -> p { sofar = sofar p + 1,+ latest = Just l })+ putDebug k l -putDebug :: String -> String -> IO ()++putDebug :: String+ -> String+ -> IO () putDebug _ _ = return () --putDebug k "" = when (False && debugMode) $ hPutStrLn stderr $ "P: "++k --putDebug k l = when (False && debugMode) $ hPutStrLn stderr $ "P: "++k++" : "++l -{-# NOINLINE _progressMode #-}+ _progressMode :: IORef Bool-_progressMode = unsafePerformIO $ do hSetBuffering stderr LineBuffering- newIORef True+_progressMode = unsafePerformIO $ do+ hSetBuffering stderr LineBuffering+ newIORef True+{-# NOINLINE _progressMode #-} -{-# NOINLINE _progressData #-} _progressData :: IORef (String, Map String ProgressData)--- Warning: A do-notation statement discarded a result of type GHC.Conc.ThreadId.-_progressData = unsafePerformIO $ do _ <- forkIO handleProgress- newIORef ("", empty)+_progressData = unsafePerformIO $ do+ _ <- forkIO handleProgress+ newIORef ("", empty)+{-# NOINLINE _progressData #-} -mkhPutCr :: Handle -> IO (String -> IO ())++mkhPutCr :: Handle+ -> IO (String -> IO ()) mkhPutCr fe = do- isTerm <- hIsTerminalDevice fe- stdoutIsTerm <- hIsTerminalDevice stdout- return $ if isTerm then \s -> do hPutStr fe $ '\r':s++"\r"- hFlush fe- let spaces = '\r':replicate (length s) ' '++"\r"- hPutStr fe spaces- when stdoutIsTerm $ hPutStr stdout spaces- else \s -> when (not $ null s) $ do hPutStrLn fe s- hFlush fe+ isTerm <- hIsTerminalDevice fe+ stdoutIsTerm <- hIsTerminalDevice stdout+ return $+ if isTerm+ then \s -> do+ hPutStr fe $ '\r':s ++ "\r"+ hFlush fe+ let spaces = '\r':replicate (length s) ' ' ++ "\r"+ hPutStr fe spaces+ when stdoutIsTerm $ hPutStr stdout spaces+ else \s -> when (not $ null s) $ do hPutStrLn fe s+ hFlush fe + setProgressMode :: Bool -> IO () setProgressMode m = writeIORef _progressMode m + withoutProgress :: IO a -> IO a-withoutProgress j = withProgressMode $ \m -> do debugMessage "Disabling progress reports..."- setProgressMode False- a <- j `onException` setProgressMode m- if m then debugMessage "Reenabling progress reports."- else debugMessage "Leaving progress reports off."- setProgressMode m- return a+withoutProgress j = withProgressMode $ \m -> do+ debugMessage "Disabling progress reports..."+ setProgressMode False+ a <- j `onException` setProgressMode m+ if m then debugMessage "Reenabling progress reports."+ else debugMessage "Leaving progress reports off."+ setProgressMode m+ return a -updateProgressData :: String -> (ProgressData -> ProgressData) -> IO ()-updateProgressData k f = whenProgressMode $ modifyIORef _progressData (\(_,m) -> (k,adjust f k m)) -setProgressData :: String -> ProgressData -> IO ()-setProgressData k p = whenProgressMode $ modifyIORef _progressData (\(a,m) -> (a,insert k p m))+updateProgressData :: String+ -> (ProgressData -> ProgressData)+ -> IO ()+updateProgressData k f =+ whenProgressMode $ modifyIORef _progressData (\(_,m) -> (k,adjust f k m)) ++setProgressData :: String+ -> ProgressData+ -> IO ()+setProgressData k p =+ whenProgressMode $ modifyIORef _progressData (\(a,m) -> (a,insert k p m))++ getProgressData :: String -> IO (Maybe ProgressData)-getProgressData k = withProgressMode $ \p -> if p then (lookup k . snd) `fmap` readIORef _progressData- else return Nothing+getProgressData k = withProgressMode $ \p ->+ if p+ then (lookup k . snd) `fmap` readIORef _progressData+ else return Nothing + getProgressLast :: IO String-getProgressLast = withProgressMode $ \p -> if p then fst `fmap` readIORef _progressData- else return ""+getProgressLast = withProgressMode $ \p ->+ if p+ then fst `fmap` readIORef _progressData+ else return "" + whenProgressMode :: IO a -> IO () whenProgressMode j = withProgressMode $ const $ j >> return () + withProgressMode :: (Bool -> IO a) -> IO a withProgressMode j = readIORef _progressMode >>= j -data ProgressData = ProgressData { sofar :: !Int,- latest :: !(Maybe String),- total :: !(Maybe Int)}+
src/Ratified.hs view
@@ -1,2 +1,8 @@-module Ratified( readFile, hGetContents ) where+-- | XXX: Perhaps a word of explanation here [WL]+module Ratified+ (+ readFile+ , hGetContents+ ) where+ import System.IO( hGetContents )
src/SHA1.hs view
@@ -17,7 +17,17 @@ -- name shadowing disabled because a,b,c,d,e are shadowed loads in step 4 {-# OPTIONS_GHC -fno-warn-name-shadowing #-}+ {-# LANGUAGE CPP #-}++-- |+-- Module : SHA1+-- Copyright : 2001, 2004 Ian Lynagh <igloo@earth.li>+-- License : GPL+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable+ module SHA1 (sha1PS) where import ByteStringUtils (unsafeWithInternals)
src/URL.hs view
@@ -1,5 +1,13 @@ {-# LANGUAGE CPP, ForeignFunctionInterface #-} +-- |+-- Module : URL+-- Copyright : 2008 Dmitry Kurochkin <dmitry.kurochkin@gmail.com>+-- License : GPL+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable+ module URL ( copyUrl, copyUrlFirst, setDebugHTTP, disableHTTPPipelining, maxPipelineLength, waitUrl, Cachable(Cachable, Uncachable, MaxAge),
src/Workaround.hs view
@@ -1,14 +1,38 @@ {-# LANGUAGE CPP #-} -module Workaround ( renameFile, setExecutable, getCurrentDirectory,- installHandler, raiseSignal, Handler(..), Signal,- sigINT, sigHUP, sigABRT, sigALRM, sigTERM, sigPIPE ) where+-- |+-- Module : Workaround+-- Copyright : 2008 David Roundy <droundy@darcs.net>+-- License : GPL+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable +module Workaround+ (+ renameFile+ , setExecutable+ , getCurrentDirectory+ , installHandler+ , raiseSignal+ , Handler(..)+ , Signal+ , sigINT+ , sigHUP+ , sigABRT+ , sigALRM+ , sigTERM+ , sigPIPE+ ) where+ #ifdef WIN32+ import qualified System.Directory ( renameFile, getCurrentDirectory, removeFile ) import qualified Control.Exception ( block ) import qualified System.IO.Error ( isDoesNotExistError, ioError, catch )+ #else+ import System.Posix.Signals(installHandler, raiseSignal, Handler(..), Signal, sigINT, sigHUP, sigABRT, sigALRM, sigTERM, sigPIPE) import System.Directory ( renameFile, getCurrentDirectory )@@ -18,36 +42,57 @@ groupReadMode, groupWriteMode, groupExecuteMode, otherReadMode, otherWriteMode, otherExecuteMode) import Data.Bits ( (.&.), (.|.), complement )+ #endif #ifdef WIN32 -- Dummy implementation of POSIX signals-data Handler = Default | Ignore | Catch (IO ())+data Handler = Default+ | Ignore+ | Catch (IO ())+ type Signal = Int -installHandler :: Signal -> Handler -> Maybe () -> IO ()+installHandler :: Signal+ -> Handler+ -> Maybe ()+ -> IO () installHandler _ _ _ = return () + raiseSignal :: Signal -> IO () raiseSignal _ = return () -sigINT, {- sigKILL, -} sigHUP, {- sigQUIT, -} sigABRT, sigALRM, sigTERM, sigPIPE :: Signal++sigINT :: Signal sigINT = 0+ -- not used: sigKILL = 0++sigHUP :: Signal sigHUP = 0+ -- not used: sigQUIT = 0++sigABRT :: Signal sigABRT = 0++sigTERM :: Signal sigTERM = 0++sigPIPE :: Signal sigPIPE = 0++sigALRM :: Signal sigALRM = 0 -{--System.Directory.renameFile incorrectly fails when the new file already-exists. This code works around that bug at the cost of losing atomic-writes.--} -renameFile :: FilePath -> FilePath -> IO ()+-- | System.Directory.renameFile incorrectly fails when the new file already+-- exists. This code works around that bug at the cost of losing atomic+-- writes.+renameFile :: FilePath+ -> FilePath+ -> IO () renameFile old new = Control.Exception.block $ System.Directory.renameFile old new `System.IO.Error.catch` \_ ->@@ -58,37 +103,43 @@ else System.IO.Error.ioError e) System.Directory.renameFile old new -setExecutable :: FilePath -> Bool -> IO ()++setExecutable :: FilePath+ -> Bool+ -> IO () setExecutable _ _ = return () -{--System.Directory.getCurrentDirectory returns a path with backslashes in it-under windows, and some of the code gets confused by that, so we override-getCurrentDirectory and translates '\\' to '/'--} +-- | System.Directory.getCurrentDirectory returns a path with backslashes in it+-- under windows, and some of the code gets confused by that, so we override+-- getCurrentDirectory and translates '\\' to '/' getCurrentDirectory :: IO FilePath-getCurrentDirectory = do d <- System.Directory.getCurrentDirectory- return $ map rb d- where rb '\\' = '/'- rb c = c+getCurrentDirectory = do+ d <- System.Directory.getCurrentDirectory+ return $ map rb d+ where+ rb '\\' = '/'+ rb c = c #else -setExecutable :: FilePath -> Bool -> IO ()-setExecutable f ex =- do st <- getFileStatus f- umask <- setFileCreationMask 0++setExecutable :: FilePath+ -> Bool+ -> IO ()+setExecutable f ex = do+ st <- getFileStatus f+ umask <- setFileCreationMask 0 -- Warning: A do-notation statement discarded a result of type System.Posix.Types.FileMode.- _ <- setFileCreationMask umask- let rw = fileMode st .&.- (ownerReadMode .|. ownerWriteMode .|.- groupReadMode .|. groupWriteMode .|.- otherReadMode .|. otherWriteMode)- total = if ex then rw .|.- ((ownerExecuteMode .|. groupExecuteMode .|. otherExecuteMode)- .&. complement umask)- else rw- setFileMode f total+ _ <- setFileCreationMask umask+ let rw = fileMode st .&.+ (ownerReadMode .|. ownerWriteMode .|.+ groupReadMode .|. groupWriteMode .|.+ otherReadMode .|. otherWriteMode)+ total = if ex then rw .|.+ ((ownerExecuteMode .|. groupExecuteMode .|. otherExecuteMode)+ .&. complement umask)+ else rw+ setFileMode f total #endif
src/darcs.hs view
@@ -17,57 +17,72 @@ {-# LANGUAGE CPP #-} -module Main (main) where+-- |+-- Module : Main+-- Copyright : 2002-2003 David Roundy+-- License : GPL+-- Maintainer : darcs-devel@darcs.net+-- Stability : experimental+-- Portability : portable -import Prelude+module Main ( main ) where -import System.IO ( stdin, stdout, stderr, hSetBinaryMode )+import Control.Exception.Extensible ( AssertionFailed(..), handle ) import Control.Monad ( forM_ )+import System.IO ( stdin, stdout, stderr, hSetBinaryMode ) import System.Exit ( exitWith, ExitCode(..) ) import System.Environment ( getArgs )-import Control.Exception.Extensible ( AssertionFailed(..), handle ) -import Darcs.RunCommand ( runTheCommand )+import Darcs.Commands.Help ( helpCmd, listAvailableCommands, printVersion,+ commandControlList ) import Darcs.Flags ( DarcsFlag(Verbose) )-import Darcs.Commands.Help ( helpCmd, listAvailableCommands, printVersion, commandControlList )-import Darcs.SignalHandler ( withSignalsHandled )-import Version ( version, context, builddeps ) import Darcs.Global ( withAtexit, atexit ) import Darcs.Repository( reportBadSources )-import Preproc( preprocMain )+import Darcs.RunCommand ( runTheCommand )+import Darcs.SignalHandler ( withSignalsHandled )++import Darcs.Utils ( setDarcsEncodings ) import Exec ( ExecException(..) )+import Preproc( preprocMain )+import Version ( version, context, builddeps )+ #include "impossible.h" execExceptionHandler :: ExecException -> IO a-execExceptionHandler (ExecException cmd args redirects reason) =- do putStrLn $ "Failed to execute external command: " ++ unwords (cmd:args) ++ "\n"- ++ "Lowlevel error: " ++ reason ++ "\n"- ++ "Redirects: " ++ show redirects ++"\n"- exitWith $ ExitFailure 3+execExceptionHandler (ExecException cmd args redirects reason) = do+ putStrLn . unlines $+ [ "Failed to execute external command: " ++ unwords (cmd:args)+ , "Lowlevel error: " ++ reason+ , "Redirects: " ++ show redirects+ ]+ exitWith $ ExitFailure 3 main :: IO ()-main = withAtexit $ withSignalsHandled $- handle execExceptionHandler $- handle (\(AssertionFailed e) -> bug e) $ do- atexit reportBadSources- argv <- getArgs- case argv of- -- User called "darcs" without arguments.- [] -> printVersion >> helpCmd [] []- -- User called "darcs --foo" for some special foo.- ["-h"] -> helpCmd [] []- ["--help"] -> helpCmd [] []- ["--overview"] -> helpCmd [Verbose] []- ["--commands"] -> listAvailableCommands- ["-v"] -> putStrLn version- ["--version"] -> putStrLn version- ["--exact-version"] -> do- putStrLn $ "darcs compiled on "++__DATE__++", at "++__TIME__- putStrLn context- putStrLn $ "Compiled with:\n"- putStr builddeps- ("--preprocess-manual":rest) -> preprocMain rest- -- User called a normal darcs command, "darcs foo [args]".- _ -> do- forM_ [stdout, stdin, stderr] $ \h -> hSetBinaryMode h True- runTheCommand commandControlList (head argv) (tail argv)+main = withAtexit . withSignalsHandled . handleExecFail . handleAssertFail $ do+ atexit reportBadSources+ setDarcsEncodings+ argv <- getArgs+ -- Explicitly handle no-args and special "help" arguments.+ case argv of+ [] -> printVersion >> runHelpCmd+ ["-h"] -> runHelpCmd+ ["--help"] -> runHelpCmd+ ["--overview"] -> helpCmd [Verbose] []+ ["--commands"] -> listAvailableCommands+ ["-v"] -> putStrLn version+ ["--version"] -> putStrLn version+ ["--exact-version"] -> printExactVersion+ ("--preprocess-manual" : rest) -> preprocMain rest+ _ -> do+ forM_ [stdout, stdin, stderr] $ \h -> hSetBinaryMode h True+ runTheCommand commandControlList (head argv) (tail argv)+ where+ handleExecFail = handle execExceptionHandler+ handleAssertFail = handle $ \(AssertionFailed e) -> bug e+ runHelpCmd = helpCmd [] []+ printExactVersion = do+ putStrLn $ "darcs compiled on " ++ __DATE__ ++ ", at " ++ __TIME__+ putStrLn context+ putStrLn "Compiled with:\n"+ putStr builddeps+
src/test.hs view
@@ -124,6 +124,7 @@ , tests :: [String] , testDir :: Maybe FilePath , plain :: Bool+ , hideSuccesses :: Bool , threads :: Int } deriving (Data, Typeable, Eq) @@ -136,6 +137,7 @@ , tests %> Help "Pattern to limit the tests to run." %+ short 't' , testDir %> Help "Directory to run tests in" %+ Default (Nothing :: Maybe FilePath) , plain %> Help "Use plain-text output."+ , hideSuccesses %> Help "Hide successes." , threads %> Default (1 :: Int) %+ short 'j' ] data DarcsTest = DarcsTest deriving Typeable@@ -144,6 +146,7 @@ let args = [ "-j", show $ threads conf ] ++ concat [ ["-t", x ] | x <- tests conf ] ++ [ "--plain" | True <- [plain conf] ]+ ++ [ "--hide-successes" | True <- [hideSuccesses conf] ] ++ [ "--maximum-unsuitable-generated-tests", "700" ] case testDir conf of Nothing -> return ()
tests/amend-unrecord.sh view
@@ -45,10 +45,13 @@ echo 'ugh' > bar darcs add bar-echo y | darcs amend-record -a+# use amend to check it's still a short form for amend-record+# if we make amend-unrecord visible rather than hidden that would change+echo y | darcs amend -a darcs show contents bar | diff -q bar - -echo y | darcs amend-record --unrecord -a foo+# test that amend-unrecord alias exists, and --all and specifying files works+echo y | darcs amend-unrecord -a foo (echo x ; echo y) > foo.expected darcs show contents foo | diff -q foo.expected - darcs show contents bar | diff -q bar -
+ tests/failing-issue1522_trailing_slash_borkage.sh view
@@ -0,0 +1,35 @@+#!/usr/bin/env bash+## Test for issue1522 - Trailing slash borkage+##+## Copyright (C) 2012 Andreas Brandt+##+## Permission is hereby granted, free of charge, to any person+## obtaining a copy of this software and associated documentation+## files (the "Software"), to deal in the Software without+## restriction, including without limitation the rights to use, copy,+## modify, merge, publish, distribute, sublicense, and/or sell copies+## of the Software, and to permit persons to whom the Software is+## furnished to do so, subject to the following conditions:+##+## The above copyright notice and this permission notice shall be+## included in all copies or substantial portions of the Software.+##+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS+## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN+## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+## SOFTWARE.++. lib+rm -rf R+mkdir R++cd R+darcs init --repo R+touch R/d+darcs record --repo R -lam Yow! d/++cd ..
+ tests/failing-issue2100-add-failures.sh view
@@ -0,0 +1,39 @@+#!/usr/bin/env bash+## Test for issue2100 - darcs add should not fail on first failure+##+## Copyright (C) 2011 Eric Kow +##+## Permission is hereby granted, free of charge, to any person+## obtaining a copy of this software and associated documentation+## files (the "Software"), to deal in the Software without+## restriction, including without limitation the rights to use, copy,+## modify, merge, publish, distribute, sublicense, and/or sell copies+## of the Software, and to permit persons to whom the Software is+## furnished to do so, subject to the following conditions:+##+## The above copyright notice and this permission notice shall be+## included in all copies or substantial portions of the Software.+##+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS+## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN+## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+## SOFTWARE.++. lib # Load some portability helpers.+darcs init --repo R # Create our test repos.++cd R+mkdir d+echo 'Example content.' > d/f+not darcs add d/non-existent+not darcs whatsnew+not darcs add d/non-existent d/f+darcs whatsnew > log2 2>&1+grep "./d/non-existent does not exist" log2+not grep "addfile ./d/non-existent" log2+grep "adddir ./d" log2+grep "addfile ./d/f" log2
tests/issue1645-ignore-symlinks-case-fold.sh view
@@ -67,6 +67,6 @@ ln -s ./recorded-file ./Recorded-File ln -s "`pwd`"/recorded-file ./ReCorded-File not darcs w -l >log 2>&1 # expecting "No changes!"-darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well+not darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well not grep -vE "(^ *$|^\+|No changes!)" log rm Recorded-File ReCorded-File
tests/issue1645-ignore-symlinks.sh view
@@ -66,7 +66,7 @@ ln -s ../recorded-dir ./recorded-dir/loop # relative symlink ln -s "`pwd`"/recorded-dir ./recorded-dir/loop2 # absolute symlink not darcs w -l >log 2>&1 # expecting "No changes!"-darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well+not darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well not grep -vE "(^ *$|^\+|No changes!)" log # Case 3: looping symlink to boring dir@@ -75,7 +75,7 @@ ln -s ../boring-dir ./boring-dir/loop ln -s "`pwd`"/boting-dir ./boring-dir/loop2 not darcs w -l >log 2>&1 # expecting "No changes!"-darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well+not darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well not grep -vE "(^ *$|^\+|No changes!)" log # Case 4: non-looping symlink to non-recorded non-boring dir@@ -92,7 +92,7 @@ ln -s ./recorded-dir ./link ln -s "`pwd`"/recorded-dir ./link2 not darcs w -l >log 2>&1 # expecting "No changes!"-darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well+not darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well not grep -vE "(^ *$|^\+|No changes!)" log rm link link2 @@ -100,7 +100,7 @@ ln -s ./boring-dir ./link ln -s "`pwd`"/boring-dir ./link2 not darcs w -l >log 2>&1 # expecting "No changes!"-darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well+not darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well not grep -vE "(^ *$|^\+|No changes!)" log rm link link2 @@ -108,7 +108,7 @@ ln -s ../S link (cd ..; ln -s "`pwd`"/S ./R/link2) not darcs w -l >log 2>&1 # expecting "No changes!"-darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well+not darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well not grep -vE "(^ *$|^\+|No changes!)" log rm link link2 @@ -129,7 +129,7 @@ ln -s ./recorded-file ./link ln -s "`pwd`"/recorded-file ./link2 not darcs w -l >log 2>&1 # expecting "No changes!"-darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well+not darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well not grep -vE "(^ *$|^\+|No changes!)" log rm link link2 @@ -137,7 +137,7 @@ ln -s ./log ./link ln -s "`pwd`"/log ./link2 not darcs w -l >log 2>&1 # expecting "No changes!"-darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well+not darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well not grep -vE "(^ *$|^\+|No changes!)" log rm link link2 @@ -145,7 +145,7 @@ ln -s /completely/bogus/path ./link ln -s ../../../../not/exist ./link2 not darcs w -l >log 2>&1 # expecting "No changes!"-darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well+not darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well not grep -vE "(^ *$|^\+|No changes!)" log rm link link2 @@ -153,14 +153,14 @@ ln -s l l ln -s "`pwd`"/l2 ./l2 not darcs w -l >log 2>&1 # expecting "No changes!"-darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well+not darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well not grep -vE "(^ *$|^\+|No changes!)" log rm l l2 # Case 13: link to device file outside the repo ln -s /dev/zero l not darcs w -l >log 2>&1 # expecting "No changes!"-darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well+not darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well not grep -vE "(^ *$|^\+|No changes!)" log rm l @@ -169,6 +169,6 @@ ln -s f l ln -s "`pwd`"/f ./l2 not darcs w -l >log 2>&1 # expecting "No changes!"-darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well+not darcs rec -alm "should not happen" >>log 2>&1 # expecting "No changes!" as well not grep -vE "(^ *$|^\+|No changes!)" log rm f l l2
tests/issue1749-rmdir.sh view
@@ -35,8 +35,8 @@ darcs rec -a -m"add dir and file" darcs remove dir-darcs rec -a -m"remove dir" # removed dir but didn't remove file+# removed dir but not file - should be nothing to add+not darcs rec -a -m"remove dir" -darcs obliterate -a --patch "remove dir"+darcs obliterate -a --patch "remove dir" | grep 'No patches selected!' darcs check-
tests/issue1763-pull-fails-on-non-ascii-filenames.sh view
@@ -27,7 +27,10 @@ . lib -abort_windows # FIXME! We should figure out what's going on here+# This test should work on Windows because the codepoints in the filename+# are all <256. However an equivalent test with codepoints >=256 would+# likely fail.+# abort_windows rm -rf R S darcs init --repo R
tests/issue194.sh view
@@ -8,7 +8,7 @@ cd temp1/ ; echo 'x' > _darcs/prefs/author ; cd .. cd temp1/-touch test ; darcs record+touch test darcs add test ; darcs record -a -m 'test' darcs mv test best ; darcs record -a -m 'test -> best' darcs mv best test ; darcs record -a -m 'best -> test'
tests/issue2013_send_to_context.sh view
@@ -35,7 +35,7 @@ cd temp2 darcs init echo foo > a-darcs record -a -m add_a -A x+darcs record -alm add_a -A x # setup test cd ..@@ -45,7 +45,7 @@ darcs changes --context > context touch foo bar darcs add foo bar-darcs record -a -m add_foo_bar -A x+darcs record -alm add_foo_bar -A x # Test that --to works with send --context darcs send --author=me -a --to=random@random --sendmail-command='grep "^To: random@random$" %<' --context context
+ tests/issue2136-changes_created_as_for_multiple_files.sh view
@@ -0,0 +1,86 @@+#!/usr/bin/env bash++## Ensure changes --xml reports correct original filenames for multiple files.+##+## Copyright (C) 2012 Owen Stephens+##+## Permission is hereby granted, free of charge, to any person+## obtaining a copy of this software and associated documentation+## files (the "Software"), to deal in the Software without+## restriction, including without limitation the rights to use, copy,+## modify, merge, publish, distribute, sublicense, and/or sell copies+## of the Software, and to permit persons to whom the Software is+## furnished to do so, subject to the following conditions:+##+## The above copyright notice and this permission notice shall be+## included in all copies or substantial portions of the Software.+##+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS+## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN+## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+## SOFTWARE.++. lib++rm -rf R++darcs init --repo R++cd R++mkdir tldir++touch tldir/f1+darcs rec -alm 'Add tldir/f1'+echo foo >> tldir/f1+darcs rec -am 'Modify tldir/f1'+darcs move tldir/f1 tldir/f2+darcs rec -am 'Move tldir/f1 -> tldir/f2'++touch f3+darcs rec -alm 'Add f3'+darcs move f3 f4+darcs rec -am 'Move f3 -> f4'+darcs move f4 f5+darcs rec -am 'Move f4 -> f5'+touch f6+darcs rec -alm 'Add non-changing file f6'++mkdir tldir/d1+darcs rec -alm 'Add tldir/d1'+darcs move tldir/d1 tldir/d2+darcs rec -am 'Move tldir/d1 -> tldir/d2'++mkdir d3+darcs rec -alm 'Add d3'+darcs move d3 d4+darcs rec -am 'Move d3 -> d4'+darcs move d4 d5+darcs rec -am 'Move d4 -> d5'++# Ensure all original names are reported, both forwards, and reversed.+xmlChanges=$(darcs cha --xml tldir/f2 f5 tldir/d2 d5 f6)+xmlChangesRev=$(darcs cha --reverse --xml tldir/f2 f5 tldir/d2 d5 f6)++# xmlChanges needs to be quoted everywhere, otherwise this hack to retrieve the+# 2 following lines won't work.+checkRename () {+ echo "$1" | grep "<created_as current_name='\./$2' original_name='\./$3'>" -C2 | tail -1 | grep "<name>$4</name>"+}++checkInXML () {+ checkRename "$1" "d5" "d3" "Add d3"+ checkRename "$1" "f5" "f3" "Add f3"+ checkRename "$1" "tldir/d2" "tldir/d1" "Add tldir/d1"+ checkRename "$1" "tldir/f2" "tldir/f1" "Add tldir/f1"+}++checkInXML "$xmlChanges"+checkInXML "$xmlChangesRev"++# But don't mention unchanged files.+echo "$xmlChanges" | not grep "<created_as[^>]*'\./f6'"
+ tests/issue2139-mv-to-dir.sh view
@@ -0,0 +1,54 @@+#!/usr/bin/env bash+## Test for issue2139 - darcs should accept to mv to the+## current working directory+##+## Copyright (C) 2012 Eric Kow+##+## Permission is hereby granted, free of charge, to any person+## obtaining a copy of this software and associated documentation+## files (the "Software"), to deal in the Software without+## restriction, including without limitation the rights to use, copy,+## modify, merge, publish, distribute, sublicense, and/or sell copies+## of the Software, and to permit persons to whom the Software is+## furnished to do so, subject to the following conditions:+##+## The above copyright notice and this permission notice shall be+## included in all copies or substantial portions of the Software.+##+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS+## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN+## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+## SOFTWARE.++. lib # Load some portability helpers.++darcs init --repo R # Create our test repos.++cd R++# move dir to root+mkdir a a/a2 a/a3+darcs record -lam 'Some directories (a)'+darcs mv a/a2 .+test -d a2 +cd a+darcs mv a3 ..+not test -d a3+cd ..+test -d a3++# move dir to non-root dir+mkdir b b2 b3+darcs record -lam 'Some directories (b)'+darcs mv b2 b+test -d b/b2+cd b+darcs mv ../b3 .+test -d b3+cd ..++cd ..
tests/match-date.sh view
@@ -71,7 +71,6 @@ dd=04 hhmmss="15:08" create_entry "$year-$mm-$dd $hhmmss"-echo "/tester/a///" | tr / \\012 | darcs record -m "" --pipe bar match_date "$year-$mm-$dd" match_date "$year$mm$dd" match_date "$year-$mm"
tests/mv-formerly-pl.sh view
@@ -97,7 +97,7 @@ # regardless of case-ok, we do NOT want this mv at all not darcs mv cs-n-4 foo.d/cs-n-4 2>&1 | grep "already exists" -not darcs mv --case-ok cs-y-4 foo.d/cs-y-4 2>&1 | grep "already exists"+not darcs mv --case-ok cs-y-4 foo.d/cs-y-4 2>&1 | grep "unadded" # --------------------------- # end case sensitivity series
tests/network/issue2090-transfer-mode.sh view
@@ -23,6 +23,8 @@ ## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ## SOFTWARE. +echo 'Comment this line out and run the script by hand'; exit 200+ . $(dirname $0)/../lib . $(dirname $0)/sshlib
tests/network/ssh.sh view
@@ -1,4 +1,6 @@ #!/bin/bash+echo 'Comment this line out and run the script by hand'; exit 200+ . $(dirname $0)/../lib . $(dirname $0)/sshlib
tests/pending_has_conflicts.sh view
@@ -38,10 +38,8 @@ write_buggy_pending -darcs record -a -m foo 2>&1 | tee out+not darcs record -a -m foo 2>&1 | tee out grep 'pending has conflicts' out--darcs record -a -m foo darcs changes -v
+ tests/rename_shouldnt_affect_prefixes.sh view
@@ -0,0 +1,50 @@+#!/usr/bin/env bash+## Renaming a -> b should not affect any filenames with prefix b, when looking+## for the original name of the files in changes --xml, or when annotating.+##+## Copyright (C) 2012 Owen Stephens+##+## Permission is hereby granted, free of charge, to any person+## obtaining a copy of this software and associated documentation+## files (the "Software"), to deal in the Software without+## restriction, including without limitation the rights to use, copy,+## modify, merge, publish, distribute, sublicense, and/or sell copies+## of the Software, and to permit persons to whom the Software is+## furnished to do so, subject to the following conditions:+##+## The above copyright notice and this permission notice shall be+## included in all copies or substantial portions of the Software.+##+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS+## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN+## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+## SOFTWARE.++. lib++rm -rf R++darcs init --repo R++cd R++echo -e 'a\nb\nc' > a+cp a bb+darcs rec -alm 'Add a bb'++darcs move a b+darcs rec -am 'Move a -> b'++darcs cha --xml b bb > changes.xml++grep "original_name='./a'" < changes.xml+# Ensure we've not used a prefix of the filename for the move.+not grep "original_name='./ab'" < changes.xml++# Ensure that we are able to annotate bb (if the rename has affected bb+# internally, we'll not be able to annotate the file)+darcs annotate bb | not grep unknown
tests/rmdir.sh view
@@ -47,12 +47,6 @@ echo hello world > foo/baz cd .. -# remove a directory from temp1 and record-cd temp1-rm -rf foo-darcs record -a -m del-cd ..- cd temp3 test -e foo/baz test -e foo/bar
tests/rollback.sh view
@@ -27,7 +27,7 @@ darcs revert --all fi : Create local change and record it- echo X$i > f && darcs record -A me --ignore-time -m x$i --all f+ echo X$i > f && darcs record -l -A me --ignore-time -m x$i --all f done cd ..
tests/workingdir.sh view
@@ -44,13 +44,13 @@ echo temp2 > d darcs pull -a -t 1 grep temp1 b-grep temp2 b-darcs-backup0-grep temp2 d-darcs-backup0+grep temp2 b.~0~+grep temp2 d.~0~ # now make sure we didn't overdo it darcs pull -a grep '1-b2' b-test -e b-darcs-backup0-test ! -e b-darcs-backup1+test -e b.~0~+test ! -e b.~1~ cd .. rm -rf temp2 @@ -60,8 +60,8 @@ echo 2-b2 > b darcs pull -a grep "v v v" b-grep "2-b2" b-darcs-backup0-not grep "v v v" b-darcs-backup0+grep "2-b2" b.~0~+not grep "v v v" b.~0~ cd .. rm -rf temp2 @@ -69,11 +69,11 @@ darcs get temp1 temp2 --to-patch a cd temp2 echo temp2 > a/y-echo old-bak > a/y-darcs-backup0+echo old-bak > a/y.~0~ darcs pull -a grep temp0 a/y-grep old-bak a/y-darcs-backup0-grep temp2 a/y-darcs-backup1+grep old-bak a/y.~0~+grep temp2 a/y.~1~ cd .. rm -rf temp2 @@ -81,11 +81,11 @@ darcs get temp1 temp2 --to-patch a cd temp2 mkdir a/y-echo old-bak > a/y-darcs-backup0+echo old-bak > a/y.~0~ darcs pull -a grep temp0 a/y-grep old-bak a/y-darcs-backup0-test -d a/y-darcs-backup1+grep old-bak a/y.~0~+test -d a/y.~1~ cd .. rm -rf temp2