darcs 2.4.4 → 2.5
raw patch · 221 files changed
+11541/−8840 lines, 221 filesdep +extensible-exceptionsdep +regex-posixdep +tardep −utf8-stringdep ~basedep ~hashed-storagedep ~haskelinesetup-changedbinary-addedPVP ok
version bump matches the API change (PVP)
Dependencies added: extensible-exceptions, regex-posix, tar, text
Dependencies removed: utf8-string
Dependency ranges changed: base, hashed-storage, haskeline, mmap
API changes (from Hackage documentation)
- ByteStringUtils: break_after_nth_newline :: Int -> ByteString -> Maybe (ByteString, ByteString)
- ByteStringUtils: break_before_nth_newline :: Int -> ByteString -> (ByteString, ByteString)
- ByteStringUtils: is_funky :: ByteString -> Bool
- Darcs.ArgumentDefaults: get_default_flags :: String -> [DarcsOption] -> [DarcsFlag] -> IO [DarcsFlag]
- Darcs.Arguments: HTTPPipelining :: DarcsFlag
- Darcs.Arguments: optionFromDarcsoption :: AbsolutePath -> DarcsOption -> [OptDescr DarcsFlag]
- Darcs.Arguments: testByDefault :: [DarcsFlag] -> [DarcsFlag]
- Darcs.CheckFileSystem: can_I_use_mmap :: IO Bool
- Darcs.Commands.Record: fileExists :: Slurpy -> SubPath -> IO Bool
- Darcs.CommandsAux: check_paths :: (Patchy p) => [DarcsFlag] -> FL p -> IO ()
- Darcs.CommandsAux: has_malicious_path :: (Patchy p) => p -> Bool
- Darcs.CommandsAux: malicious_patches :: (Patchy p) => [Sealed2 p] -> [Sealed2 p]
- Darcs.Compat: atomic_create :: FilePath -> IO ()
- Darcs.Compat: mk_stdout_temp :: String -> IO String
- Darcs.Compat: sloppy_atomic_create :: FilePath -> IO ()
- Darcs.Compat: stdout_is_a_pipe :: IO Bool
- Darcs.Email: make_email :: String -> [(String, String)] -> (Maybe Doc) -> Doc -> (Maybe String) -> Doc
- Darcs.Email: read_email :: ByteString -> ByteString
- Darcs.External: darcs_program :: IO String
- Darcs.External: diff_program :: IO String
- Darcs.External: sendmail_path :: IO String
- Darcs.Flags: HTTPPipelining :: DarcsFlag
- Darcs.Flags: want_external_merge :: [DarcsFlag] -> Maybe String
- Darcs.Global: with_atexit :: IO a -> IO a
- Darcs.IO: instance (MonadPlus m, MonadError e m) => ReadableDirectory (TreeMonad m)
- Darcs.Lock: rm_recursive :: FilePath -> IO ()
- Darcs.Lock: tempdir_loc :: IO FilePath
- Darcs.Lock: world_readable_temp :: String -> IO String
- Darcs.Match: instance (RepoPatch p) => MatchMonad IO p
- Darcs.Match: instance (RepoPatch p) => MatchMonad SlurpMonad p
- Darcs.Patch: applyToSlurpy :: (Apply p, Monad m) => p -> Slurpy -> m Slurpy
- Darcs.Patch: commuteFL :: (Commute p) => (p :> FL p) -> Either (Sealed2 p) ((FL p :> p))
- Darcs.Patch: forceReplaceSlurpy :: Prim -> Slurpy -> Maybe Slurpy
- Darcs.Patch: merger :: String -> Patch -> Patch -> Patch
- Darcs.Patch: unravel :: Patch -> [FL Prim]
- Darcs.Patch.Apply: applyToSlurpy :: (Apply p, Monad m) => p -> Slurpy -> m Slurpy
- Darcs.Patch.Apply: forceReplaceSlurpy :: Prim -> Slurpy -> Maybe Slurpy
- Darcs.Patch.Bundle: hash_bundle :: (RepoPatch p) => [PatchInfo] -> FL (Named p) -> String
- Darcs.Patch.Bundle: make_bundle :: (RepoPatch p) => [DarcsFlag] -> Tree IO -> [PatchInfo] -> FL (Named p) -> IO Doc
- Darcs.Patch.Bundle: make_bundle2 :: (RepoPatch p) => [DarcsFlag] -> Tree IO -> [PatchInfo] -> FL (Named p) -> FL (Named p) -> IO Doc
- Darcs.Patch.Bundle: make_context :: [PatchInfo] -> Doc
- Darcs.Patch.Bundle: scan_bundle :: (RepoPatch p) => ByteString -> Either String (SealedPatchSet p)
- Darcs.Patch.Bundle: scan_context :: (RepoPatch p) => ByteString -> PatchSet p
- Darcs.Patch.Choices: instance (Invert p) => Invert (PatchChoice p)
- Darcs.Patch.Commute: mangle_unravelled :: [Sealed (FL Prim)] -> Sealed (FL Prim)
- Darcs.Patch.Commute: public_unravel :: Patch -> [Sealed (FL Prim)]
- Darcs.Patch.Core: join_patchesFL :: FL Patch -> Patch
- Darcs.Patch.Core: merger_undo :: Patch -> Patch
- Darcs.Patch.Core: n_fn :: FilePath -> FilePath
- Darcs.Patch.Depends: commute_to_end :: (RepoPatch p) => FL (Named p) -> PatchSet p -> (FL (Named p) :< RL (RL (PatchInfoAnd p)))
- Darcs.Patch.Depends: deep_optimize_patchset :: PatchSet p -> PatchSet p
- Darcs.Patch.Depends: get_common_and_uncommon :: (RepoPatch p) => (PatchSet p, PatchSet p) -> ([PatchInfo], RL (PatchInfoAnd p) :\/: RL (PatchInfoAnd p))
- Darcs.Patch.Depends: get_common_and_uncommon_or_missing :: (RepoPatch p) => (PatchSet p, PatchSet p) -> Either PatchInfo ([PatchInfo], RL (PatchInfoAnd p) :\/: RL (PatchInfoAnd p))
- Darcs.Patch.Depends: get_patches_beyond_tag :: (RepoPatch p) => PatchInfo -> PatchSet p -> FlippedSeal (RL (PatchInfoAnd p))
- Darcs.Patch.Depends: get_patches_in_tag :: (RepoPatch p) => PatchInfo -> PatchSet p -> SealedPatchSet p
- Darcs.Patch.Depends: get_tags_right :: RL (RL (PatchInfoAnd p)) -> [PatchInfo]
- Darcs.Patch.Depends: instance Error MissingPatch
- Darcs.Patch.Depends: optimize_patchset :: PatchSet p -> PatchSet p
- Darcs.Patch.Depends: patchset_intersection :: (RepoPatch p) => [SealedPatchSet p] -> SealedPatchSet p
- Darcs.Patch.Depends: patchset_union :: (RepoPatch p) => [SealedPatchSet p] -> SealedPatchSet p
- Darcs.Patch.Depends: slightly_optimize_patchset :: PatchSet p -> PatchSet p
- Darcs.Patch.FileName: break_on_dir :: FileName -> Maybe (FileName, FileName)
- Darcs.Patch.FileName: decode_white :: String -> FilePath
- Darcs.Patch.FileName: encode_white :: FilePath -> String
- Darcs.Patch.FileName: norm_path :: FileName -> FileName
- Darcs.Patch.FileName: own_name :: FileName -> FileName
- Darcs.Patch.Info: add_junk :: PatchInfo -> IO PatchInfo
- Darcs.Patch.Info: human_friendly :: PatchInfo -> Doc
- Darcs.Patch.Info: invert_name :: PatchInfo -> PatchInfo
- Darcs.Patch.Info: is_inverted :: PatchInfo -> Bool
- Darcs.Patch.Info: is_tag :: PatchInfo -> Bool
- Darcs.Patch.Info: just_author :: PatchInfo -> String
- Darcs.Patch.Info: just_name :: PatchInfo -> String
- Darcs.Patch.Info: make_alt_filename :: PatchInfo -> String
- Darcs.Patch.Info: make_filename :: PatchInfo -> String
- Darcs.Patch.Info: pi_author :: PatchInfo -> String
- Darcs.Patch.Info: pi_date :: PatchInfo -> CalendarTime
- Darcs.Patch.Info: pi_log :: PatchInfo -> [String]
- Darcs.Patch.Info: pi_name :: PatchInfo -> String
- Darcs.Patch.Info: pi_rename :: PatchInfo -> String -> PatchInfo
- Darcs.Patch.Info: pi_tag :: PatchInfo -> Maybe String
- Darcs.Patch.Info: set_pi_date :: String -> PatchInfo -> PatchInfo
- Darcs.Patch.Info: to_xml :: PatchInfo -> Doc
- Darcs.Patch.Match: apply_matcher :: Matcher p -> PatchInfoAnd p -> Bool
- Darcs.Patch.Match: make_matcher :: String -> (Sealed2 (PatchInfoAnd p) -> Bool) -> Matcher p
- Darcs.Patch.Match: match_parser :: (Patchy p) => CharParser st (MatchFun p)
- Darcs.Patch.Match: match_pattern :: (Patchy p) => PatchMatch -> Matcher p
- Darcs.Patch.Match: patch_match :: String -> PatchMatch
- Darcs.Patch.MatchData: patch_match :: String -> PatchMatch
- Darcs.Patch.Non: prop_adjust_twice :: (Patchy p, ToFromPrim p) => p -> Non p -> Maybe Doc
- Darcs.Patch.Non: type NonPatch = Non Prim
- Darcs.Patch.Permutations: head_permutationsFL :: (Commute p) => FL p -> [FL p]
- Darcs.Patch.Permutations: head_permutationsRL :: (Commute p) => RL p -> [RL p]
- Darcs.Patch.Permutations: remove_subsequenceFL :: (MyEq p, Commute p) => FL p -> FL p -> Maybe (FL p)
- Darcs.Patch.Permutations: remove_subsequenceRL :: (MyEq p, Commute p) => RL p -> RL p -> Maybe (RL p)
- Darcs.Patch.Prim: commute_no_conflicts :: (Conflict p) => (p :> p) -> Maybe ((p :> p))
- Darcs.Patch.Prim: commute_no_conflictsFL :: (Conflict p) => (p :> FL p) -> Maybe ((FL p :> p))
- Darcs.Patch.Prim: commute_no_conflictsRL :: (Conflict p) => (RL p :> p) -> Maybe ((p :> RL p))
- Darcs.Patch.Prim: is_identity :: Prim -> EqCheck
- Darcs.Patch.Prim: n_fn :: FilePath -> FilePath
- Darcs.Patch.Prim: try_shrinking_inverse :: FL Prim -> Maybe (FL Prim)
- Darcs.Patch.Prim: try_tok_internal :: String -> ByteString -> ByteString -> ByteString -> Maybe [ByteString]
- Darcs.Patch.Properties: commute_inverses :: (Patchy p) => ((p :> p) -> Maybe ((p :> p))) -> (p :> p) -> Maybe Doc
- Darcs.Patch.Properties: identity_commutes :: (Patchy p) => p -> Maybe Doc
- Darcs.Patch.Properties: inverse_doesnt_commute :: (Patchy p) => p -> Maybe Doc
- Darcs.Patch.Properties: join_commute :: ((Prim :> Prim) -> Maybe (Prim)) -> (Prim :> (Prim :> Prim)) -> Maybe Doc
- Darcs.Patch.Properties: join_inverses :: ((Prim :> Prim) -> Maybe (Prim)) -> Prim -> Maybe Doc
- Darcs.Patch.Properties: merge_arguments_consistent :: (Patchy p) => (p -> Maybe Doc) -> (p :\/: p) -> Maybe Doc
- Darcs.Patch.Properties: merge_commute :: (Patchy p) => (p :\/: p) -> Maybe Doc
- Darcs.Patch.Properties: merge_consistent :: (Patchy p) => (p -> Maybe Doc) -> (p :\/: p) -> Maybe Doc
- Darcs.Patch.Properties: merge_either_way :: (Patchy p) => (p :\/: p) -> Maybe Doc
- Darcs.Patch.Properties: partial_permutivity :: (Patchy p) => ((p :> p) -> Maybe ((p :> p))) -> (p :> (p :> p)) -> Maybe Doc
- Darcs.Patch.Properties: patch_and_inverse_commute :: (Patchy p) => ((p :> p) -> Maybe ((p :> p))) -> (p :> p) -> Maybe Doc
- Darcs.Patch.ReadMonads: alter_input :: (ParserM m) => (ByteString -> ByteString) -> m ()
- Darcs.Patch.ReadMonads: instance Monad LM
- Darcs.Patch.ReadMonads: instance ParserM LM
- Darcs.Patch.ReadMonads: lex_char :: (ParserM m) => Char -> m ()
- Darcs.Patch.ReadMonads: lex_eof :: (ParserM m) => m ()
- Darcs.Patch.ReadMonads: lex_string :: (ParserM m) => String -> m ()
- Darcs.Patch.ReadMonads: lex_strings :: (ParserM m) => [String] -> m String
- Darcs.Patch.ReadMonads: maybe_work :: (ParserM m) => (ByteString -> Maybe (a, ByteString)) -> m (Maybe a)
- Darcs.Patch.ReadMonads: my_lex :: ByteString -> Maybe (ByteString, ByteString)
- Darcs.Patch.ReadMonads: parse_lazily :: LM a -> ByteString -> (a, ByteString)
- Darcs.Patch.ReadMonads: parse_strictly :: SM a -> ByteString -> Maybe (a, ByteString)
- Darcs.Patch.ReadMonads: peek_input :: (ParserM m) => m ByteString
- Darcs.Patch.Real: instance (MyEq p) => Eq (Sealed p)
- Darcs.Patch.Real: is_consistent :: RealPatch -> Maybe Doc
- Darcs.Patch.Real: is_duplicate :: RealPatch -> Bool
- Darcs.Patch.Real: is_forward :: RealPatch -> Maybe Doc
- Darcs.Patch.Set: type PatchSet p = RL (RL (PatchInfoAnd p))
- Darcs.Patch.TouchesFiles: choose_touching :: (Patchy p) => [FilePath] -> FL p -> Sealed (FL p)
- Darcs.Patch.TouchesFiles: deselect_not_touching :: (Patchy p) => [FilePath] -> PatchChoices p -> PatchChoices p
- Darcs.Patch.TouchesFiles: look_touch :: (Patchy p) => [FilePath] -> p -> (Bool, [FilePath])
- Darcs.Patch.TouchesFiles: select_not_touching :: (Patchy p) => [FilePath] -> PatchChoices p -> PatchChoices p
- Darcs.Patch.TouchesFiles: select_touching :: (Patchy p) => [FilePath] -> PatchChoices p -> PatchChoices p
- Darcs.Population: lookup_creation_pop :: PatchInfo -> FilePath -> Population -> Maybe Population
- Darcs.Population: lookup_pop :: FilePath -> Population -> Maybe Population
- Darcs.Population: modified_to_xml :: Info -> Doc
- Darcs.RemoteApply: apply_as :: [DarcsFlag] -> Maybe String
- Darcs.RemoteApply: remote_apply :: [DarcsFlag] -> String -> Doc -> IO ExitCode
- Darcs.Repository: add_to_pending :: (RepoPatch p) => Repository p -> FL Prim -> IO ()
- Darcs.Repository: copy_oldrepo_patches :: (RepoPatch p) => [DarcsFlag] -> Repository p -> FilePath -> IO ()
- Darcs.Repository: pendingChanges :: (RepoPatch p) => Repository p -> [SubPath] -> IO (Sealed (FL Prim))
- Darcs.Repository: read_repo :: (RepoPatch p) => Repository p -> IO (PatchSet p)
- Darcs.Repository: slurp_pending :: (RepoPatch p) => Repository p -> IO Slurpy
- Darcs.Repository: slurp_recorded :: (RepoPatch p) => Repository p -> IO Slurpy
- Darcs.Repository: slurp_recorded_and_unrecorded :: (RepoPatch p) => Repository p -> IO (Slurpy, Slurpy)
- Darcs.Repository: type PatchSet p = RL (RL (PatchInfoAnd p))
- Darcs.Repository.ApplyPatches: apply_patches :: (Patchy p) => [DarcsFlag] -> FL (PatchInfoAnd p) -> IO ()
- Darcs.Repository.ApplyPatches: apply_patches_with_feedback :: (Patchy p) => [DarcsFlag] -> String -> FL (PatchInfoAnd p) -> IO ()
- Darcs.Repository.Checkpoint: get_checkpoint :: (RepoPatch p) => Repository p -> IO (Maybe (Sealed (Named p)))
- Darcs.Repository.Checkpoint: get_checkpoint_by_default :: (RepoPatch p) => Repository p -> IO (Maybe (Sealed (Named p)))
- Darcs.Repository.Checkpoint: identify_checkpoint :: (RepoPatch p) => Repository p -> IO (Maybe PatchInfo)
- Darcs.Repository.Checkpoint: write_checkpoint_patch :: (RepoPatch p) => Named p -> IO ()
- Darcs.Repository.DarcsRepo: add_to_inventory :: FilePath -> [PatchInfo] -> IO ()
- Darcs.Repository.DarcsRepo: add_to_tentative_inventory :: (RepoPatch p) => [DarcsFlag] -> Named p -> IO FilePath
- Darcs.Repository.DarcsRepo: add_to_tentative_pristine :: (Effect p) => p -> IO ()
- Darcs.Repository.DarcsRepo: copy_patches :: [DarcsFlag] -> FilePath -> FilePath -> [PatchInfo] -> IO ()
- Darcs.Repository.DarcsRepo: finalize_pristine_changes :: IO ()
- Darcs.Repository.DarcsRepo: finalize_tentative_changes :: IO ()
- Darcs.Repository.DarcsRepo: read_repo :: (RepoPatch p) => [DarcsFlag] -> String -> IO (SealedPatchSet p)
- Darcs.Repository.DarcsRepo: read_tentative_repo :: (RepoPatch p) => [DarcsFlag] -> String -> IO (SealedPatchSet p)
- Darcs.Repository.DarcsRepo: remove_from_tentative_inventory :: (RepoPatch p) => Bool -> [DarcsFlag] -> FL (Named p) -> IO ()
- Darcs.Repository.DarcsRepo: revert_tentative_changes :: IO ()
- Darcs.Repository.DarcsRepo: write_and_read_patch :: (RepoPatch p) => [DarcsFlag] -> PatchInfoAnd p -> IO (PatchInfoAnd p)
- Darcs.Repository.DarcsRepo: write_inventory :: (RepoPatch p) => FilePath -> PatchSet p -> IO ()
- Darcs.Repository.DarcsRepo: write_inventory_and_patches :: (RepoPatch p) => [DarcsFlag] -> PatchSet p -> IO ()
- Darcs.Repository.HashedIO: applyHashed :: (Patchy q) => Cache -> [DarcsFlag] -> String -> q -> IO String
- Darcs.Repository.HashedIO: clean_hashdir :: Cache -> HashedDir -> [String] -> IO ()
- Darcs.Repository.HashedIO: listHashedContents :: String -> Cache -> String -> IO [String]
- Darcs.Repository.HashedIO: slurpHashedPristine :: Cache -> Compression -> String -> IO Slurpy
- Darcs.Repository.HashedIO: writeHashedPristine :: Cache -> Compression -> Slurpy -> IO String
- Darcs.Repository.HashedRepo: add_to_tentative_inventory :: (RepoPatch p) => Cache -> Compression -> PatchInfoAnd p -> IO FilePath
- Darcs.Repository.HashedRepo: apply_to_tentative_pristine :: (Patchy q) => Cache -> [DarcsFlag] -> q -> IO ()
- Darcs.Repository.HashedRepo: clean_pristine :: Repository p -> IO ()
- Darcs.Repository.HashedRepo: copy_partials_pristine :: (FilePathLike fp) => Cache -> Compression -> String -> String -> [fp] -> IO ()
- Darcs.Repository.HashedRepo: copy_pristine :: Cache -> Compression -> String -> String -> IO ()
- Darcs.Repository.HashedRepo: copy_repo :: (RepoPatch p) => Repository p -> [DarcsFlag] -> String -> IO ()
- Darcs.Repository.HashedRepo: finalize_tentative_changes :: (RepoPatch p) => Repository p -> Compression -> IO ()
- Darcs.Repository.HashedRepo: read_repo :: (RepoPatch p) => Repository p -> String -> IO (PatchSet p)
- Darcs.Repository.HashedRepo: read_tentative_repo :: (RepoPatch p) => Repository p -> String -> IO (PatchSet p)
- Darcs.Repository.HashedRepo: remove_from_tentative_inventory :: (RepoPatch p) => Repository p -> Compression -> FL (Named p) -> IO ()
- Darcs.Repository.HashedRepo: revert_tentative_changes :: IO ()
- Darcs.Repository.HashedRepo: slurp_all_but_darcs :: FilePath -> IO Slurpy
- Darcs.Repository.HashedRepo: slurp_pristine :: Cache -> Compression -> String -> String -> IO Slurpy
- Darcs.Repository.HashedRepo: write_and_read_patch :: (RepoPatch p) => Cache -> Compression -> PatchInfoAnd p -> IO (PatchInfoAnd p)
- Darcs.Repository.HashedRepo: write_tentative_inventory :: (RepoPatch p) => Cache -> Compression -> PatchSet p -> IO ()
- Darcs.Repository.Internal: announce_merge_conflicts :: String -> [DarcsFlag] -> FL Prim -> IO Bool
- Darcs.Repository.Internal: check_unrecorded_conflicts :: (RepoPatch p) => [DarcsFlag] -> FL (Named p) -> IO Bool
- Darcs.Repository.Internal: make_new_pending :: (RepoPatch p) => Repository p -> FL Prim -> IO ()
- Darcs.Repository.Internal: read_repo :: (RepoPatch p) => Repository p -> IO (PatchSet p)
- Darcs.Repository.Internal: slurp_pending :: (RepoPatch p) => Repository p -> IO Slurpy
- Darcs.Repository.Internal: slurp_recorded :: (RepoPatch p) => Repository p -> IO Slurpy
- Darcs.Repository.Internal: slurp_recorded_and_unrecorded :: (RepoPatch p) => Repository p -> IO (Slurpy, Slurpy)
- Darcs.Repository.Internal: type PatchSet p = RL (RL (PatchInfoAnd p))
- Darcs.Repository.LowLevel: read_pending :: (RepoPatch p) => Repository p -> IO (Sealed (FL Prim))
- Darcs.Repository.LowLevel: read_pendingfile :: String -> IO (Sealed (FL Prim))
- Darcs.Repository.Motd: get_motd :: String -> IO ByteString
- Darcs.Repository.Motd: show_motd :: [DarcsFlag] -> String -> IO ()
- Darcs.Repository.Pristine: slurpPristine :: Pristine -> IO (Maybe Slurpy)
- Darcs.Repository.State: pendingChanges :: (RepoPatch p) => Repository p -> [SubPath] -> IO (Sealed (FL Prim))
- Darcs.Resolution: external_resolution :: (RepoPatch p) => Tree IO -> String -> FL Prim -> FL Prim -> p -> IO (Sealed (FL Prim))
- Darcs.Resolution: patchset_conflict_resolutions :: (RepoPatch p) => PatchSet p -> Sealed (FL Prim)
- Darcs.Resolution: standard_resolution :: (RepoPatch p) => p -> Sealed (FL Prim)
- Darcs.RunCommand: run_the_command :: String -> [String] -> IO ()
- Darcs.SelectChanges: view_changes :: (RepoPatch p) => [DarcsFlag] -> FL (PatchInfoAnd p) -> IO ()
- Darcs.SelectChanges: with_selected_changes :: (RepoPatch p) => WithPatches (PatchInfoAnd p) a
- Darcs.SelectChanges: with_selected_changes' :: WithPatches Prim a
- Darcs.SelectChanges: with_selected_changes_reversed :: (RepoPatch p) => WithPatches (PatchInfoAnd p) a
- Darcs.SelectChanges: with_selected_changes_to_files :: (RepoPatch p) => WithPatchesToFiles (PatchInfoAnd p) a
- Darcs.SelectChanges: with_selected_changes_to_files' :: WithPatchesToFiles Prim a
- Darcs.SelectChanges: with_selected_last_changes_reversed :: (RepoPatch p) => WithPatches (PatchInfoAnd p) a
- Darcs.SelectChanges: with_selected_last_changes_reversed' :: WithPatches Prim a
- Darcs.SelectChanges: with_selected_last_changes_to_files :: (RepoPatch p) => WithPatchesToFiles (PatchInfoAnd p) a
- Darcs.SelectChanges: with_selected_last_changes_to_files' :: WithPatchesToFiles Prim a
- Darcs.SelectChanges: with_selected_last_changes_to_files_reversed :: (RepoPatch p) => WithPatchesToFiles (PatchInfoAnd p) a
- Darcs.SelectChanges: with_selected_patch_from_repo :: (RepoPatch p) => String -> Repository p -> [DarcsFlag] -> ((FL (PatchInfoAnd p) :> PatchInfoAnd p) -> IO ()) -> IO ()
- Darcs.SignalHandler: stdout_is_a_pipe :: IO Bool
- Darcs.SlurpDirectory: co_slurp :: Slurpy -> FilePath -> IO Slurpy
- Darcs.SlurpDirectory: data SlurpMonad a
- Darcs.SlurpDirectory: data Slurpy
- Darcs.SlurpDirectory: doesDirectoryReallyExist :: FilePath -> IO Bool
- Darcs.SlurpDirectory: doesFileReallyExist :: FilePath -> IO Bool
- Darcs.SlurpDirectory: empty_slurpy :: Slurpy
- Darcs.SlurpDirectory: get_dircontents :: Slurpy -> [Slurpy]
- Darcs.SlurpDirectory: get_filecontents :: Slurpy -> FileContents
- Darcs.SlurpDirectory: get_length :: Slurpy -> FileOffset
- Darcs.SlurpDirectory: get_mtime :: Slurpy -> EpochTime
- Darcs.SlurpDirectory: get_path_list :: Slurpy -> FilePath -> [FilePath]
- Darcs.SlurpDirectory: get_slurp :: FileName -> Slurpy -> Maybe Slurpy
- Darcs.SlurpDirectory: isFileReallySymlink :: FilePath -> IO Bool
- Darcs.SlurpDirectory: is_dir :: Slurpy -> Bool
- Darcs.SlurpDirectory: is_file :: Slurpy -> Bool
- Darcs.SlurpDirectory: list_slurpy :: Slurpy -> [FilePath]
- Darcs.SlurpDirectory: list_slurpy_dirs :: Slurpy -> [FilePath]
- Darcs.SlurpDirectory: list_slurpy_files :: Slurpy -> [FilePath]
- Darcs.SlurpDirectory: mmap_slurp :: FilePath -> IO Slurpy
- Darcs.SlurpDirectory: slurp :: (FilePathLike p) => p -> IO Slurpy
- Darcs.SlurpDirectory: slurp_has :: FilePath -> Slurpy -> Bool
- Darcs.SlurpDirectory: slurp_has_anycase :: FilePath -> Slurpy -> Bool
- Darcs.SlurpDirectory: slurp_hasdir :: FileName -> Slurpy -> Bool
- Darcs.SlurpDirectory: slurp_hasfile :: FileName -> Slurpy -> Bool
- Darcs.SlurpDirectory: slurp_modfile :: FileName -> (FileContents -> Maybe FileContents) -> Slurpy -> Maybe Slurpy
- Darcs.SlurpDirectory: slurp_name :: Slurpy -> FilePath
- Darcs.SlurpDirectory: slurp_remove :: FileName -> Slurpy -> Maybe Slurpy
- Darcs.SlurpDirectory: slurp_removedir :: FileName -> Slurpy -> Maybe Slurpy
- Darcs.SlurpDirectory: slurp_removefile :: FileName -> Slurpy -> Maybe Slurpy
- Darcs.SlurpDirectory: slurp_unboring :: (FilePath -> Bool) -> FilePath -> IO Slurpy
- Darcs.SlurpDirectory: syncSlurpy :: (Slurpy -> IO Slurpy) -> Slurpy -> IO Slurpy
- Darcs.SlurpDirectory: type FileContents = ByteString
- Darcs.SlurpDirectory: undefined_size :: FileOffset
- Darcs.SlurpDirectory: undefined_time :: EpochTime
- Darcs.SlurpDirectory: withSlurpy :: Slurpy -> SlurpMonad a -> Either String (Slurpy, a)
- Darcs.SlurpDirectory: writeSlurpy :: Slurpy -> FilePath -> IO ()
- Darcs.SlurpDirectory: write_files :: Slurpy -> [FilePath] -> IO ()
- Darcs.SlurpDirectory.Internal: SlurpDir :: (Maybe String) -> SlurpyMap -> SlurpyContents
- Darcs.SlurpDirectory.Internal: SlurpFile :: (Maybe String, EpochTime, FileOffset) -> FileContents -> SlurpyContents
- Darcs.SlurpDirectory.Internal: Slurpy :: !FileName -> !SlurpyContents -> Slurpy
- Darcs.SlurpDirectory.Internal: co_slurp :: Slurpy -> FilePath -> IO Slurpy
- Darcs.SlurpDirectory.Internal: data SlurpMonad a
- Darcs.SlurpDirectory.Internal: data Slurpy
- Darcs.SlurpDirectory.Internal: data SlurpyContents
- Darcs.SlurpDirectory.Internal: doesDirectoryReallyExist :: FilePath -> IO Bool
- Darcs.SlurpDirectory.Internal: doesFileReallyExist :: FilePath -> IO Bool
- Darcs.SlurpDirectory.Internal: empty_slurpy :: Slurpy
- Darcs.SlurpDirectory.Internal: get_dircontents :: Slurpy -> [Slurpy]
- Darcs.SlurpDirectory.Internal: get_filecontents :: Slurpy -> FileContents
- Darcs.SlurpDirectory.Internal: get_length :: Slurpy -> FileOffset
- Darcs.SlurpDirectory.Internal: get_mtime :: Slurpy -> EpochTime
- Darcs.SlurpDirectory.Internal: get_path_list :: Slurpy -> FilePath -> [FilePath]
- Darcs.SlurpDirectory.Internal: get_slurp :: FileName -> Slurpy -> Maybe Slurpy
- Darcs.SlurpDirectory.Internal: instance Eq Slurpy
- Darcs.SlurpDirectory.Internal: instance Functor SlurpMonad
- Darcs.SlurpDirectory.Internal: instance Monad SlurpMonad
- Darcs.SlurpDirectory.Internal: instance MonadPlus SlurpMonad
- Darcs.SlurpDirectory.Internal: instance Ord Slurpy
- Darcs.SlurpDirectory.Internal: instance ReadableDirectory SlurpMonad
- Darcs.SlurpDirectory.Internal: instance Show Slurpy
- Darcs.SlurpDirectory.Internal: instance WriteableDirectory SlurpMonad
- Darcs.SlurpDirectory.Internal: isFileReallySymlink :: FilePath -> IO Bool
- Darcs.SlurpDirectory.Internal: is_dir :: Slurpy -> Bool
- Darcs.SlurpDirectory.Internal: is_file :: Slurpy -> Bool
- Darcs.SlurpDirectory.Internal: list_slurpy :: Slurpy -> [FilePath]
- Darcs.SlurpDirectory.Internal: list_slurpy_dirs :: Slurpy -> [FilePath]
- Darcs.SlurpDirectory.Internal: list_slurpy_files :: Slurpy -> [FilePath]
- Darcs.SlurpDirectory.Internal: map_to_slurpies :: SlurpyMap -> [Slurpy]
- Darcs.SlurpDirectory.Internal: mmap_slurp :: FilePath -> IO Slurpy
- Darcs.SlurpDirectory.Internal: slurp :: (FilePathLike p) => p -> IO Slurpy
- Darcs.SlurpDirectory.Internal: slurp_has :: FilePath -> Slurpy -> Bool
- Darcs.SlurpDirectory.Internal: slurp_has_anycase :: FilePath -> Slurpy -> Bool
- Darcs.SlurpDirectory.Internal: slurp_hasdir :: FileName -> Slurpy -> Bool
- Darcs.SlurpDirectory.Internal: slurp_hasfile :: FileName -> Slurpy -> Bool
- Darcs.SlurpDirectory.Internal: slurp_modfile :: FileName -> (FileContents -> Maybe FileContents) -> Slurpy -> Maybe Slurpy
- Darcs.SlurpDirectory.Internal: slurp_name :: Slurpy -> FilePath
- Darcs.SlurpDirectory.Internal: slurp_remove :: FileName -> Slurpy -> Maybe Slurpy
- Darcs.SlurpDirectory.Internal: slurp_removedir :: FileName -> Slurpy -> Maybe Slurpy
- Darcs.SlurpDirectory.Internal: slurp_removefile :: FileName -> Slurpy -> Maybe Slurpy
- Darcs.SlurpDirectory.Internal: slurp_unboring :: (FilePath -> Bool) -> FilePath -> IO Slurpy
- Darcs.SlurpDirectory.Internal: slurpies_to_map :: [Slurpy] -> SlurpyMap
- Darcs.SlurpDirectory.Internal: syncSlurpy :: (Slurpy -> IO Slurpy) -> Slurpy -> IO Slurpy
- Darcs.SlurpDirectory.Internal: type FileContents = ByteString
- Darcs.SlurpDirectory.Internal: undefined_size :: FileOffset
- Darcs.SlurpDirectory.Internal: undefined_time :: EpochTime
- Darcs.SlurpDirectory.Internal: withSlurpy :: Slurpy -> SlurpMonad a -> Either String (Slurpy, a)
- Darcs.SlurpDirectory.Internal: writeSlurpy :: Slurpy -> FilePath -> IO ()
- Darcs.SlurpDirectory.Internal: write_files :: Slurpy -> [FilePath] -> IO ()
- Darcs.Test: get_test :: [DarcsFlag] -> IO (IO ExitCode)
- Darcs.Test: run_posthook :: [DarcsFlag] -> AbsolutePath -> IO ExitCode
- Darcs.Test: run_prehook :: [DarcsFlag] -> AbsolutePath -> IO ExitCode
- Darcs.URL: is_absolute :: String -> Bool
- Darcs.URL: is_file :: String -> Bool
- Darcs.URL: is_relative :: String -> Bool
- Darcs.URL: is_ssh :: String -> Bool
- Darcs.URL: is_ssh_nopath :: String -> Bool
- Darcs.URL: is_url :: String -> Bool
- Darcs.Utils: add_to_error_loc :: Exception -> String -> Exception
- Darcs.Utils: edit_file :: (FilePathLike p) => p -> IO ExitCode
- Darcs.Utils: get_viewer :: IO String
- Darcs.Utils: isHardwareFaultError :: IOError -> Bool
- Darcs.Utils: isUnsupportedOperationError :: IOError -> Bool
- Darcs.Utils: promptCharFancy :: String -> [Char] -> Maybe Char -> [Char] -> IO Char
- Darcs.Utils: run_editor :: FilePath -> IO ExitCode
- Darcs.Witnesses.Ordered: unsafeFL :: [a] -> FL a
- Darcs.Witnesses.Ordered: unsafeRL :: [a] -> RL a
- Darcs.Witnesses.Ordered: unsafeUnFL :: FL a -> [a]
- Darcs.Witnesses.Ordered: unsafeUnRL :: RL a -> [a]
- Darcs.Witnesses.Sealed: unsafeUnflippedseal :: FlippedSeal a -> a
- Darcs.Witnesses.Show: app_prec :: Int
- Exec: exec_interactive :: String -> String -> IO ExitCode
- HTTP: request_url :: String -> FilePath -> a -> IO String
- HTTP: wait_next_url :: IO (String, String)
- IsoDate: iso8601_duration :: CharParser a TimeDiff
- IsoDate: iso8601_interval :: Int -> CharParser a (Either TimeDiff (MCalendarTime, MCalendarTime))
- Printer: insert_before_lastline :: Doc -> Doc -> Doc
- Printer: wrap_text :: Int -> String -> Doc
- URL: pipeliningEnabledByDefault :: Bool
- URL: setHTTPPipelining :: Bool -> IO ()
+ ByteStringUtils: breakAfterNthNewline :: Int -> ByteString -> Maybe (ByteString, ByteString)
+ ByteStringUtils: breakBeforeNthNewline :: Int -> ByteString -> (ByteString, ByteString)
+ ByteStringUtils: decodeLocale :: ByteString -> String
+ ByteStringUtils: decodeString :: String -> String
+ ByteStringUtils: encodeLatin1 :: String -> ByteString
+ ByteStringUtils: encodeLocale :: String -> ByteString
+ ByteStringUtils: gzReadStdin :: IO ByteString
+ ByteStringUtils: isFunky :: ByteString -> Bool
+ ByteStringUtils: packStringToUTF8 :: String -> ByteString
+ ByteStringUtils: unpackPSFromUTF8 :: ByteString -> String
+ ByteStringUtils: utf8ToLocale :: ByteString -> ByteString
+ Darcs.ArgumentDefaults: getDefaultFlags :: String -> [DarcsOption] -> [DarcsFlag] -> IO [DarcsFlag]
+ Darcs.Arguments: Bisect :: DarcsFlag
+ Darcs.Arguments: DarcsMutuallyExclusive :: [DarcsAtomicOption] -> ([DarcsFlag] -> [DarcsFlag]) -> DarcsOption
+ Darcs.Arguments: DarcsSingleOption :: DarcsAtomicOption -> DarcsOption
+ Darcs.Arguments: KeepDate :: DarcsFlag
+ Darcs.Arguments: NoKeepDate :: DarcsFlag
+ Darcs.Arguments: OptimizeHTTP :: DarcsFlag
+ Darcs.Arguments: applyDefaults :: [DarcsOption] -> [DarcsFlag] -> [DarcsFlag]
+ Darcs.Arguments: atomicOptions :: DarcsOption -> [DarcsAtomicOption]
+ Darcs.Arguments: bisect :: DarcsOption
+ Darcs.Arguments: data DarcsAtomicOption
+ Darcs.Arguments: fixUrlFlag :: [DarcsFlag] -> DarcsFlag -> IO DarcsFlag
+ Darcs.Arguments: getOutput :: [DarcsFlag] -> FilePath -> Maybe AbsolutePathOrStd
+ Darcs.Arguments: keepDate :: DarcsOption
+ Darcs.Arguments: nubOptions :: [DarcsOption] -> [DarcsFlag] -> [DarcsFlag]
+ Darcs.Arguments: optimizeHTTP :: DarcsOption
+ Darcs.Arguments: optionFromDarcsOption :: AbsolutePath -> DarcsOption -> [OptDescr DarcsFlag]
+ Darcs.Commands: usageHelper :: [CommandControl] -> String
+ Darcs.Commands.Pull: fetch :: DarcsCommand
+ Darcs.Commands.Record: askAboutDepends :: (RepoPatch p) => Repository p -> FL Prim -> [DarcsFlag] -> [PatchInfo] -> IO [PatchInfo]
+ Darcs.Commands.TrackDown: instance Show BisectDir
+ Darcs.Commands.WhatsNew: announceFiles :: (RepoPatch p) => Repository p -> [SubPath] -> String -> IO [SubPath]
+ Darcs.CommandsAux: checkPaths :: (Patchy p) => [DarcsFlag] -> FL p -> IO ()
+ Darcs.CommandsAux: hasMaliciousPath :: (Patchy p) => p -> Bool
+ Darcs.CommandsAux: maliciousPatches :: (Patchy p) => [Sealed2 p] -> [Sealed2 p]
+ Darcs.Compat: atomicCreate :: FilePath -> IO ()
+ Darcs.Compat: mkStdoutTemp :: String -> IO String
+ Darcs.Compat: sloppyAtomicCreate :: FilePath -> IO ()
+ Darcs.Compat: stdoutIsAPipe :: IO Bool
+ Darcs.Email: makeEmail :: String -> [(String, String)] -> (Maybe Doc) -> Doc -> (Maybe String) -> Doc
+ Darcs.Email: readEmail :: ByteString -> ByteString
+ Darcs.External: darcsProgram :: IO String
+ Darcs.External: diffProgram :: IO String
+ Darcs.External: fetchFileLazyPS :: String -> Cachable -> IO ByteString
+ Darcs.External: sendmailPath :: IO String
+ Darcs.Flags: Bisect :: DarcsFlag
+ Darcs.Flags: KeepDate :: DarcsFlag
+ Darcs.Flags: NoKeepDate :: DarcsFlag
+ Darcs.Flags: OptimizeHTTP :: DarcsFlag
+ Darcs.Flags: defaultFlag :: [DarcsFlag] -> DarcsFlag -> [DarcsFlag] -> [DarcsFlag]
+ Darcs.Flags: wantExternalMerge :: [DarcsFlag] -> Maybe String
+ Darcs.Global: withAtexit :: IO a -> IO a
+ Darcs.Hopefully: compareWPatchInfo :: WPatchInfo -> WPatchInfo -> EqCheck
+ Darcs.Hopefully: data WPatchInfo
+ Darcs.Hopefully: instance MyEq WPatchInfo
+ Darcs.Hopefully: patchDesc :: PatchInfoAnd p -> String
+ Darcs.Hopefully: unWPatchInfo :: WPatchInfo -> PatchInfo
+ Darcs.Hopefully: winfo :: PatchInfoAnd p -> WPatchInfo
+ Darcs.IO: instance (Functor m, MonadPlus m, MonadError e m) => ReadableDirectory (TreeMonad m)
+ Darcs.Lock: readLocaleFile :: (FilePathLike p) => p -> IO String
+ Darcs.Lock: rmRecursive :: FilePath -> IO ()
+ Darcs.Lock: tempdirLoc :: IO FilePath
+ Darcs.Lock: worldReadableTemp :: String -> IO String
+ Darcs.Lock: writeLocaleFile :: (FilePathLike p) => p -> String -> IO ()
+ Darcs.Patch: commuteFLorComplain :: (Commute p) => (p :> FL p) -> Either (Sealed2 p) ((FL p :> p))
+ Darcs.Patch: isInconsistent :: (Conflict p) => p -> Maybe Doc
+ Darcs.Patch.Bundle: contextPatches :: (RepoPatch p) => PatchSet p -> FlippedSeal (RL (PatchInfoAnd p))
+ Darcs.Patch.Bundle: hashBundle :: (RepoPatch p) => [PatchInfo] -> FL (Named p) -> String
+ Darcs.Patch.Bundle: makeBundle :: (RepoPatch p) => Maybe (Tree IO) -> RL (PatchInfoAnd p) -> FL (Named p) -> IO Doc
+ Darcs.Patch.Bundle: makeBundle2 :: (RepoPatch p) => Maybe (Tree IO) -> RL (PatchInfoAnd p) -> FL (Named p) -> FL (Named p) -> IO Doc
+ Darcs.Patch.Bundle: makeBundleN :: (RepoPatch p) => Maybe (Tree IO) -> PatchSet p -> FL (Named p) -> IO Doc
+ Darcs.Patch.Bundle: patchFilename :: String -> String
+ Darcs.Patch.Bundle: scanBundle :: (RepoPatch p) => ByteString -> Either String (SealedPatchSet p)
+ Darcs.Patch.Bundle: scanContext :: (RepoPatch p) => ByteString -> PatchSet p
+ Darcs.Patch.Choices: makeEverythingSooner :: (Patchy p) => PatchChoices p -> PatchChoices p
+ Darcs.Patch.Choices: patchSlot' :: (Patchy p) => TaggedPatch p -> State (PatchChoices p) Slot
+ Darcs.Patch.Choices: refineChoices :: (Patchy p, Monad m, Functor m) => (FL (TaggedPatch p) -> PatchChoices p -> m (PatchChoices p)) -> PatchChoices p -> m (PatchChoices p)
+ Darcs.Patch.Commute: mangleUnravelled :: [Sealed (FL Prim)] -> Sealed (FL Prim)
+ Darcs.Patch.Commute: publicUnravel :: Patch -> [Sealed (FL Prim)]
+ Darcs.Patch.Core: joinPatchesFL :: FL Patch -> Patch
+ Darcs.Patch.Core: mergerUndo :: Patch -> Patch
+ Darcs.Patch.Core: nFn :: FilePath -> FilePath
+ Darcs.Patch.Depends: areUnrelatedRepos :: (RepoPatch p) => PatchSet p -> PatchSet p -> Bool
+ Darcs.Patch.Depends: commuteToEnd :: (RepoPatch p) => RL (PatchInfoAnd p) -> PatchSet p -> (PatchSet p :>> RL (PatchInfoAnd p))
+ Darcs.Patch.Depends: countUsThem :: (RepoPatch p) => PatchSet p -> PatchSet p -> (Int, Int)
+ Darcs.Patch.Depends: deepOptimizePatchset :: PatchSet p -> PatchSet p
+ Darcs.Patch.Depends: findCommonWithThem :: (RepoPatch p) => PatchSet p -> PatchSet p -> (PatchSet p :>> FL (PatchInfoAnd p))
+ Darcs.Patch.Depends: findUncommon :: (RepoPatch p) => PatchSet p -> PatchSet p -> (FL (PatchInfoAnd p) :\/: FL (PatchInfoAnd p))
+ Darcs.Patch.Depends: getPatchesBeyondTag :: (RepoPatch p) => PatchInfo -> PatchSet p -> FlippedSeal (RL (PatchInfoAnd p))
+ Darcs.Patch.Depends: getPatchesInTag :: (RepoPatch p) => PatchInfo -> PatchSet p -> SealedPatchSet p
+ Darcs.Patch.Depends: getTagsRight :: PatchSet p -> [PatchInfo]
+ Darcs.Patch.Depends: merge2FL :: (RepoPatch p) => FL (PatchInfoAnd p) -> FL (PatchInfoAnd p) -> Sealed (FL (PatchInfoAnd p))
+ Darcs.Patch.Depends: mergeThem :: (RepoPatch p) => PatchSet p -> PatchSet p -> Sealed (FL (PatchInfoAnd p))
+ Darcs.Patch.Depends: newsetIntersection :: (RepoPatch p) => [SealedPatchSet p] -> SealedPatchSet p
+ Darcs.Patch.Depends: newsetUnion :: (RepoPatch p) => [SealedPatchSet p] -> SealedPatchSet p
+ Darcs.Patch.Depends: optimizePatchset :: PatchSet p -> PatchSet p
+ Darcs.Patch.Depends: removeFromPatchSet :: (RepoPatch p) => FL (PatchInfoAnd p) -> PatchSet p -> Maybe (PatchSet p)
+ Darcs.Patch.Depends: slightlyOptimizePatchset :: PatchSet p -> PatchSet p
+ Darcs.Patch.Depends: splitOnTag :: (RepoPatch p) => PatchInfo -> PatchSet p -> (PatchSet p :>> RL (PatchInfoAnd p))
+ Darcs.Patch.FileName: breakOnDir :: FileName -> Maybe (FileName, FileName)
+ Darcs.Patch.FileName: decodeWhite :: String -> FilePath
+ Darcs.Patch.FileName: encodeWhite :: FilePath -> String
+ Darcs.Patch.FileName: normPath :: FileName -> FileName
+ Darcs.Patch.FileName: ownName :: FileName -> FileName
+ Darcs.Patch.Info: PatchInfo :: !ByteString -> !ByteString -> !ByteString -> ![ByteString] -> !Bool -> PatchInfo
+ Darcs.Patch.Info: _piAuthor :: PatchInfo -> !ByteString
+ Darcs.Patch.Info: _piDate :: PatchInfo -> !ByteString
+ Darcs.Patch.Info: _piLog :: PatchInfo -> ![ByteString]
+ Darcs.Patch.Info: _piName :: PatchInfo -> !ByteString
+ Darcs.Patch.Info: addJunk :: PatchInfo -> IO PatchInfo
+ Darcs.Patch.Info: humanFriendly :: PatchInfo -> Doc
+ Darcs.Patch.Info: invertName :: PatchInfo -> PatchInfo
+ Darcs.Patch.Info: isInverted :: PatchInfo -> !Bool
+ Darcs.Patch.Info: isTag :: PatchInfo -> Bool
+ Darcs.Patch.Info: justAuthor :: PatchInfo -> String
+ Darcs.Patch.Info: justLog :: PatchInfo -> String
+ Darcs.Patch.Info: justName :: PatchInfo -> String
+ Darcs.Patch.Info: makeAltFilename :: PatchInfo -> String
+ Darcs.Patch.Info: makeFilename :: PatchInfo -> String
+ Darcs.Patch.Info: piAuthor :: PatchInfo -> String
+ Darcs.Patch.Info: piDate :: PatchInfo -> CalendarTime
+ Darcs.Patch.Info: piDateBytestring :: PatchInfo -> ByteString
+ Darcs.Patch.Info: piDateString :: PatchInfo -> String
+ Darcs.Patch.Info: piLog :: PatchInfo -> [String]
+ Darcs.Patch.Info: piName :: PatchInfo -> String
+ Darcs.Patch.Info: piRename :: PatchInfo -> String -> PatchInfo
+ Darcs.Patch.Info: piTag :: PatchInfo -> Maybe String
+ Darcs.Patch.Info: setPiDate :: String -> PatchInfo -> PatchInfo
+ Darcs.Patch.Info: toXml :: PatchInfo -> Doc
+ Darcs.Patch.Match: applyMatcher :: Matcher p -> PatchInfoAnd p -> Bool
+ Darcs.Patch.Match: makeMatcher :: String -> (Sealed2 (PatchInfoAnd p) -> Bool) -> Matcher p
+ Darcs.Patch.Match: matchParser :: (Patchy p) => CharParser st (MatchFun p)
+ Darcs.Patch.Match: matchPattern :: (Patchy p) => PatchMatch -> Matcher p
+ Darcs.Patch.Match: patchMatch :: String -> PatchMatch
+ Darcs.Patch.MatchData: patchMatch :: String -> PatchMatch
+ Darcs.Patch.Non: propAdjustTwice :: (Patchy p, ToFromPrim p) => p -> Non p -> Maybe Doc
+ Darcs.Patch.Patchy: commuteFLorComplain :: (Commute p) => (p :> FL p) -> Either (Sealed2 p) ((FL p :> p))
+ Darcs.Patch.Permutations: headPermutationsRL :: (Commute p) => RL p -> [RL p]
+ Darcs.Patch.Permutations: removeSubsequenceFL :: (MyEq p, Commute p) => FL p -> FL p -> Maybe (FL p)
+ Darcs.Patch.Permutations: removeSubsequenceRL :: (MyEq p, Commute p) => RL p -> RL p -> Maybe (RL p)
+ Darcs.Patch.Permutations: simpleHeadPermutationsFL :: (Commute p) => FL p -> [FL p]
+ Darcs.Patch.Prim: commuteNoConflicts :: (Conflict p) => (p :> p) -> Maybe ((p :> p))
+ Darcs.Patch.Prim: commuteNoConflictsFL :: (Conflict p) => (p :> FL p) -> Maybe ((FL p :> p))
+ Darcs.Patch.Prim: commuteNoConflictsRL :: (Conflict p) => (RL p :> p) -> Maybe ((p :> RL p))
+ Darcs.Patch.Prim: isIdentity :: Prim -> EqCheck
+ Darcs.Patch.Prim: isInconsistent :: (Conflict p) => p -> Maybe Doc
+ Darcs.Patch.Prim: nFn :: FilePath -> FilePath
+ Darcs.Patch.Prim: showHunk :: FileNameFormat -> FileName -> Int -> [ByteString] -> [ByteString] -> Doc
+ Darcs.Patch.Prim: showPrimFL :: FileNameFormat -> FL Prim -> Doc
+ Darcs.Patch.Prim: tryShrinkingInverse :: FL Prim -> Maybe (FL Prim)
+ Darcs.Patch.Prim: tryTokInternal :: String -> ByteString -> ByteString -> ByteString -> Maybe [ByteString]
+ Darcs.Patch.Properties: commuteInverses :: (Patchy p) => ((p :> p) -> Maybe ((p :> p))) -> (p :> p) -> Maybe Doc
+ Darcs.Patch.Properties: identityCommutes :: (Patchy p) => p -> Maybe Doc
+ Darcs.Patch.Properties: inverseDoesntCommute :: (Patchy p) => p -> Maybe Doc
+ Darcs.Patch.Properties: joinCommute :: ((Prim :> Prim) -> Maybe (Prim)) -> (Prim :> (Prim :> Prim)) -> Maybe Doc
+ Darcs.Patch.Properties: joinInverses :: ((Prim :> Prim) -> Maybe (Prim)) -> Prim -> Maybe Doc
+ Darcs.Patch.Properties: mergeArgumentsConsistent :: (Patchy p) => (p -> Maybe Doc) -> (p :\/: p) -> Maybe Doc
+ Darcs.Patch.Properties: mergeCommute :: (Patchy p) => (p :\/: p) -> Maybe Doc
+ Darcs.Patch.Properties: mergeConsistent :: (Patchy p) => (p -> Maybe Doc) -> (p :\/: p) -> Maybe Doc
+ Darcs.Patch.Properties: mergeEitherWay :: (Patchy p) => (p :\/: p) -> Maybe Doc
+ Darcs.Patch.Properties: partialPermutivity :: (Patchy p) => ((p :> p) -> Maybe ((p :> p))) -> (p :> (p :> p)) -> Maybe Doc
+ Darcs.Patch.Properties: patchAndInverseCommute :: (Patchy p) => ((p :> p) -> Maybe ((p :> p))) -> (p :> p) -> Maybe Doc
+ Darcs.Patch.ReadMonads: alterInput :: (ParserM m) => (ByteString -> ByteString) -> m ()
+ Darcs.Patch.ReadMonads: lexChar :: (ParserM m) => Char -> m ()
+ Darcs.Patch.ReadMonads: lexEof :: (ParserM m) => m ()
+ Darcs.Patch.ReadMonads: lexString :: (ParserM m) => String -> m ()
+ Darcs.Patch.ReadMonads: lexStrings :: (ParserM m) => [String] -> m String
+ Darcs.Patch.ReadMonads: maybeWork :: (ParserM m) => (ByteString -> Maybe (a, ByteString)) -> m (Maybe a)
+ Darcs.Patch.ReadMonads: myLex :: ByteString -> Maybe (ByteString, ByteString)
+ Darcs.Patch.ReadMonads: parseStrictly :: SM a -> ByteString -> Maybe (a, ByteString)
+ Darcs.Patch.ReadMonads: peekInput :: (ParserM m) => m ByteString
+ Darcs.Patch.Real: isConsistent :: RealPatch -> Maybe Doc
+ Darcs.Patch.Real: isDuplicate :: RealPatch -> Bool
+ Darcs.Patch.Real: isForward :: RealPatch -> Maybe Doc
+ Darcs.Patch.Set: PatchSet :: RL (PatchInfoAnd p) -> RL (Tagged p) -> PatchSet p
+ Darcs.Patch.Set: Tagged :: PatchInfoAnd p -> Maybe String -> RL (PatchInfoAnd p) -> Tagged p
+ Darcs.Patch.Set: data Origin
+ Darcs.Patch.Set: data PatchSet p
+ Darcs.Patch.Set: data Tagged p
+ Darcs.Patch.Set: newset2FL :: PatchSet p -> FL (PatchInfoAnd p)
+ Darcs.Patch.Set: newset2RL :: PatchSet p -> RL (PatchInfoAnd p)
+ Darcs.Patch.Set: progressPatchSet :: String -> PatchSet p -> PatchSet p
+ Darcs.Patch.Set: tags :: PatchSet p -> [PatchInfo]
+ Darcs.Patch.Show: instance Show2 Patch
+ Darcs.Patch.TouchesFiles: choosePreTouching :: (Patchy p) => [FilePath] -> FL p -> Sealed (FL p)
+ Darcs.Patch.TouchesFiles: chooseTouching :: (Patchy p) => [FilePath] -> FL p -> Sealed (FL p)
+ Darcs.Patch.TouchesFiles: deselectNotTouching :: (Patchy p) => [FilePath] -> PatchChoices p -> PatchChoices p
+ Darcs.Patch.TouchesFiles: lookTouch :: (Patchy p) => [FilePath] -> p -> (Bool, [FilePath])
+ Darcs.Patch.TouchesFiles: selectNotTouching :: (Patchy p) => [FilePath] -> PatchChoices p -> PatchChoices p
+ Darcs.Patch.TouchesFiles: selectTouching :: (Patchy p) => [FilePath] -> PatchChoices p -> PatchChoices p
+ Darcs.Population: lookupCreationPop :: PatchInfo -> FilePath -> Population -> Maybe Population
+ Darcs.Population: lookupPop :: FilePath -> Population -> Maybe Population
+ Darcs.Population: modifiedToXml :: Info -> Doc
+ Darcs.RemoteApply: applyAs :: [DarcsFlag] -> Maybe String
+ Darcs.RemoteApply: remoteApply :: [DarcsFlag] -> String -> Doc -> IO ExitCode
+ Darcs.RepoPath: stdOut :: AbsolutePathOrStd
+ Darcs.Repository: Ca :: [CacheLoc] -> Cache
+ Darcs.Repository: Cache :: !CacheType -> !WritableOrNot -> !String -> CacheLoc
+ Darcs.Repository: HashedInventoriesDir :: HashedDir
+ Darcs.Repository: HashedPatchesDir :: HashedDir
+ Darcs.Repository: HashedPristineDir :: HashedDir
+ Darcs.Repository: NotWritable :: WritableOrNot
+ Darcs.Repository: Writable :: WritableOrNot
+ Darcs.Repository: addToPending :: (RepoPatch p) => Repository p -> FL Prim -> IO ()
+ Darcs.Repository: copyOldrepoPatches :: (RepoPatch p) => [DarcsFlag] -> Repository p -> FilePath -> IO ()
+ Darcs.Repository: data CacheLoc
+ Darcs.Repository: data HashedDir
+ Darcs.Repository: data PatchSet p
+ Darcs.Repository: data WritableOrNot
+ Darcs.Repository: extractOptions :: Repository p -> [DarcsFlag]
+ Darcs.Repository: modifyCache :: (RepoPatch p) => Repository p -> (Cache -> Cache) -> Repository p
+ Darcs.Repository: newtype Cache
+ Darcs.Repository: readRepo :: (RepoPatch p) => Repository p -> IO (PatchSet p)
+ Darcs.Repository: readTentativeRepo :: (RepoPatch p) => Repository p -> IO (PatchSet p)
+ Darcs.Repository.ApplyPatches: applyPatches :: (Patchy p) => [DarcsFlag] -> FL (PatchInfoAnd p) -> IO ()
+ Darcs.Repository.ApplyPatches: applyPatchesWithFeedback :: (Patchy p) => [DarcsFlag] -> String -> FL (PatchInfoAnd p) -> IO ()
+ Darcs.Repository.Cache: compareByLocality :: CacheLoc -> CacheLoc -> Ordering
+ Darcs.Repository.Cache: speculateFilesUsingCache :: Cache -> HashedDir -> [String] -> IO ()
+ Darcs.Repository.Cache: unionRemoteCaches :: Cache -> Cache -> String -> IO (Cache)
+ Darcs.Repository.Checkpoint: getCheckpoint :: (RepoPatch p) => Repository p -> IO (Maybe (Sealed (Named p)))
+ Darcs.Repository.Checkpoint: getCheckpointByDefault :: (RepoPatch p) => Repository p -> IO (Maybe (Sealed (Named p)))
+ Darcs.Repository.Checkpoint: identifyCheckpoint :: (RepoPatch p) => Repository p -> IO (Maybe PatchInfo)
+ Darcs.Repository.Checkpoint: writeCheckpointPatch :: (RepoPatch p) => Named p -> IO ()
+ Darcs.Repository.DarcsRepo: addToInventory :: FilePath -> [PatchInfo] -> IO ()
+ Darcs.Repository.DarcsRepo: addToTentativeInventory :: (RepoPatch p) => [DarcsFlag] -> Named p -> IO FilePath
+ Darcs.Repository.DarcsRepo: addToTentativePristine :: (Effect p) => p -> IO ()
+ Darcs.Repository.DarcsRepo: copyPatches :: [DarcsFlag] -> FilePath -> FilePath -> [PatchInfo] -> IO ()
+ Darcs.Repository.DarcsRepo: finalizePristineChanges :: IO ()
+ Darcs.Repository.DarcsRepo: finalizeTentativeChanges :: IO ()
+ Darcs.Repository.DarcsRepo: readCheckpoints :: String -> IO [PatchInfo]
+ Darcs.Repository.DarcsRepo: readRepo :: (RepoPatch p) => [DarcsFlag] -> String -> IO (SealedPatchSet p)
+ Darcs.Repository.DarcsRepo: readTentativeRepo :: (RepoPatch p) => [DarcsFlag] -> String -> IO (SealedPatchSet p)
+ Darcs.Repository.DarcsRepo: removeFromTentativeInventory :: (RepoPatch p) => Bool -> [DarcsFlag] -> FL (PatchInfoAnd p) -> IO ()
+ Darcs.Repository.DarcsRepo: revertTentativeChanges :: IO ()
+ Darcs.Repository.DarcsRepo: writeAndReadPatch :: (RepoPatch p) => [DarcsFlag] -> PatchInfoAnd p -> IO (PatchInfoAnd p)
+ Darcs.Repository.DarcsRepo: writeInventory :: (RepoPatch p) => FilePath -> PatchSet p -> IO ()
+ Darcs.Repository.DarcsRepo: writeInventoryAndPatches :: (RepoPatch p) => [DarcsFlag] -> PatchSet p -> IO ()
+ Darcs.Repository.HashedIO: cleanHashdir :: Cache -> HashedDir -> [String] -> IO ()
+ Darcs.Repository.HashedRepo: addToTentativeInventory :: (RepoPatch p) => Cache -> Compression -> PatchInfoAnd p -> IO FilePath
+ Darcs.Repository.HashedRepo: applyToTentativePristine :: (Patchy q) => [DarcsFlag] -> q -> IO ()
+ Darcs.Repository.HashedRepo: cleanPristine :: Repository p -> IO ()
+ Darcs.Repository.HashedRepo: copyPartialsPristine :: (FilePathLike fp) => Cache -> Compression -> String -> String -> [fp] -> IO ()
+ Darcs.Repository.HashedRepo: copyPristine :: Cache -> Compression -> String -> String -> IO ()
+ Darcs.Repository.HashedRepo: copyRepo :: (RepoPatch p) => Repository p -> [DarcsFlag] -> String -> IO ()
+ Darcs.Repository.HashedRepo: copySources :: (RepoPatch p) => Repository p -> String -> IO ()
+ Darcs.Repository.HashedRepo: finalizeTentativeChanges :: (RepoPatch p) => Repository p -> Compression -> IO ()
+ Darcs.Repository.HashedRepo: readRepo :: (RepoPatch p) => Repository p -> String -> IO (PatchSet p)
+ Darcs.Repository.HashedRepo: readTentativeRepo :: (RepoPatch p) => Repository p -> String -> IO (PatchSet p)
+ Darcs.Repository.HashedRepo: removeFromTentativeInventory :: (RepoPatch p) => Repository p -> Compression -> FL (PatchInfoAnd p) -> IO ()
+ Darcs.Repository.HashedRepo: revertTentativeChanges :: IO ()
+ Darcs.Repository.HashedRepo: writeAndReadPatch :: (RepoPatch p) => Cache -> Compression -> PatchInfoAnd p -> IO (PatchInfoAnd p)
+ Darcs.Repository.HashedRepo: writeTentativeInventory :: (RepoPatch p) => Cache -> Compression -> PatchSet p -> IO ()
+ Darcs.Repository.Internal: BadRepository :: String -> IdentifyRepo p
+ Darcs.Repository.Internal: GoodRepository :: (Repository p) -> IdentifyRepo p
+ Darcs.Repository.Internal: NonRepository :: String -> IdentifyRepo p
+ Darcs.Repository.Internal: announceMergeConflicts :: String -> [DarcsFlag] -> FL Prim -> IO Bool
+ Darcs.Repository.Internal: checkUnrecordedConflicts :: (RepoPatch p) => [DarcsFlag] -> FL (Named p) -> IO Bool
+ Darcs.Repository.Internal: data IdentifyRepo p
+ Darcs.Repository.Internal: data PatchSet p
+ Darcs.Repository.Internal: makeNewPending :: (RepoPatch p) => Repository p -> FL Prim -> IO ()
+ Darcs.Repository.Internal: readRepo :: (RepoPatch p) => Repository p -> IO (PatchSet p)
+ Darcs.Repository.Internal: readTentativeRepo :: (RepoPatch p) => Repository p -> IO (PatchSet p)
+ Darcs.Repository.InternalTypes: extractOptions :: Repository p -> [DarcsFlag]
+ Darcs.Repository.InternalTypes: modifyCache :: (RepoPatch p) => Repository p -> (Cache -> Cache) -> Repository p
+ Darcs.Repository.LowLevel: readPending :: (RepoPatch p) => Repository p -> IO (Sealed (FL Prim))
+ Darcs.Repository.LowLevel: readPendingfile :: String -> IO (Sealed (FL Prim))
+ Darcs.Repository.Motd: getMotd :: String -> IO ByteString
+ Darcs.Repository.Motd: showMotd :: [DarcsFlag] -> String -> IO ()
+ Darcs.Repository.Prefs: globalCacheDir :: IO (Maybe FilePath)
+ Darcs.Repository.State: TreeFilter :: (forall tr. (FilterTree tr m) => tr m -> tr m) -> TreeFilter m
+ Darcs.Repository.State: applyTreeFilter :: TreeFilter m -> forall tr. (FilterTree tr m) => tr m -> tr m
+ Darcs.Repository.State: newtype TreeFilter m
+ Darcs.Resolution: externalResolution :: (RepoPatch p) => Tree IO -> String -> FL Prim -> FL Prim -> p -> IO (Sealed (FL Prim))
+ Darcs.Resolution: patchsetConflictResolutions :: (RepoPatch p) => PatchSet p -> Sealed (FL Prim)
+ Darcs.Resolution: standardResolution :: (RepoPatch p) => p -> Sealed (FL Prim)
+ Darcs.RunCommand: runTheCommand :: [CommandControl] -> String -> [String] -> IO ()
+ Darcs.SelectChanges: First :: WhichChanges
+ Darcs.SelectChanges: FirstReversed :: WhichChanges
+ Darcs.SelectChanges: Last :: WhichChanges
+ Darcs.SelectChanges: LastReversed :: WhichChanges
+ Darcs.SelectChanges: data WhichChanges
+ Darcs.SelectChanges: runSelection :: (Patchy p) => PatchSelection p -> PatchSelectionContext p -> IO ((FL p :> FL p))
+ Darcs.SelectChanges: selectChanges :: (Patchy p) => WhichChanges -> FL p -> PatchSelection p
+ Darcs.SelectChanges: selectionContext :: (RepoPatch p) => String -> [DarcsFlag] -> Maybe (Splitter (PatchInfoAnd p)) -> [FilePath] -> PatchSelectionContext (PatchInfoAnd p)
+ Darcs.SelectChanges: selectionContextPrim :: String -> [DarcsFlag] -> Maybe (Splitter Prim) -> [FilePath] -> PatchSelectionContext Prim
+ Darcs.SelectChanges: viewChanges :: (Patchy p) => [DarcsFlag] -> [Sealed2 p] -> IO ()
+ Darcs.SelectChanges: withSelectedPatchFromRepo :: (RepoPatch p) => String -> Repository p -> [DarcsFlag] -> ((FL (PatchInfoAnd p) :> PatchInfoAnd p) -> IO ()) -> IO ()
+ Darcs.SignalHandler: instance Exception SignalException
+ Darcs.SignalHandler: instance Show SignalException
+ Darcs.SignalHandler: stdoutIsAPipe :: IO Bool
+ Darcs.Test: getTest :: [DarcsFlag] -> IO (IO ExitCode)
+ Darcs.Test: runPosthook :: [DarcsFlag] -> AbsolutePath -> IO ExitCode
+ Darcs.Test: runPrehook :: [DarcsFlag] -> AbsolutePath -> IO ExitCode
+ Darcs.URL: isAbsolute :: String -> Bool
+ Darcs.URL: isFile :: String -> Bool
+ Darcs.URL: isRelative :: String -> Bool
+ Darcs.URL: isSsh :: String -> Bool
+ Darcs.URL: isSshNopath :: String -> Bool
+ Darcs.URL: isUrl :: String -> Bool
+ Darcs.Utils: PromptConfig :: String -> [Char] -> [Char] -> Maybe Char -> [Char] -> PromptConfig
+ Darcs.Utils: addToErrorLoc :: IOException -> String -> IOException
+ Darcs.Utils: data PromptConfig
+ Darcs.Utils: doesDirectoryReallyExist :: FilePath -> IO Bool
+ Darcs.Utils: doesFileReallyExist :: FilePath -> IO Bool
+ Darcs.Utils: editFile :: (FilePathLike p) => p -> IO ExitCode
+ Darcs.Utils: getViewer :: IO String
+ Darcs.Utils: isFileReallySymlink :: FilePath -> IO Bool
+ Darcs.Utils: pAdvancedCharacters :: PromptConfig -> [Char]
+ Darcs.Utils: pBasicCharacters :: PromptConfig -> [Char]
+ Darcs.Utils: pDefault :: PromptConfig -> Maybe Char
+ Darcs.Utils: pHelp :: PromptConfig -> [Char]
+ Darcs.Utils: pPrompt :: PromptConfig -> String
+ Darcs.Utils: promptChar :: PromptConfig -> IO Char
+ Darcs.Utils: runEditor :: FilePath -> IO ExitCode
+ Darcs.Utils: treeHas :: (MonadError e m, Functor m, Monad m) => Tree m -> FilePath -> m Bool
+ Darcs.Utils: treeHasAnycase :: (MonadError e m, Functor m, Monad m) => Tree m -> FilePath -> m Bool
+ Darcs.Utils: treeHasDir :: (MonadError e m, Functor m, Monad m) => Tree m -> FilePath -> m Bool
+ Darcs.Utils: treeHasFile :: (MonadError e m, Functor m, Monad m) => Tree m -> FilePath -> m Bool
+ Darcs.Witnesses.Ordered: (:>>) :: (a1) -> (a2) -> :>> a1 a2
+ Darcs.Witnesses.Ordered: anyFL :: (a -> Bool) -> FL a -> Bool
+ Darcs.Witnesses.Ordered: filterFLFL :: (p -> EqCheck) -> FL p -> FL p
+ Darcs.Witnesses.Ordered: filterRL :: (p -> Bool) -> RL p -> [Sealed2 p]
+ Darcs.Witnesses.Ordered: instance (MyEq a) => Eq (Sealed a)
+ Darcs.Witnesses.Ordered: instance (MyEq a, MyEq b) => MyEq (a :< b)
+ Darcs.Witnesses.Ordered: instance (MyEq a, MyEq b) => MyEq (a :> b)
+ Darcs.Witnesses.Ordered: spanFL_M :: (Monad m) => (a -> m Bool) -> FL a -> m ((FL a :> FL a))
+ Darcs.Witnesses.Ordered: splitAtRL :: Int -> RL a -> (RL a :< RL a)
+ Darcs.Witnesses.Ordered: toFL :: [FreeLeft a] -> Sealed (FL a)
+ Darcs.Witnesses.Ordered: unsafeCoercePEnd :: a -> a
+ Darcs.Witnesses.Ordered: unsafeCoercePStart :: a -> a
+ Darcs.Witnesses.Sealed: class Gap w
+ Darcs.Witnesses.Sealed: data FreeLeft p
+ Darcs.Witnesses.Sealed: data FreeRight p
+ Darcs.Witnesses.Sealed: emptyGap :: (Gap w) => (p) -> w p
+ Darcs.Witnesses.Sealed: freeGap :: (Gap w) => (p) -> w p
+ Darcs.Witnesses.Sealed: instance Gap FreeLeft
+ Darcs.Witnesses.Sealed: instance Gap FreeRight
+ Darcs.Witnesses.Sealed: joinGap :: (Gap w) => (p -> q -> r) -> w p -> w q -> w r
+ Darcs.Witnesses.Sealed: unFreeLeft :: FreeLeft p -> Sealed (p)
+ Darcs.Witnesses.Sealed: unFreeRight :: FreeRight p -> FlippedSeal p
+ Darcs.Witnesses.Sealed: unsafeUnsealFlipped :: FlippedSeal a -> a
+ Darcs.Witnesses.Show: ShowDictClass :: ShowDict a
+ Darcs.Witnesses.Show: ShowDictRecord :: (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> ShowDict a
+ Darcs.Witnesses.Show: appPrec :: Int
+ Darcs.Witnesses.Show: data ShowDict a
+ Darcs.Witnesses.Show: showD :: ShowDict a -> a -> String
+ Darcs.Witnesses.Show: showDict1 :: (Show1 a) => ShowDict (a)
+ Darcs.Witnesses.Show: showDict2 :: (Show2 a) => ShowDict (a)
+ Darcs.Witnesses.Show: showListD :: ShowDict a -> [a] -> ShowS
+ Darcs.Witnesses.Show: showsPrecD :: ShowDict a -> Int -> a -> ShowS
+ Darcs.Witnesses.WZipper: FZipper :: RL a -> FL a -> FZipper a
+ Darcs.Witnesses.WZipper: clowns :: FZipper a -> Sealed ((RL a))
+ Darcs.Witnesses.WZipper: data FZipper a
+ Darcs.Witnesses.WZipper: flToZipper :: FL a -> FZipper a
+ Darcs.Witnesses.WZipper: focus :: FZipper a -> Maybe (Sealed2 a)
+ Darcs.Witnesses.WZipper: jokers :: FZipper a -> FlippedSeal (FL a)
+ Darcs.Witnesses.WZipper: left :: FZipper p -> FZipper p
+ Darcs.Witnesses.WZipper: leftmost :: FZipper p -> Bool
+ Darcs.Witnesses.WZipper: lengthFZ :: FZipper a -> Int
+ Darcs.Witnesses.WZipper: nullFZ :: FZipper a -> Bool
+ Darcs.Witnesses.WZipper: right :: FZipper p -> FZipper p
+ Darcs.Witnesses.WZipper: rightmost :: FZipper p -> Bool
+ Darcs.Witnesses.WZipper: toEnd :: FZipper p -> FZipper p
+ English: presentParticiple :: String -> String
+ Exec: execInteractive :: String -> String -> IO ExitCode
+ Exec: instance Exception ExecException
+ HTTP: requestUrl :: String -> FilePath -> a -> IO String
+ HTTP: waitNextUrl :: IO (String, String)
+ IsoDate: iso8601Duration :: CharParser a TimeDiff
+ IsoDate: iso8601Interval :: Int -> CharParser a (Either TimeDiff (MCalendarTime, MCalendarTime))
+ Printer: insertBeforeLastline :: Doc -> Doc -> Doc
+ Printer: wrapText :: Int -> String -> Doc
+ URL: disableHTTPPipelining :: IO ()
+ URL: maxPipelineLength :: IO Int
- Darcs.Arguments: DarcsAbsPathOption :: [Char] -> [String] -> (AbsolutePath -> DarcsFlag) -> String -> String -> DarcsOption
+ Darcs.Arguments: DarcsAbsPathOption :: [Char] -> [String] -> (AbsolutePath -> DarcsFlag) -> String -> String -> DarcsAtomicOption
- Darcs.Arguments: DarcsAbsPathOrStdOption :: [Char] -> [String] -> (AbsolutePathOrStd -> DarcsFlag) -> String -> String -> DarcsOption
+ Darcs.Arguments: DarcsAbsPathOrStdOption :: [Char] -> [String] -> (AbsolutePathOrStd -> DarcsFlag) -> String -> String -> DarcsAtomicOption
- Darcs.Arguments: DarcsArgOption :: [Char] -> [String] -> (String -> DarcsFlag) -> String -> String -> DarcsOption
+ Darcs.Arguments: DarcsArgOption :: [Char] -> [String] -> (String -> DarcsFlag) -> String -> String -> DarcsAtomicOption
- Darcs.Arguments: DarcsMultipleChoiceOption :: [DarcsOption] -> DarcsOption
+ Darcs.Arguments: DarcsMultipleChoiceOption :: [DarcsAtomicOption] -> DarcsOption
- Darcs.Arguments: DarcsNoArgOption :: [Char] -> [String] -> DarcsFlag -> String -> DarcsOption
+ Darcs.Arguments: DarcsNoArgOption :: [Char] -> [String] -> DarcsFlag -> String -> DarcsAtomicOption
- Darcs.Arguments: DarcsOptAbsPathOption :: [Char] -> [String] -> String -> (AbsolutePath -> DarcsFlag) -> String -> String -> DarcsOption
+ Darcs.Arguments: DarcsOptAbsPathOption :: [Char] -> [String] -> String -> (AbsolutePath -> DarcsFlag) -> String -> String -> DarcsAtomicOption
- Darcs.Arguments: arein :: [DarcsOption] -> [DarcsFlag] -> Bool
+ Darcs.Arguments: arein :: DarcsOption -> [DarcsFlag] -> Bool
- Darcs.Arguments: isin :: (String -> DarcsFlag) -> [DarcsFlag] -> Bool
+ Darcs.Arguments: isin :: DarcsAtomicOption -> [DarcsFlag] -> Bool
- Darcs.Arguments: setDefault :: DarcsOption
+ Darcs.Arguments: setDefault :: Bool -> DarcsOption
- Darcs.Commands.ShowFiles: manifestCmd :: ([DarcsFlag] -> Slurpy -> [FilePath]) -> [DarcsFlag] -> [String] -> IO ()
+ Darcs.Commands.ShowFiles: manifestCmd :: ([DarcsFlag] -> Tree IO -> [FilePath]) -> [DarcsFlag] -> [String] -> IO ()
- Darcs.Commands.ShowFiles: toListManifest :: [DarcsFlag] -> Slurpy -> [FilePath]
+ Darcs.Commands.ShowFiles: toListManifest :: [DarcsFlag] -> Tree m -> [FilePath]
- Darcs.Diff: treeDiff :: (FilePath -> FileType) -> Tree IO -> Tree IO -> IO (FL Prim)
+ Darcs.Diff: treeDiff :: (Functor m, Monad m, Gap w) => (FilePath -> FileType) -> Tree m -> Tree m -> m (w (FL Prim))
- Darcs.Patch.Choices: patchSlot :: TaggedPatch p -> PatchChoices p -> Slot
+ Darcs.Patch.Choices: patchSlot :: (Patchy p) => TaggedPatch p -> PatchChoices p -> (Slot, PatchChoices p)
- Darcs.Patch.Commute: merger :: String -> Patch -> Patch -> Patch
+ Darcs.Patch.Commute: merger :: String -> Patch -> Patch -> Sealed (Patch)
- Darcs.Patch.Commute: unravel :: Patch -> [FL Prim]
+ Darcs.Patch.Commute: unravel :: Patch -> [Sealed (FL Prim)]
- Darcs.Patch.Patchy: bracketedFL :: (ReadPatch p, ParserM m) => Word8 -> Word8 -> m (Maybe (Sealed (FL p)))
+ Darcs.Patch.Patchy: bracketedFL :: (ReadPatch p, ParserM m) => (m (Maybe (Sealed (p)))) -> Word8 -> Word8 -> m (Maybe (Sealed (FL p)))
- Darcs.Patch.Patchy: commuteFL :: (Commute p) => (p :> FL p) -> Either (Sealed2 p) ((FL p :> p))
+ Darcs.Patch.Patchy: commuteFL :: (Commute p) => (p :> FL p) -> Maybe ((FL p :> p))
- Darcs.Patch.Permutations: partitionFL :: (Commute p) => (p -> Bool) -> FL p -> (FL p :> FL p)
+ Darcs.Patch.Permutations: partitionFL :: (Commute p) => (p -> Bool) -> FL p -> ((FL p :> (FL p :> FL p)))
- Darcs.Patch.Prim: subcommutes :: [(String, CommuteFunction)]
+ Darcs.Patch.Prim: subcommutes :: [(String, (Prim :< Prim) -> Perhaps ((Prim :< Prim)))]
- Darcs.Patch.Set: type SealedPatchSet p = Sealed (RL (RL (PatchInfoAnd p)))
+ Darcs.Patch.Set: type SealedPatchSet p = Sealed ((PatchSet p))
- Darcs.RepoPath: useAbsoluteOrStd :: (AbsolutePath -> IO a) -> IO a -> AbsolutePathOrStd -> IO a
+ Darcs.RepoPath: useAbsoluteOrStd :: (AbsolutePath -> a) -> a -> AbsolutePathOrStd -> a
- Darcs.Repository: applyToWorking :: (Patchy p) => Repository p1 -> [DarcsFlag] -> p -> IO ()
+ Darcs.Repository: applyToWorking :: (Patchy p) => Repository p1 -> [DarcsFlag] -> p -> IO (Repository p1)
- Darcs.Repository: checkUnrelatedRepos :: [DarcsFlag] -> [PatchInfo] -> PatchSet p -> PatchSet p -> IO ()
+ Darcs.Repository: checkUnrelatedRepos :: (RepoPatch p) => [DarcsFlag] -> PatchSet p -> PatchSet p -> IO ()
- Darcs.Repository: maybeIdentifyRepository :: [DarcsFlag] -> String -> IO (Either String (Repository p))
+ Darcs.Repository: maybeIdentifyRepository :: [DarcsFlag] -> String -> IO (IdentifyRepo p)
- Darcs.Repository: patchSetToPatches :: (RepoPatch p) => RL (RL (PatchInfoAnd p)) -> FL (Named p)
+ Darcs.Repository: patchSetToPatches :: (RepoPatch p) => PatchSet p -> FL (Named p)
- Darcs.Repository: readUnrecorded :: (RepoPatch p) => Repository p -> IO (Tree IO)
+ Darcs.Repository: readUnrecorded :: (RepoPatch p) => Repository p -> [SubPath] -> IO (Tree IO)
- Darcs.Repository: tentativelyAddPatch :: (RepoPatch p) => Repository p -> [DarcsFlag] -> PatchInfoAnd p -> IO ()
+ Darcs.Repository: tentativelyAddPatch :: (RepoPatch p) => Repository p -> [DarcsFlag] -> PatchInfoAnd p -> IO (Repository p)
- Darcs.Repository: tentativelyRemovePatches :: (RepoPatch p) => Repository p -> [DarcsFlag] -> FL (Named p) -> IO ()
+ Darcs.Repository: tentativelyRemovePatches :: (RepoPatch p) => Repository p -> [DarcsFlag] -> FL (PatchInfoAnd p) -> IO (Repository p)
- Darcs.Repository: tentativelyReplacePatches :: (RepoPatch p) => Repository p -> [DarcsFlag] -> FL (Named p) -> IO ()
+ Darcs.Repository: tentativelyReplacePatches :: (RepoPatch p) => Repository p -> [DarcsFlag] -> FL (PatchInfoAnd p) -> IO (Repository p)
- Darcs.Repository: type SealedPatchSet p = Sealed (RL (RL (PatchInfoAnd p)))
+ Darcs.Repository: type SealedPatchSet p = Sealed ((PatchSet p))
- Darcs.Repository.Internal: applyToWorking :: (Patchy p) => Repository p1 -> [DarcsFlag] -> p -> IO ()
+ Darcs.Repository.Internal: applyToWorking :: (Patchy p) => Repository p1 -> [DarcsFlag] -> p -> IO (Repository p1)
- Darcs.Repository.Internal: maybeIdentifyRepository :: [DarcsFlag] -> String -> IO (Either String (Repository p))
+ Darcs.Repository.Internal: maybeIdentifyRepository :: [DarcsFlag] -> String -> IO (IdentifyRepo p)
- Darcs.Repository.Internal: patchSetToPatches :: (RepoPatch p) => RL (RL (PatchInfoAnd p)) -> FL (Named p)
+ Darcs.Repository.Internal: patchSetToPatches :: (RepoPatch p) => PatchSet p -> FL (Named p)
- Darcs.Repository.Internal: tentativelyAddPatch :: (RepoPatch p) => Repository p -> [DarcsFlag] -> PatchInfoAnd p -> IO ()
+ Darcs.Repository.Internal: tentativelyAddPatch :: (RepoPatch p) => Repository p -> [DarcsFlag] -> PatchInfoAnd p -> IO (Repository p)
- Darcs.Repository.Internal: tentativelyAddPatch_ :: (RepoPatch p) => UpdatePristine -> Repository p -> [DarcsFlag] -> PatchInfoAnd p -> IO ()
+ Darcs.Repository.Internal: tentativelyAddPatch_ :: (RepoPatch p) => UpdatePristine -> Repository p -> [DarcsFlag] -> PatchInfoAnd p -> IO (Repository p)
- Darcs.Repository.Internal: tentativelyRemovePatches :: (RepoPatch p) => Repository p -> [DarcsFlag] -> FL (Named p) -> IO ()
+ Darcs.Repository.Internal: tentativelyRemovePatches :: (RepoPatch p) => Repository p -> [DarcsFlag] -> FL (PatchInfoAnd p) -> IO (Repository p)
- Darcs.Repository.Internal: tentativelyReplacePatches :: (RepoPatch p) => Repository p -> [DarcsFlag] -> FL (Named p) -> IO ()
+ Darcs.Repository.Internal: tentativelyReplacePatches :: (RepoPatch p) => Repository p -> [DarcsFlag] -> FL (PatchInfoAnd p) -> IO (Repository p)
- Darcs.Repository.Internal: type SealedPatchSet p = Sealed (RL (RL (PatchInfoAnd p)))
+ Darcs.Repository.Internal: type SealedPatchSet p = Sealed ((PatchSet p))
- Darcs.Repository.State: readUnrecorded :: (RepoPatch p) => Repository p -> IO (Tree IO)
+ Darcs.Repository.State: readUnrecorded :: (RepoPatch p) => Repository p -> [SubPath] -> IO (Tree IO)
- Darcs.Repository.State: restrictBoring :: Tree m -> IO ((FilterTree t m) => t m -> t m)
+ Darcs.Repository.State: restrictBoring :: Tree m -> IO (TreeFilter m)
- Darcs.Repository.State: restrictSubpaths :: (RepoPatch p) => Repository p -> [SubPath] -> IO (forall t m. (FilterTree t m) => t m -> t m)
+ Darcs.Repository.State: restrictSubpaths :: (RepoPatch p) => Repository p -> [SubPath] -> IO (TreeFilter m)
- Darcs.SignalHandler: catchNonSignal :: IO a -> (Exception -> IO a) -> IO a
+ Darcs.SignalHandler: catchNonSignal :: IO a -> (SomeException -> IO a) -> IO a
- Darcs.SignalHandler: tryNonSignal :: IO a -> IO (Either Exception a)
+ Darcs.SignalHandler: tryNonSignal :: IO a -> IO (Either SomeException a)
- Darcs.Utils: prettyException :: Exception -> String
+ Darcs.Utils: prettyException :: SomeException -> String
- Darcs.Witnesses.Ordered: data (:||:) a1 a2
+ Darcs.Witnesses.Ordered: data (:>>) a1 a2
- Darcs.Witnesses.Ordered: filterFL :: (p -> EqCheck) -> FL p -> FL p
+ Darcs.Witnesses.Ordered: filterFL :: (a -> Bool) -> FL a -> [Sealed2 a]
Files
- Distribution/ShellHarness.hs +15/−7
- NEWS +59/−0
- README +3/−14
- Setup.lhs +32/−20
- contrib/darcs-errors.hlint +2/−2
- contrib/update_roundup.pl +9/−4
- darcs.cabal +414/−346
- doc/darcs.css +4/−0
- release/distributed-context +1/−1
- src/ByteStringUtils.hs +89/−20
- src/CommandLine.hs +4/−4
- src/Darcs/ArgumentDefaults.lhs +26/−26
- src/Darcs/Arguments.lhs +330/−209
- src/Darcs/CheckFileSystem.hs +0/−59
- src/Darcs/ColorPrinter.hs +56/−58
- src/Darcs/Commands.lhs +8/−7
- src/Darcs/Commands/Add.lhs +84/−90
- src/Darcs/Commands/AmendRecord.lhs +60/−47
- src/Darcs/Commands/Annotate.lhs +39/−31
- src/Darcs/Commands/Apply.lhs +81/−75
- src/Darcs/Commands/Changes.lhs +73/−64
- src/Darcs/Commands/Check.lhs +9/−6
- src/Darcs/Commands/Convert.lhs +51/−47
- src/Darcs/Commands/Diff.lhs +22/−22
- src/Darcs/Commands/Dist.lhs +25/−27
- src/Darcs/Commands/Get.lhs +45/−43
- src/Darcs/Commands/MarkConflicts.lhs +19/−15
- src/Darcs/Commands/Move.lhs +55/−47
- src/Darcs/Commands/Optimize.lhs +94/−59
- src/Darcs/Commands/Pull.lhs +167/−57
- src/Darcs/Commands/Push.lhs +46/−36
- src/Darcs/Commands/Put.lhs +29/−26
- src/Darcs/Commands/Record.lhs +87/−87
- src/Darcs/Commands/Remove.lhs +53/−43
- src/Darcs/Commands/Repair.lhs +0/−1
- src/Darcs/Commands/Replace.lhs +14/−11
- src/Darcs/Commands/Revert.lhs +16/−11
- src/Darcs/Commands/Rollback.lhs +29/−28
- src/Darcs/Commands/Send.lhs +79/−87
- src/Darcs/Commands/SetPref.lhs +5/−2
- src/Darcs/Commands/ShowAuthors.lhs +5/−4
- src/Darcs/Commands/ShowContents.lhs +2/−2
- src/Darcs/Commands/ShowFiles.lhs +29/−27
- src/Darcs/Commands/ShowRepo.lhs +6/−5
- src/Darcs/Commands/ShowTags.lhs +7/−6
- src/Darcs/Commands/Tag.lhs +6/−6
- src/Darcs/Commands/TrackDown.lhs +107/−30
- src/Darcs/Commands/TransferMode.lhs +1/−1
- src/Darcs/Commands/Unrecord.lhs +75/−45
- src/Darcs/Commands/Unrevert.lhs +33/−33
- src/Darcs/Commands/WhatsNew.lhs +29/−22
- src/Darcs/CommandsAux.hs +19/−19
- src/Darcs/Compat.hs +16/−16
- src/Darcs/Diff.hs +51/−30
- src/Darcs/Email.hs +43/−41
- src/Darcs/External.hs +43/−29
- src/Darcs/FilePathMonad.hs +6/−6
- src/Darcs/Flags.hs +20/−10
- src/Darcs/Global.hs +10/−10
- src/Darcs/Hopefully.hs +33/−12
- src/Darcs/IO.hs +10/−10
- src/Darcs/Lock.hs +55/−50
- src/Darcs/Match.lhs +109/−202
- src/Darcs/Patch.lhs +13/−26
- src/Darcs/Patch/Apply.lhs +55/−62
- src/Darcs/Patch/Bundle.hs +96/−74
- src/Darcs/Patch/Choices.hs +237/−204
- src/Darcs/Patch/Commute.lhs +285/−299
- src/Darcs/Patch/Core.lhs +11/−11
- src/Darcs/Patch/Depends.hs +279/−457
- src/Darcs/Patch/FileName.hs +51/−54
- src/Darcs/Patch/Info.hs +192/−140
- src/Darcs/Patch/Match.lhs +42/−25
- src/Darcs/Patch/MatchData.hs +3/−3
- src/Darcs/Patch/Non.hs +13/−16
- src/Darcs/Patch/OldDate.hs +75/−75
- src/Darcs/Patch/Patchy.hs +22/−18
- src/Darcs/Patch/Permutations.hs +37/−32
- src/Darcs/Patch/Prim.lhs +212/−202
- src/Darcs/Patch/Properties.lhs +54/−54
- src/Darcs/Patch/Read.hs +76/−82
- src/Darcs/Patch/ReadMonads.hs +57/−48
- src/Darcs/Patch/Real.hs +112/−115
- src/Darcs/Patch/RegChars.hs +1/−1
- src/Darcs/Patch/Set.hs +37/−25
- src/Darcs/Patch/Show.lhs +3/−0
- src/Darcs/Patch/Split.hs +2/−2
- src/Darcs/Patch/TouchesFiles.hs +29/−21
- src/Darcs/Patch/Viewing.hs +2/−2
- src/Darcs/Population.hs +31/−28
- src/Darcs/PopulationData.hs +5/−5
- src/Darcs/ProgressPatches.hs +3/−3
- src/Darcs/RemoteApply.hs +30/−30
- src/Darcs/RepoPath.hs +14/−10
- src/Darcs/Repository.hs +191/−111
- src/Darcs/Repository/ApplyPatches.hs +9/−9
- src/Darcs/Repository/Cache.hs +73/−8
- src/Darcs/Repository/Checkpoint.hs +42/−64
- src/Darcs/Repository/DarcsRepo.lhs +163/−145
- src/Darcs/Repository/Format.hs +1/−1
- src/Darcs/Repository/HashedIO.hs +34/−120
- src/Darcs/Repository/HashedRepo.hs +259/−178
- src/Darcs/Repository/Internal.hs +208/−232
- src/Darcs/Repository/InternalTypes.hs +14/−2
- src/Darcs/Repository/LowLevel.hs +6/−6
- src/Darcs/Repository/Merge.hs +29/−27
- src/Darcs/Repository/Motd.lhs +6/−6
- src/Darcs/Repository/Prefs.lhs +34/−27
- src/Darcs/Repository/Pristine.hs +9/−19
- src/Darcs/Repository/Repair.hs +55/−46
- src/Darcs/Repository/State.hs +41/−38
- src/Darcs/Resolution.lhs +24/−25
- src/Darcs/RunCommand.hs +40/−38
- src/Darcs/SelectChanges.hs +834/−641
- src/Darcs/SignalHandler.hs +38/−30
- src/Darcs/SlurpDirectory.hs +0/−35
- src/Darcs/SlurpDirectory/Internal.hs +0/−661
- src/Darcs/Test.lhs +17/−17
- src/Darcs/Test/Email.hs +28/−28
- src/Darcs/Test/Patch/Check.hs +120/−120
- src/Darcs/Test/Patch/Info.hs +158/−0
- src/Darcs/Test/Patch/QuickCheck.hs +67/−81
- src/Darcs/Test/Patch/Test.hs +195/−190
- src/Darcs/Test/Patch/Unit.hs +98/−96
- src/Darcs/Test/Unit.lhs +766/−0
- src/Darcs/TheCommands.hs +2/−1
- src/Darcs/URL.hs +20/−20
- src/Darcs/Utils.hs +106/−66
- src/Darcs/Witnesses/Ordered.hs +75/−33
- src/Darcs/Witnesses/Sealed.hs +69/−23
- src/Darcs/Witnesses/Show.hs +34/−11
- src/Darcs/Witnesses/WZipper.hs +77/−0
- src/DateMatcher.hs +8/−5
- src/English.hs +4/−0
- src/Exec.hs +17/−14
- src/HTTP.hs +9/−8
- src/IsoDate.hs +109/−109
- src/Lcs.hs +4/−4
- src/Preproc.hs +29/−29
- src/Printer.lhs +23/−23
- src/Progress.hs +3/−3
- src/SHA1.hs +25/−25
- src/Ssh.hs +6/−5
- src/URL.hs +60/−59
- src/building_darcs.tex +2/−2
- src/darcs.hs +18/−19
- src/fpstring.c +2/−0
- src/fpstring.h +0/−1
- src/hscurl.c +20/−8
- src/hscurl.h +2/−0
- src/unit.hs +3/−0
- src/unit.lhs +0/−770
- src/witnesses.hs +23/−22
- tests/amend-record.sh +30/−1
- tests/check.sh +2/−2
- tests/dist.sh +23/−3
- tests/external-resolution.sh +11/−0
- tests/failed-amend-should-not-break-repo.sh +3/−3
- tests/failing-issue1014_identical_patches.sh +2/−0
- tests/failing-issue1337_darcs_changes_false_positives.sh +0/−37
- tests/failing-issue1363-mark-conflicts.sh +62/−0
- tests/failing-issue1461_case_folding.sh +4/−0
- tests/failing-issue1727_move_current_directory.sh +49/−0
- tests/failing-issue1737-move_args.sh +38/−0
- tests/failing-issue1740-mv-dir.sh +36/−0
- tests/failing-issue1845-paths-working-copy.sh +37/−0
- tests/failing-issue1848-rollback-p.sh +36/−0
- tests/failing-issue1909-unrecord-O-misses-tag.sh +20/−0
- tests/filepath.sh +1/−1
- tests/get.sh +2/−1
- tests/issue121.sh +45/−0
- tests/issue1210-no-global-cache-in-sources.sh +36/−0
- tests/issue1232_convert_forgets_prefs.sh +62/−0
- tests/issue1277-repo-format.sh +46/−0
- tests/issue1290-diff-index.sh +22/−0
- tests/issue1337_darcs_changes_false_positives.sh +37/−0
- tests/issue1427_apply_gz.sh +41/−0
- tests/issue1473.sh +42/−0
- tests/issue1618-amend-preserve-logfile.sh +1/−1
- tests/issue1620-record-lies-about-leaving-logfile.sh +1/−1
- tests/issue1726_darcs_always-boring.sh +73/−0
- tests/issue1739-escape-multibyte-chars-correctly.sh +71/−0
- tests/issue1749-rmdir.sh +42/−0
- tests/issue1756_moves_index.sh +50/−0
- tests/issue1763-pull-fails-on-non-ascii-filenames.sh +77/−0
- tests/issue1825-remove-pending.sh +43/−0
- tests/issue183_mv_order.sh +45/−0
- tests/issue1845-record-removed.sh +16/−0
- tests/issue1857-pristine-conversion.sh +37/−0
- tests/issue1860-incomplete-pristine.sh +45/−0
- tests/issue1865-get-context.sh +20/−0
- tests/issue1871-record-dot.sh +18/−0
- tests/issue1873-apply-failed-to-read-patch.sh +55/−0
- tests/issue1875-honor-no-set-default.sh +39/−0
- tests/issue1877_noisy_xml_output.sh +47/−0
- tests/issue1879-same-patchinfo-uncommon.sh +38/−0
- tests/issue1888-changes-context.sh +43/−0
- tests/issue1898-set-default-notification.sh +54/−0
- tests/issue1913-diffing.sh +39/−0
- tests/issue1951-add-outside-repo.sh +151/−0
- tests/issue538.sh +4/−4
- tests/lib +3/−6
- tests/match-date.sh +14/−1
- tests/mergeresolved.sh +0/−2
- tests/network/issue1503_prefer_local_caches_to_remote_one.sh +33/−0
- tests/output.sh +1/−1
- tests/posthook.sh +32/−2
- tests/printer.sh +9/−9
- tests/pull-dont-prompt-deps.sh +6/−4
- tests/pull.sh +2/−2
- tests/pull_binary.sh +1/−1
- tests/push-dont-prompt-deps.sh +6/−4
- tests/query_manifest.sh +6/−5
- tests/record.sh +4/−3
- tests/repos/minimal-darcs-2.4.tgz binary
- tests/send-dont-prompt-deps.sh +6/−4
- tests/tentative_revert.sh +2/−2
- tests/trackdown-bisect-helper.hs +38/−0
- tests/trackdown-bisect.sh +141/−0
- tests/utf8.sh +189/−0
- tests/whatsnew-pending.sh +15/−0
Distribution/ShellHarness.hs view
@@ -17,12 +17,20 @@ import Data.Maybe import Data.List ( isInfixOf, isPrefixOf, (\\), nubBy, isSuffixOf ) import Control.Concurrent+import qualified Control.Exception as Exception+import Control.Monad++-- Handle exceptions migration. We could use extensible-exceptions+-- but Cabal can't handle package dependencies of Setup.lhs+-- automatically so it'd be disruptive for users.+-- Once we drop older GHCs we can clean up the use sites properly+-- and perhaps think about being more restrictive in which exceptions+-- are caught at each site. #if __GLASGOW_HASKELL__ >= 610-import Control.OldException+catchAny f h = Exception.catch f (\e -> h (e :: Exception.SomeException)) #else-import Control.Exception+catchAny = Exception.catch #endif-import Control.Monad runTests :: Maybe FilePath -> String -> [String] -> IO Bool runTests darcs_path cwd tests = do@@ -62,7 +70,7 @@ ,("DARCS_DONT_ESCAPE_ANYTHING","1")] shell = takeWhile (/= '\n') bash putStrLn $ "Using bash shell in '"++shell++"'"- catch (appendFile (".darcs/defaults") "\nALL --ignore-times\n")+ catchAny (appendFile (".darcs/defaults") "\nALL ignore-times\nsend no-edit-description\n") (\e -> fail $ "Unable to set preferences: " ++ show e) run_helper shell tests [] (set_env myenv env) @@ -102,8 +110,8 @@ system $ "hpc sum --union --output=" ++ tixdir </> "sum.tix" ++ " " ++ unwords tixfiles forM tixfiles $ \f -> removeFile f return ()- mapM_ (\x-> - setPermissions x (Permissions + mapM_ (\x->+ setPermissions x (Permissions {readable = True ,writable = True ,executable = False@@ -146,7 +154,7 @@ let readWrite i = do x <- hGetLine i writeChan ch $ Just x readWrite i- `catch` \_ -> writeChan ch Nothing+ `catchAny` \_ -> writeChan ch Nothing readEO = do x <- readChan ch case x of Just l -> do y <- readEO
NEWS view
@@ -1,3 +1,62 @@+Darcs 2.5, 30 October 2010:++ * Important changes in Darcs 2.5++ * trackdown can now do binary search with the --bisect option+ * darcs always stores patch metadata encoded with UTF-8+ * diff now supports the --index option+ * amend-record now supports the --ask-deps option+ * apply now supports the --match option+ * amend-record has a new --keep-date option+ * inventory-changing commands (like record and pull) now operate in+ constant time with respect to the number of patches in the repository+ * the push, pull, send and fetch commands no longer set the default+ repository by default+ * the --edit-description option is now on by default for the send command++ * Issues resolved in Darcs 2.5++ * 64: store metadata as UTF-8+ * 121: add --ask-deps support to amend-record+ * 643: darcs send -o outputs remote repo email address+ * 1159: avoid bogus repository cache entries+ * 1176: caches interfere with --remote-repo flag+ * 1208: add trackdown --bisect+ * 1210: global cache gets recorded in _darcs/prefs/sources+ * 1232: darcs convert copies _darcs/prefs/prefs+ * 1250: check for newlines in setpref values+ * 1277: percolate repository format errors correctly+ * 1288: the main darcs code now compiles and runs with witnesses+ * 1290: support diff --index+ * 1337: don't show unrelated patches in darcs changes on untracked path+ * 1389: change predist pref to point people to use 'cabal sdist'+ * 1427: accept gzipped patch bundles in darcs apply+ * 1456: make dist write more portable archives+ * 1473: make annotate accept '.' as argument+ * 1503: prefer local caches to remote ones+ * 1713: shorter interactive prompts+ * 1716: allow mail header lines of all whitespace in test+ * 1719: do not back up files when no conflict markers are inserted+ * 1726: don't consider all files with _darcs prefix boring+ * 1739: make ColorPrinter handle characters > 255+ * 1763: use correct filename encoding in conflictors+ * 1765: refuse to remove non-tracked directories recursively+ * 1769: add support for --match 'comment ...'+ * 1784: push and pull print remote address right away+ * 1815: work around Cabal sdist permissions issue+ * 1817: fix support for external merge tools+ * 1824: avoid PACKAGE_VERSION under Windows+ * 1825: do not omit important prims in unrecordedChanges w/ files+ * 1860: (un)applying move patches doesn't corrupt pristine+ * 1861: fix typo in --no-boring help+ * 1874: recognise network tests on cabal test command line+ * 1875: avoid accidentally setting default+ * 1879: notice unexpected commute failure on merge+ * 1887: add a missing newline to --list-options output+ * 1893: move fields of conditional builds within scope of condition+ * 1898: notify user when they can use set-default+ * 1913: sort changes in treeDiff+ Darcs 2.4.4, 9 May 2010 * Important changes in Darcs 2.4.4
README view
@@ -11,20 +11,9 @@ Compilation and Installation ============================ -Building Darcs requires the cabal package, version 1.6 or higher. The-cabal-install package is also recommended.--Using GHC 6.10.3 or newer is STRONGLY RECOMMENDED. You can compile darcs with-GHC 6.8, but there are several caveats. If you are using 6.8.2 or older, please-disable mmap support (pass -f-mmap to cabal install or runghc Setup configure-below). Note that the GHC 6.8.2 that ships with Debian Lenny is not affected-and it should be safe to keep mmap enabled. It is also recommended to disable-use of Hackage zlib when compiling with GHC 6.8.2 (including the Debian Lenny-version): pass -f-zlib to cabal. When using zlib, we have seen occasional-crashes with error messages like "openBinaryFile: file locked" -- this is a-known GHC 6.8.2 bug (and is fixed in GHC 6.8.3). Last, if you are using a-64-bit system, darcs may hang when you exit a pager when compiled with GHC-older than 6.10.3. Although this is harmless, it is quite inconvenient.+Building Darcs requires GHC, version 6.10.3 or higher. It also requires the+Cabal package, version 1.6 or higher. The cabal-install package is also+recommended. If you have the "cabal-install" package on your system (that is, there is a "cabal" executable in your path), you can use the following command to create
Setup.lhs view
@@ -3,6 +3,9 @@ -- copyright (c) 2008 Duncan Coutts -- portions copyright (c) 2008 David Roundy +import Prelude hiding ( catch )+import qualified Prelude+ import Distribution.Simple ( defaultMainWithHooks, UserHooks(..), simpleUserHooks ) import Distribution.ModuleName( toFilePath )@@ -12,11 +15,12 @@ , updatePackageDescription, cppOptions, ccOptions , library, libBuildInfo, otherModules ) import Distribution.Package- ( packageVersion )+ ( packageVersion, packageName, PackageName(..) ) import Distribution.Version ( Version(versionBranch) )+import Data.Version( showVersion ) import Distribution.Simple.LocalBuildInfo- ( LocalBuildInfo(..), absoluteInstallDirs )+ ( LocalBuildInfo(..), absoluteInstallDirs, externalPackageDeps ) import Distribution.Simple.InstallDirs (mandir, CopyDest (NoCopyDest)) import Distribution.Simple.Setup (buildVerbosity, copyDest, copyVerbosity, fromFlag,@@ -54,10 +58,18 @@ import qualified Distribution.ShellHarness as Harness ( runTests ) +import qualified Control.Exception as Exception++-- Handle exceptions migration. We could use extensible-exceptions+-- but Cabal can't handle package dependencies of Setup.lhs+-- automatically so it'd be disruptive for users.+-- Once we drop older GHCs we can clean up the use sites properly+-- and perhaps think about being more restrictive in which exceptions+-- are caught at each site. #if __GLASGOW_HASKELL__ >= 610-import qualified Control.OldException as Exception+catchAny f h = Exception.catch f (\e -> h (e :: Exception.SomeException)) #else-import qualified Control.Exception as Exception+catchAny = Exception.catch #endif main :: IO ()@@ -200,7 +212,7 @@ case reads (out) of ((n,_):_) -> return $ Just ((n :: Int) - 1) _ -> return Nothing- `Exception.catch` \_ -> return Nothing+ `catchAny` \_ -> return Nothing numPatchesDist <- parseFile versionFile return $ case (numPatchesDarcs, numPatchesDist) of@@ -219,12 +231,15 @@ ctx <- context verbosity (packageVersion pkg) rewriteFile (dir </> "Version.hs") $ unlines ["module Version where"- ,"version, context :: String"+ ,"builddeps, version, context :: String" ,"version = \"" ++ version ++ " (" ++ state ++ ")\""+ ,"builddeps = " ++ (show $ formatdeps (externalPackageDeps lbi)) ,"context = " ++ case ctx of Just x -> show x Nothing -> show "context not available" ]+ where formatdeps = unlines . map (formatone . snd)+ formatone p = case packageName p of PackageName n -> n ++ "-" ++ showVersion (packageVersion p) context :: Verbosity -> Version -> IO (Maybe String) context verbosity version = do@@ -237,7 +252,7 @@ ["changes", "--from-tag", display version ] out <- rawSystemStdout verbosity "darcs" ["changes", "--context"] return $ Just out- `Exception.catch` \_ -> return Nothing+ `catchAny` \_ -> return Nothing contextDist <- parseFile contextFile return $ case (contextDarcs, contextDist) of@@ -312,14 +327,11 @@ run <- map kindify `fmap` filterM doesFileExist c return $ take 1 run kindify test = case splitDirectories test of- [p, y] -> (parse_kind p y, y)- _ -> error $ "Bad format in " ++ test ++- ": expected type/test"- parse_kind "tests" y = if isTest Bug y then Bug else Test- parse_kind "network" _ = Network- parse_kind x _ = error $ "Test prefix must be one of " ++- "[tests, network] in " ++ x-+ ["tests", y] -> (parse_kind y, y)+ ["tests","network",y] -> (Network, y)+ xs -> error $ "Bad format in " ++ test +++ ": expected type/test" ++ " but got " ++ show xs+ parse_kind y = if isTest Bug y then Bug else Test allTests :: FilePath -> TestKind -> [String] -> IO () allTests darcs_path k s =@@ -339,7 +351,7 @@ (do cwd <- getCurrentDirectory when (name /= "") (setCurrentDirectory name) return cwd)- (\oldwd -> setCurrentDirectory oldwd `catch` (\_ -> return ()))+ (\oldwd -> setCurrentDirectory oldwd `catchAny` (\_ -> return ())) (const m) cloneTree :: FilePath -> FilePath -> IO ()@@ -356,7 +368,7 @@ mk_dest fp = dest ++ "/" ++ fp zipWithM_ cloneSubTree (map mk_source fps') (map mk_dest fps') else fail ("cloneTreeExcept: Bad source " ++ source)- `catch` fail ("cloneTreeExcept: Bad source " ++ source)+ `catchAny` fail ("cloneTreeExcept: Bad source " ++ source) cloneSubTree :: FilePath -> FilePath -> IO () cloneSubTree source dest =@@ -372,9 +384,9 @@ else if isfile then do cloneFile source dest else fail ("cloneSubTree: Bad source "++ source)- `catch` (\e -> if isDoesNotExistError e- then return ()- else ioError e)+ `Prelude.catch` (\e -> if isDoesNotExistError e+ then return ()+ else ioError e) cloneFile :: FilePath -> FilePath -> IO () cloneFile = copyFile
contrib/darcs-errors.hlint view
@@ -41,5 +41,5 @@ ==> Data.ByteString.hGetContents error "Avoid BL.hGetContents" = Data.ByteString.Lazy.Char8.hGetContents ==> Data.ByteString.hGetContents-error "Avoid BL.readFile" = Data.ByteString.Lazy.Char8.readFile ==> Data.ByteString.readFile-error "Avoid BL.readFile" = Data.ByteString.Lazy.readFile ==> Data.ByteString.readFile+-- error "Avoid BL.readFile" = Data.ByteString.Lazy.Char8.readFile ==> Data.ByteString.readFile+-- error "Avoid BL.readFile" = Data.ByteString.Lazy.readFile ==> Data.ByteString.readFile
contrib/update_roundup.pl view
@@ -16,12 +16,17 @@ use MIME::Lite; use XML::Simple; +my $UPDATE_STRING="status=resolved";+if (scalar(@ARGV) == 1) {+ $UPDATE_STRING=$ARGV[0];+}+ unless ($ENV{DARCS_PATCHES_XML}) { die "DARCS_PATCHES_XML was expected to be set in the environment, but was not found. Are you running this from a Darcs 2.0 or newer posthook?" } -my $xml = eval { XMLin($ENV{DARCS_PATCHES_XML}); };+my $xml = eval { XMLin($ENV{DARCS_PATCHES_XML}, forcearray=>['patch']); }; die "hmmm.. we couldn't parse your XML. The error was: $@" if $@; # $xml structure returned looks like this: @@ -59,11 +64,11 @@ # Each patches can potentially update the status of a different issue, so generates a different e-mail my $msg = MIME::Lite->new( From => $email, - To =>'bugs@darcs.net',+ To =>'bugs@lists.osuosl.org', #To =>'mark@stosberg.com',- Subject =>"[$issue] [status=resolved]",+ Subject =>"[$issue] [$UPDATE_STRING]", Type =>'text/plain',- Data => qq!The following patch updated the status of $issue to be resolved:+ Data => qq!The following patch updated issue $issue with $UPDATE_STRING * $patch_name $comment
darcs.cabal view
@@ -1,5 +1,5 @@ Name: darcs-version: 2.4.4+version: 2.5 License: GPL License-file: COPYING Author: David Roundy <droundy@darcs.net>, <darcs-users@darcs.net>@@ -33,7 +33,7 @@ Homepage: http://darcs.net/ Build-Type: Custom-Cabal-Version: >= 1.6+Cabal-Version: >= 1.8 Tested-with: GHC==6.8.2 extra-source-files:@@ -59,7 +59,9 @@ -- testsuite Distribution/ShellHarness.hs tests/repos/*.tgz tests/repos/README+ tests/repos/minimal-darcs-2.4.tgz tests/*.sh+ tests/trackdown-bisect-helper.hs tests/hspwd.hs tests/network/*.sh tests/lib@@ -75,12 +77,6 @@ flag curl description: Use libcurl for HTTP support. -flag curl-pipelining- description: Use libcurl's HTTP pipelining.- default: False- --TODO: needs Cabal ticket #342 to allow default True- -- and decide on this automatically- flag http description: Use the pure Haskell HTTP package for HTTP support. @@ -99,6 +95,10 @@ description: Use GADT type witnesses. default: False +flag library+ description: Build darcs library+ default: True+ flag color description: Use ansi color escapes. @@ -118,224 +118,267 @@ Executable witnesses main-is: witnesses.hs- hs-source-dirs: src- include-dirs: src- cpp-options: -DGADT_WITNESSES=1- -- FIXME...- c-sources: src/atomic_create.c- src/fpstring.c- src/maybe_relink.c- src/umask.c- src/Crypt/sha2.c- -- this module isn't exported by libdarcs, so not included in the tarball- -- if not mentioned- other-modules: Darcs.Test.Patch.Check - extensions:- CPP- UndecidableInstances- ScopedTypeVariables- RankNTypes- GADTs- ImpredicativeTypes- if !flag(type-witnesses) buildable: False+ else+ buildable: True - if os(windows)- hs-source-dirs: src/win32- include-dirs: src/win32- other-modules: CtrlC- System.Posix- System.Posix.Files- System.Posix.IO- cpp-options: -DWIN32- c-sources: src/win32/send_email.c- build-depends: unix-compat >= 0.1.2+ hs-source-dirs: src+ include-dirs: src+ cpp-options: -DGADT_WITNESSES=1+ -- FIXME...+ c-sources: src/atomic_create.c+ src/fpstring.c+ src/maybe_relink.c+ src/umask.c+ src/Crypt/sha2.c+ -- this module isn't exported by libdarcs, so not included in the tarball+ -- if not mentioned+ other-modules: Darcs.Test.Patch.Check + extensions:+ CPP+ PatternGuards+ UndecidableInstances+ ScopedTypeVariables+ -- PatternSignatures is needed for GHC 6.8+ PatternSignatures+ RankNTypes+ GADTs+ TypeOperators++ if os(windows)+ hs-source-dirs: src/win32+ include-dirs: src/win32+ other-modules: CtrlC+ System.Posix+ System.Posix.Files+ System.Posix.IO+ cpp-options: -DWIN32+ c-sources: src/win32/send_email.c+ build-depends: unix-compat >= 0.1.2,+ regex-posix >= 0.94.4 && < 0.95++ build-depends: base < 5,+ extensible-exceptions >= 0.1 && < 0.2,+ regex-compat >= 0.71 && < 0.94,+ mtl >= 1.0 && < 1.2,+ parsec >= 2.0 && < 3.1,+ html == 1.0.*,+ filepath == 1.1.*,+ haskeline >= 0.6.2.2 && < 0.7,+ hashed-storage >= 0.5.2 && < 0.6,+ base >= 3,+ bytestring >= 0.9.0 && < 0.10,+ text >= 0.3,+ old-time == 1.0.*,+ directory == 1.0.*,+ process == 1.0.*,+ containers >= 0.1 && < 0.4,+ array >= 0.1 && < 0.4,+ random == 1.0.*,+ tar == 0.3.*,+ zlib >= 0.5.1.0 && < 0.6.0.0,+ QuickCheck >= 2.1.0.0,+ test-framework >= 0.2.2,+ test-framework-quickcheck2 >= 0.2.2+ if !os(windows)+ build-depends: unix >= 1.0 && < 2.5+ if flag(http)+ build-depends: network == 2.2.*,+ HTTP >= 3000.0 && < 4000.1++ -- ---------------------------------------------------------------------- -- darcs library -- ---------------------------------------------------------------------- Library- hs-source-dirs: src- include-dirs: src+ if !flag(library)+ buildable: False+ else+ buildable: True - exposed-modules: CommandLine- Crypt.SHA256- Darcs.ArgumentDefaults- Darcs.Arguments- Darcs.Bug- Darcs.CheckFileSystem- Darcs.ColorPrinter- Darcs.Commands- Darcs.Commands.Add- Darcs.Commands.AmendRecord- Darcs.Commands.Annotate- Darcs.Commands.Apply- Darcs.CommandsAux- Darcs.Commands.Changes- Darcs.Commands.Check- Darcs.Commands.Convert- Darcs.Commands.Diff- Darcs.Commands.Dist- Darcs.Commands.Get- Darcs.Commands.GZCRCs- Darcs.Commands.Help- Darcs.Commands.Init- Darcs.Commands.MarkConflicts- Darcs.Commands.Move- Darcs.Commands.Optimize- Darcs.Commands.Pull- Darcs.Commands.Push- Darcs.Commands.Put- Darcs.Commands.Record- Darcs.Commands.Remove- Darcs.Commands.Repair- Darcs.Commands.Replace- Darcs.Commands.Revert- Darcs.Commands.Rollback- Darcs.Commands.Send- Darcs.Commands.SetPref- Darcs.Commands.Show- Darcs.Commands.ShowAuthors- Darcs.Commands.ShowBug- Darcs.Commands.ShowContents- Darcs.Commands.ShowFiles- Darcs.Commands.ShowIndex- Darcs.Commands.ShowRepo- Darcs.Commands.ShowTags- Darcs.Commands.Tag- Darcs.Commands.TrackDown- Darcs.Commands.TransferMode- Darcs.Commands.Unrecord- Darcs.Commands.Unrevert- Darcs.Commands.WhatsNew- Darcs.Compat- Darcs.Diff- Darcs.Email- Darcs.External- Darcs.FilePathMonad- Darcs.Flags- Darcs.Global- Darcs.Hopefully- Darcs.IO- Darcs.Lock- Darcs.Match- Darcs.Witnesses.Ordered- Darcs.Patch- Darcs.Patch.Apply- Darcs.Patch.Bundle- Darcs.Patch.Choices- Darcs.Patch.Commute- Darcs.Patch.Core- Darcs.Patch.Depends- Darcs.Patch.FileName- Darcs.Patch.Info- Darcs.Patch.Match- Darcs.Patch.MatchData- Darcs.Patch.Non- Darcs.Patch.OldDate- Darcs.Patch.Patchy- Darcs.Patch.Permutations- Darcs.Patch.Prim- Darcs.Patch.Properties- Darcs.Patch.Read- Darcs.Patch.ReadMonads- Darcs.Patch.Real- Darcs.Patch.RegChars- Darcs.Patch.Set- Darcs.Patch.Show- Darcs.Patch.Split- Darcs.Patch.TouchesFiles- Darcs.Patch.Viewing- Darcs.Population- Darcs.PopulationData- Darcs.PrintPatch- Darcs.ProgressPatches- Darcs.RemoteApply- Darcs.RepoPath- Darcs.Repository- Darcs.Repository.ApplyPatches- Darcs.Repository.Cache- Darcs.Repository.Checkpoint- Darcs.Repository.DarcsRepo- Darcs.Repository.Format- Darcs.Repository.HashedIO- Darcs.Repository.HashedRepo- Darcs.Repository.Internal- Darcs.Repository.LowLevel- Darcs.Repository.Merge- Darcs.Repository.InternalTypes- Darcs.Repository.Motd- Darcs.Repository.Prefs- Darcs.Repository.Pristine- Darcs.Repository.Repair- Darcs.Repository.State- Darcs.Resolution- Darcs.RunCommand- Darcs.Witnesses.Sealed- Darcs.SelectChanges- Darcs.Witnesses.Show- Darcs.SignalHandler- Darcs.SlurpDirectory- Darcs.SlurpDirectory.Internal- Darcs.Test- Darcs.TheCommands- Darcs.URL- Darcs.Utils- DateMatcher- English- Exec- ByteStringUtils- HTTP- IsoDate- Lcs- Printer- Progress- Ratified- SHA1- Ssh- URL- Workaround+ hs-source-dirs: src+ include-dirs: src - other-modules: Version+ exposed-modules: CommandLine+ Crypt.SHA256+ Darcs.ArgumentDefaults+ Darcs.Arguments+ Darcs.Bug+ Darcs.ColorPrinter+ Darcs.Commands+ Darcs.Commands.Add+ Darcs.Commands.AmendRecord+ Darcs.Commands.Annotate+ Darcs.Commands.Apply+ Darcs.CommandsAux+ Darcs.Commands.Changes+ Darcs.Commands.Check+ Darcs.Commands.Convert+ Darcs.Commands.Diff+ Darcs.Commands.Dist+ Darcs.Commands.Get+ Darcs.Commands.GZCRCs+ Darcs.Commands.Help+ Darcs.Commands.Init+ Darcs.Commands.MarkConflicts+ Darcs.Commands.Move+ Darcs.Commands.Optimize+ Darcs.Commands.Pull+ Darcs.Commands.Push+ Darcs.Commands.Put+ Darcs.Commands.Record+ Darcs.Commands.Remove+ Darcs.Commands.Repair+ Darcs.Commands.Replace+ Darcs.Commands.Revert+ Darcs.Commands.Rollback+ Darcs.Commands.Send+ Darcs.Commands.SetPref+ Darcs.Commands.Show+ Darcs.Commands.ShowAuthors+ Darcs.Commands.ShowBug+ Darcs.Commands.ShowContents+ Darcs.Commands.ShowFiles+ Darcs.Commands.ShowIndex+ Darcs.Commands.ShowRepo+ Darcs.Commands.ShowTags+ Darcs.Commands.Tag+ Darcs.Commands.TrackDown+ Darcs.Commands.TransferMode+ Darcs.Commands.Unrecord+ Darcs.Commands.Unrevert+ Darcs.Commands.WhatsNew+ Darcs.Compat+ Darcs.Diff+ Darcs.Email+ Darcs.External+ Darcs.FilePathMonad+ Darcs.Flags+ Darcs.Global+ Darcs.Hopefully+ Darcs.IO+ Darcs.Lock+ Darcs.Match+ Darcs.Witnesses.Ordered+ Darcs.Witnesses.WZipper+ Darcs.Patch+ Darcs.Patch.Apply+ Darcs.Patch.Bundle+ Darcs.Patch.Choices+ Darcs.Patch.Commute+ Darcs.Patch.Core+ Darcs.Patch.Depends+ Darcs.Patch.FileName+ Darcs.Patch.Info+ Darcs.Patch.Match+ Darcs.Patch.MatchData+ Darcs.Patch.Non+ Darcs.Patch.OldDate+ Darcs.Patch.Patchy+ Darcs.Patch.Permutations+ Darcs.Patch.Prim+ Darcs.Patch.Properties+ Darcs.Patch.Read+ Darcs.Patch.ReadMonads+ Darcs.Patch.Real+ Darcs.Patch.RegChars+ Darcs.Patch.Set+ Darcs.Patch.Show+ Darcs.Patch.Split+ Darcs.Patch.TouchesFiles+ Darcs.Patch.Viewing+ Darcs.Population+ Darcs.PopulationData+ Darcs.PrintPatch+ Darcs.ProgressPatches+ Darcs.RemoteApply+ Darcs.RepoPath+ Darcs.Repository+ Darcs.Repository.ApplyPatches+ Darcs.Repository.Cache+ Darcs.Repository.Checkpoint+ Darcs.Repository.DarcsRepo+ Darcs.Repository.Format+ Darcs.Repository.HashedIO+ Darcs.Repository.HashedRepo+ Darcs.Repository.Internal+ Darcs.Repository.LowLevel+ Darcs.Repository.Merge+ Darcs.Repository.InternalTypes+ Darcs.Repository.Motd+ Darcs.Repository.Prefs+ Darcs.Repository.Pristine+ Darcs.Repository.Repair+ Darcs.Repository.State+ Darcs.Resolution+ Darcs.RunCommand+ Darcs.Witnesses.Sealed+ Darcs.SelectChanges+ Darcs.Witnesses.Show+ Darcs.SignalHandler+ Darcs.Test+ Darcs.TheCommands+ Darcs.URL+ Darcs.Utils+ DateMatcher+ English+ Exec+ ByteStringUtils+ HTTP+ IsoDate+ Lcs+ Printer+ Progress+ Ratified+ SHA1+ Ssh+ URL+ Workaround - c-sources: src/atomic_create.c- src/fpstring.c- src/maybe_relink.c- src/umask.c- src/Crypt/sha2.c- cc-options: -D_REENTRANT+ other-modules: Version - if os(windows)- hs-source-dirs: src/win32- include-dirs: src/win32- other-modules: CtrlC- System.Posix- System.Posix.Files- System.Posix.IO- cpp-options: -DWIN32- build-depends: unix-compat >= 0.1.2+ c-sources: src/atomic_create.c+ src/fpstring.c+ src/maybe_relink.c+ src/umask.c+ src/Crypt/sha2.c+ cc-options: -D_REENTRANT - if os(solaris)- cc-options: -DHAVE_SIGINFO_H+ if os(windows)+ hs-source-dirs: src/win32+ include-dirs: src/win32+ other-modules: CtrlC+ System.Posix+ System.Posix.Files+ System.Posix.IO+ cpp-options: -DWIN32+ build-depends: unix-compat >= 0.1.2,+ regex-posix >= 0.94.4 && < 0.95 - build-depends: base < 4,- regex-compat >= 0.71 && < 0.94,- mtl >= 1.0 && < 1.2,- parsec >= 2.0 && < 3.1,- html == 1.0.*,- filepath == 1.1.*,- haskeline >= 0.6.1 && < 0.7,- hashed-storage == 0.4.13+ if os(solaris)+ cc-options: -DHAVE_SIGINFO_H - if !os(windows)- build-depends: unix >= 1.0 && < 2.5+ build-depends: base < 5,+ extensible-exceptions >= 0.1 && < 0.2,+ regex-compat >= 0.71 && < 0.94,+ mtl >= 1.0 && < 1.2,+ parsec >= 2.0 && < 3.1,+ html == 1.0.*,+ filepath == 1.1.*,+ haskeline >= 0.6.2.2 && < 0.7,+ hashed-storage >= 0.5.2 && < 0.6,+ tar == 0.3.* - build-depends: base >= 3,- bytestring >= 0.9.0 && < 0.10,- utf8-string == 0.3.*,+ if !os(windows)+ build-depends: unix >= 1.0 && < 2.5++ build-depends: base >= 3,+ bytestring >= 0.9.0 && < 0.10,+ text >= 0.3, old-time == 1.0.*, directory == 1.0.*, process == 1.0.*,@@ -344,71 +387,72 @@ random == 1.0.* - -- We need optimizations, regardless of what Hackage says- ghc-options: -Wall -O2 -funbox-strict-fields -fwarn-tabs- ghc-prof-options: -prof -auto-all+ -- We need optimizations, regardless of what Hackage says+ -- Note: "if true" works around a cabal bug with order of flag composition+ if true+ ghc-options: -Wall -O2 -funbox-strict-fields -fwarn-tabs - if flag(hpc)- ghc-prof-options: -fhpc+ if impl(ghc>=6.12)+ ghc-options: -fno-warn-unused-do-bind - if flag(curl)- extra-libraries: curl- includes: curl/curl.h- cpp-options: -DHAVE_CURL- c-sources: src/hscurl.c- cc-options: -DHAVE_CURL+ ghc-prof-options: -prof -auto-all - if flag(curl-pipelining)- -- curl 7.19.1 has bug-free pipelining- if !os(windows)- pkgconfig-depends: libcurl >= 7.19.1- cpp-options: -DCURL_PIPELINING -DCURL_PIPELINING_DEFAULT+ if flag(hpc)+ ghc-prof-options: -fhpc - if flag(http)- build-depends: network == 2.2.*,- HTTP >= 3000.0 && < 4000.1- cpp-options: -DHAVE_HTTP- x-have-http:+ 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(curl) && !flag(http)) || flag(deps-only)- buildable: False+ if flag(http)+ build-depends: network == 2.2.*,+ HTTP >= 3000.0 && < 4000.1+ cpp-options: -DHAVE_HTTP+ x-have-http: - if flag(mmap) && !os(windows)- build-depends: mmap >= 0.2 && < 0.5- cpp-options: -DHAVE_MMAP+ if (!flag(curl) && !flag(http)) || flag(deps-only)+ buildable: False - build-depends: zlib >= 0.5.1.0 && < 0.6.0.0+ if flag(mmap) && !os(windows)+ build-depends: mmap >= 0.5 && < 0.6+ cpp-options: -DHAVE_MMAP - -- The terminfo package cannot be built on Windows.- if flag(terminfo) && !os(windows)- build-depends: terminfo == 0.3.*- cpp-options: -DHAVE_TERMINFO+ build-depends: zlib >= 0.5.1.0 && < 0.6.0.0 - if flag(color)- x-use-color:+ -- The terminfo package cannot be built on Windows.+ if flag(terminfo) && !os(windows)+ build-depends: terminfo == 0.3.*+ cpp-options: -DHAVE_TERMINFO - extensions:- CPP,- ForeignFunctionInterface,- BangPatterns,- PatternGuards,- MagicHash,- UndecidableInstances,- DeriveDataTypeable,- GADTs,- ImpredicativeTypes,- TypeOperators,- ExistentialQuantification,- FlexibleContexts,- FlexibleInstances,- ScopedTypeVariables,- KindSignatures,- TypeSynonymInstances,- Rank2Types,- RankNTypes,- GeneralizedNewtypeDeriving,- MultiParamTypeClasses+ if flag(color)+ x-use-color: + extensions:+ CPP,+ ForeignFunctionInterface,+ BangPatterns,+ PatternGuards,+ MagicHash,+ UndecidableInstances,+ DeriveDataTypeable,+ GADTs,+ TypeOperators,+ ExistentialQuantification,+ FlexibleContexts,+ FlexibleInstances,+ ScopedTypeVariables,+ -- PatternSignatures is needed for GHC 6.8+ PatternSignatures,+ KindSignatures,+ TypeSynonymInstances,+ Rank2Types,+ RankNTypes,+ GeneralizedNewtypeDeriving,+ MultiParamTypeClasses+ -- ---------------------------------------------------------------------- -- darcs itself -- ----------------------------------------------------------------------@@ -424,7 +468,13 @@ src/Crypt/sha2.c -- We need optimizations, regardless of what Hackage says- ghc-options: -Wall -O2 -funbox-strict-fields+ -- Note: "if true" works around a cabal bug with order of flag composition+ if true+ ghc-options: -Wall -O2 -funbox-strict-fields -fwarn-tabs++ if impl(ghc>=6.12)+ ghc-options: -fno-warn-unused-do-bind+ ghc-prof-options: -prof -auto-all if flag(threaded) ghc-options: -threaded@@ -447,26 +497,29 @@ Preproc cpp-options: -DWIN32 c-sources: src/win32/send_email.c- build-depends: unix-compat >= 0.1.2+ build-depends: unix-compat >= 0.1.2,+ regex-posix >= 0.94.4 && < 0.95 if os(solaris) cc-options: -DHAVE_SIGINFO_H - build-depends: base < 4,+ build-depends: base < 5,+ extensible-exceptions >= 0.1 && < 0.2, regex-compat >= 0.71 && < 0.94, mtl >= 1.0 && < 1.2, parsec >= 2.0 && < 3.1, html == 1.0.*, filepath == 1.1.*,- haskeline >= 0.6.1 && < 0.7,- hashed-storage == 0.4.13+ haskeline >= 0.6.2.2 && < 0.7,+ hashed-storage >= 0.5.2 && < 0.6,+ tar == 0.3.* if !os(windows) build-depends: unix >= 1.0 && < 2.5 build-depends: base >= 3, bytestring >= 0.9.0 && < 0.10,- utf8-string == 0.3.*,+ text >= 0.3, old-time == 1.0.*, directory == 1.0.*, process == 1.0.*,@@ -481,12 +534,6 @@ c-sources: src/hscurl.c cc-options: -DHAVE_CURL - if flag(curl-pipelining)- -- curl 7.19.1 has bug-free pipelining- if !os(windows)- pkgconfig-depends: libcurl >= 7.19.1- cpp-options: -DCURL_PIPELINING -DCURL_PIPELINING_DEFAULT- if flag(http) build-depends: network == 2.2.*, HTTP >= 3000.0 && < 4000.1@@ -497,7 +544,7 @@ buildable: False if flag(mmap) && !os(windows)- build-depends: mmap >= 0.2 && < 0.5+ build-depends: mmap >= 0.5 && < 0.6 cpp-options: -DHAVE_MMAP build-depends: zlib >= 0.5.1.0 && < 0.6.0.0@@ -519,12 +566,13 @@ UndecidableInstances, DeriveDataTypeable, GADTs,- ImpredicativeTypes, TypeOperators, ExistentialQuantification, FlexibleContexts, FlexibleInstances, ScopedTypeVariables,+ -- PatternSignatures is needed for GHC 6.8+ PatternSignatures, KindSignatures, TypeSynonymInstances, Rank2Types,@@ -537,33 +585,14 @@ -- ---------------------------------------------------------------------- Executable unit- main-is: unit.lhs- hs-source-dirs: src- include-dirs: src- c-sources: src/atomic_create.c- src/fpstring.c- src/maybe_relink.c- src/umask.c- src/Crypt/sha2.c- -- list all unit test modules not exported by libdarcs; otherwise Cabal won't- -- include them in the tarball- other-modules: Darcs.Test.Email- Darcs.Test.Patch.Check- Darcs.Test.Patch.QuickCheck- Darcs.Test.Patch.Test- Darcs.Test.Patch.Unit-- -- We need optimizations, regardless of what Hackage says- ghc-options: -Wall -O2 -funbox-strict-fields- ghc-prof-options: -prof -auto-all- if flag(threaded)- ghc-options: -threaded+ main-is: unit.hs if !flag(test) buildable: False else buildable: True- build-depends: base < 4,+ build-depends: base < 5,+ extensible-exceptions >= 0.1 && < 0.2, regex-compat >= 0.71 && < 0.94, mtl >= 1.0 && < 1.2, parsec >= 2.0 && < 3.1,@@ -576,66 +605,105 @@ test-framework-quickcheck2 >= 0.2.2 - cc-options: -D_REENTRANT+ hs-source-dirs: src+ include-dirs: src+ c-sources: src/atomic_create.c+ src/fpstring.c+ src/maybe_relink.c+ src/umask.c+ src/Crypt/sha2.c+ -- list all unit test modules not exported by libdarcs; otherwise Cabal won't+ -- include them in the tarball+ other-modules: Darcs.Test.Email+ Darcs.Test.Patch.Check+ Darcs.Test.Patch.Info+ Darcs.Test.Patch.QuickCheck+ Darcs.Test.Patch.Test+ Darcs.Test.Patch.Unit+ Darcs.Test.Unit - if os(windows)- hs-source-dirs: src/win32- include-dirs: src/win32- other-modules: CtrlC- System.Posix- System.Posix.Files- System.Posix.IO- cpp-options: -DWIN32- c-sources: src/win32/send_email.c- build-depends: unix-compat >= 0.1.2+ -- We need optimizations, regardless of what Hackage says+ -- Note: "if true" works around a cabal bug with order of flag composition+ if true+ ghc-options: -Wall -O2 -funbox-strict-fields -fwarn-tabs - if os(solaris)- cc-options: -DHAVE_SIGINFO_H+ if impl(ghc>=6.12)+ ghc-options: -fno-warn-unused-do-bind - if !os(windows)- build-depends: unix >= 1.0 && < 2.5+ ghc-prof-options: -prof -auto-all+ if flag(threaded)+ ghc-options: -threaded - build-depends: base >= 3,- bytestring >= 0.9.0 && < 0.10,+ cc-options: -D_REENTRANT++ if os(windows)+ hs-source-dirs: src/win32+ include-dirs: src/win32+ other-modules: CtrlC+ System.Posix+ System.Posix.Files+ System.Posix.IO+ cpp-options: -DWIN32+ c-sources: src/win32/send_email.c+ build-depends: unix-compat >= 0.1.2,+ regex-posix >= 0.94.4 && < 0.95++ if os(solaris)+ cc-options: -DHAVE_SIGINFO_H++ if !os(windows)+ build-depends: unix >= 1.0 && < 2.5++ build-depends: base >= 3,+ bytestring >= 0.9.0 && < 0.10,+ haskeline >= 0.6.2.2 && < 0.7,+ text >= 0.3, old-time == 1.0.*, directory == 1.0.*, process == 1.0.*, containers >= 0.1 && < 0.4, array >= 0.1 && < 0.4,+ hashed-storage >= 0.5.2 && < 0.6, random == 1.0.* - if flag(mmap) && !os(windows)- build-depends: mmap >= 0.2 && < 0.5- cpp-options: -DHAVE_MMAP+ if flag(mmap) && !os(windows)+ build-depends: mmap >= 0.5 && < 0.6+ cpp-options: -DHAVE_MMAP - build-depends: zlib >= 0.5.1.0 && < 0.6.0.0+ build-depends: zlib >= 0.5.1.0 && < 0.6.0.0 - -- The terminfo package cannot be built on Windows.- if flag(terminfo) && !os(windows)- build-depends: terminfo == 0.3.*- cpp-options: -DHAVE_TERMINFO+ -- The terminfo package cannot be built on Windows.+ if flag(terminfo) && !os(windows)+ build-depends: terminfo == 0.3.*+ cpp-options: -DHAVE_TERMINFO - if flag(color)- x-use-color:+ if flag(http)+ build-depends: network == 2.2.*,+ HTTP >= 3000.0 && < 4000.1 - extensions:- CPP,- ForeignFunctionInterface,- BangPatterns,- PatternGuards,- MagicHash,- UndecidableInstances,- DeriveDataTypeable,- GADTs,- TypeOperators,- ExistentialQuantification,- FlexibleContexts,- FlexibleInstances,- ScopedTypeVariables,- KindSignatures,- TypeSynonymInstances,- Rank2Types,- RankNTypes,- GeneralizedNewtypeDeriving,- MultiParamTypeClasses- OverlappingInstances+ if flag(color)+ x-use-color:++ extensions:+ CPP,+ ForeignFunctionInterface,+ BangPatterns,+ PatternGuards,+ MagicHash,+ UndecidableInstances,+ DeriveDataTypeable,+ GADTs,+ TypeOperators,+ ExistentialQuantification,+ FlexibleContexts,+ FlexibleInstances,+ ScopedTypeVariables,+ -- PatternSignatures is needed for GHC 6.8+ PatternSignatures,+ KindSignatures,+ TypeSynonymInstances,+ Rank2Types,+ RankNTypes,+ GeneralizedNewtypeDeriving,+ MultiParamTypeClasses+ OverlappingInstances
doc/darcs.css view
@@ -28,6 +28,10 @@ height: 50px; } +BLOCKQUOTE.testimonial {+ font-style: italic;+}+ PRE { background: #eeeeee; border: 1px solid #888888;
release/distributed-context view
@@ -1,1 +1,1 @@-Just "\nContext:\n\n[TAG 2.4.4\nEric Kow <kowey@darcs.net>**20100515090819\n Ignore-this: 7d1a0e6a17c2be314f6ab1607bbcac13\n] \n"+Just "\nContext:\n\n[TAG 2.5\nReinier Lamers <tux_rocker@reinier.de>**20101024151805\n Ignore-this: 1561ce30bfb1950a440c03371e0e2f20\n] \n"
src/ByteStringUtils.hs view
@@ -12,18 +12,22 @@ -- Stability : experimental -- Portability : portable ----- GZIp and MMap IO for ByteStrings, and miscellaneous functions for Data.ByteString+-- GZIp and MMap IO for ByteStrings, encoding utilities, and miscellaneous+-- functions for Data.ByteString -- module ByteStringUtils ( unsafeWithInternals,+ unpackPSFromUTF8,+ packStringToUTF8, -- IO with mmap or gzip gzReadFilePS, mmapFilePS, gzWriteFilePS, gzWriteFilePSs,+ gzReadStdin, -- gzip handling isGZFile,@@ -40,13 +44,20 @@ breakLastPS, substrPS, readIntPS,- is_funky,+ isFunky, fromHex2PS, fromPS2Hex, betweenLinesPS,- break_after_nth_newline,- break_before_nth_newline,- intercalate+ breakAfterNthNewline,+ breakBeforeNthNewline,+ intercalate,++ -- encoding and unicode utilities+ decodeLocale,+ encodeLocale,+ encodeLatin1,+ decodeString,+ utf8ToLocale ) where import Prelude hiding ( catch )@@ -57,10 +68,12 @@ import Data.ByteString.Internal (fromForeignPtr) #if defined (HAVE_MMAP)-import Control.Exception ( catch )+import Control.Exception.Extensible ( catch, SomeException ) #endif import System.IO import System.IO.Unsafe ( unsafePerformIO )+import System.Console.Haskeline ( runInputT, defaultSettings )+import System.Console.Haskeline.Encoding ( decode, encode ) import Foreign.Storable ( peekElemOff, peek ) import Foreign.Marshal.Array ( advancePtr )@@ -70,6 +83,9 @@ import Data.Char ( ord, isSpace ) import Data.Word ( Word8 ) import Data.Int ( Int32 )+import qualified Data.Text as T ( pack, unpack )+import Data.Text.Encoding ( encodeUtf8, decodeUtf8With )+import Data.Text.Encoding.Error ( lenientDecode ) import Control.Monad ( when ) import Foreign.Ptr ( plusPtr, Ptr )@@ -114,6 +130,17 @@ readIntPS = BC.readInt . BC.dropWhile isSpace -- -----------------------------------------------------------------------------+-- Destructor functions (taking PackedStrings apart)++-- | Decodes a 'ByteString' containing UTF-8 to a 'String'. Decoding errors are+-- flagged with the U+FFFD character.+unpackPSFromUTF8 :: B.ByteString -> String+unpackPSFromUTF8 = T.unpack . decodeUtf8With lenientDecode++packStringToUTF8 :: String -> B.ByteString+packStringToUTF8 = encodeUtf8 . T.pack++-- ----------------------------------------------------------------------------- -- List-mimicking functions for PackedStrings {-# INLINE ifHeadThenTail #-}@@ -154,7 +181,7 @@ -- | 'dropSpace' efficiently returns the 'ByteString' argument with -- white space Chars removed from the front. It is more efficient than -- calling dropWhile for removing whitespace. I.e.--- +-- -- > dropWhile isSpace == dropSpace -- dropSpace :: B.ByteString -> B.ByteString@@ -165,7 +192,7 @@ -- | 'breakSpace' returns the pair of ByteStrings when the argument is -- broken at the first whitespace byte. I.e.--- +-- -- > break isSpace == breakSpace -- breakSpace :: B.ByteString -> (B.ByteString,B.ByteString)@@ -180,9 +207,9 @@ ------------------------------------------------------------------------ -{-# INLINE is_funky #-}-is_funky :: B.ByteString -> Bool-is_funky ps = case BI.toForeignPtr ps of+{-# INLINE isFunky #-}+isFunky :: B.ByteString -> Bool+isFunky ps = case BI.toForeignPtr ps of (x,s,l) -> unsafePerformIO $ withForeignPtr x $ \p-> (/=0) `fmap` has_funky_char (p `plusPtr` s) (fromIntegral l)@@ -372,6 +399,20 @@ gzWriteFilePSs f pss = BL.writeFile f $ GZ.compress $ BL.fromChunks pss +-- | Read standard input, which may or may not be gzip compressed, directly+-- into a 'B.ByteString'.+gzReadStdin :: IO B.ByteString+gzReadStdin = do header <- B.hGet stdin 2+ rest <- B.hGetContents stdin+ let allStdin = B.concat [header,rest]+ return $+ if header /= BC.pack "\31\139"+ then allStdin+ else let decompress = fst . gzDecompress Nothing+ compressed = BL.fromChunks [allStdin]+ in+ B.concat $ decompress compressed+ -- ----------------------------------------------------------------------------- -- mmapFilePS @@ -390,7 +431,7 @@ #ifdef HAVE_MMAP mmapFilePS f = do x <- mmapFileByteString f Nothing- `catch` (\_ -> do+ `catch` (\(_ :: SomeException) -> do size <- fileSize `fmap` getSymbolicLinkStatus f if size == 0 then return B.empty@@ -443,12 +484,12 @@ _ -> Nothing -- ---------------------------------------------------------------------------- break_after_nth_newline+-- breakAfterNthNewline -break_after_nth_newline :: Int -> B.ByteString+breakAfterNthNewline :: Int -> B.ByteString -> Maybe (B.ByteString, B.ByteString)-break_after_nth_newline 0 the_ps | B.null the_ps = Just (B.empty, B.empty)-break_after_nth_newline n the_ps =+breakAfterNthNewline 0 the_ps | B.null the_ps = Just (B.empty, B.empty)+breakAfterNthNewline n the_ps = case BI.toForeignPtr the_ps of (fp,the_s,l) -> unsafePerformIO $ withForeignPtr fp $ \p ->@@ -465,12 +506,12 @@ findit n the_s -- ---------------------------------------------------------------------------- break_before_nth_newline+-- breakBeforeNthNewline -break_before_nth_newline :: Int -> B.ByteString -> (B.ByteString, B.ByteString)-break_before_nth_newline 0 the_ps+breakBeforeNthNewline :: Int -> B.ByteString -> (B.ByteString, B.ByteString)+breakBeforeNthNewline 0 the_ps | B.null the_ps = (B.empty, B.empty)-break_before_nth_newline n the_ps =+breakBeforeNthNewline n the_ps = case BI.toForeignPtr the_ps of (fp,the_s,l) -> unsafePerformIO $ withForeignPtr fp $ \p ->@@ -486,3 +527,31 @@ nl = BI.c2w '\n' end = the_s + l findit n the_s++-- | Decode a ByteString to a String according to the current locale+-- unsafePerformIO in the locale function is ratified by the fact that GHC 6.12+-- and above also supply locale conversion with functions with a pure type.+-- Unrecognized byte sequences in the input are skipped.+decodeLocale :: B.ByteString -> String+decodeLocale = unsafePerformIO . runInputT defaultSettings . decode++-- | Encode a String to a ByteString with latin1 (i.e., the values of the+-- characters become the values of the bytes; if a character value is greater+-- than 255, its byte becomes the character value modulo 256)+encodeLatin1 :: String -> B.ByteString+encodeLatin1 = B.pack . (map (fromIntegral . ord))++-- | Encode a String to a ByteString according to the current locale+encodeLocale :: String -> B.ByteString+encodeLocale = unsafePerformIO . runInputT defaultSettings . encode++-- | Take a @String@ that represents byte values and re-decode it acording to+-- the current locale.+decodeString :: String -> String+decodeString = decodeLocale . encodeLatin1++-- | Convert a bytestring representing a text from UTF-8 to the current locale+utf8ToLocale :: B.ByteString -> B.ByteString+utf8ToLocale bs = encodeLocale string+ where string = unpackPSFromUTF8 bs+
src/CommandLine.hs view
@@ -51,7 +51,7 @@ -- 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) +commandline ftable = consumeAll (do l <- sepEndBy1 (arg ftable) (try separator) redir <- formatRedir spaces@@ -90,7 +90,7 @@ consumeAll p = do r <- p eof return r- + separator :: Parser () separator = do skipMany1 space @@ -99,7 +99,7 @@ Just (_,s) -> s Nothing -> error "impossible" --- | parse a commandline returning a list of strings +-- | parse a commandline returning a list of strings -- (intended to be used as argv) and a bool value which -- specifies if the command expects input on stdin -- format specifiers with a mapping in ftable are accepted@@ -111,7 +111,7 @@ urlEncode :: String -> String urlEncode s = concatMap escapeC s where escapeC x = if allowed x then [x] else '%':(intToHex $ ord x)- intToHex i = map intToDigit [i `div` 16, i `mod` 16] + intToHex i = map intToDigit [i `div` 16, i `mod` 16] allowed x = x `elem` ['a' .. 'z'] ++ ['A' .. 'Z'] ++ ['0' .. '9'] ++ "!'()*-.~"
src/Darcs/ArgumentDefaults.lhs view
@@ -16,11 +16,12 @@ % Boston, MA 02110-1301, USA. \begin{code}-module Darcs.ArgumentDefaults ( get_default_flags ) where+module Darcs.ArgumentDefaults ( getDefaultFlags ) where import Data.Maybe ( catMaybes, listToMaybe, mapMaybe ) import Darcs.Arguments ( DarcsFlag,- DarcsOption( DarcsArgOption, DarcsNoArgOption, DarcsMultipleChoiceOption ),+ atomicOptions, DarcsAtomicOption( .. ), DarcsOption ( .. ),+ applyDefaults, arein, isin ) import Darcs.Commands ( CommandControl( CommandData ), commandAlloptions )@@ -42,7 +43,7 @@ option without the ``\verb!--!'', i.e.\ \verb!verbose! rather than \verb!--verbose!. Finally, the \verb!VALUE! option can be omitted if the flag is one such as \verb!verbose! that doesn't involve a value.-If the value has spaces in it, use single quotes, not double quotes, to surround it. +If the value has spaces in it, use single quotes, not double quotes, to surround it. Each line only takes one flag. To set multiple defaults for the same command (or for \verb!ALL! commands), use multiple lines. @@ -110,59 +111,58 @@ \end{verbatim} Also, a global preferences file can be created with the name-\verb!.darcs/defaults! in your home directory, on MS Windows~\ref{ms_win}. +\verb!.darcs/defaults! in your home directory, on MS Windows~\ref{ms_win}. Options present there will be added to the repository-specific preferences. If they conflict with repository-specific options, the repository-specific ones will take precedence. \begin{code}-get_default_flags :: String -> [DarcsOption] -> [DarcsFlag] -> IO [DarcsFlag]-get_default_flags com com_opts already = do- repo_defs <- default_content $ getPreflist "defaults"- global_defs <- default_content $ getGlobal "defaults"- let repo_flags = get_flags_from com com_opts already repo_defs- global_flags = get_flags_from com com_opts+getDefaultFlags :: String -> [DarcsOption] -> [DarcsFlag] -> IO [DarcsFlag]+getDefaultFlags com com_opts already = do+ repo_defs <- defaultContent $ getPreflist "defaults"+ global_defs <- defaultContent $ getGlobal "defaults"+ let repo_flags = getFlagsFrom com com_opts already repo_defs+ global_flags = getFlagsFrom com com_opts (already++repo_flags) global_defs- return $ repo_flags ++ global_flags+ return $ applyDefaults com_opts -- hard-coded defaults (respects user preferences)+ $ repo_flags ++ global_flags -- user preferences -get_flags_from :: String -> [DarcsOption] -> [DarcsFlag] -> [(String,String,String)] -> [DarcsFlag]-get_flags_from com com_opts already defs =+getFlagsFrom :: String -> [DarcsOption] -> [DarcsFlag] -> [(String,String,String)] -> [DarcsFlag]+getFlagsFrom com com_opts already defs = options_for com_defs com_opts com_opts ++ options_for all_defs com_opts all_opts where com_defs = filter (\ (c,_,_) -> c == com) defs all_defs = filter (\ (c,_,_) -> c == "ALL") defs- options_for d o ao = concatMap (find_option o ao already) d+ options_for d o ao = concatMap (findOption o ao already) d all_opts = concatMap get_opts commandControlList get_opts (CommandData c) = let (o1, o2) = commandAlloptions c in o1 ++ o2 get_opts _ = [] -find_option :: [DarcsOption] -> [DarcsOption] -> [DarcsFlag] -> (String,String,String) -> [DarcsFlag]-find_option opts all_opts already (c, f, d) =+findOption :: [DarcsOption] -> [DarcsOption] -> [DarcsFlag] -> (String,String,String) -> [DarcsFlag]+findOption opts all_opts already (c, f, d) = if null $ mapMaybe choose_option all_opts then error $ "Bad default option: command '"++c++"' has no option '"++f++"'." else concat $ mapMaybe choose_option opts- where choose_option (DarcsNoArgOption _ fls o _)- | o `elem` already = Just []+ where choose_atomic_option (DarcsNoArgOption _ fls o _) | f `elem` fls = if null d then Just [o] else error $ "Bad default option: '"++f ++"' takes no argument, but '"++d ++"' argument given."- choose_option (DarcsArgOption _ fls o _ _)- | o `isin` already = Just []+ choose_atomic_option ao@(DarcsArgOption _ fls o _ _) | f `elem` fls = if null d then error $ "Bad default option: '"++f ++"' requires an argument, but no " ++"argument given." else Just [o d]- choose_option (DarcsMultipleChoiceOption os)- | os `arein` already = Just []- | otherwise = listToMaybe $ mapMaybe choose_option os- choose_option _ = Nothing+ choose_atomic_option _ = Nothing+ choose_option o+ | o `arein` already = Just []+ | otherwise = listToMaybe $ mapMaybe choose_atomic_option $ atomicOptions o -default_content :: IO [String] -> IO [(String,String,String)]-default_content = fmap (catMaybes . map (doline.words))+defaultContent :: IO [String] -> IO [(String,String,String)]+defaultContent = fmap (catMaybes . map (doline.words)) where doline (c:a:r) = Just (c, drop_dashdash a, unwords r) doline _ = Nothing drop_dashdash ('-':'-':a) = a
src/Darcs/Arguments.lhs view
@@ -21,17 +21,17 @@ #include "gadts.h" -module Darcs.Arguments ( DarcsFlag( .. ), flagToString,+module Darcs.Arguments ( DarcsFlag( .. ), flagToString, applyDefaults, nubOptions, maxCount, isin, arein, definePatches, defineChanges,- fixFilePathOrStd, fixUrl,+ fixFilePathOrStd, fixUrl, fixUrlFlag, fixSubPaths, areFileArgs,- DarcsOption( .. ), optionFromDarcsoption,+ DarcsAtomicOption( .. ), atomicOptions,+ DarcsOption( .. ), optionFromDarcsOption, help, listOptions, listFiles, anyVerbosity, disable, restrictPaths, notest, test, workingRepoDir,- testByDefault, remoteRepo, leaveTestDir, possiblyRemoteRepoDir, getRepourl,@@ -45,7 +45,7 @@ recursive, inventoryChoices, getInventoryChoices, upgradeFormat, askdeps, ignoretimes, lookforadds,- askLongComment, sendmailCmd,+ askLongComment, keepDate, sendmailCmd, environmentHelpSendmail, sign, verify, editDescription, reponame, creatorhash,@@ -73,7 +73,7 @@ matchSeveralOrLast, setDefault, fancyMoveAdd,- setScriptsExecutableOption,+ setScriptsExecutableOption, bisect, sibling, flagsToSiblings, relink, relinkPristine, nolinks, files, directories, pending, posthookCmd, posthookPrompt,@@ -85,7 +85,8 @@ patchSelectFlag, networkOptions, noCache, allowUnrelatedRepos,- checkOrRepair, justThisRepo, optimizePristine+ checkOrRepair, justThisRepo, optimizePristine,+ optimizeHTTP, getOutput ) where import System.Console.GetOpt import System.Directory ( doesDirectoryExist )@@ -95,9 +96,8 @@ import Storage.Hashed.Tree( list, expand, emptyTree ) import Data.List ( (\\), nub )-import Data.Maybe ( fromMaybe, listToMaybe )+import Data.Maybe ( fromMaybe, listToMaybe, mapMaybe ) import System.Exit ( ExitCode(ExitSuccess), exitWith )-import Data.Maybe ( catMaybes ) import Control.Monad ( when, unless ) import Control.Applicative( (<$>) ) import Data.Char ( isDigit )@@ -110,25 +110,25 @@ import Darcs.Hopefully ( PatchInfoAnd, info, hopefullyM ) import Darcs.Patch ( RepoPatch, Patchy, showNicely, description, xmlSummary )-import Darcs.Patch.Info ( to_xml )+import Darcs.Patch.Info ( toXml ) import Darcs.Witnesses.Ordered ( FL, mapFL ) import qualified Darcs.Patch ( summary ) import Darcs.Utils ( askUser, maybeGetEnv, firstNotBlank, firstJustIO, withCurrentDirectory ) import Darcs.Repository.Prefs ( getPreflist, getGlobal )-import Darcs.Repository.State ( restrictBoring, readRecordedAndPending )-import Darcs.URL ( is_file )+import Darcs.Repository.State ( restrictBoring, applyTreeFilter, readRecordedAndPending )+import Darcs.URL ( isFile ) import Darcs.RepoPath ( AbsolutePath, AbsolutePathOrStd, SubPath, toFilePath,- makeSubPathOf, simpleSubPath,- ioAbsolute, ioAbsoluteOrStd,+ makeSubPathOf, ioAbsolute, ioAbsoluteOrStd, makeAbsolute, makeAbsoluteOrStd, rootDirectory )-import Darcs.Patch.MatchData ( patch_match )-import Darcs.Flags ( DarcsFlag(..), maxCount )+import Darcs.Patch.MatchData ( patchMatch )+import Darcs.Flags ( DarcsFlag(..), maxCount, defaultFlag ) import Darcs.Repository ( withRepository ) import Darcs.Global ( darcsdir )-import Printer ( Doc, putDocLn, text, vsep, ($$), vcat, insert_before_lastline,+import Darcs.Lock ( writeLocaleFile )+import Printer ( Doc, putDocLn, text, vsep, ($$), vcat, insertBeforeLastline, prefix )-import URL ( pipeliningEnabledByDefault )+import ByteStringUtils ( decodeString ) #include "impossible.h" data FlagContent = NoContent | AbsoluteContent AbsolutePath | AbsoluteOrStdContent AbsolutePathOrStd | StringContent String@@ -226,6 +226,8 @@ getContent EditLongComment = NoContent getContent NoEditLongComment = NoContent getContent PromptLongComment = NoContent+getContent KeepDate = NoContent+getContent NoKeepDate = NoContent getContent AllowConflicts = NoContent getContent MarkConflicts = NoContent getContent NoAllowConflicts = NoContent@@ -276,6 +278,7 @@ getContent Disable = NoContent getContent SetScriptsExecutable = NoContent getContent DontSetScriptsExecutable = NoContent+getContent Bisect = NoContent getContent UseHashedInventory = NoContent getContent UseOldFashionedInventory = NoContent getContent UseFormat2 = NoContent@@ -302,7 +305,6 @@ getContent AskPrehook = NoContent getContent StoreInMemory = NoContent getContent ApplyOnDisk = NoContent-getContent HTTPPipelining = NoContent getContent NoHTTPPipelining = NoContent getContent NoCache = NoContent getContent NullFlag = NoContent@@ -313,6 +315,7 @@ getContent Repair = NoContent getContent JustThisRepo = NoContent getContent OptimizePristine = NoContent+getContent OptimizeHTTP = NoContent getContentString :: DarcsFlag -> Maybe String getContentString f =@@ -343,23 +346,15 @@ AbsoluteOrStdContent s -> f == x s _ -> False -isin :: (String->DarcsFlag) -> [DarcsFlag] -> Bool-f `isin` fs = any (`isa` f) fs+isin :: DarcsAtomicOption -> [DarcsFlag] -> Bool+(DarcsNoArgOption _ _ f _) `isin` fs = f `elem` fs+(DarcsArgOption _ _ f _ _) `isin` fs = any (`isa` f) fs+(DarcsAbsPathOption _ _ f _ _) `isin` fs = any (`isAnAbsolute` f) fs+(DarcsAbsPathOrStdOption _ _ f _ _) `isin` fs = any (`isAnAbsoluteOrStd` f) fs+(DarcsOptAbsPathOption _ _ _ f _ _) `isin` fs = any (`isAnAbsolute` f) fs -arein :: [DarcsOption] -> [DarcsFlag] -> Bool-(DarcsNoArgOption _ _ f _ : dos') `arein` fs- = f `elem` fs || dos' `arein` fs-(DarcsArgOption _ _ f _ _ : dos') `arein` fs- = f `isin` fs || dos' `arein` fs-(DarcsAbsPathOption _ _ f _ _ : dos') `arein` fs- = any (`isAnAbsolute` f) fs || dos' `arein` fs-(DarcsAbsPathOrStdOption _ _ f _ _ : dos') `arein` fs- = any (`isAnAbsoluteOrStd` f) fs || dos' `arein` fs-(DarcsOptAbsPathOption _ _ _ f _ _ : dos') `arein` fs- = any (`isAnAbsolute` f) fs || dos' `arein` fs-(DarcsMultipleChoiceOption os: dos') `arein` fs- = os `arein` fs || dos' `arein` fs-[] `arein` _ = False+arein :: DarcsOption -> [DarcsFlag] -> Bool+o `arein` fs = any (`isin` fs) (atomicOptions o) -- | A type for darcs' options. The value contains the command line -- switch(es) for the option, a help string, and a function to build a@@ -369,7 +364,7 @@ -- switches, optDescr the description of the option, and argDescr the description -- of its argument, if any. mkFlag is a function which makes a @DarcsFlag@ from -- the arguments of the option.-data DarcsOption+data DarcsAtomicOption = DarcsArgOption [Char] [String] (String->DarcsFlag) String String -- ^ @DarcsArgOption shortSwitches longSwitches mkFlag ArgDescr OptDescr@ -- The constructor for options with a string argument, such as@@ -395,25 +390,71 @@ -- ^ @DarcsNoArgOption shortSwitches longSwitches mkFlag optDescr@ -- The constructon fon options with no arguments. - | DarcsMultipleChoiceOption [DarcsOption]+data DarcsOption+ = DarcsSingleOption DarcsAtomicOption+ | DarcsMultipleChoiceOption [DarcsAtomicOption] -- ^ A constructor for grouping related options together, such as -- @--hashed@, @--darcs-2@ and @--old-fashioned-inventory@. -optionFromDarcsoption :: AbsolutePath -> DarcsOption -> [OptDescr DarcsFlag]-optionFromDarcsoption _ (DarcsNoArgOption a b c h) = [Option a b (NoArg c) h]-optionFromDarcsoption _ (DarcsArgOption a b c n h) = [Option a b (ReqArg c n) h]-optionFromDarcsoption wd (DarcsMultipleChoiceOption os) = concatMap (optionFromDarcsoption wd) os-optionFromDarcsoption wd (DarcsAbsPathOrStdOption a b c n h) = [Option a b (ReqArg (c . makeAbsoluteOrStd wd) n) h]-optionFromDarcsoption wd (DarcsAbsPathOption a b c n h) = [Option a b (ReqArg (c . makeAbsolute wd) n) h]-optionFromDarcsoption wd (DarcsOptAbsPathOption a b d c n h) = [Option a b (OptArg (c . makeAbsolute wd . fromMaybe d) n) h]+ | DarcsMutuallyExclusive [DarcsAtomicOption] -- choices+ ([DarcsFlag] -> [DarcsFlag]) -- setter +type NoArgPieces = (DarcsFlag -> String -> DarcsAtomicOption, DarcsFlag , String)++mkMutuallyExclusive :: [NoArgPieces] -- ^ before+ -> NoArgPieces -- ^ default+ -> [NoArgPieces] -- ^ after+ -> DarcsOption+mkMutuallyExclusive os1 od_ os2 =+ DarcsMutuallyExclusive (map option (os1 ++ (od : os2)))+ (defaultFlag (map flag (os1 ++ os2)) (flag od))+ where+ od = third (++ " [DEFAULT]") od_+ flag (_,f,_) = f+ option (x,y,z) = x y z+ third f (x,y,z) = (x,y,f z)++nubOptions [] opts = opts+nubOptions (DarcsMutuallyExclusive ch _:options) opts = nubOptions options $ collapse opts+ where collapse (x:xs) | x `elem` flags ch = x : clear xs+ | otherwise = x : collapse xs+ collapse [] = []+ clear (x:xs) | x `elem` flags ch = clear xs+ | otherwise = x : clear xs+ clear [] = []+ flags (DarcsNoArgOption _ _ fl _:xs) = fl : flags xs+ flags (_:xs) = flags xs+ flags [] = []+nubOptions (_:options) opts = nubOptions options opts++applyDefaults :: [DarcsOption] -> [DarcsFlag] -> [DarcsFlag]+applyDefaults opts = foldr (.) id (mapMaybe getSetter opts)+ where+ getSetter (DarcsMutuallyExclusive _ f) = Just f+ getSetter _ = Nothing++optionFromDarcsAtomicOption :: AbsolutePath -> DarcsAtomicOption -> OptDescr DarcsFlag+optionFromDarcsAtomicOption _ (DarcsNoArgOption a b c h) = Option a b (NoArg c) h+optionFromDarcsAtomicOption _ (DarcsArgOption a b c n h) = Option a b (ReqArg c n) h+optionFromDarcsAtomicOption wd (DarcsAbsPathOrStdOption a b c n h) =+ Option a b (ReqArg (c . makeAbsoluteOrStd wd) n) h+optionFromDarcsAtomicOption wd (DarcsAbsPathOption a b c n h) =+ Option a b (ReqArg (c . makeAbsolute wd) n) h+optionFromDarcsAtomicOption wd (DarcsOptAbsPathOption a b d c n h) =+ Option a b (OptArg (c . makeAbsolute wd . fromMaybe d) n) h++atomicOptions :: DarcsOption -> [DarcsAtomicOption]+atomicOptions (DarcsSingleOption x) = [x]+atomicOptions (DarcsMultipleChoiceOption xs) = xs+atomicOptions (DarcsMutuallyExclusive xs _) = xs++optionFromDarcsOption :: AbsolutePath -> DarcsOption -> [OptDescr DarcsFlag]+optionFromDarcsOption wd = map (optionFromDarcsAtomicOption wd) . atomicOptions+ -- | 'concat_option' creates a DarcsMultipleChoiceOption from a list of -- option, flattening any DarcsMultipleChoiceOption in the list. concatOptions :: [DarcsOption] -> DarcsOption-concatOptions os = DarcsMultipleChoiceOption $ concatMap from_option os- where- from_option (DarcsMultipleChoiceOption xs) = xs- from_option x = [x]+concatOptions = DarcsMultipleChoiceOption . concatMap atomicOptions extractFixPath :: [DarcsFlag] -> Maybe (AbsolutePath, AbsolutePath) extractFixPath [] = Nothing@@ -431,11 +472,25 @@ Nothing -> bug "Can't fix path in fixFilePathOrStd" Just (_,o) -> withCurrentDirectory o $ ioAbsoluteOrStd f +fixUrlFlag :: [DarcsFlag] -> DarcsFlag -> IO DarcsFlag+fixUrlFlag opts (RemoteRepo f) = RemoteRepo `fmap` fixUrl opts f+fixUrlFlag _ f = return f+ fixUrl :: [DarcsFlag] -> String -> IO String-fixUrl opts f = if is_file f+fixUrl opts f = if isFile f then toFilePath `fmap` fixFilePath opts f else return f +-- | @fixSubPaths files@ returns the @SubPath@s for the paths in @files@ that+-- are inside the repository, preserving their order. Paths in @files@ that are+-- outside the repository directory are not in the result.+--+-- When converting a relative path to an absolute one, this function first tries+-- to interpret the relative path with respect to the current working directory.+-- If that fails, it tries to interpret it with respect to the repository+-- directory. Only when that fails does it omit the path from the result.+--+-- It is intended for validating file arguments to darcs commands. fixSubPaths :: [DarcsFlag] -> [FilePath] -> IO [SubPath] fixSubPaths flags fs = withCurrentDirectory o $@@ -451,7 +506,11 @@ fixit p = do ap <- ioAbsolute p case makeSubPathOf r ap of Just sp -> return $ Right sp- Nothing -> return $ maybe (Left p) Right $ simpleSubPath p+ Nothing -> withCurrentDirectory r $ do+ absolutePathByRepodir <- ioAbsolute p+ return $ case makeSubPathOf r absolutePathByRepodir of+ Just sp -> Right sp+ Nothing -> Left p partitionEither :: [Either a b] -> ([b],[a]) partitionEither es = ( [b | Right b <- es]@@ -463,32 +522,20 @@ -- | 'list_option' is an option which lists the command's arguments listOptions :: DarcsOption-listOptions = DarcsNoArgOption [] ["list-options"] ListOptions+listOptions = DarcsSingleOption $ DarcsNoArgOption [] ["list-options"] ListOptions "simply list the command's arguments" flagToString :: [DarcsOption] -> DarcsFlag -> Maybe String-flagToString x f = maybeHead $ catMaybes $ map f2o x- where f2o (DarcsArgOption _ (s:_) c _ _) = do arg <- getContentString f- if c arg == f- then return $ unwords [('-':'-':s), arg]- else Nothing+flagToString x f = listToMaybe $ mapMaybe f2o $ concatMap atomicOptions x+ where f2o (DarcsArgOption _ (s:_) c _ _) =+ do arg <- getContentString f+ if c arg == f+ then return $ unwords [('-':'-':s), arg]+ else Nothing f2o (DarcsNoArgOption _ (s:_) f' _) | f == f' = Just ('-':'-':s)- f2o (DarcsMultipleChoiceOption xs) = maybeHead $ catMaybes $ map f2o xs f2o _ = Nothing- maybeHead (a:_) = Just a- maybeHead [] = Nothing -reponame :: DarcsOption-depsSel :: DarcsOption-partial :: DarcsOption-partialCheck :: DarcsOption-tokens :: DarcsOption-workingRepoDir :: DarcsOption-possiblyRemoteRepoDir :: DarcsOption-disable :: DarcsOption-restrictPaths :: DarcsOption--pipeInteractive, allPipeInteractive, allInteractive, all_patches, interactive, pipe,+pipeInteractive, allPipeInteractive, allInteractive, humanReadable, diffflags, allowProblematicFilenames, noskipBoring, askLongComment, matchOneNontag, changesReverse, creatorhash, changesFormat, matchOneContext, happyForwarding, sendToContext,@@ -501,12 +548,10 @@ author, askdeps, lookforadds, ignoretimes, test, notest, help, forceReplace, allowUnrelatedRepos, matchOne, matchRange, matchSeveral, fancyMoveAdd, sendmailCmd,- logfile, rmlogfile, leaveTestDir, fromOpt, setDefault+ logfile, rmlogfile, leaveTestDir, fromOpt :: DarcsOption -recursive :: String -> DarcsOption- sign, applyas, verify :: DarcsOption \end{code} @@ -525,7 +570,7 @@ % darcs COMMAND --help \end{verbatim} \begin{code}-help = DarcsNoArgOption ['h'] ["help"] Help+help = DarcsSingleOption $ DarcsNoArgOption ['h'] ["help"] Help "shows brief description of command and its arguments" \end{code} @@ -537,7 +582,8 @@ can be helpful if you want to protect the repository from accidental use of advanced commands like obliterate, unpull, unrecord or amend-record. \begin{code}-disable = DarcsNoArgOption [] ["disable"] Disable+disable :: DarcsOption+disable = DarcsSingleOption $ DarcsNoArgOption [] ["disable"] Disable "disable this command" \end{code} @@ -573,7 +619,8 @@ "suppress informational output", DarcsNoArgOption [] ["standard-verbosity"] NormalVerbosity "neither verbose nor quiet output"],- DarcsNoArgOption [] ["timings"] Timings "provide debugging timings information"]+ DarcsSingleOption+ (DarcsNoArgOption [] ["timings"] Timings "provide debugging timings information")] \end{code} \begin{options}@@ -588,9 +635,12 @@ when running \verb'apply' from a mailer. \begin{code}-workingRepoDir = DarcsArgOption [] ["repodir"] WorkRepoDir "DIRECTORY"+workingRepoDir :: DarcsOption+workingRepoDir = DarcsSingleOption $ DarcsArgOption [] ["repodir"] WorkRepoDir "DIRECTORY" "specify the repository directory in which to run"-possiblyRemoteRepoDir = DarcsArgOption [] ["repo"] WorkRepoUrl "URL"++possiblyRemoteRepoDir :: DarcsOption+possiblyRemoteRepoDir = DarcsSingleOption $ DarcsArgOption [] ["repo"] WorkRepoUrl "URL" "specify the repository URL" -- | 'getRepourl' takes a list of flags and returns the url of the@@ -598,7 +648,7 @@ -- This flag is present if darcs was invoked with @--repodir=DIRECTORY@ getRepourl :: [DarcsFlag] -> Maybe String getRepourl [] = Nothing-getRepourl (WorkRepoUrl d:_) | not (is_file d) = Just d+getRepourl (WorkRepoUrl d:_) | not (isFile d) = Just d getRepourl (_:fs) = getRepourl fs \end{code} @@ -619,7 +669,7 @@ -- | 'remoteRepo' is the option used to specify the URL of the remote -- repository to work with remoteRepo :: DarcsOption-remoteRepo = DarcsArgOption [] ["remote-repo"] RemoteRepo "URL"+remoteRepo = DarcsSingleOption $ DarcsArgOption [] ["remote-repo"] RemoteRepo "URL" "specify the remote repository URL to work with" \end{code} @@ -627,10 +677,10 @@ \input{Darcs/Patch/Match.lhs} \begin{code}-patchnameOption = DarcsArgOption ['m'] ["patch-name"] PatchName "PATCHNAME"- "name of patch"+patchnameOption = DarcsSingleOption $ DarcsArgOption ['m'] ["patch-name"]+ (PatchName . decodeString) "PATCHNAME" "name of patch" -sendToContext = DarcsAbsPathOption [] ["context"] Context "FILENAME"+sendToContext = DarcsSingleOption $ DarcsAbsPathOption [] ["context"] Context "FILENAME" "send to context stored in FILENAME" matchOneContext =@@ -643,15 +693,23 @@ DarcsAbsPathOption [] ["context"] Context "FILENAME" "version specified by the context in FILENAME" ]- where mp s = OnePattern (patch_match s)+ where mp s = OnePattern (patchMatch s) -matchOne = concatOptions [__match, __patch, __tag, __index]-matchOneNontag = concatOptions [__match, __patch, __index]-matchSeveral = concatOptions [__matches, __patches, __tags]-matchRange = concatOptions [matchTo, matchFrom, __match, __patch, __last, __indexes]-matchSeveralOrRange = concatOptions [matchTo, matchFrom, __last, __indexes,- __matches, __patches, __tags]-matchSeveralOrLast = concatOptions [matchFrom, __last, __matches, __patches, __tags]+matchOne = DarcsMultipleChoiceOption [__match, __patch, __tag, __index]+ -- [NOTE --index removed from matchOneNontag because issue1926]+ -- The --index option was removed for 2.5 release because it isn't handled+ -- by amend-record (see issue1926).+ --+ -- At this moment, amend-record is the only command that uses 'matchOneNontag',+ -- so there is no other command affected.+matchOneNontag = DarcsMultipleChoiceOption [__match, __patch {- , __index -} ]+matchSeveral = DarcsMultipleChoiceOption [__matches, __patches, __tags]+matchRange = concatOptions $ [ matchTo, matchFrom+ , DarcsMultipleChoiceOption [__match, __patch, __last, __indexes] ]+matchSeveralOrRange = concatOptions [ matchTo, matchFrom+ , DarcsMultipleChoiceOption [ __last, __indexes, __matches, __patches, __tags] ]+matchSeveralOrLast = concatOptions [ matchFrom+ , DarcsMultipleChoiceOption [ __last, __matches, __patches, __tags] ] matchTo, matchFrom :: DarcsOption matchTo = DarcsMultipleChoiceOption@@ -661,7 +719,7 @@ "select changes up to a patch matching REGEXP", DarcsArgOption [] ["to-tag"] UpToTag "REGEXP" "select changes up to a tag matching REGEXP"]- where uptop s = UpToPattern (patch_match s)+ where uptop s = UpToPattern (patchMatch s) matchFrom = DarcsMultipleChoiceOption [DarcsArgOption [] ["from-match"] fromp "PATTERN" "select changes starting with a patch matching PATTERN",@@ -669,9 +727,9 @@ "select changes starting with a patch matching REGEXP", DarcsArgOption [] ["from-tag"] AfterTag "REGEXP" "select changes starting with a tag matching REGEXP"]- where fromp s = AfterPattern (patch_match s)+ where fromp s = AfterPattern (patchMatch s) -__tag, __tags, __patch, __patches, __match, __matches, __last, __index, __indexes :: DarcsOption+__tag, __tags, __patch, __patches, __match, __matches, __last, __index, __indexes :: DarcsAtomicOption __tag = DarcsArgOption ['t'] ["tag"] OneTag "REGEXP" "select tag matching REGEXP"@@ -685,10 +743,10 @@ __match = DarcsArgOption [] ["match"] mp "PATTERN" "select a single patch matching PATTERN"- where mp s = OnePattern (patch_match s)+ where mp s = OnePattern (patchMatch s) __matches = DarcsArgOption [] ["matches"] mp "PATTERN" "select patches matching PATTERN"- where mp s = SeveralPattern (patch_match s)+ where mp s = SeveralPattern (patchMatch s) __last = DarcsArgOption [] ["last"] lastn "NUMBER" "select the last NUMBER patches"@@ -710,7 +768,7 @@ isokay c = isDigit c || c == '-' matchMaxcount :: DarcsOption-matchMaxcount = DarcsArgOption [] ["max-count"] mc "NUMBER"+matchMaxcount = DarcsSingleOption $ DarcsArgOption [] ["max-count"] mc "NUMBER" "return only NUMBER results" where mc = MaxCount . numberString @@ -732,9 +790,6 @@ LeaveTestDir "don't remove the test directory", DarcsNoArgOption [] ["remove-test-directory"] NoLeaveTestDir "remove the test directory"]--testByDefault :: [DarcsFlag] -> [DarcsFlag]-testByDefault o = if NoTest `elem` o then o else Test:o \end{code} \begin{options}@@ -751,7 +806,7 @@ \verb!--ignore-times! option, which instructs darcs not to trust the file modification times, but instead to check each file's contents explicitly. \begin{code}-ignoretimes = +ignoretimes = DarcsMultipleChoiceOption [DarcsNoArgOption [] ["ignore-times"] IgnoreTimes "don't trust the file modification times"@@ -788,6 +843,16 @@ "don't give a long comment", DarcsNoArgOption [] ["prompt-long-comment"] PromptLongComment "prompt for whether to edit the long comment"]++keepDate :: DarcsOption+keepDate =+ DarcsMultipleChoiceOption+ [DarcsNoArgOption [] ["keep-date"] KeepDate+ "keep the date of the original patch",+ DarcsNoArgOption [] ["no-keep-date"] NoKeepDate+ "use the current date for the amended patch"+ ]+ \end{code} \begin{options}@@ -796,7 +861,7 @@ \darcsEnv{DARCS_EMAIL} \begin{code}-logfile = DarcsAbsPathOption [] ["logfile"] LogFile "FILE"+logfile = DarcsSingleOption $ DarcsAbsPathOption [] ["logfile"] LogFile "FILE" "give patch name and comment in file" rmlogfile = DarcsMultipleChoiceOption@@ -805,8 +870,10 @@ DarcsNoArgOption [] ["no-delete-logfile"] DontRmLogFile "keep the logfile when done [DEFAULT]"] -author = DarcsArgOption ['A'] ["author"] Author "EMAIL" "specify author id"-fromOpt = DarcsArgOption [] ["from"] Author "EMAIL" "specify email address"+author = DarcsSingleOption $+ DarcsArgOption ['A'] ["author"] (Author . decodeString) "EMAIL" "specify author id"+fromOpt = DarcsSingleOption $+ DarcsArgOption [] ["from"] (Author . decodeString) "EMAIL" "specify email address" fileHelpAuthor :: [String] fileHelpAuthor = [@@ -847,8 +914,8 @@ text "If you move that file to ~/.darcs/author, it will be used for patches" $$ text "you record in ALL repositories." add <- askUser "What is your email address? "- writeFile (darcsdir ++ "/prefs/author") $- unlines ["# " ++ line | line <- fileHelpAuthor] ++ "\n" ++ add+ writeLocaleFile (darcsdir ++ "/prefs/author") $+ unlines ["# " ++ line | line <- fileHelpAuthor] ++ "\n" ++ add return add else askUser "What is your email address (e.g. Fred Bloggs <fred@example.net>)? " @@ -856,10 +923,15 @@ -- then from global preferences, then from environment variables. Returns 'Nothing' if it -- could not get it. getEasyAuthor :: IO (Maybe String)-getEasyAuthor = firstJustIO [ firstNotBlank `fmap` getPreflist "author",- firstNotBlank `fmap` getGlobal "author",- maybeGetEnv "DARCS_EMAIL",- maybeGetEnv "EMAIL" ]+getEasyAuthor = do+ undecodedAuthor <-+ firstJustIO [ firstNotBlank `fmap` getPreflist "author",+ firstNotBlank `fmap` getGlobal "author",+ maybeGetEnv "DARCS_EMAIL",+ maybeGetEnv "EMAIL" ]+ case undecodedAuthor of+ Just a -> return (Just (decodeString a))+ Nothing -> return Nothing \end{code} \begin{options}@@ -871,10 +943,10 @@ file. \begin{code}-nocompress = concatOptions [__compress, __dontCompress]-uncompressNocompress = concatOptions [__compress, __dontCompress, __uncompress]+nocompress = DarcsMultipleChoiceOption [__compress, __dontCompress]+uncompressNocompress = DarcsMultipleChoiceOption [__compress, __dontCompress, __uncompress] -__compress, __dontCompress, __uncompress :: DarcsOption+__compress, __dontCompress, __uncompress :: DarcsAtomicOption __compress = DarcsNoArgOption [] ["compress"] Compress "create compressed patches" __dontCompress = DarcsNoArgOption [] ["dont-compress","no-compress"] NoCompress@@ -897,8 +969,8 @@ DarcsNoArgOption [] ["no-unified"] NonUnified "output patch in diff's dumb format"] -diffCmdFlag = DarcsArgOption [] ["diff-command"]- DiffCmd "COMMAND" "specify diff command (ignores --diff-opts)"+diffCmdFlag = DarcsSingleOption $+ DarcsArgOption [] ["diff-command"] DiffCmd "COMMAND" "specify diff command (ignores --diff-opts)" storeInMemory = DarcsMultipleChoiceOption [DarcsNoArgOption [] ["store-in-memory"] StoreInMemory@@ -906,16 +978,19 @@ DarcsNoArgOption [] ["no-store-in-memory"] ApplyOnDisk "do patch application on disk [DEFAULT]"] -target = DarcsArgOption [] ["to"] Target "EMAIL" "specify destination email"-ccSend = DarcsArgOption [] ["cc"] Cc "EMAIL" "mail results to additional EMAIL(s)"-ccApply = DarcsArgOption [] ["cc"] Cc "EMAIL" "mail results to additional EMAIL(s). Requires --reply"+target = DarcsSingleOption $+ DarcsArgOption [] ["to"] Target "EMAIL" "specify destination email"+ccSend = DarcsSingleOption $+ DarcsArgOption [] ["cc"] Cc "EMAIL" "mail results to additional EMAIL(s)"+ccApply = DarcsSingleOption $+ DarcsArgOption [] ["cc"] Cc "EMAIL" "mail results to additional EMAIL(s). Requires --reply" -- |'getCc' takes a list of flags and returns the addresses to send a copy of -- the patch bundle to when using @darcs send@. -- looks for a cc address specified by @Cc \"address\"@ in that list of flags. -- Returns the addresses as a comma separated string. getCc :: [DarcsFlag] -> String-getCc fs = lt $ catMaybes $ map whatcc fs+getCc fs = lt $ mapMaybe whatcc fs where whatcc (Cc t) = Just t whatcc _ = Nothing lt [t] = t@@ -923,7 +998,7 @@ lt (t:ts) = t++" , "++lt ts lt [] = "" -subject = DarcsArgOption [] ["subject"] Subject "SUBJECT" "specify mail subject"+subject = DarcsSingleOption $ DarcsArgOption [] ["subject"] Subject "SUBJECT" "specify mail subject" -- |'getSubject' takes a list of flags and returns the subject of the mail -- to be sent by @darcs send@. Looks for a subject specified by@@ -934,28 +1009,38 @@ getSubject (_:fs) = getSubject fs getSubject [] = Nothing -inReplyTo = DarcsArgOption [] ["in-reply-to"] InReplyTo "EMAIL" "specify in-reply-to header"+inReplyTo = DarcsSingleOption $ DarcsArgOption [] ["in-reply-to"] InReplyTo "EMAIL" "specify in-reply-to header" getInReplyTo :: [DarcsFlag] -> Maybe String getInReplyTo (InReplyTo s:_) = Just s getInReplyTo (_:fs) = getInReplyTo fs getInReplyTo [] = Nothing -output = DarcsAbsPathOrStdOption ['o'] ["output"] Output "FILE"+output = DarcsSingleOption $ DarcsAbsPathOrStdOption ['o'] ["output"] Output "FILE" "specify output filename" -outputAutoName = DarcsOptAbsPathOption ['O'] ["output-auto-name"] "." OutputAutoName "DIRECTORY"+outputAutoName = DarcsSingleOption $+ DarcsOptAbsPathOption ['O'] ["output-auto-name"] "." OutputAutoName "DIRECTORY" "output to automatically named file in DIRECTORY, default: current directory" -editDescription =- DarcsMultipleChoiceOption- [DarcsNoArgOption [] ["edit-description"] EditDescription- "edit the patch bundle description",- DarcsNoArgOption [] ["dont-edit-description","no-edit-description"] NoEditDescription- "don't edit the patch bundle description"]+getOutput :: [DarcsFlag] -> FilePath -> Maybe AbsolutePathOrStd+getOutput (Output a:_) _ = return a+getOutput (OutputAutoName a:_) f = return $ makeAbsoluteOrStd a f+getOutput (_:flags) f = getOutput flags f+getOutput [] _ = Nothing -distnameOption = DarcsArgOption ['d'] ["dist-name"] DistName "DISTNAME"- "name of version"+editDescription = mkMutuallyExclusive [] yes [no]+ where+ yes = ( DarcsNoArgOption [] ["edit-description"]+ , EditDescription+ , "edit the patch bundle description" )+ no = ( DarcsNoArgOption [] ["dont-edit-description","no-edit-description"]+ , NoEditDescription+ , "don't edit the patch bundle description" ) +distnameOption = DarcsSingleOption $+ DarcsArgOption ['d'] ["dist-name"] DistName "DISTNAME" "name of version"++recursive :: String -> DarcsOption recursive h = DarcsMultipleChoiceOption [DarcsNoArgOption ['r'] ["recursive"] Recursive h,@@ -980,15 +1065,15 @@ "Convert from hashed to darcs-1 format"] upgradeFormat :: DarcsOption-upgradeFormat =+upgradeFormat = DarcsSingleOption $ DarcsNoArgOption [] ["upgrade"] UpgradeFormat "upgrade repository to latest compatible format" -xmloutput = DarcsNoArgOption [] ["xml-output"] XMLOutput- "generate XML formatted output"+xmloutput = DarcsSingleOption $+ DarcsNoArgOption [] ["xml-output"] XMLOutput "generate XML formatted output" -creatorhash = DarcsArgOption [] ["creator-hash"] CreatorHash "HASH"- "specify hash of creator patch (see docs)"+creatorhash = DarcsSingleOption $+ DarcsArgOption [] ["creator-hash"] CreatorHash "HASH" "specify hash of creator patch (see docs)" sign = DarcsMultipleChoiceOption [DarcsNoArgOption [] ["sign"] Sign@@ -1011,11 +1096,15 @@ DarcsNoArgOption [] ["no-happy-forwarding"] NoHappyForwarding "don't forward unsigned messages without extra header [DEFAULT]"] -setDefault = DarcsMultipleChoiceOption- [DarcsNoArgOption [] ["set-default"] SetDefault- "set default repository [DEFAULT]",- DarcsNoArgOption [] ["no-set-default"] NoSetDefault- "don't set default repository"]+setDefault :: Bool -> DarcsOption+setDefault wantYes+ | wantYes = mkMutuallyExclusive [] yes [no]+ | otherwise = mkMutuallyExclusive [yes] no []+ where+ yes = ( DarcsNoArgOption [] ["set-default"], SetDefault+ , "set default repository" )+ no = ( DarcsNoArgOption [] ["no-set-default"], NoSetDefault+ , "don't set default repository" ) verify = DarcsMultipleChoiceOption [DarcsAbsPathOption [] ["verify"] Verify "PUBRING"@@ -1025,9 +1114,12 @@ DarcsNoArgOption [] ["no-verify"] NonVerify "don't verify patch signature"] -reponame = DarcsArgOption [] ["repo-name","repodir"] NewRepo "DIRECTORY"+reponame :: DarcsOption+reponame = DarcsSingleOption $+ DarcsArgOption [] ["repo-name","repodir"] NewRepo "DIRECTORY" "path of output directory" --repodir is there for compatibility --should be removed eventually+depsSel :: DarcsOption depsSel = DarcsMultipleChoiceOption [DarcsNoArgOption [] ["no-deps"] DontGrabDeps "don't automatically fulfill dependencies",@@ -1035,13 +1127,18 @@ "don't ask about patches that are depended on by matched patches (with --match or --patch)", DarcsNoArgOption [] ["prompt-for-dependencies"] PromptForDependencies "prompt about patches that are depended on by matched patches [DEFAULT]"]-tokens = DarcsArgOption [] ["token-chars"] Toks "\"[CHARS]\""++tokens :: DarcsOption+tokens = DarcsSingleOption $+ DarcsArgOption [] ["token-chars"] Toks "\"[CHARS]\"" "define token to contain these characters" -partial = concatOptions [__partial, __lazy, __ephemeral, __complete]-partialCheck = concatOptions [__complete, __partial]+partial :: DarcsOption+partial = DarcsMultipleChoiceOption [__partial, __lazy, __ephemeral, __complete]+partialCheck :: DarcsOption+partialCheck = DarcsMultipleChoiceOption [__complete, __partial] -__partial, __lazy, __ephemeral, __complete :: DarcsOption+__partial, __lazy, __ephemeral, __complete :: DarcsAtomicOption __partial = DarcsNoArgOption [] ["partial"] Partial "get partial repository using checkpoint (old-fashioned format only)" __lazy = DarcsNoArgOption [] ["lazy"] Lazy@@ -1057,7 +1154,8 @@ DarcsNoArgOption [] ["no-force"] NonForce "don't force the replace if it looks scary"] -reply = DarcsArgOption [] ["reply"] Reply "FROM" "reply to email-based patch using FROM address"+reply = DarcsSingleOption $+ DarcsArgOption [] ["reply"] Reply "FROM" "reply to email-based patch using FROM address" applyConflictOptions = DarcsMultipleChoiceOption [DarcsNoArgOption [] ["mark-conflicts"]@@ -1082,8 +1180,9 @@ DarcsNoArgOption [] ["skip-conflicts"] SkipConflicts "filter out any patches that would create conflicts" ]-useExternalMerge = DarcsArgOption [] ["external-merge"]- ExternalMerge "COMMAND" "use external tool to merge conflicts"+useExternalMerge = DarcsSingleOption $+ DarcsArgOption [] ["external-merge"] ExternalMerge "COMMAND"+ "use external tool to merge conflicts" \end{code} \begin{options}@@ -1134,8 +1233,8 @@ -- --dry-run is a possibility, automated users can examine the results more -- easily with --xml. dryRunNoxml :: DarcsOption-dryRunNoxml = DarcsNoArgOption [] ["dry-run"] DryRun- "don't actually take the action"+dryRunNoxml = DarcsSingleOption $+ DarcsNoArgOption [] ["dry-run"] DryRun "don't actually take the action" dryRun :: [DarcsOption] dryRun = [dryRunNoxml, xmloutput]@@ -1172,12 +1271,12 @@ putInfo = if XMLOutput `elem` opts then \_ -> return () else putDocLn xml_info pl | Summary `elem` opts = xml_with_summary pl- | otherwise = (to_xml . info) pl- + | otherwise = (toXml . info) pl+ xml_with_summary hp- | Just p <- hopefullyM hp = insert_before_lastline- (to_xml $ info hp) (indent $ xmlSummary p)- xml_with_summary hp = to_xml (info hp)+ | Just p <- hopefullyM hp = insertBeforeLastline+ (toXml $ info hp) (indent $ xmlSummary p)+ xml_with_summary hp = toXml (info hp) indent = prefix " " @@ -1190,7 +1289,7 @@ [DarcsNoArgOption [] ["boring"] Boring "don't skip boring files", DarcsNoArgOption [] ["no-boring"]- SkipBoring "skip borign files [DEFAULT]"]+ SkipBoring "skip boring files [DEFAULT]"] allowProblematicFilenames = DarcsMultipleChoiceOption [DarcsNoArgOption [] ["case-ok"] AllowCaseOnly@@ -1202,16 +1301,19 @@ ,DarcsNoArgOption [] ["no-reserved-ok"] DontAllowWindowsReserved "refuse to add files with Windows-reserved names [DEFAULT]"] -diffflags = DarcsArgOption [] ["diff-opts"]+diffflags = DarcsSingleOption $+ DarcsArgOption [] ["diff-opts"] DiffFlags "OPTIONS" "options to pass to diff" -changesFormat = DarcsMultipleChoiceOption- [DarcsNoArgOption [] ["context"]- (Context rootDirectory) "give output suitable for get --context",+changesFormat = concatOptions $+ [DarcsMultipleChoiceOption [+ DarcsNoArgOption [] ["context"]+ (Context rootDirectory) "give output suitable for get --context" ], xmloutput, humanReadable,+ DarcsMultipleChoiceOption [ DarcsNoArgOption [] ["number"] NumberPatches "number the changes",- DarcsNoArgOption [] ["count"] Count "output count of changes"+ DarcsNoArgOption [] ["count"] Count "output count of changes" ] ] changesReverse = DarcsMultipleChoiceOption [DarcsNoArgOption [] ["reverse"] Reverse@@ -1227,19 +1329,26 @@ "show changes to all files [DEFAULT]"] -humanReadable = DarcsNoArgOption [] ["human-readable"]- HumanReadable "give human-readable output"-pipe = DarcsNoArgOption [] ["pipe"] Pipe "ask user interactively for the patch metadata"+humanReadable = DarcsSingleOption $+ DarcsNoArgOption [] ["human-readable"] HumanReadable "give human-readable output" +pipe :: DarcsAtomicOption+pipe =+ DarcsNoArgOption [] ["pipe"] Pipe "ask user interactively for the patch metadata"++interactive :: DarcsAtomicOption interactive = DarcsNoArgOption ['i'] ["interactive"] Interactive "prompt user interactively"-all_patches = DarcsNoArgOption ['a'] ["all"] All "answer yes to all patches" -allInteractive = DarcsMultipleChoiceOption [all_patches, interactive]+allPatches :: DarcsAtomicOption+allPatches =+ DarcsNoArgOption ['a'] ["all"] All "answer yes to all patches" +allInteractive = DarcsMultipleChoiceOption [allPatches, interactive]+ allPipeInteractive- = DarcsMultipleChoiceOption [all_patches,pipe,interactive]+ = DarcsMultipleChoiceOption [allPatches,pipe,interactive] pipeInteractive = DarcsMultipleChoiceOption [pipe, interactive]@@ -1256,7 +1365,7 @@ -- | Get a list of all non-boring files and directories in the working copy. listFiles :: IO [String] listFiles = do nonboring <- restrictBoring emptyTree- working <- expand =<< nonboring <$> readPlainTree "."+ working <- expand =<< applyTreeFilter nonboring <$> readPlainTree "." return $ map (anchorPath "" . fst) $ list working -- | 'listUnregisteredFiles' returns the list of all non-boring unregistered@@ -1275,14 +1384,19 @@ optionsLatex :: [DarcsOption] -> String optionsLatex opts = "\\begin{tabular}{lll}\n"++- unlines (map optionLatex opts)+++ unlines (map optionListLatex opts)++ "\\end{tabular}\n" latexHelp :: String -> String latexHelp h = "\\begin{minipage}{7cm}\n\\raggedright\n" ++ h ++ "\\end{minipage}\n" -optionLatex :: DarcsOption -> String+optionListLatex :: DarcsOption -> String+optionListLatex (DarcsSingleOption o) = optionLatex o+optionListLatex (DarcsMultipleChoiceOption os) = unlines (map optionLatex os)+optionListLatex (DarcsMutuallyExclusive os _) = unlines (map optionLatex os)++optionLatex :: DarcsAtomicOption -> String optionLatex (DarcsNoArgOption a b _ h) = showShortOptions a ++ showLongOptions b ++ latexHelp h ++ "\\\\" optionLatex (DarcsArgOption a b _ arg h) =@@ -1297,8 +1411,6 @@ optionLatex (DarcsOptAbsPathOption a b _ _ arg h) = showShortOptions a ++ showLongOptions (map (++("[="++arg++"]")) b) ++ latexHelp h ++ "\\\\"-optionLatex (DarcsMultipleChoiceOption os) =- unlines (map optionLatex os) showShortOptions :: [Char] -> String showShortOptions [] = "&"@@ -1318,14 +1430,20 @@ DarcsNoArgOption [] ["dont-set-scripts-executable","no-set-scripts-executable"] DontSetScriptsExecutable "don't make scripts executable"] +bisect :: DarcsOption+bisect = DarcsSingleOption $ DarcsNoArgOption [] ["bisect"] Bisect+ "binary instead of linear search"+ relink, relinkPristine, sibling :: DarcsOption-relink = DarcsNoArgOption [] ["relink"] Relink+relink = DarcsSingleOption $ DarcsNoArgOption [] ["relink"] Relink "relink random internal data to a sibling" -relinkPristine = DarcsNoArgOption [] ["relink-pristine"] RelinkPristine+relinkPristine = DarcsSingleOption $+ DarcsNoArgOption [] ["relink-pristine"] RelinkPristine "relink pristine tree (not recommended)" -sibling = DarcsAbsPathOption [] ["sibling"] Sibling "URL"+sibling = DarcsSingleOption $+ DarcsAbsPathOption [] ["sibling"] Sibling "URL" "specify a sibling directory" -- | 'flagsToSiblings' collects the contents of all @Sibling@ flags in a list of flags.@@ -1335,11 +1453,11 @@ flagsToSiblings [] = [] nolinks :: DarcsOption-nolinks = DarcsNoArgOption [] ["nolinks"] NoLinks+nolinks = DarcsSingleOption $ DarcsNoArgOption [] ["nolinks"] NoLinks "do not link repository or pristine to sibling" reorderPatches :: DarcsOption-reorderPatches = DarcsNoArgOption [] ["reorder-patches"] Reorder+reorderPatches = DarcsSingleOption $ DarcsNoArgOption [] ["reorder-patches"] Reorder "reorder the patches in the repository" \end{code} \begin{options}@@ -1348,7 +1466,8 @@ \darcsEnv{SENDMAIL} \begin{code}-sendmailCmd = DarcsArgOption [] ["sendmail-command"] SendmailCmd "COMMAND" "specify sendmail command"+sendmailCmd = DarcsSingleOption $+ DarcsArgOption [] ["sendmail-command"] SendmailCmd "COMMAND" "specify sendmail command" environmentHelpSendmail :: ([String], [String]) environmentHelpSendmail = (["SENDMAIL"], [@@ -1370,7 +1489,7 @@ -- @SendmailCmd \"command\"@ in that list of flags, if any. -- This flag is present if darcs was invoked with @--sendmail-command=COMMAND@ -- Alternatively the user can set @$S@@ENDMAIL@ which will be used as a fallback if present.-getSendmailCmd :: [DarcsFlag] -> IO String +getSendmailCmd :: [DarcsFlag] -> IO String getSendmailCmd (SendmailCmd a:_) = return a getSendmailCmd (_:flags) = getSendmailCmd flags getSendmailCmd [] = do easy_sendmail <- firstJustIO [ maybeGetEnv "SENDMAIL" ]@@ -1400,7 +1519,7 @@ "only included recorded patches in output"] nullFlag :: DarcsOption -- "null" is already taken-nullFlag = DarcsNoArgOption ['0'] ["null"] NullFlag+nullFlag = DarcsSingleOption $ DarcsNoArgOption ['0'] ["null"] NullFlag "separate file names by NUL characters" \end{code} @@ -1444,7 +1563,7 @@ finishedOneIO k "DARCS_PATCHES_XML" setEnvCautiously "DARCS_PATCHES_XML" (renderString $ text "<patches>" $$- vcat (mapFL (to_xml . info) ps) $$+ vcat (mapFL (toXml . info) ps) $$ text "</patches>") finishedOneIO k "DARCS_FILES" setEnvCautiously "DARCS_FILES" (unlines$ listTouchedFiles ps)@@ -1556,36 +1675,37 @@ \begin{code} networkOptions :: [DarcsOption] networkOptions =- [DarcsMultipleChoiceOption- [DarcsNoArgOption [] ["ssh-cm"] SSHControlMaster- "use SSH ControlMaster feature",- DarcsNoArgOption [] ["no-ssh-cm"] NoSSHControlMaster- "don't use SSH ControlMaster feature [DEFAULT]"],- DarcsMultipleChoiceOption- [DarcsNoArgOption [] ["http-pipelining"] HTTPPipelining- pipelining_description,- DarcsNoArgOption [] ["no-http-pipelining"] NoHTTPPipelining- no_pipelining_description],- noCache, remote_darcs- ]- where pipelining_description =- "enable HTTP pipelining"++- (if pipeliningEnabledByDefault then " [DEFAULT]" else "")- no_pipelining_description =- "disable HTTP pipelining"++- (if pipeliningEnabledByDefault then "" else " [DEFAULT]")+ [ DarcsMultipleChoiceOption+ [ DarcsNoArgOption [] ["ssh-cm"] SSHControlMaster+ "use SSH ControlMaster feature"+ , DarcsNoArgOption [] ["no-ssh-cm"] NoSSHControlMaster+ "don't use SSH ControlMaster feature [DEFAULT]"+ ]+ , DarcsMultipleChoiceOption+ [ DarcsNoArgOption [] ["no-http-pipelining"] NoHTTPPipelining+ "disable HTTP pipelining"+ ]+ , remoteDarcs ] -remote_darcs :: DarcsOption-remote_darcs = DarcsArgOption [] ["remote-darcs"] RemoteDarcs "COMMAND"+remoteDarcs :: DarcsOption+remoteDarcs = DarcsSingleOption $+ DarcsArgOption [] ["remote-darcs"] RemoteDarcs "COMMAND" "name of the darcs executable on the remote server" noCache :: DarcsOption-noCache = DarcsNoArgOption [] ["no-cache"] NoCache+noCache = DarcsSingleOption $+ DarcsNoArgOption [] ["no-cache"] NoCache "don't use patch caches" optimizePristine :: DarcsOption-optimizePristine = DarcsNoArgOption [] ["pristine"] OptimizePristine+optimizePristine = DarcsSingleOption $+ DarcsNoArgOption [] ["pristine"] OptimizePristine "optimize hashed pristine layout"++optimizeHTTP :: DarcsOption+optimizeHTTP = DarcsSingleOption $+ DarcsNoArgOption [] ["http"] OptimizeHTTP+ "optimize repository for getting over network" \end{code} \begin{options} --umask@@ -1596,7 +1716,7 @@ \begin{code} umaskOption :: DarcsOption-umaskOption =+umaskOption = DarcsSingleOption $ DarcsArgOption [] ["umask"] UMask "UMASK" "specify umask to use when writing" \end{code}@@ -1621,6 +1741,7 @@ the command line, when pulling or applying unknown patches. \begin{code}+restrictPaths :: DarcsOption restrictPaths = DarcsMultipleChoiceOption [DarcsNoArgOption [] ["restrict-paths"] RestrictPaths@@ -1636,7 +1757,7 @@ doing pull, push and send. This option makes darcs skip this check. \begin{code}-allowUnrelatedRepos =+allowUnrelatedRepos = DarcsSingleOption $ DarcsNoArgOption [] ["ignore-unrelated-repos"] AllowUnrelatedRepos "do not check if repositories are unrelated" \end{code}@@ -1652,7 +1773,7 @@ omits any caches or other repos listed as a source of patches. \begin{code}-justThisRepo =+justThisRepo = DarcsSingleOption $ DarcsNoArgOption [] ["just-this-repo"] JustThisRepo "Limit the check or repair to the current repo" \end{code}@@ -1667,7 +1788,7 @@ This option specifies checking mode. \begin{code}-check =+check = DarcsSingleOption $ DarcsNoArgOption [] ["check"] Check "Specify checking mode" \end{code}@@ -1678,7 +1799,7 @@ This option specifies repair mode. \begin{code}-repair =+repair = DarcsSingleOption $ DarcsNoArgOption [] ["repair"] Repair "Specify repair mode"
− src/Darcs/CheckFileSystem.hs
@@ -1,59 +0,0 @@--- Copyright (C) 2004 David Roundy------ This program is free software; you can redistribute it and/or modify--- it under the terms of the GNU General Public License as published by--- the Free Software Foundation; either version 2, or (at your option)--- any later version.------ This program is distributed in the hope that it will be useful,--- but WITHOUT ANY WARRANTY; without even the implied warranty of--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the--- GNU General Public License for more details.------ You should have received a copy of the GNU General Public License--- along with this program; see the file COPYING. If not, write to--- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,--- Boston, MA 02110-1301, USA.--module Darcs.CheckFileSystem ( can_I_use_mmap ) where--import System.Directory ( removeFile, removeDirectory, setCurrentDirectory,- createDirectory,- )-import Control.Exception ( block )--import Darcs.Utils ( withCurrentDirectory )-import Darcs.Lock ( withOpenTemp )---- Beware that the below test will return true in any directory where we--- don't have write permission. This is risky, but means we'll do the--- right thing in the common case where we're dealing with posix--- filesystems and directories in which we don't have permission to write.--can_I_remove_open_files :: IO Bool-can_I_remove_open_files = block $ - (withOpenTemp $ \ (_,f) ->- (do { removeFile f; return True}) `catch` \_ -> return False)- `catch` \_ -> return True--can_I_remove_directories_holding_open_files :: IO Bool-can_I_remove_directories_holding_open_files = block $- (do createDirectory "darcs_testing_for_nfs"- okay <- (withCurrentDirectory "darcs_testing_for_nfs" $- do withOpenTemp $ \ (_,f) -> - (do removeFile f- setCurrentDirectory ".."- removeDirectory "darcs_testing_for_nfs"- return True- ) `catch` \_ -> return False- ) `catch` \_ -> return True- removeDirectory "darcs_testing_for_nfs" `catch` \_ -> return ()- return okay- ) `catch` \_ -> return True--can_I_use_mmap :: IO Bool-can_I_use_mmap = do a <- can_I_remove_open_files- if a then can_I_remove_directories_holding_open_files- else return False--
src/Darcs/ColorPrinter.hs view
@@ -8,13 +8,14 @@ invisiblePrinter, (<>), (<?>), Doc(Doc,unDoc), unsafeBothText, simplePrinter, hcat, unsafeText, unsafeChar, space, unsafePackedString, renderStringWith, prefix )-import Data.Char ( isAscii, isPrint, isSpace, isControl, ord, chr, intToDigit )+import Data.Char ( isAscii, isPrint, isSpace, isControl, ord, chr ) import Data.Bits ( bit, xor ) import System.Environment ( getEnv ) import qualified Data.ByteString.Char8 as BC (unpack, any, last, spanEnd) import qualified Data.ByteString as B (null, init) import System.IO.Unsafe ( unsafePerformIO ) import System.IO ( hIsTerminalDevice, Handle )+import Text.Printf ( printf ) dollar, cr :: Doc dollar = unsafeBothText "$"@@ -101,7 +102,7 @@ -- | @'fancyPrinters' h@ returns a set of printers suitable for outputting -- to @h@ fancyPrinters :: Printers-fancyPrinters h = let policy = getPolicy h in +fancyPrinters h = let policy = getPolicy h in Printers { colorP = colorPrinter policy, invisibleP = invisiblePrinter, hiddenP = colorPrinter policy Green,@@ -115,11 +116,11 @@ -- That is, if @policy@ has @poLineColor@ set, then colors the line, otherwise -- does nothing. lineColorTrans :: Policy -> Color -> Doc -> Doc-lineColorTrans po | poLineColor po = \c d -> prefix (set_color c) d <?> unsafeBothText reset_color+lineColorTrans po | poLineColor po = \c d -> prefix (setColor c) d <?> unsafeBothText resetColor | otherwise = const id lineColorSuffix :: Policy -> [Printable] -> [Printable]-lineColorSuffix po | poLineColor po = \d -> S reset_color : d+lineColorSuffix po | poLineColor po = \d -> S resetColor : d | otherwise = id colorPrinter :: Policy -> Color -> Printer@@ -141,7 +142,7 @@ then Doc $ escapePrinter po p else Doc (escapePrinter po (PS leadPS)) <> Doc (escapePrinter po{poSpace=True} (PS trailPS))- <> mark_escape po dollar+ <> markEscape po dollar prString s = let (trail',lead') = span isSpace (reverse s) lead = reverse lead'@@ -149,7 +150,7 @@ in if (not.null) trail then Doc (escapePrinter po (S lead)) <> Doc (escapePrinter po{poSpace=True} (S trail))- <> mark_escape po dollar+ <> markEscape po dollar else Doc (escapePrinter po p) escapePrinter :: Policy -> Printer@@ -161,7 +162,7 @@ | otherwise = epr p epr (S s) = escape po s- epr (PS ps) = if BC.any (not.no_escape po) ps+ epr (PS ps) = if BC.any (not.noEscape po) ps then escape po (BC.unpack ps) else unsafePackedString ps epr (Both s _) = escape po s@@ -175,28 +176,26 @@ initPR (Both s ps) = Both (init s) (B.init ps) --- escape assumes the input is in ['\0'..'\255']- -- | @'escape' policy string@ escapes @string@ according to the rules -- defined in 'policy', turning it into a 'Doc'. escape :: Policy -> String -> Doc escape _ "" = unsafeText "" escape po s = hcat (map escapeChar s) where- escapeChar c | no_escape po c = unsafeChar c+ escapeChar c | noEscape po c = unsafeChar c escapeChar ' ' = space escapeChar c = (emph.unsafeText.quoteChar) c- emph = mark_escape po+ emph = markEscape po --- | @'no_escape' policy c@ tells wether @c@ will be left as-is+-- | @'noEscape' policy c@ tells wether @c@ will be left as-is -- when escaping according to @policy@-no_escape :: Policy -> Char -> Bool-no_escape po c | poSpace po && isSpace c = False-no_escape po c | c `elem` poEscX po = False-no_escape po c | c `elem` poNoEscX po = True-no_escape _ '\t' = True -- tabs will likely be converted to spaces-no_escape _ '\n' = True-no_escape po c = if (poIsprint po) then isPrint c+noEscape :: Policy -> Char -> Bool+noEscape po c | poSpace po && isSpace c = False+noEscape po c | c `elem` poEscX po = False+noEscape po c | c `elem` poNoEscX po = True+noEscape _ '\t' = True -- tabs will likely be converted to spaces+noEscape _ '\n' = True+noEscape po c = if (poIsprint po) then isPrint c else isPrintableAscii c || c >= '\x80' && po8bit po @@ -216,66 +215,65 @@ | otherwise = sHex where cHat = chr $ (bit 6 `xor`) $ ord c- sHex = let (q, r) = quotRem (ord c) 16- in ['\\', intToDigit q, intToDigit r]+ sHex = "<U+" ++ printf "%04X" c ++ ">" -- make colors and highlightings --- | @'mark_escape' policy doc@ marks @doc@ with the appropriate+-- | @'markEscape' policy doc@ marks @doc@ with the appropriate -- marking for escaped characters according to @policy@-mark_escape :: Policy -> Doc -> Doc-mark_escape po | poAltColor po = make_invert- | poColor po = make_color Red- | otherwise = make_asciiart+markEscape :: Policy -> Doc -> Doc+markEscape po | poAltColor po = makeInvert+ | poColor po = makeColor Red+ | otherwise = makeAsciiart -- | @'color' policy color doc@ colors @doc@ with color @color@ if -- @policy@ is not set to use an alternative to color. In that case, -- it makes the text bold instead. color :: Policy -> Color -> Doc -> Doc-color po | poAltColor po = \_ -> make_bold- | otherwise = make_color+color po | poAltColor po = \_ -> makeBold+ | otherwise = makeColor -make_color, make_color' :: Color -> Doc -> Doc+makeColor, makeColor' :: Color -> Doc -> Doc -make_color' = with_color . set_color+makeColor' = withColor . setColor --- memoized version of make_color'-make_color Blue = make_color' Blue-make_color Red = make_color' Red-make_color Green = make_color' Green-make_color Cyan = make_color' Cyan-make_color Magenta = make_color' Magenta+-- memoized version of makeColor'+makeColor Blue = makeColor' Blue+makeColor Red = makeColor' Red+makeColor Green = makeColor' Green+makeColor Cyan = makeColor' Cyan+makeColor Magenta = makeColor' Magenta -set_color :: Color -> String-set_color Blue = "\x1B[01;34m" -- bold blue-set_color Red = "\x1B[01;31m" -- bold red-set_color Green = "\x1B[01;32m" -- bold green-set_color Cyan = "\x1B[36m" -- light cyan-set_color Magenta = "\x1B[35m" -- light magenta+setColor :: Color -> String+setColor Blue = "\x1B[01;34m" -- bold blue+setColor Red = "\x1B[01;31m" -- bold red+setColor Green = "\x1B[01;32m" -- bold green+setColor Cyan = "\x1B[36m" -- light cyan+setColor Magenta = "\x1B[35m" -- light magenta --- | @'make_asciiart' doc@ tries to make @doc@ (usually a+-- | @'makeAsciiart' doc@ tries to make @doc@ (usually a -- single escaped char) stand out with the help of only plain -- ascii, i.e., no color or font style.-make_asciiart :: Doc -> Doc-make_asciiart x = unsafeBothText "[_" <> x <> unsafeBothText "_]"+makeAsciiart :: Doc -> Doc+makeAsciiart x = unsafeBothText "[_" <> x <> unsafeBothText "_]" -- | the string to reset the terminal's color.-reset_color :: String-reset_color = "\x1B[00m"+resetColor :: String+resetColor = "\x1B[00m" --- | @'with_color' color doc@ returns a colorized version of @doc@.--- @color@ is a string that represents a color, given by 'set_color'-with_color :: String -> Doc -> Doc-with_color c =+-- | @'withColor' color doc@ returns a colorized version of @doc@.+-- @color@ is a string that represents a color, given by 'setColor'+withColor :: String -> Doc -> Doc+withColor c = let c' = unsafeBothText c- r' = unsafeBothText reset_color+ r' = unsafeBothText resetColor in \x -> c' <> x <> r' --- | 'make_bold' boldens a doc.-make_bold :: Doc -> Doc--- | 'make_invert' returns an invert video version of a doc.-make_invert :: Doc -> Doc-make_bold = with_color "\x1B[01m"-make_invert = with_color "\x1B[07m"+-- | 'makeBold' boldens a doc.+makeBold :: Doc -> Doc+-- | 'makeInvert' returns an invert video version of a doc.+makeInvert :: Doc -> Doc+makeBold = withColor "\x1B[01m"+makeInvert = withColor "\x1B[07m"
src/Darcs/Commands.lhs view
@@ -33,7 +33,7 @@ disambiguateCommands, CommandArgs(..), getCommandHelp, getCommandMiniHelp, getSubcommands,- usage, subusage, chompNewline,+ usage, usageHelper, subusage, chompNewline, extractCommands, superName, nodefaults,@@ -45,8 +45,8 @@ import Data.List ( sort, isPrefixOf ) import Darcs.Arguments ( DarcsFlag(Quiet,Verbose, DryRun), DarcsOption, disable, help,- anyVerbosity, posthookCmd, posthookPrompt,- prehookCmd, prehookPrompt, optionFromDarcsoption )+ anyVerbosity, noCache, posthookCmd, posthookPrompt,+ prehookCmd, prehookPrompt, optionFromDarcsOption ) import Darcs.RepoPath ( AbsolutePath, rootDirectory ) import Printer ( Doc, putDocLn, hPutDocLn, text, (<+>), errorDoc ) import System.IO ( stderr )@@ -146,7 +146,8 @@ , commandAdvancedOptions = opts2 } = (opts1 ++ [disable, help], anyVerbosity ++ opts2 ++- [posthookCmd, posthookPrompt+ [noCache+ ,posthookCmd, posthookPrompt ,prehookCmd, prehookPrompt]) -- Supercommands cannot be disabled.@@ -158,7 +159,7 @@ commandOptions :: AbsolutePath -> DarcsCommand -> ([OptDescr DarcsFlag], [OptDescr DarcsFlag]) commandOptions cwd c = (convert basic, convert advanced) where (basic, advanced) = commandAlloptions c- convert = concatMap (optionFromDarcsoption cwd)+ convert = concatMap (optionFromDarcsOption cwd) nodefaults :: [DarcsFlag] -> AbsolutePath -> [String] -> IO [String] nodefaults _ _ xs = return xs@@ -203,7 +204,7 @@ ("Usage: darcs "++commandName super++" SUBCOMMAND ... " ++ "\n\n"++ commandDescription super++ "\n\nSubcommands:\n" ++ usageHelper (getSubcommands super) ++ "\nOptions:")- (optionFromDarcsoption rootDirectory help))+ (optionFromDarcsOption rootDirectory help)) ++ "\n" ++ commandHelp super usageHelper :: [CommandControl] -> String@@ -297,7 +298,7 @@ putVerbose :: [DarcsFlag] -> Doc -> IO () putVerbose opts = when (amVerbose opts) . putDocLn- + putInfo :: [DarcsFlag] -> Doc -> IO () putInfo opts = unless (amQuiet opts) . putDocLn
src/Darcs/Commands/Add.lhs view
@@ -20,6 +20,10 @@ module Darcs.Commands.Add ( add, expandDirs ) where import Data.List ( (\\), nub)+import Data.Maybe( isNothing )+import Control.Monad ( when, unless, liftM )+import Storage.Hashed.Tree( Tree, findTree, expand )+import Storage.Hashed.AnchoredPath( floatPath, anchorPath, parents ) import Darcs.Commands(DarcsCommand(..), putVerbose, putWarning, nodefaults) import Darcs.Arguments (noskipBoring, allowProblematicFilenames,@@ -32,23 +36,22 @@ import Darcs.Flags( includeBoring, doAllowCaseOnly, doAllowWindowsReserved,) import Darcs.Utils ( withCurrentDirectory, nubsort ) import IsoDate ( getIsoDateTime )-import Darcs.Repository ( amInRepository, withRepoLock, ($-),- slurp_pending, add_to_pending )-import Darcs.Patch ( Prim, applyToSlurpy, addfile, adddir, move )-import Darcs.Witnesses.Ordered ( FL(..), unsafeFL, concatFL, nullFL )-import Darcs.SlurpDirectory ( Slurpy, slurp_has_anycase, slurp_has,- isFileReallySymlink, doesDirectoryReallyExist, - doesFileReallyExist, slurp_hasdir,- )-import Darcs.Patch.FileName ( fp2fn )+import Darcs.Repository.State( readRecordedAndPending )+import Darcs.Repository ( amInRepository, withRepoLock, ($-), addToPending )+import Darcs.Patch ( Prim, applyToTree, addfile, adddir, move )+import Darcs.Witnesses.Ordered ( FL(..), (+>+), nullFL )+import Darcs.Witnesses.Sealed ( Sealed(..), Gap(..), FreeLeft, unFreeLeft )+import Darcs.Utils ( isFileReallySymlink, doesDirectoryReallyExist+ , doesFileReallyExist, treeHas, treeHasDir, treeHasAnycase ) import Darcs.RepoPath ( SubPath, toFilePath, simpleSubPath, toPath )-import Control.Monad ( when, unless, liftM ) import Darcs.Repository.Prefs ( darcsdirFilter, boringFileFilter ) import Data.Maybe ( maybeToList, fromJust ) import System.FilePath.Posix ( takeDirectory, (</>) ) import qualified System.FilePath.Windows as WindowsFilePath import Printer( text ) +#include "gadts.h"+ addDescription :: String addDescription = "Add one or more new files or directories." @@ -96,12 +99,14 @@ addCmd :: [DarcsFlag] -> [String] -> IO () addCmd opts args = withRepoLock opts $- \repository ->- do cur <- slurp_pending repository- origfiles <- fixSubPaths opts args- when (null origfiles) $+ do -- TODO do not expand here, and use findM/findIO or such later+ -- (needs adding to hashed-storage first though)+ cur <- expand =<< readRecordedAndPending repository+ when (null args) $ putStrLn "Nothing specified, nothing added." >> putStrLn "Maybe you wanted to say `darcs add --recursive .'?"- parlist <- getParents cur (map toFilePath origfiles)+ origfiles <- fixSubPaths opts args+ let parlist = getParents cur (map toFilePath origfiles) flist' <- if Recursive `elem` opts then expandDirs origfiles else return origfiles@@ -111,19 +116,19 @@ then return darcsdirFilter else boringFileFilter let fixedOpts = if DryRun `elem` opts then Verbose:opts else opts- mapM_ (putWarning fixedOpts . text . ((msg_skipping msgs ++ " boring file ")++)) $+ mapM_ (putWarning fixedOpts . text . ((msgSkipping msgs ++ " boring file ")++)) $ flist \\ nboring flist date <- getIsoDateTime- ps <- addp msgs fixedOpts date cur $ nboring flist+ Sealed ps <- fmap unFreeLeft $ addp msgs fixedOpts date cur $ nboring flist when (nullFL ps && not (null args)) $ fail "No files were added"- unless gotDryRun $ add_to_pending repository ps+ unless gotDryRun $ addToPending repository ps where gotDryRun = DryRun `elem` opts msgs | gotDryRun = dryRunMessages | otherwise = normalMessages -addp :: AddMessages -> [DarcsFlag] -> String -> Slurpy -> [FilePath] -> IO (FL Prim)+addp :: AddMessages -> [DarcsFlag] -> String -> Tree IO -> [FilePath] -> IO (FreeLeft (FL Prim)) addp msgs opts date cur0 files = do (ps, dups) <- foldr@@ -146,99 +151,95 @@ isDir <- doesDirectoryReallyExist f if isDir then return $- "The following directory "++msg_is msgs++" already in the repository"+ "The following directory "++msgIs msgs++" already in the repository" else return $- "The following file "++msg_is msgs++" already in the repository"+ "The following file "++msgIs msgs++" already in the repository" fs -> do areDirs <- mapM doesDirectoryReallyExist fs if and areDirs then return $- "The following directories "++msg_are msgs++" already in the repository"+ "The following directories "++msgAre msgs++" already in the repository" else (if or areDirs then return $ "The following files and directories " ++- msg_are msgs ++ " already in the repository"+ msgAre msgs ++ " already in the repository" else return $- "The following files " ++ msg_are msgs ++ " already in the repository")+ "The following files " ++ msgAre msgs ++ " already in the repository") putWarning opts . text $ dupMsg ++ caseMsg mapM_ (putWarning opts . text) uniq_dups- return $ concatFL $ unsafeFL ps+ return $ foldr (joinGap (+>+)) (emptyGap NilFL) ps where- addp' :: Slurpy -> FilePath -> IO (Slurpy, Maybe (FL Prim), Maybe FilePath)- addp' cur f =- if already_has- then return (cur, Nothing, Just f)- else- if is_badfilename- then do putWarning opts . text $- "The filename " ++ f ++ " is invalid under Windows.\nUse --reserved-ok to allow it."- return add_failure- else do- isdir <- doesDirectoryReallyExist f- if isdir- then trypatch $ myadddir f- else do isfile <- doesFileReallyExist f- if isfile- then trypatch $ myaddfile f- else do islink <- isFileReallySymlink f- if islink then- putWarning opts . text $- "Sorry, file " ++ f ++ " is a symbolic link, which is unsupported by darcs."- else putWarning opts . text $ "File "++ f ++" does not exist!"- return add_failure- where already_has = if gotAllowCaseOnly- then slurp_has f cur- else slurp_has_anycase f cur- is_badfilename = not (gotAllowWindowsReserved || WindowsFilePath.isValid f)+ 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+ isdir <- doesDirectoryReallyExist f+ isfile <- doesFileReallyExist f+ islink <- isFileReallySymlink f+ case (already_has, is_badfilename, isdir, isfile, islink) 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+ (_, _, True, _, _) -> trypatch $ myadddir f+ (_, _, _, True, _) -> trypatch $ myaddfile f+ (_, _, _, _, True) -> 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 p =- case applyToSlurpy p cur of- Nothing -> do putWarning opts . text $ msg_skipping msgs ++ " '" ++ f ++ "' ... " ++ parent_error- return (cur, Nothing, Nothing)- Just s' -> do putVerbose opts . text $ msg_adding msgs++" '"++f++"'"- return (s', Just p, Nothing)+ trypatch :: FreeLeft (FL Prim) -> IO (Tree IO, Maybe (FreeLeft (FL Prim)), Maybe FilePath)+ trypatch p = do Sealed p' <- return $ unFreeLeft p+ tree <- applyToTree p' cur+ putVerbose opts . text $ msgAdding msgs++" '"++f++"'"+ return (tree, Just p, Nothing)+ `catch` \_ -> do+ err <- parent_error+ putWarning opts . text $ msgSkipping msgs ++ " '" ++ f ++ "' ... " ++ err+ return (cur, Nothing, Nothing) parentdir = takeDirectory f- have_parentdir = slurp_hasdir (fp2fn parentdir) cur- parent_error = if have_parentdir- then ""- else "couldn't add parent directory '"++parentdir++- "' to repository."+ have_parentdir = treeHasDir cur parentdir+ parent_error = have_parentdir >>= \x -> return $+ if x then ""+ else "couldn't add parent directory '"++parentdir++"' to repository." myadddir d = if gotFancyMoveAdd- then adddir (d++"-"++date) :>:- move (d++"-"++date) d :>: NilFL- else adddir d :>: NilFL+ then freeGap (adddir (d++"-"++date) :>:+ move (d++"-"++date) d :>: NilFL)+ else freeGap (adddir d :>: NilFL) myaddfile d = if gotFancyMoveAdd- then addfile (d++"-"++date) :>:- move (d++"-"++date) d :>: NilFL- else addfile d :>: NilFL+ then freeGap (addfile (d++"-"++date) :>:+ move (d++"-"++date) d :>: NilFL)+ else freeGap (addfile d :>: NilFL) gotFancyMoveAdd = FancyMoveAdd `elem` opts gotAllowCaseOnly = doAllowCaseOnly opts gotAllowWindowsReserved = doAllowWindowsReserved opts data AddMessages = AddMessages- { msg_skipping :: String- , msg_adding :: String- , msg_is :: String- , msg_are :: String+ { msgSkipping :: String+ , msgAdding :: String+ , msgIs :: String+ , msgAre :: String } normalMessages, dryRunMessages :: AddMessages normalMessages = AddMessages- { msg_skipping = "Skipping"- , msg_adding = "Adding"- , msg_is = "is"- , msg_are = "are"+ { msgSkipping = "Skipping"+ , msgAdding = "Adding"+ , msgIs = "is"+ , msgAre = "are" } dryRunMessages = AddMessages- { msg_skipping = "Would skip"- , msg_adding = "Would add"- , msg_is = "would be"- , msg_are = "would be"+ { msgSkipping = "Would skip"+ , msgAdding = "Would add"+ , msgIs = "would be"+ , msgAre = "would be" } -- |FIXME: this documentation makes *no* sense to me, and the@@ -261,15 +262,8 @@ else do fs <- withCurrentDirectory f listFiles return $ f: map (f </>) fs -getParents :: Slurpy -> [FilePath] -> IO [FilePath]-getParents cur fs =- concat `fmap` mapM (getParent cur) fs-getParent :: Slurpy -> FilePath -> IO [FilePath]-getParent cur f =- if slurp_hasdir (fp2fn parentdir) cur- then return []- else do grandparents <- getParent cur parentdir- return (grandparents ++ [parentdir])- where parentdir = takeDirectory f-\end{code}+getParents :: Tree IO -> [FilePath] -> [FilePath]+getParents cur = map (anchorPath "") . go . map floatPath+ where go fs = filter (isNothing . findTree cur) $ concatMap parents fs +\end{code}
src/Darcs/Commands/AmendRecord.lhs view
@@ -24,9 +24,9 @@ import System.Exit ( ExitCode(..), exitWith ) import Control.Monad ( when ) -import Darcs.Flags ( DarcsFlag(Author, LogFile, PatchName,- EditLongComment, PromptLongComment) )-import Darcs.Lock ( world_readable_temp )+import Darcs.Flags ( DarcsFlag(Author, LogFile, PatchName, AskDeps,+ EditLongComment, PromptLongComment, KeepDate) )+import Darcs.Lock ( worldReadableTemp ) import Darcs.RepoPath ( toFilePath ) import Darcs.Hopefully ( PatchInfoAnd, n2pia, hopefully, info ) import Darcs.Repository ( Repository, withRepoLock, ($-), withGutsOf,@@ -38,27 +38,30 @@ infopatch, getdeps, adddeps, effect, ) import Darcs.Patch.Prim ( canonizeFL )-import Darcs.Patch.Info ( pi_author, pi_name, pi_log,- PatchInfo, patchinfo, is_inverted, invert_name,+import Darcs.Patch.Info ( piAuthor, piName, piLog, piDateString,+ PatchInfo, patchinfo, isInverted, invertName, ) import Darcs.Patch.Split ( primSplitter ) import Darcs.Witnesses.Ordered ( FL(..), (:>)(..), (+>+), nullFL )-import Darcs.SelectChanges ( with_selected_changes_to_files',- with_selected_patch_from_repo )+import Darcs.SelectChanges ( selectChanges, WhichChanges(..),+ selectionContextPrim,+ runSelection,+ withSelectedPatchFromRepo ) import Darcs.Commands ( DarcsCommand(..), nodefaults )-import Darcs.Commands.Record ( getDate, getLog )+import Darcs.Commands.Record ( getDate, getLog, askAboutDepends ) import Darcs.Arguments ( DarcsFlag ( All ), areFileArgs, fixSubPaths, defineChanges, allInteractive, ignoretimes,- askLongComment, author, patchnameOption,+ askLongComment, askdeps, keepDate, author, patchnameOption, leaveTestDir, nocompress, lookforadds, workingRepoDir, matchOneNontag, umaskOption,- notest, testByDefault, listRegisteredFiles,+ test, listRegisteredFiles, getEasyAuthor, setScriptsExecutableOption ) import Darcs.Utils ( askUser, clarifyErrors ) import Printer ( putDocLn )+#include "gadts.h" amendrecordDescription :: String amendrecordDescription =@@ -96,7 +99,7 @@ "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 ~/.darcs/defaults, \n" ++- "where `David Roundy' is your name. " ++ + "where `David Roundy' is your name. " ++ "On Windows use C:/Documents And Settings/user/Application Data/darcs/defaults\n" amendrecord :: DarcsCommand@@ -112,63 +115,79 @@ commandAdvancedOptions = [nocompress, ignoretimes, umaskOption, setScriptsExecutableOption], commandBasicOptions = [matchOneNontag,- notest,+ test, leaveTestDir, allInteractive,- author, patchnameOption, askLongComment,+ author, patchnameOption, askdeps, askLongComment, keepDate, lookforadds, workingRepoDir]} amendrecordCmd :: [DarcsFlag] -> [String] -> IO () amendrecordCmd opts args =- withRepoLock (testByDefault opts) $- \repository -> do+ withRepoLock opts $- \(repository :: Repository p C(r u r)) -> do files <- sort `fmap` fixSubPaths opts args when (areFileArgs files) $ putStrLn $ "Amending changes in "++unwords (map show files)++":\n"- with_selected_patch_from_repo "amend" repository opts $ \ (_ :> oldp) -> do+ withSelectedPatchFromRepo "amend" repository opts $ \ (_ :> oldp) -> do ch <- unrecordedChanges opts repository files case ch of NilFL | not (hasEditMetadata opts) -> putStrLn "No changes!"- _ ->- with_selected_changes_to_files' "add" (filter (==All) opts) (Just primSplitter)- (map toFilePath files) ch $ addChangesToPatch opts repository oldp+ _ -> do+ let context = selectionContextPrim "add" (filter (==All) opts) (Just primSplitter)+ (map toFilePath files)+ chosenPatches <- runSelection (selectChanges First ch) context+ addChangesToPatch opts repository oldp chosenPatches -addChangesToPatch :: forall t p . (RepoPatch p) => [DarcsFlag] -> Repository p -> PatchInfoAnd p- -> (FL Prim :> t) -> IO ()-addChangesToPatch opts repository oldp (chs:>_) = +addChangesToPatch :: forall p C(r u t x y) . (RepoPatch p)+ => [DarcsFlag] -> Repository p C(r u t) -> PatchInfoAnd p C(x t)+ -> (FL Prim :> FL Prim) 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- (mlogf, newp) <- updatePatchHeader opts oldp chs- defineChanges newp invalidateIndex repository withGutsOf repository $ do- tentativelyRemovePatches repository opts (hopefully oldp :>: NilFL)- tentativelyAddPatch repository opts newp+ repository' <- tentativelyRemovePatches repository opts (oldp :>: NilFL)+ (mlogf, newp) <- updatePatchHeader opts repository' oldp chs+ defineChanges newp+ repository'' <- tentativelyAddPatch repository' opts newp let failmsg = maybe "" (\lf -> "\nLogfile left in "++lf++".") mlogf- finalizeRepositoryChanges repository `clarifyErrors` failmsg- maybe (return ()) removeFile mlogf- putStrLn "Finished amending patch:"- putDocLn $ description newp+ finalizeRepositoryChanges repository'' `clarifyErrors` failmsg+ maybe (return ()) removeFile mlogf+ putStrLn "Finished amending patch:"+ putDocLn $ description newp -updatePatchHeader :: forall p. (RepoPatch p) => [DarcsFlag] -> PatchInfoAnd p -> FL Prim- -> IO (Maybe String, PatchInfoAnd p)-updatePatchHeader opts oldp chs = do+updatePatchHeader :: forall p C(x y r u t) . (RepoPatch p)+ => [DarcsFlag] -> Repository p C(r u t)+ -> PatchInfoAnd p C(t x) -> FL Prim C(x y)+ -> IO (Maybe String, PatchInfoAnd p C(t y))+updatePatchHeader opts repository oldp chs = do++ 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_pinf = info oldp- prior = (pi_name old_pinf, pi_log old_pinf)- make_log = world_readable_temp "darcs-amend-record"- old_author = pi_author old_pinf- date <- getDate opts+ 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 -> pi_author old_pinf- maybe_invert = if is_inverted old_pinf then invert_name else id+ 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- return $ (mlogf, fixp oldp chs new_pinf) + let newp = n2pia (adddeps (infopatch new_pinf (fromPrims newchs)) newdeps)++ return (mlogf, newp)+ warnIfHijacking :: [DarcsFlag] -> String -> IO () warnIfHijacking opts old_author = do author_here <- getEasyAuthor@@ -177,7 +196,7 @@ Just ah -> let edit_author = isJust (getAuthor opts) in if (edit_author || ah == old_author) then return ()- else do yorn <- askUser $ + else do yorn <- askUser $ "You're not "++old_author ++"! Amend anyway? " case yorn of ('y':_) -> return () _ -> exitWith $ ExitSuccess@@ -188,6 +207,7 @@ hasEditMetadata (PatchName _:_) = True hasEditMetadata (EditLongComment:_) = True hasEditMetadata (PromptLongComment:_) = True+hasEditMetadata (AskDeps:_) = True hasEditMetadata (_:fs) = hasEditMetadata fs hasEditMetadata [] = False @@ -196,11 +216,4 @@ getAuthor (_:as) = getAuthor as getAuthor [] = Nothing -fixp :: RepoPatch p => PatchInfoAnd p -> FL Prim -> PatchInfo -> PatchInfoAnd p-fixp oldp chs new_pinf =- let pdeps = getdeps $ hopefully oldp- oldchs = effect oldp- infodepspatch pinfo deps p = adddeps (infopatch pinfo p) deps- in n2pia $ infodepspatch new_pinf pdeps $ fromPrims $ canonizeFL- $ oldchs +>+ chs \end{code}
src/Darcs/Commands/Annotate.lhs view
@@ -37,21 +37,25 @@ ) import Darcs.Flags ( isUnified ) import Storage.Hashed.Plain( readPlainTree )-import Darcs.Repository ( Repository, PatchSet, amInRepository, withRepository, ($-), read_repo,+import Darcs.Repository ( Repository, amInRepository, withRepository, ($-), readRepo, getMarkedupFile )+import Darcs.Patch.Set ( PatchSet, newset2RL )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif import Darcs.Patch ( RepoPatch, Named, LineMark(..), patch2patchinfo, xmlSummary ) import qualified Darcs.Patch ( summary ) import Darcs.Witnesses.Ordered ( mapRL, concatRL ) import qualified Data.ByteString.Char8 as BC ( unpack, ByteString ) import Darcs.PrintPatch ( printPatch, contextualPrintPatch )-import Darcs.Patch.Info ( PatchInfo, human_friendly, to_xml, make_filename,+import Darcs.Patch.Info ( PatchInfo, humanFriendly, toXml, makeFilename, showPatchInfo ) import Darcs.PopulationData ( Population(..), PopTree(..), DirMark(..), nameI, modifiedByI, modifiedHowI, createdByI, creationNameI, )-import Darcs.Population ( getRepoPopVersion, lookup_pop, lookup_creation_pop,- modified_to_xml,+import Darcs.Population ( getRepoPopVersion, lookupPop, lookupCreationPop,+ modifiedToXml, ) import Darcs.Hopefully ( info ) import Darcs.RepoPath ( SubPath, toFilePath )@@ -107,7 +111,7 @@ when (not $ haveNonrangeMatch opts) $ fail $ "Annotate requires either a patch pattern or a " ++ "file or directory argument."- Sealed2 p <- matchPatch opts `fmap` read_repo repository+ Sealed2 p <- matchPatch opts `fmap` readRepo repository if Summary `elem` opts then do putDocLn $ showpi $ patch2patchinfo p putDocLn $ show_summary p@@ -118,12 +122,14 @@ contextualPrintPatch c p else printPatch p where showpi | MachineReadable `elem` opts = showPatchInfo- | XMLOutput `elem` opts = to_xml- | otherwise = human_friendly+ | XMLOutput `elem` opts = toXml+ | otherwise = humanFriendly show_summary :: RepoPatch p => Named p C(x y) -> Doc show_summary = if XMLOutput `elem` opts then xmlSummary else Darcs.Patch.summary++annotateCmd opts [""] = annotateCmd opts [] \end{code} If a directory name is given, annotate will output details of the last@@ -140,13 +146,16 @@ that patch was applied. If a directory and a tag name are given, the details of the patches involved in the specified tagged version will be output. \begin{code}-annotateCmd opts args@[_] = withRepository opts $- \repository -> do- r <- read_repo repository- (rel_file_or_directory:_) <- fixSubPaths opts args+annotateCmd opts [file] = withRepository opts $- \repository -> do+ r <- readRepo repository+ fixed_args <- fixSubPaths opts [file]+ (rel_file_or_directory:_) <- case fixed_args of+ [] -> fail ("The supplied path " ++ file ++ " is not usable")+ fs -> return fs let file_or_directory = rel_file_or_directory pinfo <- if haveNonrangeMatch opts then return $ patch2patchinfo `unseal2` (matchPatch opts r)- else case mapRL info $ concatRL r of+ else case mapRL info $ newset2RL r of [] -> fail "Annotate does not currently work correctly on empty repositories." (x:_) -> return x pop <- getRepoPopVersion "." pinfo@@ -154,10 +163,9 @@ -- deal with --creator-hash option let maybe_creation_pi = findCreationPatchinfo opts r lookup_thing = case maybe_creation_pi of- Nothing -> lookup_pop- Just cp -> lookup_creation_pop cp-- if toFilePath file_or_directory == ""+ Nothing -> lookupPop+ Just cp -> lookupCreationPop cp+ if toFilePath file_or_directory == "." || toFilePath file_or_directory == "" then case pop of (Pop _ pt) -> annotatePop opts pinfo pt else case lookup_thing (toFilePath file_or_directory) pop of Nothing -> fail $ "There is no file or directory named '"++@@ -166,13 +174,13 @@ | modifiedHowI i == RemovedDir && modifiedByI i /= pinfo -> errorDoc $ text ("The directory '" ++ toFilePath rel_file_or_directory ++ "' was removed by")- $$ human_friendly (modifiedByI i)+ $$ humanFriendly (modifiedByI i) | otherwise -> annotatePop opts pinfo pt Just (Pop _ pt@(PopFile i)) | modifiedHowI i == RemovedFile && modifiedByI i /= pinfo -> errorDoc $ text ("The file '" ++ toFilePath rel_file_or_directory ++ "' was removed by")- $$ human_friendly (modifiedByI i)+ $$ humanFriendly (modifiedByI i) | otherwise -> annotateFile repository opts pinfo file_or_directory pt annotateCmd _ _ = fail "annotate accepts at most one argument"@@ -235,10 +243,10 @@ createdAsXml pinfo as = text "<created_as original_name='" <> escapeXML as <> text "'>"- $$ to_xml pinfo+ $$ toXml pinfo $$ text "</created_as>" --removed_by_xml :: PatchInfo -> String---removed_by_xml pinfo = "<removed_by>\n"++to_xml pinfo++"</removed_by>\n"+--removed_by_xml pinfo = "<removed_by>\n"++toXml pinfo++"</removed_by>\n" p2xmlOpen :: PatchInfo -> PopTree -> Doc p2xmlOpen _ (PopFile inf) =@@ -250,7 +258,7 @@ Nothing -> empty Just ci -> createdAsXml ci (BC.unpack $ fromJust $ creationNameI inf)- modified = modified_to_xml inf+ modified = modifiedToXml inf p2xmlOpen _ (PopDir inf _) = text "<directory name='" <> escapeXML f <> text "'>" $$ created@@ -260,7 +268,7 @@ Nothing -> empty Just ci -> createdAsXml ci (BC.unpack $ fromJust $ creationNameI inf)- modified = modified_to_xml inf+ modified = modifiedToXml inf p2xmlClose :: PatchInfo -> PopTree -> Doc p2xmlClose _(PopFile _) = text "</file>"@@ -289,7 +297,7 @@ <> showPatchInfo ci <> text (" as " ++ createdname) else putAnn $ text $ "File "++toFilePath f mk <- getMarkedupFile repository ci createdname- old_pis <- (dropWhile (/= pinfo).mapRL info.concatRL) `fmap` read_repo repository+ old_pis <- (dropWhile (/= pinfo).mapRL info.newset2RL) `fmap` readRepo repository mapM_ (annotateMarkedup opts pinfo old_pis) mk when (XMLOutput `elem` opts) $ putDocLn $ p2xmlClose pinfo (PopFile inf) where ci = fromJust $ createdByI inf@@ -340,7 +348,7 @@ | wheni `elem` old_pis = return () | otherwise = putDocLn $ text "<normal_line>" $$ text "<removed_by>"- $$ to_xml wheni+ $$ toXml wheni $$ text "</removed_by>" $$ escapeXML (BC.unpack l) $$ text "</normal_line>"@@ -350,7 +358,7 @@ $$ text "</added_line>" | wheni `elem` old_pis = putDocLn $ text "<normal_line>" $$ text "<added_by>"- $$ to_xml wheni+ $$ toXml wheni $$ text "</added_by>" $$ escapeXML (BC.unpack l) $$ text "</normal_line>"@@ -359,24 +367,24 @@ | whenadd == pinfo = putDocLn $ text "<added_line>" $$ text "<removed_by>"- $$ to_xml whenrem+ $$ toXml whenrem $$ text "</removed_by>" $$ escapeXML (BC.unpack l) $$ text "</added_line>" | whenrem == pinfo = putDocLn $ text "<removed_line>" $$ text "<added_by>"- $$ to_xml whenadd+ $$ toXml whenadd $$ text "</added_by>" $$ escapeXML (BC.unpack l) $$ text "</removed_line>" | whenadd `elem` old_pis && not (whenrem `elem` old_pis) = putDocLn $ text "<normal_line>" $$ text "<removed_by>"- $$ to_xml whenrem+ $$ toXml whenrem $$ text "</removed_by>" $$ text "<added_by>"- $$ to_xml whenadd+ $$ toXml whenadd $$ text "</added_by>" $$ escapeXML (BC.unpack l) $$ text "</normal_line>"@@ -395,14 +403,14 @@ examine a file even if it has been renamed multiple times. \begin{code}-findCreationPatchinfo :: [DarcsFlag] -> PatchSet p C(x) -> Maybe PatchInfo+findCreationPatchinfo :: [DarcsFlag] -> PatchSet p C(Origin x) -> Maybe PatchInfo findCreationPatchinfo [] _ = Nothing-findCreationPatchinfo (CreatorHash h:_) r = findHash h $ mapRL info $ concatRL r+findCreationPatchinfo (CreatorHash h:_) r = findHash h $ mapRL info $ newset2RL r findCreationPatchinfo (_:fs) r = findCreationPatchinfo fs r findHash :: String -> [PatchInfo] -> Maybe PatchInfo findHash _ [] = Nothing findHash h (pinf:pinfs)- | take (length h) (make_filename pinf) == h = Just pinf+ | take (length h) (makeFilename pinf) == h = Just pinf | otherwise = findHash h pinfs \end{code}
src/Darcs/Commands/Apply.lhs view
@@ -18,20 +18,22 @@ \darcsCommand{apply} \begin{code} {-# OPTIONS_GHC -cpp #-}-{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP, PatternGuards #-} module Darcs.Commands.Apply ( apply ) where import System.Exit ( ExitCode(..), exitWith ) import Prelude hiding ( catch )-import System.IO ( hClose, stdin, stdout, stderr )-import Control.Exception ( catch, throw, Exception( ExitException ) )+import System.IO ( hClose, stdout, stderr )+import Control.Exception.Extensible+ ( catch, fromException, SomeException, throwIO ) import Control.Monad ( when ) -import Darcs.Hopefully ( PatchInfoAnd, n2pia, conscientiously, info )+import Darcs.Hopefully ( PatchInfoAnd, n2pia, conscientiously, hopefullyM, info ) import Darcs.SignalHandler ( withSignalsBlocked ) import Darcs.Commands ( DarcsCommand(..), putVerbose )-import Darcs.CommandsAux ( check_paths )-import Darcs.Arguments ( DarcsFlag( Reply, Interactive, All ),+import Darcs.CommandsAux ( checkPaths )+import Darcs.Arguments ( DarcsFlag( Reply, Interactive, All),+ matchSeveral, definePatches, getCc, workingRepoDir, notest, nocompress, applyConflictOptions,@@ -40,37 +42,45 @@ reply, verify, listFiles, fixFilePathOrStd, umaskOption, allInteractive, sendmailCmd,- leaveTestDir, happyForwarding, + leaveTestDir, happyForwarding, dryRun, printDryRunMessageAndExit,- setScriptsExecutableOption, restrictPaths+ setScriptsExecutableOption, restrictPaths,+ changesReverse )-import Darcs.Flags(doHappyForwarding)+import Darcs.Flags(doHappyForwarding, doReverse) import qualified Darcs.Arguments as DarcsArguments ( ccApply ) import Darcs.RepoPath ( toFilePath, useAbsoluteOrStd ) import Darcs.Repository ( Repository, SealedPatchSet, withRepoLock, ($-), amInRepository, tentativelyMergePatches,- read_repo,+ readRepo, finalizeRepositoryChanges, applyToWorking, invalidateIndex )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif+import Darcs.Patch.Set ( newset2RL ) import Darcs.Patch ( RepoPatch, description )-import Darcs.Patch.Info ( PatchInfo, human_friendly )-import Darcs.Witnesses.Ordered ( FL, RL, (:\/:)(..), (:>)(..),- mapFL, nullFL, mapFL_FL, mapRL, concatRL, reverseRL )-import ByteStringUtils ( linesPS, unlinesPS )-import qualified Data.ByteString as B (ByteString, null, readFile, hGetContents, init, take, drop)+import Darcs.Patch.Info ( PatchInfo, humanFriendly )+import Darcs.Witnesses.Ordered ( FL, RL(..), (:\/:)(..), (:>)(..), (:>>)(..),+ mapFL, mapRL, nullFL, mapFL_FL, reverseFL )+import ByteStringUtils ( linesPS, unlinesPS, gzReadFilePS, gzReadStdin )+import Data.List( (\\) )+import qualified Data.ByteString as B (ByteString, null, init, take, drop) import qualified Data.ByteString.Char8 as BC (unpack, last, pack) import Darcs.External ( sendEmail, sendEmailDoc, resendEmail, verifyPS )-import Darcs.Email ( read_email )+import Darcs.Email ( readEmail ) import Darcs.Lock ( withStdoutTemp, readBinFile )-import Darcs.Patch.Depends ( get_common_and_uncommon_or_missing )-import Darcs.SelectChanges ( with_selected_changes, filterOutConflicts )-import Darcs.Patch.Bundle ( scan_bundle )+import Darcs.Patch.Depends ( findUncommon, findCommonWithThem )+import Darcs.SelectChanges ( selectChanges, WhichChanges(..),+ runSelection, selectionContext,+ filterOutConflicts )+import Darcs.Patch.Bundle ( scanBundle ) import Darcs.Witnesses.Sealed ( Sealed(Sealed) )-import Printer ( packedString, vcat, text, ($$), errorDoc, empty )+import Printer ( packedString, vcat, text, ($$), empty, renderString ) #include "impossible.h" @@ -126,22 +136,23 @@ sendmailCmd, ignoretimes, nocompress, setScriptsExecutableOption, umaskOption,- restrictPaths],+ restrictPaths, changesReverse], commandBasicOptions = [verify, allInteractive]++dryRun++- [applyConflictOptions,- useExternalMerge,- notest,- leaveTestDir,- workingRepoDir]}+ [matchSeveral,+ applyConflictOptions,+ useExternalMerge,+ notest,+ leaveTestDir,+ workingRepoDir]} applyCmd :: [DarcsFlag] -> [String] -> IO () applyCmd _ [""] = fail "Empty filename argument given to apply!" applyCmd opts [unfixed_patchesfile] = withRepoLock opts $- \repository -> do patchesfile <- fixFilePathOrStd opts unfixed_patchesfile- ps <- useAbsoluteOrStd (B.readFile . toFilePath) (B.hGetContents stdin) patchesfile+ ps <- useAbsoluteOrStd (gzReadFilePS . toFilePath) gzReadStdin patchesfile let from_whom = getFrom ps- us <- read_repo repository+ us <- readRepo repository either_them <- getPatchBundle opts ps Sealed them <- case either_them of@@ -150,24 +161,35 @@ if forwarded then exitWith ExitSuccess else fail er- (_, us':\/:them') <- case get_common_and_uncommon_or_missing (us, them) of- Left pinfo ->- if pinfo `elem` mapRL info (concatRL us)- then cannotApplyPartialRepo pinfo ""- else cannotApplyMissing pinfo- Right x -> return x- let their_ps = mapFL_FL (n2pia . conscientiously (text ("We cannot apply this patch "- ++"bundle, since we're missing:") $$))- $ reverseRL them'- (hadConflicts, Sealed their_ps_filtered) <- filterOutConflicts opts us' repository their_ps+ common :>> ours <- return $ findCommonWithThem us them++ -- all patches that are in "them" and not in "common" need to be available; check that+ let common_i = mapRL info $ newset2RL common+ them_i = mapRL info $ newset2RL them+ required = them_i \\ common_i -- FIXME quadratic?+ check :: RL (PatchInfoAnd p) C(x y) -> [PatchInfo] -> IO ()+ check (p :<: ps) bad = case hopefullyM p of+ Nothing | info p `elem` required -> check ps (info p : bad)+ _ -> check ps bad+ check NilRL [] = return ()+ check NilRL bad = fail . renderString $ vcat $ map humanFriendly bad +++ [ text "\nFATAL: Cannot apply this bundle. We are missing the above patches." ]++ check (newset2RL them) []++ (us':\/:them') <- return $ findUncommon us them+ (hadConflicts, Sealed their_ps) <- filterOutConflicts opts (reverseFL us') repository them' when hadConflicts $ putStrLn "Skipping some patches which would cause conflicts."- when (nullFL their_ps_filtered) $+ when (nullFL their_ps) $ do putStr $ "All these patches have already been applied. " ++ "Nothing to do.\n" exitWith ExitSuccess- with_selected_changes "apply" fixed_opts Nothing their_ps_filtered $- \ (to_be_applied:>_) ->- applyItNow opts from_whom repository us' to_be_applied+ let context = selectionContext "apply" fixed_opts Nothing []+ selector = if doReverse opts+ then selectChanges FirstReversed+ else selectChanges First+ (to_be_applied :> _) <- runSelection (selector their_ps) context+ applyItNow opts from_whom repository us' to_be_applied where fixed_opts = if Interactive `elem` opts then opts else All : opts@@ -175,38 +197,24 @@ applyItNow :: FORALL(p r u t x y z) RepoPatch p => [DarcsFlag] -> String -> Repository p C(r u t)- -> RL (PatchInfoAnd p) C(x r) -> FL (PatchInfoAnd p) C(x z) -> IO ()+ -> FL (PatchInfoAnd p) C(x t) -> FL (PatchInfoAnd p) C(x z) -> IO () applyItNow opts from_whom repository us' to_be_applied = do printDryRunMessageAndExit "apply" opts to_be_applied when (nullFL to_be_applied) $ do putStrLn "You don't want to apply any patches, so I'm exiting!" exitWith ExitSuccess- check_paths opts to_be_applied+ checkPaths opts to_be_applied redirectOutput opts from_whom $ do- putVerbose opts $ text "We have the following extra patches:"- putVerbose opts . vcat $ mapRL description us' putVerbose opts $ text "Will apply the following patches:" putVerbose opts . vcat $ mapFL description to_be_applied definePatches to_be_applied- Sealed pw <- tentativelyMergePatches repository "apply" opts- (reverseRL us') to_be_applied+ Sealed pw <- tentativelyMergePatches repository "apply" opts us' to_be_applied invalidateIndex repository withSignalsBlocked $ do finalizeRepositoryChanges repository- applyToWorking repository opts pw `catch` \e ->+ applyToWorking repository opts pw `catch` \(e :: SomeException) -> fail ("Error applying patch to working dir:\n" ++ show e)+ return () putStrLn "Finished applying..."--cannotApplyMissing :: PatchInfo -> a-cannotApplyMissing pinfo- = errorDoc $ text "Cannot apply this patch bundle, since we're missing:"- $$ human_friendly pinfo--cannotApplyPartialRepo :: PatchInfo -> String -> a-cannotApplyPartialRepo pinfo e- = errorDoc $ text ("Cannot apply this patch bundle, "- ++ "this is a \"--partial repository")- $$ text "We don't have the following patch:"- $$ human_friendly pinfo $$ text e \end{code} Darcs apply accepts a single argument, which is the name of the patch@@ -222,15 +230,15 @@ \begin{code} getPatchBundle :: RepoPatch p => [DarcsFlag] -> B.ByteString- -> IO (Either String (SealedPatchSet p))+ -> IO (Either String (SealedPatchSet p C(Origin))) getPatchBundle opts fps = do- mps <- verifyPS opts $ read_email fps+ mps <- verifyPS opts $ readEmail fps mops <- verifyPS opts fps case (mps, mops) of (Nothing, Nothing) -> return $ Left "Patch bundle not properly signed, or gpg failed."- (Just ps, Nothing) -> return $ scan_bundle ps- (Nothing, Just ps) -> return $ scan_bundle ps+ (Just ps, Nothing) -> return $ scanBundle ps+ (Nothing, Just ps) -> return $ scanBundle ps -- We use careful_scan_bundle only below because in either of the two -- above case we know the patch was signed, so it really shouldn't -- need stripping of CRs.@@ -238,8 +246,8 @@ Left _ -> return $ careful_scan_bundle ps2 Right x -> return $ Right x where careful_scan_bundle ps =- case scan_bundle ps of- Left e -> case scan_bundle $ stripCrPS ps of+ case scanBundle ps of+ Left e -> case scanBundle $ stripCrPS ps of Right x -> Right x _ -> Left e x -> x@@ -302,7 +310,7 @@ | B.take 5 x == fromStart = BC.unpack $ B.drop 5 x | otherwise = readFrom xs -redirectOutput :: [DarcsFlag] -> String -> IO a -> IO a+redirectOutput :: forall a . [DarcsFlag] -> String -> IO a -> IO a redirectOutput opts to doit = ro opts where cc = getCc opts@@ -313,17 +321,18 @@ hClose stderr; return a; } `catch` (sendit tempf)- where sendit tempf e@(ExitException ExitSuccess) =+ where sendit :: FilePath -> SomeException -> IO a+ sendit tempf e | Just ExitSuccess <- fromException e = do sendSanitizedEmail opts f to "Patch applied" cc tempf throwIO e- sendit tempf (ExitException _) =+ sendit tempf e | Just (_ :: ExitCode) <- fromException e = do sendSanitizedEmail opts f to "Patch failed!" cc tempf- throwIO $ ExitException ExitSuccess+ throwIO ExitSuccess sendit tempf e = do sendSanitizedEmail opts f to "Darcs error applying patch!" cc $ tempf ++ "\n\nCaught exception:\n"++ show e++"\n"- throwIO $ ExitException ExitSuccess+ throwIO ExitSuccess ro (_:fs) = ro fs -- |sendSanitizedEmail sends a sanitized email using the given sendmailcmd@@ -349,9 +358,6 @@ remove_backspaces (_:rs) ('\008':s) = remove_backspaces rs s remove_backspaces "" ('\008':s) = remove_backspaces "" s remove_backspaces rs (s:ss) = remove_backspaces (s:rs) ss--throwIO :: Exception -> IO a-throwIO e = return $ throw e forwardingMessage :: B.ByteString forwardingMessage = BC.pack $
src/Darcs/Commands/Changes.lhs view
@@ -20,15 +20,17 @@ {-# OPTIONS_GHC -cpp -fglasgow-exts #-} {-# LANGUAGE CPP, PatternGuards #-} +#include "gadts.h"+ module Darcs.Commands.Changes ( changes, log ) where import Prelude hiding ( log )+import Unsafe.Coerce (unsafeCoerce) import Data.List ( intersect, sort ) import Data.Maybe ( fromMaybe ) import Control.Monad ( when, unless ) import Darcs.Hopefully ( hopefullyM, info )-import Darcs.Patch.Depends ( slightly_optimize_patchset ) import Darcs.Commands ( DarcsCommand(..), nodefaults, commandAlias ) import Darcs.Arguments ( DarcsFlag(Context, HumanReadable, MachineReadable, Interactive, Count,@@ -45,30 +47,31 @@ ) import Darcs.Flags ( doReverse, showChangesOnlyToFiles ) import Darcs.RepoPath ( toFilePath, rootDirectory )-import Darcs.Patch.FileName ( fp2fn, fn2fp, norm_path )-import Darcs.Repository ( Repository, PatchSet, PatchInfoAnd,+import Darcs.Patch.FileName ( fp2fn, fn2fp, normPath )+import Darcs.Repository ( Repository, PatchInfoAnd, withRepositoryDirectory, ($-), findRepository,- read_repo, unrecordedChanges )-import Darcs.Patch.Info ( to_xml, showPatchInfo )-import Darcs.Patch.Depends ( get_common_and_uncommon )-import Darcs.Patch.TouchesFiles ( look_touch )+ readRepo, unrecordedChanges )+import Darcs.Patch.Set ( PatchSet(..), newset2RL )+import Darcs.Patch.Info ( toXml, showPatchInfo )+import Darcs.Patch.Depends ( findCommonWithThem )+import Darcs.Patch.Bundle( contextPatches )+import Darcs.Patch.TouchesFiles ( lookTouch ) import Darcs.Patch ( RepoPatch, invert, xmlSummary, description, applyToFilepaths, listTouchedFiles, effect, identity )-import Darcs.Witnesses.Ordered ( (:\/:)(..), RL(..), unsafeFL, unsafeUnRL, concatRL,- EqCheck(..), filterFL )+import Darcs.Witnesses.Ordered ( RL(..), EqCheck(..), filterFLFL, filterRL,+ reverseFL, (:>>)(..), mapRL ) import Darcs.Match ( firstMatch, secondMatch, matchAPatchread, haveNonrangeMatch, matchFirstPatchset, matchSecondPatchset, ) import Darcs.Commands.Annotate ( createdAsXml ) import Printer ( Doc, putDocLnWith, simplePrinters, (<+>),- renderString, prefix, text, vcat, vsep, - ($$), empty, errorDoc, insert_before_lastline )+ renderString, prefix, text, vcat, vsep,+ ($$), empty, errorDoc, insertBeforeLastline ) import Darcs.ColorPrinter ( fancyPrinters ) import Progress ( setProgressMode, debugMessage )-import Darcs.SelectChanges ( view_changes )-import Darcs.Witnesses.Sealed ( unsafeUnseal )-#include "impossible.h"+import Darcs.SelectChanges ( viewChanges )+import Darcs.Witnesses.Sealed ( Sealed2(..), unseal2, Sealed(..), FlippedSeal(..), seal2 ) changesDescription :: String changesDescription = "List patches in the repository."@@ -114,23 +117,23 @@ withRepositoryDirectory opts repodir $- \repository -> do unless (Debug `elem` opts) $ setProgressMode False files <- sort `fmap` fixSubPaths opts args- unrec <- if null files then return identity- else unrecordedChanges opts repository files- `catch` \_ -> return identity -- this is triggered when repository is remote- let filez = map (fn2fp . norm_path . fp2fn) $ applyToFilepaths (invert unrec) $ map toFilePath files+ Sealed unrec <- if null files then return (Sealed identity)+ else Sealed `fmap` unrecordedChanges opts repository files+ `catch` \_ -> return (Sealed identity) -- 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 debugMessage "About to read the repository..."- patches <- read_repo repository+ patches <- readRepo repository debugMessage "Done reading the repository." if Interactive `elem` opts then do let (fp_and_fs, _, _) = filtered_changes patches fp = map fst fp_and_fs- view_changes opts (unsafeFL fp)+ viewChanges opts fp else do when (not (null files) && not (XMLOutput `elem` opts)) $ putStrLn $ "Changes to "++unwords filez++":\n" debugMessage "About to print the changes..." let printers = if XMLOutput `elem` opts then simplePrinters else fancyPrinters- ps <- read_repo repository -- read repo again to prevent holding onto+ ps <- readRepo repository -- read repo again to prevent holding onto -- values forced by filtered_changes putDocLnWith printers $ changelog opts ps $ filtered_changes patches where maybe_reverse (xs,b,c) = if doReverse opts@@ -157,16 +160,21 @@ "whereas `darcs changes --last 3 foo.c' will, of the last three\n" ++ "patches, print only those that affect foo.c.\n" -getChangesInfo :: RepoPatch p => [DarcsFlag] -> [FilePath] -> PatchSet p- -> ([(PatchInfoAnd p, [FilePath])], [FilePath], Doc)+getChangesInfo :: RepoPatch p => [DarcsFlag] -> [FilePath]+ -> PatchSet p C(x y)+ -> ([(Sealed2 (PatchInfoAnd p), [FilePath])], [FilePath], Doc) getChangesInfo opts plain_fs ps =- case get_common_and_uncommon (p2s,p1s) of- (_,us:\/:_) -> filterPatchesByNames (maxCount opts) fs $ filter pf $ unsafeUnRL us+ case (sp1s, sp2s) of+ (Sealed p1s, Sealed p2s) ->+ case findCommonWithThem p2s p1s of+ _ :>> us -> filterPatchesByNames (maxCount opts) fs $ filterRL pf $ reverseFL us where fs = map (\x -> "./" ++ x) $ plain_fs- p1s = if firstMatch opts then unsafeUnseal $ matchFirstPatchset opts ps- else NilRL:<:NilRL- p2s = if secondMatch opts then unsafeUnseal $ matchSecondPatchset opts ps- else ps+ sp1s = if firstMatch opts+ then matchFirstPatchset opts ps+ else Sealed $ PatchSet NilRL NilRL+ sp2s = if secondMatch opts+ then matchSecondPatchset opts ps+ else Sealed $ ps pf = if haveNonrangeMatch opts then matchAPatchread opts else \_ -> True@@ -182,20 +190,20 @@ filterPatchesByNames :: RepoPatch p => Maybe Int -- ^ maxcount -> [FilePath] -- ^ filenames- -> [PatchInfoAnd p] -- ^ patchlist- -> ([(PatchInfoAnd p,[FilePath])], [FilePath], Doc)+ -> [Sealed2 (PatchInfoAnd p)] -- ^ patchlist+ -> ([(Sealed2 (PatchInfoAnd p),[FilePath])], [FilePath], Doc) filterPatchesByNames (Just 0) _ _ = ([], [], empty) filterPatchesByNames _ _ [] = ([], [], empty) filterPatchesByNames maxcount [] (hp:ps) = (hp, []) -:- filterPatchesByNames (subtract 1 `fmap` maxcount) [] ps-filterPatchesByNames maxcount fs (hp:ps)+filterPatchesByNames maxcount fs ((Sealed2 hp):ps) | Just p <- hopefullyM hp =- case look_touch fs (invert p) of- (True, []) -> ([(hp, fs)], fs, empty)- (True, fs') -> (hp, fs) -:- filterPatchesByNames- (subtract 1 `fmap` maxcount) fs' ps+ 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 _ _ (hp:_) =+filterPatchesByNames _ _ ((Sealed2 hp):_) = ([], [], text "Can't find changes prior to:" $$ description hp) -- | Note, lazy pattern matching is required to make functions like@@ -206,13 +214,14 @@ (-:-) :: a -> ([a],b,c) -> ([a],b,c) x -:- ~(xs,y,z) = (x:xs,y,z) -changelog :: RepoPatch p => [DarcsFlag] -> PatchSet p -> ([(PatchInfoAnd p, [FilePath])], [FilePath], Doc)+changelog :: RepoPatch p => [DarcsFlag] -> PatchSet p C(start x) ->+ ([(Sealed2 (PatchInfoAnd p), [FilePath])], [FilePath], Doc) -> Doc changelog opts patchset (pis_and_fs, orig_fs, errstring) | Count `elem` opts = text $ show $ length pis_and_fs | MachineReadable `elem` opts = if renderString errstring == ""- then vsep $ map (showPatchInfo.info) pis+ then vsep $ map (unseal2 (showPatchInfo.info)) pis else errorDoc errstring | XMLOutput `elem` opts = text "<changelog>"@@ -224,41 +233,45 @@ $$ errstring | otherwise = vsep (map (number_patch description') pis_and_fs) $$ errstring- where change_with_summary (hp, fs)+ where change_with_summary (Sealed2 hp, fs) | Just p <- hopefullyM hp = if showChangesOnlyToFiles opts then description hp $$ text "" $$- indent (showFriendly opts (filterFL xx $ effect p))+ indent (showFriendly opts (filterFLFL xx $ effect p)) else showFriendly opts p | otherwise = description hp $$ indent (text "[this patch is unavailable]") where xx x = case listTouchedFiles x of- ys | null $ ys `intersect` fs -> IsEq+ ys | null $ ys `intersect` fs -> unsafeCoerce IsEq+ -- in that case, the change does not affect the patches we are+ -- looking at, so we ignore the difference between the two states.+ -- It's all read-only anyway. _ -> NotEq- xml_with_summary hp- | Just p <- hopefullyM hp = insert_before_lastline- (to_xml $ info hp) (indent $ xmlSummary p)- xml_with_summary hp = to_xml (info hp)+ xml_with_summary (Sealed2 hp)+ | Just p <- hopefullyM hp = insertBeforeLastline+ (toXml $ info hp) (indent $ xmlSummary p)+ xml_with_summary (Sealed2 hp) = toXml (info hp) indent = prefix " " actual_xml_changes = if Summary `elem` opts then map xml_with_summary pis- else map (to_xml.info) pis+ else map (toXml . (unseal2 info)) pis xml_file_names = map (createdAsXml first_change) orig_fs first_change = if doReverse opts- then info $ head pis- else info $ last pis+ then unseal2 info $ head pis+ else unseal2 info $ last pis number_patch f x = if NumberPatches `elem` opts then case get_number (fst x) of Just n -> text (show n++":") <+> f x Nothing -> f x else f x- get_number :: PatchInfoAnd p -> Maybe Int- get_number y = gn 1 (concatRL patchset)+ get_number :: Sealed2 (PatchInfoAnd p) -> Maybe Int+ get_number (Sealed2 y) = gn 1 (newset2RL patchset) where iy = info y+ gn :: Int -> RL (PatchInfoAnd p) C(start x) -> Maybe Int gn n (b:<:bs) | seq n (info b) == iy = Just n | otherwise = gn (n+1) bs gn _ NilRL = Nothing pis = map fst pis_and_fs- description' = description . fst+ description' = unseal2 description . fst -- FIXME: this prose is unreadable. --twb, 2009-08 changesHelp'' :: String@@ -278,19 +291,15 @@ "WILL be output for a knowledgeable human to recreate the current state\n" ++ "of the repository.\n" -changesContext :: RepoPatch p => Repository p -> [DarcsFlag] -> IO ()+changesContext :: RepoPatch p => Repository p C(r u t) -> [DarcsFlag] -> IO () changesContext repository opts = do- r <- read_repo repository- putStrLn "\nContext:\n"- when (not $ null (unsafeUnRL r) || null (unsafeUnRL $ head $ unsafeUnRL r)) $- putDocLnWith simplePrinters $ changelog opts' NilRL $- getChangesInfo opts' []- (headRL (slightly_optimize_patchset r) :<: NilRL)- where opts' = if HumanReadable `elem` opts || XMLOutput `elem` opts- then opts- else MachineReadable : opts- headRL (x:<:_) = x- headRL NilRL = impossible+ FlippedSeal ps' <- contextPatches `fmap` readRepo repository+ let ps = mapRL (\p -> (seal2 p, [])) ps'+ unless fancy $ putStrLn "\nContext:\n"+ putDocLnWith simplePrinters $ changelog opts' emptyset (ps, [], empty)+ where opts' = if fancy then opts else MachineReadable : opts+ fancy = HumanReadable `elem` opts || XMLOutput `elem` opts+ emptyset = PatchSet NilRL NilRL log :: DarcsCommand log = commandAlias "log" Nothing changes
src/Darcs/Commands/Check.lhs view
@@ -26,7 +26,7 @@ import Darcs.Commands ( DarcsCommand(..), nodefaults, putInfo ) import Darcs.Arguments ( DarcsFlag(Quiet),- partialCheck, notest, testByDefault,+ partialCheck, test, leaveTestDir, workingRepoDir, ignoretimes ) import Darcs.Flags(willIgnoreTimes)@@ -34,12 +34,14 @@ , RepositoryConsistency(..) ) import Darcs.Repository ( Repository, amInRepository, withRepository, testRecorded, readRecorded )-import Darcs.Patch ( RepoPatch, showPatch )+import Darcs.Patch ( RepoPatch, showPatch, Prim ) import Darcs.Witnesses.Ordered ( FL(..) )+import Darcs.Witnesses.Sealed ( Sealed(..), unFreeLeft ) import Darcs.Repository.Prefs ( filetypeFunction ) import Darcs.Diff( treeDiff ) import Printer ( text, ($$), (<+>) ) +#include "gadts.h" checkDescription :: String checkDescription = "Check the repository for consistency."@@ -71,7 +73,7 @@ commandArgdefaults = nodefaults, commandAdvancedOptions = [], commandBasicOptions = [partialCheck,- notest,+ test, leaveTestDir, workingRepoDir, ignoretimes@@ -80,9 +82,10 @@ checkCmd :: [DarcsFlag] -> [String] -> IO () checkCmd opts _ = withRepository opts (check' opts) -check' :: (RepoPatch p) => [DarcsFlag] -> Repository p -> IO ()+check'+ :: forall p C(r u t) . (RepoPatch p) => [DarcsFlag] -> Repository p C(r u t) -> IO () check' opts repository = do- failed <- replayRepository repository (testByDefault opts) $ \ state -> do+ failed <- replayRepository repository opts $ \ state -> do case state of RepositoryConsistent -> do putInfo opts $ text "The repository is consistent!"@@ -105,7 +108,7 @@ putInfo opts $ text "Looks like we have a difference..." mc <- readRecorded repository ftf <- filetypeFunction- diff <- treeDiff ftf newpris mc+ Sealed (diff :: FL Prim C(r r2)) <- unFreeLeft `fmap` treeDiff ftf newpris mc :: IO (Sealed (FL Prim C(r))) putInfo opts $ case diff of NilFL -> text "Nothing" patch -> text "Difference: " <+> showPatch patch
src/Darcs/Commands/Convert.lhs view
@@ -34,41 +34,46 @@ import Darcs.Hopefully ( PatchInfoAnd, n2pia, info, hopefully ) import Darcs.Commands ( DarcsCommand(..), nodefaults, putInfo, putVerbose )-import Darcs.Arguments ( DarcsFlag( AllowConflicts, NewRepo,- SetScriptsExecutable, UseFormat2, NoUpdateWorking),- reponame,- setScriptsExecutableOption,- networkOptions )-import Darcs.Repository ( Repository, withRepoLock, ($-), withRepositoryDirectory, read_repo,+import Darcs.Arguments+ ( DarcsFlag+ ( AllowConflicts, NewRepo, SetScriptsExecutable, UseFormat2+ , NoUpdateWorking, NoLinks+ )+ , reponame+ , setScriptsExecutableOption+ , networkOptions+ )+import Darcs.Repository ( Repository, withRepoLock, ($-), withRepositoryDirectory, readRepo, createRepository, invalidateIndex,- slurp_recorded, optimizeInventory,+ optimizeInventory, tentativelyMergePatches, patchSetToPatches, createPristineDirectoryTree, revertRepositoryChanges, finalizeRepositoryChanges,- applyToWorking )+ applyToWorking, setScriptsExecutable ) import Darcs.Global ( darcsdir ) import Darcs.Patch ( RealPatch, Patch, Named, showPatch, patch2patchinfo, fromPrims, infopatch, modernizePatch, adddeps, getdeps, effect, flattenFL, isMerger, patchcontents ) import Darcs.Witnesses.Ordered ( FL(..), RL(..), EqCheck(..), (=/\=), bunchFL, mapFL, mapFL_FL,- concatFL, concatRL, mapRL )-import Darcs.Patch.Info ( pi_rename, pi_tag, is_tag )-import Darcs.Patch.Commute ( public_unravel )+ concatFL, mapRL )+import Darcs.Patch.Info ( piRename, piTag, isTag, PatchInfo )+import Darcs.Patch.Commute ( publicUnravel ) import Darcs.Patch.Real ( mergeUnravelled )+import Darcs.Patch.Set ( PatchSet(..), Tagged(..), newset2RL ) import Darcs.RepoPath ( ioAbsoluteOrRemote, toPath ) import Darcs.Repository.Format(identifyRepoFormat, formatHas, RepoProperty(Darcs2))-import Darcs.Repository.Motd ( show_motd )-import Darcs.Utils ( clarifyErrors, askUser )+import Darcs.Repository.Motd ( showMotd )+import Darcs.Utils ( clarifyErrors, askUser, catchall ) import Darcs.ProgressPatches ( progressFL ) import Darcs.Witnesses.Sealed ( FlippedSeal(..), Sealed(..) ) import Printer ( text, ($$) ) import Darcs.ColorPrinter ( traceDoc )-import Darcs.SlurpDirectory ( list_slurpy_files ) import Darcs.Lock ( writeBinFile )-import Workaround ( setExecutable )-import qualified Data.ByteString as B (isPrefixOf, readFile)-import qualified Data.ByteString.Char8 as BC (pack)+import Darcs.External+import System.FilePath.Posix +#include "gadts.h"+ convertDescription :: String convertDescription = "Convert a repository from a legacy format." @@ -126,22 +131,22 @@ convertCmd :: [DarcsFlag] -> [String] -> IO () convertCmd opts [inrepodir, outname] = convertCmd (NewRepo outname:opts) [inrepodir] convertCmd orig_opts [inrepodir] = do- + typed_repodir <- ioAbsoluteOrRemote inrepodir let repodir = toPath typed_repodir- + --test for converting darcs-2 repository- Right format <- identifyRepoFormat repodir -- just fail in case of error + Right format <- identifyRepoFormat repodir -- just fail in case of error when (formatHas Darcs2 format) $ fail "Repository is already in darcs 2 format."- + putStrLn convertHelp' let vow = "I understand the consequences of my action" putStrLn "Please confirm that you have read and understood the above" vow' <- askUser ("by typing `" ++ vow ++ "': ") when (vow' /= vow) $ fail "User didn't understand the consequences."- + let opts = UseFormat2:orig_opts- show_motd opts repodir+ showMotd opts repodir mysimplename <- makeRepoName opts repodir createDirectory mysimplename setCurrentDirectory mysimplename@@ -160,24 +165,25 @@ -- "universal" functions to do the conversion, but that's also -- unsatisfying. - let repository = unsafeCoerce# repositoryfoo :: Repository (FL RealPatch)- themrepo = unsafeCoerce# themrepobar :: Repository Patch- theirstuff <- read_repo themrepo+ let repository = unsafeCoerce# repositoryfoo :: Repository (FL RealPatch) C(r u t)+ themrepo = unsafeCoerce# themrepobar :: Repository Patch C(r u t)+ theirstuff <- readRepo themrepo let patches = mapFL_FL convertNamed $ patchSetToPatches theirstuff inOrderTags = iot theirstuff- where iot ((t:<:NilRL):<:r) = info t : iot r- iot (NilRL:<:r) = iot r- iot NilRL = []- iot ((_:<:x):<:y) = iot (x:<:y)- outOfOrderTags = catMaybes $ mapRL oot $ concatRL theirstuff- where oot t = if is_tag (info t) && not (info t `elem` inOrderTags)+ where iot :: PatchSet p C(s x) -> [PatchInfo]+ iot (PatchSet _ ts) = iot_ ts+ iot_ :: RL(Tagged t1) C(t y) -> [PatchInfo]+ iot_ (Tagged t _ _ :<: ts) = info t : iot_ ts+ iot_ NilRL = []+ outOfOrderTags = catMaybes $ mapRL oot $ newset2RL theirstuff+ where oot t = if isTag (info t) && not (info t `elem` inOrderTags) then Just (info t, getdeps $ hopefully t) else Nothing fixDep p = case lookup p outOfOrderTags of Just d -> p : concatMap fixDep d Nothing -> [p]- convertOne :: Patch -> FL RealPatch- convertOne x | isMerger x = case mergeUnravelled $ public_unravel $ modernizePatch x of+ convertOne :: Patch C(x y) -> FL RealPatch C(x y)+ convertOne x | isMerger x = case mergeUnravelled $ publicUnravel $ modernizePatch x of Just (FlippedSeal y) -> case effect y =/\= effect x of IsEq -> y :>: NilFL@@ -193,13 +199,13 @@ NilFL -> NilFL (x':>:NilFL) -> fromPrims $ effect x' xs -> concatFL $ mapFL_FL convertOne xs- convertNamed :: Named Patch -> PatchInfoAnd (FL RealPatch)+ convertNamed :: Named Patch C(x y) -> PatchInfoAnd (FL RealPatch) C(x y) convertNamed n = n2pia $ adddeps (infopatch (convertInfo $ patch2patchinfo n) $ convertOne $ patchcontents n) (map convertInfo $ concatMap fixDep $ getdeps n) convertInfo n | n `elem` inOrderTags = n- | otherwise = maybe n (\t -> pi_rename n ("old tag: "++t)) $ pi_tag n+ | otherwise = maybe n (\t -> piRename n ("old tag: "++t)) $ piTag n applySome xs = do Sealed pw <- tentativelyMergePatches repository "convert" (AllowConflicts:opts) NilFL xs finalizeRepositoryChanges repository -- this is to clean out pristine.hashed revertRepositoryChanges repository@@ -208,15 +214,13 @@ sequence_ $ mapFL applySome $ bunchFL 100 $ progressFL "Converting patch" patches invalidateIndex repository revertable $ createPristineDirectoryTree repository "."- when (SetScriptsExecutable `elem` opts) $- do putVerbose opts $ text "Making scripts executable"- c <- list_slurpy_files `fmap` slurp_recorded repository- let setExecutableIfScript f =- do contents <- B.readFile f- when (BC.pack "#!" `B.isPrefixOf` contents) $ do- putVerbose opts $ text ("Making executable: " ++ f)- setExecutable f True- mapM_ setExecutableIfScript c+ when (SetScriptsExecutable `elem` opts) $ setScriptsExecutable++ -- Copy over the prefs file+ let prefsRelPath = darcsdir </> "prefs" </> "prefs"+ copyFileOrUrl [NoLinks] (repodir </> prefsRelPath)+ prefsRelPath Uncachable `catchall` return ()+ optimizeInventory repository putInfo opts $ text "Finished converting." where revertable x = x `clarifyErrors` unlines@@ -260,5 +264,5 @@ return thename else mrn n $ i+1 where thename = if i == -1 then n else n++"_"++show i- -\end{code} ++\end{code}
src/Darcs/Commands/Diff.lhs view
@@ -29,12 +29,14 @@ import Control.Monad ( when ) import Data.List ( (\\) ) -import Darcs.External( diff_program )+import Storage.Hashed.Plain( writePlainTree )++import Darcs.External( diffProgram ) import CommandLine ( parseCmd ) import Darcs.Commands ( DarcsCommand(..), nodefaults ) import Darcs.Arguments ( DarcsFlag(DiffFlags, DiffCmd, LastN, AfterPatch),- matchRange, storeInMemory, + matchRange, storeInMemory, diffCmdFlag, diffflags, unidiff, workingRepoDir, fixSubPaths, )@@ -44,15 +46,16 @@ import Darcs.Match ( getPartialFirstMatch, getPartialSecondMatch, firstMatch, secondMatch, matchFirstPatchset, matchSecondPatchset )-import Darcs.Repository ( PatchSet, withRepository, ($-), read_repo,- amInRepository, slurp_recorded_and_unrecorded,+import Darcs.Repository ( withRepository, ($-), readRepo,+ amInRepository, createPristineDirectoryTree, createPartialsPristineDirectoryTree )-import Darcs.SlurpDirectory ( get_path_list, writeSlurpy )+import Darcs.Patch.Set ( PatchSet, newset2RL )+import Darcs.Repository.State ( readUnrecorded, restrictSubpaths ) import Darcs.Patch ( RepoPatch )-import Darcs.Witnesses.Ordered ( mapRL, concatRL )-import Darcs.Patch.Info ( PatchInfo, human_friendly )-import Darcs.External ( execPipeIgnoreError, clonePaths )+import Darcs.Witnesses.Ordered ( mapRL )+import Darcs.Patch.Info ( PatchInfo, humanFriendly )+import Darcs.External ( execPipeIgnoreError ) import Darcs.Lock ( withTempDir ) import Darcs.Witnesses.Sealed ( unseal ) import Printer ( Doc, putDoc, vcat, empty, ($$) )@@ -127,7 +130,7 @@ -- | 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 'diff_program'. Note that this potentially involves parsing the+-- default 'diffProgram'. Note that this potentially involves parsing the -- user's diff-command, hence the possibility for failure with an exception. getDiffCmdAndArgs :: String -> [DarcsFlag] -> String -> String -> Either String (String, [String])@@ -187,9 +190,9 @@ ++ " patch to the present, or use just '--patch' to view this specific" ++ " patch.") formerdir <- getCurrentDirectory- path_list <- if null args- then return []- else map sp2fn `fmap` fixSubPaths opts args+ subpaths <- if null args then return []+ else fixSubPaths opts args+ let path_list = map sp2fn subpaths thename <- return $ takeFileName formerdir withTempDir ("old-"++thename) $ \odir -> do setCurrentDirectory formerdir@@ -203,11 +206,8 @@ if secondMatch opts then withCurrentDirectory ndir $ getPartialSecondMatch repository opts path_list- else do (_, s) <- slurp_recorded_and_unrecorded repository- let ps = concatMap (get_path_list s . toFilePath) path_list- if null path_list- then withCurrentDirectory ndir $ writeSlurpy s "."- else clonePaths formerdir (toFilePath ndir) ps+ else withCurrentDirectory formerdir $ do+ readUnrecorded repository subpaths >>= (flip writePlainTree (toFilePath ndir)) thediff <- withCurrentDirectory (toFilePath odir ++ "/..") $ case path_list of [] -> rundiff (takeFileName $ toFilePath odir) (takeFileName $ toFilePath ndir)@@ -215,12 +215,12 @@ mapM (\f -> rundiff (takeFileName (toFilePath odir) ++ "/" ++ toFilePath f) (takeFileName (toFilePath ndir) ++ "/" ++ toFilePath f)) fs- morepatches <- read_repo repository+ morepatches <- readRepo repository putDoc $ changelog (getDiffInfo opts morepatches) $$ thediff where rundiff :: String -> String -> IO Doc rundiff f1 f2 = do- cmd <- diff_program+ cmd <- diffProgram case getDiffCmdAndArgs cmd opts f1 f2 of Left err -> fail err Right (d_cmd, d_args) ->@@ -233,9 +233,9 @@ return () return output -getDiffInfo :: RepoPatch p => [DarcsFlag] -> PatchSet p C(x) -> [PatchInfo]+getDiffInfo :: RepoPatch p => [DarcsFlag] -> PatchSet p C(start x) -> [PatchInfo] getDiffInfo opts ps =- let infos = mapRL info . concatRL+ let infos = mapRL info . newset2RL handle (match_cond, do_match) | match_cond opts = unseal infos (do_match opts ps) | otherwise = infos ps@@ -243,6 +243,6 @@ \\ handle (firstMatch, matchFirstPatchset) changelog :: [PatchInfo] -> Doc-changelog pis = vcat $ map human_friendly pis+changelog pis = vcat $ map humanFriendly pis \end{code}
src/Darcs/Commands/Dist.lhs view
@@ -25,6 +25,11 @@ import System.FilePath.Posix ( takeFileName, (</>) ) import Data.Char ( isAlphaNum ) import Control.Monad ( when )+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(DarcsCommand, commandName, commandHelp, commandDescription, commandExtraArgs,@@ -40,10 +45,9 @@ import Darcs.Repository ( amInRepository, withRepoReadLock, ($-), --withRecorded, createPartialsPristineDirectoryTree ) import Darcs.Repository.Prefs ( getPrefval )-import Darcs.Lock ( withTemp, withTempDir, readBinFile )+import Darcs.Lock ( withTempDir ) import Darcs.RepoPath ( AbsolutePath, toFilePath ) import Darcs.Utils ( withCurrentDirectory )-import Exec ( exec, Redirect(..) ) distDescription :: String distDescription = "Create a distribution tarball."@@ -106,36 +110,30 @@ then return [""] else map toFilePath `fmap` fixSubPaths opts args resultfile <- return (formerdir</>distname++".tar.gz")- withTemp $ \tarfile ->- withTempDir "darcsdist" $ \tempdir -> do- setCurrentDirectory (formerdir)- withTempDir (toFilePath tempdir </> takeFileName distname) $ \ddir -> do- if haveNonrangeMatch opts- then withCurrentDirectory ddir $ getNonrangeMatch repository opts- else createPartialsPristineDirectoryTree repository path_list (toFilePath ddir)- ec <- case predist of Nothing -> return ExitSuccess- Just pd -> system pd- if (ec == ExitSuccess) then doDist verb tarfile tempdir ddir resultfile- else- do- putStrLn "Dist aborted due to predist failure"- exitWith ec+ withTempDir "darcsdist" $ \tempdir -> do+ setCurrentDirectory (formerdir)+ withTempDir (toFilePath tempdir </> takeFileName distname) $ \ddir -> do+ if haveNonrangeMatch opts+ then withCurrentDirectory ddir $ getNonrangeMatch repository opts+ else createPartialsPristineDirectoryTree repository path_list (toFilePath ddir)+ ec <- case predist of Nothing -> return ExitSuccess+ Just pd -> system pd+ if (ec == ExitSuccess) then doDist verb tempdir ddir resultfile+ else+ do+ 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 :: Bool -> FilePath -> AbsolutePath -> AbsolutePath -> FilePath -> IO ()-doDist verb tarfile tempdir ddir resultfile = do+doDist :: Bool -> AbsolutePath -> AbsolutePath -> FilePath -> IO ()+doDist verb tempdir ddir resultfile = do setCurrentDirectory (toFilePath tempdir)- exec "tar" ["-cf", "-", safename $ takeFileName $ toFilePath ddir]- (Null, File tarfile, AsIs)- when verb $ withTemp $ \tar_listing -> do- exec "tar" ["-tf", "-"]- (File tarfile, File tar_listing, Stdout)- to <- readBinFile tar_listing- putStr to- exec "gzip" ["-c"]- (File tarfile, File resultfile, AsIs)+ let safeddir = safename $ takeFileName $ toFilePath ddir+ entries <- pack "." [safeddir]+ when verb $ putStr $ unlines $ map entryPath entries+ writeFile resultfile $ compress $ write entries putStrLn $ "Created dist as "++resultfile where safename n@(c:_) | isAlphaNum c = n
src/Darcs/Commands/Get.lhs view
@@ -36,27 +36,31 @@ partial, reponame, matchOneContext, setDefault, setScriptsExecutableOption, nolinks, networkOptions )-import Darcs.Repository ( Repository, withRepository, ($-), withRepoLock, identifyRepositoryFor, read_repo,+import Darcs.Repository ( Repository, withRepository, ($-), withRepoLock, identifyRepositoryFor, readRepo, createPristineDirectoryTree,- tentativelyRemovePatches, patchSetToPatches, patchSetToRepository,+ tentativelyRemovePatches, patchSetToRepository, copyRepository, tentativelyAddToPending, finalizeRepositoryChanges, setScriptsExecutable , invalidateIndex ) import Darcs.Repository.Format ( identifyRepoFormat, RepoFormat, RepoProperty ( Darcs2, HashedInventory ), formatHas )-import Darcs.Repository.DarcsRepo ( write_inventory )-import qualified Darcs.Repository.DarcsRepo as DR ( read_repo )-import Darcs.Repository ( PatchSet, SealedPatchSet, copy_oldrepo_patches,+import Darcs.Repository.DarcsRepo ( writeInventory )+import qualified Darcs.Repository.DarcsRepo as DR ( readRepo )+import Darcs.Repository ( SealedPatchSet, copyOldrepoPatches, createRepository)-import Darcs.Repository.ApplyPatches ( apply_patches )-import Darcs.Repository.Checkpoint ( write_checkpoint_patch, get_checkpoint )-import Darcs.Patch ( RepoPatch, Patch, apply, patch2patchinfo, invert,- effect, description )-import Darcs.Witnesses.Ordered ( (:\/:)(..), RL(..), mapRL, concatRL, reverseRL, lengthFL )+import Darcs.Patch.Set ( PatchSet(..), newset2RL )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif+import Darcs.Repository.ApplyPatches ( applyPatches )+import Darcs.Repository.Checkpoint ( writeCheckpointPatch, getCheckpoint )+import Darcs.Patch ( RepoPatch, Patch, apply, patch2patchinfo, invert, effect )+import Darcs.Witnesses.Ordered ( RL(..), reverseRL, lengthFL, mapFL_FL, (:>>)(..) ) import Darcs.External ( copyFileOrUrl, Cachable(..) )-import Darcs.Patch.Depends ( get_common_and_uncommon, get_patches_beyond_tag )+import Darcs.Hopefully ( hopefully )+import Darcs.Patch.Depends ( findCommonWithThem, countUsThem, getPatchesBeyondTag ) import Darcs.Repository.Prefs ( setDefaultrepo )-import Darcs.Repository.Motd ( show_motd )+import Darcs.Repository.Motd ( showMotd ) import Darcs.Repository.Pristine ( identifyPristine, createPristineFromWorking, ) import Darcs.SignalHandler ( catchInterrupt ) import Darcs.Commands.Init ( initialize )@@ -66,10 +70,11 @@ import Printer ( text, vcat, errorDoc, ($$) ) import Darcs.Lock ( writeBinFile ) import Darcs.RepoPath ( toFilePath, toPath, ioAbsoluteOrRemote)-import Darcs.Witnesses.Sealed ( Sealed(..), unsafeUnflippedseal )+import Darcs.Witnesses.Sealed ( Sealed(..), FlippedSeal(..) ) import Darcs.Global ( darcsdir ) import English ( englishNum, Noun(..) ) #include "impossible.h"+#include "gadts.h" getDescription :: String getDescription = "Create a local copy of a repository."@@ -126,7 +131,7 @@ commandBasicOptions = [reponame, partial, matchOneContext,- setDefault,+ setDefault True, setScriptsExecutableOption, nolinks, getInventoryChoices]}@@ -140,7 +145,7 @@ debugMessage "Starting work on get..." typed_repodir <- ioAbsoluteOrRemote inrepodir let repodir = toPath typed_repodir- show_motd opts repodir+ showMotd opts repodir when (Partial `elem` opts) $ debugMessage "Reading checkpoint..." rfsource_or_e <- identifyRepoFormat repodir rfsource <- case rfsource_or_e of Left e -> fail e@@ -165,7 +170,7 @@ Right x -> return x if formatHas HashedInventory rf -- refactor this into repository then writeBinFile (darcsdir++"/hashed_inventory") ""- else write_inventory "." (NilRL:<:NilRL :: PatchSet Patch)+ else writeInventory "." (PatchSet NilRL NilRL :: PatchSet Patch C(Origin Origin)) if not (null [p | OnePattern p <- opts]) -- --to-match given && not (Partial `elem` opts) && not (Lazy `elem` opts)@@ -183,11 +188,11 @@ -- right format has already been created. copyRepoAndGoToChosenVersion :: [DarcsFlag] -> String -> RepoFormat -> RepoFormat -> IO () copyRepoAndGoToChosenVersion opts repodir rfsource rf = do- copy_repo `catchInterrupt` (when (formatHas HashedInventory rfsource)+ copyRepo `catchInterrupt` (when (formatHas HashedInventory rfsource) (putInfo opts $ text "Using lazy repository.")) withRepository opts $- \repository -> goToChosenVersion repository opts putInfo opts $ text "Finished getting."- where copy_repo =+ where copyRepo = withRepository opts $- \repository -> do let hashUs = formatHas HashedInventory rf hashThem = formatHas HashedInventory rfsource@@ -273,25 +278,23 @@ then return $ Right () else return . Left $ "Context file "++toFilePath f++" does not exist" -goToChosenVersion :: RepoPatch p => Repository p+goToChosenVersion :: RepoPatch p => Repository p C(r u r) -> [DarcsFlag] -> IO () goToChosenVersion repository opts = when (havePatchsetMatch opts) $ do debugMessage "Going to specified version..."- patches <- read_repo repository+ patches <- readRepo repository Sealed context <- getOnePatchset repository opts- let (_,us':\/:them') = get_common_and_uncommon (patches, context)- case them' of- NilRL -> return ()- _ -> errorDoc $ text "Missing these patches from context:"- $$ (vcat $ mapRL description them')- let ps = patchSetToPatches (us':<:NilRL)+ when (snd (countUsThem patches context) > 0) $+ errorDoc $ text "Missing patches from context!" -- FIXME : - (+ _ :>> us' <- return $ findCommonWithThem patches context+ let ps = mapFL_FL hopefully us' putInfo opts $ text $ "Unapplying " ++ (show $ lengthFL ps) ++ " " ++ (englishNum (lengthFL ps) (Noun "patch") "") invalidateIndex repository withRepoLock opts $- \_ ->- do tentativelyRemovePatches repository opts ps- tentativelyAddToPending repository opts $ invert $ effect ps+ do tentativelyRemovePatches repository opts us'+ tentativelyAddToPending repository opts $ invert $ effect us' finalizeRepositoryChanges repository apply opts (invert $ effect ps) `catch` \e -> fail ("Couldn't undo patch in working dir.\n" ++ show e)@@ -306,18 +309,18 @@ "For modern darcs-2 repositories, --partial is a deprecated alias for\n" ++ "the --lazy option.\n" -copyRepoOldFashioned :: RepoPatch p => Repository p -> [DarcsFlag] -> String -> IO ()+copyRepoOldFashioned :: RepoPatch p => Repository p C(r u t) -> [DarcsFlag] -> String -> IO () copyRepoOldFashioned repository opts repodir = do myname <- getCurrentDirectory fromrepo <- identifyRepositoryFor repository repodir- mch <- get_checkpoint fromrepo- patches <- read_repo fromrepo+ mch <- getCheckpoint fromrepo+ patches <- readRepo fromrepo debugMessage "Getting the inventory..."- write_inventory "." patches+ writeInventory "." patches debugMessage "Copying patches..."- copy_oldrepo_patches opts fromrepo "."+ copyOldrepoPatches opts fromrepo "." debugMessage "Patches copied"- Sealed local_patches <- DR.read_repo opts "." :: IO (SealedPatchSet Patch)+ Sealed local_patches <- DR.readRepo opts "." :: IO (SealedPatchSet Patch C(Origin)) debugMessage "Repo read" repo_is_local <- doesDirectoryExist repodir debugMessage $ "Repo local: " ++ formatPath (show repo_is_local)@@ -338,15 +341,14 @@ else do setCurrentDirectory myname if Partial `elem` opts && isJust mch- then let Sealed p_ch = fromJust mch- pi_ch = patch2patchinfo p_ch- needed_patches = reverseRL $ unsafeUnflippedseal $- get_patches_beyond_tag pi_ch local_patches- in do write_checkpoint_patch p_ch- apply opts p_ch `catch`- \e -> fail ("Bad checkpoint!!!\n" ++ prettyError e)- apply_patches opts needed_patches- else apply_patches opts $ reverseRL $ concatRL local_patches+ then do Sealed p_ch <- return (fromJust mch)+ let pi_ch = patch2patchinfo p_ch+ FlippedSeal needed_patches <- return (getPatchesBeyondTag pi_ch local_patches)+ writeCheckpointPatch p_ch+ apply opts p_ch `catch`+ \e -> fail ("Bad checkpoint!!!\n" ++ prettyError e)+ applyPatches opts (reverseRL needed_patches)+ else applyPatches opts $ reverseRL $ newset2RL local_patches debugMessage "Writing the pristine" pristine <- identifyPristine createPristineFromWorking pristine
src/Darcs/Commands/MarkConflicts.lhs view
@@ -27,16 +27,17 @@ import Darcs.Commands ( DarcsCommand(..), nodefaults, commandAlias ) import Darcs.Arguments ( DarcsFlag, ignoretimes, workingRepoDir, umaskOption )-import Darcs.Repository ( withRepoLock, ($-), amInRepository, add_to_pending,+import Darcs.Repository ( withRepoLock, ($-), amInRepository, addToPending, applyToWorking,- read_repo, unrecordedChanges+ readRepo, unrecordedChanges, Repository )-import Darcs.Patch ( invert )+import Darcs.Patch ( invert, Prim ) import Darcs.Witnesses.Ordered ( FL(..) ) import Darcs.Witnesses.Sealed ( Sealed(Sealed) )-import Darcs.Resolution ( patchset_conflict_resolutions )+import Darcs.Resolution ( patchsetConflictResolutions ) import Darcs.Utils ( promptYorn ) #include "impossible.h"+#include "gadts.h" markconflictsDescription :: String markconflictsDescription =@@ -77,25 +78,28 @@ workingRepoDir]} markconflictsCmd :: [DarcsFlag] -> [String] -> IO ()-markconflictsCmd opts [] = withRepoLock opts $- \repository -> do+markconflictsCmd opts [] = withRepoLock opts $- \(repository :: Repository p C(r u r)) -> do pend <- unrecordedChanges opts repository []- r <- read_repo repository- Sealed res <- return $ patchset_conflict_resolutions r- case res of NilFL -> do putStrLn "No conflicts to mark."- exitWith ExitSuccess- _ -> return ()- case pend of- NilFL -> return ()- _ -> do putStrLn ("This will trash any unrecorded changes"+++ r <- readRepo repository+ Sealed res <- return $ patchsetConflictResolutions r+ (case res of NilFL -> do putStrLn "No conflicts to mark."+ exitWith ExitSuccess+ _ -> return ()) :: IO ()+ let undoUnrec :: FL Prim C(r u) -> IO (Repository p C(r r r))+ undoUnrec NilFL = return repository+ undoUnrec pend =+ do putStrLn ("This will trash any unrecorded changes"++ " in the working directory.") yorn <- promptYorn "Are you sure? " when (yorn /= 'y') $ exitWith ExitSuccess applyToWorking repository opts (invert pend) `catch` \e -> bug ("Can't undo pending changes!" ++ show e)+ repository' <- undoUnrec pend withSignalsBlocked $- do add_to_pending repository res- applyToWorking repository opts res `catch` \e ->+ do addToPending repository' res+ applyToWorking repository' opts res `catch` \e -> bug ("Problem marking conflicts in mark-conflicts!" ++ show e)+ return () putStrLn "Finished marking conflicts." markconflictsCmd _ _ = impossible
src/Darcs/Commands/Move.lhs view
@@ -21,6 +21,7 @@ {-# LANGUAGE CPP #-} module Darcs.Commands.Move ( move, mv ) where+import Control.Applicative ( (<$>) ) import Control.Monad ( when, unless, zipWithM_ ) import Data.Maybe ( catMaybes ) import Darcs.SignalHandler ( withSignalsBlocked )@@ -31,23 +32,27 @@ listFiles, allowProblematicFilenames, umaskOption, ) import Darcs.Flags ( doAllowCaseOnly, doAllowWindowsReserved )-import Darcs.RepoPath ( toFilePath, sp2fn )+import Darcs.RepoPath ( toFilePath ) import System.FilePath.Posix ( (</>), takeFileName ) import System.Directory ( renameDirectory ) import Workaround ( renameFile )-import Darcs.Repository ( Repository, withRepoLock, ($-), amInRepository,- slurp_pending, add_to_pending,- )-import Darcs.Witnesses.Ordered ( FL(..), unsafeFL )+import Darcs.Repository.State ( readRecordedAndPending )+import Darcs.Repository ( Repository, withRepoLock, ($-), amInRepository, addToPending )+import Darcs.Witnesses.Ordered ( FL(..), toFL )+import Darcs.Witnesses.Sealed ( Sealed(..), unseal, freeGap, FreeLeft, unFreeLeft ) import Darcs.Global ( debugMessage ) import qualified Darcs.Patch import Darcs.Patch ( RepoPatch, Prim )-import Darcs.SlurpDirectory ( Slurpy, slurp, slurp_has, slurp_has_anycase,- slurp_remove, slurp_hasdir, slurp_hasfile ) import Darcs.Patch.FileName ( fp2fn, fn2fp, superName ) import qualified System.FilePath.Windows as WindowsFilePath +import Darcs.Utils( treeHas, treeHasDir, treeHasAnycase, treeHasFile )+import Storage.Hashed.Tree( Tree, modifyTree )+import Storage.Hashed.Plain( readPlainTree )+import Storage.Hashed.AnchoredPath( floatPath )+ #include "impossible.h"+#include "gadts.h" moveDescription :: String moveDescription = "Move or rename files."@@ -90,18 +95,20 @@ [_,_] -> two_files [_] -> error "Cannot rename a file or directory onto itself!" xs -> bug $ "Problem in moveCmd: " ++ show xs- work <- slurp "."+ work <- readPlainTree "." let old_fp = toFilePath old new_fp = toFilePath new- if slurp_hasdir (sp2fn new) work && slurp_has old_fp work+ has_new <- treeHasDir work new_fp+ has_old <- treeHas work old_fp+ if has_new && has_old then moveToDir repository opts [old_fp] new_fp else do- cur <- slurp_pending repository- addpatch <- check_new_and_old_filenames opts cur work (old_fp,new_fp)+ cur <- readRecordedAndPending repository+ addpatch <- checkNewAndOldFilenames opts cur work (old_fp,new_fp) withSignalsBlocked $ do- case addpatch of- Nothing -> add_to_pending repository (Darcs.Patch.move old_fp new_fp :>: NilFL)- Just p -> add_to_pending repository (p :>: Darcs.Patch.move old_fp new_fp :>: NilFL)+ case unFreeLeft <$> addpatch of+ Nothing -> addToPending repository (Darcs.Patch.move old_fp new_fp :>: NilFL)+ Just (Sealed p) -> addToPending repository (p :>: Darcs.Patch.move old_fp new_fp :>: NilFL) moveFileOrDir work old_fp new_fp moveCmd opts args =@@ -111,50 +118,52 @@ finaldir = last relpaths moveToDir repository opts moved finaldir -moveToDir :: RepoPatch p => Repository p -> [DarcsFlag] -> [FilePath] -> FilePath -> IO ()+moveToDir :: RepoPatch p => Repository p C(r u t) -> [DarcsFlag] -> [FilePath] -> FilePath -> IO () moveToDir repository opts moved finaldir = let movefns = map takeFileName moved movetargets = map (finaldir </>) movefns- movepatches = zipWith Darcs.Patch.move moved movetargets+ movepatches = zipWith (\a b -> freeGap (Darcs.Patch.move a b)) moved movetargets in do- cur <- slurp_pending repository- work <- slurp "."- addpatches <- mapM (check_new_and_old_filenames opts cur work) $ zip moved movetargets+ cur <- readRecordedAndPending repository+ work <- readPlainTree "."+ addpatches <- mapM (checkNewAndOldFilenames opts cur work) $ zip moved movetargets withSignalsBlocked $ do- add_to_pending repository $ unsafeFL $ catMaybes addpatches ++ movepatches+ unseal (addToPending repository) $ toFL $ catMaybes addpatches ++ movepatches zipWithM_ (moveFileOrDir work) moved movetargets -check_new_and_old_filenames- :: [DarcsFlag] -> Slurpy -> Slurpy -> (FilePath, FilePath) -> IO (Maybe Prim)-check_new_and_old_filenames opts cur work (old,new) = do+checkNewAndOldFilenames+ :: [DarcsFlag] -> Tree IO -> Tree IO -> (FilePath, FilePath) -> IO (Maybe (FreeLeft Prim))+checkNewAndOldFilenames opts cur work (old,new) = do unless (doAllowWindowsReserved opts || WindowsFilePath.isValid new) $ fail $ "The filename " ++ new ++ " is not valid under Windows.\n" ++ "Use --reserved-ok to allow such filenames."+ has_work <- treeHas work old+ has_cur <- treeHas cur old maybe_add_file_thats_been_moved <-- if slurp_has old work -- We need to move the object- then do unless (slurp_hasdir (superName $ fp2fn new) work) $+ if has_work -- We need to move the object+ then do has_target <- treeHasDir work (fn2fp $ superName $ fp2fn new)+ unless has_target $ fail $ "The target directory " ++ (fn2fp $ superName $ fp2fn new)++ " isn't known in working directory, did you forget to add it?"- when (it_has new work) $ fail $ already_exists "working directory"+ has_new <- it_has work+ when has_new $ fail $ already_exists "working directory" return Nothing- else do unless (slurp_has new work) $ fail $ doesnt_exist "working directory"- return $ Just $ Darcs.Patch.addfile old- if slurp_has old cur- then do unless (slurp_hasdir (superName $ fp2fn new) cur) $+ else do has_new <- treeHas work new+ unless has_new $ fail $ doesnt_exist "working directory"+ return (Just (freeGap (Darcs.Patch.addfile old)))+ if has_cur+ then do has_target <- treeHasDir cur (fn2fp $ superName $ fp2fn new)+ unless has_target $ fail $ "The target directory " ++ (fn2fp $ superName $ fp2fn new)++ " isn't known in working directory, did you forget to add it?"- when (it_has new cur) $ fail $ already_exists "repository"+ has_new <- it_has cur+ when has_new $ fail $ already_exists "repository" else fail $ doesnt_exist "repository" return maybe_add_file_thats_been_moved- where it_has f s = - let ms2 = slurp_remove (fp2fn old) s- in case ms2 of- Nothing -> False- Just s2 -> if doAllowCaseOnly opts- then slurp_has f s2- else slurp_has_anycase f s2+ where it_has s = treeHas_case (modifyTree s (floatPath old) Nothing) new+ treeHas_case = if doAllowCaseOnly opts then treeHas else treeHasAnycase already_exists what_slurpy = if doAllowCaseOnly opts then "A file or dir named "++new++" already exists in "@@ -167,15 +176,14 @@ "There is no file or dir named " ++ old ++ " in the "++ what_slurpy ++ "." -moveFileOrDir :: Slurpy -> FilePath -> FilePath -> IO ()-moveFileOrDir work old new- | slurp_hasfile (fp2fn old) work =- do debugMessage $ unwords ["renameFile",old,new]- renameFile old new- | slurp_hasdir (fp2fn old) work =- do debugMessage $ unwords ["renameDirectory",old,new]- renameDirectory old new- | otherwise = return ()+moveFileOrDir :: Tree IO -> FilePath -> FilePath -> IO ()+moveFileOrDir work old new = do+ has_file <- treeHasFile work old+ has_dir <- treeHasDir work old+ when has_file $ do debugMessage $ unwords ["renameFile",old,new]+ renameFile old new+ when has_dir $ do debugMessage $ unwords ["renameDirectory",old,new]+ renameDirectory old new mv :: DarcsCommand mv = commandAlias "mv" Nothing move
src/Darcs/Commands/Optimize.lhs view
@@ -21,57 +21,59 @@ {-# LANGUAGE CPP #-} module Darcs.Commands.Optimize ( optimize ) where+import Control.Applicative ( (<$>) ) import Control.Monad ( when, unless ) import Data.Maybe ( isJust )-import System.Directory ( getDirectoryContents, doesDirectoryExist, doesFileExist )+import System.Directory ( getDirectoryContents, doesDirectoryExist,+ doesFileExist, renameFile )+import System.IO.Unsafe ( unsafeInterleaveIO ) import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Lazy as BL import Storage.Hashed.Darcs( decodeDarcsSize ) -import Darcs.Hopefully ( hopefully, info )+import Darcs.Hopefully ( info ) import Darcs.Commands ( DarcsCommand(..), nodefaults ) import Darcs.Arguments ( DarcsFlag( UpgradeFormat, UseHashedInventory, Compress, UnCompress, NoCompress, Reorder,- Relink, RelinkPristine, OptimizePristine ),+ Relink, RelinkPristine, OptimizePristine,+ OptimizeHTTP ), reorderPatches, uncompressNocompress, relink, relinkPristine, sibling, flagsToSiblings, upgradeFormat,- workingRepoDir, umaskOption, optimizePristine+ workingRepoDir, umaskOption, optimizePristine,+ -- optimizeHTTP -- disabled for darcs-2.5 ) import Darcs.Repository.Prefs ( getPreflist )-import Darcs.Repository ( Repository, PatchSet, withRepoLock, ($-), withGutsOf,- read_repo, optimizeInventory, slurp_recorded,+import Darcs.Repository ( Repository,+ withRepoLock, ($-), withGutsOf,+ readRepo, optimizeInventory, tentativelyReplacePatches, cleanRepository, amInRepository, finalizeRepositoryChanges, replacePristine )-import Darcs.Witnesses.Ordered ( RL(..), unsafeUnRL, (+<+), mapFL_FL, reverseRL, mapRL, concatRL )-import Darcs.Patch.Info ( PatchInfo, just_name )+import Darcs.Witnesses.Ordered ( (+<+), reverseRL, mapRL, (:>>)(..)+ , mapFL, bunchFL, lengthRL )+import Darcs.Patch.Info ( isTag ) import Darcs.Patch ( RepoPatch )+import Darcs.Patch.Set ( PatchSet(..), newset2RL, newset2FL, progressPatchSet ) import ByteStringUtils ( gzReadFilePS )-import Darcs.Patch.Depends ( slightly_optimize_patchset,- get_patches_beyond_tag, get_patches_in_tag,- )+import Darcs.Patch.Depends ( splitOnTag ) import Darcs.Lock ( maybeRelink, gzWriteAtomicFilePS, writeAtomicFilePS ) import Darcs.RepoPath ( toFilePath ) import Darcs.Utils ( withCurrentDirectory ) import Progress ( debugMessage )-import Darcs.SlurpDirectory ( slurp, list_slurpy_files ) import Darcs.Repository.Pristine ( identifyPristine, pristineDirectory )-import Darcs.Witnesses.Sealed ( FlippedSeal(..), unsafeUnseal ) import Darcs.Global ( darcsdir )-#include "impossible.h"+ -- imports for optimize --upgrade; to be tidied-import qualified Data.ByteString as B (empty) import System.Directory ( createDirectoryIfMissing, removeFile )-import System.FilePath.Posix ( takeExtension, (</>) )+import System.FilePath.Posix ( takeExtension, (</>), (<.>) ) -import Progress ( beginTedious, endTedious, tediousSize, progress )-import SHA1 ( sha1PS )+import Progress ( beginTedious, endTedious, tediousSize ) import Darcs.Flags ( compression )-import Darcs.Lock ( rm_recursive )-import Darcs.Witnesses.Ordered ( mapFL, mapRL_RL, bunchFL, lengthRL )+import Darcs.Lock ( rmRecursive ) import Darcs.ProgressPatches ( progressFL ) import Darcs.Repository.Cache ( hashedDir, HashedDir(HashedPristineDir) ) import Darcs.Repository.Format ( identifyRepoFormat,@@ -82,6 +84,16 @@ import Darcs.Repository.Repair ( replayRepository, RepositoryConsistency(..) ) import Darcs.Repository.State ( readRecorded ) import Darcs.Utils ( catchall )++import Storage.Hashed.Tree( TreeItem(..), list, expand, emptyTree )+import Storage.Hashed.AnchoredPath( anchorPath )+import Storage.Hashed.Plain( readPlainTree )+import Storage.Hashed.Darcs( writeDarcsHashed )++import Codec.Archive.Tar ( write )+import Codec.Archive.Tar.Entry ( fileEntry, toTarPath )+import Codec.Compression.GZip ( compress )+ #include "gadts.h" optimizeDescription :: String@@ -125,12 +137,14 @@ sibling, relink, relinkPristine, upgradeFormat,- optimizePristine]}+ optimizePristine+ ]} --optimizeHTTP]} -- disabled for 2.5 optimizeCmd :: [DarcsFlag] -> [String] -> IO () optimizeCmd origopts _ = do when (UpgradeFormat `elem` origopts) optimizeUpgradeFormat withRepoLock opts $- \repository -> do+ when (OptimizeHTTP `elem` origopts) doOptimizeHTTP if (OptimizePristine `elem` opts) then doOptimizePristine repository else do cleanRepository repository@@ -142,8 +156,6 @@ doRelink opts repository putStrLn "Done optimizing!" where opts = if UnCompress `elem` origopts then NoCompress:origopts else origopts-isTag :: PatchInfo -> Bool-isTag pinfo = take 4 (just_name pinfo) == "TAG " optimizeHelpInventory :: String optimizeHelpInventory =@@ -152,7 +164,7 @@ "remote command needs to download. It should also reduce the CPU time\n" ++ "needed for some operations.\n" -doOptimizeInventory :: RepoPatch p => Repository p -> IO ()+doOptimizeInventory :: RepoPatch p => Repository p C(r u t) -> IO () doOptimizeInventory repository = do debugMessage "Writing out a nice copy of the inventory." optimizeInventory repository@@ -204,7 +216,7 @@ "generally SHOULD NOT be used. It results in a relatively small space\n" ++ "saving at the cost of making many Darcs commands MUCH slower.\n" -doOptimizePristine :: RepoPatch p => Repository p -> IO ()+doOptimizePristine :: RepoPatch p => Repository p C(r u t) -> IO () doOptimizePristine repo = do hashed <- doesFileExist $ darcsdir </> "hashed_inventory" when hashed $ do@@ -218,33 +230,33 @@ readRecorded repo >>= replacePristine repo cleanRepository repo -doRelink :: RepoPatch p => [DarcsFlag] -> Repository p -> IO ()+doRelink :: RepoPatch p => [DarcsFlag] -> Repository p C(r u t) -> IO () doRelink opts repository = do some_siblings <- return (flagsToSiblings opts) defrepolist <- getPreflist "defaultrepo" siblings <- return (map toFilePath some_siblings ++ defrepolist)- if (siblings == []) + if (siblings == []) then putStrLn "No siblings -- no relinking done." else do when (Relink `elem` opts) $ do debugMessage "Relinking patches..."- patches <-- (fmap list_slurpy_files) (slurp $ darcsdir++"/patches")- maybeRelinkFiles siblings patches (darcsdir++"/patches")+ patch_tree <- expand =<< readPlainTree "_darcs/patches"+ let patches = [ anchorPath "" p | (p, File _) <- list patch_tree ]+ maybeRelinkFiles siblings patches "_darcs/patches" when (RelinkPristine `elem` opts) $ do pristine <- identifyPristine case (pristineDirectory pristine) of (Just d) -> do debugMessage "Relinking pristine tree..."- c <- slurp_recorded repository+ c <- readRecorded repository maybeRelinkFiles- siblings (list_slurpy_files c) d+ siblings [ anchorPath "" p | (p, File _) <- list c ] d Nothing -> return () debugMessage "Done relinking." return () return () maybeRelinkFiles :: [String] -> [String] -> String -> IO ()-maybeRelinkFiles src dst dir = +maybeRelinkFiles src dst dir = mapM_ (maybeRelinkFile src) (map ((dir ++ "/") ++) dst) maybeRelinkFile :: [String] -> String -> IO ()@@ -278,31 +290,24 @@ -- "of the default optimization. It reorders patches with respect to ALL\n" ++ -- "tags, rather than just the latest tag.\n" -doReorder :: RepoPatch p => [DarcsFlag] -> Repository p -> IO ()+doReorder :: RepoPatch p => [DarcsFlag] -> Repository p C(r u r) -> IO () doReorder opts _ | not (Reorder `elem` opts) = return () doReorder opts repository = do debugMessage "Reordering the inventory."- psnew <- chooseOrder `fmap` read_repo repository- let ps = mapFL_FL hopefully $ reverseRL $ head $ unsafeUnRL psnew- withGutsOf repository $ do tentativelyReplacePatches repository opts ps+ PatchSet ps _ <- chooseOrder `fmap` readRepo repository+ withGutsOf repository $ do tentativelyReplacePatches repository opts $ reverseRL ps finalizeRepositoryChanges repository debugMessage "Done reordering the inventory." -chooseOrder :: RepoPatch p => PatchSet p -> PatchSet p-chooseOrder ps | isJust last_tag =- case slightly_optimize_patchset $ unsafeUnseal $ get_patches_in_tag lt ps of - ((t:<:NilRL):<:pps) -> case get_patches_beyond_tag lt ps of- FlippedSeal p -> (p+<+(t:<:NilRL)) :<: pps- _ -> impossible - where last_tag = case filter isTag $ mapRL info $ concatRL ps of- (t:_) -> Just t- _ -> Nothing- lt = fromJust last_tag-chooseOrder ps = ps+chooseOrder :: forall p C(s x) . RepoPatch p => PatchSet p C(s x) -> PatchSet p C(s x)+chooseOrder ps = case filter isTag $ mapRL info $ newset2RL ps of+ [] -> ps+ (lt:_) -> case splitOnTag lt ps of+ PatchSet xs ts :>> r -> PatchSet (r+<+xs) ts \end{code} The \verb|--upgrade| option for \verb!darcs optimize! performs an inplace-upgrade of your repository to the lastest \emph{compatible} format. Right now+upgrade of your repository to the latest \emph{compatible} format. Right now means that darcs 1 old-fashioned repositories will be upgraded to darcs-1 hashed repositories (and notably, not to darcs 2 repositories as that would not be compatible; see \verb!darcs convert!).@@ -328,38 +333,68 @@ actuallyUpgradeFormat :: RepoPatch p => Repository p C(r u t) -> IO () actuallyUpgradeFormat repository = do -- convert patches/inventory- patches <- read_repo repository+ patches <- readRepo repository let k = "Hashing patch" beginTedious k- tediousSize k (lengthRL $ concatRL patches)- let patches' = mapRL_RL (mapRL_RL (progress k)) patches+ tediousSize k (lengthRL $ newset2RL patches)+ let patches' = progressPatchSet k patches cache <- getCaches [] "." let compr = compression [] -- default compression- HashedRepo.write_tentative_inventory cache compr patches'+ HashedRepo.writeTentativeInventory cache compr patches' endTedious k -- convert pristine by applying patches -- the faster alternative would be to copy pristine, but the apply method is more reliable- let patchesToApply = progressFL "Applying patch" $ reverseRL $ concatRL $ patches'+ let patchesToApply = progressFL "Applying patch" $ newset2FL $ patches' createDirectoryIfMissing False $ darcsdir </> hashedDir HashedPristineDir- writeFile (darcsdir </> hashedDir HashedPristineDir </> sha1PS B.empty) ""- sequence_ $ mapFL (HashedRepo.apply_to_tentative_pristine cache []) $ bunchFL 100 patchesToApply+ writeDarcsHashed emptyTree "_darcs/pristine.hashed"+ sequence_ $ mapFL (HashedRepo.applyToTentativePristine []) $ bunchFL 100 patchesToApply -- now make it official- HashedRepo.finalize_tentative_changes repository compr+ HashedRepo.finalizeTentativeChanges repository compr writeRepoFormat (createRepoFormat [UseHashedInventory]) (darcsdir </> "format") -- clean out old-fashioned junk debugMessage "Cleaning out old-fashioned repository files..." removeFile $ darcsdir </> "inventory" removeFile $ darcsdir </> "tentative_inventory"- rm_recursive (darcsdir </> "pristine") `catchall` rm_recursive (darcsdir </> "current")+ rmRecursive (darcsdir </> "pristine") `catchall` rmRecursive (darcsdir </> "current") rmGzsIn (darcsdir </> "patches") rmGzsIn (darcsdir </> "inventories") let checkpointDir = darcsdir </> "checkpoints" hasCheckPoints <- doesDirectoryExist checkpointDir- when hasCheckPoints $ rm_recursive checkpointDir+ when hasCheckPoints $ rmRecursive checkpointDir putStrLn "Done upgrading!" where rmGzsIn dir = withCurrentDirectory dir $ do gzs <- filter ((== ".gz") . takeExtension) `fmap` getDirectoryContents "." mapM_ removeFile gzs++doOptimizeHTTP :: IO ()+doOptimizeHTTP = do+ rf <- either fail return =<< identifyRepoFormat "."+ unless (formatHas HashedInventory rf) . fail $+ "Unsupported repository format:\n" +++ " only hashed repositories can be optimized for HTTP"+ createDirectoryIfMissing False packsDir+ ps <- dirContents' "patches" $ \x -> all (x /=) ["unrevert", "pending",+ "pending.tentative"]+ BL.writeFile (patchesTar <.> "part") . compress . write =<<+ mapM fileEntry' ps+ renameFile (patchesTar <.> "part") patchesTar+ let i = darcsdir </> "hashed_inventory"+ is <- dirContents "inventories"+ pr <- dirContents "pristine.hashed"+ BL.writeFile (basicTar <.> "part") . compress . write =<<+ mapM fileEntry' (i : (is ++ pr))+ renameFile (basicTar <.> "part") basicTar+ where+ packsDir = darcsdir </> "packs"+ basicTar = packsDir </> "basic.tar.gz"+ patchesTar = packsDir </> "patches.tar.gz"+ fileEntry' x = unsafeInterleaveIO $ do+ content <- BL.fromChunks . return <$> gzReadFilePS x+ tp <- either fail return $ toTarPath False x+ return $ fileEntry tp content+ dirContents d = dirContents' d $ const True+ dirContents' d f = map ((darcsdir </> d) </>) . filter (\x ->+ head x /= '.' && f x) <$> getDirectoryContents (darcsdir </> d) \end{code}
src/Darcs/Commands/Pull.lhs view
@@ -18,19 +18,19 @@ \darcsCommand{pull} \begin{code} {-# OPTIONS_GHC -cpp #-}-{-# LANGUAGE CPP #-} -module Darcs.Commands.Pull ( pull ) where+module Darcs.Commands.Pull ( pull, fetch ) where import System.Exit ( ExitCode(..), exitWith ) import Workaround ( getCurrentDirectory ) import Control.Monad ( when ) import Data.List ( nub )+import Data.Maybe ( fromMaybe ) import Darcs.Commands ( DarcsCommand(..), putVerbose, putInfo )-import Darcs.CommandsAux ( check_paths )+import Darcs.CommandsAux ( checkPaths ) import Darcs.Arguments ( DarcsFlag( Verbose, DryRun, MarkConflicts, Intersection, Complement, AllowConflicts,- NoAllowConflicts ),+ NoAllowConflicts, XMLOutput ), nocompress, ignoretimes, definePatches, depsSel, pullConflictOptions, useExternalMerge, matchSeveral, fixUrl,@@ -39,25 +39,41 @@ test, dryRun, setDefault, summary, workingRepoDir, remoteRepo, setScriptsExecutableOption, nolinks,- networkOptions, umaskOption, allowUnrelatedRepos, restrictPaths+ networkOptions, umaskOption, allowUnrelatedRepos, restrictPaths, changesReverse,+ getOutput, output )-import Darcs.Repository ( Repository, SealedPatchSet, identifyRepositoryFor, withGutsOf,+import Darcs.Flags( doReverse )+import Darcs.Repository ( Repository, identifyRepositoryFor, withGutsOf, amInRepository, withRepoLock, ($-), tentativelyMergePatches, finalizeRepositoryChanges, applyToWorking,- read_repo, checkUnrelatedRepos, invalidateIndex )-import Darcs.Hopefully ( info )+ readRepo, checkUnrelatedRepos, invalidateIndex, modifyCache, modifyCache, HashedDir(..), Cache(..), CacheLoc(..), WritableOrNot(..))+import qualified Darcs.Repository.Cache as DarcsCache+import Darcs.Repository.Merge+import Darcs.Hopefully ( info, hopefully, patchDesc ) import Darcs.Patch ( RepoPatch, description )-import Darcs.Witnesses.Ordered ( (:>)(..), (:\/:)(..), RL(..),- mapFL, nullFL, reverseRL, mapRL )+import Darcs.Patch.Info ( PatchInfo )+import Darcs.Patch.Bundle( makeBundleN, patchFilename )+import Darcs.Hopefully ( PatchInfoAnd )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif+import Darcs.Patch.Set ( PatchSet(..), SealedPatchSet, newset2FL )+import Darcs.Witnesses.Ordered ( (:>)(..), (:\/:)(..), FL(..), RL(..), (:>>)(..)+ , mapFL, nullFL, reverseFL, mapFL_FL, unsafeCoercePEnd ) import Darcs.Patch.Permutations ( partitionFL ) import Darcs.Repository.Prefs ( addToPreflist, defaultrepo, setDefaultrepo, getPreflist )-import Darcs.Repository.Motd (show_motd )-import Darcs.Patch.Depends ( get_common_and_uncommon,- patchset_intersection, patchset_union )-import Darcs.SelectChanges ( with_selected_changes, filterOutConflicts )+import Darcs.Repository.Motd (showMotd )+import Darcs.Patch.Depends ( findUncommon, findCommonWithThem,+ newsetIntersection, newsetUnion )+import Darcs.SelectChanges ( selectChanges,+ WhichChanges(..),+ filterOutConflicts,+ runSelection, selectionContext) import Darcs.Utils ( clarifyErrors, formatPath ) import Darcs.Witnesses.Sealed ( Sealed(..), seal )-import Printer ( putDocLn, vcat, ($$), text )+import Printer ( putDocLn, vcat, ($$), text, putDoc )+import Darcs.Lock ( writeDocBinFile )+import Darcs.RepoPath ( useAbsoluteOrStd, stdOut ) #include "impossible.h" #include "gadts.h"@@ -66,6 +82,10 @@ pullDescription = "Copy and apply patches from another repository to this one." +fetchDescription :: String+fetchDescription =+ "Fetch patches from another repository, but don't apply them."+ pullHelp :: String pullHelp = "Pull is used to bring changes made in another repository into the current\n"++@@ -76,6 +96,43 @@ "without an argument, pull will use the repository from which you have most\n"++ "recently either pushed or pulled.\n" +fetchHelp :: String+fetchHelp =+ "fetch is used to bring changes made in another repository\n" +++ "into the current repository without actually applying\n"+++ "them. Fetch allows you to bring over all or\n"+++ "some of the patches that are in that repository but not in this one. Fetch\n"+++ "accepts arguments, which are URLs from which to fetch, and when called\n"+++ "without an argument, fetch will use the repository from which you have most\n"+++ "recently either pushed or pulled.\n"+++ "The fetched patches are stored into a patch bundle, to be later\n" +++ "applied using \"darcs apply\"."+++fetch :: DarcsCommand+fetch = DarcsCommand {+ commandName = "fetch",+ commandHelp = fetchHelp,+ commandDescription = fetchDescription,+ commandExtraArgs = -1,+ commandExtraArgHelp = ["[REPOSITORY]..."],+ commandCommand = fetchCmd,+ commandPrereq = amInRepository,+ commandGetArgPossibilities = getPreflist "repos",+ commandArgdefaults = defaultrepo,+ commandAdvancedOptions = [repoCombinator,+ remoteRepo] +++ networkOptions,+ commandBasicOptions = [matchSeveral,+ allInteractive]+ ++dryRun+++ [summary,+ depsSel,+ setDefault False,+ workingRepoDir,+ output,+ allowUnrelatedRepos]}+ pull :: DarcsCommand pull = DarcsCommand {commandName = "pull", commandHelp = pullHelp,@@ -92,7 +149,9 @@ remoteRepo, setScriptsExecutableOption, umaskOption,- restrictPaths] +++ restrictPaths,+ changesReverse+ ] ++ networkOptions, commandBasicOptions = [matchSeveral, allInteractive,@@ -100,66 +159,116 @@ useExternalMerge, test]++dryRun++[summary, depsSel,- setDefault,+ setDefault False, workingRepoDir, allowUnrelatedRepos]} +mergeOpts :: [DarcsFlag] -> [DarcsFlag]+mergeOpts opts | NoAllowConflicts `elem` opts = opts+ | AllowConflicts `elem` opts = opts+ | otherwise = MarkConflicts : opts+ pullCmd :: [DarcsFlag] -> [String] -> IO ()-pullCmd opts unfixedrepodirs@(_:_) = withRepoLock opts $- \repository -> do+pullCmd opts repos =+ do+ pullingFrom <- mapM (fixUrl opts) repos+ withRepoLock opts $- \ initRepo -> do+ let repository = modifyCache initRepo $ addReposToCache pullingFrom+ r <- fetchPatches opts' repos "pull" repository+ applyPatches opts' repository r+ where+ opts' = mergeOpts opts+ addReposToCache repos (Ca cache) = Ca $ [ toReadOnlyCache r | r <- repos ] ++ cache+ toReadOnlyCache = Cache DarcsCache.Repo NotWritable+++fetchCmd :: [DarcsFlag] -> [String] -> IO ()+fetchCmd opts repos =+ withRepoLock opts $- \ repository ->+ fetchPatches opts repos "fetch" repository+ >>= makeBundle opts++fetchPatches :: FORALL(p r u t) (RepoPatch p) => [DarcsFlag] -> [String] -> String ->+ Repository p C(r u r) ->+ IO ( SealedPatchSet p C(Origin), Sealed ((FL (PatchInfoAnd p) :\/: FL (PatchInfoAnd p)) C(r)))+fetchPatches opts unfixedrepodirs@(_:_) jobname repository = do here <- getCurrentDirectory repodirs <- (nub . filter (/= here)) `fmap` mapM (fixUrl opts) unfixedrepodirs -- Test to make sure we aren't trying to pull from the current repo when (null repodirs) $ fail "Can't pull from current repository!"- (Sealed them, Sealed compl) <- readRepos repository opts repodirs old_default <- getPreflist "defaultrepo"- setDefaultrepo (head repodirs) opts- mapM_ (addToPreflist "repos") repodirs- when (old_default == repodirs) $+ when (old_default == repodirs && not (XMLOutput `elem` opts)) $ let pulling = if DryRun `elem` opts then "Would pull" else "Pulling" in putInfo opts $ text $ pulling++" from "++concatMap formatPath repodirs++"..."- mapM_ (show_motd opts) repodirs- us <- read_repo repository- (common, us' :\/: them'') <- return $ get_common_and_uncommon (us, them)- (_ , _ :\/: compl') <- return $ get_common_and_uncommon (us, compl)- checkUnrelatedRepos opts common us them- let avoided = mapRL info compl'- ps :> _ <- return $ partitionFL (not . (`elem` avoided) . info) $ reverseRL them''- do when (Verbose `elem` opts) $- do case us' of- (x@(_:<:_)) -> putDocLn $ text "We have the following new (to them) patches:"- $$ (vcat $ mapRL description x)- _ -> return ()- when (not $ nullFL ps) $ putDocLn $ text "They have the following patches to pull:"- $$ (vcat $ mapFL description ps)- let merge_opts | NoAllowConflicts `elem` opts = opts- | AllowConflicts `elem` opts = opts- | otherwise = MarkConflicts : opts- (hadConflicts, Sealed psFiltered) <- filterOutConflicts merge_opts us' repository ps- when hadConflicts $ putStrLn "Skipping some patches which would cause conflicts."- when (nullFL psFiltered)- $ do putInfo opts $ text "No remote changes to pull in!"- definePatches psFiltered- exitWith ExitSuccess- with_selected_changes "pull" opts Nothing psFiltered $- \ (to_be_pulled:>_) ->+ (Sealed them, Sealed compl) <- readRepos repository opts repodirs+ setDefaultrepo (head repodirs) opts+ mapM_ (addToPreflist "repos") repodirs+ mapM_ (showMotd opts) repodirs+ us <- readRepo repository+ checkUnrelatedRepos opts us them++ common :>> _ <- return $ findCommonWithThem us them+ us' :\/: them' <- return $ findUncommon us them+ _ :\/: compl' <- return $ findUncommon us compl++ let avoided = mapFL info compl'+ ps :> _ <- return $ partitionFL (not . (`elem` avoided) . info) them'+ when (Verbose `elem` opts) $+ do case us' of+ (x@(_:>:_)) -> putDocLn $ text "We have the following new (to them) patches:"+ $$ (vcat $ mapFL description x)+ _ -> return ()+ when (not $ nullFL ps) $ putDocLn $ text "They have the following patches to pull:"+ $$ (vcat $ mapFL description ps)+ (hadConflicts, Sealed psFiltered) <- filterOutConflicts opts (reverseFL us') repository ps+ when hadConflicts $ putStrLn "Skipping some patches which would cause conflicts."+ when (nullFL psFiltered) $ do putInfo opts $ text "No remote changes to pull in!"+ definePatches psFiltered+ exitWith ExitSuccess+ let context = selectionContext jobname opts Nothing []+ selector = if doReverse opts+ then selectChanges FirstReversed+ else selectChanges First+ (to_be_pulled :> _) <- runSelection (selector psFiltered) $ context+ return (seal common, seal $ us' :\/: to_be_pulled)++fetchPatches _ [] jobname _ = fail $ "No default repository to " ++ jobname +++ " from, please specify one"++applyPatches ::+ forall p C(r u t a). (RepoPatch p) => [DarcsFlag] -> Repository p C(r u r) ->+ (SealedPatchSet p C(Origin), Sealed ((FL (PatchInfoAnd p) :\/: FL (PatchInfoAnd p)) C(r)))+ -> IO ()+applyPatches opts repository (_, Sealed (us' :\/: to_be_pulled)) = do printDryRunMessageAndExit "pull" opts to_be_pulled definePatches to_be_pulled when (nullFL to_be_pulled) $ do putStrLn "You don't want to pull any patches, and that's fine with me!" exitWith ExitSuccess- check_paths opts to_be_pulled+ checkPaths opts to_be_pulled putVerbose opts $ text "Getting and merging the following patches:" putVerbose opts $ vcat $ mapFL description to_be_pulled- Sealed pw <- tentativelyMergePatches repository "pull" merge_opts- (reverseRL us') to_be_pulled+ Sealed pw <- tentativelyMergePatches repository "pull" opts us' to_be_pulled invalidateIndex repository withGutsOf repository $ do finalizeRepositoryChanges repository revertable $ applyToWorking repository opts pw+ return () putInfo opts $ text "Finished pulling and applying." -pullCmd _ [] = fail "No default repository to pull from, please specify one"+makeBundle ::+ forall p C(r u t x) . (RepoPatch p) => [DarcsFlag] ->+ (SealedPatchSet p C(Origin), Sealed ((FL (PatchInfoAnd p) :\/: FL (PatchInfoAnd p)) C(r)))+ -> IO ()+makeBundle opts (Sealed common, Sealed (_ :\/: to_be_fetched)) =+ do+ bundle <- makeBundleN Nothing (unsafeCoercePEnd common) $+ mapFL_FL hopefully to_be_fetched+ let fname = case to_be_fetched of+ (x:>:_)-> patchFilename $ patchDesc x+ o = fromMaybe stdOut (getOutput opts fname)+ useAbsoluteOrStd writeDocBinFile putDoc o $ bundle revertable :: IO a -> IO a revertable x =@@ -188,17 +297,18 @@ the second patchset(s) to be complemented against Rc. -} -readRepos :: RepoPatch p => Repository p C(r u t) -> [DarcsFlag] -> [String] -> IO (SealedPatchSet p,SealedPatchSet p)+readRepos :: RepoPatch p => Repository p C(r u t) -> [DarcsFlag] -> [String]+ -> IO (SealedPatchSet p C(Origin),SealedPatchSet p C(Origin)) readRepos _ _ [] = impossible readRepos to_repo opts us = do rs <- mapM (\u -> do r <- identifyRepositoryFor to_repo u- ps <- read_repo r+ ps <- readRepo r return $ seal ps) us return $ if Intersection `elem` opts- then (patchset_intersection rs, seal NilRL)+ then (newsetIntersection rs, seal (PatchSet NilRL NilRL)) else if Complement `elem` opts- then (head rs, patchset_union $ tail rs)- else (patchset_union rs, seal NilRL)+ then (head rs, newsetUnion $ tail rs)+ else (newsetUnion rs, seal (PatchSet NilRL NilRL)) \end{code} @@ -208,7 +318,7 @@ If you provide more than one repository as an argument to pull, darcs' behavior is determined by the presence of the \verb!--complement!,-\verb!--intersection!, and \verb!--union! flags. +\verb!--intersection!, and \verb!--union! flags. \begin{itemize}
src/Darcs/Commands/Push.lhs view
@@ -33,27 +33,33 @@ applyas, matchSeveral, fixUrl, depsSel, allInteractive, dryRun, nolinks, remoteRepo, networkOptions,- setDefault, sign, allowUnrelatedRepos+ setDefault, sign, allowUnrelatedRepos,+ changesReverse )++import Darcs.Flags(doReverse) import Darcs.Hopefully ( PatchInfoAnd, hopefully ) import Darcs.Repository ( Repository, withRepoReadLock, ($-), identifyRepositoryFor,- read_repo, amInRepository, checkUnrelatedRepos )+ readRepo, amInRepository, checkUnrelatedRepos ) import Darcs.Patch ( RepoPatch, description )-import Darcs.Witnesses.Ordered ( (:>)(..), (:\/:)(..), RL, FL, nullRL,- nullFL, reverseRL, mapFL_FL, mapRL, lengthRL )+import Darcs.Witnesses.Ordered ( (:>)(..), RL, FL, nullRL,+ nullFL, reverseFL, mapFL_FL, mapRL, (:>>)(..) ) import Darcs.Repository.Prefs ( defaultrepo, setDefaultrepo, getPreflist ) import Darcs.External ( maybeURLCmd, signString )-import Darcs.URL ( is_url, is_file )-import Darcs.SelectChanges ( with_selected_changes )+import Darcs.URL ( isUrl, isFile )+import Darcs.SelectChanges ( selectChanges, WhichChanges(..),+ selectionContext, runSelection ) import Darcs.Utils ( formatPath )-import Darcs.Patch.Depends ( get_common_and_uncommon )-import Darcs.Patch.Bundle ( make_bundle )+import Darcs.Patch.Depends ( findCommonWithThem, countUsThem )+import Darcs.Patch.Bundle ( makeBundleN ) import Darcs.Patch.Patchy( ShowPatch )-import Darcs.Patch.Info ( PatchInfo ) import Darcs.Patch.Set ( PatchSet )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif import Printer ( Doc, vcat, empty, text, ($$) )-import Darcs.RemoteApply ( remote_apply, apply_as )-import Darcs.Email ( make_email )+import Darcs.RemoteApply ( remoteApply, applyAs )+import Darcs.Email ( makeEmail ) import English (englishNum, Noun(..)) #include "impossible.h" @@ -80,13 +86,14 @@ commandArgdefaults = defaultrepo, commandAdvancedOptions = [applyas, nolinks,- remoteRepo] +++ remoteRepo,+ changesReverse] ++ networkOptions, commandBasicOptions = [matchSeveral, depsSel, allInteractive, sign]++dryRun++[summary, workingRepoDir,- setDefault,+ setDefault False, allowUnrelatedRepos]} pushCmd :: [DarcsFlag] -> [String] -> IO ()@@ -103,10 +110,10 @@ (bundle) <- withRepoReadLock opts $- prepareBundle opts repodir sbundle <- signString opts bundle- let body = if is_file repodir+ let body = if isFile repodir then sbundle- else make_email repodir [] Nothing sbundle Nothing- rval <- remote_apply opts repodir body+ else makeEmail repodir [] Nothing sbundle Nothing+ rval <- remoteApply opts repodir body case rval of ExitFailure ec -> do putStrLn $ "Apply failed!" exitWith (ExitFailure ec) ExitSuccess -> putInfo opts $ text "Push successful."@@ -115,26 +122,30 @@ prepareBundle :: forall p C(r u t) . (RepoPatch p) => [DarcsFlag] -> String -> Repository p C(r u t) -> IO (Doc) prepareBundle opts repodir repository = do- them <- identifyRepositoryFor repository repodir >>= read_repo old_default <- getPreflist "defaultrepo"- setDefaultrepo repodir opts when (old_default == [repodir]) $ let pushing = if DryRun `elem` opts then "Would push" else "Pushing" in putInfo opts $ text $ pushing++" to "++formatPath repodir++"..."- us <- read_repo repository- case get_common_and_uncommon (us, them) of- (common, us' :\/: them') -> do- prePushChatter opts common us us' them them'- with_selected_changes "push" opts Nothing (reverseRL us') $ bundlePatches opts common+ them <- identifyRepositoryFor repository repodir >>= readRepo+ setDefaultrepo repodir opts+ us <- readRepo repository+ common :>> us' <- return $ findCommonWithThem us them+ prePushChatter opts us (reverseFL us') them+ let context = selectionContext "push" opts Nothing []+ selector = if doReverse opts+ then selectChanges FirstReversed+ else selectChanges First+ runSelection (selector us') context+ >>= bundlePatches opts common -prePushChatter :: forall p a C(x y t) . (ShowPatch a) =>- [DarcsFlag] -> [PatchInfo] -> PatchSet p C(x) ->- RL a C(t x) -> PatchSet p C(y) -> RL a C(t y) -> IO ()-prePushChatter opts common us us' them them' = do- checkUnrelatedRepos opts common us them- let num_to_pull = lengthRL them'- let pull_reminder = if num_to_pull > 0- then text $ "The remote repository has " ++ show num_to_pull +prePushChatter :: forall p a C(x y t) . (RepoPatch p, ShowPatch a) =>+ [DarcsFlag] -> PatchSet p C(Origin x) ->+ RL a C(t x) -> PatchSet p C(Origin y) -> IO ()+prePushChatter opts us us' them = do+ checkUnrelatedRepos opts us them+ let num_to_pull = snd $ countUsThem us them+ pull_reminder = if num_to_pull > 0+ then text $ "The remote repository has " ++ show num_to_pull ++ " " ++ englishNum num_to_pull (Noun "patch") " to pull." else empty putVerbose opts $ text "We have the following patches to push:" $$ (vcat $ mapRL description us')@@ -142,7 +153,7 @@ when (nullRL us') $ do putInfo opts $ text "No recorded local changes to push!" exitWith ExitSuccess -bundlePatches :: forall t p C(x y z w). RepoPatch p => [DarcsFlag] -> [PatchInfo]+bundlePatches :: forall t p C(x y z w a). RepoPatch p => [DarcsFlag] -> PatchSet p C(a z) -> (FL (PatchInfoAnd p) :> t) C(z w) -> IO (Doc) bundlePatches opts common (to_be_pushed :> _) =@@ -153,8 +164,7 @@ putInfo opts $ text "You don't want to push any patches, and that's fine with me!" exitWith ExitSuccess- bundle <- make_bundle []- (bug "using slurpy in make_bundle called from Push")+ bundle <- makeBundleN Nothing common (mapFL_FL hopefully to_be_pushed) return (bundle) @@ -170,8 +180,8 @@ checkOptionsSanity :: [DarcsFlag] -> String -> IO () checkOptionsSanity opts repodir =- if is_url repodir then do- when (apply_as opts /= Nothing) $+ if isUrl repodir then do+ when (applyAs opts /= Nothing) $ abortRun opts $ text "Cannot --apply-as when pushing to URLs" maybeapply <- maybeURLCmd "APPLY" repodir when (maybeapply == Nothing) $
src/Darcs/Commands/Put.lhs view
@@ -15,26 +15,31 @@ networkOptions, flagToString, getInventoryChoices, setScriptsExecutableOption, workingRepoDir, setDefault )-import Darcs.Repository ( withRepoReadLock, ($-), patchSetToPatches, read_repo, amInRepository )+import Darcs.Repository ( withRepoReadLock, ($-), patchSetToPatches, readRepo, amInRepository ) import Darcs.Repository.Format ( identifyRepoFormat, RepoProperty ( Darcs2, HashedInventory ), formatHas )-import Darcs.Patch.Bundle ( make_bundle2 )-import Darcs.Witnesses.Ordered ( FL(..) )+import Darcs.Patch.Bundle ( makeBundle2 )+import Darcs.Patch.Set ( PatchSet )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif+import Darcs.Witnesses.Ordered ( FL(..), RL(..), nullFL, EqCheck(..), unsafeCoerceP ) import Darcs.Match ( havePatchsetMatch, getOnePatchset ) import Darcs.Repository.Prefs ( getPreflist, setDefaultrepo )-import Darcs.URL ( is_url, is_file )+import Darcs.URL ( isUrl, isFile ) import Darcs.Utils ( withCurrentDirectory ) import Progress ( debugMessage ) import Darcs.RepoPath ( ioAbsoluteOrRemote, toPath ) import Darcs.External ( execSSH )-import Darcs.RemoteApply ( remote_apply )+import Darcs.RemoteApply ( remoteApply ) import Darcs.Commands.Init ( initialize )-import Darcs.Email ( make_email )+import Darcs.Email ( makeEmail ) import Darcs.Witnesses.Sealed ( Sealed(..), seal ) import Printer ( text ) #include "impossible.h"+#include "gadts.h" -putDescription :: String +putDescription :: String putDescription = "Makes a copy of the repository" @@ -62,7 +67,7 @@ commandAdvancedOptions = [applyas] ++ networkOptions, commandBasicOptions = [matchOneContext, setScriptsExecutableOption, getInventoryChoices,- setDefault, workingRepoDir]}+ setDefault True, workingRepoDir]} putCmd :: [DarcsFlag] -> [String] -> IO () putCmd _ [""] = fail "Empty repository argument given to put."@@ -76,7 +81,7 @@ req_absolute_repo_dir = toPath t_req_absolute_repo_dir when (cur_absolute_repo_dir == req_absolute_repo_dir) $ fail "Can't put to current repository!"- when (is_url req_absolute_repo_dir) $ error "Can't put to a URL!"+ when (isUrl req_absolute_repo_dir) $ error "Can't put to a URL!" debugMessage "Creating repository" putVerbose opts $ text "Creating repository"@@ -89,37 +94,35 @@ not (UseOldFashionedInventory `elem` opts) then UseHashedInventory:filter (/= UseFormat2) opts else UseOldFashionedInventory:filter (/= UseFormat2) opts- if is_file req_absolute_repo_dir+ if isFile req_absolute_repo_dir then do createDirectory req_absolute_repo_dir withCurrentDirectory req_absolute_repo_dir $ (commandCommand initialize) initopts []- else do -- is_ssh req_absolute_repo_dir+ else do -- isSsh req_absolute_repo_dir remoteInit req_absolute_repo_dir initopts withCurrentDirectory cur_absolute_repo_dir $- withRepoReadLock opts $- \repository -> do+ withRepoReadLock opts $- \repository -> (do setDefaultrepo req_absolute_repo_dir opts- Sealed patchset <- if havePatchsetMatch opts- then getOnePatchset repository opts -- todo: make sure getOnePatchset has the right type- else read_repo repository >>= (return . seal)- Sealed patchset2 <- if havePatchsetMatch opts- then getOnePatchset repository opts -- todo: make sure getOnePatchset has the right type- else read_repo repository >>= (return . seal)+ let doRead = if havePatchsetMatch opts+ then getOnePatchset repository opts -- todo: make sure getOnePatchset has the right type+ else readRepo repository >>= (return . seal)+ Sealed (patchset :: PatchSet p C(Origin x1)) <- doRead+ Sealed (patchset2 :: PatchSet p C(Origin x2)) <- doRead+ IsEq <- return (unsafeCoerceP IsEq) :: IO (EqCheck C(x1 x2)) let patches = patchSetToPatches patchset patches2 = patchSetToPatches patchset2- nullFL NilFL = True- nullFL _ = False when (nullFL patches) $ do putInfo opts $ text "No patches were selected to put. Nothing to be done." exitWith ExitSuccess- bundle <- make_bundle2 opts emptyTree [] patches patches2- let message = if is_file req_absolute_repo_dir+ bundle <- makeBundle2 Nothing NilRL patches patches2+ let message = if isFile req_absolute_repo_dir then bundle- else make_email req_absolute_repo_dir [] Nothing bundle Nothing+ else makeEmail req_absolute_repo_dir [] Nothing bundle Nothing putVerbose opts $ text "Applying patches in new repository..."- rval <- remote_apply opts req_absolute_repo_dir message+ rval <- remoteApply opts req_absolute_repo_dir message case rval of ExitFailure ec -> do putStrLn $ "Apply failed!" exitWith (ExitFailure ec)- ExitSuccess -> putInfo opts $ text "Put successful."+ ExitSuccess -> putInfo opts $ text "Put successful.") :: IO () putCmd _ _ = impossible remoteInit :: FilePath -> [DarcsFlag] -> IO ()@@ -127,7 +130,7 @@ let args = catMaybes $ map (flagToString $ commandBasicOptions initialize) opts command = "darcs initialize --repodir='" ++ path ++ "' " ++ unwords args exitCode <- execSSH addr command- when (exitCode /= ExitSuccess) $ + when (exitCode /= ExitSuccess) $ fail "Couldn't initialize remote repository." where (addr,':':path) = break (==':') repo \end{code}
src/Darcs/Commands/Record.lhs view
@@ -19,58 +19,65 @@ \begin{code} {-# LANGUAGE CPP, PatternGuards #-} -module Darcs.Commands.Record ( record, commit, getDate, getLog, fileExists ) where+module Darcs.Commands.Record ( record, commit, getDate, getLog,+ askAboutDepends+ ) where import qualified Ratified( hGetContents )-import Control.Exception ( handleJust, Exception( ExitException ) )-import Control.Monad ( filterM, when )+import Control.Exception.Extensible ( handleJust )+import Control.Monad ( when ) import System.IO ( stdin )-import Data.List ( sort, isPrefixOf )+import Data.List ( sort, isPrefixOf, union )+import Data.Char ( ord ) import System.Exit ( exitWith, exitFailure, ExitCode(..) )-import System.IO ( hPutStrLn )-import System.Directory ( doesFileExist, doesDirectoryExist, removeFile )-import Data.Maybe ( isJust )+import System.Directory ( removeFile )+import Data.Maybe ( isJust, catMaybes )+import qualified Data.ByteString as B ( hPut ) -import Darcs.Lock ( readBinFile, writeBinFile, world_readable_temp, appendToFile )-import Darcs.Hopefully ( info, n2pia )+import Darcs.Lock ( readLocaleFile, writeLocaleFile, worldReadableTemp, appendToFile )+import Darcs.Hopefully ( info, n2pia, PatchInfoAnd ) import Darcs.Repository ( Repository, amInRepository, withRepoLock, ($-), withGutsOf,- read_repo,- slurp_recorded,+ readTentativeRepo, tentativelyAddPatch, finalizeRepositoryChanges , invalidateIndex, unrecordedChanges ) import Darcs.Patch ( RepoPatch, Patch, Prim, namepatch, summary, anonymous, adddeps, fromPrims )+import Darcs.Patch.Set ( PatchSet(..) ) import Darcs.Witnesses.Ordered ( FL(..), RL(..), (:>)(..), (+>+),- unsafeUnFL, unsafeCompare,+ unsafeCompare, reverseRL, mapFL, mapFL_FL, nullFL )+import Darcs.Witnesses.Sealed import Darcs.Patch.Info ( PatchInfo ) import Darcs.Patch.Split ( primSplitter )-import Darcs.SlurpDirectory ( slurp_hasfile, slurp_hasdir ) import Darcs.Patch.Choices ( patchChoicesTps, tpPatch,- forceFirst, getChoices, tag )-import Darcs.SelectChanges ( with_selected_changes_to_files',- with_selected_changes_reversed )-import Darcs.RepoPath ( FilePathLike, SubPath, sp2fn, toFilePath )-import Darcs.SlurpDirectory ( Slurpy, empty_slurpy )+ forceFirsts, getChoices, tag )+import Darcs.SelectChanges ( selectChanges, WhichChanges(..),+ selectionContext, selectionContextPrim,+ runSelection+ )+import Darcs.RepoPath ( FilePathLike, SubPath, toFilePath ) import Darcs.Commands ( DarcsCommand(..), nodefaults, commandStub )+import Darcs.Commands.WhatsNew ( announceFiles ) import Darcs.Arguments ( DarcsFlag( PromptLongComment, NoEditLongComment, EditLongComment, LogFile, Pipe, PatchName, AskDeps, All ), fileHelpAuthor, getAuthor, workingRepoDir, lookforadds,- fixSubPaths, defineChanges, testByDefault,+ fixSubPaths, defineChanges, askLongComment, askdeps, patchSelectFlag,- allPipeInteractive, leaveTestDir, notest,+ allPipeInteractive, leaveTestDir, test, author, patchnameOption, umaskOption, ignoretimes, nocompress, rmlogfile, logfile, listRegisteredFiles, setScriptsExecutableOption ) import Darcs.Flags (willRemoveLogFile)-import Darcs.Utils ( askUser, promptYorn, edit_file, clarifyErrors )+import Darcs.Utils ( askUser, promptYorn, editFile, clarifyErrors ) import Progress ( debugMessage) import Darcs.ProgressPatches( progressFL) import IsoDate ( getIsoDateTime, cleanLocalDate )-import Printer ( hPutDocLn, text, wrap_text, ($$) )+import Printer ( hPutDocLn, text, wrapText, ($$) )+import ByteStringUtils ( encodeLocale ) #include "impossible.h"+#include "gadts.h" recordDescription :: String recordDescription = "Create a patch from unrecorded changes."@@ -98,7 +105,7 @@ umaskOption, setScriptsExecutableOption], commandBasicOptions = [patchnameOption, author,- notest,+ test, leaveTestDir, allPipeInteractive, askdeps,@@ -119,41 +126,24 @@ commit :: DarcsCommand commit = commandStub "commit" commitHelp commitDescription record -fileExists :: Slurpy -> SubPath -> IO Bool-fileExists s rp = do file <- doesFileExist fp- dir <- doesDirectoryExist fp- return (file || dir ||- slurp_hasfile (sp2fn rp) s ||- slurp_hasdir (sp2fn rp) s)- where fp = toFilePath rp- recordCmd :: [DarcsFlag] -> [String] -> IO () recordCmd opts args = do checkNameIsNotOption opts- withRepoLock (testByDefault opts) $- \repository -> do- rec <- if null args then return empty_slurpy- else slurp_recorded repository+ withRepoLock opts $- \repository -> do files <- sort `fmap` fixSubPaths opts args- let non_repo_files = if null files && (not $ null args) then args else []- existing_files <- filterM (fileExists rec) files- non_existent_files <- filterM (fmap not . fileExists rec) files- when (not $ null existing_files) $- putStrLn $ "Recording changes in "++unwords (map show existing_files)++":\n"- when (not $ null non_existent_files) $- putStrLn $ "Non existent files or directories: "++unwords (map show non_existent_files)++"\n"- when (((not $ null non_existent_files) || (not $ null non_repo_files)) && null existing_files) $- fail "None of the files you specified exist!"+ existing_files <- announceFiles repository files "Recording changes in"+ when (null existing_files && (not $ null files)) $+ fail "None of the files you specified exist!" debugMessage "About to get the unrecorded changes." changes <- unrecordedChanges opts repository files debugMessage "I've gotten unrecorded." case allow_empty_with_askdeps changes of Nothing -> do when (Pipe `elem` opts) $ do getDate opts return ()- if ((not $ null existing_files) || (not $ null non_existent_files))- then putStrLn "No changes in selected files or directories!"- else putStrLn "No changes!"+ putStrLn "No changes!" Just ch -> doRecord repository opts existing_files ch- where allow_empty_with_askdeps NilFL+ where allow_empty_with_askdeps :: FL p C(x y) -> Maybe (FL p C(x y))+ allow_empty_with_askdeps NilFL | AskDeps `elem` opts = Just NilFL | otherwise = Nothing allow_empty_with_askdeps p = Just p@@ -170,7 +160,7 @@ then do let keepAsking = do yorn <- promptYorn ("You specified " ++ show n ++ " as the patch name. Is that really what you want?")- case yorn of + case yorn of 'y' -> return () 'n' -> do putStrLn "Okay, aborting the record."@@ -180,27 +170,29 @@ else return () -doRecord :: RepoPatch p => Repository p -> [DarcsFlag] -> [SubPath] -> FL Prim -> IO ()+doRecord :: RepoPatch p => Repository p C(r u r) -> [DarcsFlag] -> [SubPath] -> FL Prim C(r x) -> IO () doRecord repository opts files ps = do- let make_log = world_readable_temp "darcs-record"+ let make_log = worldReadableTemp "darcs-record" date <- getDate opts my_author <- getAuthor opts debugMessage "I'm slurping the repository." debugMessage "About to select changes..."- with_selected_changes_to_files' "record" opts (Just primSplitter)- (map toFilePath files) ps $ \ (chs:>_) ->- do when (is_empty_but_not_askdeps chs) $+ (chs :> _ ) <- runSelection (selectChanges First ps) $+ selectionContextPrim "record" opts (Just primSplitter)+ (map toFilePath files)+ when (is_empty_but_not_askdeps chs) $ do putStrLn "Ok, if you don't want to record anything, that's fine!" exitWith ExitSuccess- handleJust onlySuccessfulExits (\_ -> return ()) $+ handleJust onlySuccessfulExits (\_ -> return ()) $ do deps <- if AskDeps `elem` opts- then askAboutDepends repository chs opts+ then askAboutDepends repository chs opts [] else return [] when (AskDeps `elem` opts) $ debugMessage "I've asked about dependencies." if nullFL chs && null deps then putStrLn "Ok, if you don't want to record anything, that's fine!" else do defineChanges chs (name, my_log, logf) <- getLog opts Nothing make_log chs+ debugMessage ("Patch name as received from getLog: " ++ show (map ord name)) doActualRecord repository opts name date my_author my_log logf deps chs where is_empty_but_not_askdeps l@@ -208,9 +200,9 @@ -- a "partial tag" patch; see below. | otherwise = nullFL l -doActualRecord :: RepoPatch p => Repository p -> [DarcsFlag] -> String -> String -> String+doActualRecord :: RepoPatch p => Repository p C(r u r) -> [DarcsFlag] -> String -> String -> String -> [String] -> Maybe String- -> [PatchInfo] -> FL Prim -> IO ()+ -> [PatchInfo] -> FL Prim C(r x) -> IO () doActualRecord repository opts name date my_author my_log logf deps chs = do debugMessage "Writing the patch file..." mypatch <- namepatch date name my_author my_log $@@ -257,12 +249,12 @@ data PName = FlagPatchName String | PriorPatchName String | NoPatchName -getLog :: [DarcsFlag] -> Maybe (String, [String]) -> IO String -> FL Prim ->+getLog :: FORALL(x y) [DarcsFlag] -> Maybe (String, [String]) -> IO String -> FL Prim C(x y) -> IO (String, [String], Maybe String) getLog opts m_old make_log chs = gl opts where patchname_specified = patchname_helper opts- patchname_helper (PatchName n:_) | take 4 n == "TAG " = FlagPatchName $ '.':n- | otherwise = FlagPatchName n+ patchname_helper (PatchName n:_) | "TAG " `isPrefixOf` n = FlagPatchName $ '.':n+ | otherwise = FlagPatchName n patchname_helper (_:fs) = patchname_helper fs patchname_helper [] = case m_old of Just (p,_) -> PriorPatchName p Nothing -> NoPatchName@@ -278,7 +270,7 @@ return (p, thelog, Nothing) gl (LogFile f:fs) = do -- round 1 (patchname)- mlp <- lines `fmap` readBinFile f `catch` (\_ -> return [])+ mlp <- lines `fmap` readLocaleFile f `catch` (\_ -> return []) firstname <- case (patchname_specified, mlp) of (FlagPatchName p, []) -> return p (_, p:_) -> return p -- logfile trumps prior!@@ -286,7 +278,7 @@ (NoPatchName, []) -> prompt_patchname True -- round 2 append_info f firstname- when (EditLongComment `elem` fs) $ do edit_file f+ when (EditLongComment `elem` fs) $ do editFile f return () (name, thelog, _) <- read_long_comment f firstname let toRemove = if willRemoveLogFile opts@@ -319,7 +311,7 @@ NoPatchName -> prompt_patchname True >>= prompt_long_comment prompt_patchname retry = do n <- askUser "What is the patch name? "- if n == "" || take 4 n == "TAG "+ if n == "" || "TAG " `isPrefixOf` n then if retry then prompt_patchname retry else fail "Bad patch name!" else return n@@ -328,27 +320,29 @@ if yorn == 'y' then actually_get_log oldname else return (oldname, [], Nothing) actually_get_log p = do logf <- make_log- writeBinFile logf $ unlines $ p : default_log+ -- TODO: make sure encoding used for logf is the same everywhere+ -- probably should be locale because the editor will assume it+ writeLocaleFile logf $ unlines $ p : default_log append_info logf p- edit_file logf+ editFile logf read_long_comment logf p read_long_comment :: FilePathLike p => p -> String -> IO (String, [String], Maybe p) read_long_comment f oldname =- do t <- (lines.filter (/='\r')) `fmap` readBinFile f+ do t <- (lines.filter (/='\r')) `fmap` readLocaleFile f case t of [] -> return (oldname, [], Just f) (n:ls) -> return (n, takeWhile (not.(eod `isPrefixOf`)) ls, Just f) append_info f oldname =- do fc <- readBinFile f+ do fc <- readLocaleFile f appendToFile f $ \h -> do case fc of- _ | null (lines fc) -> hPutStrLn h oldname- | last fc /= '\n' -> hPutStrLn h ""+ _ | null (lines fc) -> B.hPut h (encodeLocale (oldname ++ "\n"))+ | last fc /= '\n' -> B.hPut h (encodeLocale "\n") | otherwise -> return () hPutDocLn h $ text eod $$ text ""- $$ wrap_text 75+ $$ wrapText 75 ("Place the long patch description above the "++ eod++ " marker. The first line of this file "++@@ -356,7 +350,7 @@ $$ text "" $$ text "This patch contains the following changes:" $$ text ""- $$ summary (fromPrims chs :: Patch)+ $$ summary (fromPrims chs :: Patch C(x y)) eod :: String eod = "***END OF DESCRIPTION***"@@ -388,27 +382,33 @@ depended-upon patches. \begin{code}-askAboutDepends :: RepoPatch p => Repository p -> FL Prim -> [DarcsFlag] -> IO [PatchInfo]-askAboutDepends repository pa' opts = do- pps <- read_repo repository+askAboutDepends :: forall p C(r u t x y) . RepoPatch p => Repository p C(r u t) -> FL Prim C(t y) -> [DarcsFlag] -> [PatchInfo] -> IO [PatchInfo]+askAboutDepends repository pa' opts olddeps = do+ -- ideally we'd just default the olddeps to yes but still ask about them.+ -- SelectChanges doesn't currently (17/12/09) offer a way to do this so would+ -- have to have this support added first.+ pps <- readTentativeRepo repository pa <- n2pia `fmap` anonymous (fromPrims pa')- let ps = (reverseRL $ headRL pps)+>+(pa:>:NilFL)- (pc, tps) = patchChoicesTps ps- ta = case filter ((pa `unsafeCompare`) . tpPatch) $ unsafeUnFL tps of- [tp] -> tag tp+ FlippedSeal ps <- return+ ((case pps of+ PatchSet x _ -> FlippedSeal ((reverseRL x)+>+(pa:>:NilFL))+ PatchSet NilRL NilRL -> impossible) :: FlippedSeal (FL (PatchInfoAnd p)) C(y))+ let (pc, tps) = patchChoicesTps ps+ tas = case catMaybes (mapFL (\tp -> if pa `unsafeCompare` (tpPatch tp) || info (tpPatch tp) `elem` olddeps+ then Just (tag tp) else Nothing) tps) of+ [] -> error "askAboutDepends: []"- _ -> error "askAboutDepends: many"- ps' = mapFL_FL tpPatch $ middle_choice $ forceFirst ta pc- with_selected_changes_reversed "depend on" (filter askdep_allowed opts) Nothing ps'- $ \(deps:>_) -> return $ mapFL info deps- where headRL (x:<:_) = x- headRL NilRL = impossible+ tgs -> tgs+ Sealed2 ps' <- return $ case getChoices (forceFirsts tas pc) of _ :> mc :> _ -> Sealed2 $ mapFL_FL tpPatch mc+ (deps:>_) <- runSelection (selectChanges FirstReversed ps') $+ selectionContext "depend on" (filter askdep_allowed opts) Nothing []+ return $ olddeps `union` mapFL info deps+ where askdep_allowed = not . patchSelectFlag- middle_choice p = mc where (_ :> mc :> _) = getChoices p -onlySuccessfulExits :: Exception -> Maybe ()-onlySuccessfulExits (ExitException ExitSuccess) = Just ()+onlySuccessfulExits :: ExitCode -> Maybe ()+onlySuccessfulExits ExitSuccess = Just () onlySuccessfulExits _ = Nothing recordHelp'' :: String
src/Darcs/Commands/Remove.lhs view
@@ -22,8 +22,7 @@ module Darcs.Commands.Remove ( remove, rm, unadd ) where -import Control.Monad ( when )-import Data.Maybe ( isJust )+import Control.Monad ( when, foldM ) import Darcs.Commands ( DarcsCommand(..), nodefaults, commandAlias, commandStub, putWarning@@ -31,19 +30,19 @@ import Darcs.Arguments ( DarcsFlag (Recursive), fixSubPaths, listRegisteredFiles, workingRepoDir, umaskOption,- recursive + recursive ) import Darcs.RepoPath ( SubPath, sp2fn ) import Darcs.Repository ( Repository, withRepoLock, ($-), amInRepository,- add_to_pending, readRecordedAndPending, readUnrecorded )+ addToPending, readRecordedAndPending, readUnrecorded ) import Darcs.Diff( treeDiff ) import Darcs.Patch ( RepoPatch, Prim, adddir, rmdir, addfile, rmfile ) import Darcs.Patch.FileName( fn2fp ) import Darcs.Witnesses.Ordered ( FL(..), (+>+) )-import Darcs.Witnesses.Sealed ( Sealed(..) )-import Darcs.Repository.Prefs ( filetypeFunction )-import Storage.Hashed.Tree( TreeItem(..), find, modifyTree, expand, list )-import Storage.Hashed.AnchoredPath( anchorPath )+import Darcs.Witnesses.Sealed ( Sealed(..), Gap(..), FreeLeft, unFreeLeft )+import Darcs.Repository.Prefs ( filetypeFunction, FileType )+import Storage.Hashed.Tree( Tree, TreeItem(..), find, modifyTree, expand, list )+import Storage.Hashed.AnchoredPath( anchorPath, AnchoredPath ) import Storage.Hashed( floatPath ) import Darcs.Commands.Add( expandDirs )@@ -89,51 +88,62 @@ when (null args) $ putStrLn "Nothing specified, nothing removed." Sealed p <- makeRemovePatch opts repository args- add_to_pending repository p+ addToPending repository p +-- | makeRemovePatch builds a list of patches to remove the given filepaths.+-- This function does not recursively process directories. The 'Recursive'+-- flag should be handled by the caller by adding all offspring of a directory+-- to the files list. makeRemovePatch :: RepoPatch p => [DarcsFlag] -> Repository p C(r u t) -> [SubPath] -> IO (Sealed (FL Prim C(u))) makeRemovePatch opts repository files = do recorded <- expand =<< readRecordedAndPending repository- unrecorded <- readUnrecorded repository+ unrecorded <- readUnrecorded repository files ftf <- filetypeFunction- mrp ftf recorded unrecorded $ map (floatPath . fn2fp . sp2fn) files- where mrp ftf recorded unrecorded (f:fs) = do+ result <- foldM removeOnePath (ftf,recorded,unrecorded, []) $ map (floatPath . fn2fp . sp2fn) files+ case result of+ (_, _, _, patches) -> return $+ unFreeLeft $ foldr (joinGap (+>+)) (emptyGap NilFL) $ reverse patches+ where removeOnePath (ftf, recorded, unrecorded, patches) f = do let recorded' = modifyTree recorded f Nothing unrecorded' = modifyTree unrecorded f Nothing- f_fp = anchorPath "" f- skipAndWarn reason =- do putWarning opts . text $ "Can't remove " ++ f_fp- ++ " (" ++ reason ++ ")"- return $ Nothing-- local <- case (find recorded f, find unrecorded f) of- (Just (SubTree _), Just (SubTree unrecordedChildren)) -> do- if not $ null (list unrecordedChildren)- then skipAndWarn "it is not empty"- else return $ Just (rmdir f_fp :>: NilFL)- (Just (File _), Just (File _)) ->- Just `fmap` treeDiff ftf unrecorded unrecorded'- (Just (File _), _) ->- return $ Just (addfile f_fp :>: rmfile f_fp :>: NilFL)- (Just (SubTree _), _) ->- return $ Just (adddir f_fp :>: rmdir f_fp :>: NilFL)- (_, _) -> skipAndWarn "it is not tracked by darcs"- - -- we can tell if the remove succeeded by looking if local is- -- empty. If the remove succeeded, we should pass on updated- -- recorded and unrecorded that reflect the removal- let nextRecorded | isJust local = recorded'- | otherwise = recorded- nextUnrecorded | isJust local = unrecorded'- | otherwise = unrecorded+ local <- makeRemoveGap opts ftf recorded unrecorded unrecorded' f+ -- we can tell if the remove succeeded by looking if local is+ -- empty. If the remove succeeded, we should pass on updated+ -- recorded and unrecorded that reflect the removal+ return $ case local of+ Just gap -> (ftf, recorded', unrecorded', gap : patches)+ _ -> (ftf, recorded, unrecorded, patches) - Sealed rest <- mrp ftf nextRecorded nextUnrecorded fs- return . Sealed $ case local of- Just localFL -> (localFL +>+ rest)- Nothing -> rest+-- | Takes a file path and returns the FL of patches to remove that, wrapped in+-- a 'Gap'.+-- Returns 'Nothing' in case the path cannot be removed (if it is not tracked,+-- or if it's a directory and it's not tracked).+-- The three 'Tree' arguments are the recorded state, the unrecorded state+-- excluding the removal of this file, and the unrecorded state including the+-- removal of this file.+makeRemoveGap :: [DarcsFlag] -> (FilePath -> FileType)+ -> Tree IO -> Tree IO -> Tree IO -> AnchoredPath+ -> IO (Maybe (FreeLeft (FL Prim)))+makeRemoveGap opts ftf recorded unrecorded unrecorded' f =+ case (find recorded f, find unrecorded f) of+ (Just (SubTree _), Just (SubTree unrecordedChildren)) -> do+ if not $ null (list unrecordedChildren)+ then skipAndWarn "it is not empty"+ else return $ Just $ freeGap (rmdir f_fp :>: NilFL)+ (Just (File _), Just (File _)) ->+ Just `fmap` treeDiff ftf unrecorded unrecorded'+ (Just (File _), _) ->+ return $ Just $ freeGap (addfile f_fp :>: rmfile f_fp :>: NilFL)+ (Just (SubTree _), _) ->+ return $ Just $ freeGap (adddir f_fp :>: rmdir f_fp :>: NilFL)+ (_, _) -> skipAndWarn "it is not tracked by darcs"+ where f_fp = anchorPath "" f+ skipAndWarn reason =+ do putWarning opts . text $ "Can't remove " ++ f_fp+ ++ " (" ++ reason ++ ")"+ return $ Nothing - mrp _ _ _ [] = return (Sealed NilFL) rmDescription :: String rmDescription = "Help newbies find `darcs remove'."
src/Darcs/Commands/Repair.lhs view
@@ -18,7 +18,6 @@ \darcsCommand{repair} \begin{code} module Darcs.Commands.Repair ( repair, repairCmd ) where-import System.IO import Control.Monad( unless ) import System.Directory( renameFile )
src/Darcs/Commands/Replace.lhs view
@@ -36,7 +36,7 @@ ignoretimes, umaskOption, tokens, forceReplace, workingRepoDir, fixSubPaths ) import Darcs.Repository ( withRepoLock, ($-),- add_to_pending,+ addToPending, amInRepository, applyToWorking, readUnrecorded, readRecordedAndPending@@ -45,7 +45,8 @@ import Darcs.Patch.Apply ( forceTokReplace ) import Darcs.Patch.FileName( fn2fp ) import Darcs.Patch.Patchy ( Apply )-import Darcs.Witnesses.Ordered ( FL(..), unsafeFL, (+>+), concatFL )+import Darcs.Witnesses.Ordered ( FL(..), (+>+), concatFL, toFL )+import Darcs.Witnesses.Sealed ( Sealed(..), mapSeal, FreeLeft, Gap(..) ) import Darcs.Patch.RegChars ( regChars ) import Data.Char ( isSpace ) import Darcs.RepoPath ( SubPath, toFilePath, sp2fn )@@ -57,6 +58,7 @@ import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString as BS #include "impossible.h"+#include "gadts.h" replaceDescription :: String replaceDescription = "Substitute one word for another."@@ -166,22 +168,23 @@ unless (isTok toks tok) $ fail $ "'"++tok++"' is not a valid token!" checkToken old checkToken new- work <- readUnrecorded repository+ work <- readUnrecorded repository [] cur <- readRecordedAndPending repository files <- filterM (exists work) fs- pswork <- concatFL . unsafeFL <$> mapM (repl toks cur work) files- add_to_pending repository pswork+ Sealed pswork <- mapSeal concatFL . toFL <$> mapM (repl toks cur work) files+ addToPending repository pswork applyToWorking repository opts pswork `catch` \e -> fail $ "Can't do replace on working!\n" ++ "Perhaps one of the files already contains '"++ new++"'?\n" ++ show e+ return () where ftf _ = TextFile skipmsg f = "Skipping file '" ++ toFilePath f ++ "' which isn't in the repository." exists tree file = if isJust $ findFile tree (floatSubPath file) then return True else do putStrLn $ skipmsg file return False- repl :: String -> Tree IO -> Tree IO -> SubPath -> IO (FL Prim)+ repl :: String -> Tree IO -> Tree IO -> SubPath -> IO (FreeLeft (FL Prim)) repl toks cur work f = do work_replaced <- maybeApplyToTree replace_patch work cur_replaced <- maybeApplyToTree replace_patch cur@@ -191,11 +194,11 @@ putStrLn $ "Perhaps the recorded version of this " ++ "file already contains '" ++new++"'?" putStrLn $ "Use the --force option to override."- return NilFL+ return (emptyGap NilFL) where f_fp = toFilePath f replace_patch = tokreplace f_fp toks old new - get_force_replace :: SubPath -> String -> Tree IO -> IO (FL Prim)+ get_force_replace :: SubPath -> String -> Tree IO -> IO (FreeLeft (FL Prim)) get_force_replace f toks tree = do let path = floatSubPath f content <- readBlob $ fromJust $ findFile tree path@@ -204,7 +207,7 @@ case newcontent of Nothing -> bug "weird forcing bug in replace." Just _ -> do pfix <- treeDiff ftf tree tree'- return $ pfix +>+ (tokreplace f_fp toks old new :>: NilFL)+ return $ joinGap (+>+) pfix (freeGap (tokreplace f_fp toks old new :>: NilFL)) where f_fp = toFilePath f replaceCmd _ _ = fail "Usage: darcs replace OLD NEW [FILES]"@@ -212,7 +215,7 @@ floatSubPath :: SubPath -> AnchoredPath floatSubPath = floatPath . fn2fp . sp2fn -maybeApplyToTree :: Apply p => p -> Tree IO -> IO (Maybe (Tree IO))+maybeApplyToTree :: Apply p => p C(x y) -> Tree IO -> IO (Maybe (Tree IO)) maybeApplyToTree patch tree = catch (Just `fmap` applyToTree patch tree) (\_ -> return Nothing) @@ -232,7 +235,7 @@ -- found, it validates the argument and returns it, without the -- surrounding square brackets. Otherwise, it returns either -- 'defaultToks' or 'filenameToks' as explained in 'replaceHelp'.--- +-- -- Note: Limitations in the current replace patch file format prevents -- tokens and token-char specifiers from containing any whitespace. chooseToks :: [DarcsFlag] -> String -> String -> IO String
src/Darcs/Commands/Revert.lhs view
@@ -33,18 +33,20 @@ import Darcs.Utils ( askUser ) import Darcs.RepoPath ( toFilePath ) import Darcs.Repository ( withRepoLock, ($-), withGutsOf,- add_to_pending,+ addToPending, applyToWorking, amInRepository, readRecorded, unrecordedChanges ) import Darcs.Patch ( invert, applyToFilepaths, commute ) import Darcs.Witnesses.Ordered ( FL(..), (:>)(..), lengthFL, nullFL, (+>+) )-import Darcs.SelectChanges ( with_selected_last_changes_to_files' )-import Darcs.Patch.TouchesFiles ( choose_touching )+import Darcs.SelectChanges ( selectChanges, WhichChanges(Last), selectionContextPrim, runSelection )+import Darcs.Patch.TouchesFiles ( chooseTouching ) import Darcs.Commands.Unrevert ( writeUnrevert )-import Darcs.Witnesses.Sealed ( unsafeUnseal )+import Darcs.Witnesses.Sealed ( Sealed(..) ) +#include "gadts.h"+ revertDescription :: String revertDescription = "Discard unrecorded changes." @@ -84,14 +86,16 @@ changes <- unrecordedChanges opts repository files let pre_changed_files = applyToFilepaths (invert changes) (map toFilePath files) rec <- readRecorded repository- case unsafeUnseal $ choose_touching pre_changed_files changes of+ Sealed touching_changes <- return (chooseTouching pre_changed_files changes)+ (case touching_changes of NilFL -> putStrLn "There are no changes to revert!"- _ -> with_selected_last_changes_to_files' "revert" opts Nothing- pre_changed_files changes $ \ (norevert:>p) ->- if nullFL p- then putStrLn $ "If you don't want to revert after all," +++ _ -> do+ let context = selectionContextPrim "revert" opts Nothing pre_changed_files+ (norevert:>p) <- runSelection (selectChanges Last changes) context+ if nullFL p+ then putStrLn $ "If you don't want to revert after all," ++ " that's fine with me!"- else do+ else do let theseChanges = englishNum (lengthFL p) . This . Noun $ "change" yorn <- if All `elem` opts then return "y"@@ -99,7 +103,7 @@ case yorn of ('y':_) -> return () _ -> exitWith $ ExitSuccess withGutsOf repository $ do- add_to_pending repository $ invert p+ addToPending repository $ invert p when (Debug `elem` opts) $ putStrLn "About to write the unrevert file." case commute (norevert:>p) of Just (p':>_) -> writeUnrevert repository p' rec NilFL@@ -107,6 +111,7 @@ when (Debug `elem` opts) $ putStrLn "About to apply to the working directory." applyToWorking repository opts (invert p) `catch` \e -> fail ("Unable to apply inverse patch!" ++ show e)+ return ()) :: IO () putStrLn "Finished reverting." \end{code}
src/Darcs/Commands/Rollback.lhs view
@@ -22,7 +22,7 @@ module Darcs.Commands.Rollback ( rollback ) where -import Control.Monad ( when, filterM )+import Control.Monad ( when ) import System.Exit ( exitWith, ExitCode(..) ) import Data.List ( sort ) import Data.Maybe ( isJust )@@ -39,27 +39,32 @@ import Darcs.RepoPath ( toFilePath ) import Darcs.Repository ( Repository, amInRepository, withRepoLock, ($-), applyToWorking,- read_repo, slurp_recorded,+ readRepo, tentativelyMergePatches, withGutsOf, finalizeRepositoryChanges, invalidateIndex ) import Darcs.Patch ( RepoPatch, summary, invert, namepatch, effect, fromPrims, sortCoalesceFL, canonize )+import Darcs.Patch.Set ( newset2RL ) import Darcs.Patch.Prim ( Prim ) import Darcs.Witnesses.Ordered import Darcs.Hopefully ( PatchInfoAnd, n2pia )-import Darcs.Lock ( world_readable_temp )-import Darcs.SlurpDirectory ( empty_slurpy )+import Darcs.Lock ( worldReadableTemp ) import Darcs.Match ( firstMatch )-import Darcs.SelectChanges ( with_selected_last_changes_to_files_reversed,- with_selected_last_changes_to_files' )-import Darcs.Commands.Record ( fileExists, getLog )+import Darcs.SelectChanges ( selectChanges,+ WhichChanges(..),+ selectionContext, selectionContextPrim,+ runSelection+ )+import Darcs.Commands.Record ( getLog ) import Darcs.Commands.Unrecord ( getLastPatches )+import Darcs.Commands.WhatsNew ( announceFiles ) import Darcs.Utils ( clarifyErrors ) import Printer ( renderString ) import Progress ( debugMessage ) import Darcs.Witnesses.Sealed ( Sealed(..), FlippedSeal(..) ) import IsoDate ( getIsoDateTime ) #include "impossible.h"+#include "gadts.h" rollbackDescription :: String rollbackDescription =@@ -92,37 +97,32 @@ rollbackCmd :: [DarcsFlag] -> [String] -> IO () rollbackCmd opts args = withRepoLock opts $- \repository -> do- rec <- if null args then return empty_slurpy- else slurp_recorded repository files <- sort `fmap` fixSubPaths opts args- existing_files <- map toFilePath `fmap` filterM (fileExists rec) files- non_existent_files <- map toFilePath `fmap` filterM (fmap not . fileExists rec) files- when (not $ null existing_files) $- putStrLn $ "Recording changes in "++unwords existing_files++":\n"- when (not $ null non_existent_files) $- putStrLn $ "Non existent files or directories: "++unwords non_existent_files++"\n"- when ((not $ null non_existent_files) && null existing_files) $+ let files_fp = map toFilePath files+ existing_files <- announceFiles repository files "Recording changes in"+ when (null existing_files && (not $ null files)) $ fail "None of the files you specified exist!"- allpatches <- read_repo repository+ allpatches <- readRepo repository FlippedSeal patches <- return $ if firstMatch opts then getLastPatches opts allpatches- else FlippedSeal $ concatRL allpatches- with_selected_last_changes_to_files_reversed "rollback" opts Nothing existing_files- (reverseRL patches) $ \ (_ :> ps) ->- do when (nullFL ps) $ do putStrLn "No patches selected!"- exitWith ExitSuccess- definePatches ps- with_selected_last_changes_to_files' "rollback" opts Nothing- existing_files (concatFL $ mapFL_FL canonize $ sortCoalesceFL $ effect ps)+ else FlippedSeal $ newset2RL allpatches+ let patches_context = selectionContext "rollback" opts Nothing files_fp+ (_ :> ps) <- runSelection (selectChanges LastReversed (reverseRL patches)) patches_context+ when (nullFL ps) $ do putStrLn "No patches selected!"+ exitWith ExitSuccess+ definePatches ps+ let hunks_context = selectionContextPrim "rollback" opts Nothing files_fp+ hunks = (concatFL $ mapFL_FL canonize $ sortCoalesceFL $ effect ps)+ runSelection (selectChanges Last hunks) hunks_context >>= (rollItBackNow opts repository ps) rollItBackNow :: (RepoPatch p1, RepoPatch p) =>- [DarcsFlag] -> Repository p1 -> FL (PatchInfoAnd p)- -> (t :> FL Prim) -> IO ()+ [DarcsFlag] -> Repository p1 C(r u t) -> FL (PatchInfoAnd p) C(x y)+ -> (q :> FL Prim) C(a t) -> IO () rollItBackNow opts repository ps (_ :> ps'') = do when (nullFL ps'') $ do putStrLn "No changes selected!" exitWith ExitSuccess- let make_log = world_readable_temp "darcs-rollback"+ let make_log = worldReadableTemp "darcs-rollback" newlog = Just ("", "":"rolling back:":"":lines (renderString $ summary ps )) --tentativelyRemovePatches repository opts (mapFL_FL hopefully ps) (name, my_log, logf) <- getLog opts newlog make_log $ invert ps''@@ -139,6 +139,7 @@ finalizeRepositoryChanges repository debugMessage "About to apply rolled-back changes to working directory." revertable $ applyToWorking repository opts pw+ return () when (isJust logf) $ removeFile (fromJust logf) putStrLn "Finished rolling back." where revertable x = x `clarifyErrors` unlines
src/Darcs/Commands/Send.lhs view
@@ -18,21 +18,21 @@ \darcsCommand{send} \begin{code} {-# OPTIONS_GHC -cpp #-}-{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP, TypeOperators #-} module Darcs.Commands.Send ( send ) where-import Data.Char ( isAlpha, isDigit, isSpace, toLower ) import System.Exit ( exitWith, ExitCode( ExitSuccess ) ) import System.IO.Error ( ioeGetErrorString ) import System.IO ( hClose ) import Control.Monad ( when, unless, forM_ )-import Data.Maybe ( isJust, isNothing )+import Storage.Hashed.Tree ( Tree )+import Data.Maybe ( isNothing ) import Darcs.Commands ( DarcsCommand(..), putInfo, putVerbose ) import Darcs.Arguments ( DarcsFlag( EditDescription, LogFile,- Target, OutputAutoName, Output, Context,+ Target, Context, DryRun, Quiet, Unified- ),+ ), getOutput, fixUrl, definePatches, getCc, getAuthor, workingRepoDir, editDescription, logfile, rmlogfile,@@ -43,31 +43,34 @@ allInteractive, getSendmailCmd, printDryRunMessageAndExit, summary, allowUnrelatedRepos,- fromOpt, dryRun, sendToContext,+ fromOpt, dryRun, sendToContext, getOutput,+ changesReverse, )-import Darcs.Flags ( willRemoveLogFile )-import Darcs.Hopefully ( PatchInfoAnd, hopefully, info )+import Darcs.Flags ( willRemoveLogFile, doReverse )+import Darcs.Hopefully ( PatchInfoAnd, hopefully, patchDesc ) import Darcs.Repository ( PatchSet, Repository, amInRepository, identifyRepositoryFor, withRepoReadLock, ($-),- read_repo, readRecorded, prefsUrl, checkUnrelatedRepos )+ readRepo, readRecorded, prefsUrl, checkUnrelatedRepos )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif import Darcs.Patch ( RepoPatch, description, applyToTree, invert )-import Darcs.Witnesses.Ordered ( FL(..), RL(..), (:>)(..), (:\/:)(..),- mapRL_RL, mapFL, mapRL, reverseRL, mapFL_FL, lengthFL, nullFL )-import Darcs.Patch.Bundle ( make_bundle, scan_context )-import Darcs.Patch.Info ( just_name )+import Darcs.Witnesses.Ordered ( FL(..), (:>)(..), (:\/:)(..), (:>>)(..),+ mapFL, mapFL_FL, lengthFL, nullFL, unsafeCoerceP )+import Darcs.Patch.Bundle ( makeBundleN, scanContext, patchFilename ) import Darcs.Repository.Prefs ( defaultrepo, setDefaultrepo, getPreflist ) import Darcs.External ( signString, sendEmailDoc, fetchFilePS, Cachable(..), generateEmail ) import ByteStringUtils ( mmapFilePS ) import qualified Data.ByteString.Char8 as BC (unpack)-import Darcs.Lock ( withOpenTemp, writeDocBinFile, readDocBinFile, world_readable_temp, removeFileMayNotExist )-import Darcs.SelectChanges ( with_selected_changes )-import Darcs.Patch.Depends ( get_common_and_uncommon )-import Darcs.Utils ( askUser, catchall, edit_file, formatPath )+import Darcs.Lock ( withOpenTemp, writeDocBinFile, readDocBinFile, worldReadableTemp, removeFileMayNotExist )+import Darcs.SelectChanges ( selectChanges, WhichChanges(..), selectionContext, runSelection )+import Darcs.Patch.Depends ( findCommonWithThem )+import Darcs.Utils ( askUser, catchall, editFile, formatPath ) import Progress ( debugMessage )-import Darcs.Email ( make_email )+import Darcs.Email ( makeEmail ) import Printer ( Doc, vsep, vcat, text, ($$), (<+>), (<>), putDoc ) import Darcs.RepoPath ( FilePathLike, toFilePath, AbsolutePath, AbsolutePathOrStd,- getCurrentDirectory, makeAbsoluteOrStd, useAbsoluteOrStd )+ getCurrentDirectory, useAbsoluteOrStd ) import HTTP ( postUrl ) #include "impossible.h" @@ -123,7 +126,7 @@ commandArgdefaults = defaultrepo, commandAdvancedOptions = [logfile, rmlogfile, remoteRepo,- sendToContext] +++ sendToContext, changesReverse] ++ networkOptions, commandBasicOptions = [matchSeveral, depsSel, allInteractive,@@ -132,7 +135,8 @@ output,outputAutoName,sign] ++dryRun++[summary, editDescription,- setDefault, workingRepoDir,+ setDefault False,+ workingRepoDir, sendmailCmd, allowUnrelatedRepos]} @@ -147,9 +151,9 @@ -- Test to make sure we aren't trying to push to the current repo here <- getCurrentDirectory when (repodir == toFilePath here) $- fail ("Can't send to current repository! Did you mean send -"++"-context?")+ fail ("Can't send to current repository! Did you mean send --context?") repo <- identifyRepositoryFor repository repodir- them <- read_repo repo+ them <- readRepo repo old_default <- getPreflist "defaultrepo" setDefaultrepo repodir input_opts when (old_default == [repodir] && not (Quiet `elem` input_opts)) $@@ -158,43 +162,47 @@ sendToThem repository input_opts wtds repodir them where the_context [] = return Nothing the_context (Context foo:_)- = (Just . scan_context )`fmap` mmapFilePS (toFilePath foo)+ = (Just . scanContext )`fmap` mmapFilePS (toFilePath foo) the_context (_:fs) = the_context fs sendCmd _ _ = impossible -sendToThem :: RepoPatch p => Repository p C(r u t) -> [DarcsFlag] -> [WhatToDo] -> String -> PatchSet p C(x) -> IO ()+sendToThem :: RepoPatch p => Repository p C(r u t) -> [DarcsFlag] -> [WhatToDo] -> String -> PatchSet p C(Origin x) -> IO () sendToThem repo opts wtds their_name them = do- us <- read_repo repo- case get_common_and_uncommon (us, them) of- (common, us' :\/: _) -> do- checkUnrelatedRepos opts common us them- (case us' of- NilRL -> do putInfo opts $ text "No recorded local changes to send!"- exitWith ExitSuccess- _ -> putVerbose opts $ text "We have the following patches to send:"- $$ (vcat $ mapRL description us')) :: IO ()- pristine <- readRecorded repo- let our_ps = reverseRL us'- with_selected_changes "send" opts Nothing our_ps $- \ (to_be_sent :> _) -> do- printDryRunMessageAndExit "send" opts to_be_sent- when (nullFL to_be_sent) $ do- putInfo opts $ text "You don't want to send any patches, and that's fine with me!"- exitWith ExitSuccess- definePatches to_be_sent- pristine' <- applyToTree (invert $ mapRL_RL hopefully us') pristine- unsig_bundle <- make_bundle (Unified:opts) pristine' common (mapFL_FL hopefully to_be_sent)- bundle <- signString opts unsig_bundle- let make_fname (tb:>:_) = patchFilename $ patchDesc tb- make_fname _ = impossible- fname = make_fname to_be_sent- outname = getOutput opts fname- case outname of- Just fname' -> writeBundleToFile opts to_be_sent bundle fname' their_name- Nothing -> sendBundle opts to_be_sent bundle fname wtds their_name+ us <- readRepo repo+ common :>> us' <- return $ findCommonWithThem us them+ checkUnrelatedRepos opts us them+ (case us' of+ NilFL -> do putInfo opts $ text "No recorded local changes to send!"+ exitWith ExitSuccess+ _ -> putVerbose opts $ text "We have the following patches to send:"+ $$ (vcat $ mapFL description us')) :: IO ()+ pristine <- readRecorded repo+ let context = selectionContext "send" opts Nothing []+ selector = if doReverse opts+ then selectChanges FirstReversed+ else selectChanges First+ (to_be_sent :> _) <- runSelection (selector us') context+ printDryRunMessageAndExit "send" opts to_be_sent+ when (nullFL to_be_sent) $ do+ putInfo opts $ text "You don't want to send any patches, and that's fine with me!"+ exitWith ExitSuccess+ definePatches to_be_sent+ bundle <- prepareBundle opts common pristine (us':\/:to_be_sent)+ let make_fname (tb:>:_) = patchFilename $ patchDesc tb+ make_fname _ = impossible+ fname = make_fname to_be_sent+ outname = getOutput opts fname+ case outname of+ Just fname' -> writeBundleToFile opts to_be_sent bundle fname' wtds their_name+ Nothing -> sendBundle opts to_be_sent bundle fname wtds their_name -patchDesc :: forall p C(x y) . PatchInfoAnd p C(x y) -> String-patchDesc p = just_name $ info p+prepareBundle :: forall p C(x y z). RepoPatch p => [DarcsFlag] -> PatchSet p C(Origin z)+ -> Tree IO -> ((FL (PatchInfoAnd p)) :\/: (FL (PatchInfoAnd p))) C(x y)+ -> IO Doc+prepareBundle opts common pristine (us' :\/: to_be_sent) = do+ pristine' <- applyToTree (invert $ mapFL_FL hopefully us') pristine+ unsig_bundle <- makeBundleN (Just pristine') (unsafeCoerceP common) (mapFL_FL hopefully to_be_sent)+ signString opts unsig_bundle sendBundle :: forall p C(x y) . (RepoPatch p) => [DarcsFlag] -> FL (PatchInfoAnd p) C(x y) -> Doc -> String -> [WhatToDo] -> String -> IO ()@@ -214,13 +222,13 @@ Nothing -> auto_subject to_be_sent Just subj -> subj (mailcontents, mailfile) <- getDescription opts their_name to_be_sent- let body = make_email their_name+ let body = makeEmail their_name (maybe [] (\x -> [("In-Reply-To", x), ("References", x)]) . getInReplyTo $ opts) (Just mailcontents) bundle (Just fname) contentAndBundle = Just (mailcontents, bundle)- + sendmail = do sm_cmd <- getSendmailCmd opts (sendEmailDoc from (lt [t | SendMail t <- thetargets]) (thesubject) (getCc opts)@@ -247,36 +255,30 @@ postUrl url (BC.unpack nbody) "message/rfc822") `catch` const sendmail cleanup opts mailfile- where- lt [t] = t- lt [t,""] = t- lt (t:ts) = t++" , "++lt ts- lt [] = "" ++lt :: [String] -> String+lt [t] = t+lt [t,""] = t+lt (t:ts) = t++" , "++lt ts+lt [] = ""+ cleanup :: (FilePathLike t) => [DarcsFlag] -> Maybe t -> IO () cleanup opts (Just mailfile) = when (isNothing (getFileopt opts) || (willRemoveLogFile opts)) $ removeFileMayNotExist mailfile cleanup _ Nothing = return () writeBundleToFile :: forall p C(x y) . (RepoPatch p) => [DarcsFlag] -> FL (PatchInfoAnd p) C(x y) -> Doc ->- AbsolutePathOrStd -> String -> IO ()-writeBundleToFile opts to_be_sent bundle fname their_name =+ AbsolutePathOrStd -> [WhatToDo] -> String -> IO ()+writeBundleToFile opts to_be_sent bundle fname wtds their_name = do (d,f) <- getDescription opts their_name to_be_sent let putabs a = do writeDocBinFile a (d $$ bundle) putStrLn $ "Wrote patch to " ++ toFilePath a ++ "." putstd = putDoc (d $$ bundle) useAbsoluteOrStd putabs putstd fname+ let mails = lt [ t | SendMail t <- wtds ]+ unless (null mails) $ putInfo opts $ text $ "The usual recipent for this bundle is: " ++ mails cleanup opts f--safeFileChar :: Char -> Char-safeFileChar c | isAlpha c = toLower c- | isDigit c = c- | isSpace c = '-'-safeFileChar _ = '_'--patchFilename :: String -> String-patchFilename the_summary = name ++ ".dpatch"- where name = map safeFileChar the_summary \end{code} \begin{options}@@ -331,11 +333,7 @@ decideOnBehavior :: RepoPatch p => [DarcsFlag] -> Repository p C(r u t) -> IO [WhatToDo] decideOnBehavior opts the_remote_repo = case the_targets of- [] ->- if isJust $ getOutput opts ""- then return []- else- do wtds <- check_post+ [] -> do wtds <- check_post unless (null wtds) $ announce_recipients wtds return wtds ts -> do announce_recipients ts@@ -371,12 +369,6 @@ else when (null the_targets) $ putInfo opts . text $ "Patch bundle will be sent to: "++unwords (map pn emails) -getOutput :: [DarcsFlag] -> FilePath -> Maybe AbsolutePathOrStd-getOutput (Output a:_) _ = return a-getOutput (OutputAutoName a:_) f = return $ makeAbsoluteOrStd a f-getOutput (_:flags) f = getOutput flags f-getOutput [] _ = Nothing- getTargets :: [WhatToDo] -> IO [WhatToDo] getTargets [] = do fmap ((:[]) . SendMail) $ askUser "What is the target email address? " getTargets wtds = return wtds@@ -434,7 +426,7 @@ when (isNothing $ getFileopt opts) $ writeDocBinFile file patchdesc debugMessage $ "About to edit file " ++ file- edit_file file+ editFile file return () doc <- readDocBinFile file return (doc, Just file)@@ -450,7 +442,7 @@ Nothing -> if EditDescription `elem` opts then Just tempfile else Nothing- tempfile = world_readable_temp "darcs-temp-mail"+ tempfile = worldReadableTemp "darcs-temp-mail" getFileopt :: [DarcsFlag] -> Maybe AbsolutePath getFileopt (LogFile f:_) = Just f
src/Darcs/Commands/SetPref.lhs view
@@ -27,7 +27,7 @@ import Darcs.Commands ( DarcsCommand(..), nodefaults ) import Darcs.Arguments ( DarcsFlag, workingRepoDir, umaskOption )-import Darcs.Repository ( amInRepository, add_to_pending, withRepoLock, ($-) )+import Darcs.Repository ( amInRepository, addToPending, withRepoLock, ($-) ) import Darcs.Patch ( changepref ) import Darcs.Witnesses.Ordered ( FL(..) ) import Darcs.Repository.Prefs ( getPrefval, changePrefval, )@@ -100,9 +100,12 @@ oval <- getPrefval pref old <- case oval of Just v -> return v Nothing -> return ""+ when ('\n' `elem` val) $ do+ putStrLn $ val ++ "is not a valid preference value: newlines forbidden!"+ exitWith $ ExitFailure 1 changePrefval pref old val putStrLn $ "Changing value of "++pref++" from '"++old++"' to '"++val++"'"- add_to_pending repository (changepref pref old val :>: NilFL)+ addToPending repository (changepref pref old val :>: NilFL) setprefCmd _ _ = impossible \end{code}
src/Darcs/Commands/ShowAuthors.lhs view
@@ -33,8 +33,9 @@ import Darcs.Commands ( DarcsCommand(..), nodefaults, putWarning ) import Darcs.External ( viewDoc ) import Darcs.Hopefully ( info )-import Darcs.Repository ( amInRepository, read_repo, withRepository, ($-) )-import Darcs.Patch.Info ( pi_author )+import Darcs.Repository ( amInRepository, readRepo, withRepository, ($-) )+import Darcs.Patch.Info ( piAuthor )+import Darcs.Patch.Set ( newset2RL ) import Darcs.Witnesses.Ordered ( mapRL, concatRL ) import Printer ( text ) import Data.Function (on)@@ -102,9 +103,9 @@ authorsCmd :: [DarcsFlag] -> [String] -> IO () authorsCmd opts _ = withRepository opts $- \repository -> do- patches <- read_repo repository+ patches <- readRepo repository spellings <- compiledAuthorSpellings opts- let authors = mapRL (pi_author . info) $ concatRL patches+ let authors = mapRL (piAuthor . info) $ newset2RL patches viewDoc $ text $ unlines $ if Verbose `elem` opts then authors
src/Darcs/Commands/ShowContents.lhs view
@@ -34,7 +34,7 @@ import Darcs.Patch.Match( Matcher ) import Darcs.Match ( haveNonrangeMatch, applyInvToMatcher, nonrangeMatcher , InclusiveOrExclusive(..), matchExists )-import Darcs.Repository ( withRepository, ($-), findRepository, read_repo, readRecorded )+import Darcs.Repository ( withRepository, ($-), findRepository, readRepo, readRecorded ) import Darcs.Patch( RepoPatch ) import Storage.Hashed.Monad( virtualTreeIO ) @@ -73,7 +73,7 @@ let matcher = getMatcher opts unapply_to_match = applyInvToMatcher Exclusive matcher matched <- if (haveNonrangeMatch opts)- then do patchset <- read_repo repository+ then do patchset <- readRepo repository unless (matchExists matcher patchset) $ fail $ "Couldn't match pattern " ++ show matcher snd `fmap` virtualTreeIO (unapply_to_match patchset) pristine
src/Darcs/Commands/ShowFiles.lhs view
@@ -26,14 +26,16 @@ import Darcs.Arguments ( DarcsFlag(..), workingRepoDir, files, directories, pending, nullFlag, matchOne ) import Darcs.Commands ( DarcsCommand(..), nodefaults )-import Darcs.Repository ( Repository, amInRepository, slurp_pending, slurp_recorded,- withRepository )+import Darcs.Repository ( Repository, amInRepository, withRepository ) import Darcs.Patch ( RepoPatch )-import Darcs.SlurpDirectory ( Slurpy, list_slurpy, list_slurpy_files, list_slurpy_dirs, slurp )+import Darcs.Repository.State ( readRecorded, readRecordedAndPending )+import Storage.Hashed.Tree( Tree, TreeItem(..), list, expand )+import Storage.Hashed.AnchoredPath( anchorPath )+import Storage.Hashed.Plain( readPlainTree )+ import Data.List( isPrefixOf ) import Darcs.Match ( haveNonrangeMatch, getNonrangeMatch )-import Workaround ( getCurrentDirectory ) import Darcs.Lock ( withDelayedDir ) showFilesDescription :: String showFilesDescription = "Show version-controlled files in the working copy."@@ -78,45 +80,45 @@ commandBasicOptions = [files, directories, pending, nullFlag, matchOne, workingRepoDir] } -toListFiles, toListManifest :: [DarcsFlag] -> Slurpy -> [FilePath]+toListFiles, toListManifest :: [DarcsFlag] -> Tree m -> [FilePath] toListFiles opts = filesDirs (NoFiles `notElem` opts) (NoDirectories `notElem` opts) toListManifest opts = filesDirs (NoFiles `notElem` opts) (Directories `elem` opts) -filesDirs :: Bool -> Bool -> Slurpy -> [FilePath]-filesDirs False False = \_ -> []-filesDirs False True = list_slurpy_dirs-filesDirs True False = list_slurpy_files-filesDirs True True = list_slurpy+filesDirs :: Bool -> Bool -> Tree m -> [FilePath]+filesDirs False False _ = []+filesDirs False True t = "." : [ anchorPath "." p | (p, SubTree _) <- list t ]+filesDirs True False t = [ anchorPath "." p | (p, File _) <- list t ]+filesDirs True True t = "." : (map (anchorPath "." . fst) $ list t) -manifestCmd :: ([DarcsFlag] -> Slurpy -> [FilePath]) -> [DarcsFlag] -> [String] -> IO ()+manifestCmd :: ([DarcsFlag] -> Tree IO -> [FilePath]) -> [DarcsFlag] -> [String] -> IO () manifestCmd to_list opts argList = do list <- (to_list opts) `fmap` withRepository opts myslurp- case argList of + case argList of [] -> mapM_ output list prefixes -> mapM_ output (onlysubdirs prefixes list)- where myslurp :: RepoPatch p => Repository p C(r u r) -> IO Slurpy- myslurp = do let fRevisioned = haveNonrangeMatch opts- fPending = Pending `elem` opts- fNoPending = NoPending `elem` opts + where myslurp :: RepoPatch p => Repository p C(r u r) -> IO (Tree IO)+ myslurp r = do let fRevisioned = haveNonrangeMatch opts+ fPending = Pending `elem` opts+ fNoPending = NoPending `elem` opts -- this covers all 8 options- case (fRevisioned,fPending,fNoPending) of- (True,False,_) -> slurp_revision opts+ expand =<< case (fRevisioned,fPending,fNoPending) of+ (True,False,_) -> slurpRevision opts r (True,True,_) -> error $ "can't mix revisioned and pending flags"- (False,False,True) -> slurp_recorded- (False,_,False) -> slurp_pending -- pending is default+ (False,False,True) -> readRecorded r+ (False,_,False) -> readRecordedAndPending r -- pending is default (False,True,True) -> error $ "can't mix pending and no-pending flags" output_null name = do { putStr name ; putChar '\0' } output = if NullFlag `elem` opts then output_null else putStrLn- isParentDir a b = a == b - || (a ++ "/") `isPrefixOf` b - || ("./" ++ a ++ "/") `isPrefixOf` b + isParentDir a b = a == b+ || (a ++ "/") `isPrefixOf` b+ || ("./" ++ a ++ "/") `isPrefixOf` b || "./" ++ a == b onlysubdirs suffixes = filter $ or . mapM isParentDir suffixes -slurp_revision :: RepoPatch p => [DarcsFlag] -> Repository p C(r u r) -> IO Slurpy-slurp_revision opts r = withDelayedDir "revisioned.showfiles" $ \_ -> do - getNonrangeMatch r opts - slurp =<< getCurrentDirectory+slurpRevision :: RepoPatch p => [DarcsFlag] -> Repository p C(r u r) -> IO (Tree IO)+slurpRevision opts r = withDelayedDir "revisioned.showfiles" $ \_ -> do+ getNonrangeMatch r opts+ expand =<< readPlainTree "." \end{code}
src/Darcs/Commands/ShowRepo.lhs view
@@ -29,13 +29,14 @@ import Text.Html ( tag, stringToHtml ) import Darcs.Arguments ( DarcsFlag(..), workingRepoDir, files, xmloutput ) import Darcs.Commands ( DarcsCommand(..), nodefaults )-import Darcs.Repository ( withRepository, ($-), amInRepository, read_repo )+import Darcs.Repository ( withRepository, ($-), amInRepository, readRepo ) import Darcs.Repository.Internal ( Repository(..), RepoType(..) ) import Darcs.Repository.Format ( RepoFormat(..) ) import Darcs.Repository.Prefs ( getPreflist )-import Darcs.Repository.Motd ( get_motd )+import Darcs.Repository.Motd ( getMotd ) import Darcs.Patch ( RepoPatch )-import Darcs.Witnesses.Ordered ( lengthRL, concatRL )+import Darcs.Patch.Set ( newset2RL )+import Darcs.Witnesses.Ordered ( lengthRL ) import qualified Data.ByteString.Char8 as BC (unpack) showRepoHelp :: String@@ -139,11 +140,11 @@ where prefOut = uncurry out . (\(p,v) -> (p++" Pref", (dropWhile isSpace v))) . break isSpace showRepoMOTD :: RepoPatch p => PutInfo -> Repository p C(r u r) -> IO ()-showRepoMOTD out (Repo loc _ _ _) = get_motd loc >>= out "MOTD" . BC.unpack+showRepoMOTD out (Repo loc _ _ _) = getMotd loc >>= out "MOTD" . BC.unpack -- Support routines to provide information used by the PutInfo operations above. numPatches :: RepoPatch p => Repository p C(r u r) -> IO Int-numPatches r = read_repo r >>= (return . lengthRL . concatRL)+numPatches r = readRepo r >>= (return . lengthRL . newset2RL) \end{code}
src/Darcs/Commands/ShowTags.lhs view
@@ -21,9 +21,10 @@ import Darcs.Arguments ( DarcsFlag(..), workingRepoDir ) import Darcs.Commands ( DarcsCommand(..), nodefaults ) import Darcs.Hopefully ( info )-import Darcs.Repository ( amInRepository, read_repo, withRepository, ($-) )-import Darcs.Patch.Info ( pi_tag )-import Darcs.Witnesses.Ordered ( mapRL, concatRL )+import Darcs.Repository ( amInRepository, readRepo, withRepository, ($-) )+import Darcs.Patch.Info ( piTag )+import Darcs.Patch.Set ( newset2RL )+import Darcs.Witnesses.Ordered ( mapRL ) import System.IO ( stderr, hPutStrLn ) -- import Printer ( renderPS ) @@ -55,10 +56,10 @@ tagsCmd :: [DarcsFlag] -> [String] -> IO () tagsCmd opts _ = withRepository opts $- \repository -> do- patches <- read_repo repository- sequence_ $ mapRL process $ concatRL patches+ patches <- readRepo repository+ sequence_ $ mapRL process $ newset2RL patches where process hp =- case pi_tag $ info hp of+ case piTag $ info hp of Just t -> do t' <- normalize t t False putStrLn t'
src/Darcs/Commands/Tag.lhs view
@@ -31,15 +31,15 @@ pipeInteractive, askLongComment, workingRepoDir, getAuthor ) import Darcs.Hopefully ( n2pia )-import Darcs.Repository ( amInRepository, withRepoLock, ($-), read_repo,- tentativelyAddPatch, finalizeRepositoryChanges, +import Darcs.Repository ( amInRepository, withRepoLock, ($-), readRepo,+ tentativelyAddPatch, finalizeRepositoryChanges, ) import Darcs.Patch ( infopatch, identity, adddeps ) import Darcs.Patch.Info ( patchinfo )-import Darcs.Patch.Depends ( get_tags_right )+import Darcs.Patch.Depends ( getTagsRight ) import Darcs.Commands.Record ( getDate, getLog ) import Darcs.Witnesses.Ordered ( FL(..) )-import Darcs.Lock ( world_readable_temp )+import Darcs.Lock ( worldReadableTemp ) import Darcs.Flags ( DarcsFlag(..) ) import System.IO ( hPutStr, stderr ) @@ -90,7 +90,7 @@ tagCmd opts args = withRepoLock opts $- \repository -> do date <- getDate opts the_author <- getAuthor opts- deps <- get_tags_right `fmap` read_repo repository+ deps <- getTagsRight `fmap` readRepo repository (name, long_comment, mlogf) <- get_name_log opts args myinfo <- patchinfo date name the_author long_comment let mypatch = infopatch myinfo identity@@ -100,7 +100,7 @@ putStrLn $ "Finished tagging patch '"++name++"'" where get_name_log :: [DarcsFlag] -> [String] -> IO (String, [String], Maybe String) get_name_log o a = do let o2 = if null a then o else (add_patch_name o (unwords a))- (name, comment, mlogf) <- getLog o2 Nothing (world_readable_temp "darcs-tag") NilFL+ (name, comment, mlogf) <- getLog o2 Nothing (worldReadableTemp "darcs-tag") NilFL when (length name < 2) $ hPutStr stderr $ "Do you really want to tag '" ++name++"'? If not type: darcs obliterate --last=1\n"
src/Darcs/Commands/TrackDown.lhs view
@@ -25,15 +25,18 @@ import Control.Monad( when ) import Darcs.Commands ( DarcsCommand(..), nodefaults )-import Darcs.Arguments ( DarcsFlag(SetScriptsExecutable), workingRepoDir,+import Darcs.Arguments ( DarcsFlag(SetScriptsExecutable, Bisect), workingRepoDir, bisect, setScriptsExecutableOption ) import Darcs.Hopefully ( hopefully )-import Darcs.Repository ( amInRepository, read_repo, withRepoReadLock, ($-), withRecorded,+import Darcs.Repository ( amInRepository, readRepo, withRepoReadLock, ($-), withRecorded, setScriptsExecutable )-import Darcs.Witnesses.Ordered ( FL(..), mapRL_RL, concatRL )-import Darcs.Patch ( RepoPatch, Named, description, apply, invert, invertRL )+import Darcs.Witnesses.Ordered ( RL(..), (:<)(..), (+<+),+ reverseRL, splitAtRL, lengthRL, mapRL, mapFL, mapRL_RL, concatRL )+import Darcs.Patch.Patchy ( Invert, Apply, ShowPatch )+import Darcs.Patch ( RepoPatch, Named, description, apply, invert )+import Darcs.Patch.Set ( newset2RL ) import Printer ( putDocLn )-import Darcs.Test ( get_test )+import Darcs.Test ( getTest ) import Darcs.Lock ( withTempDir ) #include "gadts.h"@@ -47,7 +50,14 @@ "passes a test. Given no arguments, it uses the default repository test.\n"++ "Given one argument, it treats it as a test command. Given two arguments,\n"++ "the first is an initialization command with is run only once, and the\n"++- "second is the test command.\n"+ "second is the test command.\n\n"+++ "Without the --bisect option, trackdown does linear search starting from head,\n"+++ "and moving away from head. With the --bisect option, it does binary search.\n\n"+++ "Under the assumption that failure is monotonous, trackdown produces\n"+++ "the same result with and without --bisect. (Monotonous means that when\n"+++ "moving away from head, the test result changes only once from \"fail\" to \"ok\".)\n"+++ "If failure is not monotonous, any one of the patches that break the test is\n"+++ "found at random." trackdown :: DarcsCommand trackdown = DarcsCommand {commandName = "trackdown",@@ -61,14 +71,14 @@ commandGetArgPossibilities = return [], commandArgdefaults = nodefaults, commandAdvancedOptions = [setScriptsExecutableOption],- commandBasicOptions = [workingRepoDir]}+ commandBasicOptions = [workingRepoDir, bisect]} trackdownCmd :: [DarcsFlag] -> [String] -> IO () trackdownCmd opts args = withRepoReadLock opts $- \repository -> do- patches <- read_repo repository+ patches <- readRepo repository (init,test) <- case args of [] ->- do t <- get_test opts+ do t <- getTest opts return (return ExitSuccess, t) [cmd] -> do putStrLn $ "Tracking down command:\n"++cmd@@ -80,20 +90,96 @@ _ -> fail "Trackdown expects zero to two arguments." withRecorded repository (withTempDir "trackingdown") $ \_ -> do when (SetScriptsExecutable `elem` opts) setScriptsExecutable- init- trackNext opts test . invertRL . mapRL_RL hopefully . concatRL $ patches+ _ <- init+ (if Bisect `elem` opts+ then trackBisect+ else trackNextLinear) opts test (mapRL_RL hopefully . newset2RL $ patches) -trackNext :: RepoPatch p => [DarcsFlag] -> (IO ExitCode) -> FL (Named p) C(x y) -> IO ()-trackNext opts test (p:>:ps) = do+-- | linear search (without --bisect)+trackNextLinear :: RepoPatch p => [DarcsFlag] -> IO ExitCode -> RL (Named p) C(x y) -> IO ()+trackNextLinear opts test (p:<:ps) = do test_result <- test if test_result == ExitSuccess then putStrLn "Success!"- else do apply opts p `catch` \e -> fail ("Bad patch:\n" ++ show e)+ else do apply opts (invert p) `catch` \e -> fail ("Bad patch:\n" ++ show e) putStrLn "Trying without the patch:" putDocLn $ description $ invert p hFlush stdout- trackNext opts test ps-trackNext _ _ NilFL = putStrLn "Noone passed the test!"+ trackNextLinear opts test ps+trackNextLinear _opts test NilRL = do+ test_result <- test+ if test_result == ExitSuccess+ then putStrLn "Success!"+ else putStrLn "Noone passed the test!"++-- | binary search (with --bisect)+trackBisect :: (Invert p, ShowPatch p, Apply p) => [DarcsFlag] -> IO ExitCode -> RL p C(x y) -> IO ()+trackBisect opts test NilRL = do+ test_result <- test+ if test_result == ExitSuccess+ then putStrLn "Success!"+ else putStrLn "Noone passed the test!"+trackBisect opts test ps = do+ test_result <- test+ if test_result == ExitSuccess+ then putStrLn ("Test does not fail on head.")+ else trackNextBisect opts curr_prog test BisectRight (patchTreeFromRL ps)+ where+ curr_prog = (1, 1 + round ((logBase 2 $ fromIntegral $ lengthRL ps) :: Double)) :: (Int,Int)++-- | Bisect Patch Tree+data PatchTree p C(x y) where+ Leaf :: p C(x y) -> PatchTree p C(x y)+ Fork :: PatchTree p C(y z) -> PatchTree p C(x y) -> PatchTree p C(x z)++-- | Direction of Bisect trackdown+data BisectDir = BisectLeft | BisectRight deriving Show++-- | Progress of Bisect+type BisectState = (Int, Int)++-- | Create Bisect PatchTree from the RL+patchTreeFromRL :: (Invert p, ShowPatch p, Apply p) => RL p C(x y) -> PatchTree p C(x y)+patchTreeFromRL (l :<: NilRL) = Leaf l+patchTreeFromRL xs = case splitAtRL (lengthRL xs `div` 2) xs of+ (l :< r) -> Fork (patchTreeFromRL l) (patchTreeFromRL r)++-- | Convert PatchTree back to RL+patchTree2RL :: (Invert p) => PatchTree p C(x y) -> RL p C(x y)+patchTree2RL (Leaf p) = p :<: NilRL+patchTree2RL (Fork l r) = (patchTree2RL l) +<+ (patchTree2RL r)++-- | Iterate the Patch Tree+trackNextBisect :: (Invert p, ShowPatch p, Apply p) => [DarcsFlag] -> BisectState -> IO ExitCode -> BisectDir -> PatchTree p C(x y) -> IO ()+trackNextBisect opts (dnow, dtotal) test dir (Fork l r) = do+ putStr ("Trying " ++ show dnow ++ "/" ++ show dtotal ++ " sequences...\n")+ hFlush stdout+ case dir of+ BisectRight -> jumpHalfOnRight opts l -- move in temporary repo+ BisectLeft -> jumpHalfOnLeft opts r -- within given direction+ test_result <- test -- execute test on repo+ case test_result of+ ExitSuccess -> trackNextBisect opts (dnow+1, dtotal) test BisectLeft l -- continue left (to the present)+ _ -> trackNextBisect opts (dnow+1, dtotal) test BisectRight r -- continue right (to the past)+trackNextBisect _ _ _ _ (Leaf p) = do+ putStrLn ("Last recent patch that fails the test (assuming monotony in the given range):")+ putDocLn (description p)++jumpHalfOnRight :: (Invert p, ShowPatch p, Apply p) => [DarcsFlag] -> PatchTree p C(x y) -> IO ()+jumpHalfOnRight opts l = unapplyRL opts (patchTree2RL l)++jumpHalfOnLeft :: (Invert p, ShowPatch p, Apply p) => [DarcsFlag] -> PatchTree p C(x y) -> IO ()+jumpHalfOnLeft opts r = applyRL opts (patchTree2RL r)++applyRL :: (Invert p, ShowPatch p, Apply p) => [DarcsFlag] -> RL p C(x y) -> IO ()+applyRL opts patches = sequence_ (mapFL (safeApply opts) (reverseRL $ patches))++unapplyRL :: (Invert p, ShowPatch p, Apply p) => [DarcsFlag] -> RL p C(x y) -> IO ()+unapplyRL opts patches = sequence_ (mapRL ((safeApply opts) . invert) patches)++safeApply :: (Invert p, ShowPatch p, Apply p) => [DarcsFlag] -> p C(x y) -> IO ()+safeApply opts p = apply opts p `catch` (\msg -> fail ("Bad patch (during trackdown --bisect):\n" ++ show msg))+ \end{code} Trackdown is helpful for locating when something was broken. It creates@@ -113,20 +199,11 @@ finally succeeds, the name of the hunted down patch is found in the output before the last test run. -FIXME: It is-still rather primitive. Currently it just goes back over the history in-reverse order trying each version. I'd like for it to explore different-patch combinations, to try to find the minimum number of patches that you-would need to obliterate in order to make the test succeed.--FIXME: I also would like to add an interface by which you can tell it which-patches it should consider not including. Without such a feature, the-following command:-\begin{verbatim}-% darcs trackdown 'make && false'-\end{verbatim}-would result in compiling every version in the repository--which is a-rather tedious prospect.+The \verb!--bisect! variant of trackdown can be useful when the sought after+patch is likely buried deep in the repository history; however, it currently+requires an potentially expensive process of applying or unapplying half the+repository's patches at a time. You may often find the straightforward linear+trackdown to be more efficient in practice. \subsubsection{Example usage} If you want to find the last version of darcs that had a FIXME note in the
src/Darcs/Commands/TransferMode.lhs view
@@ -24,7 +24,7 @@ module Darcs.Commands.TransferMode ( transferMode ) where import Prelude hiding ( catch )-import Control.Exception ( catch )+import Control.Exception.Extensible ( catch ) import System.IO ( stdout, hFlush ) import Darcs.Utils ( withCurrentDirectory, prettyException )
src/Darcs/Commands/Unrecord.lhs view
@@ -23,34 +23,47 @@ module Darcs.Commands.Unrecord ( unrecord, unpull, obliterate, getLastPatches ) where import Control.Monad ( when ) import System.Exit ( exitWith, ExitCode( ExitSuccess ) )+import Data.Maybe( isJust ) -import Printer ( text )-import Darcs.Hopefully ( hopefully )+import Printer ( text, putDoc )+import English ( presentParticiple )+import Darcs.Hopefully ( hopefully, info, patchDesc ) import Darcs.Commands ( DarcsCommand(..), nodefaults, commandAlias, putVerbose ) import Darcs.Arguments ( DarcsFlag,+ output, outputAutoName, getOutput, workingRepoDir, nocompress, definePatches, matchSeveralOrLast, depsSel, ignoretimes, allInteractive, umaskOption, summary, dryRun,- printDryRunMessageAndExit+ printDryRunMessageAndExit, changesReverse )+import Darcs.Flags ( doReverse ) import Darcs.Match ( firstMatch, matchFirstPatchset, matchAPatchread )-import Darcs.Repository ( PatchSet, PatchInfoAnd, withGutsOf,+import Darcs.Repository ( PatchInfoAnd, withGutsOf, withRepoLock, ($-), tentativelyRemovePatches, finalizeRepositoryChanges, tentativelyAddToPending, applyToWorking,- read_repo, amInRepository+ readRepo, amInRepository , invalidateIndex, unrecordedChanges ) import Darcs.Patch ( Patchy, RepoPatch, invert, commute, effect )-import Darcs.Witnesses.Ordered ( RL(..), (:>)(..), (:\/:)(..), (+<+),+import Darcs.Patch.Set ( PatchSet(..), Tagged(..) )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif+import Darcs.Witnesses.Ordered ( RL(..), (:>)(..), (+<+), (:>>)(..), reverseFL, mapFL_FL, nullFL,- reverseRL, mapRL )-import Darcs.Patch.Depends ( get_common_and_uncommon )-import Darcs.SelectChanges ( with_selected_last_changes_reversed )+ reverseRL, mapRL, FL(..), mapFL )+import Darcs.Patch.Depends ( findCommonWithThem )+import Darcs.SelectChanges ( selectChanges+ , WhichChanges(..)+ , selectionContext, runSelection )+import Darcs.Patch.Bundle ( makeBundle, patchFilename, contextPatches ) import Progress ( debugMessage ) import Darcs.Witnesses.Sealed ( Sealed(..), FlippedSeal(..), mapFlipped )+import Darcs.RepoPath( useAbsoluteOrStd )+import Darcs.Lock( writeDocBinFile ) #include "gadts.h" unrecordDescription :: String@@ -143,7 +156,8 @@ commandPrereq = amInRepository, commandGetArgPossibilities = return [], commandArgdefaults = nodefaults,- commandAdvancedOptions = [nocompress,umaskOption],+ commandAdvancedOptions =+ [nocompress,umaskOption,changesReverse], commandBasicOptions = [matchSeveralOrLast, depsSel, allInteractive,@@ -151,30 +165,30 @@ unrecordCmd :: [DarcsFlag] -> [String] -> IO () unrecordCmd opts _ = withRepoLock opts $- \repository -> do- allpatches <- read_repo repository+ allpatches <- readRepo repository FlippedSeal patches <- return $ if firstMatch opts then getLastPatches opts allpatches else matchingHead opts allpatches- with_selected_last_changes_reversed "unrecord" opts Nothing- (reverseRL patches) $- \ (_ :> to_unrecord) -> do- when (nullFL to_unrecord) $ do putStrLn "No patches selected!"- exitWith ExitSuccess- putVerbose opts $ text + let context = selectionContext "unrecord" opts Nothing []+ selector = if doReverse opts+ then selectChanges Last+ else selectChanges LastReversed+ (_ :> to_unrecord) <- runSelection (selector (reverseRL patches)) context+ when (nullFL to_unrecord) $ do putStrLn "No patches selected!"+ exitWith ExitSuccess+ putVerbose opts $ text "About to write out (potentially) modified patches..."- definePatches to_unrecord- invalidateIndex repository- withGutsOf repository $ do tentativelyRemovePatches repository opts $- mapFL_FL hopefully to_unrecord- finalizeRepositoryChanges repository- putStrLn "Finished unrecording."+ definePatches to_unrecord+ invalidateIndex repository+ withGutsOf repository $ do tentativelyRemovePatches repository opts to_unrecord+ finalizeRepositoryChanges repository+ putStrLn "Finished unrecording." -getLastPatches :: RepoPatch p => [DarcsFlag] -> PatchSet p C(r)+getLastPatches :: RepoPatch p => [DarcsFlag] -> PatchSet p C(Origin r) -> FlippedSeal (RL (PatchInfoAnd p)) C(r) getLastPatches opts ps = case matchFirstPatchset opts ps of- Sealed p1s -> case get_common_and_uncommon (ps,p1s) of- (_,us :\/: _) -> FlippedSeal us+ Sealed p1s -> case findCommonWithThem ps p1s of _ :>> us -> FlippedSeal (reverseFL us) unpullDescription :: String unpullDescription =@@ -267,7 +281,7 @@ commandPrereq = amInRepository, commandGetArgPossibilities = return [], commandArgdefaults = nodefaults,- commandAdvancedOptions = [nocompress,ignoretimes,umaskOption],+ commandAdvancedOptions = [nocompress,ignoretimes,umaskOption, changesReverse], commandBasicOptions = [matchSeveralOrLast, depsSel, allInteractive,@@ -285,38 +299,54 @@ -> IO () genericObliterateCmd cmdname opts _ = withRepoLock opts $- \repository -> do pend <- unrecordedChanges opts repository []- allpatches <- read_repo repository+ allpatches <- readRepo repository FlippedSeal patches <- return $ if firstMatch opts then getLastPatches opts allpatches else matchingHead opts allpatches- with_selected_last_changes_reversed cmdname opts Nothing- (reverseRL patches) $- \ (_ :> ps) ->- case commute (effect ps :> pend) of+ let+ context = selectionContext cmdname opts Nothing []+ selector = if doReverse opts+ then selectChanges Last+ else selectChanges LastReversed+ (kept :> removed) <- runSelection (selector (reverseRL patches)) context+ case commute (effect removed :> pend) of Nothing -> fail $ "Can't "++ cmdname ++ " patch without reverting some unrecorded change." Just (_ :> p_after_pending) -> do- when (nullFL ps) $ do putStrLn "No patches selected!"- exitWith ExitSuccess- printDryRunMessageAndExit "obliterate" opts ps- definePatches ps+ when (nullFL removed) $ do putStrLn "No patches selected!"+ exitWith ExitSuccess+ printDryRunMessageAndExit "obliterate" opts removed+ definePatches removed+ when (isJust $ getOutput opts "") $+ savetoBundle opts (reverseFL kept) removed invalidateIndex repository withGutsOf repository $- do tentativelyRemovePatches repository opts (mapFL_FL hopefully ps)- tentativelyAddToPending repository opts $ invert $ effect ps+ do tentativelyRemovePatches repository opts removed+ tentativelyAddToPending repository opts $ invert $ effect removed finalizeRepositoryChanges repository debugMessage "Applying patches to working directory..." applyToWorking repository opts (invert p_after_pending) `catch` \e -> fail ("Couldn't undo patch in working dir.\n" ++ show e)+ return () putStrLn $ "Finished " ++ presentParticiple cmdname ++ "." -matchingHead :: Patchy p => [DarcsFlag] -> PatchSet p C(r) -> FlippedSeal (RL (PatchInfoAnd p)) C(r)-matchingHead opts (x:<:_) | or (mapRL (matchAPatchread opts) x) = FlippedSeal x-matchingHead opts (x:<:xs) = (x +<+) `mapFlipped` matchingHead opts xs-matchingHead _ NilRL = FlippedSeal NilRL+matchingHead :: RepoPatch p => [DarcsFlag] -> PatchSet p C(Origin r)+ -> FlippedSeal (RL (PatchInfoAnd p)) C(r)+matchingHead opts set@(PatchSet x _)+ | or (mapRL (matchAPatchread opts) x) = contextPatches set+matchingHead opts (PatchSet x (Tagged t _ ps :<: ts))+ = (x +<+) `mapFlipped` matchingHead opts (PatchSet (t:<:ps) ts)+matchingHead _ (PatchSet _ NilRL) = FlippedSeal NilRL -presentParticiple :: String -> String-presentParticiple v | last v == 'e' = init v ++ "ing"- | otherwise = v ++ "ing"+savetoBundle :: RepoPatch p => [DarcsFlag]+ -> RL (PatchInfoAnd p) C(x z) -> FL (PatchInfoAnd p) C(z t)+ -> IO ()+savetoBundle opts kept removed@(x :>: _) = do+ bundle <- makeBundle Nothing kept (mapFL_FL hopefully removed)+ let filename = patchFilename $ patchDesc x+ Just outname = getOutput opts filename+ useAbsoluteOrStd writeDocBinFile putDoc outname $ bundle++savetoBundle _ _ NilFL = return () \end{code}
src/Darcs/Commands/Unrevert.lhs view
@@ -34,18 +34,22 @@ import Darcs.Repository ( SealedPatchSet, Repository, withRepoLock, ($-), unrevertUrl, considerMergeToWorking, tentativelyAddToPending, finalizeRepositoryChanges,- read_repo, amInRepository,+ readRepo, amInRepository, readRecorded, applyToWorking, unrecordedChanges ) import Darcs.Patch ( RepoPatch, Prim, commute, namepatch, fromPrims )-import Darcs.Witnesses.Ordered ( FL(..), (:>)(..), (:\/:)(..), reverseRL,- (+>+) )-import Darcs.SelectChanges ( with_selected_changes_to_files' )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif+import Darcs.Patch.Set ( PatchSet(..) )+import Darcs.Witnesses.Ordered ( FL(..), (:>)(..), (+>+) )+import Darcs.SelectChanges ( selectChanges, WhichChanges(First),+ runSelection, selectionContextPrim ) import qualified Data.ByteString as B import Darcs.Lock ( writeDocBinFile, removeFileMayNotExist )-import Darcs.Patch.Depends ( get_common_and_uncommon )+import Darcs.Patch.Depends ( mergeThem ) import Darcs.Utils ( askUser, catchall )-import Darcs.Patch.Bundle ( scan_bundle, make_bundle )+import Darcs.Patch.Bundle ( scanBundle, makeBundleN ) import IsoDate ( getIsoDateTime ) import Darcs.SignalHandler ( withSignalsBlocked ) import Progress ( debugMessage )@@ -83,25 +87,23 @@ unrevertCmd :: [DarcsFlag] -> [String] -> IO () unrevertCmd opts [] = withRepoLock opts $- \repository -> do- us <- read_repo repository+ us <- readRepo repository Sealed them <- unrevertPatchBundle repository rec <- readRecorded repository unrec <- unrecordedChanges opts repository []- case get_common_and_uncommon (us, them) of- (_, h_us :\/: h_them) -> do- Sealed pw <- considerMergeToWorking repository "pull" (MarkConflicts:opts)- (reverseRL h_us) (reverseRL h_them)- with_selected_changes_to_files' "unrevert" opts Nothing [] pw $- \ (p :> skipped) -> do- tentativelyAddToPending repository opts p- withSignalsBlocked $- do finalizeRepositoryChanges repository- applyToWorking repository opts p `catch` \e ->- fail ("Error applying unrevert to working directory...\n"- ++ show e)- debugMessage "I'm about to writeUnrevert."- writeUnrevert repository skipped rec (unrec+>+p)- debugMessage "Finished unreverting."+ Sealed h_them <- return $ mergeThem us them+ Sealed pw <- considerMergeToWorking repository "pull" (MarkConflicts:opts) NilFL h_them+ let context = selectionContextPrim "unrevert" opts Nothing []+ (p :> skipped) <- runSelection (selectChanges First pw) context+ tentativelyAddToPending repository opts p+ withSignalsBlocked $+ do finalizeRepositoryChanges repository+ applyToWorking repository opts p `catch` \e ->+ fail ("Error applying unrevert to working directory...\n"+ ++ show e)+ debugMessage "I'm about to writeUnrevert."+ writeUnrevert repository skipped rec (unrec+>+p)+ debugMessage "Finished unreverting." unrevertCmd _ _ = impossible writeUnrevert :: RepoPatch p => Repository p C(r u t) -> FL Prim C(x y)@@ -114,21 +116,19 @@ _ -> exitWith $ ExitSuccess writeUnrevert repository NilFL rec pend Just (p' :> _) -> do- rep <- read_repo repository- case get_common_and_uncommon (rep,rep) of- (common,_ :\/: _) -> do- date <- getIsoDateTime- np <- namepatch date "unrevert" "anon" [] (fromRepoPrims repository p')- bundle <- make_bundle [Unified] rec common (np :>: NilFL)- writeDocBinFile (unrevertUrl repository) bundle- where fromRepoPrims :: RepoPatch p => Repository p C(r u t) -> FL Prim C(r y) -> p C(r y)- fromRepoPrims _ xs = fromPrims xs+ rep <- readRepo repository+ date <- getIsoDateTime+ np <- namepatch date "unrevert" "anon" [] (fromRepoPrims repository p')+ bundle <- makeBundleN (Just rec) rep (np :>: NilFL)+ writeDocBinFile (unrevertUrl repository) bundle+ where fromRepoPrims :: RepoPatch p => Repository p C(r u t) -> FL Prim C(r y) -> p C(r y)+ fromRepoPrims _ xs = fromPrims xs -unrevertPatchBundle :: RepoPatch p => Repository p C(r u t) -> IO (SealedPatchSet p)+unrevertPatchBundle :: RepoPatch p => Repository p C(r u t) -> IO (SealedPatchSet p C(Origin)) unrevertPatchBundle repository = do pf <- B.readFile (unrevertUrl repository) `catchall` fail "There's nothing to unrevert!"- case scan_bundle pf of+ case scanBundle pf of Right ps -> return ps Left err -> fail $ "Couldn't parse unrevert patch:\n" ++ err \end{code}
src/Darcs/Commands/WhatsNew.lhs view
@@ -22,7 +22,7 @@ #include "gadts.h" -module Darcs.Commands.WhatsNew ( whatsnew ) where+module Darcs.Commands.WhatsNew ( whatsnew, announceFiles ) where import System.Exit ( ExitCode(..), exitWith ) import Data.List ( sort, (\\) ) import Control.Monad ( when )@@ -30,24 +30,26 @@ import Darcs.Commands ( DarcsCommand(..), nodefaults ) import Darcs.Arguments ( DarcsFlag(..), workingRepoDir, lookforadds, ignoretimes, noskipBoring,- unified, summary, noCache,+ unified, summary, areFileArgs, fixSubPaths, listRegisteredFiles, ) import Darcs.Flags( isUnified ) import Darcs.RepoPath ( SubPath, sp2fn ) -import Darcs.Repository ( Repository, withRepository, ($-),- amInRepository+import Darcs.Repository ( Repository, withRepository, ($-)+ , amInRepository, extractOptions , unrecordedChanges, readRecordedAndPending, readRecorded, readUnrecorded )-import Darcs.Repository.State( restrictBoring )+import Darcs.Repository.State( restrictBoring, applyTreeFilter ) import Darcs.Repository.Prefs ( filetypeFunction ) import Darcs.Patch ( RepoPatch, Prim, plainSummary, primIsHunk, applyToTree )+import Darcs.Patch.TouchesFiles( choosePreTouching ) import Darcs.Patch.Permutations ( partitionRL ) import Darcs.Patch.Real ( RealPatch, prim2real )-import Darcs.Patch.FileName ( fn2fp )+import Darcs.RepoPath( toFilePath ) import Darcs.PrintPatch ( printPatch, contextualPrintPatch ) import Darcs.Witnesses.Ordered ( FL(..), mapFL_FL, reverseRL, reverseFL, (:>)(..), nullFL )+import Darcs.Witnesses.Sealed ( Sealed(..), unFreeLeft ) import Darcs.Diff( treeDiff ) import Storage.Hashed.Monad( virtualTreeIO, exists )@@ -98,42 +100,47 @@ commandPrereq = amInRepository, commandGetArgPossibilities = listRegisteredFiles, commandArgdefaults = nodefaults,- commandAdvancedOptions = [ignoretimes, noskipBoring, noCache],+ commandAdvancedOptions = [ignoretimes, noskipBoring], commandBasicOptions = [summary, unified, lookforadds, workingRepoDir]} -announceFiles :: (RepoPatch p) => Repository p C(r u t) -> [SubPath] -> IO ()-announceFiles repo files =- when (areFileArgs files) $ do+filteredChanges opts repo files =+ choosePreTouching (map toFilePath files) `fmap` unrecordedChanges opts repo files++announceFiles :: (RepoPatch p) => Repository p C(r u t) -> [SubPath] -> String -> IO [SubPath]+announceFiles repo files message =+ if (areFileArgs files) then do pristine <- readRecordedAndPending repo -- TODO this is slightly inefficient, since we should really somehow -- extract the unrecorded state as a side-effect of unrecordedChanges- index <- readUnrecorded repo+ index <- readUnrecorded repo files nonboring <- restrictBoring index- working <- nonboring `fmap` readPlainTree "."- let paths = map (fn2fp . sp2fn) files+ working <- applyTreeFilter nonboring `fmap` readPlainTree "."+ let paths = map toFilePath files check = virtualTreeIO (mapM exists $ map floatPath paths) (in_working, _) <- check working (in_pending, _) <- check pristine mapM_ maybe_warn $ zip3 paths in_working in_pending- putStrLn $ "What's new in "++unwords (map show files)++":\n"+ putStrLn $ message ++ " " ++ unwords (map show files)++":\n"+ return [ path | (path, True) <- zip files (zipWith (||) in_working in_pending) ]+ else return files where maybe_warn (file, False, False) = putStrLn $ "WARNING: File '"++file++"' does not exist!"- maybe_warn (file, True, False) =+ maybe_warn (file, True, False) | LookForAdds `notElem` extractOptions repo = putStrLn $ "WARNING: File '" ++ file ++ "' not in repository!" maybe_warn _ = return () whatsnewCmd :: [DarcsFlag] -> [String] -> IO ()-whatsnewCmd opts' args +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') $- \repository -> do files <- fixSubPaths opts' args- announceFiles repository files- all_changes <- unrecordedChanges opts' repository files- chold <- unrecordedChanges (opts' \\ [LookForAdds]) repository files+ announceFiles repository 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@@ -141,7 +148,7 @@ cho_adds_t <- applyToTree (reverseRL cho_adds) pristine cha_t <- applyToTree (reverseRL cha) pristine- chn <- treeDiff ftf cho_adds_t cha_t+ Sealed chn <- unFreeLeft `fmap` treeDiff ftf cho_adds_t cha_t exitOnNoChanges (chn, chold) putDocLn $ plainSummary chold@@ -161,8 +168,8 @@ | otherwise = withRepository opts $- \repository -> do files <- sort `fmap` fixSubPaths opts args- announceFiles repository files- changes <- unrecordedChanges opts repository files+ announceFiles repository files "What's new in"+ Sealed changes <- filteredChanges opts repository files when (nullFL changes) $ putStrLn "No changes!" >> (exitWith $ ExitFailure 1) printSummary repository $ mapFL_FL prim2real changes where printSummary :: RepoPatch p => Repository p C(r u t) -> FL RealPatch C(r y) -> IO ()
src/Darcs/CommandsAux.hs view
@@ -20,7 +20,7 @@ #include "gadts.h" -module Darcs.CommandsAux ( check_paths, malicious_patches, has_malicious_path,+module Darcs.CommandsAux ( checkPaths, maliciousPatches, hasMaliciousPath, ) where import Darcs.Flags ( DarcsFlag( RestrictPaths, DontRestrictPaths ) ) import Darcs.Patch ( Patchy, listTouchedFiles )@@ -49,11 +49,11 @@ applying any patches. It checks for malicious paths in patches, and prints an error message and fails if it finds one. -}-check_paths :: Patchy p => [DarcsFlag] -> FL p C(x y) -> IO ()-check_paths opts patches- = if check_is_on && or (mapFL has_malicious_path patches)+checkPaths :: Patchy p => [DarcsFlag] -> FL p C(x y) -> IO ()+checkPaths opts patches+ = if check_is_on && or (mapFL hasMaliciousPath patches) then fail $ unlines $ ["Malicious path in patch:"] ++- (map (\s -> " " ++ s) $ concat $ mapFL malicious_paths patches) +++ (map (\s -> " " ++ s) $ concat $ mapFL maliciousPaths patches) ++ ["", "If you are sure this is ok then you can run again with the --dont-restrict-paths option."] -- TODO: print patch(es) -- NOTE: should use safe Doc printer, this can be evil chars@@ -63,19 +63,19 @@ RestrictPaths `elem` opts -- | Filter out patches that contains some malicious file path-malicious_patches :: Patchy p => [Sealed2 p] -> [Sealed2 p]-malicious_patches to_check = filter (unseal2 has_malicious_path) to_check+maliciousPatches :: Patchy p => [Sealed2 p] -> [Sealed2 p]+maliciousPatches to_check = filter (unseal2 hasMaliciousPath) to_check -has_malicious_path :: Patchy p => p C(x y) -> Bool-has_malicious_path patch =- case malicious_paths patch of+hasMaliciousPath :: Patchy p => p C(x y) -> Bool+hasMaliciousPath patch =+ case maliciousPaths patch of [] -> False _ -> True -malicious_paths :: Patchy p => p C(x y) -> [String]-malicious_paths patch =+maliciousPaths :: Patchy p => p C(x y) -> [String]+maliciousPaths patch = let paths = listTouchedFiles patch in- filter is_malicious_path paths+ filter isMaliciousPath paths {-| What is a malicious path?@@ -100,13 +100,13 @@ changes to certain preference files (_darcs\/prefs\/) in sub repositories'? -}-is_malicious_path :: String -> Bool-is_malicious_path fp =- not (is_explicitly_relative fp) ||+isMaliciousPath :: String -> Bool+isMaliciousPath fp =+ not (isExplicitlyRelative fp) || splitDirectories fp `contains_any` [ "..", darcsdir ] where contains_any a b = not . null $ intersect a b -is_explicitly_relative :: String -> Bool-is_explicitly_relative ('.':'/':_) = True -- begins with "./"-is_explicitly_relative _ = False+isExplicitlyRelative :: String -> Bool+isExplicitlyRelative ('.':'/':_) = True -- begins with "./"+isExplicitlyRelative _ = False
src/Darcs/Compat.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE CPP, ForeignFunctionInterface #-} -module Darcs.Compat (stdout_is_a_pipe, mk_stdout_temp, canonFilename,- maybeRelink, atomic_create, sloppy_atomic_create) where+module Darcs.Compat (stdoutIsAPipe, mkStdoutTemp, canonFilename,+ maybeRelink, atomicCreate, sloppyAtomicCreate) where import Prelude hiding ( catch ) @@ -23,12 +23,12 @@ BufferMode(NoBuffering) ) import System.IO.Error ( mkIOError, alreadyExistsErrorType ) import System.Posix.Files ( stdFileMode )-import System.Posix.IO ( openFd, closeFd, stdOutput, stdError, +import System.Posix.IO ( openFd, closeFd, stdOutput, stdError, dupTo, defaultFileFlags, exclusive, OpenMode(WriteOnly) ) import System.Posix.Types ( Fd(..) ) -import Darcs.SignalHandler ( stdout_is_a_pipe )+import Darcs.SignalHandler ( stdoutIsAPipe ) canonFilename :: FilePath -> IO FilePath canonFilename f@(_:':':_) = return f -- absolute windows paths@@ -44,16 +44,16 @@ simplefilename = reverse $ takeWhile (/='/') $ reverse f #ifdef WIN32-mkstemp_core :: FilePath -> IO (Fd, String)-mkstemp_core fp+mkstempCore :: FilePath -> IO (Fd, String)+mkstempCore fp = do r <- randomIO let fp' = fp ++ (showHexLen 6 (r .&. 0xFFFFFF :: Int)) fd <- openFd fp' WriteOnly (Just stdFileMode) flags return (fd, fp') where flags = defaultFileFlags { exclusive = True } #else-mkstemp_core :: String -> IO (Fd, String)-mkstemp_core str = withCString (str++"XXXXXX") $+mkstempCore :: String -> IO (Fd, String)+mkstempCore str = withCString (str++"XXXXXX") $ \cstr -> do fd <- c_mkstemp cstr if fd < 0 then throwErrno $ "Failed to create temporary file "++str@@ -65,8 +65,8 @@ c_mkstemp :: CString -> IO CInt #endif -mk_stdout_temp :: String -> IO String-mk_stdout_temp str = do (fd, fn) <- mkstemp_core str+mkStdoutTemp :: String -> IO String+mkStdoutTemp str = do (fd, fn) <- mkstempCore str hFlush stdout hFlush stderr dupTo fd stdOutput@@ -101,23 +101,23 @@ return False _ -> fail ("Unexpected situation when relinking " ++ dst)) -sloppy_atomic_create :: FilePath -> IO ()-sloppy_atomic_create fp+sloppyAtomicCreate :: FilePath -> IO ()+sloppyAtomicCreate fp = do fd <- openFd fp WriteOnly (Just stdFileMode) flags closeFd fd where flags = defaultFileFlags { exclusive = True } -atomic_create :: FilePath -> IO ()-atomic_create fp = withCString fp $ \cstr -> do+atomicCreate :: FilePath -> IO ()+atomicCreate fp = withCString fp $ \cstr -> do rc <- c_atomic_create cstr unless (rc >= 0) $ do errno <- getErrno pwd <- getCurrentDirectory if errno == eEXIST then ioError $ mkIOError alreadyExistsErrorType- ("atomic_create in "++pwd)+ ("atomicCreate in "++pwd) Nothing (Just fp)- else throwErrno $ "atomic_create "++fp++" in "++pwd+ else throwErrno $ "atomicCreate "++fp++" in "++pwd foreign import ccall unsafe "atomic_create.h atomic_create" c_atomic_create :: CString -> IO CInt
src/Darcs/Diff.hs view
@@ -22,6 +22,7 @@ module Darcs.Diff( treeDiff ) where import Darcs.Witnesses.Ordered ( FL(..), (+>+) )+import Darcs.Witnesses.Sealed ( Gap(..) ) import Darcs.Repository.Prefs ( FileType(..) ) import Darcs.Patch ( Prim, hunk, canonize, binary , addfile, rmfile, adddir, rmdir, invert)@@ -33,32 +34,53 @@ import qualified Data.ByteString.Lazy.Char8 as BLC import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BL-import ByteStringUtils( is_funky )+import Data.List ( sortBy )+import ByteStringUtils( isFunky ) #include "gadts.h"+#include "impossible.h" -treeDiff :: (FilePath -> FileType) -> Tree IO -> Tree IO -> IO (FL Prim C(x y))-#ifdef GADT_WITNESSES-treeDiff = undefined -- Sigh.-#else+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 p Nothing Nothing = impossible -- zipTrees should never return this++treeDiff :: forall m w . (Functor m, Monad m, Gap w) => (FilePath -> FileType) -> Tree m -> Tree m -> m (w (FL Prim)) treeDiff ft t1 t2 = do (from, to) <- diffTrees t1 t2- diffs <- sequence $ zipTrees diff from to- return $ foldr (+>+) NilFL diffs- where diff :: AnchoredPath -> Maybe (TreeItem IO) -> Maybe (TreeItem IO)- -> IO (FL Prim)- diff _ (Just (SubTree _)) (Just (SubTree _)) = return NilFL- diff p (Just (SubTree _)) Nothing =- return $ rmdir (anchorPath "" p) :>: NilFL- diff p Nothing (Just (SubTree _)) =- return $ adddir (anchorPath "" p) :>: NilFL- diff p Nothing b'@(Just (File _)) =- do diff' <- diff p (Just (File emptyBlob)) b'- return $ addfile (anchorPath "" p) :>: diff'- diff p a'@(Just (File _)) Nothing =- do diff' <- diff p a' (Just (File emptyBlob))- return $ diff' +>+ (rmfile (anchorPath "" p) :>: NilFL)- diff p (Just (File a')) (Just (File b')) =+ 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, Removed _) _ = LT+ organise _ (p1, Removed _) = GT++ organise (p1, Changed _ _) _ = LT+ organise _ (p1, 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@@ -66,20 +88,19 @@ TextFile | no_bin a && no_bin b -> return $ text_diff path a b _ -> return $ if a /= b- then binary path (strict a) (strict b) :>: NilFL- else NilFL- diff p _ _ = fail $ "Missing case at path " ++ show p+ 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 = NilFL- | BL.null a = diff_from_empty p b- | BL.null b = diff_to_empty p a- | otherwise = line_diff p (linesB a) (linesB 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 . is_funky . strict . BL.take 4096+ no_bin = not . isFunky . strict . BL.take 4096 linesB = map strict . BLC.split '\n' strict = BS.concat . BL.toChunks-#endif
src/Darcs/Email.hs view
@@ -1,16 +1,15 @@ {-# OPTIONS_GHC -cpp #-} {-# LANGUAGE CPP #-}-module Darcs.Email ( make_email, read_email, formatHeader ) where+module Darcs.Email ( makeEmail, readEmail, formatHeader ) where import Data.Char ( digitToInt, isHexDigit, ord, intToDigit, isPrint, toUpper ) import Data.List ( isInfixOf )-import qualified Codec.Binary.UTF8.String as UTF8 ( encode ) import Printer ( Doc, ($$), (<+>), (<>), text, empty, packedString, renderPS) -import ByteStringUtils (dropSpace, linesPS, betweenLinesPS )+import ByteStringUtils ( packStringToUTF8, dropSpace, linesPS, betweenLinesPS ) import qualified Data.ByteString as B (ByteString, length, null, tail ,drop, head, concat, singleton- ,pack, append, empty+ ,pack, append, empty, unpack ) import qualified Data.ByteString.Char8 as BC (index, head, pack) import Data.ByteString.Internal as B (c2w, createAndTrim)@@ -19,12 +18,12 @@ import Foreign.Storable ( poke ) import Data.Word ( Word8 ) --- line_max is maximum number of characters in an e-mail line excluding the CRLF--- at the end. qline_max is the number of characters in a q-encoded or+-- lineMax is maximum number of characters in an e-mail line excluding the CRLF+-- at the end. qlineMax is the number of characters in a q-encoded or -- quoted-printable-encoded line.-line_max, qline_max :: Int-line_max = 78-qline_max = 75+lineMax, qlineMax :: Int+lineMax = 78+qlineMax = 75 -- | Formats an e-mail header by encoding any non-ascii characters using UTF-8 -- and Q-encoding, and folding lines at appropriate points. It doesn't do@@ -37,39 +36,39 @@ formatHeader headerName headerValue = B.append nameColon encodedValue where nameColon = B.pack (map B.c2w (headerName ++ ":")) -- space for folding- encodedValue = fold_and_encode (' ':headerValue)+ encodedValue = foldAndEncode (' ':headerValue) (B.length nameColon) False False -- run through a string and encode non-ascii words and fold where appropriate. -- the integer argument is the current position in the current line. -- the string in the first argument must begin with whitespace, or be empty.-fold_and_encode :: String -> Int -> Bool -> Bool -> B.ByteString-fold_and_encode [] _ _ _ = B.empty-fold_and_encode s p lastWordEncoded inMidWord = +foldAndEncode :: String -> Int -> Bool -> Bool -> B.ByteString+foldAndEncode [] _ _ _ = B.empty+foldAndEncode s p lastWordEncoded inMidWord = let newline = B.singleton 10 space = B.singleton 32 s2bs = B.pack . map B.c2w -- the twelve there is the max number of ASCII chars to encode a single -- character: 4 * 3, 4 UTF-8 bytes times 3 ASCII chars per byte- safeEncChunkLength = (qline_max - B.length encoded_word_start- - B.length encoded_word_end) `div` 12+ safeEncChunkLength = (qlineMax - B.length encodedWordStart+ - B.length encodedWordEnd) `div` 12 (curSpace, afterCurSpace) = span (== ' ') s (curWord, afterCurWord) = break (== ' ') afterCurSpace qEncWord | lastWordEncoded = qEncode (curSpace ++ curWord) | otherwise = qEncode curWord mustEncode = inMidWord || any (\c -> not (isPrint c) || (ord c) > 127) curWord- || length curWord > line_max - 1+ || length curWord > lineMax - 1 || isInfixOf "=?" curWord mustFold | mustEncode && lastWordEncoded- = p + 1 + B.length qEncWord > line_max+ = p + 1 + B.length qEncWord > lineMax | mustEncode- = p + length curSpace + B.length qEncWord > line_max+ = p + length curSpace + B.length qEncWord > lineMax | otherwise- = p + length curSpace + length curWord > line_max- mustSplit = (B.length qEncWord > qline_max && mustEncode)- || length curWord > line_max - 1+ = p + length curSpace + length curWord > lineMax+ mustSplit = (B.length qEncWord > qlineMax && mustEncode)+ || length curWord > lineMax - 1 spaceToInsert | mustEncode && lastWordEncoded = space | otherwise = s2bs curSpace wordToInsert@@ -80,36 +79,39 @@ | otherwise = B.concat [spaceToInsert, wordToInsert] (rest, nextP) | mustSplit- = (drop safeEncChunkLength curWord ++ afterCurWord, qline_max + 1)- | mustEncode && mustFold + = (drop safeEncChunkLength curWord ++ afterCurWord, qlineMax + 1)+ | mustEncode && mustFold = (afterCurWord, B.length spaceToInsert + B.length wordToInsert) | otherwise = (afterCurWord, p + B.length doneChunk)- in B.append doneChunk (fold_and_encode rest nextP mustEncode mustSplit)+ in B.append doneChunk (foldAndEncode rest nextP mustEncode mustSplit) -- | Turns a piece of string into a q-encoded block -- Applies q-encoding, for use in e-mail header values, as defined in RFC 2047. -- It just takes a string and builds an encoded-word from it, it does not check -- length or necessity. qEncode :: String -> B.ByteString-qEncode s = B.concat [encoded_word_start,+qEncode s = B.concat [encodedWordStart, encodedString,- encoded_word_end]- where encodedString = B.concat (map q_encode_char s)+ encodedWordEnd]+ where encodedString = B.concat (map qEncodeChar s) -encoded_word_start, encoded_word_end :: B.ByteString-encoded_word_start = B.pack (map B.c2w "=?UTF-8?Q?")-encoded_word_end = B.pack (map B.c2w "?=")+encodedWordStart, encodedWordEnd :: B.ByteString+encodedWordStart = B.pack (map B.c2w "=?UTF-8?Q?")+encodedWordEnd = B.pack (map B.c2w "?=") -- turns a character into its q-encoded bytestring value. For most printable -- ASCII characters, that's just the singleton bytestring with that char.-q_encode_char :: Char -> B.ByteString-q_encode_char c+qEncodeChar :: Char -> B.ByteString+qEncodeChar c | c == ' ' = c2bs '_' | isPrint c && not (c `elem` ['?', '=', '_']) && ord c < 128 = c2bs c- | otherwise = B.concat (map qbyte (UTF8.encode [c]))+ | otherwise = B.concat+ (map qbyte+ (B.unpack+ (packStringToUTF8 [c]))) where c2bs = B.singleton . B.c2w -- qbyte turns a byte into its q-encoded "=hh" representation qbyte b = B.pack (map B.c2w ['='@@ -123,24 +125,24 @@ qpencode :: B.ByteString -> B.ByteString qpencode s = unsafePerformIO -- Really only (3 + 2/75) * length or something in the worst case- $ B.createAndTrim (4 * B.length s) (\buf -> encode s qline_max buf 0)+ $ B.createAndTrim (4 * B.length s) (\buf -> encode s qlineMax buf 0) encode :: B.ByteString -> Int -> Ptr Word8 -> Int -> IO Int encode ps _ _ bufi | B.null ps = return bufi encode ps n buf bufi = case B.head ps of c | c == newline -> do poke (buf `plusPtr` bufi) newline- encode ps' qline_max buf (bufi+1)+ encode ps' qlineMax buf (bufi+1) | n == 0 && B.length ps > 1 -> do poke (buf `plusPtr` bufi) equals poke (buf `plusPtr` (bufi+1)) newline- encode ps qline_max buf (bufi + 2)+ encode ps qlineMax buf (bufi + 2) | (c == tab || c == space) -> if B.null ps' || B.head ps' == newline then do poke (buf `plusPtr` bufi) c poke (buf `plusPtr` (bufi+1)) equals poke (buf `plusPtr` (bufi+2)) newline- encode ps' qline_max buf (bufi + 3)+ encode ps' qlineMax buf (bufi + 3) else do poke (buf `plusPtr` bufi) c encode ps' (n - 1) buf (bufi + 1) | (c >= bang && c /= equals && c <= tilde) ->@@ -193,8 +195,8 @@ toWord8 :: Int -> Word8 toWord8 = fromIntegral -make_email :: String -> [(String, String)] -> (Maybe Doc) -> Doc -> (Maybe String) -> Doc-make_email repodir headers mcontents bundle mfilename =+makeEmail :: String -> [(String, String)] -> (Maybe Doc) -> Doc -> (Maybe String) -> Doc+makeEmail repodir headers mcontents bundle mfilename = text "DarcsURL:" <+> text repodir $$ foldl (\m (h,v) -> m $$ (text (h ++ ":") <+> text v)) empty headers $$ text "MIME-Version: 1.0"@@ -224,8 +226,8 @@ $$ text "" $$ text "" -read_email :: B.ByteString -> B.ByteString-read_email s =+readEmail :: B.ByteString -> B.ByteString+readEmail s = case betweenLinesPS (BC.pack "Content-Description: A darcs patch for your repository!") (BC.pack "--=_--") s of
src/Darcs/External.hs view
@@ -4,7 +4,7 @@ backupByRenaming, backupByCopying, copyFileOrUrl, speculateFileOrUrl, copyFilesOrUrls, copyLocal, cloneFile, cloneTree, cloneTreeExcept, clonePartialsTree, clonePaths,- fetchFilePS, gzFetchFilePS,+ fetchFilePS, fetchFileLazyPS, gzFetchFilePS, sendEmail, generateEmail, sendEmailDoc, resendEmail, signString, verifyPS, execDocPipe, execPipeIgnoreError,@@ -14,7 +14,7 @@ maybeURLCmd, Cachable(Cachable, Uncachable, MaxAge), viewDoc, viewDocWith,- sendmail_path, diff_program, darcs_program+ sendmailPath, diffProgram, darcsProgram ) where import qualified Ratified@@ -34,7 +34,7 @@ findExecutable ) import System.Process ( runProcess, runInteractiveProcess, waitForProcess ) import Control.Concurrent ( forkIO, newEmptyMVar, putMVar, takeMVar )-import Control.Exception ( bracket, try, finally )+import Control.Exception.Extensible ( bracket, try, finally, SomeException ) import Data.Char ( toUpper ) #if defined (HAVE_MAPI) import Foreign.C ( CString, withCString )@@ -53,7 +53,7 @@ import Darcs.Flags ( DarcsFlag( SignAs, Sign, SignSSL, NoLinks, Verify, VerifySSL, RemoteDarcs ) ) import Darcs.RepoPath ( AbsolutePath, toFilePath )-import Darcs.Utils ( withCurrentDirectory, breakCommand, get_viewer, ortryrunning, )+import Darcs.Utils ( withCurrentDirectory, breakCommand, getViewer, ortryrunning, ) import Progress ( withoutProgress, progressList, debugMessage ) import ByteStringUtils (gzReadFilePS, linesPS, unlinesPS)@@ -61,22 +61,23 @@ ,hGetContents, writeFile, hPut, length ,take, concat, drop, isPrefixOf, singleton, append) import qualified Data.ByteString.Char8 as BC (unpack, pack)+import qualified Data.ByteString.Lazy as BL -import Darcs.Lock ( withTemp, withOpenTemp, tempdir_loc, removeFileMayNotExist )+import Darcs.Lock ( withTemp, withOpenTemp, tempdirLoc, removeFileMayNotExist ) import CommandLine ( parseCmd, addUrlencoded ) import URL ( copyUrl, copyUrlFirst, waitUrl ) import Ssh ( getSSH, copySSH, copySSHs, SSHCmd(..) ) import URL ( Cachable(..) ) import Exec ( exec, Redirect(..), withoutNonBlock )-import Darcs.URL ( is_file, is_url, is_ssh )+import Darcs.URL ( isFile, isUrl, isSsh ) import Darcs.Utils ( catchall ) import Printer ( Doc, Printers, putDocLnWith, hPutDoc, hPutDocLn, hPutDocWith, ($$), renderPS, simplePrinters, text, empty, packedString, vcat, renderString ) import Darcs.Email ( formatHeader ) -sendmail_path :: IO String-sendmail_path = do+sendmailPath :: IO String+sendmailPath = do l <- filterM doesFileExist $ liftM2 (</>) [ "/usr/sbin", "/sbin", "/usr/lib" ] [ "sendmail" ]@@ -84,15 +85,15 @@ when (isNothing ex && null l) $ fail "Cannot find the \"sendmail\" program." return $ head $ maybeToList ex ++ l -diff_program :: IO String-diff_program = do+diffProgram :: IO String+diffProgram = do l <- filterM (fmap isJust . findExecutable) [ "gdiff", "gnudiff", "diff" ] when (null l) $ fail "Cannot find the \"diff\" program." return $ head l -- |Get the name of the darcs executable (as supplied by @getProgName@)-darcs_program :: IO String-darcs_program = getProgName+darcsProgram :: IO String+darcsProgram = getProgName -- Another option: getEnv "DARCS" `catch` \_ -> getProgName backupByRenaming :: FilePath -> IO ()@@ -129,13 +130,23 @@ -- (either a file or an URL). If it has to download an url, then it -- will use a cache as required by its second argument. fetchFilePS :: String -> Cachable -> IO B.ByteString-fetchFilePS fou _ | is_file fou = B.readFile fou+fetchFilePS fou _ | isFile fou = B.readFile fou fetchFilePS fou cache = withTemp $ \t -> do let opts = [] -- FIXME: no network flags copyFileOrUrl opts fou t cache B.readFile t +-- | @fetchFileLazyPS fileOrUrl cache@ lazily reads the content of+-- its argument (either a file or an URL). Warning: this function may+-- constitute a fd leak; make sure to force consumption of file contents+-- to avoid that.+fetchFileLazyPS :: String -> Cachable -> IO BL.ByteString+fetchFileLazyPS fou _ | isFile fou = BL.readFile fou+fetchFileLazyPS fou cache = withTemp $ \t -> do let opts = [] -- FIXME: no network flags+ copyFileOrUrl opts fou t cache+ BL.readFile t+ gzFetchFilePS :: String -> Cachable -> IO B.ByteString-gzFetchFilePS fou _ | is_file fou = gzReadFilePS fou+gzFetchFilePS fou _ | isFile fou = gzReadFilePS fou gzFetchFilePS fou cache = withTemp $ \t-> do let opts = [] -- FIXME: no network flags copyFileOrUrl opts fou t cache gzReadFilePS t@@ -144,15 +155,18 @@ remoteDarcsCmd flags = head $ [ c | (RemoteDarcs c) <- flags ] ++ ["darcs"] copyFileOrUrl :: [DarcsFlag] -> FilePath -> FilePath -> Cachable -> IO ()-copyFileOrUrl opts fou out _ | is_file fou = copyLocal opts fou out-copyFileOrUrl _ fou out cache | is_url fou = copyRemote fou out cache-copyFileOrUrl opts fou out _ | is_ssh fou = copySSH (remoteDarcsCmd opts) fou out+copyFileOrUrl opts fou out _ | isFile fou = copyLocal opts fou out+copyFileOrUrl _ fou out cache | isUrl fou = copyRemote fou out cache+copyFileOrUrl opts fou out _ | isSsh fou = copySSH (remoteDarcsCmd opts) fou out copyFileOrUrl _ fou _ _ = fail $ "unknown transport protocol: " ++ fou speculateFileOrUrl :: String -> FilePath -> IO ()-speculateFileOrUrl fou out | is_url fou = speculateRemote fou out+speculateFileOrUrl fou out | isUrl fou = speculateRemote fou out | otherwise = return () +speculateFilesOrUrls :: FilePath -> [String] -> FilePath -> IO ()+speculateFilesOrUrls _ _ _ = return () -- FIXME+ copyLocal :: [DarcsFlag] -> String -> FilePath -> IO () copyLocal opts fou out | NoLinks `elem` opts = cloneFile fou out | otherwise = createLink fou out `catchall` cloneFile fou out@@ -249,9 +263,9 @@ copyRemoteNormal u v cache = copyUrlFirst u v cache >> waitUrl u copyFilesOrUrls :: [DarcsFlag]->FilePath->[String]->FilePath->Cachable->IO ()-copyFilesOrUrls opts dou ns out _ | is_file dou = copyLocals opts dou ns out-copyFilesOrUrls _ dou ns out c | is_url dou = copyRemotes dou ns out c-copyFilesOrUrls opts dou ns out _ | is_ssh dou = copySSHs (remoteDarcsCmd opts) dou ns out+copyFilesOrUrls opts dou ns out _ | isFile dou = copyLocals opts dou ns out+copyFilesOrUrls _ dou ns out c | isUrl dou = copyRemotes dou ns out c+copyFilesOrUrls opts dou ns out _ | isSsh dou = copySSHs (remoteDarcsCmd opts) dou ns out copyFilesOrUrls _ dou _ _ _ = fail $ "unknown transport protocol: "++dou @@ -364,8 +378,8 @@ = B.hPut h (B.append (formatHeader field value) newline) newline = B.singleton 10 -have_sendmail :: IO Bool-have_sendmail = (sendmail_path >> return True) `catch` (\_ -> return False)+haveSendmail :: IO Bool+haveSendmail = (sendmailPath >> return True) `catch` (\_ -> return False) -- | Send an email, optionally containing a patch bundle -- (more precisely, its description and the bundle itself)@@ -382,7 +396,7 @@ sendEmailDoc f "" s cc scmd mbundle body = sendEmailDoc f cc s "" scmd mbundle body sendEmailDoc f t s cc scmd mbundle body = do- use_sendmail <- have_sendmail+ use_sendmail <- haveSendmail if use_sendmail || scmd /= "" then do withOpenTemp $ \(h,fn) -> do generateEmail h f t s cc body@@ -428,7 +442,7 @@ resendEmail :: String -> String -> B.ByteString -> IO () resendEmail "" _ _ = return () resendEmail t scmd body = do- use_sendmail <- have_sendmail+ use_sendmail <- haveSendmail if use_sendmail || scmd /= "" then do withOpenTemp $ \(h,fn) -> do@@ -468,7 +482,7 @@ execSendmail :: [(Char,String)] -> String -> String -> IO ExitCode execSendmail ftable scmd fn = if scmd == "" then do- cmd <- sendmail_path+ cmd <- sendmailPath exec cmd ["-i", "-t"] (File fn, Null, AsIs) else case parseCmd (addUrlencoded ftable) scmd of Right (arg0:opts, wantstdin) ->@@ -633,7 +647,7 @@ viewDocWith pr msg = do isTerminal <- hIsTerminalDevice stdout if isTerminal && lengthGreaterThan (20 :: Int) (lines $ renderString msg)- then do viewerPlusArgs <- get_viewer+ then do viewerPlusArgs <- getViewer let (viewer:args) = words viewerPlusArgs pipeDocToPager viewer args pr msg `ortryrunning` pipeDocToPager "less" [] pr msg@@ -655,7 +669,7 @@ return ExitSuccess pipeDocToPager c args pr inp = withoutNonBlock $ withoutProgress $ do- tmp <- tempdir_loc+ tmp <- tempdirLoc bracket (openBinaryTempFile tmp "darcs-pager") cleanup $ \(fn,fh) -> do hPutDocWith pr fh inp hClose fh@@ -666,5 +680,5 @@ putStrLn $ "Command not found:\n "++ show (c:args) return x where- cleanup (f,h) = do try $ hClose h+ cleanup (f,h) = do try (hClose h) :: IO (Either SomeException ()) removeFileMayNotExist f
src/Darcs/FilePathMonad.hs view
@@ -25,8 +25,8 @@ import Data.Maybe ( catMaybes ) import Darcs.IO ( ReadableDirectory(..), WriteableDirectory(..) )-import Darcs.Patch.FileName ( FileName, fp2fn, fn2fp, superName, break_on_dir,- norm_path, movedirfilename )+import Darcs.Patch.FileName ( FileName, fp2fn, fn2fp, superName, breakOnDir,+ normPath, movedirfilename ) #include "impossible.h" data FilePathMonad a = FPM ([FileName] -> ([FileName], a))@@ -50,13 +50,13 @@ instance ReadableDirectory FilePathMonad where -- We can't check it actually is a directory here mDoesDirectoryExist d =- FPM $ \fs -> (fs, norm_path d `elem` map norm_path fs)+ FPM $ \fs -> (fs, normPath d `elem` map normPath fs) -- We can't check it actually is a file here mDoesFileExist f =- FPM $ \fs -> (fs, norm_path f `elem` map norm_path fs)+ FPM $ \fs -> (fs, normPath f `elem` map normPath fs) mInCurrentDirectory d (FPM j) = FPM $ \fs -> (fs, snd $ j $ catMaybes $ map indir fs)- where indir f = do (d',f') <- break_on_dir f+ where indir f = do (d',f') <- breakOnDir f if d == d' then Just f' else Nothing mGetDirectoryContents =@@ -69,7 +69,7 @@ let splitfs = map splitf fs others = catMaybes $ map snd splitfs (myfs, a) = j $ catMaybes $ map fst splitfs- splitf f = case break_on_dir f of+ splitf f = case breakOnDir f of Just (d', f') | d' == d -> (Just f', Nothing) _ -> (Nothing, Just f) in (others ++ myfs, a)
src/Darcs/Flags.hs view
@@ -15,12 +15,13 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -module Darcs.Flags ( DarcsFlag( .. ), Compression( .. ), compression, - want_external_merge, isInteractive,+module Darcs.Flags ( DarcsFlag( .. ), Compression( .. ), compression,+ wantExternalMerge, isInteractive, maxCount, willIgnoreTimes, willRemoveLogFile, isUnified, willStoreInMemory, doHappyForwarding, includeBoring, doAllowCaseOnly, doAllowWindowsReserved, doReverse,- showChangesOnlyToFiles+ showChangesOnlyToFiles,+ defaultFlag, ) where import Data.Maybe( fromMaybe ) import Darcs.Patch.MatchData ( PatchMatch )@@ -59,6 +60,7 @@ | EditDescription | NoEditDescription | Toks String | EditLongComment | NoEditLongComment | PromptLongComment+ | KeepDate | NoKeepDate | AllowConflicts | MarkConflicts | NoAllowConflicts | SkipConflicts | Boring | SkipBoring@@ -83,12 +85,12 @@ | NonApply | NonVerify | NonForce | DryRun | SetDefault | NoSetDefault | FancyMoveAdd | NoFancyMoveAdd- | Disable | SetScriptsExecutable | DontSetScriptsExecutable+ | Disable | SetScriptsExecutable | DontSetScriptsExecutable | Bisect | UseHashedInventory | UseOldFashionedInventory | UseFormat2 | PristinePlain | PristineNone | NoUpdateWorking | Sibling AbsolutePath | Relink | RelinkPristine | NoLinks- | OptimizePristine+ | OptimizePristine | OptimizeHTTP | UpgradeFormat | Files | NoFiles | Directories | NoDirectories | Pending | NoPending@@ -96,7 +98,7 @@ | PrehookCmd String | NoPrehook | AskPrehook | RunPrehook | UMask String | StoreInMemory | ApplyOnDisk- | HTTPPipelining | NoHTTPPipelining+ | NoHTTPPipelining | NoCache | AllowUnrelatedRepos | Check | Repair | JustThisRepo@@ -108,10 +110,10 @@ compression f | NoCompress `elem` f = NoCompression | otherwise = GzipCompression -want_external_merge :: [DarcsFlag] -> Maybe String-want_external_merge [] = Nothing-want_external_merge (ExternalMerge c:_) = Just c-want_external_merge (_:fs) = want_external_merge fs+wantExternalMerge :: [DarcsFlag] -> Maybe String+wantExternalMerge [] = Nothing+wantExternalMerge (ExternalMerge c:_) = Just c+wantExternalMerge (_:fs) = wantExternalMerge fs isInteractive :: [DarcsFlag] -> Bool isInteractive = isInteractive_ True@@ -166,3 +168,11 @@ showChangesOnlyToFiles :: [DarcsFlag] -> Bool showChangesOnlyToFiles = getBoolFlag OnlyChangesToFiles ChangesToAllFiles++-- | Set flags to a default value, but only one has not already been provided+defaultFlag :: [DarcsFlag] -- ^ distractors+ -> DarcsFlag -- ^ default+ -> [DarcsFlag] -- ^ flags+ -> [DarcsFlag] -- ^ updated flags+defaultFlag alts def flags =+ if any (`elem` flags) alts then flags else def : flags
src/Darcs/Global.hs view
@@ -20,7 +20,7 @@ -- 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, with_atexit,+module Darcs.Global ( atexit, withAtexit, sshControlMasterDisabled, setSshControlMasterDisabled, verboseMode, setVerboseMode, timingsMode, setTimingsMode,@@ -32,7 +32,7 @@ import Control.Monad ( when ) import Control.Concurrent.MVar-import Control.Exception (bracket_, catch, block, unblock)+import Control.Exception.Extensible (bracket_, catch, block, unblock, SomeException) import Data.IORef ( IORef, newIORef, readIORef, writeIORef ) import Data.IORef ( modifyIORef ) import System.IO.Unsafe (unsafePerformIO)@@ -40,15 +40,15 @@ import System.Time ( calendarTimeToString, toCalendarTime, getClockTime ) import Prelude hiding (catch) -{-# NOINLINE atexit_actions #-}-atexit_actions :: MVar (Maybe [IO ()])-atexit_actions = unsafePerformIO (newMVar (Just []))+{-# NOINLINE atexitActions #-}+atexitActions :: MVar (Maybe [IO ()])+atexitActions = unsafePerformIO (newMVar (Just [])) -- | 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_ atexit_actions $ \ml -> do+ modifyMVar_ atexitActions $ \ml -> do case ml of Just l -> do return (Just (action : l))@@ -56,19 +56,19 @@ hPutStrLn stderr "It's too late to use atexit" return Nothing -with_atexit :: IO a -> IO a-with_atexit prog = do+withAtexit :: IO a -> IO a+withAtexit prog = do bracket_ (return ()) exit prog where exit = block $ do- Just actions <- swapMVar atexit_actions Nothing+ Just actions <- swapMVar atexitActions Nothing -- from now on atexit will not register new actions mapM_ runAction actions runAction action = do- catch (unblock action) $ \exn -> do+ catch (unblock action) $ \(exn :: SomeException) -> do hPutStrLn stderr $ "Exception thrown by an atexit registered action:" hPutStrLn stderr $ show exn
src/Darcs/Hopefully.hs view
@@ -21,21 +21,22 @@ #include "gadts.h" module Darcs.Hopefully ( Hopefully, PatchInfoAnd,+ WPatchInfo, unWPatchInfo, compareWPatchInfo, piap, n2pia, patchInfoAndPatch,- conscientiously, hopefully, info,+ conscientiously, hopefully, info, winfo, hopefullyM, createHashed, extractHash,- actually, unavailable ) where+ actually, unavailable, patchDesc ) where import System.IO.Unsafe ( unsafeInterleaveIO ) import Darcs.SignalHandler ( catchNonSignal ) import Printer ( Doc, renderString, errorDoc, text, ($$) )-import Darcs.Patch.Info ( PatchInfo, human_friendly, idpatchinfo )+import Darcs.Patch.Info ( PatchInfo, humanFriendly, idpatchinfo, justName ) import Darcs.Patch ( RepoPatch, Named, patch2patchinfo ) import Darcs.Patch.Prim ( Effect(..), Conflict(..) ) import Darcs.Patch.Patchy ( Patchy, ReadPatch(..), Apply(..), Invert(..), ShowPatch(..), Commute(..) )-import Darcs.Witnesses.Ordered ( MyEq, unsafeCompare, (:>)(..), (:\/:)(..), (:/\:)(..) )+import Darcs.Witnesses.Ordered ( MyEq, EqCheck(..), unsafeCoerceP, unsafeCompare, (:>)(..), (:\/:)(..), (:/\:)(..) ) import Darcs.Witnesses.Sealed ( Sealed(Sealed), seal, mapSeal ) import Darcs.Utils ( prettyException ) @@ -58,6 +59,19 @@ -- know its info. data PatchInfoAnd p C(a b) = PIAP !PatchInfo (Hopefully (Named p) C(a b)) +-- | @'WPatchInfo' C(a b)@ represents the info of a patch, marked with+-- the patch's witnesses.+newtype WPatchInfo C(a b) = WPatchInfo { unWPatchInfo :: PatchInfo }++-- This is actually unsafe if we ever commute patches and then compare them+-- using this function. TODO: consider adding an extra existential to WPatchInfo+-- (as with TaggedPatch in Darcs.Patch.Choices)+compareWPatchInfo :: WPatchInfo C(a b) -> WPatchInfo C(c d) -> EqCheck C((a, b) (c, d))+compareWPatchInfo (WPatchInfo x) (WPatchInfo y) = if x == y then unsafeCoerceP IsEq else NotEq++instance MyEq WPatchInfo where+ WPatchInfo x `unsafeCompare` WPatchInfo y = x == y+ fmapH :: (a C(x y) -> b C(w z)) -> Hopefully a C(x y) -> Hopefully b C(w z) fmapH f (Hopefully sh) = Hopefully (ff sh) where ff (Actually a) = Actually (f a)@@ -69,6 +83,12 @@ info :: PatchInfoAnd p C(a b) -> PatchInfo info (PIAP i _) = i +patchDesc :: forall p C(x y) . PatchInfoAnd p C(x y) -> String+patchDesc p = justName $ info p++winfo :: PatchInfoAnd p C(a b) -> WPatchInfo C(a b)+winfo (PIAP i _) = WPatchInfo i+ -- | @'piap' i p@ creates a PatchInfoAnd containing p with info i. piap :: PatchInfo -> Named p C(a b) -> PatchInfoAnd p C(a b) piap i p = PIAP i (Hopefully $ Actually p)@@ -95,14 +115,14 @@ conscientiously er (PIAP pinf hp) = case hopefully2either hp of Right p -> p- Left e -> errorDoc $ er (human_friendly pinf $$ text e)+ Left e -> errorDoc $ er (humanFriendly pinf $$ text e) -- | @hopefullyM@ is a version of @hopefully@ which calls @fail@ in a -- monad instead of erroring. hopefullyM :: Monad m => PatchInfoAnd p C(a b) -> m (Named p C(a b)) hopefullyM (PIAP pinf hp) = case hopefully2either hp of Right p -> return p- Left e -> fail $ renderString (human_friendly pinf $$ text e)+ Left e -> fail $ renderString (humanFriendly pinf $$ text e) -- Any recommendations for a nice adverb to name the below? hopefully2either :: Hopefully a C(x y) -> Either String (a C(x y))@@ -139,17 +159,17 @@ instance (Conflict p, Effect p, ShowPatch p) => ShowPatch (PatchInfoAnd p) where showPatch (PIAP n p) = case hopefully2either p of Right x -> showPatch x- Left _ -> human_friendly n+ Left _ -> humanFriendly n showContextPatch (PIAP n p) = case hopefully2either p of Right x -> showContextPatch x- Left _ -> return $ human_friendly n- description (PIAP n _) = human_friendly n+ Left _ -> return $ humanFriendly n+ description (PIAP n _) = humanFriendly n summary (PIAP n p) = case hopefully2either p of Right x -> summary x- Left _ -> human_friendly n+ Left _ -> humanFriendly n showNicely (PIAP n p) = case hopefully2either p of Right x -> showNicely x- Left _ -> human_friendly n+ Left _ -> humanFriendly n instance Commute p => Commute (PatchInfoAnd p) where commute (x :> y) = do y' :> x' <- commute (hopefully x :> hopefully y)@@ -179,8 +199,9 @@ instance Conflict p => Conflict (PatchInfoAnd p) where listConflictedFiles = listConflictedFiles . hopefully resolveConflicts = resolveConflicts . hopefully- commute_no_conflicts (x:>y) = do y':>x' <- commute_no_conflicts (hopefully x :> hopefully y)+ commuteNoConflicts (x:>y) = do y':>x' <- commuteNoConflicts (hopefully x :> hopefully y) return (info y `piap` y' :> info x `piap` x') conflictedEffect = conflictedEffect . hopefully+ isInconsistent = isInconsistent . hopefully instance RepoPatch p => Patchy (PatchInfoAnd p)
src/Darcs/IO.hs view
@@ -24,7 +24,7 @@ import Data.Char ( toLower ) import Data.List ( isSuffixOf ) import System.IO.Error ( isDoesNotExistError, isPermissionError )-import Control.Exception ( catch, catchJust, ioErrors )+import Control.Exception.Extensible ( catch, SomeException, IOException ) import Control.Monad.Error import System.Directory ( getDirectoryContents, createDirectory, removeDirectory, removeFile,@@ -108,7 +108,7 @@ fail $ "Cannot remove non-empty file "++fp removeFile fp mRemoveDirectory = removeDirectory . fn2fp- mRename a b = catchJust ioErrors+ mRename a b = catch (renameDirectory x y `mplus` renameFile x y) -- We need to catch does not exist errors, since older -- versions of darcs allowed users to rename nonexistent@@ -132,7 +132,7 @@ newtype SilentIO a = SIO { runSilently :: IO a } instance TolerantMonad SilentIO where- warning io = SIO $ io `catch` \_ -> return ()+ warning io = SIO $ io `catch` \(_ :: SomeException) -> return () runIO (SIO io) = io runTM io = SIO io @@ -192,15 +192,15 @@ mCreateFile f = warning $ backup f >> mWriteFilePS f B.empty mCreateDirectory d = warning $ backup d >> mCreateDirectory d mRemoveFile f = warning $ mRemoveFile f- mRemoveDirectory d = warning $ catchJust ioErrors+ mRemoveDirectory d = warning $ catch (mRemoveDirectory d)- (\e ->+ (\(e :: IOException) -> if "(Directory not empty)" `isSuffixOf` show e then ioError $ userError $ "Not deleting " ++ fn2fp d ++ " because it is not empty." else ioError $ userError $ "Not deleting " ++ fn2fp d ++ " because:\n" ++ show e)- mRename a b = warning $ catchJust ioErrors+ mRename a b = warning $ catch (let do_backup = if (map toLower x == map toLower y) then backupByCopying y -- avoid making the original vanish else backupByRenaming y@@ -224,15 +224,15 @@ mCreateFile f = warning $ backup f >> mWriteFilePS f B.empty mCreateDirectory d = warning $ backup d >> mCreateDirectory d mRemoveFile f = warning $ mRemoveFile f- mRemoveDirectory d = warning $ catchJust ioErrors+ mRemoveDirectory d = warning $ catch (mRemoveDirectory d)- (\e ->+ (\(e :: SomeException) -> if "(Directory not empty)" `isSuffixOf` show e then ioError $ userError $ "Not deleting " ++ fn2fp d ++ " because it is not empty." else ioError $ userError $ "Not deleting " ++ fn2fp d ++ " because:\n" ++ show e)- mRename a b = warning $ catchJust ioErrors+ mRename a b = warning $ catch (let do_backup = if (map toLower x == map toLower y) then backupByCopying y -- avoid making the original vanish else backupByRenaming y@@ -255,7 +255,7 @@ floatFn :: FileName -> AnchoredPath floatFn = floatPath . fn2fp -instance (MonadPlus m, MonadError e m) => ReadableDirectory (HSM.TreeMonad m) where+instance (Functor m, MonadPlus m, MonadError e m) => ReadableDirectory (HSM.TreeMonad m) where mDoesDirectoryExist d = HSM.directoryExists (floatFn d) mDoesFileExist f = HSM.fileExists (floatFn f) mInCurrentDirectory d action = HSM.withDirectory (floatFn d) action
src/Darcs/Lock.hs view
@@ -21,13 +21,14 @@ withTemp, withOpenTemp, withStdoutTemp, withTempDir, withPermDir, withDelayedDir, withNamedTemp, writeToFile, appendToFile,- writeBinFile, writeDocBinFile, appendBinFile, appendDocBinFile,- readBinFile, readDocBinFile,+ writeBinFile, writeLocaleFile, writeDocBinFile,+ appendBinFile, appendDocBinFile,+ readBinFile, readLocaleFile, readDocBinFile, writeAtomicFilePS, gzWriteAtomicFilePS, gzWriteAtomicFilePSs, gzWriteDocFile,- rm_recursive, removeFileMayNotExist,+ rmRecursive, removeFileMayNotExist, canonFilename, maybeRelink,- world_readable_temp, tempdir_loc,+ worldReadableTemp, tempdirLoc, editText, environmentHelpTmpdir, environmentHelpKeepTmpdir ) where@@ -40,8 +41,8 @@ hClose, hPutStr, Handle, IOMode(WriteMode, AppendMode), hFlush, stdout ) import System.IO.Error ( isDoesNotExistError, isAlreadyExistsError )-import Control.Exception ( bracket, catchJust, ioErrors, throwIO,- Exception(IOException), catch, try )+import Control.Exception.Extensible+ ( bracket, throwIO, catch, try, SomeException ) import System.Directory ( removeFile, removeDirectory, doesFileExist, doesDirectoryExist, getDirectoryContents, createDirectory,@@ -49,23 +50,23 @@ ) import System.FilePath.Posix ( splitDirectories ) import Workaround ( renameFile )-import Darcs.Utils ( withCurrentDirectory, maybeGetEnv, firstJustIO, run_editor )+import Darcs.Utils ( withCurrentDirectory, maybeGetEnv, firstJustIO, runEditor ) import Control.Monad ( unless, when ) -import Darcs.URL ( is_relative )-import Darcs.Utils ( catchall, add_to_error_loc )+import Darcs.URL ( isRelative )+import Darcs.Utils ( catchall, addToErrorLoc ) import Darcs.RepoPath ( AbsolutePath, FilePathLike, toFilePath, getCurrentDirectory, setCurrentDirectory ) -import ByteStringUtils ( gzWriteFilePSs)+import ByteStringUtils ( gzWriteFilePSs, decodeLocale, encodeLocale ) import qualified Data.ByteString as B (null, readFile, writeFile, hPut, ByteString) import qualified Data.ByteString.Char8 as BC (unpack) import Darcs.SignalHandler ( withSignalsBlocked ) import Printer ( Doc, hPutDoc, packedString, empty, renderPSs ) import Darcs.Global ( atexit, darcsdir )-import Darcs.Compat ( mk_stdout_temp, canonFilename, maybeRelink,- atomic_create, sloppy_atomic_create )+import Darcs.Compat ( mkStdoutTemp, canonFilename, maybeRelink,+ atomicCreate, sloppyAtomicCreate ) import System.Posix.Files ( getSymbolicLinkStatus, isDirectory, fileMode, getFileStatus, setFileMode ) import System.Posix ( sleep )@@ -103,7 +104,7 @@ catchNonExistence :: IO a -> a -> IO a catchNonExistence job nonexistval =- catchJust ioErrors job $+ catch job $ \e -> if isDoesNotExistError e then return nonexistval else ioError e @@ -111,27 +112,23 @@ takeLock :: FilePathLike p => p -> IO Bool takeLock fp =- do atomic_create $ toFilePath fp+ do atomicCreate $ toFilePath fp return True- `catch` \e -> case e of- IOException e'- | isAlreadyExistsError e' ->- return False- _ -> do pwd <- getCurrentDirectory- throwIO $ add_to_error_loc e- ("takeLock "++toFilePath fp++" in "++toFilePath pwd)+ `catch` \e -> if isAlreadyExistsError e+ then return False+ else do pwd <- getCurrentDirectory+ throwIO $ addToErrorLoc e+ ("takeLock "++toFilePath fp++" in "++toFilePath pwd) takeFile :: FilePath -> IO Bool takeFile fp =- do sloppy_atomic_create fp+ do sloppyAtomicCreate fp return True- `catch` \e -> case e of- IOException e'- | isAlreadyExistsError e' ->- return False- _ -> do pwd <- getCurrentDirectory- throwIO $ add_to_error_loc e- ("takeFile "++fp++" in "++toFilePath pwd)+ `catch` \e -> if isAlreadyExistsError e+ then return False+ else do pwd <- getCurrentDirectory+ throwIO $ addToErrorLoc e+ ("takeFile "++fp++" in "++toFilePath pwd) -- |'withTemp' safely creates an empty file (not open for writing) and -- returns its name.@@ -151,16 +148,16 @@ -- would reintroduce a race condition). withOpenTemp :: ((Handle, String) -> IO a) -> IO a withOpenTemp = bracket get_empty_file cleanup- where cleanup (h,f) = do try $ hClose h+ where cleanup (h,f) = do try (hClose h) :: IO (Either SomeException ()) removeFileMayNotExist f get_empty_file = invert `fmap` openBinaryTempFile "." "darcs" invert (a,b) = (b,a) withStdoutTemp :: (String -> IO a) -> IO a-withStdoutTemp = bracket (mk_stdout_temp "stdout_") removeFileMayNotExist+withStdoutTemp = bracket (mkStdoutTemp "stdout_") removeFileMayNotExist -tempdir_loc :: IO FilePath-tempdir_loc = firstJustIO [ readBinFile (darcsdir++"/prefs/tmpdir") >>= return . Just . head.words >>= chkdir,+tempdirLoc :: IO FilePath+tempdirLoc = firstJustIO [ readBinFile (darcsdir++"/prefs/tmpdir") >>= return . Just . head.words >>= chkdir, maybeGetEnv "DARCS_TMPDIR" >>= chkdir, getTemporaryDirectory >>= chkdir . Just, getCurrentDirectorySansDarcs,@@ -190,8 +187,8 @@ withDir :: WithDirKind -> String -> (AbsolutePath -> IO a) -> IO a withDir _ "" _ = bug "withDir called with empty directory name" withDir kind abs_or_relative_name job = do- absolute_name <- if is_relative abs_or_relative_name- then fmap (++ abs_or_relative_name) tempdir_loc+ absolute_name <- if isRelative abs_or_relative_name+ then fmap (++ abs_or_relative_name) tempdirLoc else return abs_or_relative_name formerdir <- getCurrentDirectory bracket (create_directory absolute_name 0)@@ -199,8 +196,8 @@ k <- keep_tmpdir unless k $ do case kind of Perm -> return ()- Temp -> rm_recursive (toFilePath dir)- Delayed -> atexit $ rm_recursive (toFilePath dir))+ Temp -> rmRecursive (toFilePath dir)+ Delayed -> atexit $ rmRecursive (toFilePath dir)) job where newname name 0 = name newname name n = name ++ "-" ++ show n@@ -209,11 +206,9 @@ = do createDirectory $ newname name n setCurrentDirectory $ newname name n getCurrentDirectory- `catch` (\e -> case e of- IOException e'- | isAlreadyExistsError e' ->- create_directory name (n+1)- _ -> throwIO e)+ `catch` (\e -> if isAlreadyExistsError e+ then create_directory name (n+1)+ else throwIO e) keep_tmpdir = isJust `fmap` maybeGetEnv "DARCS_KEEP_TMPDIR" environmentHelpKeepTmpdir :: ([String], [String])@@ -254,21 +249,21 @@ doesDirectoryReallyExist f = catchNonExistence (isDirectory `fmap` getSymbolicLinkStatus f) False -rm_recursive :: FilePath -> IO ()-rm_recursive d =+rmRecursive :: FilePath -> IO ()+rmRecursive d = do isd <- doesDirectoryReallyExist d if not isd then removeFile d else when isd $ do conts <- actual_dir_contents withCurrentDirectory d $- mapM_ rm_recursive conts+ mapM_ rmRecursive conts removeDirectory d where actual_dir_contents = -- doesn't include . or .. do c <- getDirectoryContents d return $ filter (/=".") $ filter (/="..") c -world_readable_temp :: String -> IO String-world_readable_temp f = wrt 0+worldReadableTemp :: String -> IO String+worldReadableTemp f = wrt 0 where wrt :: Int -> IO String wrt 100 = fail $ "Failure creating temp named "++f wrt n = let f_new = f++"-"++show n@@ -278,17 +273,22 @@ withNamedTemp :: String -> (String -> IO a) -> IO a withNamedTemp n = bracket get_empty_file removeFileMayNotExist- where get_empty_file = world_readable_temp n+ where get_empty_file = worldReadableTemp n editText :: String -> B.ByteString -> IO B.ByteString editText desc txt = withNamedTemp desc $ \f -> do B.writeFile f txt- run_editor f+ runEditor f B.readFile f readBinFile :: FilePathLike p => p -> IO String readBinFile = fmap BC.unpack . B.readFile . toFilePath +-- | Reads a file. Differs from readBinFile in that it interprets the file in+-- the current locale instead of as ISO-8859-1.+readLocaleFile :: FilePathLike p => p -> IO String+readLocaleFile f = decodeLocale `fmap` B.readFile (toFilePath f)+ readDocBinFile :: FilePathLike p => p -> IO Doc readDocBinFile fp = do ps <- B.readFile $ toFilePath fp return $ if B.null ps then empty else packedString ps@@ -302,6 +302,11 @@ writeBinFile :: FilePathLike p => p -> String -> IO () writeBinFile f s = writeToFile f $ \h -> hPutStr h s +-- | Writes a file. Differs from writeBinFile in that it writes the string+-- encoded with the current locale instead of what GHC thinks is right.+writeLocaleFile :: FilePathLike p => p -> String -> IO ()+writeLocaleFile f s = writeToFile f $ \h -> B.hPut h (encodeLocale s)+ writeDocBinFile :: FilePathLike p => p -> Doc -> IO () writeDocBinFile f d = writeToFile f $ \h -> hPutDoc h d @@ -335,5 +340,5 @@ renameFile newf (toFilePath f) appendToFile :: FilePathLike p => p -> (Handle -> IO ()) -> IO ()-appendToFile f job = withSignalsBlocked $ +appendToFile f job = withSignalsBlocked $ bracket (openBinaryFile (toFilePath f) AppendMode) hClose job
src/Darcs/Match.lhs view
@@ -34,36 +34,38 @@ ) where import Text.Regex ( mkRegex, matchRegex )-import Control.Monad ( when, unless )+import Control.Monad ( when ) import Data.Maybe ( isJust )+import Data.List ( isPrefixOf ) import Darcs.Hopefully ( PatchInfoAnd, info, piap, conscientiously, hopefully )-import Darcs.Patch.Info ( just_name )+import Darcs.Patch.Info ( justName ) import Darcs.Patch ( RepoPatch, Patch, Patchy, Named, invert, invertRL, patch2patchinfo, apply )-import Darcs.Repository ( Repository, PatchSet, SealedPatchSet, read_repo,- slurp_recorded, createPristineDirectoryTree )-import Darcs.Repository.ApplyPatches ( apply_patches )-import Darcs.Patch.Depends ( get_patches_in_tag, get_patches_beyond_tag )+import Darcs.Repository ( Repository, readRepo, createPristineDirectoryTree )+import Darcs.Patch.Set ( PatchSet(..), Tagged(..), SealedPatchSet, newset2RL )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif+import Darcs.Repository.ApplyPatches ( applyPatches )+import Darcs.Patch.Depends ( getPatchesInTag, getPatchesBeyondTag ) import Darcs.Witnesses.Ordered ( RL(..), concatRL, consRLSealed ) import ByteStringUtils ( mmapFilePS )-import qualified Data.ByteString as B (ByteString) import Darcs.Flags ( DarcsFlag( OnePatch, SeveralPatch, Context, AfterPatch, UpToPatch, LastN, PatchIndexRange, OneTag, AfterTag, UpToTag, OnePattern, SeveralPattern,- AfterPattern, UpToPattern ), willStoreInMemory )-import Darcs.Patch.Bundle ( scan_context )-import Darcs.Patch.Match ( Matcher, MatchFun, match_pattern, apply_matcher, make_matcher, parseMatch )+ AfterPattern, UpToPattern ) )+import Darcs.Patch.Bundle ( scanContext )+import Darcs.Patch.Match ( Matcher, MatchFun, matchPattern, applyMatcher, makeMatcher, parseMatch ) import Darcs.Patch.MatchData ( PatchMatch ) import Printer ( text, ($$) ) import Darcs.RepoPath ( toFilePath )-import Darcs.IO ( WriteableDirectory(..), ReadableDirectory(..) )-import Darcs.SlurpDirectory ( SlurpMonad, writeSlurpy, withSlurpy )-import Darcs.Patch.FileName ( FileName, superName, norm_path, (///) )+import Darcs.IO ( WriteableDirectory(..) )+import Darcs.Patch.FileName ( FileName ) import Darcs.Witnesses.Sealed ( FlippedSeal(..), Sealed2(..), seal, flipSeal, seal2, unsealFlipped, unseal2, unseal ) #include "impossible.h"@@ -126,15 +128,15 @@ hasC (_:xs) = hasC xs getNonrangeMatch :: RepoPatch p => Repository p C(r u t) -> [DarcsFlag] -> IO ()-getNonrangeMatch r fs = withRecordedMatchSmart fs r (getNonrangeMatchS fs)+getNonrangeMatch r fs = withRecordedMatch r (getNonrangeMatchS fs) getPartialNonrangeMatch :: RepoPatch p => Repository p C(r u t) -> [DarcsFlag] -> [FileName] -> IO ()-getPartialNonrangeMatch r fs files =- withRecordedMatchOnlySomeSmart fs r files (getNonrangeMatchS fs)+getPartialNonrangeMatch r fs _ =+ withRecordedMatch r (getNonrangeMatchS fs) -getNonrangeMatchS :: (MatchMonad m p, RepoPatch p) =>- [DarcsFlag] -> PatchSet p C(x) -> m ()+getNonrangeMatchS :: (RepoPatch p) =>+ [DarcsFlag] -> PatchSet p C(Origin x) -> IO () getNonrangeMatchS fs repo = case nonrangeMatcher fs of Just m -> if nonrangeMatcherIsTag fs@@ -151,25 +153,28 @@ || isJust (hasIndexRange fs) getFirstMatch :: RepoPatch p => Repository p C(r u t) -> [DarcsFlag] -> IO ()-getFirstMatch r fs = withRecordedMatchSmart fs r (getFirstMatchS fs)+getFirstMatch r fs = withRecordedMatch r (getFirstMatchS fs) getPartialFirstMatch :: RepoPatch p => Repository p C(r u t) -> [DarcsFlag] -> [FileName] -> IO ()-getPartialFirstMatch r fs files =- withRecordedMatchOnlySomeSmart fs r files (getFirstMatchS fs)+getPartialFirstMatch r fs _ =+ withRecordedMatch r (getFirstMatchS fs) -getFirstMatchS :: (MatchMonad m p, RepoPatch p) =>- [DarcsFlag] -> PatchSet p C(x) -> m ()+getFirstMatchS :: (RepoPatch p) =>+ [DarcsFlag] -> PatchSet p C(Origin x) -> IO () getFirstMatchS fs repo = case hasLastn fs of- Just n -> applyInvRL `unsealFlipped` (safetake n $ concatRL repo)- Nothing -> case firstMatcher fs of+ Just n -> unpullLastN repo n+ Nothing ->+ case hasIndexRange fs of+ Just (_,b) -> unpullLastN repo b -- b is chronologically earlier than a+ Nothing ->+ case firstMatcher fs of Nothing -> fail "Pattern not specified in getFirstMatch." Just m -> if firstMatcherIsTag fs then getTagS m repo else getMatcherS Inclusive m repo - -- | @secondMatch fs@ tells whether @fs@ implies a "second match", that -- is if we match against patches up to a point in the past on, rather -- than against all patches until now.@@ -178,14 +183,19 @@ getPartialSecondMatch :: RepoPatch p => Repository p C(r u t) -> [DarcsFlag] -> [FileName] -> IO ()-getPartialSecondMatch r fs files =- withRecordedMatchOnlySomeSmart fs r files $ \repo ->+getPartialSecondMatch r fs _ =+ withRecordedMatch r $ \repo -> case secondMatcher fs of- Nothing -> fail "Two patterns not specified in get_second_match."+ Nothing -> case hasIndexRange fs of+ Just (a,_) -> unpullLastN repo (a-1)+ Nothing -> fail "Two patterns not specified in get_second_match." Just m -> if secondMatcherIsTag fs then getTagS m repo else getMatcherS Exclusive m repo +unpullLastN :: Patchy p => PatchSet p C(x y) -> Int -> IO ()+unpullLastN repo n = applyInvRL `unsealFlipped` (safetake n $ newset2RL repo)+ checkMatchSyntax :: [DarcsFlag] -> IO () checkMatchSyntax opts = do case getMatchPattern opts of@@ -199,14 +209,14 @@ getMatchPattern (_:fs) = getMatchPattern fs tagmatch :: String -> Matcher p-tagmatch r = make_matcher ("tag-name "++r) tm+tagmatch r = makeMatcher ("tag-name "++r) tm where tm (Sealed2 p) =- let n = just_name (info p) in- take 4 n == "TAG " && isJust (matchRegex (mkRegex r) $ drop 4 n)+ let n = justName (info p) in+ "TAG " `isPrefixOf` n && isJust (matchRegex (mkRegex r) $ drop 4 n) mymatch :: String -> Matcher p-mymatch r = make_matcher ("patch-name "++r) mm- where mm (Sealed2 p) = isJust . matchRegex (mkRegex r) . just_name . info $ p+mymatch r = makeMatcher ("patch-name "++r) mm+ where mm (Sealed2 p) = isJust . matchRegex (mkRegex r) . justName . info $ p -- | strictJust is a strict version of the Just constructor, used to ensure@@ -232,10 +242,10 @@ -- @--tag@ options are passed (or their plural variants). nonrangeMatcher :: Patchy p => [DarcsFlag] -> Maybe (Matcher p) nonrangeMatcher [] = Nothing-nonrangeMatcher (OnePattern m:_) = strictJust $ match_pattern m+nonrangeMatcher (OnePattern m:_) = strictJust $ matchPattern m nonrangeMatcher (OneTag t:_) = strictJust $ tagmatch t nonrangeMatcher (OnePatch p:_) = strictJust $ mymatch p-nonrangeMatcher (SeveralPattern m:_) = strictJust $ match_pattern m+nonrangeMatcher (SeveralPattern m:_) = strictJust $ matchPattern m nonrangeMatcher (SeveralPatch p:_) = strictJust $ mymatch p nonrangeMatcher (_:fs) = nonrangeMatcher fs @@ -252,8 +262,8 @@ -- returns @Nothing@. firstMatcher :: Patchy p => [DarcsFlag] -> Maybe (Matcher p) firstMatcher [] = Nothing-firstMatcher (OnePattern m:_) = strictJust $ match_pattern m-firstMatcher (AfterPattern m:_) = strictJust $ match_pattern m+firstMatcher (OnePattern m:_) = strictJust $ matchPattern m+firstMatcher (AfterPattern m:_) = strictJust $ matchPattern m firstMatcher (AfterTag t:_) = strictJust $ tagmatch t firstMatcher (OnePatch p:_) = strictJust $ mymatch p firstMatcher (AfterPatch p:_) = strictJust $ mymatch p@@ -266,8 +276,8 @@ secondMatcher :: Patchy p => [DarcsFlag] -> Maybe (Matcher p) secondMatcher [] = Nothing-secondMatcher (OnePattern m:_) = strictJust $ match_pattern m-secondMatcher (UpToPattern m:_) = strictJust $ match_pattern m+secondMatcher (OnePattern m:_) = strictJust $ matchPattern m+secondMatcher (UpToPattern m:_) = strictJust $ matchPattern m secondMatcher (OnePatch p:_) = strictJust $ mymatch p secondMatcher (UpToPatch p:_) = strictJust $ mymatch p secondMatcher (UpToTag t:_) = strictJust $ tagmatch t@@ -283,7 +293,7 @@ matchAPatchread :: Patchy p => [DarcsFlag] -> PatchInfoAnd p C(x y) -> Bool matchAPatchread fs = case nonrangeMatcher fs of Nothing -> const True- Just m -> apply_matcher m+ Just m -> applyMatcher m -- | @matchAPatch fs p@ tells whether @p@ matches the matchers in -- the flags @fs@@@ -291,9 +301,9 @@ matchAPatch fs p = case nonrangeMatcher fs of Nothing -> True- Just m -> apply_matcher m (patch2patchinfo p `piap` p)+ Just m -> applyMatcher m (patch2patchinfo p `piap` p) -matchPatch :: RepoPatch p => [DarcsFlag] -> PatchSet p C(x) -> Sealed2 (Named p)+matchPatch :: RepoPatch p => [DarcsFlag] -> PatchSet p C(start x) -> Sealed2 (Named p) matchPatch fs ps = case hasIndexRange fs of Just (a,a') | a == a' -> case (unseal myhead) $ dropn (a-1) ps of@@ -301,22 +311,23 @@ Nothing -> error "Patch out of range!" | otherwise -> bug ("Invalid index range match given to matchPatch: "++ show (PatchIndexRange a a'))- where myhead :: PatchSet p C(x) -> Maybe (Sealed2 (PatchInfoAnd p))- myhead (NilRL:<:x) = myhead x- myhead ((x:<:_):<:_) = Just $ seal2 x- myhead NilRL = Nothing+ where myhead :: PatchSet p C(start x) -> Maybe (Sealed2 (PatchInfoAnd p))+ myhead (PatchSet NilRL (Tagged t _ _ :<: _)) = Just $ seal2 t+ myhead (PatchSet (x:<:_) _) = Just $ seal2 x+ myhead _ = Nothing Nothing -> case nonrangeMatcher fs of Nothing -> bug "Couldn't matchPatch" Just m -> findAPatch m ps -getOnePatchset :: RepoPatch p => Repository p C(r u t) -> [DarcsFlag] -> IO (SealedPatchSet p)+getOnePatchset :: RepoPatch p => Repository p C(r u t) -> [DarcsFlag] ->+ IO (SealedPatchSet p C(Origin)) getOnePatchset repository fs = case nonrangeMatcher fs of- Just m -> do ps <- read_repo repository+ Just m -> do ps <- readRepo repository if nonrangeMatcherIsTag fs then return $ getMatchingTag m ps else return $ matchAPatchset m ps- Nothing -> (seal . scan_context) `fmap` mmapFilePS (toFilePath $ context_f fs)+ Nothing -> (seal . scanContext) `fmap` mmapFilePS (toFilePath $ context_f fs) where context_f [] = bug "Couldn't match_nonrange_patchset" context_f (Context f:_) = f context_f (_:xs) = context_f xs@@ -338,7 +349,8 @@ -- patches in @matchFirstPatchset fs ps@ are the ones we don't want. -- -- Question: are they really? Florent-matchFirstPatchset :: RepoPatch p => [DarcsFlag] -> PatchSet p C(x) -> SealedPatchSet p+matchFirstPatchset :: RepoPatch p => [DarcsFlag] -> PatchSet p C(start x)+ -> SealedPatchSet p C(start) matchFirstPatchset fs patchset = case hasLastn fs of Just n -> dropn n patchset@@ -353,15 +365,16 @@ else matchAPatchset m patchset -- | @dropn n ps@ drops the @n@ last patches from @ps@.-dropn :: Int -> PatchSet p C(x) -> SealedPatchSet p+dropn :: Int -> PatchSet p C(start x) -> SealedPatchSet p C(start) dropn n ps | n <= 0 = seal ps-dropn n (NilRL:<:ps) = dropn n ps-dropn _ NilRL = seal $ NilRL:<:NilRL-dropn n ((_:<:ps):<:xs) = dropn (n-1) $ ps:<:xs+dropn n (PatchSet NilRL (Tagged t _ ps :<: ts)) = dropn n $ PatchSet (t:<:ps) ts+dropn _ (PatchSet NilRL NilRL) = seal $ PatchSet NilRL NilRL+dropn n (PatchSet (_:<:ps) ts) = dropn (n-1) $ PatchSet ps ts -- | @matchSecondPatchset fs ps@ returns the part of @ps@ before its -- second matcher, ie the one that comes last dependencywise.-matchSecondPatchset :: RepoPatch p => [DarcsFlag] -> PatchSet p C(x) -> SealedPatchSet p+matchSecondPatchset :: RepoPatch p => [DarcsFlag] -> PatchSet p C(start x)+ -> SealedPatchSet p C(start) matchSecondPatchset fs ps = case hasIndexRange fs of Just (a,_) -> dropn (a-1) ps@@ -374,82 +387,61 @@ -- | @findAPatch m ps@ returns the last patch in @ps@ matching @m@, and -- calls 'error' if there is none.-findAPatch :: RepoPatch p => Matcher p -> PatchSet p C(x) -> Sealed2 (Named p)-findAPatch m NilRL = error $ "Couldn't find patch matching " ++ show m-findAPatch m (NilRL:<:xs) = findAPatch m xs-findAPatch m ((p:<:ps):<:xs) | apply_matcher m p = seal2 $ hopefully p- | otherwise = findAPatch m (ps:<:xs)+findAPatch :: RepoPatch p => Matcher p -> PatchSet p C(start x) -> Sealed2 (Named p)+findAPatch m (PatchSet NilRL NilRL) = error $ "Couldn't find patch matching " ++ show m+findAPatch m (PatchSet NilRL (Tagged t _ ps :<: ts)) = findAPatch m (PatchSet (t:<:ps) ts)+findAPatch m (PatchSet (p:<:ps) ts) | applyMatcher m p = seal2 $ hopefully p+ | otherwise = findAPatch m (PatchSet ps ts) -- | @matchAPatchset m ps@ returns a (the largest?) subset of @ps@ -- ending in patch which matches @m@. Calls 'error' if there is none.-matchAPatchset :: RepoPatch p => Matcher p -> PatchSet p C(x) -> SealedPatchSet p-matchAPatchset m NilRL = error $ "Couldn't find patch matching " ++ show m-matchAPatchset m (NilRL:<:xs) = matchAPatchset m xs-matchAPatchset m ((p:<:ps):<:xs) | apply_matcher m p = seal ((p:<:ps):<:xs)- | otherwise = matchAPatchset m (ps:<:xs)+matchAPatchset :: RepoPatch p => Matcher p -> PatchSet p C(start x)+ -> SealedPatchSet p C(start)+matchAPatchset m (PatchSet NilRL NilRL) = error $ "Couldn't find patch matching " ++ show m+matchAPatchset m (PatchSet NilRL (Tagged t _ ps :<: ts)) = matchAPatchset m (PatchSet (t:<:ps) ts)+matchAPatchset m (PatchSet (p:<:ps) ts) | applyMatcher m p = seal (PatchSet (p:<:ps) ts)+ | otherwise = matchAPatchset m (PatchSet ps ts) -- | @getMatchingTag m ps@, where @m@ is a 'Matcher' which matches tags -- returns a 'SealedPatchSet' containing all patches in the last tag which -- matches @m@. Last tag means the most recent tag in repository order, -- i.e. the last one you'd see if you ran darcs changes -t @m@. Calls -- 'error' if there is no matching tag.-getMatchingTag :: RepoPatch p => Matcher p -> PatchSet p C(x) -> SealedPatchSet p-getMatchingTag m NilRL = error $ "Couldn't find a tag matching " ++ show m-getMatchingTag m (NilRL:<:xs) = getMatchingTag m xs-getMatchingTag m xxx@((p:<:ps):<:xs)- | apply_matcher m p = get_patches_in_tag (info p) xxx- | otherwise = getMatchingTag m (ps:<:xs)+getMatchingTag :: RepoPatch p => Matcher p -> PatchSet p C(start x) -> SealedPatchSet p C(start)+getMatchingTag m (PatchSet NilRL NilRL) = error $ "Couldn't find a tag matching " ++ show m+getMatchingTag m (PatchSet NilRL (Tagged t _ ps :<: ts)) = getMatchingTag m (PatchSet (t:<:ps) ts)+getMatchingTag m (PatchSet (p:<:ps) ts)+ | applyMatcher m p = seal $ PatchSet (p:<:ps) ts+ | otherwise = getMatchingTag m (PatchSet ps ts) -- | @matchExists m ps@ tells whether there is a patch matching -- @m@ in @ps@-matchExists :: Matcher p -> PatchSet p C(x) -> Bool-matchExists _ NilRL = False-matchExists m (NilRL:<:xs) = matchExists m xs-matchExists m ((p:<:ps):<:xs) | apply_matcher m $ p = True- | otherwise = matchExists m (ps:<:xs)--applyInvToMatcher :: (RepoPatch p, WriteableDirectory m) => InclusiveOrExclusive -> Matcher p -> PatchSet p C(x) -> m ()-applyInvToMatcher _ _ NilRL = impossible-applyInvToMatcher ioe m (NilRL:<:xs) = applyInvToMatcher ioe m xs-applyInvToMatcher ioe m ((p:<:ps):<:xs)- | apply_matcher m p = when (ioe == Inclusive) (applyInvp p)- | otherwise = applyInvp p >> applyInvToMatcher ioe m (ps:<:xs)+matchExists :: Matcher p -> PatchSet p C(start x) -> Bool+matchExists _ (PatchSet NilRL NilRL) = False+matchExists m (PatchSet NilRL (Tagged t _ ps :<: ts)) = matchExists m (PatchSet (t:<:ps) ts)+matchExists m (PatchSet (p:<:ps) ts) | applyMatcher m $ p = True+ | otherwise = matchExists m (PatchSet ps ts) --- | @maybeReadFile@ recursively gets the contents of all files--- in a directory, or just the contents of a file if called on a--- simple file.-maybeReadFile :: ReadableDirectory m => FileName -> m ([(FileName, B.ByteString)])-maybeReadFile file = do- d <- mDoesDirectoryExist file- if d- then do- children <- mInCurrentDirectory file mGetDirectoryContents- maybe_read_files [file /// ch | ch <- children]- else do- e <- mDoesFileExist file- if e- then do- contents <- mReadFilePS file- return [(norm_path file, contents)]- else return []- where maybe_read_files [] = return []- maybe_read_files (f:fs) = do- x <- maybeReadFile f- y <- maybe_read_files fs- return $ concat [x,y]+applyInvToMatcher :: (RepoPatch p, WriteableDirectory m) => InclusiveOrExclusive -> Matcher p -> PatchSet p C(Origin x) -> m ()+applyInvToMatcher _ _ (PatchSet NilRL NilRL) = impossible+applyInvToMatcher ioe m (PatchSet NilRL (Tagged t _ ps :<: ts)) = applyInvToMatcher ioe m+ (PatchSet (t:<:ps) ts)+applyInvToMatcher ioe m (PatchSet (p:<:ps) xs)+ | applyMatcher m p = when (ioe == Inclusive) (applyInvp p)+ | otherwise = applyInvp p >> applyInvToMatcher ioe m (PatchSet ps xs) -getMatcherS :: (MatchMonad m p, RepoPatch p) =>- InclusiveOrExclusive -> Matcher p -> PatchSet p C(x) -> m ()+getMatcherS :: (WriteableDirectory m, RepoPatch p) =>+ InclusiveOrExclusive -> Matcher p -> PatchSet p C(Origin x) -> m () getMatcherS ioe m repo = if matchExists m repo then applyInvToMatcher ioe m repo else fail $ "Couldn't match pattern "++ show m -getTagS :: (MatchMonad m p, RepoPatch p) =>- Matcher p -> PatchSet p C(x) -> m ()+getTagS :: (RepoPatch p) =>+ Matcher p -> PatchSet p C(Origin x) -> IO () getTagS match repo = do let pinfo = patch2patchinfo `unseal2` (findAPatch match repo)- case get_patches_beyond_tag pinfo repo of+ case getPatchesBeyondTag pinfo repo of FlippedSeal extras -> applyInvRL extras -- | @applyInvp@ tries to get the patch that's in a 'PatchInfoAnd@@ -471,96 +463,11 @@ safetake _ NilRL = error "There aren't that many patches..." safetake i (a:<:as) = a `consRLSealed` safetake (i-1) as --- | A @MatchMonad p m@ is a monad in which we match patches from @p@--- by playing with them in @m@, a 'WriteableDirectory' monad. How we--- play with the patches depends on the instance of @MatchMonad@ we're--- using. If we use @IO@, then we'll apply the patches directly in--- @m@, if we use @SlurpMonad@, then we'll apply the patches to a--- slurpy, and write to disk at the end. Note that both @IO@ and--- @SlurpMonad@ have an instance of 'WriteableDirectory' that--- implicitely writes in the current directory.-class (RepoPatch p, WriteableDirectory m) => MatchMonad m p where- withRecordedMatch :: Repository p C(r u t)- -> (PatchSet p C(r) -> m ()) -> IO ()- -- ^ @withRecordedMatch@ is responsible for getting the recorded state- -- into the monad, and then applying the second argument, and- -- finally placing the resulting state into the current directory.- withRecordedMatchOnlySomeFiles- :: Repository p C(r u t) -> [FileName]- -> (PatchSet p C(r) -> m ()) -> IO ()- -- ^ @withRecordedMatchOnlySomeFiles@ is a variant of- -- withRecordedMatch that may only return some of the files- -- (e.g. if we want to run diff on just a few files).- withRecordedMatchOnlySomeFiles r _ j = withRecordedMatch r j- applyInvRL :: RL (PatchInfoAnd p) C(x r) -> m ()- applyInvRL NilRL = return ()- applyInvRL (p:<:ps) = applyInvp p >> applyInvRL ps--withRecordedMatchIO :: RepoPatch p => Repository p C(r u t)- -> (PatchSet p C(r) -> IO ()) -> IO ()-withRecordedMatchIO = withRecordedMatch---- | @withRecordedMatchSmart@ hides away the choice of the--- 'SlurpMonad' to use in order to apply 'withRecordedMatch'.--- If we have the @--store-in-memory@ flag, then use 'SlurpMonad', else--- use @IO@. In both case, the result is in the @IO@ monad.------ Suggestion: shouldn't we name @withRecordedMatchSmart@--- @withRecordedMatch@, and give the monad function another name such--- as @withRecordedMatchRaw@?-withRecordedMatchSmart :: RepoPatch p => [DarcsFlag] -> Repository p C(r u t)- -> (forall m. MatchMonad m p => PatchSet p C(r) -> m ())- -> IO ()-withRecordedMatchSmart opts r j =- do if willStoreInMemory opts then withSM r j- else withRecordedMatchIO r j- where withSM :: RepoPatch p => Repository p C(r u t)- -> (PatchSet p C(r) -> SlurpMonad ()) -> IO ()- withSM = withRecordedMatch---- | @withRecordedMatchOnlySomeSmart@ is the smart version of--- 'withRecordedMatchOnlySome'. It runs 'withRecordedMatchOnlySome'--- either in the 'SlurpMonad' or in @IO@ according to the--- @--store-in-memory@ flag.-withRecordedMatchOnlySomeSmart :: RepoPatch p => [DarcsFlag] -> Repository p C(r u t)- -> [FileName]- -> (forall m. MatchMonad m p => PatchSet p C(r) -> m ())- -> IO ()-withRecordedMatchOnlySomeSmart opts r [] j = withRecordedMatchSmart opts r j-withRecordedMatchOnlySomeSmart opts r files j =- do if willStoreInMemory opts then withSM r files j- else withIO r files j- where withSM :: RepoPatch p => Repository p C(r u t) -> [FileName]- -> (PatchSet p C(r) -> SlurpMonad ()) -> IO ()- withSM = withRecordedMatchOnlySomeFiles- withIO :: RepoPatch p => Repository p C(r u t) -> [FileName]- -> (PatchSet p C(r) -> IO ()) -> IO ()- withIO = withRecordedMatchOnlySomeFiles--instance RepoPatch p => MatchMonad IO p where- withRecordedMatch r job = do createPristineDirectoryTree r "."- read_repo r >>= job- applyInvRL = apply_patches [] . invertRL -- this gives nicer feedback--instance RepoPatch p => MatchMonad SlurpMonad p where- withRecordedMatch r job =- do ps <- read_repo r- s <- slurp_recorded r- case withSlurpy s (job ps) of- Left err -> fail err- Right (s',_) -> writeSlurpy s' "."- withRecordedMatchOnlySomeFiles r fs job =- do ps <- read_repo r- s <- slurp_recorded r- case withSlurpy s (job ps >> mapM maybeReadFile fs) of- Left err -> fail err- Right (_,fcs) -> mapM_ createAFile $ concat fcs- where createAFile (p,c) = do ensureDirectories $ superName p- mWriteFilePS p c- ensureDirectories d =- do isPar <- mDoesDirectoryExist d- unless isPar $ do- ensureDirectories $ superName d- mCreateDirectory d+withRecordedMatch :: RepoPatch p => Repository p C(r u t)+ -> (PatchSet p C(Origin r) -> IO ()) -> IO ()+withRecordedMatch r job = do createPristineDirectoryTree r "."+ readRepo r >>= job +applyInvRL :: (Patchy p) => RL (PatchInfoAnd p) C(x r) -> IO ()+applyInvRL = applyPatches [] . invertRL -- this gives nicer feedback \end{code}
src/Darcs/Patch.lhs view
@@ -54,24 +54,20 @@ infopatch, changepref, thing, things, isSimilar, primIsAddfile, primIsHunk, primIsSetpref,-#ifndef GADT_WITNESSES- merger, isMerger, merge,+ isMerger, merge, commute, listTouchedFiles, hunkMatches, -- for PatchTest- unravel, elegantMerge,-#else- Commute(..),-#endif+ elegantMerge, resolveConflicts, Effect, effect, primIsBinary, gzWritePatch, writePatch, primIsAdddir, invert, invertFL, invertRL, identity,- commuteFL, commuteRL,+ commuteFLorComplain, commuteRL, readPatch, canonize, sortCoalesceFL, tryToShrink,- applyToSlurpy, patchname, patchcontents,- applyToFilepaths, forceReplaceSlurpy, apply,+ patchname, patchcontents,+ applyToFilepaths, apply, applyToTree, patch2patchinfo, LineMark(AddedLine, RemovedLine, AddedRemovedLine, None),@@ -79,7 +75,7 @@ summary, plainSummary, xmlSummary, adddeps, getdeps, listConflictedFiles,- modernizePatch,+ modernizePatch, isInconsistent, -- for Population DirMark(..), patchChanges, applyToPop, ) where@@ -88,9 +84,7 @@ flattenFL, adddeps, namepatch, anonymous,-#ifndef GADT_WITNESSES isMerger,-#endif getdeps, isNullPatch, nullP, infopatch, patch2patchinfo, patchname, patchcontents )@@ -99,34 +93,27 @@ showPatch, showNicely, showContextPatch, invert, invertRL, invertFL, identity, thing, things,- commuteFL, commuteRL, apply,+ commuteFLorComplain, commuteRL, apply, description, summary,-#ifndef GADT_WITNESSES- commute, listTouchedFiles, hunkMatches,-#else- Commute(..)-#endif+ commute, listTouchedFiles, hunkMatches ) import Darcs.Patch.Viewing ( xmlSummary, plainSummary ) import Darcs.Patch.Apply ( applyToPop, patchChanges, emptyMarkedupFile,- markupFile, forceReplaceSlurpy,- applyToFilepaths, applyToSlurpy,+ markupFile,+ applyToFilepaths, LineMark(..), MarkedUpFile, applyToTree ) import Darcs.Patch.Commute ( modernizePatch,-#ifndef GADT_WITNESSES- unravel,- merger, merge, elegantMerge,-#endif+ merge, elegantMerge, ) import Darcs.Patch.Prim ( FromPrims, fromPrims, joinPatches, FromPrim, fromPrim, Conflict, Effect(effect), listConflictedFiles, resolveConflicts, Prim, canonize, sortCoalesceFL, rmdir, rmfile, tokreplace, adddir, addfile,- binary, changepref, hunk, move, + binary, changepref, hunk, move, primIsAdddir, primIsAddfile, primIsHunk, primIsBinary, primIsSetpref,- isSimilar,+ isSimilar, isInconsistent, tryToShrink ) import Darcs.Witnesses.Ordered ( FL ) import Darcs.Patch.Real ( RealPatch )
src/Darcs/Patch/Apply.lhs view
@@ -22,14 +22,13 @@ #include "gadts.h" -module Darcs.Patch.Apply ( applyToFilepaths, applyToSlurpy,+module Darcs.Patch.Apply ( applyToFilepaths, forceTokReplace, markupFile, emptyMarkedupFile, patchChanges, applyToPop, applyToTree,- LineMark(..), MarkedUpFile,- forceReplaceSlurpy )+ LineMark(..), MarkedUpFile ) where import Prelude hiding ( catch, pi )@@ -38,7 +37,7 @@ import qualified Data.ByteString.Char8 as BC (split, break, pack, singleton) import qualified Data.ByteString as B (ByteString, null, empty, concat, isPrefixOf)-import ByteStringUtils ( linesPS, unlinesPS, break_after_nth_newline, break_before_nth_newline, )+import ByteStringUtils ( linesPS, unlinesPS, breakAfterNthNewline, breakBeforeNthNewline, ) import Darcs.Patch.FileName ( fn2ps, fn2fp, fp2fn, movedirfilename )@@ -51,10 +50,9 @@ import Darcs.Patch.Core ( Patch(..), Named(..) ) import Darcs.Patch.Prim ( Prim(..), Effect(effect), DirPatchType(..), FilePatchType(..),- try_tok_internal )+ tryTokInternal, showHunk, FileNameFormat(..) ) import Darcs.Patch.Info ( PatchInfo ) import Control.Monad ( when )-import Darcs.SlurpDirectory ( FileContents, Slurpy, withSlurpy, slurp_modfile ) import Darcs.Patch.RegChars ( regChars ) import Darcs.Repository.Prefs ( changePrefval ) import Darcs.Global ( darcsdir )@@ -66,6 +64,9 @@ import Storage.Hashed.Tree( Tree ) import Storage.Hashed.Monad( virtualTreeIO )+import Printer( renderString )++type FileContents = B.ByteString \end{code} @@ -114,11 +115,6 @@ applyToFilepaths :: Apply p => p C(x y) -> [FilePath] -> [FilePath] applyToFilepaths pa fs = withFilePaths fs (apply [] pa) -applyToSlurpy :: (Apply p, Monad m) => p C(x y) -> Slurpy -> m Slurpy-applyToSlurpy p s = case withSlurpy s (apply [] p) of- Left err -> fail err- Right (s', ()) -> return s'- instance Apply p => Apply (Named p) where apply opts (NamedP _ _ p) = apply opts p applyAndTryToFix (NamedP n d p) = mapMaybeSnd (NamedP n d) `fmap` applyAndTryToFix p@@ -131,11 +127,6 @@ applyAndTryToFix (ComP xs) = mapMaybeSnd ComP `fmap` applyAndTryToFix xs applyAndTryToFix x = do mapMaybeSnd ComP `fmap` applyAndTryToFixFL x -forceReplaceSlurpy :: Prim C(x y) -> Slurpy -> Maybe Slurpy-forceReplaceSlurpy (FP f (TokReplace tcs old new)) s =- slurp_modfile f (forceTokReplace tcs old new) s-forceReplaceSlurpy _ _ = bug "Can only forceReplaceSlurpy on a replace."- instance Apply Prim where apply opts (Split ps) = applyFL opts ps apply _ Identity = return ()@@ -144,7 +135,7 @@ apply opts p@(FP _ (Hunk _ _ _)) = applyFL opts (p :>: NilFL) apply _ (FP f (TokReplace t o n)) = mModifyFilePSs f doreplace where doreplace ls =- case mapM (try_tok_internal t (BC.pack o) (BC.pack n)) ls of+ case mapM (tryTokInternal t (BC.pack o) (BC.pack n)) ls of Nothing -> fail $ "replace patch to " ++ fn2fp f ++ " couldn't apply." Just ls' -> return $ map B.concat ls'@@ -190,7 +181,9 @@ hunkmod (Hunk line old new:>:hs) ps = case applyHunkLines [(line,old,new)] ps of Just ps' -> hunkmod hs ps'- Nothing -> fail $ "Error applying hunk to file " ++ fn2fp f+ Nothing -> fail $ "Error applying hunk " +++ (renderString $ showHunk NewFormat f line old new) +++ " to file " ++ fn2fp f ++ ": " ++ show ps hunkmod _ _ = impossible applyFL opts (p:>:ps) = do apply opts p applyFL opts ps@@ -206,14 +199,14 @@ -> B.ByteString -> Maybe [B.ByteString] applyHunks [] ps = Just [ps] applyHunks ((l, [], n):hs) ps- = case break_before_nth_newline (l - 2) ps of+ = case breakBeforeNthNewline (l - 2) ps of (prfix, after_prefix) -> do rest <- applyHunks hs after_prefix return $ intersperse nl (prfix:n) ++ rest where nl = BC.singleton '\n' applyHunks ((l, o, n):hs) ps- = case break_before_nth_newline (l - 2) ps of+ = case breakBeforeNthNewline (l - 2) ps of (prfix, after_prefix) ->- case break_before_nth_newline (length o) after_prefix of+ case breakBeforeNthNewline (length o) after_prefix of (oo, _) | oo /= unlinesPS (B.empty:o) -> fail "applyHunks error" (_, suffix) -> do rest <- applyHunks hs suffix@@ -226,7 +219,7 @@ applyHunkLines [(1, [], n)] ps | B.null ps = Just $ unlinesPS (n++[B.empty]) applyHunkLines hs@((l, o, n):hs') ps = do pss <- case l of- 1 -> case break_after_nth_newline (length o) ps of+ 1 -> case breakAfterNthNewline (length o) ps of Nothing -> if ps == unlinesPS o then return $ intersperse nl n else fail "applyHunkLines: Unexpected hunks"@@ -308,64 +301,64 @@ markupFile x p = mps (effect p) where mps :: FL Prim C(a b) -> (FilePath, MarkedUpFile) -> (FilePath, MarkedUpFile) mps NilFL = id- mps (pp:>:pps) = mps pps . markup_prim x pp+ mps (pp:>:pps) = mps pps . markupPrim x pp -markup_prim :: PatchInfo -> Prim C(x y)+markupPrim :: PatchInfo -> Prim C(x y) -> (FilePath, MarkedUpFile) -> (FilePath, MarkedUpFile)-markup_prim _ (Split NilFL) (f, mk) = (f, mk)-markup_prim n (Split (p:>:ps)) (f, mk) = markup_prim n (Split ps) $- markup_prim n p (f, mk)-markup_prim _ (FP _ AddFile) (f, mk) = (f, mk)-markup_prim _ (FP _ RmFile) (f, mk) = (f, mk)-markup_prim n (FP f' (Hunk line old new)) (f, mk)+markupPrim _ (Split NilFL) (f, mk) = (f, mk)+markupPrim n (Split (p:>:ps)) (f, mk) = markupPrim n (Split ps) $+ markupPrim n p (f, mk)+markupPrim _ (FP _ AddFile) (f, mk) = (f, mk)+markupPrim _ (FP _ RmFile) (f, mk) = (f, mk)+markupPrim n (FP f' (Hunk line old new)) (f, mk) | fn2fp f' /= f = (f, mk)- | otherwise = (f, markup_hunk n line old new mk)-markup_prim name (FP f' (TokReplace t o n)) (f, mk)+ | otherwise = (f, markupHunk n line old new mk)+markupPrim name (FP f' (TokReplace t o n)) (f, mk) | fn2fp f' /= f = (f, mk)- | otherwise = (f, markup_tok name t o n mk)-markup_prim _ (DP _ _) (f, mk) = (f, mk)-markup_prim _ (Move d d') (f, mk) = (fn2fp $ movedirfilename d d' (fp2fn f), mk)-markup_prim _ (ChangePref _ _ _) (f,mk) = (f,mk)-markup_prim _ Identity (f,mk) = (f,mk)-markup_prim n (FP f' (Binary _ _)) (f,mk)+ | otherwise = (f, markupTok name t o n mk)+markupPrim _ (DP _ _) (f, mk) = (f, mk)+markupPrim _ (Move d d') (f, mk) = (fn2fp $ movedirfilename d d' (fp2fn f), mk)+markupPrim _ (ChangePref _ _ _) (f,mk) = (f,mk)+markupPrim _ Identity (f,mk) = (f,mk)+markupPrim n (FP f' (Binary _ _)) (f,mk) | fn2fp f' == f = (f,(BC.pack "Binary file", AddedLine n):mk) | otherwise = (f,mk) -markup_hunk :: PatchInfo -> Int -> [B.ByteString] -> [B.ByteString]+markupHunk :: PatchInfo -> Int -> [B.ByteString] -> [B.ByteString] -> MarkedUpFile -> MarkedUpFile-markup_hunk n l old new ((sf, RemovedLine pi):mk) =- (sf, RemovedLine pi) : markup_hunk n l old new mk-markup_hunk n l old new ((sf, AddedRemovedLine po pn):mk) =- (sf, AddedRemovedLine po pn) : markup_hunk n l old new mk+markupHunk n l old new ((sf, RemovedLine pi):mk) =+ (sf, RemovedLine pi) : markupHunk n l old new mk+markupHunk n l old new ((sf, AddedRemovedLine po pn):mk) =+ (sf, AddedRemovedLine po pn) : markupHunk n l old new mk -markup_hunk name 1 old (n:ns) mk =- (n, AddedLine name) : markup_hunk name 1 old ns mk-markup_hunk n 1 (o:os) [] ((sf, None):mk)- | o == sf = (sf, RemovedLine n) : markup_hunk n 1 os [] mk+markupHunk name 1 old (n:ns) mk =+ (n, AddedLine name) : markupHunk name 1 old ns mk+markupHunk n 1 (o:os) [] ((sf, None):mk)+ | o == sf = (sf, RemovedLine n) : markupHunk n 1 os [] mk | otherwise = [(BC.pack "Error in patch application", AddedLine n)]-markup_hunk n 1 (o:os) [] ((sf, AddedLine nold):mk)- | o == sf = (sf, AddedRemovedLine nold n) : markup_hunk n 1 os [] mk+markupHunk n 1 (o:os) [] ((sf, AddedLine nold):mk)+ | o == sf = (sf, AddedRemovedLine nold n) : markupHunk n 1 os [] mk | otherwise = [(BC.pack "Error in patch application", AddedLine n)]-markup_hunk _ 1 [] [] mk = mk+markupHunk _ 1 [] [] mk = mk -markup_hunk n l old new ((sf, AddedLine pi):mk)- | l > 1 = (sf, AddedLine pi) : markup_hunk n (l-1) old new mk- | l < 1 = (sf, AddedLine pi) : markup_hunk n (l-1) old new mk-markup_hunk n l old new ((sf, None):mk)- | l > 1 = (sf, None) : markup_hunk n (l-1) old new mk- | l < 1 = (sf, None) : markup_hunk n (l-1) old new mk+markupHunk n l old new ((sf, AddedLine pi):mk)+ | l > 1 = (sf, AddedLine pi) : markupHunk n (l-1) old new mk+ | l < 1 = (sf, AddedLine pi) : markupHunk n (l-1) old new mk+markupHunk n l old new ((sf, None):mk)+ | l > 1 = (sf, None) : markupHunk n (l-1) old new mk+ | l < 1 = (sf, None) : markupHunk n (l-1) old new mk -markup_hunk _ _ _ _ [] = []+markupHunk _ _ _ _ [] = [] -markup_hunk _ _ _ _ mk = (BC.pack "Error: ",None) : mk+markupHunk _ _ _ _ mk = (BC.pack "Error: ",None) : mk -markup_tok :: PatchInfo -> String -> String -> String+markupTok :: PatchInfo -> String -> String -> String -> MarkedUpFile -> MarkedUpFile-markup_tok name t ostr nstr mk = concatMap mt mk+markupTok name t ostr nstr mk = concatMap mt mk where o = BC.pack ostr n = BC.pack nstr mt (sf, AddedLine pi) =- case B.concat `fmap` try_tok_internal t o n sf of+ case B.concat `fmap` tryTokInternal t o n sf of Just sf' | sf' == sf -> [(sf, AddedLine pi)] | otherwise -> [(sf, AddedRemovedLine pi name), (sf', AddedLine name)]@@ -404,7 +397,7 @@ \begin{code} applyToPop :: PatchInfo -> FL Prim C(x y) -> Population -> Population applyToPop _ NilFL = id-applyToPop pinf (p:>:ps) = applyToPop pinf ps . applyToPop' pinf p +applyToPop pinf (p:>:ps) = applyToPop pinf ps . applyToPop' pinf p applyToPop' :: PatchInfo -> Prim C(x y) -> Population -> Population
src/Darcs/Patch/Bundle.hs view
@@ -20,19 +20,25 @@ #include "gadts.h" -module Darcs.Patch.Bundle ( hash_bundle, make_bundle, make_bundle2, scan_bundle,- make_context, scan_context,+module Darcs.Patch.Bundle ( hashBundle, makeBundle, makeBundle2, makeBundleN, scanBundle,+ contextPatches, scanContext, patchFilename ) where -import Darcs.Flags ( DarcsFlag, isUnified )+import Data.Char ( isAlpha, toLower, isDigit, isSpace ) import Darcs.Hopefully ( PatchInfoAnd, piap, patchInfoAndPatch, unavailable, hopefully ) import Darcs.Patch ( RepoPatch, Named, showPatch, showContextPatch, readPatch )-import Darcs.Patch.Info ( PatchInfo, readPatchInfo, showPatchInfo, human_friendly, is_tag )-import Darcs.Patch.Set ( PatchSet, SealedPatchSet )+import Darcs.Patch.Info ( PatchInfo, readPatchInfo, showPatchInfo, humanFriendly, isTag )+import Darcs.Patch.Set ( PatchSet(..), Tagged(..), SealedPatchSet )+import Darcs.Patch.Depends ( slightlyOptimizePatchset )+import Darcs.Hopefully( info )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif+import Darcs.Witnesses.Sealed ( flipSeal, FlippedSeal(..) ) import Darcs.Witnesses.Ordered ( RL(..), FL(..), unsafeCoerceP,- reverseFL, (+<+), mapFL, mapFL_FL )+ reverseFL, (+<+), mapFL, mapFL_FL, mapRL ) import Printer ( Doc, renderPS, newline, text, ($$), (<>), vcat, vsep, renderString ) @@ -46,24 +52,32 @@ import Storage.Hashed.Tree( Tree ) import Storage.Hashed.Monad( virtualTreeIO ) -hash_bundle :: RepoPatch p => [PatchInfo] -> FL (Named p) C(x y) -> String-hash_bundle _ to_be_sent = sha1PS $ renderPS+hashBundle :: RepoPatch p => [PatchInfo] -> FL (Named p) C(x y) -> String+hashBundle _ to_be_sent = sha1PS $ renderPS $ vcat (mapFL showPatch to_be_sent) <> newline -make_bundle :: RepoPatch p => [DarcsFlag] -> Tree IO -> [PatchInfo] -> FL (Named p) C(x y) -> IO Doc-make_bundle opts the_s common to_be_sent = make_bundle2 opts the_s common to_be_sent to_be_sent+makeBundleN :: RepoPatch p => Maybe (Tree IO)+ -> PatchSet p C(start x) -> FL (Named p) C(x y) -> IO Doc+makeBundleN the_s (PatchSet ps (Tagged t _ _ :<: _)) to_be_sent =+ makeBundle2 the_s (ps +<+ (t :<: NilRL)) to_be_sent to_be_sent+makeBundleN the_s (PatchSet ps NilRL) to_be_sent =+ makeBundle2 the_s ps to_be_sent to_be_sent --- | In make_bundle2, it is presumed that the two patch sequences are+makeBundle :: RepoPatch p => Maybe (Tree IO) -> RL (PatchInfoAnd p) C(start x)+ -> FL (Named p) C(x y) -> IO Doc+makeBundle the_s common to_be_sent = makeBundle2 the_s common to_be_sent to_be_sent++-- | In makeBundle2, it is presumed that the two patch sequences are -- identical, but that they may be lazily generated. If two different -- patch sequences are passed, a bundle with a mismatched hash will be -- generated, which is not the end of the world, but isn't very useful -- either.-make_bundle2 :: RepoPatch p => [DarcsFlag] -> Tree IO -> [PatchInfo]+makeBundle2 :: RepoPatch p => Maybe (Tree IO) -> RL (PatchInfoAnd p) C(start x) -> FL (Named p) C(x y) -> FL (Named p) C(x y) -> IO Doc-make_bundle2 opts the_s common to_be_sent to_be_sent2 =- do patches <- case (isUnified opts) of- True -> fst `fmap` virtualTreeIO (showContextPatch to_be_sent) the_s- False -> return (vsep $ mapFL showPatch to_be_sent)+makeBundle2 the_s common' to_be_sent to_be_sent2 =+ do patches <- case the_s of+ Just tree -> fst `fmap` virtualTreeIO (showContextPatch to_be_sent) tree+ Nothing -> return (vsep $ mapFL showPatch to_be_sent) return $ format patches where format the_new = text "" $$ text "New patches:"@@ -74,26 +88,27 @@ $$ text "" $$ (vcat $ map showPatchInfo common) $$ text "Patch bundle hash:"- $$ text (hash_bundle common to_be_sent2)+ $$ text (hashBundle common to_be_sent2) $$ text ""+ common = mapRL info common' -scan_bundle :: RepoPatch p => B.ByteString -> Either String (SealedPatchSet p)-scan_bundle ps+scanBundle :: RepoPatch p => B.ByteString -> Either String (SealedPatchSet p C(Origin))+scanBundle ps | B.null ps = Left "Bad patch bundle!" | otherwise =- case silly_lex ps of+ case sillyLex ps of ("New patches:",rest) ->- case get_patches rest of+ case getPatches rest of (Sealed patches, rest') ->- case silly_lex rest' of+ case sillyLex rest' of ("Context:", rest'') -> case getContext rest'' of (cont,maybe_hash) -> case substrPS (BC.pack "Patch bundle hash:") maybe_hash of Just n ->- if hash_bundle cont (mapFL_FL hopefully patches)- == fst (silly_lex $ snd $ silly_lex $+ if hashBundle cont (mapFL_FL hopefully patches)+ == fst (sillyLex $ snd $ sillyLex $ B.drop n maybe_hash) then seal_up_patches patches cont else Left $@@ -107,37 +122,33 @@ ("Context:",rest) -> case getContext rest of (cont, rest') ->- case silly_lex rest' of+ case sillyLex rest' of ("New patches:", rest'') ->- case parse_patches rest'' of+ case parsePatches rest'' of Sealed ps'' -> seal_up_patches ps'' cont (a,_) -> Left $ "Malformed patch bundle: '" ++ a ++ "' is not 'New patches:'" ("-----BEGIN PGP SIGNED MESSAGE-----",rest) ->- scan_bundle $ filter_gpg_dashes rest- (_,rest) -> scan_bundle rest+ scanBundle $ filterGpgDashes rest+ (_,rest) -> scanBundle rest where seal_up_patches :: RepoPatch p => FL (PatchInfoAnd p) C(x y) -> [PatchInfo]- -> Either String (SealedPatchSet p)+ -> Either String (SealedPatchSet p C(Origin)) seal_up_patches xxx yyy = case reverse yyy of- (x:_) | is_tag x ->- Right $ Sealed ((reverseFL xxx +<+ unavailable_patches yyy)- :<: NilRL)- -- The above NilRL isn't quite- -- right, because ther *are*- -- earlier patches, but we- -- can't set this to undefined- -- because there are- -- situations where we look at- -- the rest. :{-- -- bug "No more patches in patch bundle!")- _ -> Right $ Sealed ((reverseFL xxx +<+ unavailable_patches yyy)- :<: NilRL)+ (x:ry) | isTag x ->+ Right $ Sealed (PatchSet+ (reverseFL xxx +<+ unavailablePatches (reverse ry))+ (Tagged (piUnavailable x) Nothing NilRL :<: NilRL))+ _ -> Right $ Sealed (PatchSet (reverseFL xxx +<+ unavailablePatches yyy)+ NilRL)+ -- The above NilRLs aren't quite right, because ther *are*+ -- earlier patches, but we can't set this to undefined+ -- because there are situations where we look at the rest.+ -- :{ --- filter_gpg_dashes is needed because clearsigned patches escape dashes:-filter_gpg_dashes :: B.ByteString -> B.ByteString-filter_gpg_dashes ps =+-- filterGpgDashes is needed because clearsigned patches escape dashes:+filterGpgDashes :: B.ByteString -> B.ByteString+filterGpgDashes ps = unlinesPS $ map drop_dashes $ takeWhile (/= BC.pack "-----END PGP SIGNED MESSAGE-----") $ dropWhile not_context_or_newpatches $ linesPS ps@@ -148,14 +159,14 @@ not_context_or_newpatches s = (s /= BC.pack "Context:") && (s /= BC.pack "New patches:") -unavailable_patches :: RepoPatch p => [PatchInfo] -> RL (PatchInfoAnd p) C(x y)-unavailable_patches [] = unsafeCoerceP NilRL-unavailable_patches (x:xs) = pi_unavailable x :<: unavailable_patches xs+unavailablePatches :: RepoPatch p => [PatchInfo] -> RL (PatchInfoAnd p) C(x y)+unavailablePatches [] = unsafeCoerceP NilRL+unavailablePatches (x:xs) = piUnavailable x :<: unavailablePatches xs -pi_unavailable :: RepoPatch p => PatchInfo -> PatchInfoAnd p C(x y)-pi_unavailable i = (i `patchInfoAndPatch`+piUnavailable :: RepoPatch p => PatchInfo -> PatchInfoAnd p C(x y)+piUnavailable i = (i `patchInfoAndPatch` unavailable ("Patch not stored in patch bundle:\n" ++- renderString (human_friendly i)))+ renderString (humanFriendly i))) getContext :: B.ByteString -> ([PatchInfo],B.ByteString) getContext ps = case readPatchInfo ps of@@ -165,50 +176,61 @@ Nothing -> ([],ps) (-:-) :: a C(x y) -> (Sealed (FL a C(y)),b) -> (Sealed (FL a C(x)),b) p -:- (Sealed ps, r) = (Sealed (p:>:ps), r)-get_patches :: RepoPatch p => B.ByteString -> (Sealed (FL (PatchInfoAnd p) C(x)), B.ByteString)-get_patches ps =+getPatches :: RepoPatch p => B.ByteString -> (Sealed (FL (PatchInfoAnd p) C(x)), B.ByteString)+getPatches ps = case readPatchInfo ps of Nothing -> (Sealed NilFL, ps) Just (pinfo,_) -> case readPatch ps of Nothing -> (Sealed NilFL, ps)- Just (Sealed p, r) -> (pinfo `piap` p) -:- get_patches r-parse_patches :: RepoPatch p => B.ByteString -> Sealed (FL (PatchInfoAnd p) C(x))-parse_patches ps =+ Just (Sealed p, r) -> (pinfo `piap` p) -:- getPatches r+parsePatches :: RepoPatch p => B.ByteString -> Sealed (FL (PatchInfoAnd p) C(x))+parsePatches ps = case readPatchInfo ps of Nothing -> Sealed NilFL Just (pinfo,_) -> case readPatch ps of Nothing -> Sealed NilFL- Just (Sealed p, r) -> ((pinfo `piap` p) :>:) `mapSeal` parse_patches r+ Just (Sealed p, r) -> ((pinfo `piap` p) :>:) `mapSeal` parsePatches r -silly_lex :: B.ByteString -> (String, B.ByteString)-silly_lex ps = (BC.unpack a, b)+sillyLex :: B.ByteString -> (String, B.ByteString)+sillyLex ps = (BC.unpack a, b) where (a, b) = BC.break (== '\n') (dropSpace ps) {--silly_lex ps = (BC.unpack $ BC.takeWhile (/='\n') ps', BC.dropWhile (/='\n') ps')+sillyLex ps = (BC.unpack $ BC.takeWhile (/='\n') ps', BC.dropWhile (/='\n') ps') where ps' = dropSpace ps -} -make_context :: [PatchInfo] -> Doc-make_context common =- text ""- $$ text "Context:"- $$ text ""- $$ (vcat $ map showPatchInfo $ common)- $$ text "" -scan_context :: RepoPatch p => B.ByteString -> PatchSet p C(x)-scan_context ps+contextPatches :: RepoPatch p => PatchSet p C(Origin x) -> FlippedSeal (RL (PatchInfoAnd p)) C(x)+contextPatches set = case slightlyOptimizePatchset set of+ PatchSet ps (Tagged t _ _ :<: _) -> flipSeal (ps +<+ (t :<: NilRL))+ PatchSet ps NilRL -> flipSeal ps++-- are the type witnesses sensible?+scanContext :: RepoPatch p => B.ByteString -> PatchSet p C(Origin x)+scanContext ps | B.null ps = error "Bad context!" | otherwise =- case silly_lex ps of+ case sillyLex ps of ("Context:",rest) -> case getContext rest of- (cont, _) -> unavailable_patches cont :<: NilRL+ (cont@(_:_), _) | isTag (last cont) ->+ PatchSet (unavailablePatches $ init cont)+ (Tagged (piUnavailable $ last cont) Nothing NilRL :<: NilRL)+ (cont, _) -> PatchSet (unavailablePatches cont) NilRL ("-----BEGIN PGP SIGNED MESSAGE-----",rest) ->- scan_context $ filter_gpg_dashes rest- (_,rest) -> scan_context rest+ scanContext $ filterGpgDashes rest+ (_,rest) -> scanContext rest+++patchFilename :: String -> String+patchFilename the_summary = name ++ ".dpatch"+ where name = map safeFileChar the_summary+ safeFileChar c | isAlpha c = toLower c+ | isDigit c = c+ | isSpace c = '-'+ safeFileChar _ = '_'
src/Darcs/Patch/Choices.hs view
@@ -33,40 +33,50 @@ -- the "last" group, which also means that any patches that depend on it -- must be in the "last" group. ----- Internally, a PatchChoices doesn't actually reorder the patches until it is--- asked for the final output (e.g. by 'get_first_choice'). Instead, each--- patch is placed in a state of definitely first, definitely last and--- undecided; undecided leans towards "middle". In case you're wondering--- about the first-middle-last language, it's because in some cases the--- "yes" answers will be last (as is the case for the revert command), and--- in others first (as in record, pull and push).+-- Internally, a PatchChoices doesn't always reorder the patches until+-- it is asked for the final output (e.g. by 'get_first_choice').+-- Instead, each patch is placed in a state of definitely first,+-- definitely last and undecided; undecided leans towards+-- "middle". The patches that are first are commuted to the head+-- immediately, but patches that are middle and last are mixed+-- together. In case you're wondering about the first-middle-last+-- language, it's because in some cases the "yes" answers will be last+-- (as is the case for the revert command), and in others first (as in+-- record, pull and push).+--+-- Some patch marked "middle" may in fact be unselectable because of+-- dependencies: when a patch is marked "last", its dependencies are+-- not updated until patchSlot is called on them. module Darcs.Patch.Choices ( PatchChoices, patchChoices, patchChoicesTps, patchChoicesTpsSub,- patchSlot,- getChoices,+ patchSlot, patchSlot',+ getChoices, refineChoices, separateFirstMiddleFromLast, separateFirstFromMiddleLast, forceFirst, forceFirsts, forceLast, forceLasts, forceMatchingFirst, forceMatchingLast, selectAllMiddles,- makeUncertain, makeEverythingLater,+ makeUncertain, makeEverythingLater, makeEverythingSooner, TaggedPatch, Tag, tag, tpPatch, Slot(..),- substitute,+ substitute ) where -import System.IO.Unsafe ( unsafePerformIO )-import Data.IORef ( newIORef, writeIORef, readIORef )+import Control.Monad.State( State(..) )+ import Darcs.Patch-import Darcs.Patch.Permutations ( commuteWhatWeCanRL )+import Darcs.Patch.Permutations ( commuteWhatWeCanRL, commuteWhatWeCanFL ) import Darcs.Patch.Patchy ( Invert, Commute ) import Darcs.Witnesses.Ordered ( FL(..), RL(..), MyEq, unsafeCompare, EqCheck(..), (:>)(..), (:\/:)(..), (:/\:)(..), (:||:)(..),- zipWithFL, mapFL_FL, mapFL, concatFL,- (+>+), reverseRL, unsafeCoerceP )+ zipWithFL, mapFL_FL, concatFL,+ (+>+), reverseRL, unsafeCoerceP, anyFL ) import Darcs.Witnesses.Sealed ( Sealed2(..) ) +#include "impossible.h"++ -- | 'TG' @mp i@ acts as a temporary identifier to help us keep track of patches -- during the selection process. These are useful for finding patches that -- may have moved around during patch selection (being pushed forwards or@@ -82,20 +92,18 @@ data Tag = TG (Maybe Tag) Integer deriving ( Eq, Ord ) data TaggedPatch p C(x y) = TP Tag (p C(x y)) -data PatchChoice p C(x y) = PC (TaggedPatch p C(x y)) Slot-newtype PatchChoices p C(x y) = PCs (FL (PatchChoice p) C(x y))+-- | The @Bool@ parameter indicates whether the patch has been explicitely+-- selected (or rejected) by the user.+data PatchChoice p C(x y) = PC { pcPatch :: (TaggedPatch p C(x y))+ , choice :: Bool} +data PatchChoices p C(x y) where+ PCs { firsts :: FL (TaggedPatch p) C(x m)+ , lasts :: FL (PatchChoice p) C(m y)} :: PatchChoices p C(x y)+ -- | See module documentation for 'Darcs.Patch.Choices' data Slot = InFirst | InMiddle | InLast -negTag :: Tag -> Tag-negTag (TG k n) = TG k (-n)--invertTag :: Slot -> Slot-invertTag InFirst = InLast-invertTag InLast = InFirst-invertTag t = t- tag :: TaggedPatch p C(x y) -> Tag tag (TP tg _) = tg @@ -126,6 +134,14 @@ merge (TP t1 p1 :\/: TP t2 p2) = case merge (p1 :\/: p2) of p2' :/\: p1' -> TP t2 p2' :/\: TP t1 p1' +instance Commute p => Commute (PatchChoice p) where+ commute (PC p1 c1 :> PC p2 c2) = do p2' :> p1' <- commute (p1 :> p2)+ return (PC p2' c2 :> PC p1' c1)+ listTouchedFiles (PC p _) = listTouchedFiles p+ hunkMatches f (PC p _) = hunkMatches f p+ merge (PC tp1 c1 :\/: PC tp2 c2) = case merge (tp1 :\/: tp2) of+ tp2' :/\: tp1' -> PC tp2' c2 :/\: PC tp1' c1+ patchChoices :: Patchy p => FL p C(x y) -> PatchChoices p C(x y) patchChoices = fst . patchChoicesTps @@ -135,7 +151,7 @@ => Maybe Tag -> FL p C(x y) -> (PatchChoices p C(x y), FL (TaggedPatch p) C(x y)) patchChoicesTpsSub tg ps = let tps = zipWithFL TP (map (TG tg) [1..]) ps- in (PCs $ zipWithFL (flip PC) (repeat InMiddle) tps, tps)+ in (PCs NilFL (mapFL_FL (\tp -> PC tp False) tps), tps) -- |Tag a sequence of patches. patchChoicesTps :: Patchy p => FL p C(x y) -> (PatchChoices p C(x y), FL (TaggedPatch p) C(x y))@@ -144,204 +160,215 @@ instance MyEq p => MyEq (PatchChoice p) where unsafeCompare (PC tp1 _) (PC tp2 _) = unsafeCompare tp1 tp2 -instance Invert p => Invert (PatchChoice p) where- invert (PC tp mf) = PC (invert tp) (invertTag mf)- identity = PC identity InMiddle -instance Commute p => Commute (PatchChoice p) where- commute (PC t1 x1 :> PC t2 x2)- = do t2' :> t1' <- commute (t1 :> t2)- return (PC t2' x2 :> PC t1' x1)- merge (PC t1 x1 :\/: PC t2 x2)- = case merge (t1 :\/: t2) of- t2' :/\: t1' -> PC t2' x2 :/\: PC t1' x1- listTouchedFiles (PC t _) = listTouchedFiles t- hunkMatches f (PC t _) = hunkMatches f t--invertSeq :: (Invert p, Invert q) => (p :> q) C(x y) -> (q :> p) C(y x)-invertSeq (x :> y) = (invert y :> invert x)- separateFirstFromMiddleLast :: Patchy p => PatchChoices p C(x z) -> (FL (TaggedPatch p) :> FL (TaggedPatch p)) C(x z)-separateFirstFromMiddleLast (PCs e) = pull_only_firsts e+separateFirstFromMiddleLast (PCs f l) = f :> mapFL_FL (\ (PC tp _) -> tp) l separateFirstMiddleFromLast :: Patchy p => PatchChoices p C(x z) -> (FL (TaggedPatch p) :> FL (TaggedPatch p)) C(x z)-separateFirstMiddleFromLast (PCs e) = pull_firsts_middles e+separateFirstMiddleFromLast (PCs f l) =+ case pushLasts l of+ (m :> l') -> f +>+ m :> l' +-- | @getChoices@ evaluates a @PatchChoices@ into the first, middle and last sequences+-- by doing the commutes that were needed. getChoices :: Patchy p => PatchChoices p C(x y) -> (FL (TaggedPatch p) :> FL (TaggedPatch p) :> FL (TaggedPatch p)) C(x y)-getChoices (PCs e) = case pull_firsts e of- f :> ml -> case pull_firsts (invert ml) of- l :> m -> f :> mapFL_FL pc2tp (invert m) :> invert l- where pc2tp (PC tp _) = tp--{--This unsafePerformIO hack was reported by Igloo as being necessary for-constant space performance when working with a very large set of changes-(e.g. from an initial import) where the second element of the returned tuple-is expected to be small, and will only be accessed after the entire first-element has been forced.-On a quick scan on 20080729 it seemed like only revert/unrevert actually-make use of both elements of the tuple.-We should (a) add a test case that checks on constant space usage and-(b) clean up this interface and code, perhaps by replacing the FL :> FL-with a custom structure that forces traversal of the first element to-get at the second (but then how would we commute/pattern-match? messy...)--}--pull_firsts_middles :: Patchy p => FL (PatchChoice p) C(x z) -> (FL (TaggedPatch p) :> FL (TaggedPatch p)) C(x z)-pull_firsts_middles easyPC =- let r = unsafePerformIO- $ newIORef (error "pull_firsts_middles called badly")- f :: Patchy p => RL (TaggedPatch p) C(a x) -> FL (PatchChoice p) C(x z) -> FL (TaggedPatch p) C(a d)- f acc NilFL = unsafePerformIO (writeIORef r (reverseRL acc)) `seq` (unsafeCoerceP NilFL)- f acc (PC tp InLast:>:e) = f (tp:<:acc) e- f acc (PC tp _:>:e) = case commuteWhatWeCanRL (acc :> tp) of- more :> tp' :> acc' -> reverseRL more+>+tp':>:f acc' e- xs = f NilRL easyPC- in (xs :> unsafePerformIO (readIORef r))--pull_only_firsts :: Patchy p => FL (PatchChoice p) C(x z) -> (FL (TaggedPatch p) :> FL (TaggedPatch p)) C(x z)-pull_only_firsts easyPC =- let r = unsafePerformIO- $ newIORef (error "pull_only_firsts called badly")- f :: Patchy p => RL (TaggedPatch p) C(a x) -> FL (PatchChoice p) C(x z) -> FL (TaggedPatch p) C(a d)- f acc NilFL = unsafePerformIO (writeIORef r (reverseRL acc)) `seq` (unsafeCoerceP NilFL)- f acc (PC tp InFirst:>:e) = case commuteWhatWeCanRL (acc :> tp) of- more :> tp' :> acc' -> reverseRL more+>+tp':>:f acc' e- f acc (PC tp _:>:e) = f (tp:<:acc) e- xs = f NilRL easyPC- in (xs :> unsafePerformIO (readIORef r))--{--pull_middles_lasts :: EasyPC p -> ([TaggedPatch p], [TaggedPatch p])-pull_middles_lasts easyPC =- let r = unsafePerformIO- $ newIORef (error "pull_middles_lasts called badly")- f acc [] = unsafePerformIO (writeIORef r (reverse acc)) `seq` []- f acc (PC tp (Just True):e) = f (tp:acc) e- f acc (PC (TP t p) _:e) = case commute_up_list p acc of- (acc', p') -> TP t p':f acc' e- xs = f [] easyPC- in (xs, unsafePerformIO (readIORef r))--}----pull_only_lasts :: EasyPC p -> ([TaggedPatch p], [TaggedPatch p])---pull_only_lasts easyPC =--- let r = unsafePerformIO--- $ newIORef (error "pull_only_lasts called badly")--- f acc [] = unsafePerformIO (writeIORef r (reverse acc)) `seq` []--- f acc (PC (TP t p) (Just False):e) = case commute_up_list p acc of--- (acc', p') -> TP t p':f acc' e--- f acc (PC tp _:e) = f (tp:acc) e--- xs = f [] easyPC--- in (xs, unsafePerformIO (readIORef r))--pull_firsts :: Patchy p => FL (PatchChoice p) C(x z) -> (FL (TaggedPatch p) :> FL (PatchChoice p)) C(x z)-pull_firsts e = case pull_first e of- Nothing -> (NilFL :> e)- Just (p:>e') -> case pull_firsts e' of- (ps:>e'') -> (p:>:ps :> e'')--pull_lasts :: Patchy p => FL (PatchChoice p) C(x y) -> (FL (PatchChoice p) :> FL (TaggedPatch p)) C(x y)-pull_lasts e = invertSeq $ pull_firsts $ invert e+getChoices (PCs f l) =+ case pushLasts l of+ (m :> l') -> f :> m :> l' -pull_first :: Patchy p => FL (PatchChoice p) C(x z) -> Maybe ((TaggedPatch p :> FL (PatchChoice p)) C(x z))-pull_first NilFL = Nothing-pull_first (PC tp InFirst:>:e) = Just (tp :> e)-pull_first (PC (TP t p) InLast:>:e) =- case pull_first e of- Just (TP t2 p2 :> e') ->- case commute (p:>p2) of- Just (p2':>p') -> Just (TP t2 p2' :> PC (TP t p') InLast:>:e')- Nothing -> error "Aaack fixme!"- Nothing -> Nothing-pull_first (PC tp@(TP t p) InMiddle:>:e) =- case pull_first e of- Just (TP t2 p2 :> e') ->- case commute (p:>p2) of- Just (p2':>p') -> Just (TP t2 p2' :> (PC (TP t p') InMiddle:>:e'))- Nothing -> Just (tp :> PC (TP (negTag t2) p2) InFirst:>:e')- Nothing -> Nothing+pushLasts :: Patchy p => FL (PatchChoice p) C(x y)+ -> (FL (TaggedPatch p) :> FL (TaggedPatch p)) C(x y)+pushLasts NilFL = NilFL :> NilFL+pushLasts (PC tp False :>: pcs) =+ case pushLasts pcs of+ (m :> l) -> (tp :>: m) :> l+pushLasts (PC tp True :>: pcs) =+ case pushLasts pcs of+ (m :> l) ->+ case commuteWhatWeCanFL (tp :> m) of+ (m' :> tp' :> deps) -> m' :> (tp' :>: deps +>+ l) -patchSlot :: forall p C(a b x y). TaggedPatch p C(a b) -> PatchChoices p C(x y) -> Slot-patchSlot tp (PCs e) = ipf e- where ipf :: FL (PatchChoice p) C(u v) -> Slot- ipf (PC a mb:>:e') | tag a == tag tp = mb- | otherwise = ipf e'- -- actually, the following should be impossible, but this is a reasonable answer- ipf NilFL = InLast+-- | @refineChoices act@ performs @act@ on the middle part of a sequence+-- of choices, in order to hopefully get more patches into the @first@ and+-- @last@ parts of a @PatchChoices@.+refineChoices :: (Patchy p, Monad m, Functor m) =>+ (FORALL(u v) FL (TaggedPatch p) C(u v) ->+ PatchChoices p C(u v) ->+ m (PatchChoices p C(u v)))+ -> PatchChoices p C(x y) -> m (PatchChoices p C(x y))+refineChoices act ps =+ case getChoices ps of+ (f :> m :> l) -> do+ let mchoices = PCs NilFL . mapFL_FL (flip PC False) $ m+ (PCs f' l') <- act m mchoices+ return . PCs (f +>+ f') $ l' +>+ mapFL_FL (flip PC True) l --- | 'setSimplys' @ts s ps@ assigns all patches in @ps@ with a tag in @ts@ to slot @s@--- (and any other patch to slot 'InMiddle')-setSimplys :: [Tag] -> Slot -> FL (PatchChoice p) C(x y) -> FL (PatchChoice p) C(x y)-setSimplys ts s e = mapFL_FL ch e- where ch (PC tp@(TP t _) _)- | t `elem` ts = PC tp s- | otherwise = PC tp InMiddle+patchSlot :: forall p C(a b x y). Patchy p => TaggedPatch p C(a b)+ -> PatchChoices p C(x y) -> (Slot, PatchChoices p C(x y))+patchSlot (TP t _) pc@(PCs f l) =+ if foundIn f+ then (InFirst, pc)+ else psLast f NilRL NilRL l+ where+ foundIn = anyFL ((== t) . tag)+ psLast :: FORALL(m b l)+ FL (TaggedPatch p) C(x m) ->+ RL (TaggedPatch p) C(m b) ->+ RL (TaggedPatch p) C(b l) ->+ FL (PatchChoice p) C(l y) ->+ (Slot, PatchChoices p C(x y))+ psLast firsts middles bubble (PC tp True :>: ls)+ | tag tp == t = (InLast+ , PCs { firsts = firsts+ , lasts = settleM middles+ +>+ settleB bubble+ +>+ PC tp True :>: ls})+ psLast firsts middles bubble (PC tp False :>: ls)+ | tag tp == t =+ case commuteRL (bubble :> tp) of+ Just (tp' :> bubble') -> (InMiddle,+ PCs { firsts = firsts+ , lasts = settleM middles+ +>+ PC tp' False+ :>: settleB bubble'+ +>+ ls})+ Nothing -> (InLast,+ PCs { firsts = firsts+ , lasts = settleM middles+ +>+ settleB bubble+ +>+ PC tp True+ :>: ls})+ psLast firsts middles bubble (PC tp True :>: ls) =+ psLast firsts middles (tp :<: bubble) ls+ psLast firsts middles bubble (PC tp False :>: ls) =+ case commuteRL (bubble :> tp) of+ Just (tp' :> bubble') -> psLast firsts (tp' :<: middles) bubble' ls+ Nothing -> psLast firsts middles (tp :<: bubble) ls+ psLast _ _ _ NilFL = impossible+ settleM middles = mapFL_FL (\tp -> PC tp False) $ reverseRL middles+ settleB bubble = mapFL_FL (\tp -> PC tp True) $ reverseRL bubble +patchSlot' :: Patchy p =>+ TaggedPatch p C(a b) -> State (PatchChoices p C(x y)) Slot+patchSlot' = State . patchSlot -m2ids :: (FORALL(x y) TaggedPatch p C(x y) -> Bool) -> FL (PatchChoice p) C(a b) -> [Tag]-m2ids m (PC tp@(TP t _) _:>:e)- | m tp = t:m2ids m e- | otherwise = m2ids m e-m2ids _ NilFL = []+forceMatchingFirst :: forall p C(a b). Patchy p =>+ ( FORALL(x y) TaggedPatch p C(x y) -> Bool)+ -> PatchChoices p C(a b)+ -> PatchChoices p C(a b)+forceMatchingFirst pred (PCs f l) =+ fmfLasts f NilRL l+ where+ fmfLasts :: FL (TaggedPatch p) C(a m)+ -> RL (PatchChoice p) C(m n)+ -> FL (PatchChoice p) C(n b)+ -> PatchChoices p C(a b)+ fmfLasts f l1 (a :>: l2)+ | pred_pc a =+ case commuteWhatWeCanRL (l1 :> a) of+ (deps :> a' :> l1') ->+ let+ f' = f +>+ mapFL_FL pcPatch (reverseRL deps) +>+ (pcPatch a' :>: NilFL)+ in fmfLasts f' l1' l2+ fmfLasts f l1 (a :>: l2) = fmfLasts f (a :<: l1) l2+ fmfLasts f l1 NilFL = PCs { firsts = f+ , lasts = reverseRL l1 }+ pred_pc :: FORALL(x y) PatchChoice p C(x y) -> Bool+ pred_pc (PC tp _) = pred tp -forceMatchingFirst :: Patchy p => (FORALL(x y) TaggedPatch p C(x y) -> Bool)- -> PatchChoices p C(a b) -> PatchChoices p C(a b)-forceMatchingFirst m (PCs e) =- let thd (PC (TP t _) _) = t- xs = m2ids m e- not_needed = case pull_firsts $ setSimplys xs InFirst e of- _ :> rest -> mapFL thd rest- ch pc@(PC tp@(TP t _) _)- | t `elem` not_needed = pc- | otherwise = PC tp InFirst- in PCs $ mapFL_FL ch e+forceFirsts :: Patchy p => [Tag] -> PatchChoices p C(a b)+ -> PatchChoices p C(a b)+forceFirsts ps = forceMatchingFirst ((`elem` ps) . tag) -forceFirsts :: Patchy p => [Tag] -> PatchChoices p C(x y) -> PatchChoices p C(x y)-forceFirsts ps pc = forceMatchingFirst ((`elem` ps) . tag) pc+forceFirst :: Patchy p => Tag -> PatchChoices p C(a b)+ -> PatchChoices p C(a b)+forceFirst p = forceMatchingFirst ((== p) . tag)+--TODO: stop after having seen the patch we want to force first -forceFirst :: Patchy p => Tag -> PatchChoices p C(x y) -> PatchChoices p C(x y)-forceFirst p pc = forceMatchingFirst ((== p) . tag) pc+selectAllMiddles :: forall p C(x y). Patchy p => Bool+ -> PatchChoices p C(x y) -> PatchChoices p C(x y)+selectAllMiddles True (PCs f l) = PCs f (mapFL_FL g l)+ where g (PC tp _) = PC tp True+selectAllMiddles False (PCs f l) = samf f NilRL NilRL l+ where+ samf :: FORALL(m1 m2 m3)+ FL (TaggedPatch p) C(x m1) ->+ RL (TaggedPatch p) C(m1 m2) ->+ RL (PatchChoice p) C(m2 m3) ->+ FL (PatchChoice p) C(m3 y) ->+ PatchChoices p C(x y)+ samf f1 f2 l1 (pc@(PC tp False) :>: l2) =+ case commuteRL (l1 :> pc) of+ Nothing -> samf f1 f2 (PC tp True :<: l1) l2+ Just ((PC tp' _) :> l1') -> samf f1 (tp' :<: f2) l1' l2+ samf f1 f2 l1 (PC tp True :>: l2) = samf f1 f2 (PC tp True :<: l1) l2+ samf f1 f2 l1 NilFL = PCs (f1 +>+ reverseRL f2) (reverseRL l1) -selectAllMiddles :: Patchy p => Bool -> PatchChoices p C(x y) -> PatchChoices p C(x y)-selectAllMiddles b (PCs e) = PCs (mapFL_FL f e)- where f (PC tp InMiddle) = PC tp (if b then InLast else InFirst)- f pc = pc+forceMatchingLast :: Patchy p => (FORALL(x y) TaggedPatch p C(x y) -> Bool)+ -> PatchChoices p C(a b)+ -> PatchChoices p C(a b)+forceMatchingLast pred (PCs f l) = do+ fmlFirst pred True NilRL f l -reverse_pc :: Patchy p => PatchChoices p C(x y) -> PatchChoices p C(y x)-reverse_pc (PCs e) = PCs $ invert e+fmlFirst :: forall p C(a b m1 m2) . Patchy p =>+ (FORALL(x y) TaggedPatch p C(x y) -> Bool) -> Bool+ -> RL (TaggedPatch p) C(a m1)+ -> FL (TaggedPatch p) C(m1 m2)+ -> FL (PatchChoice p) C(m2 b)+ -> PatchChoices p C(a b)+fmlFirst pred b f1 (a :>: f2) l+ | pred a =+ case commuteWhatWeCanFL (a :> f2) of+ (f2' :> a' :> deps) ->+ let+ l' = mapFL_FL (\tp -> PC tp b) (a' :>: deps) +>+ l+ in+ fmlFirst pred b f1 f2' l'+fmlFirst pred b f1 (a :>: f2) l = fmlFirst pred b (a :<: f1) f2 l+fmlFirst pred b f1 NilFL l = PCs { firsts = reverseRL f1+ , lasts = mapFL_FL ch l}+ where ch (PC tp c) = (PC tp (if pred tp then b else c) ) -forceMatchingLast :: Patchy p => (FORALL(x y) TaggedPatch p C(x y) -> Bool)+forceLasts :: Patchy p => [Tag] -> PatchChoices p C(a b) -> PatchChoices p C(a b)-forceMatchingLast m (PCs e) =- let thd (PC (TP t _) _) = t- xs = m2ids m e- not_needed = case pull_lasts $ setSimplys xs InLast e of- rest :> _ -> mapFL thd rest- ch pc@(PC tp@(TP t _) _)- | t `elem` not_needed = pc- | otherwise = PC tp InLast- in PCs $ mapFL_FL ch e--forceLast :: Patchy p => Tag -> PatchChoices p C(x y) -> PatchChoices p C(x y)-forceLast p pc = reverse_pc $ forceFirst p $ reverse_pc pc+forceLasts ps = forceMatchingLast ((`elem` ps) . tag) -forceLasts :: Patchy p => [Tag] -> PatchChoices p C(x y) -> PatchChoices p C(x y)-forceLasts ps pc = reverse_pc $ forceFirsts ps $ reverse_pc pc+forceLast :: Patchy p => Tag+ -> PatchChoices p C(a b) -> PatchChoices p C(a b)+forceLast p = forceMatchingLast ((== p) . tag) -makeUncertain :: Patchy p => Tag -> PatchChoices p C(x y) -> PatchChoices p C(x y)-makeUncertain t (PCs e) = PCs $ mapFL_FL ch e- where ch pc@(PC x _) = if t == tag x then PC x InMiddle else pc+makeUncertain :: Patchy p => Tag -> PatchChoices p C(a b) -> PatchChoices p C(a b)+makeUncertain t (PCs f l) = fmlFirst ((== t) . tag) False NilRL f l makeEverythingLater :: Patchy p => PatchChoices p C(x y) -> PatchChoices p C(x y)-makeEverythingLater (PCs e) = PCs $ mapFL_FL ch e- where ch (PC tp InMiddle) = PC tp InLast- ch (PC tp InFirst) = PC tp InMiddle- ch x = x+makeEverythingLater (PCs f l) =+ let m = mapFL_FL (\tp -> PC tp False) f+ l' = mapFL_FL (\(PC tp _) -> PC tp True) l+ in+ PCs NilFL $ m +>+ l' +makeEverythingSooner :: forall p C(x y).+ Patchy p => PatchChoices p C(x y) -> PatchChoices p C(x y)+makeEverythingSooner (PCs f l) =+ case mes NilRL NilRL l+ of (m :> l) ->+ PCs (f +>+ m) l+ where+ mes :: FORALL(m1 m2 m3)+ RL (TaggedPatch p) C(m1 m2) ->+ RL (TaggedPatch p) C(m2 m3) ->+ FL (PatchChoice p) C(m3 y) ->+ (FL (TaggedPatch p) :> FL (PatchChoice p)) C(m1 y)+ mes middle bubble (PC tp True :>: ls) = mes middle (tp :<: bubble) ls+ mes middle bubble (PC tp False :>: ls) =+ case commuteRL (bubble :> tp) of+ Nothing -> mes middle (tp :<: bubble) ls+ Just (tp' :> bubble') -> mes (tp' :<: middle) bubble' ls+ mes middle bubble NilFL = (reverseRL middle) :> mapFL_FL (\tp -> PC tp False) (reverseRL bubble)+ -- | 'substitute' @(a :||: bs)@ @pcs@ replaces @a@ with @bs@ in @pcs@ preserving the choice -- associated with @a@ substitute :: forall p C(x y)@@ -349,8 +376,14 @@ => Sealed2 (TaggedPatch p :||: FL (TaggedPatch p)) -> PatchChoices p C(x y) -> PatchChoices p C(x y)-substitute (Sealed2 (tp :||: new_tps)) (PCs pcs) = PCs (concatFL (mapFL_FL translate pcs))- where translate :: PatchChoice p C(a b) -> FL (PatchChoice p) C(a b)- translate (PC tp' c)- | IsEq <- compareTags tp tp' = mapFL_FL (flip PC c) new_tps- | otherwise = PC tp' c :>: NilFL+substitute (Sealed2 (tp :||: new_tps)) (PCs f l) =+ PCs (concatFL $ mapFL_FL substTp f) (concatFL $ mapFL_FL substPc l)+ where+ substTp :: TaggedPatch p C(a b) -> FL (TaggedPatch p) C(a b)+ substTp tp'+ | IsEq <- compareTags tp tp' = new_tps+ | otherwise = tp' :>: NilFL+ substPc :: PatchChoice p C(a b) -> FL (PatchChoice p) C(a b)+ substPc (PC tp' c)+ | IsEq <- compareTags tp tp' = mapFL_FL (flip PC c) new_tps+ | otherwise = PC tp' c :>: NilFL
src/Darcs/Patch/Commute.lhs view
@@ -25,67 +25,56 @@ module Darcs.Patch.Commute ( fromPrims, modernizePatch,-#ifndef GADT_WITNESSES merge, elegantMerge, merger, unravel,-#endif- public_unravel, mangle_unravelled,+ publicUnravel, mangleUnravelled, CommuteFunction, Perhaps(..), -- for other commutes: toMaybe, ) where -import Control.Monad ( MonadPlus, mplus, msum, mzero )+import Control.Monad ( MonadPlus, mplus, msum, mzero, guard ) import Darcs.Patch.FileName ( FileName, fn2fp, fp2fn )-import Darcs.Patch.Info ( invert_name, idpatchinfo )+import Darcs.Patch.Info ( invertName, idpatchinfo ) import Darcs.Patch.Patchy ( Commute(..), Invert(..), toFwdCommute, toRevCommute ) import Darcs.Patch.Core ( Patch(..), Named(..),-#ifndef GADT_WITNESSES flattenFL, isMerger,-#endif- merger_undo,- join_patchesFL )+ mergerUndo,+ joinPatchesFL ) import Darcs.Patch.Prim ( Prim(..), FromPrims(..), Conflict(..), Effect(..), is_filepatch, sortCoalesceFL,-#ifndef GADT_WITNESSES FilePatchType(..), DirPatchType(..),-#else- FilePatchType(Hunk),-#endif primIsHunk, modernizePrim ) import qualified Data.ByteString.Char8 as BC (pack, last) import qualified Data.ByteString as B (null, ByteString) import Data.Maybe ( isJust ) import Data.List ( intersperse, sort )-#ifndef GADT_WITNESSES-import Darcs.Patch.Permutations ( head_permutationsRL, head_permutationsFL )+import Darcs.Patch.Permutations ( headPermutationsRL, simpleHeadPermutationsFL ) import Printer ( text, vcat, ($$) ) import Darcs.Patch.Patchy ( invertRL ) import Darcs.Patch.Show ( showPatch_ )-import Data.List ( nubBy )-import Darcs.Witnesses.Sealed ( unsafeUnseal )-#endif+import Data.List ( nub, nubBy )+import Darcs.Witnesses.Sealed ( unsafeUnseal, unsafeUnsealFlipped ) import Darcs.Utils ( nubsort ) #include "impossible.h"-import Darcs.Witnesses.Sealed ( Sealed(..), mapSeal )+import Darcs.Witnesses.Sealed ( Sealed(..), mapSeal, unseal, FlippedSeal(..), mapFlipped, unsealFlipped ) import Darcs.Witnesses.Ordered ( mapFL, mapFL_FL, unsafeCoerceP,+ unsafeCoercePStart, unsafeCoercePEnd, FL(..), RL(..), (:/\:)(..), (:<)(..), (:\/:)(..), (:>)(..),-#ifndef GADT_WITNESSES lengthFL, mapRL,-#endif reverseFL, reverseRL, concatFL,- MyEq, unsafeCompare+ MyEq, unsafeCompare, EqCheck(..), (=\/=) ) --import Darcs.ColorPrinter ( traceDoc ) --import Printer ( greenText ) \end{code}- + \section{Commuting patches} \subsection{Composite patches}@@ -167,9 +156,8 @@ toPerhaps (Just x) = Succeeded x toPerhaps Nothing = Failed -#ifndef GADT_WITNESSES-clever_commute :: CommuteFunction -> CommuteFunction-clever_commute c (p1:<p2) =+cleverCommute :: CommuteFunction -> CommuteFunction+cleverCommute c (p1:<p2) = case c (p1 :< p2) of Succeeded x -> Succeeded x Failed -> Failed@@ -177,15 +165,14 @@ Succeeded (p1' :< p2') -> Succeeded (invert p2' :< invert p1') Failed -> Failed Unknown -> Unknown-#endif -speedy_commute :: CommuteFunction-speedy_commute (p1 :< p2) -- Deal with common case quickly!+speedyCommute :: CommuteFunction+speedyCommute (p1 :< p2) -- Deal with common case quickly! | p1_modifies /= Nothing && p2_modifies /= Nothing && p1_modifies /= p2_modifies = Succeeded (unsafeCoerceP p2 :< unsafeCoerceP p1) | otherwise = Unknown- where p1_modifies = is_filepatch_merger p1- p2_modifies = is_filepatch_merger p2+ where p1_modifies = isFilepatchMerger p1+ p2_modifies = isFilepatchMerger p2 instance Commute p => Commute (Named p) where commute (NamedP n1 d1 p1 :> NamedP n2 d2 p2) =@@ -202,9 +189,10 @@ instance Conflict p => Conflict (Named p) where listConflictedFiles (NamedP _ _ p) = listConflictedFiles p resolveConflicts (NamedP _ _ p) = resolveConflicts p+ isInconsistent (NamedP _ _ p) = isInconsistent p -everything_else_commute :: MaybeCommute -> CommuteFunction-everything_else_commute c x = eec x+everythingElseCommute :: MaybeCommute -> CommuteFunction+everythingElseCommute c x = eec x where eec :: CommuteFunction eec (PP px :< PP py) = toPerhaps $ do x' :> y' <- commute (py :> px)@@ -225,10 +213,8 @@ return (p':<:ps' :< p2'') eec _xx = msum [-#ifndef GADT_WITNESSES- clever_commute commute_recursive_merger _xx- ,clever_commute other_commute_recursive_merger _xx-#endif+ cleverCommute commuteRecursiveMerger _xx+ ,cleverCommute otherCommuteRecursiveMerger _xx ] {-@@ -242,42 +228,39 @@ then commutex (B^-1, A^-1) == Just (A'^-1, B'^-1) -} -#ifndef GADT_WITNESSES-merger_commute :: (Patch :< Patch) -> Perhaps (Patch :< Patch)-merger_commute (Merger _ _ p1 p2 :< pA)- | unsafeCompare pA p1 = Succeeded (merger "0.0" p2 p1 :< p2)- | unsafeCompare pA (invert (merger "0.0" p2 p1)) = Failed-merger_commute (Merger _ _+unsafeMerger :: String -> Patch C(x y) -> Patch C(x z) -> Patch C(a b)+unsafeMerger x p1 p2 = unsafeCoercePStart $ unsafeUnseal $ merger x p1 p2++mergerCommute :: (Patch :< Patch) C(x y) -> Perhaps ((Patch :< Patch) C(x y))+mergerCommute (Merger _ _ p1 p2 :< pA)+ | unsafeCompare pA p1 = Succeeded (unsafeMerger "0.0" p2 p1 :< unsafeCoercePStart p2)+ | unsafeCompare pA (invert (unsafeMerger "0.0" p2 p1)) = Failed+mergerCommute (Merger _ _ (Merger _ _ c b) (Merger _ _ c' a) :< Merger _ _ b' c'') | unsafeCompare b' b && unsafeCompare c c' && unsafeCompare c c'' =- Succeeded (merger "0.0" (merger "0.0" b a) (merger "0.0" b c) :<- merger "0.0" b a)-merger_commute _ = Unknown-#endif+ Succeeded (unsafeMerger "0.0" (unsafeMerger "0.0" b (unsafeCoercePStart a)) (unsafeMerger "0.0" b c) :<+ unsafeMerger "0.0" b (unsafeCoercePStart a))+mergerCommute _ = Unknown instance Commute Patch where merge (y :\/: z) =-#ifndef GADT_WITNESSES- case actual_merge (y:\/:z) of- y' -> case commute (z :> y') of+ case actualMerge (y:\/:z) of+ Sealed y' -> case commute (z :> y') of Nothing -> bugDoc $ text "merge_patches bug" $$ showPatch_ y $$ showPatch_ z $$ showPatch_ y'- Just (_ :> z') -> z' :/\: y'-#else- case elegantMerge (y:\/:z) of- Just (z' :/\: y') -> z' :/\: y'- Nothing -> undefined-#endif+ Just (_ :> z') -> -- actualMerge returns one arm of a+ -- merge result, so commuting then gives+ -- us the other arm but we have to assert+ -- that the starting context is correct+ unsafeCoercePStart z' :/\: y' commute x = toMaybe $ msum- [toFwdCommute speedy_commute x,-#ifndef GADT_WITNESSES- toFwdCommute (clever_commute merger_commute) x,-#endif- toFwdCommute (everything_else_commute (toRevCommute commute)) x+ [toFwdCommute speedyCommute x,+ toFwdCommute (cleverCommute mergerCommute) x,+ toFwdCommute (everythingElseCommute (toRevCommute commute)) x ] -- Recurse on everything, these are potentially spoofed patches listTouchedFiles (ComP ps) = nubsort $ concat $ mapFL listTouchedFiles ps@@ -291,70 +274,60 @@ hunkMatches f c@(Regrem _ _ _ _) = hunkMatches f $ invert c hunkMatches f (PP p) = hunkMatches f p -commute_no_merger :: MaybeCommute-commute_no_merger x =-#ifndef GADT_WITNESSES- toMaybe $ msum [speedy_commute x,- everything_else_commute commute_no_merger x]-#else- bug "commute_no_merger undefined when compiled with GADTs" x-#endif+commuteNoMerger :: MaybeCommute+commuteNoMerger x =+ toMaybe $ msum [speedyCommute x,+ everythingElseCommute commuteNoMerger x] -is_filepatch_merger :: Patch C(x y) -> Maybe FileName-is_filepatch_merger (PP p) = is_filepatch p-is_filepatch_merger (Merger _ _ p1 p2) = do- f1 <- is_filepatch_merger p1- f2 <- is_filepatch_merger p2+isFilepatchMerger :: Patch C(x y) -> Maybe FileName+isFilepatchMerger (PP p) = is_filepatch p+isFilepatchMerger (Merger _ _ p1 p2) = do+ f1 <- isFilepatchMerger p1+ f2 <- isFilepatchMerger p2 if f1 == f2 then return f1 else Nothing-is_filepatch_merger (Regrem und unw p1 p2)- = is_filepatch_merger (Merger und unw p1 p2)-is_filepatch_merger (ComP _) = Nothing+isFilepatchMerger (Regrem und unw p1 p2)+ = isFilepatchMerger (Merger und unw p1 p2)+isFilepatchMerger (ComP _) = Nothing -#ifndef GADT_WITNESSES-commute_recursive_merger :: (Patch :< Patch) -> Perhaps (Patch :< Patch)-commute_recursive_merger (p@(Merger _ _ p1 p2) :< pA) = toPerhaps $+commuteRecursiveMerger :: (Patch :< Patch) C(x y) -> Perhaps ((Patch :< Patch) C(x y))+commuteRecursiveMerger (p@(Merger _ _ p1 p2) :< pA) = toPerhaps $ do (_ :> pA') <- commute (pA :> undo) commute (pA' :> invert undo)- (_ :> pAmid) <- commute (pA :> invert p1)+ (_ :> pAmid) <- commute (pA :> unsafeCoercePStart (invert p1)) (p1' :> pAx) <- commute (pAmid :> p1)- assert (pAx `unsafeCompare` pA)+ guard (pAx `unsafeCompare` pA) (p2' :> _) <- commute (pAmid :> p2) (p2o :> _) <- commute (invert pAmid :> p2')- assert (p2o `unsafeCompare` p2)+ guard (p2o `unsafeCompare` p2) let p' = if unsafeCompare p1' p1 && unsafeCompare p2' p2- then p- else merger "0.0" p1' p2'- undo' = merger_undo p'+ then unsafeCoerceP p+ else unsafeMerger "0.0" p1' p2'+ undo' = mergerUndo p' (pAo :> _) <- commute (undo' :> pA')- assert (pAo `unsafeCompare` pA)+ guard (pAo `unsafeCompare` pA) return (pA' :< p')- where undo = merger_undo p-commute_recursive_merger _ = Unknown+ where undo = mergerUndo p+commuteRecursiveMerger _ = Unknown -other_commute_recursive_merger :: (Patch :< Patch) -> Perhaps (Patch :< Patch)-other_commute_recursive_merger (pA':< p_old@(Merger _ _ p1' p2')) =+otherCommuteRecursiveMerger :: (Patch :< Patch) C(x y) -> Perhaps ((Patch :< Patch) C(x y))+otherCommuteRecursiveMerger (pA':< p_old@(Merger _ _ p1' p2')) = toPerhaps $- do (pA :> _) <- commute (merger_undo p_old :> pA')- (pAmid :> p1) <- commute (p1' :> pA)+ do (pA :> _) <- commute (mergerUndo p_old :> pA')+ (pAmid :> p1) <- commute (unsafeCoercePEnd p1' :> pA) (_ :> pAmido) <- commute (pA :> invert p1)- assert (pAmido `unsafeCompare` pAmid)+ guard (pAmido `unsafeCompare` pAmid) (p2 :> _) <- commute (invert pAmid :> p2') (p2o' :> _) <- commute (pAmid :> p2)- assert (p2o' `unsafeCompare` p2')+ guard (p2o' `unsafeCompare` p2') let p = if p1 `unsafeCompare` p1' && p2 `unsafeCompare` p2'- then p_old- else merger "0.0" p1 p2- undo = merger_undo p- assert (not $ pA `unsafeCompare` p1) -- special case here...+ then unsafeCoerceP p_old+ else unsafeMerger "0.0" p1 p2+ undo = mergerUndo p+ guard (not $ pA `unsafeCompare` p1) -- special case here... (_ :> pAo') <- commute (pA :> undo)- assert (pAo' `unsafeCompare` pA')+ guard (pAo' `unsafeCompare` pA') return (p :< pA)-other_commute_recursive_merger _ = Unknown--assert :: Bool -> Maybe ()-assert False = Nothing-assert True = Just ()-#endif+otherCommuteRecursiveMerger _ = Unknown type CommuteFunction = FORALL(x y) (Patch :< Patch) C(x y) -> Perhaps ((Patch :< Patch) C(x y)) type MaybeCommute = FORALL(x y) (Patch :< Patch) C(x y) -> Maybe ((Patch :< Patch) C(x y))@@ -423,9 +396,8 @@ elegantMerge (p1 :\/: p2) = do p1' :> ip2' <- commute (invert p2 :> p1) p1o :> _ <- commute (p2 :> p1')- if unsafeCompare p1o p1 -- should be a redundant check- then return $ invert ip2' :/\: p1'- else Nothing+ guard $ unsafeCompare p1o p1 -- should be a redundant check+ return $ invert ip2' :/\: p1' \end{code} @@ -448,33 +420,58 @@ situation is considerably more complicated. \begin{code}-#ifndef GADT_WITNESSES-actual_merge :: (Patch :\/: Patch) -> Patch-actual_merge (ComP the_p1s :\/: ComP the_p2s) =- join_patchesFL $ mc the_p1s the_p2s- where mc :: FL Patch -> FL Patch -> FL Patch- mc NilFL (_:>:_) = NilFL- mc p1s NilFL = p1s- mc p1s (p2:>:p2s) = mc (merge_patches_after_patch p1s p2) p2s-actual_merge (ComP p1s :\/: p2) = seq p2 $- join_patchesFL $ merge_patches_after_patch p1s p2-actual_merge (p1 :\/: ComP p2s) = seq p1 $ merge_patch_after_patches p1 p2s+{-+A note about mergers and type witnesses+--------------------------------------- -actual_merge (p1 :\/: p2) = case elegantMerge (p1:\/:p2) of- Just (_ :/\: p1') -> p1'- Nothing -> merger "0.0" p2 p1+The merger code predates the introduction of type witnesses, and because+of its complexity has proved the hardest part of the codebase to retrofit.+Attempting to do this has exposed various places where the code behaves+oddly (e.g. 'putBefore' below); these are likely to be bugs but fixing+them would be potentially disruptive and dangerous as it might change+the existing merge behaviour and thus break existing repositories. -merge_patch_after_patches :: Patch -> FL Patch -> Patch-merge_patch_after_patches p (p1:>:p1s) =- merge_patch_after_patches (actual_merge (p:\/:p1)) p1s-merge_patch_after_patches p NilFL = p+As a result the addition of witnesses to this code has required the+liberal use of unsafe operators. In effect, witnesses bring no safety+in this area; the sole purpose of adding them here was to allow this+code to run as part of a codebase that uses witnesses everywhere else. -merge_patches_after_patch :: FL Patch -> Patch -> FL Patch-merge_patches_after_patch p2s p =- case commute (join_patchesFL p2s :> merge_patch_after_patches p p2s) of- Just (_ :> ComP p2s') -> p2s'- _ -> impossible-#endif+A key problem point is the type of the 'Merger' and 'Regrem' constructors+of Patch, where the witnesses seem odd. It is likely that some or many+of the unsafe operations could be removed by finding a better type for+these constructors.+-}+++actualMerge :: (Patch :\/: Patch) C(x y) -> Sealed (Patch C(y))++actualMerge (ComP the_p1s :\/: ComP the_p2s) =+ mapSeal joinPatchesFL $ mc (the_p1s :\/: the_p2s)+ where mc :: (FL Patch :\/: FL Patch) C(x y) -> Sealed (FL Patch C(y))+ mc (NilFL :\/: (_:>:_)) = Sealed NilFL+ mc (p1s :\/: NilFL) = Sealed p1s+ mc (p1s :\/: (p2:>:p2s)) = case mergePatchesAfterPatch (p1s:\/:p2) of+ Sealed x -> mc (x:\/:p2s)+actualMerge (ComP p1s :\/: p2) = seq p2 $+ mapSeal joinPatchesFL $ mergePatchesAfterPatch (p1s:\/:p2)+actualMerge (p1 :\/: ComP p2s) = seq p1 $ mergePatchAfterPatches (p1:\/:p2s)++actualMerge (p1 :\/: p2) = case elegantMerge (p1:\/:p2) of+ Just (_ :/\: p1') -> Sealed p1'+ Nothing -> merger "0.0" p2 p1++mergePatchAfterPatches :: (Patch :\/: FL Patch) C(x y) -> Sealed (Patch C(y))+mergePatchAfterPatches (p :\/: (p1:>:p1s)) =+ case actualMerge (p:\/:p1) of+ Sealed x -> mergePatchAfterPatches (x :\/: p1s)+mergePatchAfterPatches (p :\/: NilFL) = Sealed p++mergePatchesAfterPatch :: (FL Patch :\/: Patch) C(x y) -> Sealed (FL Patch C(y))+mergePatchesAfterPatch (p2s :\/: p) =+ case mergePatchAfterPatches (p :\/: p2s) of+ Sealed x -> case commute (joinPatchesFL p2s :> x) of+ Just (_ :> ComP p2s') -> Sealed (unsafeCoercePStart p2s')+ _ -> impossible \end{code} Much of the merger code depends on a routine which recreates from a single@@ -568,52 +565,53 @@ discussion. \begin{code}-#ifndef GADT_WITNESSES-unwind :: Patch -> RL Patch -- Recreates a patch history in reverse.-unwind (Merger _ unwindings _ _) = unwindings-unwind p = p :<: NilRL;+unwind :: Patch C(x y) -> Sealed (RL Patch C(x)) -- Recreates a patch history in reverse.+unwind (Merger _ unwindings _ _) = Sealed unwindings+unwind p = Sealed (p :<: NilRL) -true_unwind :: Patch -> RL Patch -- Recreates a patch history in reverse.-true_unwind p@(Merger _ _ p1 p2) =+trueUnwind :: Patch C(x y) -> Sealed (RL Patch C(x)) -- Recreates a patch history in reverse.+trueUnwind p@(Merger _ _ p1 p2) = case (unwind p1, unwind p2) of- (_:<:p1s,_:<:p2s) -> p :<: p1 :<: reconcile_unwindings p p1s p2s+ (Sealed (_:<:p1s),Sealed (_:<:p2s)) ->+ Sealed (p :<: unsafeCoerceP p1 :<: unsafeUnsealFlipped (reconcileUnwindings p p1s (unsafeCoercePEnd p2s))) _ -> impossible-true_unwind _ = impossible+trueUnwind _ = impossible -reconcile_unwindings :: Patch -> RL Patch -> RL Patch -> RL Patch-reconcile_unwindings _ NilRL p2s = p2s-reconcile_unwindings _ p1s NilRL = p1s-reconcile_unwindings p (p1:<:p1s) p2s =+reconcileUnwindings :: Patch C(a b) -> RL Patch C(x z) -> RL Patch C(y z) -> FlippedSeal (RL Patch) C(z)+reconcileUnwindings _ NilRL p2s = FlippedSeal p2s+reconcileUnwindings _ p1s NilRL = FlippedSeal p1s+reconcileUnwindings p (p1:<:p1s) p2s@(p2:<:tp2s) = case [(p1s', p2s')|- p1s'@(hp1s':<:_) <- head_permutationsRL (p1:<:p1s),- p2s'@(hp2s':<:_) <- head_permutationsRL p2s,+ p1s'@(hp1s':<:_) <- headPermutationsRL (p1:<:p1s),+ p2s'@(hp2s':<:_) <- headPermutationsRL p2s, hp1s' `unsafeCompare` hp2s'] of ((p1':<:p1s', _:<:p2s'):_) ->- p1' :<: reconcile_unwindings p p1s' p2s'- [] -> case reverseFL `fmap` put_before p1 (reverseRL p2s) of- Just p2s' -> p1 :<: reconcile_unwindings p p1s p2s'+ mapFlipped (p1' :<:) $ reconcileUnwindings p p1s' (unsafeCoercePEnd p2s')+ [] -> case reverseFL `fmap` putBefore p1 (reverseRL p2s) of+ Just p2s' -> mapFlipped (p1 :<:) $ reconcileUnwindings p p1s p2s' Nothing ->- case fmap reverseFL $ put_before (headRL p2s) $+ case fmap reverseFL $ putBefore p2 $ reverseRL (p1:<:p1s) of- Just p1s' -> case p2s of- hp2s:<:tp2s -> hp2s :<:- reconcile_unwindings p p1s' tp2s- NilRL -> impossible+ Just p1s' -> mapFlipped (p2 :<:) $+ reconcileUnwindings p p1s' tp2s Nothing ->- bugDoc $ text "in function reconcile_unwindings"+ bugDoc $ text "in function reconcileUnwindings" $$ text "Original patch:" $$ showPatch_ p- _ -> bug "in reconcile_unwindings"+ _ -> bug "in reconcileUnwindings" -put_before :: Patch -> FL Patch -> Maybe (FL Patch)-put_before p1 (p2:>:p2s) =- do p1' :> p2' <- commute (p2 :> invert p1)+-- This code seems wrong, shouldn't the commute be invert p1 :> p2 ? And why isn't p1' re-inverted?+-- it seems to have been this way forever:+-- Fri May 23 10:27:04 BST 2003 droundy@abridgegame.org+-- * fix bug in unwind and add docs on unwind algorithm.+putBefore :: Patch C(y z) -> FL Patch C(x z) -> Maybe (FL Patch C(y w))+putBefore p1 (p2:>:p2s) =+ do p1' :> p2' <- commute (unsafeCoerceP p2 :> invert p1) commute (p2' :> p1)- (p2' :>:) `fmap` put_before p1' p2s-put_before _ NilFL = Just NilFL-#endif+ (unsafeCoerceP p2' :>:) `fmap` putBefore p1' (unsafeCoerceP p2s)+putBefore _ NilFL = Just (unsafeCoerceP NilFL) \end{code}- + \section{Conflicts} There are a couple of simple constraints on the routine which determines@@ -638,149 +636,135 @@ \begin{code} instance Conflict Patch where- commute_no_conflicts (x:>y) = do x' :< y' <- commute_no_merger (y :< x)+ commuteNoConflicts (x:>y) = do x' :< y' <- commuteNoMerger (y :< x) return (y':>x')-#ifndef GADT_WITNESSES resolveConflicts patch = rcs NilFL $ reverseFL $ flattenFL patch- where rcs :: FL Patch C(w y) -> RL Patch C(x y) -> [[Sealed (FL Prim C(w))]]+ where rcs :: FL Patch C(y w) -> RL Patch C(x y) -> [[Sealed (FL Prim C(w))]] rcs _ NilRL = [] rcs passedby (p@(Merger _ _ _ _):<:ps) =- case commute_no_merger (join_patchesFL passedby:<p) of+ case commuteNoMerger (joinPatchesFL passedby:<p) of Just (p'@(Merger _ _ p1 p2):<_) ->- (map Sealed $ nubBy unsafeCompare $ effect (glump09 p1 p2) : unravel p')+ (map Sealed $ nubBy unsafeCompare $+ effect (unsafeCoercePStart $ unsafeUnseal (glump09 p1 p2)) : map (unsafeCoercePStart . unsafeUnseal) (unravel p')) : rcs (p :>: passedby) ps Nothing -> rcs (p :>: passedby) ps _ -> impossible rcs passedby (p:<:ps) = seq passedby $ rcs (p :>: passedby) ps-#else- resolveConflicts = bug "haven't defined resolveConflicts with type witnesses."-#endif -public_unravel :: Patch C(x y) -> [Sealed (FL Prim C(y))]-#ifdef GADT_WITNESSES-public_unravel = bug "Haven't implemented public_unravel with type witnesses."-#else-public_unravel p = map Sealed $ unravel p-#endif+-- This type seems wrong - the most natural type for the result would seem to be+-- [Sealed (FL Prim C(x))], given the type of unwind.+-- However downstream code in darcs convert assumes the C(y) type, and I was unable+-- to figure out whether this could/should reasonably be changed -- Ganesh 13/4/10+publicUnravel :: Patch C(x y) -> [Sealed (FL Prim C(y))]+publicUnravel = map (mapSeal unsafeCoercePStart) . unravel -#ifndef GADT_WITNESSES-unravel :: Patch -> [FL Prim]-unravel p = nubBy unsafeCompare $- map (sortCoalesceFL . concatFL . mapFL_FL effect) $- get_supers $ map reverseRL $ new_ur p $ unwind p+unravel :: Patch C(x y) -> [Sealed (FL Prim C(x))]+unravel p = nub $ map (mapSeal (sortCoalesceFL . concatFL . mapFL_FL effect)) $+ getSupers $ map (mapSeal reverseRL) $ unseal (newUr p) $ unwind p -get_supers :: [FL Patch] -> [FL Patch]-get_supers (x:xs) =- case filter (not.(x `is_superpatch_of`)) xs of- xs' -> if or $ map (`is_superpatch_of` x) xs'- then get_supers xs'- else x : get_supers xs'-get_supers [] = []-is_superpatch_of :: FL Patch -> FL Patch -> Bool-x `is_superpatch_of` y | lengthFL y > lengthFL x = False-x `is_superpatch_of` y = x `iso` y- where iso :: FL Patch -> FL Patch -> Bool+getSupers :: [Sealed (FL Patch C(x))] -> [Sealed (FL Patch C(x))]+getSupers (x:xs) =+ case filter (not.(x `isSuperpatchOf`)) xs of+ xs' -> if or $ map (`isSuperpatchOf` x) xs'+ then getSupers xs'+ else x : getSupers xs'+getSupers [] = []++isSuperpatchOf :: Sealed (FL Patch C(x)) -> Sealed (FL Patch C(x)) -> Bool+Sealed x `isSuperpatchOf` Sealed y | lengthFL y > lengthFL x = False -- should be just an optimisation+Sealed x `isSuperpatchOf` Sealed y = x `iso` y+ where iso :: FL Patch C(x y) -> FL Patch C(x z) -> Bool _ `iso` NilFL = True NilFL `iso` _ = False a `iso` (b:>:bs) =- case filter ((`unsafeCompare` b) . headFL) $- head_permutationsFL a of- ((_:>:as):_) -> as `iso` bs- [] -> False- _ -> bug "bug in is_superpatch_of"--headFL :: FL a -> a-headFL (x:>:_) = x-headFL NilFL = bug "bad headFL"+ head $ ([as `iso` bs | (ah :>: as) <- simpleHeadPermutationsFL a, IsEq <- [ah =\/= b]] :: [Bool]) ++ [False] -merger :: String -> Patch -> Patch -> Patch-merger "0.0" p1 p2 = Merger undoit unwindings p1 p2+merger :: String -> Patch C(x y) -> Patch C(x z) -> Sealed (Patch C(y))+merger "0.0" p1 p2 = Sealed $ Merger undoit unwindings p1 p2 where fake_p = Merger identity NilRL p1 p2- unwindings = true_unwind fake_p+ unwindings = unsafeUnseal (trueUnwind fake_p) p = Merger identity unwindings p1 p2 undoit = case (isMerger p1, isMerger p2) of- (True ,True ) -> join_patchesFL $ invertRL $ tailRL $ unwind p- where tailRL (_:<:t) = t- tailRL _ = impossible- (False,False) -> invert p1- (True ,False) -> join_patchesFL NilFL- (False,True ) -> join_patchesFL (invert p1 :>: merger_undo p2 :>: NilFL)+ (True ,True ) -> case unwind p of+ Sealed (_:<:t) -> unsafeCoerceP $ joinPatchesFL $ invertRL t+ _ -> impossible+ (False,False) -> unsafeCoerceP $ invert p1+ (True ,False) -> unsafeCoerceP $ joinPatchesFL NilFL+ (False,True ) -> unsafeCoerceP $ joinPatchesFL (invert p1 :>: mergerUndo p2 :>: NilFL) merger g _ _ = error $ "Cannot handle mergers other than version 0.0\n"++g ++ "\nPlease use darcs optimize --modernize with an older darcs." -glump09 :: Patch -> Patch -> Patch-glump09 p1 p2 = fromPrims $ unsafeUnseal $ mangle_unravelled $ map Sealed $ unravel $ merger "0.0" p1 p2--#endif+glump09 :: Patch C(x y) -> Patch C(x z) -> Sealed (Patch C(y))+glump09 p1 p2 = mapSeal fromPrims $ mangleUnravelled $ unseal unravel $ merger "0.0" p1 p2 -mangle_unravelled :: [Sealed (FL Prim C(x))] -> Sealed (FL Prim C(x))-mangle_unravelled pss = if only_hunks pss- then (:>: NilFL) `mapSeal` mangle_unravelled_hunks pss+mangleUnravelled :: [Sealed (FL Prim C(x))] -> Sealed (FL Prim C(x))+mangleUnravelled pss = if onlyHunks pss+ then (:>: NilFL) `mapSeal` mangleUnravelledHunks pss else head pss -only_hunks :: [Sealed (FL Prim C(x))] -> Bool-only_hunks [] = False-only_hunks pss = fn2fp f /= "" && all oh pss- where f = get_a_filename pss+onlyHunks :: [Sealed (FL Prim C(x))] -> Bool+onlyHunks [] = False+onlyHunks pss = fn2fp f /= "" && all oh pss+ where f = getAFilename pss oh :: Sealed (FL Prim C(x)) -> Bool oh (Sealed (p:>:ps)) = primIsHunk p && [fn2fp f] == listTouchedFiles p && oh (Sealed ps) oh (Sealed NilFL) = True -apply_hunks :: [Maybe B.ByteString] -> FL Prim C(x y) -> [Maybe B.ByteString]-apply_hunks ms (FP _ (Hunk l o n):>:ps) = apply_hunks (rls l ms) ps+applyHunks :: [Maybe B.ByteString] -> FL Prim C(x y) -> [Maybe B.ByteString]+applyHunks ms (FP _ (Hunk l o n):>:ps) = applyHunks (rls l ms) ps where rls 1 mls = map Just n ++ drop (length o) mls rls i (ml:mls) = ml : rls (i-1) mls- rls _ [] = bug "rls in apply_hunks"-apply_hunks ms NilFL = ms-apply_hunks _ (_:>:_) = impossible+ rls _ [] = bug "rls in applyHunks"+applyHunks ms NilFL = ms+applyHunks _ (_:>:_) = impossible -get_old :: [Maybe B.ByteString] -> [Sealed (FL Prim C(x))] -> [Maybe B.ByteString]-get_old mls (ps:pss) = get_old (get_hunks_old mls ps) pss-get_old mls [] = mls+getOld :: [Maybe B.ByteString] -> [Sealed (FL Prim C(x))] -> [Maybe B.ByteString]+getOld mls (ps:pss) = getOld (getHunksOld mls ps) pss+getOld mls [] = mls -get_a_filename :: [Sealed (FL Prim C(x))] -> FileName-get_a_filename ((Sealed (FP f _:>:_)):_) = f-get_a_filename _ = fp2fn ""+getAFilename :: [Sealed (FL Prim C(x))] -> FileName+getAFilename ((Sealed (FP f _:>:_)):_) = f+getAFilename _ = fp2fn "" -get_hunks_old :: [Maybe B.ByteString] -> Sealed (FL Prim C(x))+getHunksOld :: [Maybe B.ByteString] -> Sealed (FL Prim C(x)) -> [Maybe B.ByteString]-get_hunks_old mls (Sealed ps) =- apply_hunks (apply_hunks mls ps) (invert ps)+getHunksOld mls (Sealed ps) =+ applyHunks (applyHunks mls ps) (invert ps) -get_hunks_new :: [Maybe B.ByteString] -> Sealed (FL Prim C(x))+getHunksNew :: [Maybe B.ByteString] -> Sealed (FL Prim C(x)) -> [Maybe B.ByteString]-get_hunks_new mls (Sealed ps) = apply_hunks mls ps+getHunksNew mls (Sealed ps) = applyHunks mls ps -get_hunkline :: [[Maybe B.ByteString]] -> Int-get_hunkline = ghl 1+getHunkline :: [[Maybe B.ByteString]] -> Int+getHunkline = ghl 1 where ghl :: Int -> [[Maybe B.ByteString]] -> Int ghl n pps = if any (isJust . head) pps then n else ghl (n+1) $ map tail pps -make_chunk :: Int -> [Maybe B.ByteString] -> [B.ByteString]-make_chunk n mls = pull_chunk $ drop (n-1) mls+makeChunk :: Int -> [Maybe B.ByteString] -> [B.ByteString]+makeChunk n mls = pull_chunk $ drop (n-1) mls where pull_chunk (Just l:mls') = l : pull_chunk mls' pull_chunk (Nothing:_) = [] pull_chunk [] = bug "should this be [] in pull_chunk?" -mangle_unravelled_hunks :: [Sealed (FL Prim C(x))] -> Sealed (Prim C(x))---mangle_unravelled_hunks [[h1],[h2]] = Deal with simple cases handily?-mangle_unravelled_hunks pss =- if null nchs then bug "mangle_unravelled_hunks"+mangleUnravelledHunks :: [Sealed (FL Prim C(x))] -> Sealed (Prim C(x))+--mangleUnravelledHunks [[h1],[h2]] = Deal with simple cases handily?+mangleUnravelledHunks pss =+ if null nchs then bug "mangleUnravelledHunks" else Sealed (FP filename (Hunk l old new))- where oldf = get_old (repeat Nothing) pss- newfs = map (get_hunks_new oldf) pss- l = get_hunkline $ oldf : newfs- nchs = sort $ map (make_chunk l) newfs- filename = get_a_filename pss- old = make_chunk l oldf+ where oldf = getOld (repeat Nothing) pss+ newfs = map (getHunksNew oldf) pss+ l = getHunkline $ oldf : newfs+ nchs = sort $ map (makeChunk l) newfs+ filename = getAFilename pss+ old = makeChunk l oldf new = [top] ++ concat (intersperse [middle] nchs) ++ [bottom] top = BC.pack $ "v v v v v v v" ++ eol_c middle = BC.pack $ "*************" ++ eol_c@@ -790,27 +774,35 @@ else "" instance Effect Patch where- effect p@(Merger _ _ _ _) = sortCoalesceFL $ effect $ merger_undo p+ effect p@(Merger _ _ _ _) = sortCoalesceFL $ effect $ mergerUndo p effect p@(Regrem _ _ _ _) = invert $ effect $ invert p effect (ComP ps) = concatFL $ mapFL_FL effect ps effect (PP p) = effect p isHunk p = do PP p' <- return p isHunk p' +-- |@modernizePatch@ is used during conversion to Darcs 2 format.+-- It does the following:+--+-- * removes mergers by linearising them, thus removing the ability+-- to commute them+--+-- * drops mv a b ; add b which was introduced by an error in earlier+-- versions of darcs (TODO: check this; identify the versions) modernizePatch :: Patch C(x y) -> Patch C(x y) modernizePatch p@(Merger _ _ _ _) = fromPrims $ effect p modernizePatch p@(Regrem _ _ _ _) = fromPrims $ effect p modernizePatch (ComP ps) = ComP $ filtermv $ mapFL_FL modernizePatch ps where filtermv :: FL Patch C(x y) -> FL Patch C(x y)-#ifndef GADT_WITNESSES- filtermv (PP (Move _ b):>:xs) | hasadd xs = filtermv xs- where hasadd (PP (FP b' AddFile):>:_) | b' == b = True- hasadd (PP (DP b' AddDir):>:_) | b' == b = True- hasadd (PP (FP b' RmFile):>:_) | b' == b = False- hasadd (PP (DP b' RmDir):>:_) | b' == b = False+ filtermv (PP (Move _ b :: Prim C(x z)):>:xs)+ | IsEq <- hasadd xs = filtermv xs+ where hasadd :: FL Patch C(a b) -> EqCheck C(x z)+ hasadd (PP (FP b' AddFile):>:_) | b' == b = unsafeCoerceP IsEq+ hasadd (PP (DP b' AddDir):>:_) | b' == b = unsafeCoerceP IsEq+ hasadd (PP (FP b' RmFile):>:_) | b' == b = NotEq+ hasadd (PP (DP b' RmDir):>:_) | b' == b = NotEq hasadd (_:>:z) = hasadd z- hasadd NilFL = False-#endif+ hasadd NilFL = NotEq filtermv (x:>:xs) = x :>: filtermv xs filtermv NilFL = NilFL @@ -818,32 +810,26 @@ instance FromPrims Patch where fromPrims (p :>: NilFL) = PP p- fromPrims ps = join_patchesFL $ mapFL_FL PP ps- joinPatches = join_patchesFL+ fromPrims ps = joinPatchesFL $ mapFL_FL PP ps+ joinPatches = joinPatchesFL -#ifndef GADT_WITNESSES-new_ur :: Patch -> RL Patch -> [RL Patch]-new_ur p (Merger _ _ p1 p2 :<: ps) =- case filter ((`unsafeCompare` p1) . headRL) $ head_permutationsRL ps of- ((_:<:ps'):_) -> new_ur p (p1:<:ps') ++ new_ur p (p2:<:ps')- _ -> bugDoc $ text "in function new_ur"+newUr :: Patch C(a b) -> RL Patch C(x y) -> [Sealed (RL Patch C(x))]+newUr p (Merger _ _ p1 p2 :<: ps) =+ case filter (\(pp:<:_) -> pp `unsafeCompare` p1) $ headPermutationsRL ps of+ ((_:<:ps'):_) -> newUr p (unsafeCoercePStart p1:<:ps') ++ newUr p (unsafeCoercePStart p2:<:ps')+ _ -> bugDoc $ text "in function newUr" $$ text "Original patch:" $$ showPatch_ p $$ text "Unwound:"- $$ vcat (mapRL showPatch_ $ unwind p)--new_ur op ps =- case filter (isMerger.headRL) $ head_permutationsRL ps of- [] -> [ps]- (ps':_) -> new_ur op ps'+ $$ vcat (unseal (mapRL showPatch_) $ unwind p) -headRL :: RL a -> a-headRL (x:<:_) = x-headRL _ = bug "bad headRL"-#endif+newUr op ps =+ case filter (\(p:<:_) -> isMerger p) $ headPermutationsRL ps of+ [] -> [Sealed ps]+ (ps':_) -> newUr op ps' instance Invert p => Invert (Named p) where- invert (NamedP n d p) = NamedP (invert_name n) (map invert_name d) (invert p)+ invert (NamedP n d p) = NamedP (invertName n) (map invertName d) (invert p) identity = NamedP idpatchinfo [] identity instance Invert Patch where@@ -856,29 +842,29 @@ identity = ComP NilFL instance MyEq Patch where- unsafeCompare = eq_patches+ unsafeCompare = eqPatches instance MyEq p => MyEq (Named p) where unsafeCompare (NamedP n1 d1 p1) (NamedP n2 d2 p2) = n1 == n2 && d1 == d2 && unsafeCompare p1 p2 -eq_patches :: Patch C(x y) -> Patch C(w z) -> Bool-eq_patches (PP p1) (PP p2) = unsafeCompare p1 p2-eq_patches (ComP ps1) (ComP ps2)- = eq_FL eq_patches ps1 ps2-eq_patches (ComP NilFL) (PP Identity) = True-eq_patches (PP Identity) (ComP NilFL) = True-eq_patches (Merger _ _ p1a p1b) (Merger _ _ p2a p2b)- = eq_patches p1a p2a &&- eq_patches p1b p2b-eq_patches (Regrem _ _ p1a p1b) (Regrem _ _ p2a p2b)- = eq_patches p1a p2a &&- eq_patches p1b p2b-eq_patches _ _ = False+eqPatches :: Patch C(x y) -> Patch C(w z) -> Bool+eqPatches (PP p1) (PP p2) = unsafeCompare p1 p2+eqPatches (ComP ps1) (ComP ps2)+ = eqFL eqPatches ps1 ps2+eqPatches (ComP NilFL) (PP Identity) = True+eqPatches (PP Identity) (ComP NilFL) = True+eqPatches (Merger _ _ p1a p1b) (Merger _ _ p2a p2b)+ = eqPatches p1a p2a &&+ eqPatches p1b p2b+eqPatches (Regrem _ _ p1a p1b) (Regrem _ _ p2a p2b)+ = eqPatches p1a p2a &&+ eqPatches p1b p2b+eqPatches _ _ = False -eq_FL :: (FORALL(b c d e) a C(b c) -> a C(d e) -> Bool)+eqFL :: (FORALL(b c d e) a C(b c) -> a C(d e) -> Bool) -> FL a C(x y) -> FL a C(w z) -> Bool-eq_FL _ NilFL NilFL = True-eq_FL f (x:>:xs) (y:>:ys) = f x y && eq_FL f xs ys-eq_FL _ _ _ = False+eqFL _ NilFL NilFL = True+eqFL f (x:>:xs) (y:>:ys) = f x y && eqFL f xs ys+eqFL _ _ _ = False \end{code}
src/Darcs/Patch/Core.lhs view
@@ -27,21 +27,21 @@ module Darcs.Patch.Core ( Patch(..), Named(..),- join_patchesFL, concatFL, flattenFL,+ joinPatchesFL, concatFL, flattenFL, nullP, isNullPatch, infopatch,- n_fn,+ nFn, adddeps, namepatch, anonymous,- merger_undo, isMerger,+ mergerUndo, isMerger, getdeps, patch2patchinfo, patchname, patchcontents, ) where import Prelude hiding ( pi )-import Darcs.Patch.Info ( PatchInfo, patchinfo, make_filename )+import Darcs.Patch.Info ( PatchInfo, patchinfo, makeFilename ) import Darcs.Patch.Patchy ( Patchy ) import Darcs.Witnesses.Ordered-import Darcs.Patch.Prim ( Prim(..), FromPrim(..), Effect(effect, effectRL), n_fn )+import Darcs.Patch.Prim ( Prim(..), FromPrim(..), Effect(effect, effectRL), nFn ) #include "impossible.h" data Patch C(x y) where@@ -90,9 +90,9 @@ isMerger (Regrem _ _ _ _) = True isMerger _ = False -merger_undo :: Patch C(x y) -> Patch C(x y)-merger_undo (Merger undo _ _ _) = undo-merger_undo _ = impossible+mergerUndo :: Patch C(x y) -> Patch C(x y)+mergerUndo (Merger undo _ _ _) = undo+mergerUndo _ = impossible \end{code} %Another nice thing to be able to do with composite patches is to `flatten'@@ -106,8 +106,8 @@ flattenFL (PP Identity) = NilFL flattenFL p = p :>: NilFL -join_patchesFL :: FL Patch C(x y) -> Patch C(x y)-join_patchesFL ps = ComP $! ps+joinPatchesFL :: FL Patch C(x y) -> Patch C(x y)+joinPatchesFL ps = ComP $! ps infopatch :: Patchy p => PatchInfo -> p C(x y) -> Named p C(x y) adddeps :: Named p C(x y) -> [PatchInfo] -> Named p C(x y)@@ -130,7 +130,7 @@ patch2patchinfo (NamedP i _ _) = i patchname :: Named p C(x y) -> String-patchname (NamedP i _ _) = make_filename i+patchname (NamedP i _ _) = makeFilename i patchcontents :: Named p C(x y) -> p C(x y) patchcontents (NamedP _ _ p) = p
src/Darcs/Patch/Depends.hs view
@@ -16,59 +16,42 @@ -- Boston, MA 02110-1301, USA. {-# OPTIONS_GHC -cpp -fglasgow-exts #-}-{-# LANGUAGE CPP #-}--- , ScopedTypeVariables, TypeOperators #-}+{-# LANGUAGE CPP , ScopedTypeVariables #-}+--, TypeOperators #-} #include "gadts.h" -module Darcs.Patch.Depends ( get_common_and_uncommon, get_tags_right,- get_common_and_uncommon_or_missing,- optimize_patchset, deep_optimize_patchset,- slightly_optimize_patchset,- get_patches_beyond_tag, get_patches_in_tag,- patchset_union, patchset_intersection,- commute_to_end,+module Darcs.Patch.Depends ( getTagsRight,+ areUnrelatedRepos,+ mergeThem, findCommonWithThem,+ countUsThem, removeFromPatchSet,+ optimizePatchset, deepOptimizePatchset,+ slightlyOptimizePatchset,+ getPatchesBeyondTag, getPatchesInTag,+ splitOnTag,+ newsetUnion, newsetIntersection,+ commuteToEnd, findUncommon, merge2FL ) where-import Data.List ( delete, intersect )-import Control.Monad ( liftM2 )-import Control.Monad.Error ( Error(..) )+import Data.List ( delete, intersect, (\\) ) -import Darcs.Patch ( RepoPatch, Named, getdeps, commute,- commuteFL,- patch2patchinfo, merge )-import Darcs.Witnesses.Ordered ( (:\/:)(..), (:<)(..), (:/\:)(..), (:>)(..),- RL(..), FL(..),- (+<+),- reverseFL, mapFL_FL, mapFL, concatReverseFL,- lengthRL, concatRL, reverseRL, mapRL,- unsafeCoerceP, EqCheck(..) )-import Darcs.Patch.Permutations ( partitionRL )-import Darcs.Patch.Info ( PatchInfo, human_friendly, is_tag )-import Darcs.Patch.Set ( PatchSet, SealedPatchSet )-import Darcs.Patch.Patchy ( sloppyIdentity )-import Darcs.Hopefully ( PatchInfoAnd, piap, info, n2pia,- hopefully, conscientiously, hopefullyM )+import Darcs.Patch ( RepoPatch, getdeps, commute, commuteFLorComplain, commuteRL )+import Darcs.Patch.Info ( PatchInfo, isTag, humanFriendly )+import Darcs.Patch.Patchy ( mergeFL )+import Darcs.Patch.Permutations ( partitionFL, partitionRL, removeSubsequenceRL )+import Darcs.Hopefully( PatchInfoAnd, hopefully, hopefullyM, info )+import Darcs.Witnesses.Ordered ( (:\/:)(..), (:/\:)(..), (:>)(..), (:>>)(..),+ (=\/=), (=/\=), (+>+), EqCheck(..), mapFL,+ RL(..), FL(..), isShorterThanRL, (+<+),+ reverseFL, reverseRL, mapRL, unsafeCoerceP, unsafeCoercePStart )+import Darcs.Patch.Set ( PatchSet(..), Tagged(..), SealedPatchSet, newset2RL )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif import Darcs.ProgressPatches ( progressRL )-import Darcs.Witnesses.Sealed (Sealed(..), FlippedSeal(..), Sealed2(..)- , flipSeal, seal, unseal, mapFlipped )-import Printer ( errorDoc, renderString, ($$), text )+import Darcs.Witnesses.Sealed (Sealed(..), FlippedSeal(..), flipSeal, seal )+import Printer ( renderString, vcat ) #include "impossible.h" -get_common_and_uncommon :: RepoPatch p => (PatchSet p C(x),PatchSet p C(y)) ->- ([PatchInfo],(RL (PatchInfoAnd p) :\/: RL (PatchInfoAnd p)) C(x y))-get_common_and_uncommon_or_missing :: RepoPatch p => (PatchSet p C(x),PatchSet p C(y)) ->- Either PatchInfo ([PatchInfo],(RL (PatchInfoAnd p) :\/: RL (PatchInfoAnd p)) C(x y))--get_common_and_uncommon = - either missingPatchError id . get_common_and_uncommon_err--get_common_and_uncommon_or_missing = - either (\(MissingPatch x _) -> Left x) Right . get_common_and_uncommon_err--get_common_and_uncommon_err :: RepoPatch p => (PatchSet p C(x),PatchSet p C(y)) ->- Either MissingPatch ([PatchInfo],(RL (PatchInfoAnd p) :\/: RL (PatchInfoAnd p)) C(x y))-get_common_and_uncommon_err (ps1,ps2) = gcau (optimize_patchset ps1) ps2- {-| with_partial_intersection takes two 'PatchSet's and splits them into a /common/ intersection portion and two sets of patches. The intersection, however,@@ -77,334 +60,85 @@ efficient function, because it makes use of the already-broken-up nature of 'PatchSet's. -'PatchSet's have the property that if-@-(info $ last $ head a) == (info $ last $ head b)-@-then @(tail a)@ and @(tail b)@ are identical repositories, and we want to take-advantage of this if possible, to avoid reading too many inventories. In-the case of --partial repositories or patch bundles, it is crucial that we-don't need to read the whole history, since it isn't available.--TODO:--The length equalising isn't necessarily right. We probably also be-thinking about not going past the end of a partial repository, or favour-local repository stuff over remote repository stuff.--Also, when comparing l1 to l2, we should really be comparing the-newly discovered one to /all/ the lasts in the other patch set-that we've got so far.--}-with_partial_intersection :: forall a p C(x y). RepoPatch p => PatchSet p C(x) -> PatchSet p C(y)- -> (FORALL(z) PatchSet p C(z) -> RL (PatchInfoAnd p) C(z x)- -> RL (PatchInfoAnd p) C(z y) -> a)- -> a-with_partial_intersection NilRL ps2 j = j (NilRL:<:NilRL) NilRL (concatRL ps2)-with_partial_intersection ps1 NilRL j = j (NilRL:<:NilRL) (concatRL ps1) NilRL-with_partial_intersection (NilRL:<:ps1) ps2 j =- with_partial_intersection ps1 ps2 j-with_partial_intersection ps1 (NilRL:<:ps2) j =- with_partial_intersection ps1 ps2 j--- NOTE: symmetry is broken here, so we want the PatchSet with more history--- first!-with_partial_intersection ((pi1:<:NilRL):<:common) ((pi2:<:NilRL):<:_) j--- NOTE: Since the patchsets have the same starting but different ending--- we can coerce them. The type system is not aware of our invariant on tags,--- but both pi1 and pi2 should be tags, thus we check they are both identity--- patches.- | info pi1 == info pi2- , IsEq <- sloppyIdentity pi1- , IsEq <- sloppyIdentity pi2 = j common NilRL (unsafeCoerceP NilRL)-with_partial_intersection (orig_ps1:<:orig_ps1s) (orig_ps2:<:orig_ps2s) j- = f (lengthRL orig_ps1) (last $ mapRL info orig_ps1) (orig_ps1:>:NilFL) orig_ps1s- (lengthRL orig_ps2) (last $ mapRL info orig_ps2) (orig_ps2:>:NilFL) orig_ps2s- where {- Invariants: nx = length $ concatReverseFL psx- lx = last $ concatReverseFL psx -}- f :: Int -> PatchInfo -> FL (RL (PatchInfoAnd p)) C(r x) -> PatchSet p C(r)- -> Int -> PatchInfo -> FL (RL (PatchInfoAnd p)) C(u y) -> PatchSet p C(u)- -> a- f _n1 l1 ps1 ps1s _n2 l2 ps2 _ps2s- | l1 == l2 = j ps1s (unsafeCoerceP (concatReverseFL ps1)) (unsafeCoerceP (concatReverseFL ps2))- f n1 l1 ps1 ps1s n2 l2 ps2 ps2s- = case compare n1 n2 of- GT -> case dropWhileNilRL ps2s of- ps2':<:ps2s' ->- f n1 l1 ps1 ps1s- (n2 + lengthRL ps2') (last $ mapRL info ps2') (ps2':>:ps2) ps2s'- NilRL -> -- We keep going round f so the l1 == l2 case- -- has a chance to kick in- case dropWhileNilRL ps1s of- ps1':<:ps1s' ->- f (n1 + lengthRL ps1') (last $ mapRL info ps1')- (ps1':>:ps1) ps1s'- n2 l2 ps2 ps2s- NilRL -> j (NilRL:<:NilRL) (concatReverseFL ps1) (concatReverseFL ps2)- _ -> case dropWhileNilRL ps1s of- ps1':<:ps1s' ->- f (n1 + lengthRL ps1') (last $ mapRL info ps1') (ps1':>:ps1) ps1s'- n2 l2 ps2 ps2s- NilRL -> -- We keep going round f so the l1 == l2 case- -- has a chance to kick in- case dropWhileNilRL ps2s of- ps2':<:ps2s' ->- f n1 l1 ps1 NilRL- (n2 + lengthRL ps2') (last $ mapRL info ps2')- (ps2':>:ps2) ps2s'- NilRL -> j (NilRL:<:NilRL) (concatReverseFL ps1) (concatReverseFL ps2)--{-|-'gcau' determines a list of /common/ patches and patches unique to each of-the two 'PatchSet's. The list of /common/ patches only needs to include all-patches that are not interspersed with the /unique/ patches, but including-more patches in the list of /common/ patches doesn't really hurt, except-for efficiency considerations. Mostly, we want to access as few elements-as possible of the 'PatchSet' list, since those can be expensive (or-unavailable). In other words, the /common/ patches need not be minimal,-whereas the 'PatchSet's should be minimal for performance reasons.--'PatchSet's have the property that if-@-(info $ last $ head a) == (info $ last $ head b)-@-then @(tail a)@ and @(tail b)@ are identical repositories, and we want to take-advantage of this if possible, to avoid reading too many inventories. In-the case of --partial repositories or patch bundles, it is crucial that we-don't need to read the whole history, since it isn't available.--TODO:--The length equalising isn't necessarily right. We probably also be-thinking about not going past the end of a partial repository, or favour-local repository stuff over remote repo stuff.--Also, when comparing l1 to l2, we should really be comparing the-newly discovered one to /all/ the lasts in the other patch set-that we've got so far.+Note that the first argument to with_partial_intersection should be+the repository that is more cheaply accessed (i.e. local), as+with_partial_intersection does its best to reduce the number of+inventories that are accessed from its rightmost argument. -} -gcau :: forall p C(x y). RepoPatch p => PatchSet p C(x) -> PatchSet p C(y)- -> Either MissingPatch ([PatchInfo],(RL (PatchInfoAnd p) :\/: RL (PatchInfoAnd p)) C(x y))-gcau NilRL ps2 = return ([], NilRL :\/: concatRL ps2)-gcau ps1 NilRL = return ([], concatRL ps1 :\/: NilRL)-gcau (NilRL:<:ps1) ps2 = gcau ps1 ps2-gcau ps1 (NilRL:<:ps2) = gcau ps1 ps2-gcau ((pi1:<:NilRL):<:_) ((pi2:<:NilRL):<:_)- | info pi1 == info pi2- , IsEq <- sloppyIdentity pi1- , IsEq <- sloppyIdentity pi2 = return ([info pi1], NilRL :\/: unsafeCoerceP NilRL)-gcau (orig_ps1:<:orig_ps1s) (orig_ps2:<:orig_ps2s)- = f (lengthRL orig_ps1) (unseal info $ lastRL orig_ps1) (orig_ps1:>:NilFL) orig_ps1s- (lengthRL orig_ps2) (unseal info $ lastRL orig_ps2) (orig_ps2:>:NilFL) orig_ps2s- where {- Invariants: nx = lengthRL $ concatReverseFL psx- lx = last $ concatReverseFL psx -}- f :: Int -> PatchInfo -> FL (RL (PatchInfoAnd p)) C(r x) -> PatchSet p C(r)- -> Int -> PatchInfo -> FL (RL (PatchInfoAnd p)) C(u y) -> PatchSet p C(u)- -> Either MissingPatch ([PatchInfo],(RL (PatchInfoAnd p) :\/: RL (PatchInfoAnd p)) C(x y))- f _n1 l1 ps1 _ps1s _n2 l2 ps2 _ps2s- | l1 == l2 = gcau_simple (unsafeCoerceP (concatReverseFL ps1)) (unsafeCoerceP (concatReverseFL ps2))- f n1 l1 ps1 ps1s n2 l2 ps2 ps2s- = case n1 `compare` n2 of- GT -> case dropWhileNilRL ps2s of- ps2':<:ps2s' ->- f n1 l1 ps1 ps1s- (n2 + lengthRL ps2') (unseal info $ lastRL ps2') (ps2':>:ps2) ps2s'- NilRL -> -- We keep going round f so the l1 == l2 case- -- has a chance to kick in- case dropWhileNilRL ps1s of- ps1':<:ps1s' ->- f (n1 + lengthRL ps1') (unseal info $ lastRL ps1')- (ps1':>:ps1) ps1s'- n2 l2 ps2 ps2s- NilRL -> gcau_simple (concatReverseFL ps1) (concatReverseFL ps2)- _ -> case dropWhileNilRL ps1s of- ps1':<:ps1s' ->- f (n1 + lengthRL ps1') (unseal info $ lastRL ps1') (ps1':>:ps1) ps1s'- n2 l2 ps2 ps2s- NilRL -> -- We keep going round f so the l1 == l2 case- -- has a chance to kick in- case dropWhileNilRL ps2s of- ps2':<:ps2s' ->- f n1 l1 ps1 NilRL- (n2 + lengthRL ps2') (unseal info $ lastRL ps2')- (ps2':>:ps2) ps2s'- NilRL -> gcau_simple (concatReverseFL ps1) (concatReverseFL ps2)--lastRL :: RL a C(x y) -> Sealed (a C(x))-lastRL (a:<:NilRL) = seal a-lastRL (_:<:as) = lastRL as-lastRL NilRL = bug "lastRL on empty list"--dropWhileNilRL :: PatchSet p C(x) -> PatchSet p C(x)-dropWhileNilRL (NilRL:<:xs) = dropWhileNilRL xs-dropWhileNilRL xs = xs---- | Filters the common elements from @ps1@ and @ps2@ and returns the simplified sequences.-gcau_simple :: RepoPatch p => RL (PatchInfoAnd p) C(x y) -- ^ @ps1@- -> RL (PatchInfoAnd p) C(u v) -- ^ @ps2@- -> Either MissingPatch ([PatchInfo],(RL (PatchInfoAnd p) :\/: RL (PatchInfoAnd p)) C(y v))-gcau_simple ps1 ps2 = do- FlippedSeal ex1 <- get_extra common ps1- FlippedSeal ex2 <- get_extra common ps2- let ps1' = filter (`elem` common) $ ps1_info- return (ps1', (unsafeCoerceP ex1 :\/: ex2))- where common = ps1_info `intersect` mapRL info ps2- ps1_info = mapRL info ps1--data MissingPatch = MissingPatch !PatchInfo !String--instance Error MissingPatch where- -- we don't really need those methods- noMsg = bug "MissingPatch doesn't define noMsg."---- | Returns a sub-sequence from @patches@, where all the elements of @common@ have--- been removed by commuting them back into the early part of the history.------ An informal illustration of this process as it traverses a mixed list of patches--- where C and x denote common patches and extra patches accordingly. Variants of--- patches obtained through commutation are indicated by letters following the--- patch name.------ > in: x6 < x5 < C4 < x3 < C2 < x1 skip: extra:--- > in: x5 < C4 < x3 < C2 < x1 skip: extra: x6--- > in: C4 < x3 < C2 < x1 skip: extra: x5 > x6--- > in: x3 < C2 < x1 skip: C4 extra: x5 > x6--- > in: C2 < x1 skip: C4b extra: x3b > x5 > x6--- > in: x1 skip: C2 > C4b extra: x3b > x5 > x6--- > in: skip: C2b > C4c extra: x1b > x3b > x5 > x6------ This function is undefined if for any reason we fail to commute an extra--- patch past one of the common ones. Such a failure would indicate that the--- common patch depends on the extra one, contradicting the claim that the--- \"common\" patch is shared with another repository lacking the extra patches.--- Unfortunately, such cases have crept up in practice. Some notable cases can--- be found on the bugtracker as:------ * issue27 - different patches with identical patch info; mistaken identity.--- Note how @common@ consists only of a list of 'PatchInfo' which--- we trust to uniquely identify such patches.------ * issue1014 - duplicate patches-get_extra :: RepoPatch p => [PatchInfo] -- ^ @common@- -> RL (PatchInfoAnd p) C(u x) -- ^ @patches@- -> Either MissingPatch (FlippedSeal (RL (PatchInfoAnd p)) C(y))-get_extra = get_extra_aux (return $ unsafeCoerceP NilFL)- where- get_extra_aux :: RepoPatch p => Either MissingPatch (FL (Named p) C(x y))- -> [PatchInfo]- -> RL (PatchInfoAnd p) C(u x)- -> Either MissingPatch (FlippedSeal (RL (PatchInfoAnd p)) C(y))- get_extra_aux _ _ NilRL = return (flipSeal NilRL)- get_extra_aux skipped common (hp:<:pps) =- if info hp `elem` common && is_tag (info hp)- then case getdeps `fmap` hopefullyM hp of- Just ds -> get_extra_aux (liftM2 (:>:) ep skipped) (ds++delete (info hp) common) pps- Nothing -> get_extra_aux (liftM2 (:>:) ep skipped) (delete (info hp) common) pps- else if info hp `elem` common- then get_extra_aux (liftM2 (:>:) ep skipped) (delete (info hp) common) pps- else do- p <- ep- skpd <- skipped- case commuteFL (p :> skpd) of- Right (skipped_patch' :> p') -> do- FlippedSeal x <- get_extra_aux (return skipped_patch') common pps- return $ flipSeal (info hp `piap` p' :<: x)- -- Failure to commute indicates a bug because it means- -- that a patch was interspersed between the common- -- patches. This should only happen if that patch was- -- commuted there. This uses 2 properties:- -- 1) commute is its own inverse- -- 2) if patches commute in one adjacent context then- -- they commute in any context where they are- -- adjacent- Left (Sealed2 hpc) -> errorDoc $ text "bug in get_extra commuting patches:"- $$ text "First patch is:"- $$ human_friendly (info hp)- $$ text "Second patch is:"- $$ human_friendly (info $ n2pia hpc)- where ep = case hopefullyM hp of- Right p' -> return p'- Left e -> Left (MissingPatch (info hp) e)--missingPatchError :: MissingPatch -> a-missingPatchError (MissingPatch pinfo e) =- errorDoc- ( text "failed to read patch in get_extra:"- $$ human_friendly pinfo $$ text e- $$ text "Perhaps this is a 'partial' repository?" )--get_extra_old :: RepoPatch p => [PatchInfo]- -> RL (PatchInfoAnd p) C(u x)- -> FlippedSeal (RL (PatchInfoAnd p)) C(y)-get_extra_old common pps =- either missingPatchError id (get_extra common pps)+with_partial_intersection+ :: RepoPatch p => PatchSet p C(start x) -> PatchSet p C(start y)+ -> (FORALL(a c) RL (Tagged p) C(start a)+ -> RL (PatchInfoAnd p) C(a x)+ -> RL (PatchInfoAnd p) C(a c)+ -> ddd)+ -> ddd+with_partial_intersection (PatchSet ps1 NilRL) s j = j NilRL ps1 (newset2RL s)+with_partial_intersection s (PatchSet ps2 NilRL) j =+ j NilRL (newset2RL s) ps2+with_partial_intersection bbb (PatchSet a (Tagged ta _ _ :<: _)) j+ | Just (PatchSet b t) <- simpleTag (info ta) bbb = j t b (unsafeCoerceP a)+with_partial_intersection aaa (PatchSet b (Tagged tb _ pb :<: tbs)) j+ = case hopefullyM tb of+ Just _ -> with_partial_intersection aaa (PatchSet (b+<+tb:<:pb) tbs) j+ Nothing -> case splitOnTag (info tb) aaa of+ PatchSet NilRL com :>> us -> j com us (unsafeCoerceP b)+ _ -> impossible -get_patches_beyond_tag :: RepoPatch p => PatchInfo -> PatchSet p C(x) -> FlippedSeal (RL (PatchInfoAnd p)) C(x)-get_patches_beyond_tag t ((hp:<:NilRL):<:_) | info hp == t = flipSeal NilRL-get_patches_beyond_tag t patchset@((hp:<:ps):<:pps) =+getPatchesBeyondTag :: RepoPatch p => PatchInfo -> PatchSet p C(start x) -> FlippedSeal (RL (PatchInfoAnd p)) C(x)+getPatchesBeyondTag t (PatchSet ps (Tagged hp _ _ :<:_)) | info hp == t = flipSeal ps+getPatchesBeyondTag t patchset@(PatchSet (hp:<:ps) ts) = if info hp == t- then if get_tags_right patchset == [info hp]- then flipSeal NilRL -- special case to avoid looking at redundant patches- else get_extra_old [t] (concatRL patchset)- else mapFlipped (hp:<:) $ get_patches_beyond_tag t (ps:<:pps)-get_patches_beyond_tag t (NilRL:<:pps) = get_patches_beyond_tag t pps-get_patches_beyond_tag t NilRL = bug $ "tag\n" ++- renderString (human_friendly t) ++- "\nis not in the patchset in get_patches_beyond_tag."---- | @get_patches_in_tag t ps@ returns a 'SealedPatchSet' of all--- patches in @ps@ which are contained in @t@.-get_patches_in_tag :: RepoPatch p => PatchInfo -> PatchSet p C(x) -> SealedPatchSet p-get_patches_in_tag t pps@((hp:<:NilRL):<:xs)- | info hp == t = seal pps- | otherwise = get_patches_in_tag t xs+ then if getTagsRight patchset == [info hp]+ then flipSeal $ NilRL -- special case to avoid looking at redundant patches+ else case splitOnTag t patchset of _ :>> e -> flipSeal e+ else case getPatchesBeyondTag t (PatchSet ps ts) of+ FlippedSeal xxs -> FlippedSeal (hp :<: xxs)+getPatchesBeyondTag t (PatchSet NilRL NilRL) = bug $ "tag\n" +++ renderString (humanFriendly t) +++ "\nis not in the patchset in getPatchesBeyondTag."+getPatchesBeyondTag t0 (PatchSet NilRL (Tagged t _ ps :<: ts)) =+ getPatchesBeyondTag t0 (PatchSet (t:<:ps) ts) -get_patches_in_tag t ((hp:<:ps):<:xs)- | info hp /= t = get_patches_in_tag t (ps:<:xs)+splitOnTag :: RepoPatch p => PatchInfo -> PatchSet p C(start x) -> (PatchSet p C(start) :>> RL (PatchInfoAnd p)) C(x)+splitOnTag t (PatchSet ps (Tagged hp x ps2 :<: ts))+ | info hp == t = (PatchSet NilRL (Tagged hp x ps2 :<: ts)) :>> ps+splitOnTag t patchset@(PatchSet (hp:<:ps) ts)+ | info hp == t = if getTagsRight patchset == [info hp]+ then PatchSet NilRL (Tagged hp Nothing ps :<: ts) :>> NilRL+ else case partitionRL ((`notElem` (t:ds)) . info) (hp:<:ps) of+ (x:<:a) :> b ->+ if getTagsRight (PatchSet (x:<:a) ts) == [t]+ then PatchSet NilRL (Tagged x Nothing a :<: ts) :>> b+ else case splitOnTag t $ eatOne $ PatchSet (x:<:a) ts of+ xx :>> yy -> xx :>> (b +<+ yy)+ -- NilRL :> _ -> impossible+ where ds = getdeps (hopefully hp)+ eatOne :: PatchSet p C(start x) -> PatchSet p C(start x)+ eatOne (PatchSet ps1 (Tagged x _ ps2 :<: ts')) = PatchSet (ps1+<+x:<:ps2) ts'+ eatOne _ = bug "a stubborn case in splitOnTag (theoretically possible)"+splitOnTag t (PatchSet (p:<:ps) ts) = case splitOnTag t (PatchSet ps ts) of+ ns :>> x -> ns :>> (p:<:x)+splitOnTag t0 (PatchSet NilRL (Tagged t _ ps :<: ts)) = splitOnTag t0 (PatchSet (t:<:ps) ts)+splitOnTag t0 (PatchSet NilRL NilRL) = bug $ "tag\n" +++ renderString (humanFriendly t0) +++ "\nis not in the patchset in splitOnTag." -get_patches_in_tag t ((pa:<:ps):<:xs) = gpit thepis (pa:>:NilFL) (ps:<:xs)- where thepis = getdeps $ conscientiously- (\e -> text "Couldn't read tag"- $$ human_friendly t- $$ text ""- $$ e) pa- gpit :: RepoPatch p => [PatchInfo] -> (FL (PatchInfoAnd p)) C(x y) -> PatchSet p C(x) -> SealedPatchSet p- gpit _ sofar NilRL = seal $ reverseFL sofar :<: NilRL- gpit deps sofar ((hp:<:NilRL):<:xs')- | info hp `elem` deps- , IsEq <- sloppyIdentity hp = seal $ (reverseFL $ hp :>: sofar) :<: xs'- | IsEq <- sloppyIdentity hp = gpit deps sofar xs'- gpit deps sofar (NilRL:<:xs') = gpit deps sofar xs'- gpit deps sofar ((hp:<:ps'):<:xs')- | info hp `elem` deps- = let odeps = filter (/=info hp) deps- alldeps = if is_tag $ info hp- then odeps ++ getdeps (hopefully hp)- else odeps- in gpit alldeps (hp:>:sofar) (ps':<:xs')- | otherwise- = gpit deps (commute_by sofar $ hopefully hp) (ps':<:xs')- commute_by :: RepoPatch p => FL (PatchInfoAnd p) C(x y) -> (Named p) C(w x)- -> FL (PatchInfoAnd p) C(w z)- commute_by NilFL _ = unsafeCoerceP NilFL- commute_by (hpa:>:xs') p =- case commute (p :> hopefully hpa) of- Nothing -> bug "Failure commuting patches in commute_by called by gpit!"- Just (a' :> p') -> (info hpa `piap` a') :>: commute_by xs' p'+-- | @getPatchesInTag t ps@ returns a 'SealedPatchSet' of all+-- patches in @ps@ which are contained in @t@.+getPatchesInTag :: RepoPatch p => PatchInfo -> PatchSet p C(start x) -> SealedPatchSet p C(start)+getPatchesInTag t ns = case splitOnTag t ns of+ ns' :>> _ -> seal ns' -get_patches_in_tag t _ = errorDoc $ text "Couldn't read tag"- $$ human_friendly t+getTagsRight :: PatchSet p C(start x) -> [PatchInfo]+getTagsRight (PatchSet ps NilRL) = getTagsR (mapRL infoAndDeps ps)+getTagsRight (PatchSet ps (Tagged t _ _ :<: _)) = getTagsR (mapRL infoAndDeps (ps+<+t:<:NilRL)) -get_tags_right :: RL (RL (PatchInfoAnd p)) C(x y) -> [PatchInfo]-get_tags_right NilRL = []-get_tags_right (ps:<:_) = get_tags_r (mapRL info_and_deps ps)+getTagsR :: [(PatchInfo, Maybe [PatchInfo])] -> [PatchInfo]+getTagsR [] = []+getTagsR ((i0,Nothing):pps0) = i0 : getTagsR pps0+getTagsR ((i0,Just ds0):pps0) = i0 : getTagsR (drop_tags_r ds0 pps0) where- get_tags_r :: [(PatchInfo, Maybe [PatchInfo])] -> [PatchInfo]- get_tags_r [] = []- get_tags_r (hp:pps) = case snd hp of- Just ds -> fst hp : get_tags_r (drop_tags_r ds pps)- Nothing -> fst hp : get_tags_r pps- drop_tags_r :: [PatchInfo] -> [(PatchInfo, Maybe [PatchInfo])] -> [(PatchInfo, Maybe [PatchInfo])] drop_tags_r [] pps = pps@@ -414,105 +148,193 @@ Just ds' -> drop_tags_r (ds'++delete (fst hp) ds) pps Nothing -> drop_tags_r (delete (fst hp) ds) pps | otherwise = hp : drop_tags_r ds pps- - info_and_deps :: PatchInfoAnd p C(x y) -> (PatchInfo, Maybe [PatchInfo])- info_and_deps p - | is_tag (info p) = (info p, getdeps `fmap` hopefullyM p)++infoAndDeps :: PatchInfoAnd p C(x y) -> (PatchInfo, Maybe [PatchInfo])+infoAndDeps p+ | isTag (info p) = (info p, getdeps `fmap` hopefullyM p) | otherwise = (info p, Nothing) -deep_optimize_patchset :: PatchSet p C(x) -> PatchSet p C(x)-deep_optimize_patchset pss = optimize_patchset (concatRL pss :<: NilRL)+deepOptimizePatchset :: PatchSet p C(start x) -> PatchSet p C(start x)+deepOptimizePatchset ns = optimizePatchset (PatchSet (newset2RL ns) NilRL) -optimize_patchset :: PatchSet p C(x) -> PatchSet p C(x)-optimize_patchset NilRL = NilRL-optimize_patchset (ps:<:pss) = opsp ps +<+ pss- where - opsp :: RL (PatchInfoAnd p) C(x y) -> RL (RL (PatchInfoAnd p)) C(x y)- opsp NilRL = NilRL- opsp (hp:<:pps)- | is_tag (info hp) && get_tags_right ((hp:<:pps):<:NilRL) == [info hp]- = (hp:<:NilRL) :<: opsp pps- | otherwise = hp -:- opsp pps+optimizePatchset :: PatchSet p C(start x) -> PatchSet p C(start x)+optimizePatchset (PatchSet NilRL ts) = PatchSet NilRL ts+optimizePatchset (PatchSet (p:<:ps) ts)+ | isTag (info p) && getTagsRight (PatchSet (p:<:ps) ts) == [info p]+ = case optimizePatchset (PatchSet ps ts) of+ PatchSet ps' ts' -> PatchSet NilRL (Tagged p Nothing ps' :<: ts')+ | otherwise = case optimizePatchset (PatchSet ps ts) of+ PatchSet ps' ts' -> PatchSet (p:<:ps') ts' -(-:-) :: (PatchInfoAnd p) C(x y) -> RL (RL (PatchInfoAnd p)) C(a x) -> RL (RL (PatchInfoAnd p)) C(a y)-pp -:- NilRL = (pp:<:NilRL) :<: NilRL-pp -:- (p:<:ps) = ((pp:<:p) :<: ps)+slightlyOptimizePatchset :: PatchSet p C(start x) -> PatchSet p C(start x)+slightlyOptimizePatchset (PatchSet ps0 ts0) = sops $ PatchSet (progressRL "Optimizing inventory" ps0) ts0+ where sops :: PatchSet p C(start y) -> PatchSet p C(start y)+ sops (PatchSet NilRL ts) = PatchSet NilRL ts+ sops (PatchSet (hp:<:ps) ts)+ | isTag (info hp) = if getTagsRight (PatchSet (hp:<:ps) ts) == [info hp]+ then PatchSet NilRL (Tagged hp Nothing ps :<: ts)+ else case sops $ PatchSet (progressRL "Optimizing inventory" ps) ts of+ PatchSet ps' ts' -> PatchSet (hp:<:ps') ts'+ | otherwise = case sops $ PatchSet ps ts of+ PatchSet ps' ts' -> PatchSet (hp:<:ps') ts' -slightly_optimize_patchset :: PatchSet p C(x) -> PatchSet p C(x)-slightly_optimize_patchset NilRL = NilRL-slightly_optimize_patchset (ps:<:pss) = sops (progressRL "Optimizing inventory" ps) +<+ pss- where sops :: RL (PatchInfoAnd p) C(x y) -> RL (RL (PatchInfoAnd p)) C(x y)- sops NilRL = NilRL- sops (pinfomp :<: NilRL) = (pinfomp :<: NilRL) :<: NilRL- sops (hp:<:pps) | is_tag (info hp) = if get_tags_right ((hp:<:pps):<:NilRL) == [info hp]- then (hp:<:NilRL) :<: (pps:<: NilRL)- else hp -:- sops (progressRL "Optimizing inventory" pps)- | otherwise = hp -:- sops pps+commuteToEnd :: forall p C(start x y). RepoPatch p => RL (PatchInfoAnd p) C(x y)+ -> PatchSet p C(start y) -> (PatchSet p C(start) :>> RL (PatchInfoAnd p)) C(x)+commuteToEnd NilRL (PatchSet ps ts) = PatchSet NilRL ts :>> ps+commuteToEnd (p:<:ps) (PatchSet xs ts)+ | info p `elem` mapRL info xs = case fastRemoveRL p xs of+ Just xs' -> commuteToEnd ps (PatchSet xs' ts)+ Nothing -> impossible -- "Nothing is impossible"+commuteToEnd ps (PatchSet xs (Tagged t _ ys :<: ts)) =+ commuteToEnd ps (PatchSet (xs+<+t:<:ys) ts)+commuteToEnd _ _ = impossible -commute_to_end :: forall p C(x y). RepoPatch p => FL (Named p) C(x y) -> PatchSet p C(y)- -> (FL (Named p) :< RL (RL (PatchInfoAnd p))) C(() x)-commute_to_end select from = ctt (mapFL patch2patchinfo select) from NilFL- where--- In order to preserve the structure of the original PatchSet, we commute--- the patches we are going to throw away past the patches we plan to keep.--- This puts them at the end of the PatchSet where it is safe to discard them.--- We return all the patches in the PatchSet which have been commuted.- ctt :: [PatchInfo] -> PatchSet p C(v) -> FL (Named p) C(v u)- -> (FL (Named p) :< RL (RL (PatchInfoAnd p))) C(() x)- -- This unsafeCoerceP should be fine, because if we run out of- -- patches in the selection the ending context of the second param- -- should be x (because we have commute all of the selected sequence,- -- with context C(x y), past the elements of the second parameter.- -- Unfortunately this is hard to express in the type system while- -- using an accumulator to build up the return value.- ctt [] ps acc = (unsafeCoerceP acc) :< ps- ctt sel (NilRL:<:ps) acc = ctt sel ps acc- ctt sel ((hp:<:hps):<:ps) acc- | info hp `elem` sel- = case commuteFL (hopefully hp :> acc) of- Left _ -> bug "patches to commute_to_end does not commutex (1)"- Right (acc' :> _) -> ctt (delete (info hp) sel) (hps:<:ps) acc'- | otherwise- = ctt sel (hps:<:ps) (hopefully hp:>:acc)- ctt _ _ _ = bug "patches to commute_to_end does not commutex (2)"+removeFromPatchSet :: RepoPatch p => FL (PatchInfoAnd p) C(x y)+ -> PatchSet p C(start y) -> Maybe (PatchSet p C(start x))+removeFromPatchSet bad0 = rfns (reverseFL bad0)+ where rfns :: RepoPatch p => RL (PatchInfoAnd p) C(x y)+ -> PatchSet p C(start y) -> Maybe (PatchSet p C(start x))+ rfns bad (PatchSet ps ts)+ | all (`elem` (mapRL info ps)) (mapRL info bad) =+ do ps' <- removeSubsequenceRL bad ps+ Just $ PatchSet ps' ts+ rfns _ (PatchSet _ NilRL) = Nothing+ rfns bad (PatchSet ps (Tagged t _ tps :<: ts)) =+ rfns bad (PatchSet (ps+<+t:<:tps) ts) -patchset_intersection :: RepoPatch p => [SealedPatchSet p] -> SealedPatchSet p-patchset_intersection [] = seal (NilRL :<: NilRL)-patchset_intersection [x] = x-patchset_intersection (Sealed y:ys) = - case patchset_intersection ys of- Sealed ys' -> with_partial_intersection y ys' $- \common a b -> - case mapRL info a `intersect` mapRL info b of- morecommon -> - case partitionRL (\e -> info e `notElem` morecommon) a of- commonps :> _ -> seal $ commonps :<: common+findCommonWithThem :: RepoPatch p => PatchSet p C(start x) -> PatchSet p C(start y)+ -> (PatchSet p C(start) :>> FL (PatchInfoAnd p)) C(x)+findCommonWithThem us them =+ with_partial_intersection us them $+ \common us' them' ->+ case partitionFL ((`elem` mapRL info them') . info) $ reverseRL us' of+ _ :> bad@(_:>:_) :> _ -> bug $ "Failed to commute common patches:\n" +++ (renderString $ vcat $ mapRL (humanFriendly . info) $ reverseFL bad)+ common2 :> _ :> only_ours -> PatchSet (reverseFL common2) common :>> unsafeCoerceP only_ours -patchset_union :: forall p. RepoPatch p => [SealedPatchSet p] -> SealedPatchSet p-patchset_union [] = seal (NilRL :<: NilRL)-patchset_union [x] = x-patchset_union (Sealed y:ys) = - case patchset_union ys of- Sealed ys' -> with_partial_intersection y ys' f- where- f :: FORALL(z x y) PatchSet p C(z) -- ^ @common@- -> RL (PatchInfoAnd p) C(z x) -- ^ @a@- -> RL (PatchInfoAnd p) C(z y) -- ^ @b@- -> SealedPatchSet p- f common a b = g_s $ gcau_simple a b- where- g_s :: Either MissingPatch- ([PatchInfo],(RL (PatchInfoAnd p) :\/: RL (PatchInfoAnd p)) C(x y))- -> SealedPatchSet p- g_s (Left e) = missingPatchError e- g_s (Right (_, a' :\/: b')) =- case (merge_sets (a' :\/: b')) of- Sealed a'b' -> seal $ (a'b' +<+ b) :<: common+findUncommon :: RepoPatch p => PatchSet p C(start x) -> PatchSet p C(start y)+ -> (FL (PatchInfoAnd p) :\/: FL (PatchInfoAnd p)) C(x y)+findUncommon us them =+ case findCommonWithThem us them of+ common :>> us' -> case findCommonWithThem them us of+ _ :>> them' -> unsafeCoercePStart us' :\/: them' -merge_sets :: RepoPatch p => (RL (PatchInfoAnd p) :\/: RL (PatchInfoAnd p)) C(x y) -> Sealed (RL (PatchInfoAnd p) C(y))-merge_sets (l :\/: r) =- let pl = mapFL_FL hopefully $ reverseRL l- pr = mapFL_FL hopefully $ reverseRL r- p2pimp p = patch2patchinfo p `piap` p- in case merge (pl:\/: pr) of- (_:/\:pl') -> seal $ reverseFL $ mapFL_FL p2pimp pl'+countUsThem :: RepoPatch p => PatchSet p C(start x) -> PatchSet p C(start y) -> (Int, Int)+countUsThem us them =+ with_partial_intersection us them $+ \_ us' them' -> let uu = mapRL info us'+ tt = mapRL info them'+ in (length $ uu \\ tt, length $ tt \\ uu)++mergeThem :: RepoPatch p => PatchSet p C(start x) -> PatchSet p C(start y)+ -> Sealed (FL (PatchInfoAnd p) C(x))+mergeThem us them =+ with_partial_intersection us them $+ \_ us' them' -> merge2FL (reverseRL us') (reverseRL them')++newsetIntersection :: RepoPatch p => [SealedPatchSet p C(start)] -> SealedPatchSet p C(start)+newsetIntersection [] = seal $ PatchSet NilRL NilRL+newsetIntersection [x] = x+newsetIntersection (Sealed y:ys) =+ case newsetIntersection ys of+ Sealed z -> with_partial_intersection y z $+ \common a b ->+ case mapRL info a `intersect` mapRL info b of+ morecommon ->+ case partitionRL (\e -> info e `notElem` morecommon) a of+ commonps :> _ -> seal $ PatchSet commonps common++newsetUnion :: RepoPatch p => [SealedPatchSet p C(start)] -> SealedPatchSet p C(start)+newsetUnion [] = seal $ PatchSet NilRL NilRL+newsetUnion [x] = x+newsetUnion (Sealed y@(PatchSet psy tsy):Sealed y2:ys) =+ case mergeThem y y2 of+ Sealed p2 -> newsetUnion $ seal (PatchSet (reverseFL p2+<+psy) tsy) : ys++-- | Merge two FLs (say L and R), starting in a common context. The result is a+-- FL starting in the original end context of L, going to a new context that is+-- the result of applying all patches from R on top of patches from L.+--+-- While this function is similar to 'mergeFL', there are three important+-- differences to keep in mind:+--+-- * 'mergeFL' does not correctly deal with duplicate patches whereas this one+-- does+-- (Question from Eric Kow: in what sense? Why not fix the mergeFL instance?)+--+-- * 'mergeFL' returns both paths of the merge diamond, but this version only+-- returns one, so you'd better choose the order carefully, eg.+-- (@merge2FL l r@)+--+-- * The conventional order we use in this function is reversed from+-- 'mergeFL' (so @mergeFL r l@ vs. @merge2FL l r@. This does not+-- matter so much for the former since you get both paths.+-- (Question from Eric Kow: should we flip merge2FL for more uniformity in+-- the code?)+merge2FL :: RepoPatch p => FL (PatchInfoAnd p) C(x y)+ -> FL (PatchInfoAnd p) C(x z)+ -> Sealed (FL (PatchInfoAnd p) C(y))+merge2FL _ NilFL = seal NilFL+merge2FL NilFL ys = seal ys+merge2FL xs (y:>:ys) | Just xs' <- fastRemoveFL y xs = merge2FL xs' ys+merge2FL (x:>:xs) ys | Just ys' <- fastRemoveFL x ys = merge2FL xs ys'+ | otherwise = case mergeFL (x :\/: ys) of+ ys' :/\: _ -> merge2FL xs ys'++simpleTag :: PatchInfo -> PatchSet p C(start x) -> Maybe (PatchSet p C(start x))+simpleTag t0 (PatchSet ps (Tagged t h pst :<: ts))+ | t0 == info t = Just $ PatchSet ps (Tagged t h pst :<: ts)+ | otherwise = do PatchSet ps' ts' <- simpleTag t0 (PatchSet (t:<:pst) ts)+ Just $ PatchSet (ps +<+ ps') ts'+simpleTag _ _ = Nothing++areUnrelatedRepos :: RepoPatch p => PatchSet p C(start x) -> PatchSet p C(start y) -> Bool+areUnrelatedRepos us them =+ with_partial_intersection us them checkit+ where checkit (Tagged _ _ _ :<: _) _ _ = False+ checkit _ u t | t `isShorterThanRL` 5 = False+ | u `isShorterThanRL` 5 = False+ | otherwise = null $ intersect (mapRL info u) (mapRL info t)++-- | Remove a patch from FL, using PatchInfo equality. The result is Just+-- whenever the patch has been found and removed. If the patch is not present+-- in the sequence at all or any commutation fails, we get Nothing. First two+-- cases are optimisations for the common cases where the head of the list is+-- the patch to remove, or the patch is not there at all.+fastRemoveFL :: RepoPatch p => PatchInfoAnd p C(x y) -> FL (PatchInfoAnd p) C(x z)+ -> Maybe (FL (PatchInfoAnd p) C(y z))+fastRemoveFL _ NilFL = Nothing+fastRemoveFL a (b:>:bs) | IsEq <- a =\/= b = Just bs+ | info a `notElem` mapFL info bs = Nothing+fastRemoveFL a (b:>:bs) = do a' :> bs' <- pullout NilRL bs+ a'' :> b' <- commute (b :> a')+ IsEq <- return (a'' =\/= a)+ Just (b':>:bs')+ where i = info a+ pullout :: RepoPatch p => RL (PatchInfoAnd p) C(a0 a)+ -> FL (PatchInfoAnd p) C(a b)+ -> Maybe ((PatchInfoAnd p :> FL (PatchInfoAnd p)) C(a0 b))+ pullout _ NilFL = Nothing+ pullout acc (x:>:xs) | info x == i = do x' :> acc' <- commuteRL (acc :> x)+ Just (x' :> reverseRL acc' +>+ xs)+ | otherwise = pullout (x:<:acc) xs++fastRemoveRL :: RepoPatch p => PatchInfoAnd p C(y z) -> RL (PatchInfoAnd p) C(x z)+ -> Maybe (RL (PatchInfoAnd p) C(x y))+fastRemoveRL _ NilRL = Nothing+fastRemoveRL a (b:<:bs) | IsEq <- a =/\= b = Just bs+ | info a `notElem` mapRL info bs = Nothing+fastRemoveRL a (b:<:bs) = do bs' :> a' <- pullout NilFL bs+ b' :> a'' <- commute (a' :> b)+ IsEq <- return (a'' =/\= a)+ Just (b':<:bs')+ where i = info a+ pullout :: RepoPatch p => FL (PatchInfoAnd p) C(b c)+ -> RL (PatchInfoAnd p) C(a b)+ -> Maybe ((RL (PatchInfoAnd p) :> PatchInfoAnd p) C(a c))+ pullout _ NilRL = Nothing+ pullout acc (x:<:xs) | info x == i = do acc' :> x' <-+ either (const Nothing)+ Just (commuteFLorComplain (x :> acc))+ Just (reverseFL acc' +<+ xs :> x')+ | otherwise = pullout (x:>:acc) xs
src/Darcs/Patch/FileName.hs view
@@ -21,28 +21,24 @@ fp2fn, fn2fp, fn2ps, ps2fn, niceps2fn, fn2niceps,- break_on_dir, norm_path, own_name, superName,+ breakOnDir, normPath, ownName, superName, movedirfilename,- encode_white, decode_white,+ encodeWhite, decodeWhite, (///), breakup ) where import System.IO import Data.Char ( isSpace, chr, ord )-import qualified Codec.Binary.UTF8.String as UTF8 ( encode )-import Codec.Binary.UTF8.Generic ( toString )-import Data.Word ( Word8( ) )+import ByteStringUtils ( packStringToUTF8, unpackPSFromUTF8 ) import qualified Data.ByteString.Char8 as BC (unpack, pack)-import qualified Data.ByteString as B (ByteString, pack)+import qualified Data.ByteString as B (ByteString) newtype FileName = FN FilePath deriving ( Eq, Ord )-encode :: [Char] -> [Word8]-encode = UTF8.encode instance Show FileName where- showsPrec d (FN fp) = showParen (d > app_prec) $ showString "fp2fn " . showsPrec (app_prec + 1) fp- where app_prec = 10+ showsPrec d (FN fp) = showParen (d > appPrec) $ showString "fp2fn " . showsPrec (appPrec + 1) fp+ where appPrec = 10 {-# INLINE fp2fn #-} fp2fn :: FilePath -> FileName@@ -54,77 +50,78 @@ {-# INLINE niceps2fn #-} niceps2fn :: B.ByteString -> FileName-niceps2fn = FN . decode_white . BC.unpack+niceps2fn = FN . decodeWhite . BC.unpack {-# INLINE fn2niceps #-} fn2niceps :: FileName -> B.ByteString-fn2niceps (FN fp) = BC.pack $ encode_white fp+fn2niceps (FN fp) = BC.pack $ encodeWhite fp {-# INLINE fn2ps #-} fn2ps :: FileName -> B.ByteString-fn2ps (FN fp) = B.pack $ encode $ encode_white fp+fn2ps (FN fp) = packStringToUTF8 $ encodeWhite fp {-# INLINE ps2fn #-} ps2fn :: B.ByteString -> FileName-ps2fn ps = FN $ decode_white $ toString ps+ps2fn ps = FN $ decodeWhite $ unpackPSFromUTF8 ps --- | 'encode_white' translates whitespace in filenames to a darcs-specific--- format (backslash followed by numerical representation according to 'ord').--- Note that backslashes are also escaped since they are used in the encoding.+-- | 'encodeWhite' translates whitespace in filenames to a darcs-specific+-- format (numerical representation according to 'ord' surrounded by+-- backslashes). Note that backslashes are also escaped since they are used+-- in the encoding. ----- > encode_white "hello there" == "hello\32there"--- > encode_white "hello\there" == "hello\\there"-encode_white :: FilePath -> String-encode_white (c:cs) | isSpace c || c == '\\' =- '\\' : (show $ ord c) ++ "\\" ++ encode_white cs-encode_white (c:cs) = c : encode_white cs-encode_white [] = []+-- > encodeWhite "hello there" == "hello\32\there"+-- > encodeWhite "hello\there" == "hello\92\there"+encodeWhite :: FilePath -> String+encodeWhite (c:cs) | isSpace c || c == '\\' =+ '\\' : (show $ ord c) ++ "\\" ++ encodeWhite cs+encodeWhite (c:cs) = c : encodeWhite cs+encodeWhite [] = [] --- | 'decode_white' interprets the Darcs-specific \"encoded\" filenames--- produced by 'encode_white'+-- | 'decodeWhite' interprets the Darcs-specific \"encoded\" filenames+-- produced by 'encodeWhite' ----- > decode_white "hello\32there" == "hello there"--- > decode_white "hello\\there" == "hello\there"--- > decode_white "hello\there" == error "malformed filename"-decode_white :: String -> FilePath-decode_white ('\\':cs) =+-- > decodeWhite "hello\32\there" == "hello there"+-- > decodeWhite "hello\92\there" == "hello\there"+-- > decodeWhite "hello\there" == error "malformed filename"+decodeWhite :: String -> FilePath+decodeWhite ('\\':cs) = case break (=='\\') cs of (theord, '\\':rest) ->- chr (read theord) : decode_white rest+ chr (read theord) : decodeWhite rest _ -> error "malformed filename"-decode_white (c:cs) = c: decode_white cs-decode_white "" = ""+decodeWhite (c:cs) = c: decodeWhite cs+decodeWhite "" = "" -own_name :: FileName -> FileName-own_name (FN f) = case breakLast '/' f of Nothing -> FN f+ownName :: FileName -> FileName+ownName (FN f) = case breakLast '/' f of Nothing -> FN f Just (_,f') -> FN f' superName :: FileName -> FileName-superName fn = case norm_path fn of+superName fn = case normPath fn of FN f -> case breakLast '/' f of Nothing -> FN "." Just (d,_) -> FN d-break_on_dir :: FileName -> Maybe (FileName,FileName)-break_on_dir (FN p) = case breakFirst '/' p of+breakOnDir :: FileName -> Maybe (FileName,FileName)+breakOnDir (FN p) = case breakFirst '/' p of Nothing -> Nothing- Just (d,f) | d == "." -> break_on_dir $ FN f+ Just (d,f) | d == "." -> breakOnDir $ FN f | otherwise -> Just (FN d, FN f)-norm_path :: FileName -> FileName -- remove "./"-norm_path (FN p) = FN $ repath $ drop_dotdot $ breakup p+normPath :: FileName -> FileName -- remove "./"+normPath (FN p) = FN $ repath $ dropDotdot $ breakup p repath :: [String] -> String repath [] = "" repath [f] = f repath (d:p) = d ++ "/" ++ repath p -drop_dotdot :: [String] -> [String]-drop_dotdot ("":p) = drop_dotdot p-drop_dotdot (".":p) = drop_dotdot p-drop_dotdot ("..":p) = ".." : (drop_dotdot p)-drop_dotdot (_:"..":p) = drop_dotdot p-drop_dotdot (d:p) = case drop_dotdot p of+dropDotdot :: [String] -> [String]+dropDotdot ("":p) = dropDotdot p+dropDotdot (".":p) = dropDotdot p+dropDotdot ("..":p) = ".." : (dropDotdot p)+dropDotdot (_:"..":p) = dropDotdot p+dropDotdot (d:p) = case dropDotdot p of ("..":p') -> p' p' -> d : p'-drop_dotdot [] = []+dropDotdot [] = [] -- | Split a file path at the slashes breakup :: String -> [String]@@ -143,8 +140,8 @@ Just (a,b) -> Just (reverse b, reverse a) (///) :: FileName -> FileName -> FileName-(FN "")///b = norm_path b-a///b = norm_path $ fp2fn $ fn2fp a ++ "/" ++ fn2fp b+(FN "")///b = normPath b+a///b = normPath $ fp2fn $ fn2fp a ++ "/" ++ fn2fp b movedirfilename :: FileName -> FileName -> FileName -> FileName movedirfilename old new name =@@ -153,6 +150,6 @@ take (length old'+1) name' == old'++"/" then fp2fn ("./"++new'++drop (length old') name') else name- where old' = fn2fp $ norm_path old- new' = fn2fp $ norm_path new- name' = fn2fp $ norm_path name+ where old' = fn2fp $ normPath old+ new' = fn2fp $ normPath new+ name' = fn2fp $ normPath name
src/Darcs/Patch/Info.hs view
@@ -15,22 +15,25 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -module Darcs.Patch.Info ( PatchInfo, patchinfo, invert_name, is_inverted,- idpatchinfo, add_junk,- make_filename, make_alt_filename, readPatchInfo,- just_name, just_author, repopatchinfo, RepoPatchInfo,- human_friendly, to_xml, pi_date, set_pi_date,- pi_name, pi_rename, pi_author, pi_tag, pi_log,- showPatchInfo, is_tag+module Darcs.Patch.Info ( PatchInfo(..), patchinfo, invertName,+ idpatchinfo, addJunk,+ makeFilename, makeAltFilename, readPatchInfo,+ justName, justAuthor, justLog, repopatchinfo,+ RepoPatchInfo, humanFriendly, toXml, piDate,+ setPiDate, piDateString, piDateBytestring,+ piName, piRename, piAuthor, piTag, piLog,+ showPatchInfo, isTag ) where import Text.Html hiding (name, text) import System.Random ( randomRIO ) import Numeric ( showHex ) import Control.Monad ( when ) -import ByteStringUtils +import ByteStringUtils import qualified Data.ByteString as B (length, splitAt, null, drop- ,isPrefixOf, tail, concat, ByteString )+ ,isPrefixOf, tail, concat+ ,empty, head, cons, append+ ,ByteString ) import qualified Data.ByteString.Char8 as BC (index, head, unpack, pack, break) import Data.List( isPrefixOf ) @@ -49,11 +52,18 @@ repopatchinfo :: String -> PatchInfo -> RepoPatchInfo repopatchinfo r pi = RPI r pi -data PatchInfo = PatchInfo { _pi_date :: !B.ByteString- , _pi_name :: !B.ByteString- , _pi_author :: !B.ByteString- , _pi_log :: ![B.ByteString]- , is_inverted :: !Bool+-- | A PatchInfo value contains the metadata of a patch. The date, name, author+-- and log fields are UTF-8 encoded text in darcs 2.4 and later, and just+-- sequences of bytes (decoded with whatever is the locale when displayed) in+-- earlier darcs.+--+-- The members with names that start with '_' are not supposed to be used+-- directly in code that does not care how the patch info is stored.+data PatchInfo = PatchInfo { _piDate :: !B.ByteString+ , _piName :: !B.ByteString+ , _piAuthor :: !B.ByteString+ , _piLog :: ![B.ByteString]+ , isInverted :: !Bool } deriving (Eq,Ord) @@ -63,65 +73,74 @@ patchinfo :: String -> String -> String -> [String] -> IO PatchInfo patchinfo date name author log =- add_junk $ PatchInfo { _pi_date = BC.pack date- , _pi_name = BC.pack name- , _pi_author = BC.pack author- , _pi_log = map BC.pack log- , is_inverted = False }+ addJunk $ PatchInfo { _piDate = BC.pack date+ , _piName = packStringToUTF8 name+ , _piAuthor = packStringToUTF8 author+ , _piLog = map packStringToUTF8 log+ , isInverted = False } -add_junk :: PatchInfo -> IO PatchInfo-add_junk pinf =+-- | addJunk adds a line that contains a random number to make the patch+-- unique.+addJunk :: PatchInfo -> IO PatchInfo+addJunk pinf = do x <- randomRIO (0,2^(128 ::Integer) :: Integer)- when (_pi_log pinf /= ignore_junk (_pi_log pinf)) $+ when (_piLog pinf /= ignoreJunk (_piLog pinf)) $ do putStrLn "Lines beginning with 'Ignore-this: ' will be ignored." yorn <- promptYorn "Proceed? " when (yorn == 'n') $ fail "User cancelled because of Ignore-this."- return $ pinf { _pi_log = BC.pack (head ignored++showHex x ""):- _pi_log pinf }+ return $ pinf { _piLog = BC.pack (head ignored++showHex x ""):+ _piLog pinf } ignored :: [String] -- this is a [String] so we can change the junk header. ignored = ["Ignore-this: "] -ignore_junk :: [B.ByteString] -> [B.ByteString]-ignore_junk = filter isnt_ignored+ignoreJunk :: [B.ByteString] -> [B.ByteString]+ignoreJunk = filter isnt_ignored where isnt_ignored x = doesnt_start_with x (map BC.pack ignored) -- TODO doesnt_start_with x ys = not $ any (`B.isPrefixOf` x) ys -- * Patch info formatting-invert_name :: PatchInfo -> PatchInfo-invert_name pi = pi { is_inverted = not (is_inverted pi) }+invertName :: PatchInfo -> PatchInfo+invertName pi = pi { isInverted = not (isInverted pi) } -just_name :: PatchInfo -> String-just_name pinf = if is_inverted pinf then "UNDO: " ++ BC.unpack (_pi_name pinf)- else BC.unpack (_pi_name pinf)+-- | Get the name, including an "UNDO: " prefix if the patch is inverted.+justName :: PatchInfo -> String+justName pinf = if isInverted pinf then "UNDO: " ++ nameString+ else nameString+ where nameString = metadataToString (_piName pinf) -just_author :: PatchInfo -> String-just_author = BC.unpack . _pi_author+-- | Returns the author of a patch.+justAuthor :: PatchInfo -> String+justAuthor = metadataToString . _piAuthor -human_friendly :: PatchInfo -> Doc-human_friendly pi =- text (friendly_d $ _pi_date pi) <> text " " <> packedString (_pi_author pi)- $$ hfn (_pi_name pi)- $$ vcat (map ((text " " <>) . packedString) (ignore_junk $ _pi_log pi))- where hfn x = case pi_tag pi of- Nothing -> inverted <+> packedString x- Just t -> text " tagged" <+> text t- inverted = if is_inverted pi then text " UNDO:" else text " *"+justLog :: PatchInfo -> String+justLog = unlines . map BC.unpack . _piLog --- note the difference with just_name-pi_name :: PatchInfo -> String-pi_name = BC.unpack . _pi_name+humanFriendly :: PatchInfo -> Doc+humanFriendly pi =+ text (friendlyD $ _piDate pi) <> text " " <> text (piAuthor pi)+ $$ hfn (piName pi)+ $$ vcat (map ((text " " <>) . text) (piLog pi))+ where hfn x = case piTag pi of+ Nothing -> inverted <+> text x+ Just t -> text " tagged" <+> text t+ inverted = if isInverted pi then text " UNDO:" else text " *" -pi_rename :: PatchInfo -> String -> PatchInfo-pi_rename x n = x { _pi_name = BC.pack n }+-- | Returns the name of the patch. Unlike 'justName', it does not preprend+-- "UNDO: " to the name if the patch is inverted.+piName :: PatchInfo -> String+piName = metadataToString . _piName -pi_author :: PatchInfo -> String-pi_author = BC.unpack . _pi_author+piRename :: PatchInfo -> String -> PatchInfo+piRename x n = x { _piName = packStringToUTF8 n } -is_tag :: PatchInfo -> Bool-is_tag pinfo = "TAG " `isPrefixOf` just_name pinfo+-- | Returns the author of a patch.+piAuthor :: PatchInfo -> String+piAuthor = metadataToString . _piAuthor +isTag :: PatchInfo -> Bool+isTag pinfo = "TAG " `isPrefixOf` justName pinfo -- | Note: we ignore timezone information in the date string, -- systematically treating a time as UTC. So if the patch@@ -134,46 +153,63 @@ readPatchDate = ignoreTz . readUTCDate . BC.unpack where ignoreTz ct = ct { ctTZ = 0 } -pi_date :: PatchInfo -> CalendarTime-pi_date = readPatchDate . _pi_date+piDate :: PatchInfo -> CalendarTime+piDate = readPatchDate . _piDate -set_pi_date :: String -> PatchInfo -> PatchInfo-set_pi_date date pi = pi { _pi_date = BC.pack date }+piDateString :: PatchInfo -> String+piDateString = BC.unpack . _piDate -pi_log :: PatchInfo -> [String]-pi_log = map BC.unpack . ignore_junk . _pi_log+piDateBytestring :: PatchInfo -> B.ByteString+piDateBytestring = _piDate -pi_tag :: PatchInfo -> Maybe String-pi_tag pinf =+setPiDate :: String -> PatchInfo -> PatchInfo+setPiDate date pi = pi { _piDate = BC.pack date }++-- | Get the log message of a patch.+piLog :: PatchInfo -> [String]+piLog = map metadataToString . ignoreJunk . _piLog++-- | Get the tag name, if the patch is a tag patch.+piTag :: PatchInfo -> Maybe String+piTag pinf = if l == t- then Just $ BC.unpack r+ then Just $ metadataToString r else Nothing- where (l, r) = B.splitAt (B.length t) (_pi_name pinf)+ where (l, r) = B.splitAt (B.length t) (_piName pinf) t = BC.pack "TAG " -friendly_d :: B.ByteString -> String---friendly_d d = calendarTimeToString . readPatchDate . d-friendly_d d = unsafePerformIO $ do+-- | Convert a metadata ByteString to a string. It first tries to convert+-- using UTF-8, and if that fails, tries the locale encoding.+-- We try UTF-8 first because UTF-8 is clearly recognizable, widely used,+-- and people may have UTF-8 patches even when UTF-8 is not their locale.+metadataToString :: B.ByteString -> String+metadataToString bs | not ('\xfffd' `elem` bsUtf8) = bsUtf8+ | otherwise = decodeLocale bs+ where bsUtf8 = unpackPSFromUTF8 bs++friendlyD :: B.ByteString -> String+--friendlyD d = calendarTimeToString . readPatchDate . d+friendlyD d = unsafePerformIO $ do ct <- toCalendarTime $ toClockTime $ readPatchDate d return $ calendarTimeToString ct -to_xml :: PatchInfo -> Doc-to_xml pi =+toXml :: PatchInfo -> Doc+toXml pi = text "<patch"- <+> text "author='" <> escapeXML (just_author pi) <> text "'"- <+> text "date='" <> escapeXML (BC.unpack $ _pi_date pi) <> text "'"- <+> text "local_date='" <> escapeXML (friendly_d $ _pi_date pi) <> text "'"- <+> text "inverted='" <> text (show $ is_inverted pi) <> text "'"- <+> text "hash='" <> text (make_filename pi) <> text "'>"+ <+> text "author='" <> escapeXMLByteString (_piAuthor pi) <> text "'"+ <+> text "date='" <> escapeXMLByteString (_piDate pi) <> text "'"+ <+> text "local_date='" <> escapeXML (friendlyD $ _piDate pi) <> text "'"+ <+> text "inverted='" <> text (show $ isInverted pi) <> text "'"+ <+> text "hash='" <> text (makeFilename pi) <> text "'>" $$ prefix "\t" (- text "<name>" <> escapeXML (pi_name pi) <> text "</name>"- $$ comments_as_xml (_pi_log pi))+ text "<name>" <> escapeXMLByteString (_piName pi) <> text "</name>"+ $$ commentsAsXml (_piLog pi)) $$ text "</patch>" -comments_as_xml :: [B.ByteString] -> Doc-comments_as_xml comments+commentsAsXml :: [B.ByteString] -> Doc+commentsAsXml comments | B.length comments' > 0 = text "<comment>"- <> escapeXML (BC.unpack comments')+ <> escapeXMLByteString comments' <> text "</comment>" | otherwise = empty where comments' = unlinesPS comments@@ -184,6 +220,14 @@ escapeXML = text . strReplace '\'' "'" . strReplace '"' """ . strReplace '>' ">" . strReplace '<' "<" . strReplace '&' "&" +-- Escape XML characters in a UTF-8 encoded ByteString, and turn it into a Doc.+-- The data will be in the Doc as a bytestring.+escapeXMLByteString :: B.ByteString -> Doc+escapeXMLByteString = packedString . bstrReplace '\'' "'"+ . bstrReplace '"' """+ . bstrReplace '>' ">"+ . bstrReplace '<' "<"+ . bstrReplace '&' "&" strReplace :: Char -> String -> String -> String strReplace _ _ [] = []@@ -191,61 +235,69 @@ | x == z = y ++ (strReplace x y zs) | otherwise = z : (strReplace x y zs) -make_alt_filename :: PatchInfo -> String-make_alt_filename pi@(PatchInfo { is_inverted = False }) =- fix_up_fname (midtrunc (pi_name pi)++"-"++just_author pi++"-"++BC.unpack (_pi_date pi))-make_alt_filename pi@(PatchInfo { is_inverted = True}) =- make_alt_filename (pi { is_inverted = False }) ++ "-inverted"+bstrReplace :: Char -> String -> B.ByteString -> B.ByteString+bstrReplace c s bs | B.null bs = B.empty+ | otherwise = if BC.head bs == c+ then B.append (BC.pack s)+ (bstrReplace c s (B.tail bs))+ else B.cons (B.head bs)+ (bstrReplace c s (B.tail bs)) +makeAltFilename :: PatchInfo -> String+makeAltFilename pi@(PatchInfo { isInverted = False }) =+ fixUpFname (midtrunc (piName pi)++"-"++justAuthor pi++"-"++BC.unpack (_piDate pi))+makeAltFilename pi@(PatchInfo { isInverted = True}) =+ makeAltFilename (pi { isInverted = False }) ++ "-inverted"+ -- This makes darcs-1 (non-hashed repos) filenames, and is also generally used in both in -- hashed and non-hashed repo code for making patch "hashes"-make_filename :: PatchInfo -> String-make_filename pi =+makeFilename :: PatchInfo -> String+makeFilename pi = showIsoDateTime d++"-"++sha1_a++"-"++sha1PS sha1_me++".gz" where b2ps True = BC.pack "t" b2ps False = BC.pack "f"- sha1_me = B.concat [_pi_name pi,- _pi_author pi,- _pi_date pi,- B.concat $ _pi_log pi,- b2ps $ is_inverted pi]- d = readPatchDate $ _pi_date pi- sha1_a = take 5 $ sha1PS $ _pi_author pi+ sha1_me = B.concat [_piName pi,+ _piAuthor pi,+ _piDate pi,+ B.concat $ _piLog pi,+ b2ps $ isInverted pi]+ d = readPatchDate $ _piDate pi+ sha1_a = take 5 $ sha1PS $ _piAuthor pi midtrunc :: String -> String midtrunc s | length s < 73 = s | otherwise = (take 40 s)++"..."++(reverse $ take 30 $ reverse s)-fix_up_fname :: String -> String-fix_up_fname = map munge_char+fixUpFname :: String -> String+fixUpFname = map mungeChar -munge_char :: Char -> Char-munge_char '*' = '+'-munge_char '?' = '2'-munge_char '>' = '7'-munge_char '<' = '2'-munge_char ' ' = '_'-munge_char '"' = '~'-munge_char '`' = '.'-munge_char '\'' = '.'-munge_char '/' = '1'-munge_char '\\' = '1'-munge_char '!' = '1'-munge_char ':' = '.'-munge_char ';' = ','-munge_char '{' = '~'-munge_char '}' = '~'-munge_char '(' = '~'-munge_char ')' = '~'-munge_char '[' = '~'-munge_char ']' = '~'-munge_char '=' = '+'-munge_char '#' = '+'-munge_char '%' = '8'-munge_char '&' = '6'-munge_char '@' = '9'-munge_char '|' = '1'-munge_char c = c+mungeChar :: Char -> Char+mungeChar '*' = '+'+mungeChar '?' = '2'+mungeChar '>' = '7'+mungeChar '<' = '2'+mungeChar ' ' = '_'+mungeChar '"' = '~'+mungeChar '`' = '.'+mungeChar '\'' = '.'+mungeChar '/' = '1'+mungeChar '\\' = '1'+mungeChar '!' = '1'+mungeChar ':' = '.'+mungeChar ';' = ','+mungeChar '{' = '~'+mungeChar '}' = '~'+mungeChar '(' = '~'+mungeChar ')' = '~'+mungeChar '[' = '~'+mungeChar ']' = '~'+mungeChar '=' = '+'+mungeChar '#' = '+'+mungeChar '%' = '8'+mungeChar '&' = '6'+mungeChar '@' = '9'+mungeChar '|' = '1'+mungeChar c = c instance HTML RepoPatchInfo where toHtml = htmlPatchInfo@@ -265,10 +317,10 @@ -- note that below I assume the name has no newline in it. showPatchInfo :: PatchInfo -> Doc showPatchInfo pi =- blueText "[" <> packedString (_pi_name pi)- $$ packedString (_pi_author pi) <> text inverted <> packedString (_pi_date pi)- <> myunlines (_pi_log pi) <> blueText "] "- where inverted = if is_inverted pi then "*-" else "**"+ blueText "[" <> packedString (_piName pi)+ $$ packedString (_piAuthor pi) <> text inverted <> packedString (_piDate pi)+ <> myunlines (_piLog pi) <> blueText "] "+ where inverted = if isInverted pi then "*-" else "**" myunlines [] = empty myunlines xs = mul xs where mul [] = text "\n"@@ -290,18 +342,18 @@ (author,s2) -> case BC.break (\c->c==']'||c=='\n') $ B.drop 2 s2 of (ct,s''') ->- do (log, s4) <- lines_starting_with_ending_with ' ' ']' $ dn s'''- return $ (PatchInfo { _pi_date = ct- , _pi_name = name- , _pi_author = author- , _pi_log = log- , is_inverted = BC.index s2 1 /= '*'+ do (log, s4) <- linesStartingWithEndingWith ' ' ']' $ dn s'''+ return $ (PatchInfo { _piDate = ct+ , _piName = name+ , _piAuthor = author+ , _piLog = log+ , isInverted = BC.index s2 1 /= '*' }, s4) where dn x = if B.null x || BC.head x /= '\n' then x else B.tail x -lines_starting_with_ending_with :: Char -> Char -> B.ByteString+linesStartingWithEndingWith :: Char -> Char -> B.ByteString -> Maybe ([B.ByteString],B.ByteString)-lines_starting_with_ending_with st en s = lswew s+linesStartingWithEndingWith st en s = lswew s where lswew x | B.null x = Nothing lswew x =@@ -320,14 +372,14 @@ htmlPatchInfo :: RepoPatchInfo -> Html htmlPatchInfo (RPI r pi) =- toHtml $ (td << patch_link r pi) `above`- ((td ! [align "right"] << mail_link (just_author pi)) `beside`- (td << (friendly_d $ _pi_date pi)))+ toHtml $ (td << patchLink r pi) `above`+ ((td ! [align "right"] << mailLink (justAuthor pi)) `beside`+ (td << (friendlyD $ _piDate pi))) -patch_link :: String -> PatchInfo -> Html-patch_link r pi =+patchLink :: String -> PatchInfo -> Html+patchLink r pi = toHtml $ hotlink- ("darcs?"++r++"**"++make_filename pi)- [toHtml $ pi_name pi]-mail_link :: String -> Html-mail_link email = toHtml $ hotlink ("mailto:"++email) [toHtml email]+ ("darcs?"++r++"**"++makeFilename pi)+ [toHtml $ piName pi]+mailLink :: String -> Html+mailLink email = toHtml $ hotlink ("mailto:"++email) [toHtml email]
src/Darcs/Patch/Match.lhs view
@@ -22,10 +22,10 @@ #include "gadts.h" module Darcs.Patch.Match ( PatchMatch, Matcher, MatchFun,- patch_match, match_pattern,- apply_matcher, make_matcher,+ patchMatch, matchPattern,+ applyMatcher, makeMatcher, parseMatch,- match_parser, helpOnMatchers,+ matchParser, helpOnMatchers, ) where import Text.ParserCombinators.Parsec@@ -36,12 +36,12 @@ import Darcs.Hopefully ( PatchInfoAnd, hopefully, info ) import Darcs.Patch ( Patch, Patchy, hunkMatches, listTouchedFiles, patchcontents )-import Darcs.Patch.Info ( just_name, just_author, make_filename,- pi_date )+import Darcs.Patch.Info ( justName, justAuthor, justLog, makeFilename,+ piDate ) import Darcs.Witnesses.Sealed ( Sealed2(..), seal2 ) import DateMatcher ( parseDateMatcher ) -import Darcs.Patch.MatchData ( PatchMatch(..), patch_match )+import Darcs.Patch.MatchData ( PatchMatch(..), patchMatch ) import qualified Data.ByteString.Char8 as BC -- | A type for predicates over patches which do not care about@@ -55,25 +55,25 @@ instance Show (Matcher p) where show (MATCH s _) = '"':s ++ "\"" -make_matcher :: String -> (Sealed2 (PatchInfoAnd p) -> Bool) -> Matcher p-make_matcher s m = MATCH s m+makeMatcher :: String -> (Sealed2 (PatchInfoAnd p) -> Bool) -> Matcher p+makeMatcher s m = MATCH s m --- | @apply_matcher@ applies a matcher to a patch.-apply_matcher :: Matcher p -> PatchInfoAnd p C(x y) -> Bool-apply_matcher (MATCH _ m) = m . seal2+-- | @applyMatcher@ applies a matcher to a patch.+applyMatcher :: Matcher p -> PatchInfoAnd p C(x y) -> Bool+applyMatcher (MATCH _ m) = m . seal2 parseMatch :: Patchy p => PatchMatch -> Either String (MatchFun p) parseMatch (PatternMatch s) =- case parse match_parser "match" s of+ case parse matchParser "match" s of Left err -> Left $ "Invalid -"++"-match pattern '"++s++ "'.\n"++ unlines (map (" "++) $ lines $ show err) -- indent Right m -> Right m -match_pattern :: Patchy p => PatchMatch -> Matcher p-match_pattern p@(PatternMatch s) =+matchPattern :: Patchy p => PatchMatch -> Matcher p+matchPattern p@(PatternMatch s) = case parseMatch p of Left err -> error err- Right m -> make_matcher s m+ Right m -> makeMatcher s m trivial :: Patchy p => MatchFun p trivial = const True@@ -81,7 +81,7 @@ \subsection{Match} -Currently \verb!--match! accepts six primitive match types, although+Currently \verb!--match! accepts eight primitive match types, although there are plans to expand it to match more patterns. Also, note that the syntax is still preliminary and subject to change. @@ -199,6 +199,18 @@ darcs annotate --summary --match 'touch foo/bar.c' \end{verbatim} +The seventh match type accepts a regular expression which is checked+against every hunk. The syntax is+\begin{verbatim}+darcs annotate --summary --match 'hunk "^instance .* Foo where$"'+\end{verbatim}++The eight match type accepts a regular expression which is checked+against the long comment. The syntax is+\begin{verbatim}+darcs annotate --summary --match 'comment "remote repository"'+\end{verbatim}+ The \verb!--match! pattern can include the logical operators \verb!&&!, \verb!||! and \verb!not!, as well as grouping of patterns with parentheses. For example@@ -207,8 +219,8 @@ \end{verbatim} \begin{code}-match_parser :: Patchy p => CharParser st (MatchFun p)-match_parser = do m <- option trivial submatch+matchParser :: Patchy p => CharParser st (MatchFun p)+matchParser = do m <- option trivial submatch eof return m @@ -266,7 +278,7 @@ "complement (not), conjunction (and) and disjunction (or) operators.", "The C notation for logic operators (!, && and ||) can also be used.", "",- " --patches=regex is a synonym for --matches='name regex'", + " --patches=regex is a synonym for --matches='name regex'", " --from-patch and --to-patch are synonyms for --from-match='name... and --to-match='name...", " --from-patch and --to-match can be unproblematically combined:", " darcs changes --from-patch='html.*documentation' --to-match='date 20040212'",@@ -306,8 +318,11 @@ , ["\"David Roundy\"", "droundy", "droundy@darcs.net"] , authormatch ) , ("hunk", "check a regular expression against the contents of a hunk patch"- , ["foo = 2", "^instance .* Foo where$"]+ , ["\"foo = 2\"", "\"^instance .* Foo where$\""] , hunkmatch )+ , ("comment", "check a regular expression against the log message"+ , ["\"prevent deadlocks\""]+ , logmatch ) , ("hash", "match the darcs hash for a patch" , ["20040403105958-53a90-c719567e92c3b0ab9eddd5290b705712b8b918ef"] , hashmatch )@@ -333,22 +348,24 @@ mymatch, exactmatch, authormatch, hunkmatch, hashmatch, datematch, touchmatch :: Patchy p => String -> MatchFun p -mymatch r (Sealed2 hp) = isJust $ matchRegex (mkRegex r) $ just_name (info hp)+mymatch r (Sealed2 hp) = isJust $ matchRegex (mkRegex r) $ justName (info hp) -exactmatch r (Sealed2 hp) = r == (just_name (info hp))+exactmatch r (Sealed2 hp) = r == (justName (info hp)) -authormatch a (Sealed2 hp) = isJust $ matchRegex (mkRegex a) $ just_author (info hp)+authormatch a (Sealed2 hp) = isJust $ matchRegex (mkRegex a) $ justAuthor (info hp) +logmatch :: Patchy p => String -> MatchFun p+logmatch l (Sealed2 hp) = isJust $ matchRegex (mkRegex l) $ justLog (info hp) hunkmatch r (Sealed2 hp) = let patch = patchcontents $ hopefully hp regexMatcher = isJust . (matchRegex (mkRegex r) . BC.unpack) in hunkMatches regexMatcher patch -hashmatch h (Sealed2 hp) = let rh = make_filename (info hp) in+hashmatch h (Sealed2 hp) = let rh = makeFilename (info hp) in (rh == h) || (rh == h++".gz") datematch d (Sealed2 hp) = let dm = unsafePerformIO $ parseDateMatcher d- in dm $ pi_date (info hp)+ in dm $ piDate (info hp) touchmatch r (Sealed2 hp) = let files = listTouchedFiles $ patchcontents $ hopefully hp in or $ map (isJust . matchRegex (mkRegex r)) files
src/Darcs/Patch/MatchData.hs view
@@ -15,7 +15,7 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -module Darcs.Patch.MatchData ( PatchMatch(..), patch_match,+module Darcs.Patch.MatchData ( PatchMatch(..), patchMatch, ) where data PatchMatch = PatternMatch String@@ -24,5 +24,5 @@ instance Show PatchMatch where show (PatternMatch m) = "pattern " ++ show m -patch_match :: String -> PatchMatch-patch_match s = PatternMatch s+patchMatch :: String -> PatchMatch+patchMatch s = PatternMatch s
src/Darcs/Patch/Non.hs view
@@ -24,11 +24,11 @@ -- |'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.Non- ( NonPatch, Non(..), Nonable(..), unNon,+ ( Non(..), Nonable(..), unNon, showNon, readNon, showNons, readNons, add, rem, addP, remP, addPs, remPs, remAddP, remAddPs, remNons, (*>), (>*), (*>>), (>>*),- prop_adjust_twice ) where+ propAdjustTwice ) where import Prelude hiding ( rem ) import Data.List ( delete )@@ -36,7 +36,7 @@ import Darcs.Patch.Prim ( Prim, FromPrim(..), ToFromPrim(..), Effect(..), showPrim, FileNameFormat(..), sortCoalesceFL ) import Darcs.Patch.Patchy-import Darcs.Patch.ReadMonads ( ParserM, lex_char )+import Darcs.Patch.ReadMonads ( ParserM, lexChar ) import Darcs.Witnesses.Ordered import Darcs.Patch.Read ( readPrim ) import Darcs.Patch.Viewing ()@@ -61,13 +61,13 @@ readNons = peekfor "{{" rns (return []) where rns = peekfor "}}" (return []) $ do Just (Sealed ps) <- readPatch' False- lex_char ':'+ lexChar ':' Just (Sealed p) <- readPrim NewFormat (Non ps p :) `liftM` rns readNon :: (ReadPatch p, ParserM m) => m (Maybe (Non p C(x))) readNon = do Just (Sealed ps) <- readPatch' False- peekfor ":" (do Just (Sealed p) <- readPatch' False+ peekfor ":" (do Just (Sealed p) <- readPrim NewFormat return $ Just $ Non ps p) (return Nothing) @@ -80,9 +80,6 @@ data Non p C(x) where Non :: FL p C(a x) -> Prim C(x y) -> Non p C(a) --- | Convenience type for non primitive patches-type NonPatch C(x) = Non Prim C(x)- -- | 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)@@ -160,7 +157,7 @@ n *> p = invert p >* n (>*) :: (Patchy p, ToFromPrim p) => p C(x y) -> Non p C(y) -> Maybe (Non p C(x))-y >* (Non c x) = case commuteFL (y :> c) of+y >* (Non c x) = case commuteFLorComplain (y :> c) of Right (c' :> y') -> do px' :> _ <- commute (y' :> fromPrim x) x' <- toPrim px'@@ -176,8 +173,8 @@ adj NilRL n = Just n adj (x:<:xs) n = fromPrim x >* n >>= adj xs -prop_adjust_twice :: (Patchy p, ToFromPrim p) => p C(x y) -> Non p C(y) -> Maybe Doc-prop_adjust_twice p n =+propAdjustTwice :: (Patchy p, ToFromPrim 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")@@ -189,17 +186,17 @@ Nothing -> Just (redText "prop_adjust_inverse 5") Just n'' | n'' /= n -> Just (redText "prop_adjust_inverse 6") _ -> Nothing- + instance Nonable Prim where non = Non NilFL instance Show2 p => Show (Non p C(x)) where- showsPrec = showsPrec1+ showsPrec d (Non cs p) = showParen (d > appPrec) $ showString "Non " .+ showsPrec2 (appPrec + 1) cs . showString " " .+ showsPrec (appPrec + 1) p instance Show2 p => Show1 (Non p) where- showsPrec1 d (Non cs p) = showParen (d > app_prec) $ showString "Non " .- showsPrec2 (app_prec + 1) cs . showString " " .- showsPrec (app_prec + 1) p+ showDict1 = ShowDictClass instance Patchy Prim
src/Darcs/Patch/OldDate.hs view
@@ -52,7 +52,7 @@ (readI $ B.take 2 $ B.drop 12 bd) -- Second 0 Sunday 0 -- Picosecond, weekday and day of year unknown "GMT" 0 False- else let dt = do { x <- date_time tz; eof; return x }+ else let dt = do { x <- dateTime tz; eof; return x } in case parse dt "" d of Left e -> Left $ "bad date: "++d++" - "++show e Right ct -> Right ct@@ -104,87 +104,87 @@ ----- Date/Time Parser ----------------------------------------------- -date_time :: Int -> CharParser a CalendarTime-date_time tz =- choice [try $ cvs_date_time tz,- try $ iso8601_date_time tz,- old_date_time]+dateTime :: Int -> CharParser a CalendarTime+dateTime tz =+ choice [try $ cvsDateTime tz,+ try $ iso8601DateTime tz,+ oldDateTime] -cvs_date_time :: Int -> CharParser a CalendarTime-cvs_date_time tz =+cvsDateTime :: Int -> CharParser a CalendarTime+cvsDateTime tz = do y <- year char '/'- mon <- month_num + mon <- monthNum char '/' d <- day- my_spaces+ mySpaces h <- hour char ':' m <- minute char ':' s <- second- z <- option tz $ my_spaces >> zone+ z <- option tz $ mySpaces >> zone return (CalendarTime y mon d h m s 0 Monday 0 "" z False) -old_date_time :: CharParser a CalendarTime-old_date_time = do wd <- day_name- my_spaces- mon <- month_name- my_spaces+oldDateTime :: CharParser a CalendarTime+oldDateTime = do wd <- dayName+ mySpaces+ mon <- monthName+ mySpaces d <- day- my_spaces+ mySpaces h <- hour char ':' m <- minute char ':' s <- second- my_spaces+ mySpaces z <- zone- my_spaces+ mySpaces y <- year return (CalendarTime y mon d h m s 0 wd 0 "" z False) -{- FIXME: In case you ever want to use this outside of darcs, you should note - that this implementation of ISO 8601 is not complete. +{- FIXME: In case you ever want to use this outside of darcs, you should note+ that this implementation of ISO 8601 is not complete. - reluctant to implement (ambiguous!): - * years > 9999 - * truncated representations with implied century (89 for 1989) - unimplemented: + reluctant to implement (ambiguous!):+ * years > 9999+ * truncated representations with implied century (89 for 1989)+ unimplemented: * repeated durations (not relevant) * lowest order component fractions in intervals- * negative dates (BC) + * negative dates (BC) unverified or too relaxed: * the difference between 24h and 0h- * allows stuff like 2005-1212; either you use the hyphen all the way + * allows stuff like 2005-1212; either you use the hyphen all the way (2005-12-12) or you don't use it at all (20051212), but you don't use- it halfway, likewise with time - * No bounds checking whatsoever on intervals! + it halfway, likewise with time+ * No bounds checking whatsoever on intervals! (next action: read iso doc to see if bounds-checking required?) -}-iso8601_date_time :: Int -> CharParser a CalendarTime-iso8601_date_time localTz = try $ - do d <- iso8601_date- t <- option id $ try $ do optional $ oneOf " T" - iso8601_time +iso8601DateTime :: Int -> CharParser a CalendarTime+iso8601DateTime localTz = try $+ do d <- iso8601Date+ t <- option id $ try $ do optional $ oneOf " T"+ iso8601Time return $ t $ d { ctTZ = localTz } -iso8601_date :: CharParser a CalendarTime-iso8601_date = +iso8601Date :: CharParser a CalendarTime+iso8601Date = do d <- calendar_date <|> week_date <|> ordinal_date return $ foldr ($) nullCalendar d- where + where calendar_date = -- yyyy-mm-dd try $ do d <- optchain year_ [ (dash, month_), (dash, day_) ]- -- allow other variants to be parsed correctly + -- allow other variants to be parsed correctly notFollowedBy (digit <|> char 'W') return d- week_date = --yyyy-Www-dd + week_date = --yyyy-Www-dd try $ do yfn <- year_ optional dash char 'W' -- offset human 'week 1' -> computer 'week 0'- w' <- (\x -> x-1) `liftM` two_digits- wd <- option 1 $ do { optional dash; n_digits 1 }+ w' <- (\x -> x-1) `liftM` twoDigits+ wd <- option 1 $ do { optional dash; nDigits 1 } let y = yfn nullCalendar firstDay = ctWDay y -- things that make this complicated@@ -199,32 +199,32 @@ ordinal_date = -- yyyy-ddd try $ optchain year_ [ (dash, yearDay_) ] --- year_ = try $ do y <- four_digits <?> "year (0000-9999)"+ year_ = try $ do y <- fourDigits <?> "year (0000-9999)" return $ \c -> c { ctYear = y }- month_ = try $ do m <- two_digits <?> "month (1 to 12)"+ month_ = try $ do m <- twoDigits <?> "month (1 to 12)" -- we (artificially) use ctPicosec to indicate -- whether the month has been specified. return $ \c -> c { ctMonth = intToMonth m, ctPicosec = 0 }- day_ = try $ do d <- two_digits <?> "day in month (1 to 31)"+ day_ = try $ do d <- twoDigits <?> "day in month (1 to 31)" return $ \c -> c { ctDay = d }- yearDay_ = try $ do d <- n_digits 3 <?> "day in year (1 to 366)"+ yearDay_ = try $ do d <- nDigits 3 <?> "day in year (1 to 366)" return $ \c -> c { ctYDay = d } dash = char '-' -- we return a function which sets the time on another calendar-iso8601_time :: CharParser a (CalendarTime -> CalendarTime)-iso8601_time = try $+iso8601Time :: CharParser a (CalendarTime -> CalendarTime)+iso8601Time = try $ do ts <- optchain hour_ [ (colon , min_) , (colon , sec_)- , (oneOf ",.", pico_) ] + , (oneOf ",.", pico_) ] z <- option id $ choice [ zulu , offset ] return $ foldr (.) id (z:ts)- where - hour_ = do h <- two_digits+ where+ hour_ = do h <- twoDigits return $ \c -> c { ctHour = h }- min_ = do m <- two_digits+ min_ = do m <- twoDigits return $ \c -> c { ctMin = m }- sec_ = do s <- two_digits+ sec_ = do s <- twoDigits return $ \c -> c { ctSec = s } pico_ = do digs <- many digit let picoExp = 12@@ -236,31 +236,31 @@ zulu = do { char 'Z'; return (\c -> c { ctTZ = 0 }) } offset = do sign <- choice [ do { char '+' >> return 1 } , do { char '-' >> return (-1) } ]- h <- two_digits- m <- option 0 $ do { optional colon; two_digits }+ h <- twoDigits+ m <- option 0 $ do { optional colon; twoDigits } return $ \c -> c { ctTZ = sign * 60 * ((h*60)+m) } colon = char ':' optchain :: CharParser a b -> [(CharParser a c, CharParser a b)] -> CharParser a [b]-optchain p next = try $ +optchain p next = try $ do r1 <- p- r2 <- case next of + r2 <- case next of [] -> return [] ((sep,p2):next2) -> option [] $ do { optional sep; optchain p2 next2 } return (r1:r2) -n_digits :: Int -> CharParser a Int -n_digits n = read `liftM` count n digit+nDigits :: Int -> CharParser a Int+nDigits n = read `liftM` count n digit -two_digits, four_digits :: CharParser a Int-two_digits = n_digits 2-four_digits = n_digits 4+twoDigits, fourDigits :: CharParser a Int+twoDigits = nDigits 2+fourDigits = nDigits 4 -my_spaces :: CharParser a String-my_spaces = manyN 1 $ char ' '+mySpaces :: CharParser a String+mySpaces = manyN 1 $ char ' ' -day_name :: CharParser a Day-day_name = choice+dayName :: CharParser a Day+dayName = choice [ caseString "Mon" >> return Monday , try (caseString "Tue") >> return Tuesday , caseString "Wed" >> return Wednesday@@ -271,11 +271,11 @@ ] year :: CharParser a Int-year = four_digits+year = fourDigits -month_num :: CharParser a Month-month_num = do mn <- manyNtoM 1 2 digit - return $ intToMonth $ (read mn :: Int)+monthNum :: CharParser a Month+monthNum = do mn <- manyNtoM 1 2 digit+ return $ intToMonth $ (read mn :: Int) intToMonth :: Int -> Month intToMonth 1 = January@@ -292,8 +292,8 @@ intToMonth 12 = December intToMonth _ = error "invalid month!" -month_name :: CharParser a Month-month_name = choice+monthName :: CharParser a Month+monthName = choice [ try (caseString "Jan") >> return January , caseString "Feb" >> return February , try (caseString "Mar") >> return March@@ -313,13 +313,13 @@ return (read d :: Int) hour :: CharParser a Int-hour = two_digits+hour = twoDigits minute :: CharParser a Int-minute = two_digits+minute = twoDigits second :: CharParser a Int-second = two_digits+second = twoDigits zone :: CharParser a Int zone = choice@@ -346,5 +346,5 @@ where mkZone n o = try $ do { caseString n; return (o*60*60) } space_digit = try $ do { char ' '; oneOf ['0'..'9'] } -nullCalendar :: CalendarTime +nullCalendar :: CalendarTime nullCalendar = CalendarTime 0 January 0 0 0 0 1 Sunday 0 "" 0 False
src/Darcs/Patch/Patchy.hs view
@@ -24,7 +24,8 @@ module Darcs.Patch.Patchy ( Patchy, Apply, apply, applyAndTryToFix, applyAndTryToFixFL, mapMaybeSnd,- Commute(..), commuteFL, commuteRL, commuteRLFL,+ Commute(..), commuteFLorComplain, commuteRL,+ commuteFL, commuteRLFL, mergeFL, toFwdCommute, toRevCommute, ShowPatch(..), ReadPatch, readPatch', bracketedFL, peekfor,@@ -37,7 +38,7 @@ import Storage.Hashed.Monad( TreeIO ) import Darcs.Witnesses.Sealed ( Sealed(..), Sealed2(..), seal2 )-import Darcs.Patch.ReadMonads ( ParserM, lex_eof, peek_input, my_lex, work, alter_input )+import Darcs.Patch.ReadMonads ( ParserM, lexEof, peekInput, myLex, work, alterInput ) import Darcs.Witnesses.Ordered import Printer ( Doc, (<>), text ) import Darcs.Lock ( writeDocBinFile, gzWriteDocFile )@@ -166,15 +167,18 @@ return (w'' :> z' :<: zs') commuteRL (NilRL :> w) = Just (w :> NilRL) -commuteFL :: Commute p => (p :> FL p) C(x y) -> Either (Sealed2 p) ((FL p :> p) C(x y))-commuteFL (p :> NilFL) = Right (NilFL :> p)-commuteFL (q :> p :>: ps) = case commute (q :> p) of+commuteFLorComplain :: Commute p => (p :> FL p) C(x y) -> Either (Sealed2 p) ((FL p :> p) C(x y))+commuteFLorComplain (p :> NilFL) = Right (NilFL :> p)+commuteFLorComplain (q :> p :>: ps) = case commute (q :> p) of Just (p' :> q') ->- case commuteFL (q' :> ps) of+ case commuteFLorComplain (q' :> ps) of Right (ps' :> q'') -> Right (p' :>: ps' :> q'') Left l -> Left l Nothing -> Left $ seal2 p +commuteFL :: Commute p => (p :> FL p) C(x y) -> Maybe ((FL p :> p) C(x y))+commuteFL = either (const Nothing) Just . commuteFLorComplain+ instance ReadPatch p => ReadPatch (FL p) where readPatch' want_eof = Just `liftM` read_patches where read_patches :: ParserM m => m (Sealed (FL p C(x )))@@ -186,36 +190,36 @@ return $ Sealed (p:>:ps) Nothing -> if want_eof then do --tracePeek "no more patches"- unit' <- lex_eof+ unit' <- lexEof case unit' of () -> return $ Sealed NilFL else do --tracePeek "no more patches" return $ Sealed NilFL--- tracePeek x = do y <- peek_input+-- tracePeek x = do y <- peekInput -- traceDoc (greenText x $$ greenText (show $ sal_to_string y)) return () {-# INLINE bracketedFL #-}-bracketedFL :: (ReadPatch p, ParserM m) =>- Word8 -> Word8 -> m (Maybe (Sealed (FL p C(x))))-bracketedFL pre post =+bracketedFL :: forall p m C(x) . (ReadPatch p, ParserM m) =>+ (FORALL(y) m (Maybe (Sealed (p C(y))))) -> Word8 -> Word8 -> m (Maybe (Sealed (FL p C(x))))+bracketedFL parser pre post = peekforw pre bfl (return Nothing)- where bfl :: (ReadPatch p, ParserM m) => m (Maybe (Sealed (FL p C(x))))+ where bfl :: FORALL(z) m (Maybe (Sealed (FL p C(z)))) bfl = peekforw post (return $ Just $ Sealed NilFL)- (do Just (Sealed p) <- readPatch' False+ (do Just (Sealed p) <- parser Just (Sealed ps) <- bfl return $ Just $ Sealed (p:>:ps)) {-# INLINE peekforw #-} peekforw :: ParserM m => Word8 -> m a -> m a -> m a-peekforw w ifstr ifnot = do s <- peek_input+peekforw w ifstr ifnot = do s <- peekInput case ifHeadThenTail w $ dropSpace s of- Just s' -> alter_input (const s') >> ifstr+ Just s' -> alterInput (const s') >> ifstr Nothing -> ifnot peekforPS :: ParserM m => BC.ByteString -> m a -> m a -> m a-peekforPS ps ifstr ifnot = do s <- peek_input- case ((ps ==) . fst) `fmap` my_lex s of- Just True -> work my_lex >> ifstr+peekforPS ps ifstr ifnot = do s <- peekInput+ case ((ps ==) . fst) `fmap` myLex s of+ Just True -> work myLex >> ifstr _ -> ifnot {-# INLINE peekfor #-}
src/Darcs/Patch/Permutations.hs view
@@ -26,15 +26,15 @@ commuteWhatWeCanFL, commuteWhatWeCanRL, genCommuteWhatWeCanRL, partitionFL, partitionRL,- head_permutationsFL, head_permutationsRL,+ simpleHeadPermutationsFL, headPermutationsRL, headPermutationsFL,- remove_subsequenceFL, remove_subsequenceRL,+ removeSubsequenceFL, removeSubsequenceRL, partitionConflictingFL, CommuteFn, selfCommuter, commuterIdRL, ) where import Data.Maybe ( catMaybes )-import Darcs.Patch.Patchy ( Commute, commute, commuteFL, commuteRL, Invert(..), invertFL, invertRL )+import Darcs.Patch.Patchy ( Commute, commute, commuteFLorComplain, commuteRL, Invert(..), invertFL, invertRL ) import Darcs.Witnesses.Ordered #include "impossible.h" @@ -44,25 +44,30 @@ partitionFL :: Commute p => (FORALL(u v) p C(u v) -> Bool) -- ^predicate; if true we would like the patch in the "left" list -> FL p C(x y) -- ^input 'FL'- -> (FL p :> FL p) C(x y) -- ^"left" and "right" results+ -> ((FL p :> FL p :> FL p) C(x y)) -- ^"left", "middle" and "right" -- optimise by using an accumulating parameter to track all the "right" patches that we've found so far partitionFL' :: Commute p => (FORALL(u v) p C(u v) -> Bool)- -> RL p C(x z) -- the "right" patches found so far- -> FL p C(z y)- -> (FL p :> FL p) C(x y)+ -> RL p C(a b) -- the "middle" patches found so far+ -> RL p C(b c) -- the "right" patches found so far+ -> FL p C(c d)+ -> ((FL p :> FL p :> FL p) C(a d)) -partitionFL keepleft ps = partitionFL' keepleft NilRL ps+partitionFL keepleft ps = partitionFL' keepleft NilRL NilRL ps -partitionFL' _ qs NilFL = NilFL :> reverseRL qs-partitionFL' keepleft qs (p :>: ps)- | keepleft p,- Just (p' :> qs') <- commuteRL (qs :> p)- = case partitionFL' keepleft qs' ps of- a :> b -> p' :>: a :> b- | otherwise = partitionFL' keepleft (p :<: qs) ps+partitionFL' _ middle right NilFL = (NilFL :> reverseRL middle :> reverseRL right)+partitionFL' keepleft middle right (p :>: ps)+ | keepleft p = case commuteRL (right :> p) of+ Just (p' :> right') -> case commuteRL (middle :> p') of+ Just (p'' :> middle') -> case partitionFL' keepleft middle' right' ps of+ (a :> b :> c) -> (p'' :>: a :> b :> c)+ Nothing -> partitionFL' keepleft (p' :<: middle) right' ps+ Nothing -> case commuteWhatWeCanRL (right :> p) of+ (tomiddle :> p' :> right') -> partitionFL' keepleft (p' :<: tomiddle +<+ middle) right' ps+ | otherwise = partitionFL' keepleft middle (p :<: right) ps + -- |split an 'RL' into "left" and "right" lists according to a predicate, using commutation as necessary. -- If a patch does satisfy the predicate but cannot be commuted past one that does not satisfy -- the predicate, it goes in the "left" list.@@ -84,7 +89,7 @@ partitionRL' keepright (p :<: ps) qs | keepright p,- Right (qs' :> p') <- commuteFL (p :> qs)+ Right (qs' :> p') <- commuteFLorComplain (p :> qs) = case partitionRL' keepright ps qs' of a :> b -> a :> p' :<: b | otherwise = partitionRL' keepright ps (p :>: qs)@@ -135,38 +140,38 @@ -- | 'removeRL' is like 'removeFL' except with 'RL' removeRL :: (MyEq p, Commute p) => p C(y z) -> RL p C(x z) -> Maybe (RL p C(x y))-removeRL x xs = r x $ head_permutationsRL xs+removeRL x xs = r x $ headPermutationsRL xs where r :: (MyEq p, Commute p) => p C(y z) -> [RL p C(x z)] -> Maybe (RL p C(x y)) r z ((z':<:zs):zss) | IsEq <- z =/\= z' = Just zs | otherwise = r z zss r _ _ = Nothing --- | 'remove_subsequenceFL' @ab abc@ returns @Just c'@ where all the patches in+-- | 'removeSubsequenceFL' @ab abc@ returns @Just c'@ where all the patches in -- @ab@ have been commuted out of it, if possible. If this is not possible -- for any reason (the set of patches @ab@ is not actually a subset of @abc@, -- or they can't be commuted out) we return 'Nothing'.-remove_subsequenceFL :: (MyEq p, Commute p) => FL p C(a b)+removeSubsequenceFL :: (MyEq p, Commute p) => FL p C(a b) -> FL p C(a c) -> Maybe (FL p C(b c))-remove_subsequenceFL a b | lengthFL a > lengthFL b = Nothing+removeSubsequenceFL a b | lengthFL a > lengthFL b = Nothing | otherwise = rsFL a b where rsFL :: (MyEq p, Commute p) => FL p C(a b) -> FL p C(a c) -> Maybe (FL p C(b c)) rsFL NilFL ys = Just ys- rsFL (x:>:xs) yys = removeFL x yys >>= remove_subsequenceFL xs+ rsFL (x:>:xs) yys = removeFL x yys >>= removeSubsequenceFL xs --- | 'remove_subsequenceRL' is like @remove_subsequenceFL@ except that it works+-- | 'removeSubsequenceRL' is like @removeSubsequenceFL@ except that it works -- on 'RL'-remove_subsequenceRL :: (MyEq p, Commute p) => RL p C(ab abc)+removeSubsequenceRL :: (MyEq p, Commute p) => RL p C(ab abc) -> RL p C(a abc) -> Maybe (RL p C(a ab))-remove_subsequenceRL a b | lengthRL a > lengthRL b = Nothing+removeSubsequenceRL a b | lengthRL a > lengthRL b = Nothing | otherwise = rsRL a b where rsRL :: (MyEq p, Commute p) => RL p C(ab abc) -> RL p C(a abc) -> Maybe (RL p C(a ab)) rsRL NilRL ys = Just ys- rsRL (x:<:xs) yys = removeRL x yys >>= remove_subsequenceRL xs+ rsRL (x:<:xs) yys = removeRL x yys >>= removeSubsequenceRL xs -- | This is a minor variant of 'headPermutationsFL' with each permutation -- is simply returned as a 'FL'-head_permutationsFL :: Commute p => FL p C(x y) -> [FL p C(x y)]-head_permutationsFL ps = map (\ (x:>xs) -> x:>:xs) $ headPermutationsFL ps+simpleHeadPermutationsFL :: Commute p => FL p C(x y) -> [FL p C(x y)]+simpleHeadPermutationsFL ps = map (\ (x:>xs) -> x:>:xs) $ headPermutationsFL ps -- | 'headPermutationsFL' @p:>:ps@ returns all the permutations of the list -- in which one element of @ps@ is commuted past @p@@@ -191,13 +196,13 @@ where swapfirstFL (p1:>p2:>xs) = do p2':>p1' <- commute (p1:>p2) Just $ p2':>p1':>:xs --- | 'head_permutationsRL' is like 'headPermutationsFL', except that we+-- | 'headPermutationsRL' is like 'headPermutationsFL', except that we -- operate on an 'RL' (in other words, we are pushing things to the end of a -- patch sequence instead of to the beginning).-head_permutationsRL :: Commute p => RL p C(x y) -> [RL p C(x y)]-head_permutationsRL NilRL = []-head_permutationsRL (p:<:ps) =- (p:<:ps) : catMaybes (map (swapfirstRL.(p:<:)) $ head_permutationsRL ps)+headPermutationsRL :: Commute p => RL p C(x y) -> [RL p C(x y)]+headPermutationsRL NilRL = []+headPermutationsRL (p:<:ps) =+ (p:<:ps) : catMaybes (map (swapfirstRL.(p:<:)) $ headPermutationsRL ps) where swapfirstRL (p1:<:p2:<:xs) = do p1':>p2' <- commute (p2:>p1) Just $ p2':<:p1':<:xs swapfirstRL _ = Nothing
src/Darcs/Patch/Prim.lhs view
@@ -23,11 +23,11 @@ #include "gadts.h" module Darcs.Patch.Prim- ( Prim(..), IsConflictedPrim(IsC), ConflictState(..), showPrim,+ ( Prim(..), IsConflictedPrim(IsC), ConflictState(..), showPrim, showPrimFL, showHunk, DirPatchType(..), FilePatchType(..), CommuteFunction, Perhaps(..), null_patch, nullP, isNullPatch,- is_identity,+ isIdentity, formatFileName, FileNameFormat(..), adddir, addfile, binary, changepref, hunk, move, rmdir, rmfile, tokreplace,@@ -35,29 +35,27 @@ isSimilar, primIsAdddir, is_filepatch, canonize, tryToShrink, modernizePrim, subcommutes, sortCoalesceFL, join, canonizeFL,- try_tok_internal,- try_shrinking_inverse,- n_fn,+ tryTokInternal,+ tryShrinkingInverse,+ nFn, FromPrim(..), FromPrims(..), ToFromPrim(..),- Conflict(..), Effect(..), commute_no_conflictsFL, commute_no_conflictsRL+ Conflict(..), Effect(..), commuteNoConflictsFL, commuteNoConflictsRL ) where import Prelude hiding ( pi ) import Control.Monad ( MonadPlus, msum, mzero, mplus )-import Data.Maybe ( isNothing )-#ifndef GADT_WITNESSES+import Data.Maybe ( isNothing, listToMaybe, catMaybes ) import Data.Map ( elems, fromListWith, mapWithKey )-#endif import ByteStringUtils ( substrPS, fromPS2Hex) import qualified Data.ByteString as B (ByteString, length, null, head, take, concat, drop)-import qualified Data.ByteString.Char8 as BC (break, pack)+import qualified Data.ByteString.Char8 as BC (break, pack, head) -import Darcs.Patch.FileName ( FileName, fn2ps, fn2fp, fp2fn, norm_path,- movedirfilename, encode_white )+import Darcs.Patch.FileName ( FileName, fn2ps, fn2fp, fp2fn, normPath,+ movedirfilename, encodeWhite ) import Darcs.Witnesses.Ordered-import Darcs.Witnesses.Sealed ( Sealed, unseal )+import Darcs.Witnesses.Sealed ( Sealed, unseal, Sealed2(..), unsafeUnseal2 ) import Darcs.Patch.Patchy ( Invert(..), Commute(..), toFwdCommute, toRevCommute ) import Darcs.Patch.Permutations () -- for Invert instance of FL import Darcs.Witnesses.Show@@ -106,13 +104,13 @@ nullP :: Prim C(x y) -> EqCheck C(x y) nullP = sloppyIdentity -is_identity :: Prim C(x y) -> EqCheck C(x y)-is_identity (FP _ (Binary old new)) | old == new = unsafeCoerce# IsEq-is_identity (FP _ (Hunk _ old new)) | old == new = unsafeCoerce# IsEq-is_identity (FP _ (TokReplace _ old new)) | old == new = unsafeCoerce# IsEq-is_identity (Move old new) | old == new = unsafeCoerce# IsEq-is_identity Identity = IsEq-is_identity _ = NotEq+isIdentity :: Prim C(x y) -> EqCheck C(x y)+isIdentity (FP _ (Binary old new)) | old == new = unsafeCoerce# IsEq+isIdentity (FP _ (Hunk _ old new)) | old == new = unsafeCoerce# IsEq+isIdentity (FP _ (TokReplace _ old new)) | old == new = unsafeCoerce# IsEq+isIdentity (Move old new) | old == new = unsafeCoerce# IsEq+isIdentity Identity = IsEq+isIdentity _ = NotEq -- FIXME: The following code needs to be moved. @@ -157,19 +155,19 @@ evalargs :: (a -> b -> c) -> a -> b -> c evalargs f x y = (f $! x) $! y -addfile f = FP (fp2fn $ n_fn f) AddFile-rmfile f = FP (fp2fn $ n_fn f) RmFile-adddir d = DP (fp2fn $ n_fn d) AddDir-rmdir d = DP (fp2fn $ n_fn d) RmDir-move f f' = Move (fp2fn $ n_fn f) (fp2fn $ n_fn f')+addfile f = FP (fp2fn $ nFn f) AddFile+rmfile f = FP (fp2fn $ nFn f) RmFile+adddir d = DP (fp2fn $ nFn d) AddDir+rmdir d = DP (fp2fn $ nFn d) RmDir+move f f' = Move (fp2fn $ nFn f) (fp2fn $ nFn f') changepref p f t = ChangePref p f t-hunk f line old new = evalargs FP (fp2fn $ n_fn f) (Hunk line old new)+hunk f line old new = evalargs FP (fp2fn $ nFn f) (Hunk line old new) tokreplace f tokchars old new =- evalargs FP (fp2fn $ n_fn f) (TokReplace tokchars old new)-binary f old new = FP (fp2fn $! n_fn f) $ Binary old new+ evalargs FP (fp2fn $ nFn f) (TokReplace tokchars old new)+binary f old new = FP (fp2fn $! nFn f) $ Binary old new -n_fn :: FilePath -> FilePath-n_fn f = "./"++(fn2fp $ norm_path $ fp2fn f)+nFn :: FilePath -> FilePath+nFn f = "./"++(fn2fp $ normPath $ fp2fn f) instance Invert Prim where invert Identity = Identity@@ -188,48 +186,48 @@ sloppyIdentity _ = NotEq instance Show (Prim C(x y)) where- showsPrec d (Move fn1 fn2) = showParen (d > app_prec) $ showString "Move " .- showsPrec (app_prec + 1) fn1 . showString " " .- showsPrec (app_prec + 1) fn2- showsPrec d (DP fn dp) = showParen (d > app_prec) $ showString "DP " .- showsPrec (app_prec + 1) fn . showString " " .- showsPrec (app_prec + 1) dp- showsPrec d (FP fn fp) = showParen (d > app_prec) $ showString "FP " .- showsPrec (app_prec + 1) fn . showString " " .- showsPrec (app_prec + 1) fp- showsPrec d (Split l) = showParen (d > app_prec) $ showString "Split " .- showsPrec (app_prec + 1) l+ showsPrec d (Move fn1 fn2) = showParen (d > appPrec) $ showString "Move " .+ showsPrec (appPrec + 1) fn1 . showString " " .+ showsPrec (appPrec + 1) fn2+ showsPrec d (DP fn dp) = showParen (d > appPrec) $ showString "DP " .+ showsPrec (appPrec + 1) fn . showString " " .+ showsPrec (appPrec + 1) dp+ showsPrec d (FP fn fp) = showParen (d > appPrec) $ showString "FP " .+ showsPrec (appPrec + 1) fn . showString " " .+ showsPrec (appPrec + 1) fp+ showsPrec d (Split l) = showParen (d > appPrec) $ showString "Split " .+ showsPrec (appPrec + 1) l showsPrec _ Identity = showString "Identity"- showsPrec d (ChangePref p f t) = showParen (d > app_prec) $ showString "ChangePref " .- showsPrec (app_prec + 1) p . showString " " .- showsPrec (app_prec + 1) f . showString " " .- showsPrec (app_prec + 1) t+ showsPrec d (ChangePref p f t) = showParen (d > appPrec) $ showString "ChangePref " .+ showsPrec (appPrec + 1) p . showString " " .+ showsPrec (appPrec + 1) f . showString " " .+ showsPrec (appPrec + 1) t instance Show2 Prim where- showsPrec2 = showsPrec+ showDict2 = ShowDictClass instance Show (FilePatchType C(x y)) where showsPrec _ RmFile = showString "RmFile" showsPrec _ AddFile = showString "AddFile" showsPrec d (Hunk line old new) | all ((==1) . B.length) old && all ((==1) . B.length) new- = showParen (d > app_prec) $ showString "Hunk " .- showsPrec (app_prec + 1) line . showString " " .+ = showParen (d > appPrec) $ showString "Hunk " .+ showsPrec (appPrec + 1) line . showString " " . showsPrecC old . showString " " . showsPrecC new where showsPrecC [] = showString "[]"- showsPrecC ss = showParen True $ showString "packStringLetters " . showsPrec (app_prec + 1) (map B.head ss)- showsPrec d (Hunk line old new) = showParen (d > app_prec) $ showString "Hunk " .- showsPrec (app_prec + 1) line . showString " " .- showsPrec (app_prec + 1) old . showString " " .- showsPrec (app_prec + 1) new- showsPrec d (TokReplace t old new) = showParen (d > app_prec) $ showString "TokReplace " .- showsPrec (app_prec + 1) t . showString " " .- showsPrec (app_prec + 1) old . showString " " .- showsPrec (app_prec + 1) new+ showsPrecC ss = showParen True $ showString "packStringLetters " . showsPrec (appPrec + 1) (map BC.head ss)+ showsPrec d (Hunk line old new) = showParen (d > appPrec) $ showString "Hunk " .+ showsPrec (appPrec + 1) line . showString " " .+ showsPrec (appPrec + 1) old . showString " " .+ showsPrec (appPrec + 1) new+ showsPrec d (TokReplace t old new) = showParen (d > appPrec) $ showString "TokReplace " .+ showsPrec (appPrec + 1) t . showString " " .+ showsPrec (appPrec + 1) old . showString " " .+ showsPrec (appPrec + 1) new -- this case may not work usefully- showsPrec d (Binary old new) = showParen (d > app_prec) $ showString "Binary " .- showsPrec (app_prec + 1) old . showString " " .- showsPrec (app_prec + 1) new+ showsPrec d (Binary old new) = showParen (d > appPrec) $ showString "Binary " .+ showsPrec (appPrec + 1) old . showString " " .+ showsPrec (appPrec + 1) new instance Show (DirPatchType C(x y)) where showsPrec _ RmDir = showString "RmDir"@@ -243,7 +241,7 @@ data FileNameFormat = OldFormat | NewFormat formatFileName :: FileNameFormat -> FileName -> Doc formatFileName OldFormat = packedString . fn2ps-formatFileName NewFormat = text . encode_white . fn2fp+formatFileName NewFormat = text . encodeWhite . fn2fp showPrim :: FileNameFormat -> Prim C(a b) -> Doc showPrim x (FP f AddFile) = showAddFile x f@@ -258,6 +256,9 @@ showPrim x (Split ps) = showSplit x ps showPrim _ Identity = blueText "{}" +showPrimFL :: FileNameFormat -> FL Prim C(a b) -> Doc+showPrimFL f xs = vcat (mapFL (showPrim f) xs)+ \end{code} @@ -375,14 +376,14 @@ showBinary x f o n = blueText "binary" <+> formatFileName x f $$ invisibleText "oldhex"- $$ (vcat $ map makeprintable $ break_every 78 $ fromPS2Hex o)+ $$ (vcat $ map makeprintable $ breakEvery 78 $ fromPS2Hex o) $$ invisibleText "newhex"- $$ (vcat $ map makeprintable $ break_every 78 $ fromPS2Hex n)+ $$ (vcat $ map makeprintable $ breakEvery 78 $ fromPS2Hex n) where makeprintable ps = invisibleText "*" <> invisiblePS ps -break_every :: Int -> B.ByteString -> [B.ByteString]-break_every n ps | B.length ps < n = [ps]- | otherwise = B.take n ps : break_every n (B.drop n ps)+breakEvery :: Int -> B.ByteString -> [B.ByteString]+breakEvery n ps | B.length ps < n = [ps]+ | otherwise = B.take n ps : breakEvery n (B.drop n ps) \end{code} \paragraph{Split patch [OBSOLETE!]}@@ -401,8 +402,8 @@ $$ vcat (mapFL (showPrim x) ps) $$ blueText ")" -commute_split :: CommuteFunction-commute_split (Split patches :< patch) =+commuteSplit :: CommuteFunction+commuteSplit (Split patches :< patch) = toPerhaps $ cs (patches :< patch) >>= sc where cs :: ((FL Prim) :< Prim) C(x y) -> Maybe ((Prim :< (FL Prim)) C(x y)) cs (NilFL :< p1) = return (p1 :< NilFL)@@ -415,26 +416,32 @@ -> Maybe ((Prim :< Prim) C(x y)) scFL (p1' :< (p :>: NilFL)) = return (p1' :< p) scFL (p1' :< ps') = return (p1' :< Split ps')-commute_split _ = Unknown+commuteSplit _ = Unknown tryToShrink :: FL Prim C(x y) -> FL Prim C(x y)-tryToShrink = mapPrimFL try_harder_to_shrink+tryToShrink = mapPrimFL tryHarderToShrink mapPrimFL :: (FORALL(x y) FL Prim C(x y) -> FL Prim C(x y)) -> FL Prim C(w z) -> FL Prim C(w z) mapPrimFL f x =-#ifdef GADT_WITNESSES- f x-#else -- an optimisation; break the list up into independent sublists -- and apply f to each of them- case mapM toSimple $ mapFL id x of- Just sx -> foldr (+>+) NilFL $ elems $- mapWithKey (\ k p -> f (fromSimples k (p NilFL))) $+ case mapM toSimpleSealed $ mapFL Sealed2 x of+ Just sx -> concatFL $ unsealList $ elems $+ mapWithKey (\ k p -> Sealed2 (f (fromSimples k (unsealList (p []))))) $ fromListWith (flip (.)) $- map (\ (a,b) -> (a,(b:>:))) sx+ map (\ (a,b) -> (a,(b:))) sx Nothing -> f x+ where+ unsealList :: [Sealed2 p] -> FL p C(a b)+ unsealList [] = unsafeCoerceP NilFL+ unsealList (x:xs) = unsafeUnseal2 x :>: unsealList xs + toSimpleSealed :: Sealed2 Prim -> Maybe (FileName, Sealed2 Simple)+ toSimpleSealed (Sealed2 p) = fmap (\(fn, s) -> (fn, Sealed2 s)) (toSimple p)+++ data Simple C(x y) = SFP !(FilePatchType C(x y)) | SDP !(DirPatchType C(x y)) | SCP String String String deriving ( Show )@@ -455,46 +462,45 @@ fromSimples :: FileName -> FL Simple C(x y) -> FL Prim C(x y) fromSimples a bs = mapFL_FL (fromSimple a) bs-#endif -try_harder_to_shrink :: FL Prim C(x y) -> FL Prim C(x y)-try_harder_to_shrink x = try_to_shrink2 $ maybe x id (try_shrinking_inverse x)+tryHarderToShrink :: FL Prim C(x y) -> FL Prim C(x y)+tryHarderToShrink x = tryToShrink2 $ maybe x id (tryShrinkingInverse x) -try_to_shrink2 :: FL Prim C(x y) -> FL Prim C(x y)-try_to_shrink2 psold =+tryToShrink2 :: FL Prim C(x y) -> FL Prim C(x y)+tryToShrink2 psold = let ps = sortCoalesceFL psold- ps_shrunk = shrink_a_bit ps+ ps_shrunk = shrinkABit ps in if lengthFL ps_shrunk < lengthFL ps- then try_to_shrink2 ps_shrunk+ then tryToShrink2 ps_shrunk else ps_shrunk -try_shrinking_inverse :: FL Prim C(x y) -> Maybe (FL Prim C(x y))-try_shrinking_inverse (x:>:y:>:z)+tryShrinkingInverse :: FL Prim C(x y) -> Maybe (FL Prim C(x y))+tryShrinkingInverse (x:>:y:>:z) | IsEq <- invert x =\/= y = Just z- | otherwise = case try_shrinking_inverse (y:>:z) of+ | otherwise = case tryShrinkingInverse (y:>:z) of Nothing -> Nothing- Just yz' -> Just $ case try_shrinking_inverse (x:>:yz') of+ Just yz' -> Just $ case tryShrinkingInverse (x:>:yz') of Nothing -> x:>:yz' Just xyz' -> xyz'-try_shrinking_inverse _ = Nothing+tryShrinkingInverse _ = Nothing -shrink_a_bit :: FL Prim C(x y) -> FL Prim C(x y)-shrink_a_bit NilFL = NilFL-shrink_a_bit (p:>:ps) =- case try_one NilRL p ps of- Nothing -> p :>: shrink_a_bit ps+shrinkABit :: FL Prim C(x y) -> FL Prim C(x y)+shrinkABit NilFL = NilFL+shrinkABit (p:>:ps) =+ case tryOne NilRL p ps of+ Nothing -> p :>: shrinkABit ps Just ps' -> ps' -try_one :: RL Prim C(w x) -> Prim C(x y) -> FL Prim C(y z)+tryOne :: RL Prim C(w x) -> Prim C(x y) -> FL Prim C(y z) -> Maybe (FL Prim C(w z))-try_one _ _ NilFL = Nothing-try_one sofar p (p1:>:ps) =+tryOne _ _ NilFL = Nothing+tryOne sofar p (p1:>:ps) = case coalesce (p1 :< p) of Just p' -> Just (reverseRL sofar +>+ p':>:NilFL +>+ ps) Nothing -> case commute (p :> p1) of Nothing -> Nothing- Just (p1' :> p') -> try_one (p1':<:sofar) p' ps+ Just (p1' :> p') -> tryOne (p1':<:sofar) p' ps -- | 'canonizeFL' @ps@ puts a sequence of primitive patches into -- canonical form. Even if the patches are just hunk patches,@@ -516,16 +522,16 @@ -- possible, sorting the results according to the scheme defined -- in 'comparePrim' sortCoalesceFL :: FL Prim C(x y) -> FL Prim C(x y)-sortCoalesceFL = mapPrimFL sort_coalesceFL2+sortCoalesceFL = mapPrimFL sortCoalesceFL2 -- | The heart of "sortCoalesceFL"-sort_coalesceFL2 :: FL Prim C(x y) -> FL Prim C(x y)-sort_coalesceFL2 NilFL = NilFL-sort_coalesceFL2 (x:>:xs) | IsEq <- nullP x = sort_coalesceFL2 xs-sort_coalesceFL2 (x:>:xs) | IsEq <- is_identity x = sort_coalesceFL2 xs-sort_coalesceFL2 (x:>:xs) = either id id $ push_coalesce_patch x $ sort_coalesceFL2 xs+sortCoalesceFL2 :: FL Prim C(x y) -> FL Prim C(x y)+sortCoalesceFL2 NilFL = NilFL+sortCoalesceFL2 (x:>:xs) | IsEq <- nullP x = sortCoalesceFL2 xs+sortCoalesceFL2 (x:>:xs) | IsEq <- isIdentity x = sortCoalesceFL2 xs+sortCoalesceFL2 (x:>:xs) = either id id $ pushCoalescePatch x $ sortCoalesceFL2 xs --- | 'push_coalesce_patch' @new ps@ is almost like @new :>: ps@ except+-- | 'pushCoalescePatch' @new ps@ is almost like @new :>: ps@ except -- as an alternative to consing, we first try to coalesce @new@ with -- the head of @ps@. If this fails, we try again, using commutation -- to push @new@ down the list until we find a place where either@@ -534,33 +540,33 @@ -- (c) coalescing succeeds. -- The basic principle is to coalesce if we can and cons otherwise. ----- As an additional optimization, push_coalesce_patch outputs a Left+-- As an additional optimization, pushCoalescePatch outputs a Left -- value if it wasn't able to shrink the patch sequence at all, and -- a Right value if it was indeed able to shrink the patch sequence. -- This avoids the O(N) calls to lengthFL that were in the older -- code. ----- Also note that push_coalesce_patch is only ever used (and should+-- Also note that pushCoalescePatch is only ever used (and should -- only ever be used) as an internal function in in--- sort_coalesceFL2.-push_coalesce_patch :: Prim C(x y) -> FL Prim C(y z)+-- sortCoalesceFL2.+pushCoalescePatch :: Prim C(x y) -> FL Prim C(y z) -> Either (FL Prim C(x z)) (FL Prim C(x z))-push_coalesce_patch new NilFL = Left (new:>:NilFL)-push_coalesce_patch new ps@(p:>:ps')+pushCoalescePatch new NilFL = Left (new:>:NilFL)+pushCoalescePatch new ps@(p:>:ps') = case coalesce (p :< new) of Just new' | IsEq <- nullP new' -> Right ps'- | otherwise -> Right $ either id id $ push_coalesce_patch new' ps'+ | otherwise -> Right $ either id id $ pushCoalescePatch new' ps' Nothing -> if comparePrim new p == LT then Left (new:>:ps) else case commute (new :> p) of Just (p' :> new') ->- case push_coalesce_patch new' ps' of+ case pushCoalescePatch new' ps' of Right r -> Right $ either id id $- push_coalesce_patch p' r+ pushCoalescePatch p' r Left r -> Left (p' :>: r) Nothing -> Left (new:>:ps) -is_in_directory :: FileName -> FileName -> Bool-is_in_directory d f = iid (fn2fp d) (fn2fp f)+isInDirectory :: FileName -> FileName -> Bool+isInDirectory d f = iid (fn2fp d) (fn2fp f) where iid (cd:cds) (cf:cfs) | cd /= cf = False | otherwise = iid cds cfs@@ -594,8 +600,8 @@ toPerhaps (Just x) = Succeeded x toPerhaps Nothing = Failed -clever_commute :: CommuteFunction -> CommuteFunction-clever_commute c (p1:<p2) =+cleverCommute :: CommuteFunction -> CommuteFunction+cleverCommute c (p1:<p2) = case c (p1 :< p2) of Succeeded x -> Succeeded x Failed -> Failed@@ -603,22 +609,22 @@ Succeeded (p1' :< p2') -> Succeeded (invert p2' :< invert p1') Failed -> Failed Unknown -> Unknown---clever_commute c (p1,p2) = c (p1,p2) `mplus`+--cleverCommute c (p1,p2) = c (p1,p2) `mplus` -- (case c (invert p2,invert p1) of -- Succeeded (p1', p2') -> Succeeded (invert p2', invert p1') -- Failed -> Failed -- Unknown -> Unknown) -speedy_commute :: CommuteFunction-speedy_commute (p1 :< p2) -- Deal with common case quickly!+speedyCommute :: CommuteFunction+speedyCommute (p1 :< p2) -- Deal with common case quickly! | p1_modifies /= Nothing && p2_modifies /= Nothing && p1_modifies /= p2_modifies = Succeeded (unsafeCoerce# p2 :< unsafeCoerce# p1) | otherwise = Unknown where p1_modifies = is_filepatch p1 p2_modifies = is_filepatch p2 -everything_else_commute :: CommuteFunction-everything_else_commute x = eec x+everythingElseCommute :: CommuteFunction+everythingElseCommute x = eec x where eec :: CommuteFunction eec (ChangePref p f t :<p1) = Succeeded (unsafeCoerce# p1 :< ChangePref p f t)@@ -627,8 +633,8 @@ eec (p2 :< Identity) = Succeeded (Identity :< p2) eec xx = msum [- clever_commute commute_filedir xx- ,clever_commute commute_split xx+ cleverCommute commuteFiledir xx+ ,cleverCommute commuteSplit xx ] {-@@ -647,8 +653,8 @@ case elegantMerge (y:\/:z) of Just (z' :/\: y') -> z' :/\: y' Nothing -> error "Commute Prim merge"- commute x = toMaybe $ msum [toFwdCommute speedy_commute x,- toFwdCommute everything_else_commute x+ commute x = toMaybe $ msum [toFwdCommute speedyCommute x,+ toFwdCommute everythingElseCommute x ] -- Recurse on everything, these are potentially spoofed patches listTouchedFiles (Move f1 f2) = map fn2fp [f1, f2]@@ -671,50 +677,50 @@ is_filepatch (FP f _) = Just f is_filepatch _ = Nothing -is_superdir :: FileName -> FileName -> Bool-is_superdir d1 d2 = isd (fn2fp d1) (fn2fp d2)+isSuperdir :: FileName -> FileName -> Bool+isSuperdir d1 d2 = isd (fn2fp d1) (fn2fp d2) where isd s1 s2 = length s2 >= length s1 + 1 && take (length s1 + 1) s2 == s1 ++ "/" -commute_filedir :: CommuteFunction-commute_filedir (FP f1 p1 :< FP f2 p2) =+commuteFiledir :: CommuteFunction+commuteFiledir (FP f1 p1 :< FP f2 p2) = if f1 /= f2 then Succeeded ( FP f2 (unsafeCoerce# p2) :< FP f1 (unsafeCoerce# p1) ) else commuteFP f1 (p1 :< p2)-commute_filedir (DP d1 p1 :< DP d2 p2) =- if (not $ is_in_directory d1 d2) && (not $ is_in_directory d2 d1) &&+commuteFiledir (DP d1 p1 :< DP d2 p2) =+ if (not $ isInDirectory d1 d2) && (not $ isInDirectory d2 d1) && d1 /= d2 then Succeeded ( DP d2 (unsafeCoerce# p2) :< DP d1 (unsafeCoerce# p1) ) else Failed-commute_filedir (DP d dp :< FP f fp) =- if not $ is_in_directory d f+commuteFiledir (DP d dp :< FP f fp) =+ if not $ isInDirectory d f then Succeeded (FP f (unsafeCoerce# fp) :< DP d (unsafeCoerce# dp)) else Failed -commute_filedir (Move d d' :< FP f2 p2)+commuteFiledir (Move d d' :< FP f2 p2) | f2 == d' = Failed | (p2 == AddFile || p2 == RmFile) && d == f2 = Failed | otherwise = Succeeded (FP (movedirfilename d d' f2) (unsafeCoerce# p2) :< Move d d')-commute_filedir (Move d d' :< DP d2 p2)- | is_superdir d2 d' || is_superdir d2 d = Failed+commuteFiledir (Move d d' :< DP d2 p2)+ | isSuperdir d2 d' || isSuperdir d2 d = Failed | (p2 == AddDir || p2 == RmDir) && d == d2 = Failed | d2 == d' = Failed | otherwise = Succeeded (DP (movedirfilename d d' d2) (unsafeCoerce# p2) :< Move d d')-commute_filedir (Move d d' :< Move f f')+commuteFiledir (Move d d' :< Move f f') | f == d' || f' == d = Failed | f == d || f' == d' = Failed- | d `is_superdir` f && f' `is_superdir` d' = Failed+ | d `isSuperdir` f && f' `isSuperdir` d' = Failed | otherwise = Succeeded (Move (movedirfilename d d' f) (movedirfilename d d' f') :< Move (movedirfilename f' f d) (movedirfilename f' f d')) -commute_filedir _ = Unknown+commuteFiledir _ = Unknown type CommuteFunction = FORALL(x y) (Prim :< Prim) C(x y) -> Perhaps ((Prim :< Prim) C(x y))-subcommutes :: [(String, CommuteFunction)]+subcommutes :: [(String, (Prim :< Prim) C(x y) -> Perhaps ((Prim :< Prim) C(x y)))] subcommutes =- [("speedy_commute", speedy_commute),- ("commute_filedir", clever_commute commute_filedir),- ("commute_filepatches", clever_commute commute_filepatches),+ [("speedyCommute", speedyCommute),+ ("commuteFiledir", cleverCommute commuteFiledir),+ ("commuteFilepatches", cleverCommute commuteFilepatches), ("commutex", toPerhaps . toRevCommute commute) ] @@ -737,7 +743,7 @@ \begin{code} canonize :: Prim C(x y) -> FL Prim C(x y) canonize (Split ps) = sortCoalesceFL ps-canonize p | IsEq <- is_identity p = NilFL+canonize p | IsEq <- isIdentity p = NilFL canonize (FP f (Hunk line old new)) = canonizeHunk f line old new canonize p = p :>: NilFL \end{code}@@ -787,9 +793,9 @@ different files. If they happen to modify the same file, we'll have to check whether or not they commutex. \begin{code}-commute_filepatches :: CommuteFunction-commute_filepatches (FP f1 p1 :< FP f2 p2) | f1 == f2 = commuteFP f1 (p1 :< p2)-commute_filepatches _ = Unknown+commuteFilepatches :: CommuteFunction+commuteFilepatches (FP f1 p1 :< FP f2 p2) | f1 == f2 = commuteFP f1 (p1 :< p2)+commuteFilepatches _ = Unknown commuteFP :: FileName -> (FilePatchType :< FilePatchType) C(x y) -> Perhaps ((Prim :< Prim) C(x y))@@ -800,10 +806,10 @@ commuteFP f (Hunk line1 old1 new1 :< Hunk line2 old2 new2) = seq f $ toPerhaps $ commuteHunk f (Hunk line1 old1 new1 :< Hunk line2 old2 new2) commuteFP f (TokReplace t o n :< Hunk line2 old2 new2) = seq f $- case try_tok_replace t o n old2 of+ case tryTokReplace t o n old2 of Nothing -> Failed Just old2' ->- case try_tok_replace t o n new2 of+ case tryTokReplace t o n new2 of Nothing -> Failed Just new2' -> Succeeded (FP f (Hunk line2 old2' new2') :< FP f (TokReplace t o n))@@ -910,29 +916,29 @@ canonizeHunk f line old new | null old || null new = FP f (Hunk line old new) :>: NilFL-canonizeHunk f line old new = make_holey f line $ getChanges old new+canonizeHunk f line old new = makeHoley f line $ getChanges old new -make_holey :: FileName -> Int -> [(Int,[B.ByteString], [B.ByteString])]+makeHoley :: FileName -> Int -> [(Int,[B.ByteString], [B.ByteString])] -> FL Prim C(x y)-make_holey f line changes =+makeHoley f line changes = unsafeMap_l2f (\ (l,o,n) -> FP f (Hunk (l+line) o n)) changes- -try_tok_replace :: String -> String -> String++tryTokReplace :: String -> String -> String -> [B.ByteString] -> Maybe [B.ByteString]-try_tok_replace t o n mss =- mapM (fmap B.concat . try_tok_internal t (BC.pack o) (BC.pack n)) mss+tryTokReplace t o n mss =+ mapM (fmap B.concat . tryTokInternal t (BC.pack o) (BC.pack n)) mss -try_tok_internal :: String -> B.ByteString -> B.ByteString+tryTokInternal :: String -> B.ByteString -> B.ByteString -> B.ByteString -> Maybe [B.ByteString]-try_tok_internal _ o n s | isNothing (substrPS o s) &&+tryTokInternal _ o n s | isNothing (substrPS o s) && isNothing (substrPS n s) = Just [s]-try_tok_internal t o n s =+tryTokInternal t o n s = case BC.break (regChars t) s of (before,s') -> case BC.break (not . regChars t) s' of (tok,after) ->- case try_tok_internal t o n after of+ case tryTokInternal t o n after of Nothing -> Nothing Just rest -> if tok == o@@ -948,20 +954,20 @@ instance MyEq Prim where unsafeCompare (Move a b) (Move c d) = a == c && b == d unsafeCompare (DP d1 p1) (DP d2 p2)- = d1 == d2 && p1 `unsafeCompare` p2 + = d1 == d2 && p1 `unsafeCompare` p2 unsafeCompare (FP f1 fp1) (FP f2 fp2) = f1 == f2 && fp1 `unsafeCompare` fp2 unsafeCompare (Split ps1) (Split ps2)- = eq_FL unsafeCompare ps1 ps2+ = eqFL unsafeCompare ps1 ps2 unsafeCompare (ChangePref a1 b1 c1) (ChangePref a2 b2 c2) = c1 == c2 && b1 == b2 && a1 == a2 unsafeCompare Identity Identity = True unsafeCompare _ _ = False -merge_orders :: Ordering -> Ordering -> Ordering-merge_orders EQ x = x-merge_orders LT _ = LT-merge_orders GT _ = GT+mergeOrders :: Ordering -> Ordering -> Ordering+mergeOrders EQ x = x+mergeOrders LT _ = LT+mergeOrders GT _ = GT -- | 'comparePrim' @p1 p2@ is used to provide an arbitrary ordering between -- @p1@ and @p2@. Basically, identical patches are equal and@@ -977,7 +983,7 @@ comparePrim (FP f1 fp1) (FP f2 fp2) = compare (f1, fp1) $ unsafeCoerceP (f2, fp2) comparePrim (FP _ _) _ = LT comparePrim _ (FP _ _) = GT-comparePrim (Split ps1) (Split ps2) = compare_FL comparePrim ps1 $ unsafeCoerceP ps2+comparePrim (Split ps1) (Split ps2) = compareFL comparePrim ps1 $ unsafeCoerceP ps2 comparePrim (Split _) _ = LT comparePrim _ (Split _) = GT comparePrim Identity Identity = EQ@@ -986,20 +992,20 @@ comparePrim (ChangePref a1 b1 c1) (ChangePref a2 b2 c2) = compare (c1, b1, a1) (c2, b2, a2) -eq_FL :: (FORALL(b c d e) a C(b c) -> a C(d e) -> Bool)+eqFL :: (FORALL(b c d e) a C(b c) -> a C(d e) -> Bool) -> FL a C(x y) -> FL a C(w z) -> Bool-eq_FL _ NilFL NilFL = True-eq_FL f (x:>:xs) (y:>:ys) = f x y && eq_FL f xs ys-eq_FL _ _ _ = False+eqFL _ NilFL NilFL = True+eqFL f (x:>:xs) (y:>:ys) = f x y && eqFL f xs ys+eqFL _ _ _ = False -compare_FL :: (FORALL(b c d e) a C(b c) -> a C(d e) -> Ordering)+compareFL :: (FORALL(b c d e) a C(b c) -> a C(d e) -> Ordering) -> FL a C(x y) -> FL a C(w z) -> Ordering-compare_FL _ NilFL NilFL = EQ-compare_FL _ NilFL _ = LT-compare_FL _ _ NilFL = GT-compare_FL f (x:>:xs) (y:>:ys) = f x y `merge_orders` compare_FL f xs ys- +compareFL _ NilFL NilFL = EQ+compareFL _ NilFL _ = LT+compareFL _ _ NilFL = GT+compareFL f (x:>:xs) (y:>:ys) = f x y `mergeOrders` compareFL f xs ys + class FromPrim p where fromPrim :: Prim C(x y) -> p C(x y) @@ -1028,7 +1034,7 @@ nubsort $ concatMap (unseal listTouchedFiles) $ concat $ resolveConflicts p resolveConflicts :: p C(x y) -> [[Sealed (FL Prim C(y))]] resolveConflicts _ = []- -- | If 'commute_no_conflicts' @x :> y@ succeeds, we know that that @x@ commutes+ -- | If 'commuteNoConflicts' @x :> y@ succeeds, we know that that @x@ commutes -- past @y@ without any conflicts. This function is useful for patch types -- for which 'commute' is defined to always succeed; so we need some way to -- pick out the specific cases where commutation succeeds without any conflicts.@@ -1062,8 +1068,8 @@ -- -- We check that commuting @X@ and @Y@ succeeds, as does commuting @-X@ and @Y'@. -- It also checks that @Y'' == Y@ and that @-(X')@ is the same as @(-X)'@- commute_no_conflicts :: (p :> p) C(x y) -> Maybe ((p :> p) C(x y))- commute_no_conflicts (x:>y) =+ commuteNoConflicts :: (p :> p) C(x y) -> Maybe ((p :> p) C(x y))+ commuteNoConflicts (x:>y) = do y':>x' <- commute (x:>y) y'':>ix'' <- commute (invert x :> y') IsEq <- return $ y'' =\/= y@@ -1073,16 +1079,19 @@ conflictedEffect x = case listConflictedFiles x of [] -> mapFL (IsC Okay) $ effect x _ -> mapFL (IsC Conflicted) $ effect x+ isInconsistent :: p C(x y) -> Maybe Doc+ isInconsistent _ = Nothing instance Conflict p => Conflict (FL p) where listConflictedFiles = nubsort . concat . mapFL listConflictedFiles resolveConflicts NilFL = [] resolveConflicts x = resolveConflicts $ reverseFL x- commute_no_conflicts (NilFL :> x) = Just (x :> NilFL)- commute_no_conflicts (x :> NilFL) = Just (NilFL :> x)- commute_no_conflicts (xs :> ys) = do ys' :> rxs' <- commute_no_conflictsRLFL (reverseFL xs :> ys)+ commuteNoConflicts (NilFL :> x) = Just (x :> NilFL)+ commuteNoConflicts (x :> NilFL) = Just (NilFL :> x)+ commuteNoConflicts (xs :> ys) = do ys' :> rxs' <- commuteNoConflictsRLFL (reverseFL xs :> ys) return $ ys' :> reverseRL rxs' conflictedEffect = concat . mapFL conflictedEffect+ isInconsistent = listToMaybe . catMaybes . mapFL isInconsistent instance Conflict p => Conflict (RL p) where listConflictedFiles = nubsort . concat . mapRL listConflictedFiles@@ -1090,15 +1099,16 @@ where rcs :: RL p C(x y) -> FL p C(y w) -> [[Sealed (FL Prim C(w))]] rcs NilRL _ = [] rcs (p:<:ps) passedby | (_:_) <- resolveConflicts p =- case commute_no_conflictsFL (p:>passedby) of+ case commuteNoConflictsFL (p:>passedby) of Just (_:> p') -> resolveConflicts p' ++ rcs ps (p:>:passedby) Nothing -> rcs ps (p:>:passedby) rcs (p:<:ps) passedby = seq passedby $ rcs ps (p:>:passedby)- commute_no_conflicts (NilRL :> x) = Just (x :> NilRL)- commute_no_conflicts (x :> NilRL) = Just (NilRL :> x)- commute_no_conflicts (xs :> ys) = do ys' :> rxs' <- commute_no_conflictsRLFL (xs :> reverseRL ys)+ commuteNoConflicts (NilRL :> x) = Just (x :> NilRL)+ commuteNoConflicts (x :> NilRL) = Just (NilRL :> x)+ commuteNoConflicts (xs :> ys) = do ys' :> rxs' <- commuteNoConflictsRLFL (xs :> reverseRL ys) return $ reverseFL ys' :> rxs' conflictedEffect = concat . reverse . mapRL conflictedEffect+ isInconsistent = listToMaybe . catMaybes . mapRL isInconsistent data IsConflictedPrim where IsC :: !ConflictState -> !(Prim C(x y)) -> IsConflictedPrim@@ -1133,23 +1143,23 @@ effect p = concatFL $ mapFL_FL effect $ reverseRL p effectRL p = concatRL $ mapRL_RL effectRL p -commute_no_conflictsFL :: Conflict p => (p :> FL p) C(x y) -> Maybe ((FL p :> p) C(x y))-commute_no_conflictsFL (p :> NilFL) = Just (NilFL :> p)-commute_no_conflictsFL (q :> p :>: ps) = do p' :> q' <- commute_no_conflicts (q :> p)- ps' :> q'' <- commute_no_conflictsFL (q' :> ps)+commuteNoConflictsFL :: Conflict p => (p :> FL p) C(x y) -> Maybe ((FL p :> p) C(x y))+commuteNoConflictsFL (p :> NilFL) = Just (NilFL :> p)+commuteNoConflictsFL (q :> p :>: ps) = do p' :> q' <- commuteNoConflicts (q :> p)+ ps' :> q'' <- commuteNoConflictsFL (q' :> ps) return (p' :>: ps' :> q'') -commute_no_conflictsRL :: Conflict p => (RL p :> p) C(x y) -> Maybe ((p :> RL p) C(x y))-commute_no_conflictsRL (NilRL :> p) = Just (p :> NilRL)-commute_no_conflictsRL (p :<: ps :> q) = do q' :> p' <- commute_no_conflicts (p :> q)- q'' :> ps' <- commute_no_conflictsRL (ps :> q')+commuteNoConflictsRL :: Conflict p => (RL p :> p) C(x y) -> Maybe ((p :> RL p) C(x y))+commuteNoConflictsRL (NilRL :> p) = Just (p :> NilRL)+commuteNoConflictsRL (p :<: ps :> q) = do q' :> p' <- commuteNoConflicts (p :> q)+ q'' :> ps' <- commuteNoConflictsRL (ps :> q') return (q'' :> p' :<: ps') -commute_no_conflictsRLFL :: Conflict p => (RL p :> FL p) C(x y) -> Maybe ((FL p :> RL p) C(x y))-commute_no_conflictsRLFL (NilRL :> ys) = Just (ys :> NilRL)-commute_no_conflictsRLFL (xs :> NilFL) = Just (NilFL :> xs)-commute_no_conflictsRLFL (xs :> y :>: ys) = do y' :> xs' <- commute_no_conflictsRL (xs :> y)- ys' :> xs'' <- commute_no_conflictsRLFL (xs' :> ys)+commuteNoConflictsRLFL :: Conflict p => (RL p :> FL p) C(x y) -> Maybe ((FL p :> RL p) C(x y))+commuteNoConflictsRLFL (NilRL :> ys) = Just (ys :> NilRL)+commuteNoConflictsRLFL (xs :> NilFL) = Just (NilFL :> xs)+commuteNoConflictsRLFL (xs :> y :>: ys) = do y' :> xs' <- commuteNoConflictsRL (xs :> y)+ ys' :> xs'' <- commuteNoConflictsRLFL (xs' :> ys) return (y' :>: ys' :> xs'') \end{code}
src/Darcs/Patch/Properties.lhs view
@@ -34,15 +34,15 @@ #include "gadts.h" -module Darcs.Patch.Properties ( recommute, commute_inverses, permutivity, partial_permutivity,- identity_commutes, inverse_doesnt_commute,- patch_and_inverse_commute, merge_either_way,+module Darcs.Patch.Properties ( recommute, commuteInverses, permutivity, partialPermutivity,+ identityCommutes, inverseDoesntCommute,+ patchAndInverseCommute, mergeEitherWay, show_read,- merge_commute, merge_consistent, merge_arguments_consistent,- join_inverses, join_commute ) where+ mergeCommute, mergeConsistent, mergeArgumentsConsistent,+ joinInverses, joinCommute ) where import Control.Monad ( msum, mplus )-import Darcs.Witnesses.Show ( Show2(..) )+import Darcs.Witnesses.Show ( Show2(..), show2 ) import Darcs.Patch.Patchy import Darcs.Patch.Prim import Darcs.Patch ()@@ -69,18 +69,18 @@ \end{prp} \begin{code}-identity_commutes :: forall p C(x y). Patchy p => p C(x y) -> Maybe Doc-identity_commutes p = case commute (p :> identity) of- Nothing -> Just $ redText "identity_commutes failed:" $$ showPatch p+identityCommutes :: forall p C(x y). Patchy p => p C(x y) -> Maybe Doc+identityCommutes p = case commute (p :> identity) of+ Nothing -> Just $ redText "identityCommutes failed:" $$ showPatch p Just (i :> p') | IsEq <- i =\/= identity, IsEq <- p' =\/= p -> checkRightIdentity $ commute $ identity :> p- Just _ -> Just $ greenText "identity_commutes"+ Just _ -> Just $ greenText "identityCommutes" where checkRightIdentity :: Maybe ((p :> p) C(x y)) -> Maybe Doc- checkRightIdentity Nothing = Just $ redText "identity_commutes failed 2:" $$ showPatch p+ checkRightIdentity Nothing = Just $ redText "identityCommutes failed 2:" $$ showPatch p checkRightIdentity (Just (p2 :> i2)) | IsEq <- i2 =\/= identity, IsEq <- p2 =\/= p = Nothing- checkRightIdentity (Just _) = Just $ greenText "identity_commutes 2"+ checkRightIdentity (Just _) = Just $ greenText "identityCommutes 2" \end{code} \begin{prp}[Inverse doesn't commute]@@ -89,10 +89,10 @@ \end{prp} \begin{code}-inverse_doesnt_commute :: Patchy p => p C(a b) -> Maybe Doc-inverse_doesnt_commute p | IsEq <- sloppyIdentity p = Nothing+inverseDoesntCommute :: Patchy p => p C(a b) -> Maybe Doc+inverseDoesntCommute p | IsEq <- sloppyIdentity p = Nothing | otherwise = do p' :> _ <- commute (invert p :> p)- Just $ redText "inverse_doesnt_commute" $$ showPatch p'+ Just $ redText "inverseDoesntCommute" $$ showPatch p' \end{code} \subsection{Commute properties}@@ -128,9 +128,9 @@ \end{prp} \begin{code}-commute_inverses :: Patchy p => (FORALL(x y) (p :> p) C(x y) -> Maybe ((p :> p) C(x y)))+commuteInverses :: Patchy p => (FORALL(x y) (p :> p) C(x y) -> Maybe ((p :> p) C(x y))) -> (p :> p) C(a b) -> Maybe Doc-commute_inverses c (x :> y) =+commuteInverses c (x :> y) = case c (x :> y) of Nothing -> Nothing Just (y' :> x') ->@@ -161,13 +161,13 @@ This property is only true of primitive patches. \begin{code}-patch_and_inverse_commute :: Patchy p =>+patchAndInverseCommute :: Patchy p => (FORALL(x y) (p :> p) C(x y) -> Maybe ((p :> p) C(x y))) -> (p :> p) C(a b) -> Maybe Doc-patch_and_inverse_commute c (x :> y) =+patchAndInverseCommute c (x :> y) = do y' :> x' <- c (x :> y) case c (invert x :> y') of- Nothing -> Just (redText "failure in patch_and_inverse_commute")+ Nothing -> Just (redText "failure in patchAndInverseCommute") Just (y'' :> ix') -> case y'' =\/= y of NotEq -> Just (redText "y'' /= y" $$@@ -234,9 +234,9 @@ redText "z3" $$ showPatch z3 $$ redText "z3_" $$ showPatch z3_ -partial_permutivity :: Patchy p => (FORALL(x y) (p :> p) C(x y) -> Maybe ((p :> p) C(x y)))+partialPermutivity :: Patchy p => (FORALL(x y) (p :> p) C(x y) -> Maybe ((p :> p) C(x y))) -> (p :> p :> p) C(a b) -> Maybe Doc-partial_permutivity c (xx:>yy:>zz) = pp (xx:>yy:>zz) `mplus` pp (invert zz:>invert yy:>invert xx)+partialPermutivity c (xx:>yy:>zz) = pp (xx:>yy:>zz) `mplus` pp (invert zz:>invert yy:>invert xx) where pp (x:>y:>z) = do z1 :> y1 <- c (y :> z) _ :> x1 <- c (x :> z1) case c (x :> y) of@@ -244,45 +244,45 @@ Nothing -> case c (x1 :> y1) of Nothing -> Nothing- Just _ -> Just $ greenText "partial_permutivity error" $$+ Just _ -> Just $ greenText "partialPermutivity error" $$ greenText "x" $$ showPatch x $$ greenText "y" $$ showPatch y $$ greenText "z" $$ showPatch z -merge_arguments_consistent :: Patchy p =>+mergeArgumentsConsistent :: Patchy p => (FORALL(x y) p C(x y) -> Maybe Doc) -> (p :\/: p) C(a b) -> Maybe Doc-merge_arguments_consistent is_consistent (x :\/: y) =- msum [(\z -> redText "merge_arguments_consistent x" $$ showPatch x $$ z) `fmap` is_consistent x,- (\z -> redText "merge_arguments_consistent y" $$ showPatch y $$ z) `fmap` is_consistent y]+mergeArgumentsConsistent isConsistent (x :\/: y) =+ msum [(\z -> redText "mergeArgumentsConsistent x" $$ showPatch x $$ z) `fmap` isConsistent x,+ (\z -> redText "mergeArgumentsConsistent y" $$ showPatch y $$ z) `fmap` isConsistent y] -merge_consistent :: Patchy p =>+mergeConsistent :: Patchy p => (FORALL(x y) p C(x y) -> Maybe Doc) -> (p :\/: p) C(a b) -> Maybe Doc-merge_consistent is_consistent (x :\/: y) =+mergeConsistent isConsistent (x :\/: y) = case merge (x :\/: y) of y' :/\: x' ->- msum [(\z -> redText "merge_consistent x" $$ showPatch x $$ z) `fmap` is_consistent x,- (\z -> redText "merge_consistent y" $$ showPatch y $$ z) `fmap` is_consistent y,- (\z -> redText "merge_consistent x'" $$ showPatch x' $$ z $$+ msum [(\z -> redText "mergeConsistent x" $$ showPatch x $$ z) `fmap` isConsistent x,+ (\z -> redText "mergeConsistent y" $$ showPatch y $$ z) `fmap` isConsistent y,+ (\z -> redText "mergeConsistent x'" $$ showPatch x' $$ z $$ redText "where x' comes from x" $$ showPatch x $$- redText "and y" $$ showPatch y) `fmap` is_consistent x',- (\z -> redText "merge_consistent y'" $$ showPatch y' $$ z) `fmap` is_consistent y']+ redText "and y" $$ showPatch y) `fmap` isConsistent x',+ (\z -> redText "mergeConsistent y'" $$ showPatch y' $$ z) `fmap` isConsistent y'] -merge_either_way :: Patchy p => (p :\/: p) C(x y) -> Maybe Doc-merge_either_way (x :\/: y) =+mergeEitherWay :: Patchy p => (p :\/: p) C(x y) -> Maybe Doc+mergeEitherWay (x :\/: y) = case merge (x :\/: y) of y' :/\: x' -> case merge (y :\/: x) of x'' :/\: y'' | IsEq <- x'' =\/= x', IsEq <- y'' =\/= y' -> Nothing- | otherwise -> Just $ redText "merge_either_way bug"+ | otherwise -> Just $ redText "mergeEitherWay bug" -merge_commute :: Patchy p => (p :\/: p) C(x y) -> Maybe Doc-merge_commute (x :\/: y) =+mergeCommute :: Patchy p => (p :\/: p) C(x y) -> Maybe Doc+mergeCommute (x :\/: y) = case merge (x :\/: y) of y' :/\: x' -> case commute (x :> y') of- Nothing -> Just $ redText "merge_commute 1" $$+ Nothing -> Just $ redText "mergeCommute 1" $$ redText "x" $$ showPatch x $$ redText "y" $$ showPatch y $$ redText "x'" $$ showPatch x' $$@@ -291,7 +291,7 @@ | IsEq <- y_ =\/= y, IsEq <- x'_ =\/= x' -> case commute (y :> x') of- Nothing -> Just $ redText "merge_commute 2 failed" $$+ Nothing -> Just $ redText "mergeCommute 2 failed" $$ redText "x" $$ showPatch x $$ redText "y" $$ showPatch y $$ redText "x'" $$ showPatch x' $$@@ -299,14 +299,14 @@ Just (x_ :> y'_) | IsEq <- x_ =\/= x, IsEq <- y'_ =\/= y' -> Nothing- | otherwise -> Just $ redText "merge_commute 3" $$+ | otherwise -> Just $ redText "mergeCommute 3" $$ redText "x" $$ showPatch x $$ redText "y" $$ showPatch y $$ redText "x'" $$ showPatch x' $$ redText "y'" $$ showPatch y' $$ redText "x_" $$ showPatch x_ $$ redText "y'_" $$ showPatch y'_- | otherwise -> Just $ redText "merge_commute 4" $$+ | otherwise -> Just $ redText "mergeCommute 4" $$ redText "x" $$ showPatch x $$ redText "y" $$ showPatch y $$ redText "x'" $$ showPatch x' $$@@ -314,28 +314,28 @@ redText "x'_" $$ showPatch x'_ $$ redText "y_" $$ showPatch y_ -join_inverses :: (FORALL(x y) (Prim :> Prim) C(x y) -> Maybe (Prim C(x y)))+joinInverses :: (FORALL(x y) (Prim :> Prim) C(x y) -> Maybe (Prim C(x y))) -> Prim C(a b) -> Maybe Doc-join_inverses j p = case j (invert p :> p) of+joinInverses j p = case j (invert p :> p) of Just Identity -> Nothing- Just p' -> Just $ redText "join_inverses gave just" $$ showPatch p'- Nothing -> Just $ redText "join_inverses failed"+ Just p' -> Just $ redText "joinInverses gave just" $$ showPatch p'+ Nothing -> Just $ redText "joinInverses failed" -join_commute :: (FORALL(x y) (Prim :> Prim) C(x y) -> Maybe (Prim C(x y)))+joinCommute :: (FORALL(x y) (Prim :> Prim) C(x y) -> Maybe (Prim C(x y))) -> (Prim :> Prim :> Prim) C(a b) -> Maybe Doc-join_commute j (a :> b :> c) =+joinCommute j (a :> b :> c) = do x <- j (b :> c)- case commuteFL (a :> b :>: c :>: NilFL) of+ case commuteFLorComplain (a :> b :>: c :>: NilFL) of Right (b' :>: c' :>: NilFL :> a') -> case commute (a :> x) of- Nothing -> Just $ greenText "join_commute 1"+ Nothing -> Just $ greenText "joinCommute 1" Just (x' :> a'') -> case a'' =/\= a' of- NotEq -> Just $ greenText "join_commute 3"+ NotEq -> Just $ greenText "joinCommute 3" IsEq -> case j (b' :> c') of- Nothing -> Just $ greenText "join_commute 4"+ Nothing -> Just $ greenText "joinCommute 4" Just x'' -> case x' =\/= x'' of- NotEq -> Just $ greenText "join_commute 5"+ NotEq -> Just $ greenText "joinCommute 5" IsEq -> Nothing _ -> Nothing
src/Darcs/Patch/Read.hs view
@@ -30,32 +30,30 @@ import qualified Data.ByteString.Char8 as BC (head, unpack, dropWhile, break) import qualified Data.ByteString as B (ByteString, null, init, tail, empty, concat) -import Darcs.Patch.FileName ( FileName, fn2fp, fp2fn, ps2fn, decode_white )+import Darcs.Patch.FileName ( FileName, fn2fp, fp2fn, ps2fn, decodeWhite ) import Darcs.Patch.Core ( Patch(..), Named(..) ) import Darcs.Patch.Prim ( Prim(..), FileNameFormat(..), DirPatchType(..), FilePatchType(..), hunk, binary )-#ifndef GADT_WITNESSES import Darcs.Patch.Commute ( merger ) import Darcs.Patch.Patchy ( invert )-#endif import Darcs.Patch.Info ( PatchInfo, readPatchInfo )-import Darcs.Patch.ReadMonads (ParserM, work, maybe_work, alter_input,- parse_strictly, peek_input, lex_string, lex_eof, my_lex)+import Darcs.Patch.ReadMonads (ParserM, work, maybeWork, alterInput,+ parseStrictly, peekInput, lexString, lexEof, myLex) #include "impossible.h" import Darcs.Patch.Patchy ( ReadPatch, readPatch', bracketedFL )-import Darcs.Witnesses.Ordered ( FL(..) )+import Darcs.Witnesses.Ordered ( FL(..), unsafeCoerceP ) import Darcs.Witnesses.Sealed ( Sealed(..), seal, mapSeal ) readPatch :: ReadPatch p => B.ByteString -> Maybe (Sealed (p C(x )), B.ByteString)-readPatch ps = case parse_strictly (readPatch' False) ps of+readPatch ps = case parseStrictly (readPatch' False) ps of Just (Just p, ps') -> Just (p, ps') _ -> Nothing instance ReadPatch p => ReadPatch (Named p) where readPatch' want_eof- = do s <- peek_input- case liftM (BC.unpack . fst) $ my_lex s of+ = do s <- peekInput+ case liftM (BC.unpack . fst) $ myLex s of Just ('[':_) -> liftM Just $ readNamed want_eof -- ] _ -> return Nothing @@ -64,9 +62,9 @@ readPrim :: ParserM m => FileNameFormat -> m (Maybe (Sealed (Prim C(x )))) readPrim x- = do s <- peek_input- case liftM (BC.unpack . fst) $ my_lex s of- Just "{}" -> do work my_lex+ = do s <- peekInput+ case liftM (BC.unpack . fst) $ myLex s of+ Just "{}" -> do work myLex return $ Just $ seal Identity Just "(" -> liftM Just $ readSplit x -- ) Just "hunk" -> liftM (Just . seal) $ readHunk x@@ -82,69 +80,67 @@ instance ReadPatch Patch where readPatch' want_eof- = do mps <- bracketedFL (fromIntegral $ fromEnum '{') (fromIntegral $ fromEnum '}')+ = do mps <- bracketedFL (readPatch' False) (fromIntegral $ fromEnum '{') (fromIntegral $ fromEnum '}') case mps of Just (Sealed ps) -> return $ Just $ Sealed $ ComP ps- Nothing -> do s <- peek_input- case liftM (BC.unpack . fst) $ my_lex s of-#ifndef GADT_WITNESSES+ Nothing -> do s <- peekInput+ case liftM (BC.unpack . fst) $ myLex s of Just "merger" -> liftM (Just . seal) $ readMerger True Just "regrem" -> liftM (Just . seal) $ readMerger False-#endif _ -> liftM (fmap (mapSeal PP)) $ readPatch' want_eof -read_patches :: ParserM m => FileNameFormat -> String -> Bool -> m (Sealed (FL Prim C(x )))-read_patches x str want_eof+readPatches :: ParserM m => FileNameFormat -> String -> Bool -> m (Sealed (FL Prim C(x )))+readPatches x str want_eof = do mp <- readPrim x case mp of- Nothing -> do unit <- lex_string str+ Nothing -> do unit <- lexString str case unit of- () -> if want_eof then do unit' <- lex_eof+ () -> if want_eof then do unit' <- lexEof case unit' of () -> return $ seal NilFL else return $ seal NilFL- Just (Sealed p) -> do Sealed ps <- read_patches x str want_eof+ Just (Sealed p) -> do Sealed ps <- readPatches x str want_eof return $ seal (p:>:ps) readSplit :: ParserM m => FileNameFormat -> m (Sealed (Prim C(x ))) readSplit x = do- work my_lex- ps <- read_patches x ")" False+ work myLex+ ps <- readPatches x ")" False return $ Split `mapSeal` ps readFileName :: FileNameFormat -> B.ByteString -> FileName readFileName OldFormat = ps2fn-readFileName NewFormat = fp2fn . decode_white . BC.unpack+readFileName NewFormat = fp2fn . decodeWhite . BC.unpack readHunk :: ParserM m => FileNameFormat -> m (Prim C(x y)) readHunk x = do- work my_lex- fi <- work my_lex+ work myLex+ fi <- work myLex l <- work readIntPS- have_nl <- skip_newline+ have_nl <- skipNewline if have_nl- then do work $ lines_starting_with ' ' -- skipping context- old <- work $ lines_starting_with '-'- new <- work $ lines_starting_with '+'- work $ lines_starting_with ' ' -- skipping context+ then do work $ linesStartingWith ' ' -- skipping context+ old <- work $ linesStartingWith '-'+ new <- work $ linesStartingWith '+'+ work $ linesStartingWith ' ' -- skipping context return $ hunk (fn2fp $ readFileName x fi) l old new else return $ hunk (fn2fp $ readFileName x fi) l [] [] -skip_newline :: ParserM m => m Bool-skip_newline = do s <- peek_input+skipNewline :: ParserM m => m Bool+skipNewline = do s <- peekInput if B.null s then return False else if BC.head s /= '\n' then return False- else alter_input B.tail >> return True+ else alterInput B.tail >> return True readTok :: ParserM m => FileNameFormat -> m (Prim C(x y)) readTok x = do- work my_lex- f <- work my_lex- regstr <- work my_lex- o <- work my_lex- n <- work my_lex+ work myLex+ f <- work myLex+ regstr <- work myLex+ o <- work myLex+ n <- work myLex return $ FP (readFileName x f) $ TokReplace (BC.unpack (drop_brackets regstr)) (BC.unpack o) (BC.unpack n) where drop_brackets = B.init . B.tail@@ -163,90 +159,88 @@ -- > ... readBinary :: ParserM m => FileNameFormat -> m (Prim C(x y)) readBinary x = do- work my_lex- fi <- work my_lex- work my_lex- alter_input dropSpace- old <- work $ lines_starting_with '*'- work my_lex- alter_input dropSpace- new <- work $ lines_starting_with '*'+ work myLex+ fi <- work myLex+ work myLex+ alterInput dropSpace+ old <- work $ linesStartingWith '*'+ work myLex+ alterInput dropSpace+ new <- work $ linesStartingWith '*' return $ binary (fn2fp $ readFileName x fi) (fromHex2PS $ B.concat old) (fromHex2PS $ B.concat new) readAddFile :: ParserM m => FileNameFormat -> m (Prim C(x y))-readAddFile x = do work my_lex- f <- work my_lex+readAddFile x = do work myLex+ f <- work myLex return $ FP (readFileName x f) AddFile readRmFile :: ParserM m => FileNameFormat -> m (Prim C(x y))-readRmFile x = do work my_lex- f <- work my_lex+readRmFile x = do work myLex+ f <- work myLex return $ FP (readFileName x f) RmFile readMove :: ParserM m => FileNameFormat -> m (Prim C(x y))-readMove x = do work my_lex- d <- work my_lex- d' <- work my_lex+readMove x = do work myLex+ d <- work myLex+ d' <- work myLex return $ Move (readFileName x d) (readFileName x d') readChangePref :: ParserM m => m (Prim C(x y)) readChangePref- = do work my_lex- p <- work my_lex+ = do work myLex+ p <- work myLex f <- work (Just . BC.break ((==)'\n') . B.tail . BC.dropWhile (== ' ')) t <- work (Just . BC.break ((==)'\n') . B.tail) return $ ChangePref (BC.unpack p) (BC.unpack f) (BC.unpack t) readAddDir :: ParserM m => FileNameFormat -> m (Prim C(x y))-readAddDir x = do work my_lex- f <- work my_lex+readAddDir x = do work myLex+ f <- work myLex return $ DP (readFileName x f) AddDir readRmDir :: ParserM m => FileNameFormat -> m (Prim C(x y))-readRmDir x = do work my_lex- f <- work my_lex+readRmDir x = do work myLex+ f <- work myLex return $ DP (readFileName x f) RmDir -#ifndef GADT_WITNESSES readMerger :: ParserM m => Bool -> m (Patch C(x y))-readMerger b = do work my_lex- g <- work my_lex- lex_string "("+readMerger b = do work myLex+ g <- work myLex+ lexString "(" Just (Sealed p1) <- readPatch' False Just (Sealed p2) <- readPatch' False- lex_string ")"- let m = merger (BC.unpack g) p1 p2- return $ if b then m else invert m-#endif+ lexString ")"+ Sealed m <- return $ merger (BC.unpack g) p1 p2+ return $ if b then unsafeCoerceP m else unsafeCoerceP (invert m) readNamed :: (ReadPatch p, ParserM m) => Bool -> m (Sealed (Named p C(x ))) readNamed want_eof- = do mn <- maybe_work readPatchInfo+ = do mn <- maybeWork readPatchInfo case mn of Nothing -> bug "readNamed 1" Just n ->- do d <- read_depends+ do d <- readDepends Just p <- readPatch' want_eof return $ (NamedP n d) `mapSeal` p-read_depends :: ParserM m => m [PatchInfo]-read_depends = do s <- peek_input- case my_lex s of+readDepends :: ParserM m => m [PatchInfo]+readDepends = do s <- peekInput+ case myLex s of Just (xs, _) | BC.unpack xs == "<" ->- do work my_lex- read_pis+ do work myLex+ readPis _ -> return []-read_pis :: ParserM m => m [PatchInfo]-read_pis = do mpi <- maybe_work readPatchInfo+readPis :: ParserM m => m [PatchInfo]+readPis = do mpi <- maybeWork readPatchInfo case mpi of- Just pi -> do pis <- read_pis+ Just pi -> do pis <- readPis return (pi:pis)- Nothing -> do alter_input (B.tail . BC.dropWhile (/= '>'))+ Nothing -> do alterInput (B.tail . BC.dropWhile (/= '>')) return [] -lines_starting_with :: Char -> B.ByteString -> Maybe ([B.ByteString], B.ByteString)-lines_starting_with c thes =+linesStartingWith :: Char -> B.ByteString -> Maybe ([B.ByteString], B.ByteString)+linesStartingWith c thes = Just (lsw [] thes) where lsw acc s | B.null s || BC.head s /= c = (reverse acc, s) lsw acc s = let s' = B.tail s
src/Darcs/Patch/ReadMonads.hs view
@@ -1,56 +1,87 @@--module Darcs.Patch.ReadMonads (ParserM, work, maybe_work, alter_input,- parse_strictly, parse_lazily,- peek_input,- lex_char, lex_string, lex_strings, lex_eof,- my_lex) where+-- | This module defines our parsing monad. In the past there have been lazy+-- and strict parsers in this module. Currently we have only the strict+-- variant and it is used for parsing patch files.+module Darcs.Patch.ReadMonads (ParserM, work, maybeWork, alterInput,+ parseStrictly,+ peekInput,+ lexChar, lexString, lexStrings, lexEof,+ myLex) where import ByteStringUtils ( dropSpace, breakSpace ) import qualified Data.ByteString as B (null, empty, ByteString) import qualified Data.ByteString.Char8 as BC (unpack, pack) -lex_char :: ParserM m => Char -> m ()-lex_char c = lex_string [c]+-- | 'lexChar' checks if the next space delimited token from+-- the input stream matches a specific 'Char'.+-- Uses 'Maybe' inside 'ParserM' to handle failed matches, so+-- that it always returns () on success.+lexChar :: ParserM m => Char -> m ()+lexChar c = lexString [c] -lex_string :: ParserM m => String -> m ()-lex_string str = work- $ \s -> case my_lex s of+-- | 'lexString' fetches the next whitespace delimited token from+-- from the input and checks if it matches the 'String' input.+-- Uses 'Maybe' inside 'ParserM' to handle failed matches, so+-- that it always returns () on success.+lexString :: ParserM m => String -> m ()+lexString str = work+ $ \s -> case myLex s of Just (xs, ys) | xs == BC.pack str -> Just ((), ys) _ -> Nothing -lex_eof :: ParserM m => m ()-lex_eof = work+-- | 'lexEof' looks for optional spaces followed by the end of input.+-- Uses 'Maybe' inside 'ParserM' to handle failed matches, so+-- that it always returns () on success.+lexEof :: ParserM m => m ()+lexEof = work $ \s -> if B.null (dropSpace s) then Just ((), B.empty) else Nothing -lex_strings :: ParserM m => [String] -> m String-lex_strings str =+-- | Checks if any of the input 'String's match the next+-- space delimited token in the input stream.+-- Uses 'Maybe' inside 'ParserM' to handle failed matches,+-- on success it returns the matching 'String'.+lexStrings :: ParserM m => [String] -> m String+lexStrings str = work $ \s ->- case my_lex s of+ case myLex s of Just (xs, ys) | xs' `elem` str -> Just (xs', ys) where xs' = BC.unpack xs _ -> Nothing -my_lex :: B.ByteString -> Maybe (B.ByteString, B.ByteString)-my_lex s = let s' = dropSpace s+-- | 'myLex' drops leading spaces and then breaks the string at the+-- next space. Returns 'Nothing' when the string is empty after+-- dropping leading spaces, otherwise it returns the first sequence+-- of non-spaces and the remainder of the input.+myLex :: B.ByteString -> Maybe (B.ByteString, B.ByteString)+myLex s = let s' = dropSpace s in if B.null s' then Nothing else Just $ breakSpace s' -alter_input :: ParserM m+-- | Applies a function to the input stream and discards the+-- result of the function.+alterInput :: ParserM m => (B.ByteString -> B.ByteString) -> m ()-alter_input f = work (\s -> Just ((), f s))+alterInput f = work (\s -> Just ((), f s)) class Monad m => ParserM m where+ -- | Applies a parsing function inside the 'ParserM' monad. work :: (B.ByteString -> Maybe (a, B.ByteString)) -> m a- maybe_work :: (B.ByteString -> Maybe (a, B.ByteString)) -> m (Maybe a)- peek_input :: m B.ByteString+ -- | Applies a parsing function, that can return 'Nothing',+ -- inside the 'ParserM' monad.+ maybeWork :: (B.ByteString -> Maybe (a, B.ByteString)) -> m (Maybe a)+ -- | Allows for the inspection of the input that is yet to be parsed.+ peekInput :: m B.ByteString ----- Strict Monad ------parse_strictly :: SM a -> B.ByteString -> Maybe (a, B.ByteString)-parse_strictly (SM f) s = f s+-- | 'parseStrictly' applies the parser functions to a string+-- and checks that each parser produced a result as it goes.+-- The strictness is in the 'ParserM' instance for 'SM'.+parseStrictly :: SM a -> B.ByteString -> Maybe (a, B.ByteString)+parseStrictly (SM f) s = f s +-- | 'SM' is the Strict Monad for parsing. newtype SM a = SM (B.ByteString -> Maybe (a, B.ByteString)) instance Monad SM where SM m >>= k = SM $ \s -> case m s of@@ -63,29 +94,7 @@ instance ParserM SM where work f = SM f- maybe_work f = SM $ \s -> case f s of+ maybeWork f = SM $ \s -> case f s of Just (x, s') -> Just (Just x, s') Nothing -> Just (Nothing, s)- peek_input = SM $ \s -> Just (s, s)------- Lazy Monad ------parse_lazily :: LM a -> B.ByteString -> (a, B.ByteString)-parse_lazily (LM f) s = f s--newtype LM a = LM (B.ByteString -> (a, B.ByteString))-instance Monad LM where- LM m >>= k = LM $ \s -> let (x, s') = m s- LM y = k x- in y s'- return x = LM (\s -> (x,s))- fail s = error s--instance ParserM LM where- work f = LM $ \s -> case f s of- Nothing -> error "parser error"- Just x -> x- maybe_work f = LM $ \s -> case f s of- Nothing -> (Nothing, s)- Just (x, s') -> (Just x, s')- peek_input = LM $ \s -> (s, s)-+ peekInput = SM $ \s -> Just (s, s)
src/Darcs/Patch/Real.hs view
@@ -23,13 +23,13 @@ -- | Conflictor patches module Darcs.Patch.Real- ( RealPatch(..), prim2real, is_consistent, is_forward, is_duplicate,+ ( RealPatch(..), prim2real, isConsistent, isForward, isDuplicate, pullCommon, mergeUnravelled ) where import Control.Monad ( mplus, liftM ) import Data.List ( partition, nub ) import Darcs.Patch.Prim ( Prim, FromPrim(..), ToFromPrim(..), Conflict(..), Effect(..),- showPrim, FileNameFormat(NewFormat),+ showPrim, showPrimFL, FileNameFormat(NewFormat), IsConflictedPrim(..), ConflictState(..) ) import Darcs.Patch.Read ( readPrim ) import Darcs.Patch.Patchy@@ -37,16 +37,16 @@ --import Darcs.Patch.Read () --import Darcs.Patch.Viewing () --import Darcs.Patch.Apply ()-import Darcs.Patch.Commute ( mangle_unravelled )+import Darcs.Patch.Commute ( mangleUnravelled ) import Darcs.Patch.Non ( Non(..), Nonable(..), unNon, showNons, showNon, readNons, readNon, add, addP, addPs, remP, remPs, remNons, (*>), (>*), (*>>), (>>*) ) import Darcs.Patch.Permutations ( commuteWhatWeCanFL, commuteWhatWeCanRL, genCommuteWhatWeCanRL,- removeRL, removeFL, remove_subsequenceFL )+ removeRL, removeFL, removeSubsequenceFL ) import qualified Data.ByteString.Char8 as BC ( ByteString, unpack )-import Darcs.Patch.ReadMonads ( work, peek_input, my_lex )+import Darcs.Patch.ReadMonads ( work, peekInput, myLex ) import Darcs.Utils ( nubsort ) import Darcs.Witnesses.Sealed ( FlippedSeal(..), Sealed(Sealed), mapSeal ) import Darcs.Witnesses.Show@@ -85,19 +85,19 @@ Conflictor :: [Non RealPatch C(x)] -> FL Prim C(x y) -> Non RealPatch C(x) -> RealPatch C(y x) InvConflictor :: [Non RealPatch C(x)] -> FL Prim C(x y) -> Non RealPatch C(x) -> RealPatch C(x y) --- | 'is_duplicate' @p@ is ' @True@ if @p@ is either a 'Duplicate' or 'Etacilpud' patch-is_duplicate :: RealPatch C(s y) -> Bool-is_duplicate (Duplicate _) = True-is_duplicate (Etacilpud _) = True-is_duplicate _ = False+-- | 'isDuplicate' @p@ is ' @True@ if @p@ is either a 'Duplicate' or 'Etacilpud' patch+isDuplicate :: RealPatch C(s y) -> Bool+isDuplicate (Duplicate _) = True+isDuplicate (Etacilpud _) = True+isDuplicate _ = False -- | This is only used for unit testing-is_forward :: RealPatch C(s y) -> Maybe Doc-is_forward p@(InvConflictor _ _ _) =+isForward :: RealPatch C(s y) -> Maybe Doc+isForward p@(InvConflictor _ _ _) = Just $ redText "An inverse conflictor" $$ showPatch p-is_forward p@(Etacilpud _) =+isForward p@(Etacilpud _) = Just $ redText "An inverse duplicate" $$ showPatch p-is_forward _ = Nothing+isForward _ = Nothing mergeUnravelled :: [Sealed ((FL Prim) C(x))] -> Maybe (FlippedSeal RealPatch C(x)) mergeUnravelled [] = Nothing@@ -124,7 +124,7 @@ mergeConflictingNons ns = mcn $ map unNon ns where mcn :: [Sealed (FL RealPatch C(x))] -> Maybe (FL RealPatch C(x x)) mcn [] = Just NilFL- mcn [Sealed p] = case join_effects p of -- this is just a safety check, and could+ 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@@ -132,17 +132,17 @@ case merge (ps :\/: qs) of qs' :/\: _ -> mcn (Sealed (c +>+ ps +>+ qs'):zs) -join_effects :: Effect p => p C(x y) -> FL Prim C(x y)-join_effects = join_inverses . effect- where join_inverses :: FL Prim C(x y) -> FL Prim C(x y)- join_inverses NilFL = NilFL- join_inverses (p:>:ps) = case removeFL (invert p) ps' of+joinEffects :: Effect p => p C(x y) -> FL Prim C(x y)+joinEffects = joinInverses . effect+ where joinInverses :: FL Prim C(x y) -> FL Prim C(x y)+ joinInverses NilFL = NilFL+ joinInverses (p:>:ps) = case removeFL (invert p) ps' of Just ps'' -> ps'' Nothing -> p :>: ps'- where ps' = join_inverses ps+ where ps' = joinInverses ps assertConsistent :: RealPatch C(x y) -> RealPatch C(x y)-assertConsistent x = assertDoc (do e <- is_consistent x+assertConsistent x = assertDoc (do e <- isConsistent x Just (redText "Inconsistent patch:" $$ showPatch x $$ e)) x -- | @mergeAfterConflicting@ takes as input a sequence of conflicting@@ -168,7 +168,7 @@ mac (reverseFL xxx) yyy NilFL where mac :: RL RealPatch C(x y) -> FL Prim C(y z) -> FL RealPatch C(z a) -> Maybe (FL RealPatch C(x x), FL RealPatch C(x a))- mac NilRL xs goneby = case join_effects goneby of+ mac NilRL xs goneby = case joinEffects goneby of NilFL -> Just (NilFL, mapFL_FL Normal xs) _z -> --traceDoc (greenText "mac1 z" $$ showPatch _z) $ Nothing@@ -176,27 +176,27 @@ -- greenText "p" $$ showPatch p $$ -- greenText "xs" $$ showPatch xs $$ -- greenText "goneby" $$ showPatch goneby) $- case commuteFL (p :> mapFL_FL Normal xs) of- Left _ -> case genCommuteWhatWeCanRL commute_no_conflicts (ps :> p) of+ 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 = join_effects $ p':<:a+ 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 $ join_effects (p:<:ps)+ 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 commute_no_conflicts (ps :> p) of+ case genCommuteWhatWeCanRL commuteNoConflicts (ps :> p) of a:>p':>b -> do (b',xs') <- mac b xs goneby- let pa = join_effects $ p':<:a+ let pa = joinEffects $ p':<:a --traceDoc (greenText "foo2" $$ -- showPatch pa) $ Just () NilFL <- return pa@@ -235,53 +235,49 @@ allNormal _ = Nothing -- | This is used for unit-testing and for internal sanity checks-is_consistent :: RealPatch C(x y) -> Maybe Doc-is_consistent (Normal _) = Nothing-is_consistent (Duplicate _) = Nothing-is_consistent (Etacilpud _) = Nothing-is_consistent (Conflictor im mm m@(Non deps _))- | not $ everyone_conflicts im = Just $ redText "Someone doesn't conflict in im in is_consistent"- | Just _ <- remPs rmm m, _:>:_ <- mm = Just $ redText "m doesn't conflict with mm in is_consistent"- | any (\x -> any (x `conflicts_with`) nmm) im- = Just $ redText "mm conflicts with im in is_consistent where nmm is" $$+isConsistent :: RealPatch 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 all_conflicts_with m im of+ | otherwise = case allConflictsWith m im of (im1,[]) | im1 `eqSet` im -> Nothing- (_,imnc) -> Just $ redText "m doesn't conflict with im in is_consistent. unconflicting:"+ (_,imnc) -> Just $ redText "m doesn't conflict with im in isConsistent. unconflicting:" $$ showNons imnc where (nmm, rmm) = geteff im mm-is_consistent c@(InvConflictor _ _ _) = is_consistent (invert c)+isConsistent c@(InvConflictor _ _ _) = isConsistent (invert c) -everyone_conflicts :: [Non RealPatch C(x)] -> Bool-everyone_conflicts [] = True-everyone_conflicts (x:xs) = case all_conflicts_with x xs of+everyoneConflicts :: [Non RealPatch C(x)] -> Bool+everyoneConflicts [] = True+everyoneConflicts (x:xs) = case allConflictsWith x xs of ([],_) -> False- (_,xs') -> everyone_conflicts xs'+ (_,xs') -> everyoneConflicts xs' prim2real :: Prim C(x y) -> RealPatch C(x y) prim2real = Normal instance Patchy RealPatch -instance MyEq p => Eq (Sealed (p C(x))) where- (Sealed x) == (Sealed y) | IsEq <- x =\/= y = True- | otherwise = False--merge_with :: Non RealPatch C(x) -> [Non RealPatch C(x)] -> Sealed (FL Prim C(x))-merge_with p [] = effect `mapSeal` unNon p-merge_with p xs = mergeall $ map unNon $ (p:) $ unconflicting_of $- filter (\x -> not (p `depends_upon` x) && not (p `conflicts_with` x)) xs+mergeWith :: Non RealPatch C(x) -> [Non RealPatch 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 :: [Sealed (FL RealPatch 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 all_conflicts_with q qs of+ unconflicting_of (q:qs) = case allConflictsWith q qs of ([],_) -> q:qs (_,nc) -> unconflicting_of nc @@ -291,8 +287,8 @@ conflictedEffect (Conflictor _ _ (Non _ x)) = [IsC Conflicted x] conflictedEffect (InvConflictor _ _ _) = impossible conflictedEffect (Normal x) = [IsC Okay x]- resolveConflicts (Conflictor ix xx x) = [mangle_unravelled unravelled : unravelled]- where unravelled = nub $ filter isn $ map (`merge_with` (x:ix++nonxx)) (x:ix++nonxx)+ 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 C(x)] -> FL Prim C(x y) -> RL RealPatch C(x y) nonxx_aux a b = reverseFL $ xx2patches a b@@ -306,65 +302,66 @@ resolveConflicts _ = [] -- cA- commute_no_conflicts (Duplicate x :> Duplicate y) = Just (Duplicate y :> Duplicate x)- commute_no_conflicts (Etacilpud x :> Duplicate y) = Just (Duplicate y :> Etacilpud x)- commute_no_conflicts (Duplicate x :> Etacilpud y) = Just (Etacilpud y :> Duplicate x)- commute_no_conflicts (Etacilpud x :> Etacilpud y) = Just (Etacilpud y :> Etacilpud x)+ 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- commute_no_conflicts (x :> Duplicate d) = if d == addP (invert x) (non x)+ 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)- commute_no_conflicts (Duplicate d' :> x) = Just (x :> Duplicate (addP (invert x) d'))- commute_no_conflicts c@(Etacilpud _ :> _) = invertCommuteNC c- commute_no_conflicts c@(_ :> Etacilpud _) = invertCommuteNC c+ commuteNoConflicts (Duplicate d' :> x) = Just (x :> Duplicate (addP (invert x) d'))+ commuteNoConflicts c@(Etacilpud _ :> _) = invertCommuteNC c+ commuteNoConflicts c@(_ :> Etacilpud _) = invertCommuteNC c -- cE- commute_no_conflicts (Normal x :> Normal y) = do y' :> x' <- commute (x :> y)+ commuteNoConflicts (Normal x :> Normal y) = do y' :> x' <- commute (x :> y) return (Normal y' :> Normal x') -- cF -- involves a conflict -- cG- commute_no_conflicts (Normal x :> Conflictor iy yy y) =- case commuteFL (x :> invert yy) of+ 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- commute_no_conflicts c@(InvConflictor _ _ _ :> Normal _) = invertCommuteNC c+ commuteNoConflicts c@(InvConflictor _ _ _ :> Normal _) = invertCommuteNC c -- icG FIXME: where is icF?- commute_no_conflicts (Conflictor iy' yy' y' :> Normal x') =+ 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- commute_no_conflicts c@(Normal _ :> InvConflictor _ _ _) = invertCommuteNC c+ commuteNoConflicts c@(Normal _ :> InvConflictor _ _ _) = invertCommuteNC c -- cH -- this involves a conflict commute -- cI- commute_no_conflicts (Conflictor ix xx x :> Conflictor iy yy y) =+ 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 (conflicts_with y) (x':ix')- False <- return $ any (conflicts_with x') 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- commute_no_conflicts c@(InvConflictor _ _ _ :> InvConflictor _ _ _) = invertCommuteNC c+ commuteNoConflicts c@(InvConflictor _ _ _ :> InvConflictor _ _ _) = invertCommuteNC c -- cJ- commute_no_conflicts (InvConflictor ix xx x :> Conflictor iy yy y) =+ 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 (conflicts_with y') (x':ix')- False <- return $ any (conflicts_with x') iy'+ False <- return $ any (conflictsWith y') (x':ix')+ False <- return $ any (conflictsWith x') iy' return (Conflictor iy' (invert iyy') y' :> InvConflictor ix' xx' x') -- icJ- commute_no_conflicts (Conflictor iy' yy' 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 (conflicts_with y') (x':ix')- False <- return $ any (conflicts_with x') iy'+ False <- return $ any (conflictsWith y') (x':ix')+ False <- return $ any (conflictsWith x') iy' return (InvConflictor ix xx x :> Conflictor iy (invert iyy) y)+ isInconsistent = isConsistent instance FromPrim RealPatch where fromPrim = prim2real@@ -468,14 +465,14 @@ instance Commute RealPatch where -- commute (x :> y) | traceDoc (greenText "commuting x" $$ showPatch x $$ -- greenText "with y" $$ showPatch y) False = undefined- commute (x :> y) | Just (y' :> x') <- commute_no_conflicts (assertConsistent x :> assertConsistent y) = Just (y' :> x')+ 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 <- join_effects c ->+ ([], 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@@ -483,7 +480,7 @@ -- cH commute (Conflictor a1 n1 p1 :> Conflictor a2 n2 p2) | Just a2_minus_p1 <- remove1 p1' a2,- not (p2 `depends_upon` p1') =+ 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@@ -501,7 +498,7 @@ -- showPatch (assertConsistent $ Conflictor a2 n2 p2) $$ -- greenText "where n2'nons is" $$ showNons n2'nons $$ -- greenText "and others are" $$- -- showNons (fst $ all_conflicts_with p2 $ a2_minus_p1++n2nons) $$+ -- 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 $$@@ -511,14 +508,14 @@ -- greenText "p2 fixed" $$ showNon p2ooo $$ -- -- greenText "pren1" $$ showPatch pren1 $$ -- greenText "n1'" $$ showPatch n1' $$- -- greenText "p2" $$ showNon p2 - -- ) - (fst $ all_conflicts_with p2 $ a2_minus_p1++n2nons) `minus` n2'nons+ -- 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 <- join_effects c ->+ ([], 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')@@ -610,42 +607,42 @@ hunkMatches f (InvConflictor x c p) = or [or $ map (nonHunkMatches f) x, hunkMatches f c, nonHunkMatches f p] {--all_conflicts_withFL :: FL Prim C(x y) -> [Non RealPatch C(x)]+allConflictsWithFL :: FL Prim C(x y) -> [Non RealPatch C(x)] -> ([Non RealPatch C(x)], [Non RealPatch C(x)])-all_conflicts_withFL xx ns = case partition f ns of+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 all_conflicts_with y zs of+ 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 commuteFL (Normal (invert p) :> xx') of+ Just (xx' :> _) -> case commuteFLorComplain (Normal (invert p) :> xx') of Nothing -> True Just _ -> False -}-all_conflicts_with :: Non RealPatch C(x) -> [Non RealPatch C(x)]+allConflictsWith :: Non RealPatch C(x) -> [Non RealPatch C(x)] -> ([Non RealPatch C(x)], [Non RealPatch C(x)])-all_conflicts_with x ys = acw $ partition (conflicts_with x) ys+allConflictsWith x ys = acw $ partition (conflictsWith x) ys where acw ([],nc) = ([],nc)- acw (c:cs, nc) = case all_conflicts_with c nc of+ acw (c:cs, nc) = case allConflictsWith c nc of (c1,nc1) -> case acw (cs, nc1) of (xs',nc') -> (c:c1++xs',nc') -conflicts_with :: Non RealPatch C(x) -> Non RealPatch C(x) -> Bool-conflicts_with x y | x `depends_upon` y || y `depends_upon` x = False-conflicts_with x (Non cy y) =+conflictsWith :: Non RealPatch C(x) -> Non RealPatch 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 commuteFL (fromPrim (invert y) :> cx' +>+ fromPrim x' :>: NilFL) of+ Just (Non cx' x') -> case commuteFLorComplain (fromPrim (invert y) :> cx' +>+ fromPrim x' :>: NilFL) of Right _ -> False Left _ -> True Nothing -> True -depends_upon :: Non RealPatch C(x) -> Non RealPatch C(x) -> Bool-depends_upon (Non xs _) (Non ys y) =- case remove_subsequenceFL (ys +>+ fromPrim y :>: NilFL) xs of+dependsUpon :: Non RealPatch C(x) -> Non RealPatch C(x) -> Bool+dependsUpon (Non xs _) (Non ys y) =+ case removeSubsequenceFL (ys +>+ fromPrim y :>: NilFL) xs of Just _ -> True Nothing -> False @@ -663,7 +660,7 @@ return (invert iy' :> invert ix') invertCommuteNC :: (RealPatch :> RealPatch) C(x y) -> Maybe ((RealPatch :> RealPatch) C(x y))-invertCommuteNC (x :> y) = do ix' :> iy' <- commute_no_conflicts (invert y :> invert x)+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@@ -713,14 +710,14 @@ blueText "conflictor" <+> showNons i <+> blueText "[]" $$ showNon p showPatch (Conflictor i cs p) = blueText "conflictor" <+> showNons i <+> blueText "[" $$- showPatch cs $$+ 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 "[" $$- showPatch cs $$+ showPrimFL NewFormat cs $$ blueText "]" $$ showNon p showContextPatch (Normal p) = showContextPatch p@@ -728,29 +725,29 @@ instance ReadPatch RealPatch where readPatch' _ =- do s <- peek_input- case fmap (BC.unpack . fst) $ my_lex s of+ do s <- peekInput+ case fmap (BC.unpack . fst) $ myLex s of Just "duplicate" ->- do work my_lex+ do work myLex p <- readNon return $ (Sealed . Duplicate) `fmap` p Just "etacilpud" ->- do work my_lex+ do work myLex p <- readNon return $ (Sealed . Etacilpud) `fmap` p Just "conflictor" ->- do work my_lex- --let tracePeek x = do y <- peek_input+ do work myLex+ --let tracePeek x = do y <- peekInput -- traceDoc (greenText x $$ greenText (show $ BC.unpack y)) return () i <- readNons- Just (Sealed ps) <- bracketedFL (fromIntegral $ fromEnum '[') (fromIntegral $ fromEnum ']')+ Just (Sealed ps) <- bracketedFL (readPrim NewFormat) (fromIntegral $ fromEnum '[') (fromIntegral $ fromEnum ']') Just p <- readNon return $ Just $ Sealed $ Conflictor i (unsafeCoerceP ps) p Just "rotcilfnoc" ->- do work my_lex+ do work myLex i <- readNons- Just (Sealed ps) <- bracketedFL (fromIntegral $ fromEnum '[') (fromIntegral $ fromEnum ']')+ Just (Sealed ps) <- bracketedFL (readPrim NewFormat) (fromIntegral $ fromEnum '[') (fromIntegral $ fromEnum ']') Just p <- readNon return $ Just $ Sealed $ InvConflictor i ps p _ -> do mp <- readPrim NewFormat@@ -762,7 +759,7 @@ show p = renderString $ showPatch p instance Show2 RealPatch where- show2 = show+ showDict2 = ShowDictClass instance Nonable RealPatch where non (Duplicate d) = d
src/Darcs/Patch/RegChars.hs view
@@ -33,7 +33,7 @@ -- it can also be specified as a complement set by prefixing with '^' -- (caret). The dash and caret, as well as the backslash, can all be -- escaped with a backslash to suppress their special meaning.--- +-- -- NOTE: The '.' (dot) is allowed to be escaped. It has no special meaning -- if it is not escaped, but the default 'filename_toks' in -- Darcs.Commands.Replace uses an escaped dot (WHY?).
src/Darcs/Patch/Set.hs view
@@ -16,35 +16,47 @@ -- Boston, MA 02110-1301, USA. {-# OPTIONS_GHC -cpp #-}-{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP, EmptyDataDecls #-} #include "gadts.h" -module Darcs.Patch.Set ( PatchSet, SealedPatchSet ) where+module Darcs.Patch.Set ( PatchSet(..), Tagged(..), SealedPatchSet, Origin,+ progressPatchSet, tags,+ newset2RL, newset2FL ) where -import Darcs.Hopefully ( PatchInfoAnd )-import Darcs.Witnesses.Ordered ( RL )+import Progress ( progress )+import Darcs.Patch.Info ( PatchInfo )+import Darcs.Hopefully ( PatchInfoAnd, info )+import Darcs.Witnesses.Ordered ( FL, RL(..), (+<+),+ reverseRL, mapRL_RL, concatRL, mapRL ) import Darcs.Witnesses.Sealed ( Sealed ) --- | A PatchSet is in reverse order, plus has information about which--- tags are clean, meaning all patches applied prior to them are in--- the tag itself, so we can stop reading at that point. Just to--- clarify, the first patch in a PatchSet is the one most recently--- applied to the repo.------ 'PatchSet's have the property that if--- @--- (info $ last $ head a) == (info $ last $ head b)--- @--- then @(tail a)@ and @(tail b)@ are identical repositories------ Questions:------ Does this mean that in a patch set such as @[[a b t1 c d e t2][f g--- t3] [h i]]@, t1, t2 and t3 are tags, and t2 and t3 are clean?------ Can we have PatchSet with length at least 3?--- Florent-type PatchSet p C(x) = RL (RL (PatchInfoAnd p)) C(() x)+data Origin -type SealedPatchSet p = Sealed (RL (RL (PatchInfoAnd p)) C(()))+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)++data Tagged p C(x2 z) where+ Tagged :: PatchInfoAnd p C(y z) -> Maybe String+ -> RL (PatchInfoAnd p) C(x3 y) -> Tagged p C(x3 z)++newset2RL :: PatchSet p C(start x4) -> RL (PatchInfoAnd p) C(start x4)+newset2RL (PatchSet ps ts) = ps +<+ concatRL (mapRL_RL ts2rl ts)+ where ts2rl :: Tagged p C(x5 y) -> RL (PatchInfoAnd p) C(x5 y)+ ts2rl (Tagged t _ ps2) = t :<: ps2++newset2FL :: PatchSet p C(start x6) -> FL (PatchInfoAnd p) C(start x6)+newset2FL = reverseRL . newset2RL++progressPatchSet :: String -> PatchSet p C(start x7) -> PatchSet p C(start x7)+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 t h ps) = Tagged (prog t) h (mapRL_RL prog ps)++tags :: PatchSet p C(start x13) -> [PatchInfo]+tags (PatchSet _ ts) = mapRL f ts+ where f :: Tagged p C(x14 y) -> PatchInfo+ f (Tagged t _ _) = info t
src/Darcs/Patch/Show.lhs view
@@ -32,6 +32,7 @@ import Darcs.Patch.Prim ( showPrim, FileNameFormat(..) ) import Darcs.Patch.Info ( PatchInfo, showPatchInfo ) import Darcs.Witnesses.Ordered ( FL(NilFL), mapFL )+import Darcs.Witnesses.Show ( Show2(..), ShowDict(..) ) #include "gadts.h" \end{code} @@ -44,6 +45,8 @@ \begin{code} instance Show (Patch C(x y)) where show p = renderString (showPatch_ p) ++ "\n"+instance Show2 Patch where+ showDict2 = ShowDictClass showPatch_ :: Patch C(a b) -> Doc showPatch_ (PP p) = showPrim OldFormat p
src/Darcs/Patch/Split.hs view
@@ -31,7 +31,7 @@ import Darcs.Patch.Patchy ( ReadPatch(..), ShowPatch(..), Invert(..) ) import Darcs.Patch.Prim ( Prim(..), FilePatchType(..), canonize, canonizeFL )-import Darcs.Patch.ReadMonads ( parse_strictly )+import Darcs.Patch.ReadMonads ( parseStrictly ) import Darcs.Patch.Read () import Darcs.Patch.Viewing () @@ -94,7 +94,7 @@ rawSplitter = Splitter { applySplitter = \p -> Just (renderPS . showPatch $ p,- \str -> case parse_strictly (readPatch' False) str of+ \str -> case parseStrictly (readPatch' False) str of Just (Just (Sealed res), _) -> Just (withEditedHead p res) _ -> Nothing )
src/Darcs/Patch/TouchesFiles.hs view
@@ -20,9 +20,9 @@ #include "gadts.h" -module Darcs.Patch.TouchesFiles ( look_touch, choose_touching,- select_touching,- deselect_not_touching, select_not_touching,+module Darcs.Patch.TouchesFiles ( lookTouch, chooseTouching, choosePreTouching,+ selectTouching,+ deselectNotTouching, selectNotTouching, ) where import Data.List ( sort, isSuffixOf ) @@ -30,38 +30,38 @@ patchChoices, tag, getChoices, forceFirsts, forceLasts, tpPatch, )-import Darcs.Patch ( Patchy, applyToFilepaths, listTouchedFiles )+import Darcs.Patch ( Patchy, applyToFilepaths, listTouchedFiles, invert ) import Darcs.Witnesses.Ordered ( FL(..), (:>)(..), mapFL_FL, (+>+) ) import Darcs.Witnesses.Sealed ( Sealed, seal ) -select_touching :: Patchy p => [FilePath] -> PatchChoices p C(x y) -> PatchChoices p C(x y)-select_touching [] pc = pc-select_touching files pc = forceFirsts xs pc+selectTouching :: Patchy p => [FilePath] -> PatchChoices p C(x y) -> PatchChoices p C(x y)+selectTouching [] pc = pc+selectTouching files pc = forceFirsts xs pc where ct :: Patchy p => [FilePath] -> FL (TaggedPatch p) C(x y) -> [Tag] ct _ NilFL = []- ct fs (tp:>:tps) = case look_touch fs (tpPatch tp) of+ ct fs (tp:>:tps) = case lookTouch fs (tpPatch tp) of (True, fs') -> tag tp:ct fs' tps (False, fs') -> ct fs' tps xs = case getChoices pc of _ :> mc :> lc -> ct (map fix files) (mc +>+ lc) -deselect_not_touching :: Patchy p => [FilePath] -> PatchChoices p C(x y) -> PatchChoices p C(x y)-deselect_not_touching [] pc = pc-deselect_not_touching files pc = forceLasts xs pc+deselectNotTouching :: Patchy p => [FilePath] -> PatchChoices p C(x y) -> PatchChoices p C(x y)+deselectNotTouching [] pc = pc+deselectNotTouching files pc = forceLasts xs pc where ct :: Patchy p => [FilePath] -> FL (TaggedPatch p) C(x y) -> [Tag] ct _ NilFL = []- ct fs (tp:>:tps) = case look_touch fs (tpPatch tp) of+ ct fs (tp:>:tps) = case lookTouch fs (tpPatch tp) of (True, fs') -> ct fs' tps (False, fs') -> tag tp:ct fs' tps xs = case getChoices pc of fc :> mc :> _ -> ct (map fix files) (fc +>+ mc) -select_not_touching :: Patchy p => [FilePath] -> PatchChoices p C(x y) -> PatchChoices p C(x y)-select_not_touching [] pc = pc-select_not_touching files pc = forceFirsts xs pc+selectNotTouching :: Patchy p => [FilePath] -> PatchChoices p C(x y) -> PatchChoices p C(x y)+selectNotTouching [] pc = pc+selectNotTouching files pc = forceFirsts xs pc where ct :: Patchy p => [FilePath] -> FL (TaggedPatch p) C(x y) -> [Tag] ct _ NilFL = []- ct fs (tp:>:tps) = case look_touch fs (tpPatch tp) of+ ct fs (tp:>:tps) = case lookTouch fs (tpPatch tp) of (True, fs') -> ct fs' tps (False, fs') -> tag tp:ct fs' tps xs = case getChoices pc of@@ -73,13 +73,21 @@ fix "." = "." fix f = "./" ++ f -choose_touching :: Patchy p => [FilePath] -> FL p C(x y) -> Sealed (FL p C(x))-choose_touching [] p = seal p-choose_touching files p = case getChoices $ select_touching files $ patchChoices p of+chooseTouching :: Patchy p => [FilePath] -> FL p C(x y) -> Sealed (FL p C(x))+chooseTouching [] p = seal p+chooseTouching files p = case getChoices $ selectTouching files $ patchChoices p of fc :> _ :> _ -> seal $ mapFL_FL tpPatch fc -look_touch :: Patchy p => [FilePath] -> p C(x y) -> (Bool, [FilePath])-look_touch fs p = (any (\tf -> any (affects tf) fs) (listTouchedFiles p)+choosePreTouching :: (Patchy p) => [FilePath] -> FL p C(x y) -> Sealed (FL p C(x))+choosePreTouching files patch = do+ let pre_files = applyToFilepaths (invert patch) files+ relevant = case files of+ [] -> seal patch+ _ -> chooseTouching pre_files patch+ in relevant++lookTouch :: Patchy p => [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
src/Darcs/Patch/Viewing.hs view
@@ -44,7 +44,7 @@ DirPatchType(..), FilePatchType(..) ) import Darcs.Patch.Patchy ( Patchy, Apply, ShowPatch(..), identity ) import Darcs.Patch.Show ( showPatch_, showNamedPrefix )-import Darcs.Patch.Info ( showPatchInfo, human_friendly )+import Darcs.Patch.Info ( showPatchInfo, humanFriendly ) import Darcs.Patch.Apply ( applyToTree ) #include "impossible.h" #include "gadts.h"@@ -267,7 +267,7 @@ showPatch (NamedP n d p) = showNamedPrefix n d <+> showPatch p showContextPatch (NamedP n [] p) = showContextPatch p >>= return . (showPatchInfo n <>) showContextPatch (NamedP n d p) = showContextPatch p >>= return . (showNamedPrefix n d <+>)- description (NamedP n _ _) = human_friendly n+ description (NamedP n _ _) = humanFriendly n summary p = description p $$ text "" $$ prefix " " (plainSummary p) -- this isn't summary because summary does the -- wrong thing with (Named (FL p)) so that it can
src/Darcs/Population.hs view
@@ -25,8 +25,8 @@ setPopState, DirMark(..), getRepoPop, getRepoPopVersion,- modified_to_xml,- lookup_pop, lookup_creation_pop,+ modifiedToXml,+ lookupPop, lookupCreationPop, ) where import qualified Data.ByteString.Char8 as BC ( unpack, singleton, pack )@@ -34,14 +34,17 @@ import Darcs.Utils ( withCurrentDirectory ) import Darcs.Hopefully ( PatchInfoAnd, hopefully, info )-import Darcs.Patch.FileName ( fn2fp, fp2fn, fn2ps, norm_path )+import Darcs.Patch.FileName ( fn2fp, fp2fn, fn2ps, normPath ) import Darcs.Patch ( RepoPatch, applyToPop, patchcontents, patchChanges, Effect, effect ) import Darcs.Witnesses.Ordered ( FL(..), RL(..), reverseRL, concatRL, mapRL )-import Darcs.Patch.Info ( PatchInfo, idpatchinfo, to_xml )-import Darcs.Patch.Set ( PatchSet )+import Darcs.Patch.Info ( PatchInfo, idpatchinfo, toXml )+import Darcs.Patch.Set ( PatchSet(..), newset2FL, newset2RL )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif import Darcs.Witnesses.Sealed ( Sealed(..), seal, unseal )-import Darcs.Repository ( withRepositoryDirectory, ($-), read_repo )+import Darcs.Repository ( withRepositoryDirectory, ($-), readRepo ) import Darcs.Repository.Pristine ( identifyPristine, getPristinePop ) import Darcs.PopulationData ( Population(..), PopTree(..), Info(..), DirMark(..), setPopState, getPopFrom )@@ -60,8 +63,8 @@ creationNameI = Just (BC.singleton '.')} -- | apply a patchset to a population-applyPatchSetPop :: RepoPatch p => PatchSet p C(x) -> Population -> Population-applyPatchSetPop ps pop = applyPatchesPop (reverseRL $ concatRL ps) pop+applyPatchSetPop :: RepoPatch p => PatchSet p C(Origin x) -> Population -> Population+applyPatchSetPop ps pop = applyPatchesPop (newset2FL ps) pop -- | apply Patches to a population applyPatchesPop :: Effect p => FL (PatchInfoAnd p) C(x y) -> Population -> Population@@ -72,7 +75,7 @@ getRepoPop :: FilePath -> IO Population getRepoPop repobasedir = withRepositoryDirectory [] repobasedir $- \repository -> do- pinfo <- (head . mapRL info . concatRL) `liftM` read_repo repository+ pinfo <- (head . mapRL info . newset2RL) `liftM` readRepo repository -- pinfo is the latest patchinfo mp <- withCurrentDirectory repobasedir $ identifyPristine >>= getPristinePop pinfo@@ -82,9 +85,9 @@ getRepoPopVersion :: FilePath -> PatchInfo -> IO Population getRepoPopVersion repobasedir pinfo = withRepositoryDirectory [] repobasedir $- \repository ->- do pips <- concatRL `liftM` read_repo repository+ do pips <- newset2RL `liftM` readRepo repository return $ (unseal applyPatchSetPop) (mkPatchSet $ dropWhileRL ((/=pinfo).info) pips) initPop- where mkPatchSet (Sealed xs) = seal $ xs :<: NilRL+ where mkPatchSet (Sealed xs) = seal $ PatchSet xs NilRL dropWhileRL :: (FORALL(x y) a C(x y) -> Bool) -> RL a C(r v) -> Sealed (RL a C(r)) dropWhileRL _ NilRL = seal NilRL dropWhileRL p xs@(x:<:xs')@@ -93,23 +96,23 @@ -- Routines for pulling data conveniently out of a Population -lookup_pop :: FilePath -> Population -> Maybe Population-lookup_pop f p = lookup_pop' (BC.unpack $ fn2ps $ fp2fn f) p+lookupPop :: FilePath -> Population -> Maybe Population+lookupPop f p = lookupPop' (BC.unpack $ fn2ps $ fp2fn f) p -lookup_pop' :: String -> Population -> Maybe Population-lookup_pop' f p@(Pop _ (PopFile i))+lookupPop' :: String -> Population -> Maybe Population+lookupPop' f p@(Pop _ (PopFile i)) | BC.unpack (nameI i) == f = Just p | otherwise = Nothing-lookup_pop' d p@(Pop pinfo (PopDir i c))+lookupPop' d p@(Pop pinfo (PopDir i c)) | BC.unpack (nameI i) == "." =- case catMaybes $ map (lookup_pop' (dropDS d).(Pop pinfo)) c of+ case catMaybes $ map (lookupPop' (dropDS d).(Pop pinfo)) c of [apop] -> Just apop [] -> Nothing _ -> impossible | BC.unpack (nameI i) == takeWhile (/='/') d = case dropWhile (=='/') $ dropWhile (/='/') d of "" -> Just p- d' -> case catMaybes $ map (lookup_pop' d'.(Pop pinfo)) c of+ d' -> case catMaybes $ map (lookupPop' d'.(Pop pinfo)) c of [apop] -> Just apop [] -> Nothing _ -> impossible@@ -117,11 +120,11 @@ where dropDS ('.':'/':f) = dropDS f dropDS f = f -lookup_creation_pop :: PatchInfo -> FilePath -> Population -> Maybe Population-lookup_creation_pop pinfo f p = lookup_creation_pop' pinfo (BC.unpack $ fn2ps $ fp2fn f) p+lookupCreationPop :: PatchInfo -> FilePath -> Population -> Maybe Population+lookupCreationPop pinfo f p = lookupCreationPop' pinfo (BC.unpack $ fn2ps $ fp2fn f) p -lookup_creation_pop' :: PatchInfo -> String -> Population -> Maybe Population-lookup_creation_pop' b a (Pop pinfo pp) = (Pop pinfo) `fmap` lcp pp+lookupCreationPop' :: PatchInfo -> String -> Population -> Maybe Population+lookupCreationPop' b a (Pop pinfo pp) = (Pop pinfo) `fmap` lcp pp where lcp p@(PopFile i) | fixname `fmap` creationNameI i == f && createdByI i == who = Just p | otherwise = Nothing@@ -130,15 +133,15 @@ | otherwise = case catMaybes $ map lcp c of [apop] -> Just apop _ -> Nothing- fixname = BC.pack . fn2fp . norm_path . fp2fn . BC.unpack- f = Just $ BC.pack $ fn2fp $ norm_path $ fp2fn a+ fixname = BC.pack . fn2fp . normPath . fp2fn . BC.unpack+ f = Just $ BC.pack $ fn2fp $ normPath $ fp2fn a who = Just b -modified_to_xml :: Info -> Doc-modified_to_xml i | modifiedHowI i == DullDir = empty+modifiedToXml :: Info -> Doc+modifiedToXml i | modifiedHowI i == DullDir = empty | modifiedHowI i == DullFile = empty-modified_to_xml i = text "<modified>"+modifiedToXml i = text "<modified>" $$ text "<modified_how>" <> text (show (modifiedHowI i)) <> text "</modified_how>"- $$ to_xml (modifiedByI i)+ $$ toXml (modifiedByI i) $$ text "</modified>"
src/Darcs/PopulationData.hs view
@@ -103,7 +103,7 @@ then do fnames <- getDirectoryContents dirname sl <- withCurrentDirectory dirname- (mapM getPopFrom_helper $ filter not_hidden fnames)+ (mapM getPopFrom_helper $ filter notHidden fnames) let i = Info {nameI = n, modifiedByI = pinfo, modifiedHowI = DullDir,@@ -117,7 +117,7 @@ creationNameI = Just n} return $ PopFile i -not_hidden :: FilePath -> Bool-not_hidden ('.':_) = False-not_hidden ('_':_) = False-not_hidden _ = True+notHidden :: FilePath -> Bool+notHidden ('.':_) = False+notHidden ('_':_) = False+notHidden _ = True
src/Darcs/ProgressPatches.hs view
@@ -9,7 +9,7 @@ import Progress (minlist, beginTedious, endTedious, progress, progressKeepLatest, tediousSize, finishedOne)-import Darcs.Patch.Info (just_name, is_tag)+import Darcs.Patch.Info (justName, isTag) -- | Evaluate an 'FL' list and report progress.@@ -58,8 +58,8 @@ pl (y:<:NilRL) = unsafePerformIO $ do endTedious k return (y:<:NilRL) pl (y:<:ys) =- if is_tag iy - then finishedOne k ("back to "++ just_name iy) y :<: pl ys+ if isTag iy+ then finishedOne k ("back to "++ justName iy) y :<: pl ys else progressKeepLatest k y :<: pl ys where iy = info y
src/Darcs/RemoteApply.hs view
@@ -1,58 +1,58 @@ -- | This module is used by the push and put commands to apply the a bundle to a -- remote repository. By remote I do not necessarily mean a repository on another -- machine, it is just not the repository we're located in.-module Darcs.RemoteApply ( remote_apply, apply_as ) where+module Darcs.RemoteApply ( remoteApply, applyAs ) where import System.Exit ( ExitCode ) import Darcs.Flags ( DarcsFlag( ApplyAs, Debug ) ) import Darcs.Utils ( breakCommand )-import Darcs.URL ( is_url, is_ssh )+import Darcs.URL ( isUrl, isSsh ) import Darcs.External import Printer -remote_apply :: [DarcsFlag] -> String -> Doc -> IO ExitCode-remote_apply opts repodir bundle - = case apply_as opts of- Nothing -> if is_ssh repodir- then apply_via_ssh opts repodir bundle- else if is_url repodir- then apply_via_url opts repodir bundle- else apply_via_local opts repodir bundle- Just un -> if is_ssh repodir- then apply_via_ssh_and_sudo opts repodir un bundle- else apply_via_sudo un repodir bundle+remoteApply :: [DarcsFlag] -> String -> Doc -> IO ExitCode+remoteApply opts repodir bundle+ = case applyAs opts of+ Nothing -> if isSsh repodir+ then applyViaSsh opts repodir bundle+ else if isUrl repodir+ then applyViaUrl opts repodir bundle+ else applyViaLocal opts repodir bundle+ Just un -> if isSsh repodir+ then applyViaSshAndSudo opts repodir un bundle+ else applyViaSudo un repodir bundle -apply_as :: [DarcsFlag] -> Maybe String-apply_as (ApplyAs user:_) = Just user-apply_as (_:fs) = apply_as fs-apply_as [] = Nothing-apply_via_sudo :: String -> String -> Doc -> IO ExitCode-apply_via_sudo user repo bundle =- darcs_program >>= \darcs ->+applyAs :: [DarcsFlag] -> Maybe String+applyAs (ApplyAs user:_) = Just user+applyAs (_:fs) = applyAs fs+applyAs [] = Nothing+applyViaSudo :: String -> String -> Doc -> IO ExitCode+applyViaSudo user repo bundle =+ darcsProgram >>= \darcs -> pipeDoc "sudo" ["-u",user,darcs,"apply","--all","--repodir",repo] bundle-apply_via_local :: [DarcsFlag] -> String -> Doc -> IO ExitCode-apply_via_local opts repo bundle =- darcs_program >>= \darcs ->+applyViaLocal :: [DarcsFlag] -> String -> Doc -> IO ExitCode+applyViaLocal opts repo bundle =+ darcsProgram >>= \darcs -> pipeDoc darcs ("apply":"--all":"--repodir":repo:applyopts opts) bundle -apply_via_url :: [DarcsFlag] -> String -> Doc -> IO ExitCode-apply_via_url opts repo bundle =+applyViaUrl :: [DarcsFlag] -> String -> Doc -> IO ExitCode+applyViaUrl opts repo bundle = do maybeapply <- maybeURLCmd "APPLY" repo case maybeapply of- Nothing -> apply_via_local opts repo bundle+ Nothing -> applyViaLocal opts repo bundle Just apply -> do let (cmd, args) = breakCommand apply pipeDoc cmd (args ++ [repo]) bundle -apply_via_ssh :: [DarcsFlag] -> String -> Doc -> IO ExitCode-apply_via_ssh opts repo bundle =+applyViaSsh :: [DarcsFlag] -> String -> Doc -> IO ExitCode+applyViaSsh opts repo bundle = pipeDocSSH addr [remoteDarcsCmd opts++" apply --all "++unwords (applyopts opts)++ " --repodir '"++path++"'"] bundle where (addr,':':path) = break (==':') repo -apply_via_ssh_and_sudo :: [DarcsFlag] -> String -> String -> Doc -> IO ExitCode-apply_via_ssh_and_sudo opts repo username bundle =+applyViaSshAndSudo :: [DarcsFlag] -> String -> String -> Doc -> IO ExitCode+applyViaSshAndSudo opts repo username bundle = pipeDocSSH addr ["sudo -u "++username++" "++remoteDarcsCmd opts++ " apply --all --repodir '"++path++"'"] bundle where (addr,':':path) = break (==':') repo
src/Darcs/RepoPath.hs view
@@ -30,6 +30,7 @@ makeAbsoluteOrStd, ioAbsoluteOrStd, useAbsoluteOrStd,+ stdOut, -- * AbsoluteOrRemotePath AbsoluteOrRemotePath, ioAbsoluteOrRemote,@@ -49,7 +50,7 @@ import Data.List ( isPrefixOf, isSuffixOf ) import Control.Exception ( bracket ) -import Darcs.URL ( is_absolute, is_relative, is_ssh_nopath )+import Darcs.URL ( isAbsolute, isRelative, isSshNopath ) import qualified Workaround ( getCurrentDirectory ) import qualified System.Directory ( setCurrentDirectory ) import System.Directory ( doesDirectoryExist )@@ -119,7 +120,7 @@ simpleSubPath :: FilePath -> Maybe SubPath simpleSubPath x | null x = bug "simpleSubPath called with empty path"- | is_relative x = Just $ SubPath $ FilePath.normalise $ pathToPosix x+ | isRelative x = Just $ SubPath $ FilePath.normalise $ pathToPosix x | otherwise = Nothing -- | Interpret a possibly relative path wrt the current working directory.@@ -151,8 +152,8 @@ -- Regarding the last point, someone more familiar with how these functions -- are used should verify that this is indeed necessary or at least useful. makeAbsolute :: AbsolutePath -> FilePath -> AbsolutePath-makeAbsolute a dir = if not (null dir) && is_absolute dir- then AbsolutePath (norm_slashes dir')+makeAbsolute a dir = if not (null dir) && isAbsolute dir+ then AbsolutePath (normSlashes dir') else ma a dir' where dir' = FilePath.normalise $ pathToPosix dir@@ -172,7 +173,7 @@ -- | Convert to posix, remove trailing slashes, and (under Posix) -- reduce multiple leading slashes to one. simpleClean :: String -> String-simpleClean = norm_slashes . reverse . dropWhile (=='/') . reverse . pathToPosix+simpleClean = normSlashes . reverse . dropWhile (=='/') . reverse . pathToPosix -- | The root directory as an absolute path. rootDirectory :: AbsolutePath@@ -182,13 +183,16 @@ makeAbsoluteOrStd _ "-" = APStd makeAbsoluteOrStd a p = AP $ makeAbsolute a p +stdOut :: AbsolutePathOrStd+stdOut = APStd+ ioAbsoluteOrStd :: String -> IO AbsolutePathOrStd ioAbsoluteOrStd "-" = return APStd ioAbsoluteOrStd p = AP `fmap` ioAbsolute p -- | Execute either the first or the second argument action, depending on -- whether the given path is an 'AbsolutePath' or stdin/stdout.-useAbsoluteOrStd :: (AbsolutePath -> IO a) -> IO a -> AbsolutePathOrStd -> IO a+useAbsoluteOrStd :: (AbsolutePath -> a) -> a -> AbsolutePathOrStd -> a useAbsoluteOrStd _ f APStd = f useAbsoluteOrStd f _ (AP x) = f x @@ -197,7 +201,7 @@ isdir <- doesDirectoryExist p if not isdir then return $ RmtP $- case () of _ | is_ssh_nopath p -> p++"."+ case () of _ | isSshNopath p -> p++"." | "/" `isSuffixOf` p -> init p | otherwise -> p else AbsP `fmap` ioAbsolute p@@ -233,12 +237,12 @@ convert c = c -- | Reduce multiple leading slashes to one. This only affects Posix systems.-norm_slashes :: FilePath -> FilePath+normSlashes :: FilePath -> FilePath #ifndef WIN32 -- multiple slashes in front are ignored under Posix-norm_slashes ('/':p) = '/' : dropWhile (== '/') p+normSlashes ('/':p) = '/' : dropWhile (== '/') p #endif-norm_slashes p = p+normSlashes p = p getCurrentDirectory :: IO AbsolutePath getCurrentDirectory = AbsolutePath `fmap` Workaround.getCurrentDirectory
src/Darcs/Repository.hs view
@@ -22,42 +22,43 @@ #include "gadts.h" module Darcs.Repository- ( Repository, ($-), maybeIdentifyRepository, identifyRepositoryFor+ ( Repository, HashedDir(..), Cache(..), CacheLoc(..), WritableOrNot(..), ($-), maybeIdentifyRepository, identifyRepositoryFor , withRepoLock, withRepoReadLock, withRepository, withRepositoryDirectory , withGutsOf, makePatchLazy, writePatchSet, findRepository, amInRepository- , amNotInRepository, slurp_pending, replacePristine, slurp_recorded- , slurp_recorded_and_unrecorded, withRecorded, read_repo, prefsUrl- , add_to_pending, tentativelyAddPatch, tentativelyRemovePatches- , tentativelyAddToPending, tentativelyReplacePatches+ , amNotInRepository, replacePristine+ , withRecorded, readRepo, prefsUrl+ , addToPending, tentativelyAddPatch, tentativelyRemovePatches+ , tentativelyAddToPending, tentativelyReplacePatches, readTentativeRepo , tentativelyMergePatches, considerMergeToWorking, revertRepositoryChanges , finalizeRepositoryChanges, createRepository, copyRepository- , copy_oldrepo_patches, patchSetToRepository, unrevertUrl, applyToWorking+ , copyOldrepoPatches, patchSetToRepository, unrevertUrl, applyToWorking , patchSetToPatches, createPristineDirectoryTree , createPartialsPristineDirectoryTree, optimizeInventory, cleanRepository , getMarkedupFile, PatchSet, SealedPatchSet, PatchInfoAnd , setScriptsExecutable, checkUnrelatedRepos, testTentative, testRecorded+ , extractOptions, modifyCache -- * Recorded and unrecorded and pending.- , readRecorded, readUnrecorded, unrecordedChanges, readPending, pendingChanges+ , readRecorded, readUnrecorded, unrecordedChanges, readPending , readRecordedAndPending -- * Index. , readIndex, invalidateIndex ) where import System.Exit ( ExitCode(..), exitWith )+import Data.List ( isSuffixOf )+import Data.Maybe( catMaybes ) import Darcs.Repository.State( readRecorded, readUnrecorded, readWorking, unrecordedChanges- , readPending, pendingChanges, readIndex, invalidateIndex+ , readPending, readIndex, invalidateIndex , readRecordedAndPending ) import Darcs.Repository.Internal (Repository(..), RepoType(..), ($-),- maybeIdentifyRepository, identifyRepositoryFor,+ maybeIdentifyRepository, identifyRepositoryFor, IdentifyRepo(..), findRepository, amInRepository, amNotInRepository, makePatchLazy,- slurp_pending,- slurp_recorded, slurp_recorded_and_unrecorded, withRecorded,- read_repo,+ readRepo, readTentativeRepo, prefsUrl, withRepoLock, withRepoReadLock, withRepository, withRepositoryDirectory, withGutsOf, tentativelyAddPatch, tentativelyRemovePatches, tentativelyAddToPending,@@ -70,46 +71,54 @@ getMarkedupFile, setScriptsExecutable, testTentative, testRecorded,- make_new_pending+ makeNewPending ) import Darcs.Repository.Merge( tentativelyMergePatches, considerMergeToWorking )-import Darcs.Repository.Cache ( unionCaches, fetchFileUsingCache, HashedDir(..) )-import Darcs.Patch.Set ( PatchSet, SealedPatchSet )+import Darcs.Repository.Cache ( unionRemoteCaches, fetchFileUsingCache,+ speculateFileUsingCache, HashedDir(..), Cache(..), CacheLoc(..), WritableOrNot(..))+import Darcs.Patch.Set ( PatchSet(..), SealedPatchSet, newset2RL, newset2FL, progressPatchSet )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif+import URL ( maxPipelineLength ) +import Control.Applicative ( (<$>) ) import Control.Monad ( unless, when )-import Data.Either(Either(..))-import System.Directory ( createDirectory, renameDirectory )+import System.Directory ( createDirectory, renameDirectory,+ createDirectoryIfMissing, renameFile ) import System.IO.Error ( isAlreadyExistsError ) import qualified Darcs.Repository.DarcsRepo as DarcsRepo import qualified Darcs.Repository.HashedRepo as HashedRepo import Darcs.Hopefully ( PatchInfoAnd, info, extractHash )-import Darcs.Repository.Checkpoint ( identify_checkpoint, write_checkpoint_patch, get_checkpoint )-import Darcs.Repository.ApplyPatches ( apply_patches )-import Darcs.Repository.HashedRepo ( apply_to_tentative_pristine, pris2inv )-import Darcs.Repository.InternalTypes ( Pristine(..) )+import Darcs.Repository.Checkpoint ( identifyCheckpoint, writeCheckpointPatch, getCheckpoint )+import Darcs.Repository.ApplyPatches ( applyPatches )+import Darcs.Repository.HashedRepo ( applyToTentativePristine, pris2inv, revertTentativeChanges,+ copySources )+import Darcs.Repository.InternalTypes ( Pristine(..), extractOptions, modifyCache ) import Darcs.Patch ( RepoPatch, Named, Prim, Patch, patch2patchinfo, apply )-import Darcs.Witnesses.Ordered ( FL(..), RL(..), bunchFL, mapFL, mapRL, mapRL_RL, concatFL- , reverseRL ,concatRL, lengthRL, isShorterThanRL, (+>+) )+import Darcs.Witnesses.Ordered ( FL(..), RL(..), bunchFL, mapFL, mapRL+ , reverseRL ,lengthRL, (+>+), (:\/:)(..) ) import Darcs.Patch.Info ( PatchInfo )-import Darcs.Repository.Format ( RepoProperty ( HashedInventory ),+import Darcs.Repository.Format ( RepoProperty ( HashedInventory ), RepoFormat, createRepoFormat, formatHas, writeRepoFormat ) import Darcs.Repository.Prefs ( writeDefaultPrefs ) import Darcs.Repository.Pristine ( createPristine, flagsToPristine, createPristineFromWorking )-import Darcs.Patch.Depends ( get_patches_beyond_tag )+import Darcs.Patch.Depends ( getPatchesBeyondTag, areUnrelatedRepos, findUncommon )+ import Darcs.Utils ( withCurrentDirectory, catchall, promptYorn, prettyError )-import Darcs.External ( copyFileOrUrl, Cachable(..) )-import Progress ( debugMessage, tediousSize,- beginTedious, endTedious, progress )+import Darcs.External ( copyFileOrUrl, Cachable(..), fetchFileLazyPS )+import Progress ( debugMessage, tediousSize, beginTedious, endTedious ) import Darcs.ProgressPatches (progressRLShowTags, progressFL)-import Darcs.Lock ( writeBinFile, writeDocBinFile, rm_recursive )+import Darcs.Lock ( writeBinFile, writeDocBinFile, rmRecursive, withTemp ) import Darcs.Witnesses.Sealed ( Sealed(..), FlippedSeal(..), flipSeal, mapFlipped ) import Darcs.Flags ( DarcsFlag( Quiet, Partial, Lazy, Ephemeral, Complete, AllowUnrelatedRepos, NoUpdateWorking ) , compression ) import Darcs.Global ( darcsdir )+import Darcs.URL ( isFile ) import Storage.Hashed.Tree( Tree, emptyTree ) import Storage.Hashed.Hash( encodeBase16 )@@ -118,8 +127,10 @@ import ByteStringUtils( gzReadFilePS ) import System.FilePath( (</>) )-+import qualified Codec.Archive.Tar as Tar+import Codec.Compression.GZip ( compress, decompress ) import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Lazy as BL #include "impossible.h" @@ -137,7 +148,7 @@ writeRepoFormat rf (darcsdir++"/format") if formatHas HashedInventory rf then writeBinFile (darcsdir++"/hashed_inventory") ""- else DarcsRepo.write_inventory "." ((NilRL:<:NilRL) :: PatchSet Patch C(())) -- YUCK!+ else DarcsRepo.writeInventory "." (PatchSet NilRL NilRL :: PatchSet Patch C(Origin Origin)) -- YUCK! copyRepository :: RepoPatch p => Repository p C(r u t) -> IO () copyRepository fromrepository@(Repo _ opts rf _)@@ -151,44 +162,48 @@ data RepoSort = Hashed | Old +repoSort :: RepoFormat -> RepoSort+repoSort f+ | formatHas HashedInventory f = Hashed+ | otherwise = Old+ copyInventory :: forall p C(r u t). RepoPatch p => Repository p C(r u t) -> IO ()-copyInventory fromrepo@(Repo fromdir opts rf (DarcsRepository _ cremote)) = do- repo@(Repo todir xx rf2 (DarcsRepository yy c)) <- identifyRepositoryFor fromrepo "."- let newrepo :: Repository p C(r u t)- newrepo = Repo todir xx rf2 (DarcsRepository yy (c `unionCaches` cremote))- copyHashedHashed = HashedRepo.copy_repo newrepo opts fromdir- copyAnythingToOld r = withCurrentDirectory todir $ read_repo r >>=- DarcsRepo.write_inventory_and_patches opts- repoSort rfx | formatHas HashedInventory rfx = Hashed- | otherwise = Old- case repoSort rf2 of- Hashed ->- if formatHas HashedInventory rf- then copyHashedHashed- else withCurrentDirectory todir $- do HashedRepo.revert_tentative_changes- patches <- read_repo fromrepo+copyInventory fromRepo@(Repo fromDir opts fromFormat (DarcsRepository _ fromCache)) = do+ toRepo@(Repo toDir opts' toFormat (DarcsRepository toPristine toCache)) <-+ identifyRepositoryFor fromRepo "."+ toCache2 <- unionRemoteCaches toCache fromCache fromDir+ let toRepo2 :: Repository p C(r u t)+ toRepo2 = Repo toDir opts' toFormat $ DarcsRepository toPristine toCache2+ copyHashedHashed = HashedRepo.copyRepo toRepo2 opts fromDir+ copyAnyToOld r = withCurrentDirectory toDir $ readRepo r >>=+ DarcsRepo.writeInventoryAndPatches opts+ case repoSort fromFormat of+ Hashed -> case repoSort toFormat of+ Hashed -> copyHashedHashed+ Old -> copyAnyToOld fromRepo+ Old -> case repoSort toFormat of+ Hashed -> withCurrentDirectory toDir $ do+ HashedRepo.revertTentativeChanges+ patches <- readRepo fromRepo let k = "Copying patch" beginTedious k- tediousSize k (lengthRL $ concatRL patches)- let patches' = mapRL_RL (mapRL_RL (progress k)) patches- HashedRepo.write_tentative_inventory c (compression opts) patches'+ tediousSize k (lengthRL $ newset2RL patches)+ let patches' = progressPatchSet k patches+ HashedRepo.writeTentativeInventory toCache (compression opts) patches' endTedious k- HashedRepo.finalize_tentative_changes repo (compression opts)- Old -> case repoSort rf of- Hashed -> copyAnythingToOld fromrepo- _ -> copy_oldrepo_patches opts fromrepo todir+ HashedRepo.finalizeTentativeChanges toRepo $ compression opts+ Old -> copyOldrepoPatches opts fromRepo toDir -copy_oldrepo_patches :: RepoPatch p => [DarcsFlag] -> Repository p C(r u t) -> FilePath -> IO ()-copy_oldrepo_patches opts repository@(Repo dir _ _ _) out = do- Sealed patches <- DarcsRepo.read_repo opts "." :: IO (SealedPatchSet Patch)+copyOldrepoPatches :: RepoPatch p => [DarcsFlag] -> Repository p C(r u t) -> FilePath -> IO ()+copyOldrepoPatches opts repository@(Repo dir _ _ _) out = do+ Sealed patches <- DarcsRepo.readRepo opts "." :: IO (SealedPatchSet Patch C(Origin)) mpi <- if Partial `elem` opts -- FIXME this should get last pinfo *before* -- desired tag...- then identify_checkpoint repository+ then identifyCheckpoint repository else return Nothing- FlippedSeal scp <- return $ since_checkpoint mpi $ concatRL patches- DarcsRepo.copy_patches opts dir out $ mapRL info $ scp+ FlippedSeal scp <- return $ since_checkpoint mpi $ newset2RL patches+ DarcsRepo.copyPatches opts dir out $ mapRL info $ scp where since_checkpoint :: Maybe PatchInfo -> RL (PatchInfoAnd p) C(x y) -> FlippedSeal (RL (PatchInfoAnd p)) C(y) since_checkpoint Nothing ps = flipSeal ps@@ -199,118 +214,184 @@ copyPartialRepository :: forall p C(r u t). RepoPatch p => Repository p C(r u t) -> IO PorNP copyPartialRepository fromrepository@(Repo _ opts _ _) = do- mch <- get_checkpoint fromrepository :: IO (Maybe (Sealed (Named p C(x))))+ mch <- getCheckpoint fromrepository :: IO (Maybe (Sealed (Named p C(x)))) case mch of Nothing -> do putStrLn "No checkpoint." return NotPartial Just (Sealed ch) -> do copyInventory fromrepository withRepoLock opts $- \torepository -> do- write_checkpoint_patch ch- local_patches <- read_repo torepository+ writeCheckpointPatch ch+ local_patches <- readRepo torepository let pi_ch = patch2patchinfo ch- FlippedSeal ps <- return $ get_patches_beyond_tag pi_ch local_patches+ FlippedSeal ps <- return $ getPatchesBeyondTag pi_ch local_patches let needed_patches = reverseRL ps apply opts ch `catch` \e -> fail ("Bad checkpoint!\n" ++ prettyError e)- apply_patches opts needed_patches+ applyPatches opts needed_patches debugMessage "Writing the pristine" pristineFromWorking torepository return IsPartial copyFullRepository :: forall p C(r u t). RepoPatch p => Repository p C(r u t) -> IO ()-copyFullRepository fromrepository@(Repo fromdir opts rffrom _) = do- copyInventory fromrepository+copyFullRepository fromRepo@(Repo fromDir opts _ _) = do debugMessage "Copying prefs"- copyFileOrUrl opts (fromdir++"/"++darcsdir++"/prefs/prefs") (darcsdir++"/prefs/prefs") (MaxAge 600)- `catchall` return ()+ copyFileOrUrl opts (fromDir ++ "/" ++ darcsdir ++ "/prefs/prefs")+ (darcsdir ++ "/prefs/prefs") (MaxAge 600) `catchall` return ()+ if True -- isFile fromDir -- packs disabled for darcs 2.5+ then copyNotPackedRepository fromRepo+ else do+ b <- (Just <$> fetchFileLazyPS (fromDir ++ "/" ++ darcsdir +++ "/packs/basic.tar.gz") Uncachable) `catchall` return Nothing+ case b of+ Nothing -> copyNotPackedRepository fromRepo+ Just b' -> copyPackedRepository fromRepo b'++copyNotPackedRepository :: forall p C(r u t). RepoPatch p => Repository p C(r u t) -> IO ()+copyNotPackedRepository fromrepository@(Repo _ opts rffrom _) = do+ copyInventory fromrepository debugMessage "Grabbing lock in new repository..."- withRepoLock opts $- \torepository@(Repo _ _ rfto (DarcsRepository _ c)) ->+ withRepoLock opts $- \torepository@(Repo _ _ rfto _) -> if formatHas HashedInventory rffrom && formatHas HashedInventory rfto then do debugMessage "Writing working directory contents..." createPristineDirectoryTree torepository "."- fetch_patches_if_necessary opts torepository+ fetchPatchesIfNecessary opts torepository when (Partial `elem` opts) $ putStrLn $ "--partial: hashed or darcs-2 repository detected, using --lazy instead" else if formatHas HashedInventory rfto- then do local_patches <- read_repo torepository+ then do local_patches <- readRepo torepository replacePristine torepository emptyTree- let patchesToApply = progressFL "Applying patch" $ concatFL $ reverseRL $- mapRL_RL reverseRL local_patches- sequence_ $ mapFL (apply_to_tentative_pristine c opts) $ bunchFL 100 patchesToApply+ let patchesToApply = progressFL "Applying patch" $ newset2FL local_patches+ sequence_ $ mapFL (applyToTentativePristine opts) $ bunchFL 100 patchesToApply finalizeRepositoryChanges torepository debugMessage "Writing working directory contents..." createPristineDirectoryTree torepository "."- else do read_repo torepository >>= (apply_patches opts . reverseRL . concatRL)+ else do readRepo torepository >>= (applyPatches opts . newset2FL) debugMessage "Writing the pristine" pristineFromWorking torepository +copyPackedRepository :: forall p C(r u t). RepoPatch p =>+ Repository p C(r u t) -> BL.ByteString -> IO ()+copyPackedRepository fromRepo@(Repo fromDir opts _ (DarcsRepository _ fromCache)) b = do+ Repo toDir _ toFormat (DarcsRepository toPristine toCache) <-+ identifyRepositoryFor fromRepo "."+ toCache2 <- unionRemoteCaches toCache fromCache fromDir+ let toRepo :: Repository p C(r u r) -- In empty repo, t(entative) = r(ecorded)+ toRepo = Repo toDir opts toFormat $ DarcsRepository toPristine toCache2+ fromPacksDir = fromDir ++ "/" ++ darcsdir ++ "/packs/"+ createDirectoryIfMissing False $ toDir </> darcsdir </> "inventories"+ createDirectoryIfMissing False $ toDir </> darcsdir </> "pristine.hashed"+ createDirectoryIfMissing False $ toDir </> darcsdir </> "patches"+ copySources toRepo fromDir+ -- unpack inventory & pristine cache+ writeCompressed . Tar.read $ decompress b+ createPristineDirectoryTree toRepo "."+ -- pull new patches+ us <- readRepo toRepo+ them <- readRepo fromRepo+ us' :\/: them' <- return $ findUncommon us them+ revertTentativeChanges+ Sealed pw <- tentativelyMergePatches toRepo "get" opts us' them'+ invalidateIndex toRepo+ withGutsOf toRepo $ do+ finalizeRepositoryChanges toRepo+ applyToWorking toRepo opts pw+ return ()+ -- get old patches+ unless (any (`elem` opts) [Partial, Lazy, Ephemeral]) $ do+ putInfo "Copying patches, to get lazy repository hit ctrl-C..."+ writeCompressed . Tar.read . decompress =<< fetchFileLazyPS (fromPacksDir +++ "patches.tar.gz") Uncachable+ where+ writeCompressed Tar.Done = return ()+ writeCompressed (Tar.Next x xs) = case Tar.entryContent x of+ Tar.NormalFile x' _ -> do+ let p = Tar.entryPath x+ withTemp $ \p' -> do+ BL.writeFile p' $ if "hashed_inventory" `isSuffixOf` p+ then x'+ else compress x'+ renameFile p' p+ writeCompressed xs+ _ -> fail "Unexpected non-file tar entry"+ writeCompressed (Tar.Fail e) = fail e+ putInfo = when (not $ Quiet `elem` opts) . putStrLn+ -- | writePatchSet is like patchSetToRepository, except that it doesn't -- touch the working directory or pristine cache.-writePatchSet :: RepoPatch p => PatchSet p C(x) -> [DarcsFlag] -> IO (Repository p C(r u t))+writePatchSet :: RepoPatch p => PatchSet p C(Origin x) -> [DarcsFlag] -> IO (Repository p C(r u t)) writePatchSet patchset opts = do maybeRepo <- maybeIdentifyRepository opts "."- let repo@(Repo _ _ rf2 (DarcsRepository _ c)) = + let repo@(Repo _ _ rf2 (DarcsRepository _ c)) = case maybeRepo of- Right r -> r- Left e -> bug ("Current directory not repository in writePatchSet: " ++ e)+ GoodRepository r -> r+ BadRepository e -> bug ("Current directory is a bad repository in writePatchSet: " ++ e)+ NonRepository e -> bug ("Current directory not a repository in writePatchSet: " ++ e) debugMessage "Writing inventory" if formatHas HashedInventory rf2- then do HashedRepo.write_tentative_inventory c (compression opts) patchset- HashedRepo.finalize_tentative_changes repo (compression opts)- else DarcsRepo.write_inventory_and_patches opts patchset+ then do HashedRepo.writeTentativeInventory c (compression opts) patchset+ HashedRepo.finalizeTentativeChanges repo (compression opts)+ else DarcsRepo.writeInventoryAndPatches opts patchset return repo -- | patchSetToRepository takes a patch set, and writes a new repository in the current directory -- that contains all the patches in the patch set. This function is used when 'darcs get'ing a -- repository with the --to-match flag and the new repository is not in hashed format.--- This function does not (yet) work for hashed repositories. If the passed @DarcsFlag@s tell +-- This function does not (yet) work for hashed repositories. If the passed @DarcsFlag@s tell -- darcs to create a hashed repository, this function will call @error@.-patchSetToRepository :: RepoPatch p => Repository p C(r1 u1 r1) -> PatchSet p C(x)+patchSetToRepository :: RepoPatch p => Repository p C(r1 u1 r1) -> PatchSet p C(Origin x) -> [DarcsFlag] -> IO (Repository p C(r u t)) patchSetToRepository (Repo fromrepo _ rf _) patchset opts = do when (formatHas HashedInventory rf) $ -- set up sources and all that do writeFile "_darcs/tentative_pristine" "" -- this is hokey repox <- writePatchSet patchset opts- HashedRepo.copy_repo repox opts fromrepo+ HashedRepo.copyRepo repox opts fromrepo repo <- writePatchSet patchset opts- read_repo repo >>= (apply_patches opts . reverseRL . concatRL)+ readRepo repo >>= (applyPatches opts . newset2FL) debugMessage "Writing the pristine" pristineFromWorking repo return repo -checkUnrelatedRepos :: [DarcsFlag] -> [PatchInfo] -> PatchSet p C(x) -> PatchSet p C(y) -> IO ()-checkUnrelatedRepos opts common us them- | AllowUnrelatedRepos `elem` opts || not (null common)- || concatRL us `isShorterThanRL` 5 || concatRL them `isShorterThanRL` 5- = return ()- | otherwise- = do yorn <- promptYorn ("Repositories seem to be unrelated. Proceed?")- when (yorn /= 'y') $ do putStrLn "Cancelled."- exitWith ExitSuccess+checkUnrelatedRepos :: RepoPatch p => [DarcsFlag] -> PatchSet p C(start x) -> PatchSet p C(start y)+ -> IO ()+checkUnrelatedRepos opts _ _ | AllowUnrelatedRepos `elem` opts = return ()+checkUnrelatedRepos _ us them =+ if areUnrelatedRepos us them+ then do yorn <- promptYorn ("Repositories seem to be unrelated. Proceed?")+ when (yorn /= 'y') $ do putStrLn "Cancelled."+ exitWith ExitSuccess+ else return () -- | Unless a flag has been given in the first argument that tells darcs not to do so (--lazy,--- --partial or --ephemeral), this function fetches all patches that the given repository has +-- --partial or --ephemeral), this function fetches all patches that the given repository has -- with fetchFileUsingCache. This is used as a helper in copyFullRepository.-fetch_patches_if_necessary :: RepoPatch p => [DarcsFlag] -> Repository p C(r u t) -> IO ()-fetch_patches_if_necessary opts torepository@(Repo _ _ _ (DarcsRepository _ c)) = +fetchPatchesIfNecessary :: forall p C(r u t). RepoPatch p => [DarcsFlag] -> Repository p C(r u t) -> IO ()+fetchPatchesIfNecessary opts torepository@(Repo _ _ _ (DarcsRepository _ c)) = unless (Partial `elem` opts || Lazy `elem` opts || Ephemeral `elem` opts) $ do unless (Complete `elem` opts) $ putInfo "Copying patches, to get lazy repository hit ctrl-C..."- r <- read_repo torepository- let peekaboo :: PatchInfoAnd p C(x y) -> IO ()- peekaboo x = case extractHash x of- Left _ -> return ()- Right h -> fetchFileUsingCache c HashedPatchesDir h >> return ()- sequence_ $ mapRL peekaboo $ progressRLShowTags "Copying patches" $ concatRL r+ r <- readRepo torepository+ pipelineLength <- maxPipelineLength+ let patches = newset2RL r+ ppatches = progressRLShowTags "Copying patches" patches+ (first, other) = splitAt (pipelineLength - 1) $ tail $ hashes patches+ speculate | pipelineLength > 1 = [] : first : map (:[]) other+ | otherwise = []+ mapM_ fetchAndSpeculate $ zip (hashes ppatches) (speculate ++ repeat []) where putInfo = when (not $ Quiet `elem` opts) . putStrLn+ hashes :: FORALL(x y) RL (PatchInfoAnd p) C(x y) -> [String]+ hashes = catMaybes . mapRL ((either (const Nothing) Just) . extractHash)+ fetchAndSpeculate :: (String, [String]) -> IO ()+ fetchAndSpeculate (f, ss) = do+ fetchFileUsingCache c HashedPatchesDir f+ mapM_ (speculateFileUsingCache c HashedPatchesDir) ss -add_to_pending :: RepoPatch p => Repository p C(r u t) -> FL Prim C(u y) -> IO ()-add_to_pending (Repo _ opts _ _) _ | NoUpdateWorking `elem` opts = return ()-add_to_pending repo@(Repo _ opts _ _) p =+addToPending :: RepoPatch p => Repository p C(r u t) -> FL Prim C(u y) -> IO ()+addToPending (Repo _ opts _ _) _ | NoUpdateWorking `elem` opts = return ()+addToPending repo@(Repo _ opts _ _) p = do pend <- unrecordedChanges opts repo [] invalidateIndex repo- make_new_pending repo (pend +>+ p)+ makeNewPending repo (pend +>+ p) -- | Replace the existing pristine with a new one (loaded up in a Tree object). replacePristine :: Repository p C(r u t) -> Tree IO -> IO ()@@ -323,7 +404,7 @@ root <- writeDarcsHashed tree' $ darcsdir </> "pristine.hashed" writeDocBinFile t $ pris2inv (BS.unpack $ encodeBase16 root) i replace (PlainPristine n) =- do rm_recursive nold `catchall` return ()+ do rmRecursive nold `catchall` return () writePlainTree tree ntmp renameDirectory n nold renameDirectory ntmp n@@ -338,4 +419,3 @@ withCurrentDirectory dir $ readWorking >>= replacePristine repo pristineFromWorking (Repo dir _ _ (DarcsRepository p _)) = withCurrentDirectory dir $ createPristineFromWorking p-
src/Darcs/Repository/ApplyPatches.hs view
@@ -20,31 +20,31 @@ #include "gadts.h" -module Darcs.Repository.ApplyPatches ( apply_patches, apply_patches_with_feedback ) where+module Darcs.Repository.ApplyPatches ( applyPatches, applyPatchesWithFeedback ) where import Darcs.Patch ( Patchy, apply ) import Darcs.Hopefully ( PatchInfoAnd, hopefully, info )-import Darcs.Patch.Info ( human_friendly )+import Darcs.Patch.Info ( humanFriendly ) import Darcs.Witnesses.Ordered ( FL(..), lengthFL, mapFL ) import Darcs.Flags ( DarcsFlag ) import Darcs.Utils ( putDocLnError ) import Progress ( beginTedious, endTedious, tediousSize, finishedOneIO ) import Printer ( text ) -apply_patches_with_feedback :: Patchy p => [DarcsFlag] -> String -> FL (PatchInfoAnd p) C(x y) -> IO ()-apply_patches_with_feedback _ _ NilFL = return ()-apply_patches_with_feedback opts k patches =+applyPatchesWithFeedback :: Patchy p => [DarcsFlag] -> String -> FL (PatchInfoAnd p) C(x y) -> IO ()+applyPatchesWithFeedback _ _ NilFL = return ()+applyPatchesWithFeedback opts k patches = do beginTedious k tediousSize k (lengthFL patches) sequence_ $ mapFL apply_cautiously patches endTedious k where apply_cautiously :: Patchy p => PatchInfoAnd p C(a b) -> IO () apply_cautiously hp =- do finishedOneIO k (show $ human_friendly $ info hp)+ do finishedOneIO k (show $ humanFriendly $ info hp) apply opts (hopefully hp) `catch` \e -> do putDocLnError $ text "Unapplicable patch:"- putDocLnError $ human_friendly (info hp)+ putDocLnError $ humanFriendly (info hp) ioError e -apply_patches :: Patchy p => [DarcsFlag] -> FL (PatchInfoAnd p) C(x y) -> IO ()-apply_patches opts ps = apply_patches_with_feedback opts "Applying patch" ps+applyPatches :: Patchy p => [DarcsFlag] -> FL (PatchInfoAnd p) C(x y) -> IO ()+applyPatches opts ps = applyPatchesWithFeedback opts "Applying patch" ps
src/Darcs/Repository/Cache.hs view
@@ -7,17 +7,20 @@ cacheHash, okayHash, takeHash, Cache(..), CacheType(..), CacheLoc(..), WritableOrNot(..), HashedDir(..), hashedDir,- unionCaches, cleanCaches, cleanCachesWithHint,- fetchFileUsingCache, speculateFileUsingCache, writeFileUsingCache,+ unionCaches, unionRemoteCaches, cleanCaches, cleanCachesWithHint,+ fetchFileUsingCache, speculateFileUsingCache, speculateFilesUsingCache,+ writeFileUsingCache, peekInCache, repo2cache,- writable, isthisrepo, hashedFilePath, allHashedDirs+ writable, isthisrepo, hashedFilePath, allHashedDirs, compareByLocality ) where -import Control.Monad ( liftM, when, guard )+import Control.Monad ( liftM, when, guard, unless, filterM, forM_ ) import Data.List ( nub )-import Data.Maybe ( listToMaybe )-import System.Directory ( removeFile, doesFileExist, getDirectoryContents )+import Data.Maybe ( catMaybes, listToMaybe )+import System.Directory ( removeFile, doesFileExist, doesDirectoryExist,+ getDirectoryContents, getPermissions )+import qualified System.Directory as SD ( writable ) import System.Posix.Files ( linkCount, getSymbolicLinkStatus ) import System.IO ( hPutStrLn, stderr ) @@ -37,7 +40,7 @@ import Darcs.Global ( darcsdir ) import Darcs.Lock ( writeAtomicFilePS, gzWriteAtomicFilePS ) import Progress ( progressList, debugMessage, debugFail )-import Darcs.URL ( is_file )+import Darcs.URL ( isFile, isUrl, isSsh ) import Darcs.Utils ( withCurrentDirectory, catchall ) data HashedDir = HashedPristineDir | HashedPatchesDir | HashedInventoriesDir@@ -69,6 +72,54 @@ unionCaches :: Cache -> Cache -> Cache unionCaches (Ca a) (Ca b) = Ca (nub (a++b)) +-- | unionRemoteCaches merges caches. It tries to do better than just blindly+-- copying remote cache entries:+--+-- * If remote repository is accessed through network, do not copy any cache+-- entries from it. Taking local entries does not make sense and using+-- network entries can lead to darcs hang when it tries to get to+-- unaccessible host.+--+-- * If remote repositoty is local, copy all network cache entries. For local+-- cache entries if the cache directory exists and is writable it is added+-- as writable cache, if it exists but is not writable it is added as+-- read-only cache.+--+-- This approach should save us from bogus cache entries. One case it does not+-- work very well is when you fetch from partial repository over network.+-- Hopefully this is not a common case.+unionRemoteCaches :: Cache -> Cache -> String -> IO (Cache)+unionRemoteCaches local (Ca remote) repourl+ | isFile repourl = do f <- filtered+ return $ local `unionCaches` Ca f+ | otherwise = return local+ where filtered = mapM (\x -> fn x `catchall` return Nothing) remote >>=+ return . catMaybes+ fn :: CacheLoc -> IO (Maybe CacheLoc)+ fn (Cache Repo Writable _) = return Nothing+ fn c@(Cache t _ url)+ | isFile url = do+ ex <- doesDirectoryExist url+ if ex then do p <- getPermissions url+ return $ Just $+ if writable c && SD.writable p+ then c else Cache t NotWritable url+ else return Nothing+ | otherwise = return $ Just c++-- | Compares two caches, a remote cache is greater than a local one.+-- The order of the comparison is given by: local < http < ssh+compareByLocality :: CacheLoc -> CacheLoc -> Ordering+compareByLocality (Cache _ _ x) (Cache _ _ y)+ | isLocal x && isRemote y = LT+ | isRemote x && isLocal y = GT+ | isUrl x && isSsh y = LT+ | isSsh x && isUrl y = GT+ | otherwise = EQ+ where+ isRemote r= isUrl r || isSsh r+ isLocal = isFile+ repo2cache :: String -> Cache repo2cache r = Ca [Cache Repo NotWritable r] @@ -132,6 +183,16 @@ speculateFileUsingCache c sd h = do debugMessage $ "Speculating on "++h copyFileUsingCache OnlySpeculate c sd h +-- | Note that the files are likely to be useful soon: pipelined downloads will+-- add them to the (low-priority) queue, for the rest it is a noop.+speculateFilesUsingCache :: Cache -> HashedDir -> [String] -> IO ()+speculateFilesUsingCache _ _ [] = return ()+speculateFilesUsingCache cache sd hs =+ do --debugMessage $ "Thinking about speculating on "++unwords hs+ hs' <- filterM (fmap not . peekInCache cache sd) hs+ unless (null hs') $ do debugMessage $ "Speculating on "++unwords hs'+ copyFilesUsingCache OnlySpeculate cache sd hs'+ data OrOnlySpeculate = ActuallyCopy | OnlySpeculate deriving ( Eq ) copyFileUsingCache :: OrOnlySpeculate -> Cache -> HashedDir -> String -> IO ()@@ -157,7 +218,11 @@ | otherwise = sfuc cs out fn c = hashedFilePath c subdir f +copyFilesUsingCache :: OrOnlySpeculate -> Cache -> HashedDir -> [String] -> IO ()+copyFilesUsingCache oos cache subdir hs =+ do forM_ hs $ copyFileUsingCache oos cache subdir + data FromWhere = LocalOnly | Anywhere deriving ( Eq ) fetchFileUsingCachePrivate :: FromWhere -> Cache -> HashedDir -> String -> IO (String, B.ByteString)@@ -167,7 +232,7 @@ `catchall` debugFail ("Couldn't fetch `"++f++"'\nin subdir "++(hashedDir subdir)++ " from sources:\n\n"++show (Ca cache)) where ffuc (c:cs)- | not (writable c) && (Anywhere == fromWhere || is_file (fn c)) =+ | not (writable c) && (Anywhere == fromWhere || isFile (fn c)) = do debugMessage $ "In fetchFileUsingCachePrivate I'm going manually" debugMessage $ " getting "++f debugMessage $ " from " ++ fn c
src/Darcs/Repository/Checkpoint.hs view
@@ -20,102 +20,80 @@ #include "gadts.h" -module Darcs.Repository.Checkpoint ( get_checkpoint, get_checkpoint_by_default,- identify_checkpoint,- write_checkpoint_patch,+module Darcs.Repository.Checkpoint ( getCheckpoint, getCheckpointByDefault,+ identifyCheckpoint,+ writeCheckpointPatch, ) where import System.Directory ( createDirectoryIfMissing )-import System.IO.Unsafe ( unsafeInterleaveIO )-import Data.Maybe ( listToMaybe, catMaybes )+import Data.Maybe ( listToMaybe ) import Darcs.Hopefully ( PatchInfoAnd, info ) import qualified Data.ByteString as B ( null, empty, ByteString ) import Darcs.Lock ( writeDocBinFile )-import Darcs.SlurpDirectory ( Slurpy, empty_slurpy )-import Darcs.Patch ( RepoPatch, Patch, Named, patch2patchinfo,- applyToSlurpy, readPatch, gzWritePatch )-import Darcs.Witnesses.Ordered ( RL(..), FL(..), mapRL, reverseRL )-import Darcs.Repository.Internal ( Repository(..), read_repo )-import Darcs.Patch.Info ( PatchInfo, make_filename, readPatchInfo,- showPatchInfo- )+import Darcs.Patch ( RepoPatch, Named, patch2patchinfo,+ readPatch, gzWritePatch )+import Darcs.Witnesses.Ordered ( mapRL )+import Darcs.Repository.Internal ( Repository(..), readRepo )+import Darcs.Repository.DarcsRepo ( readCheckpoints )+import Darcs.Patch.Info ( PatchInfo, makeFilename, readPatchInfo,+ showPatchInfo )+import Darcs.Patch.Set( PatchSet(..), Tagged(..) ) import Darcs.External ( gzFetchFilePS, fetchFilePS, Cachable(..) ) import Darcs.Flags ( DarcsFlag( Partial, Complete ) ) import Darcs.Utils ( catchall )-import Darcs.RepoPath ( ioAbsoluteOrRemote, toPath ) import Darcs.Global ( darcsdir ) import Printer ( Doc, ($$), empty )-import Darcs.Witnesses.Sealed ( Sealed(Sealed), Sealed2(Sealed2), seal, seal2 )+import Darcs.Witnesses.Sealed ( Sealed, Sealed2(Sealed2), seal2 ) import Control.Monad ( liftM ) -read_patch_ids :: B.ByteString -> [PatchInfo]-read_patch_ids inv | B.null inv = []-read_patch_ids inv = case readPatchInfo inv of- Just (pinfo,r) -> pinfo : read_patch_ids r+readPatchIds :: B.ByteString -> [PatchInfo]+readPatchIds inv | B.null inv = []+readPatchIds inv = case readPatchInfo inv of+ Just (pinfo,r) -> pinfo : readPatchIds r Nothing -> [] -read_checkpoints :: String -> IO [(PatchInfo, Maybe Slurpy)]-read_checkpoints d = do- realdir <- toPath `fmap` ioAbsoluteOrRemote d- pistr <- fetchFilePS (realdir++"/"++darcsdir++"/checkpoints/inventory") Uncachable- `catchall` return B.empty- pis <- return $ reverse $ read_patch_ids pistr- slurpies <- sequence $ map (fetch_checkpoint realdir) pis- return $ zip pis slurpies- where fetch_checkpoint r pinfo =- unsafeInterleaveIO $ do- pstr <- gzFetchFilePS- (r++"/"++darcsdir++"/checkpoints/"++make_filename pinfo) Cachable- case fst `fmap` (readPatch pstr :: Maybe (Sealed (Named Patch C(x)), B.ByteString)) of- Nothing -> return Nothing- Just (Sealed p) -> return $ applyToSlurpy p empty_slurpy--get_checkpoint :: RepoPatch p => Repository p C(r u t) -> IO (Maybe (Sealed (Named p C(x))))-get_checkpoint repository@(Repo _ opts _ _) = if Partial `elem` opts- then get_check_internal repository+getCheckpoint :: RepoPatch p => Repository p C(r u t) -> IO (Maybe (Sealed (Named p C(x))))+getCheckpoint repository@(Repo _ opts _ _) = if Partial `elem` opts+ then getCheckInternal repository else return Nothing -get_checkpoint_by_default :: RepoPatch p => Repository p C(r u t) -> IO (Maybe (Sealed (Named p C(x))))-get_checkpoint_by_default repository@(Repo _ opts _ _) = if Complete `elem` opts+getCheckpointByDefault :: RepoPatch p => Repository p C(r u t) -> IO (Maybe (Sealed (Named p C(x))))+getCheckpointByDefault repository@(Repo _ opts _ _) = if Complete `elem` opts then return Nothing- else get_check_internal repository+ else getCheckInternal repository -identify_checkpoint :: RepoPatch p => Repository p C(r u t) -> IO (Maybe PatchInfo)-identify_checkpoint repository@(Repo r _ _ _) = do- pis <- (map sp2i . catMaybes . mapRL lastRL) `liftM` read_repo repository+identifyCheckpoint :: RepoPatch p => Repository p C(r u t) -> IO (Maybe PatchInfo)+identifyCheckpoint repository@(Repo r _ _ _) = do+ pis <- (map sp2i . extractTags) `liftM` readRepo repository pistr <- fetchFilePS (r++"/"++darcsdir++"/checkpoints/inventory") Uncachable `catchall` return B.empty- return $ listToMaybe $ filter (`elem` pis) $ reverse $ read_patch_ids pistr- where lastRL :: RL a C(x y) -> Maybe (Sealed2 a)- lastRL as = do Sealed ps <- headFL (reverseRL as)- return $ seal2 ps- headFL :: FL a C(x y) -> Maybe (Sealed (a C(x)))- headFL (x:>:_) = Just $ seal x- headFL NilFL = Nothing+ return $ listToMaybe $ filter (`elem` pis) $ reverse $ readPatchIds pistr+ where extractTags :: PatchSet p C(start end) -> [Sealed2 (PatchInfoAnd p)]+ extractTags (PatchSet _ ts) = mapRL (\(Tagged t _ _) -> seal2 t) ts sp2i :: Sealed2 (PatchInfoAnd p) -> PatchInfo sp2i (Sealed2 p) = info p -get_check_internal :: RepoPatch p => Repository p C(r u t) -> IO (Maybe (Sealed (Named p C(x))))-get_check_internal repository@(Repo r _ _ _) = do- mc <- identify_checkpoint repository+getCheckInternal :: RepoPatch p => Repository p C(r u t) -> IO (Maybe (Sealed (Named p C(x))))+getCheckInternal repository@(Repo r _ _ _) = do+ mc <- identifyCheckpoint repository case mc of Nothing -> return Nothing Just pinfo -> do ps <- gzFetchFilePS- (r++"/"++darcsdir++"/checkpoints/"++make_filename pinfo) Cachable+ (r++"/"++darcsdir++"/checkpoints/"++makeFilename pinfo) Cachable return $ case readPatch ps of Just (p, _) -> Just p Nothing -> Nothing -format_inv :: [PatchInfo] -> Doc-format_inv [] = empty-format_inv (pinfo:ps) = showPatchInfo pinfo- $$ format_inv ps+formatInv :: [PatchInfo] -> Doc+formatInv [] = empty+formatInv (pinfo:ps) = showPatchInfo pinfo+ $$ formatInv ps -write_checkpoint_patch :: RepoPatch p => Named p C(x y) -> IO ()-write_checkpoint_patch p =+writeCheckpointPatch :: RepoPatch p => Named p C(x y) -> IO ()+writeCheckpointPatch p = do createDirectoryIfMissing False (darcsdir++"/checkpoints")- gzWritePatch (darcsdir++"/checkpoints/"++make_filename (patch2patchinfo p)) p- cpi <- (map fst) `fmap` read_checkpoints "."+ gzWritePatch (darcsdir++"/checkpoints/"++makeFilename (patch2patchinfo p)) p+ cpi <- readCheckpoints "." writeDocBinFile (darcsdir++"/checkpoints/inventory")- $ format_inv $ reverse $ patch2patchinfo p:cpi+ $ formatInv $ reverse $ patch2patchinfo p:cpi
src/Darcs/Repository/DarcsRepo.lhs view
@@ -50,23 +50,24 @@ #include "gadts.h" -module Darcs.Repository.DarcsRepo ( write_inventory, write_inventory_and_patches,- add_to_inventory, add_to_tentative_pristine,- add_to_tentative_inventory, remove_from_tentative_inventory,- finalize_tentative_changes, finalize_pristine_changes,- revert_tentative_changes,- read_repo, read_tentative_repo, write_and_read_patch,- copy_patches+module Darcs.Repository.DarcsRepo ( writeInventory, writeInventoryAndPatches,+ addToInventory, addToTentativePristine,+ addToTentativeInventory, removeFromTentativeInventory,+ finalizeTentativeChanges, finalizePristineChanges,+ revertTentativeChanges,+ readRepo, readTentativeRepo, writeAndReadPatch,+ copyPatches, readCheckpoints ) where -import System.Directory ( doesDirectoryExist, createDirectoryIfMissing )+import System.Directory ( createDirectoryIfMissing ) import Workaround ( renameFile ) import Darcs.Utils ( clarifyErrors ) import Progress ( debugMessage, beginTedious, endTedious, finishedOneIO ) import Darcs.RepoPath ( ioAbsoluteOrRemote, toPath ) import System.IO ( hPutStrLn, stderr ) import System.IO.Unsafe ( unsafeInterleaveIO )-import Control.Monad ( liftM, when, unless )+import System.FilePath.Posix ( (</>) )+import Control.Monad ( when ) import Darcs.Hopefully ( Hopefully, PatchInfoAnd, patchInfoAndPatch, info, actually, hopefully, unavailable, n2pia )@@ -76,31 +77,36 @@ import qualified Data.ByteString as B (ByteString, null, readFile, empty) import qualified Data.ByteString.Char8 as BC (break, pack) -import Darcs.SlurpDirectory ( Slurpy, empty_slurpy )-import Darcs.Patch ( RepoPatch, Effect, Prim, Named, Patch, invert,+import Darcs.Patch ( RepoPatch, Effect, Prim, Named, invert, effect, patch2patchinfo,- applyToSlurpy, readPatch,- writePatch, gzWritePatch, showPatch )-import Darcs.Witnesses.Ordered ( FL(..), RL(..), (:<)(..),+ showPatch )+import qualified Darcs.Patch as Patch++import Darcs.Witnesses.Ordered ( FL(..), RL(..), (:>>)(..), (+<+), reverseFL, mapFL, unsafeCoerceP,- reverseRL, concatRL, mapRL, mapRL_RL )-import Darcs.Patch.Info ( PatchInfo, make_filename, readPatchInfo,- showPatchInfo, is_tag+ reverseRL, mapRL )+import Darcs.Patch.Info ( PatchInfo, makeFilename, readPatchInfo,+ showPatchInfo, isTag )-import Darcs.Patch.Set ( PatchSet, SealedPatchSet )+import Darcs.Patch.Set ( PatchSet(..), Tagged(..), SealedPatchSet, newset2RL )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif import Darcs.External ( gzFetchFilePS, fetchFilePS, copyFilesOrUrls, Cachable(..), cloneFile ) import Darcs.Lock ( writeBinFile, writeDocBinFile, appendDocBinFile, appendBinFile ) import Darcs.Flags ( DarcsFlag( NoCompress ) )-import Darcs.Patch.Depends ( slightly_optimize_patchset, commute_to_end, deep_optimize_patchset )+import Darcs.Patch.Depends ( slightlyOptimizePatchset, commuteToEnd, deepOptimizePatchset ) import Darcs.Repository.Pristine ( identifyPristine, applyPristine ) import Darcs.Global ( darcsdir ) import Darcs.Utils ( catchall ) import Darcs.ProgressPatches ( progressFL ) import Printer ( text, (<>), Doc, ($$), empty )-import Darcs.Witnesses.Sealed ( Sealed(Sealed), seal, unseal )+import Darcs.Witnesses.Sealed ( Sealed(Sealed), seal, unseal, mapSeal )++#include "impossible.h" \end{code} There is a very special patch which may be stored in \verb!patches! which@@ -110,19 +116,20 @@ Similarly, token replaces are stored in pending until they are recorded. \begin{code}-write_patch :: RepoPatch p => [DarcsFlag] -> Named p C(x y) -> IO FilePath-write_patch opts p =+writePatch :: RepoPatch p => [DarcsFlag] -> Named p C(x y) -> IO FilePath+writePatch opts p = do let writeFun = if NoCompress `elem` opts- then writePatch- else gzWritePatch- pname = darcsdir++"/patches/"++make_filename (patch2patchinfo p)+ then Patch.writePatch+ else Patch.gzWritePatch+ pname = darcsdir++"/patches/"++makeFilename (patch2patchinfo p) writeFun pname p return pname -write_and_read_patch :: RepoPatch p => [DarcsFlag] -> PatchInfoAnd p C(x y)+writeAndReadPatch :: RepoPatch p => [DarcsFlag] -> PatchInfoAnd p C(x y) -> IO (PatchInfoAnd p C(x y))-write_and_read_patch opts p = do fn <- write_patch opts $ hopefully p- unsafeInterleaveIO $ parse fn+writeAndReadPatch opts p =+ do fn <- writePatch opts $ hopefully p+ unsafeInterleaveIO $ parse fn where parse fn = do debugMessage ("Reading patch file: "++ fn) ps <- gzReadFilePS fn Sealed pp <- case readPatch ps of@@ -130,90 +137,101 @@ Nothing -> fail ("Couldn't parse patch file "++fn) return $ n2pia $ unsafeCoerceP pp ---format_inventory is not exported for use outside of the DarcsRepo module+--formatInventory is not exported for use outside of the DarcsRepo module --itself.-format_inventory :: [PatchInfo] -> Doc-format_inventory [] = empty-format_inventory (pinfo:ps) = showPatchInfo pinfo $$ format_inventory ps+formatInventory :: [PatchInfo] -> Doc+formatInventory [] = empty+formatInventory (pinfo:ps) = showPatchInfo pinfo $$ formatInventory ps -write_inventory :: RepoPatch p => FilePath -> PatchSet p C(x) -> IO ()--- Note that write_inventory optimizes the inventory it writes out by+writeInventory :: RepoPatch p => FilePath -> PatchSet p C(Origin x) -> IO ()+-- Note that writeInventory optimizes the inventory it writes out by -- checking on tag dependencies.--- FIXME: There is also a problem that write_inventory always writes+-- FIXME: There is also a problem that writeInventory always writes -- out the entire inventory, including the parts that you haven't -- changed...-write_inventory dir ps = withSignalsBlocked $ do+writeInventory dir ps = withSignalsBlocked $ do createDirectoryIfMissing False (dir++"/"++darcsdir++"/inventories")- simply_write_inventory "inventory" dir $ slightly_optimize_patchset ps+ simplyWriteInventory "inventory" dir $ slightlyOptimizePatchset ps -simply_write_inventory :: RepoPatch p => String -> FilePath -> PatchSet p C(x) -> IO ()-simply_write_inventory name dir NilRL =+simplyWriteInventory :: RepoPatch p => String -> FilePath -> PatchSet p C(Origin x) -> IO ()+simplyWriteInventory name dir (PatchSet NilRL NilRL) = writeBinFile (dir++"/"++darcsdir++"/"++name) ""-simply_write_inventory name dir (ps:<:NilRL) = do- writeDocBinFile (dir++"/"++darcsdir++"/"++name) $ format_inventory $ mapFL info $ reverseRL ps-simply_write_inventory _ _ (NilRL:<:_) =- fail $ "Bug in simply_write_inventory, please report!"-simply_write_inventory name dir (ps:<:pss) = do- tagname <- return $ make_filename $ last $ mapRL info ps- simply_write_inventory ("inventories/"++tagname) dir pss+simplyWriteInventory name dir (PatchSet ps NilRL) = do+ writeDocBinFile (dir++"/"++darcsdir++"/"++name) $ formatInventory $ mapFL info $ reverseRL ps+simplyWriteInventory name dir (PatchSet ps ts@(Tagged t _ _ :<: _)) = do -- nonempty Tagged+ simplyWriteTaggedInventory dir ts writeDocBinFile (dir++"/"++darcsdir++"/"++name) $ text "Starting with tag:"- $$ format_inventory (mapFL info $ reverseRL ps)+ $$ formatInventory (mapFL info $ t :>: reverseRL ps) -write_inventory_and_patches :: RepoPatch p => [DarcsFlag] -> PatchSet p C(x) -> IO ()-write_inventory_and_patches opts ps = do write_inventory "." ps- sequence_ $ mapRL (write_patch opts . hopefully) $ concatRL ps+simplyWriteTaggedInventory :: RepoPatch p => FilePath -> RL (Tagged p) C(Origin x) -> IO ()+simplyWriteTaggedInventory _ NilRL = return ()+simplyWriteTaggedInventory dir (Tagged t _ ps :<: NilRL) = do+ writeDocBinFile (dir </> "_darcs/inventories" </> makeFilename (info t)) $+ formatInventory (mapFL info $ reverseRL ps)+simplyWriteTaggedInventory dir (Tagged t _ ps :<: ts@(Tagged t2 _ _ :<: _)) =+ do simplyWriteTaggedInventory dir ts+ writeDocBinFile (dir </> "_darcs/inventories" </> makeFilename (info t)) $+ text "Starting with tag:" $$+ formatInventory (mapFL info $ t2 :>: reverseRL ps) -add_to_inventory :: FilePath -> [PatchInfo] -> IO ()-add_to_inventory dir pinfos =+writeInventoryAndPatches :: RepoPatch p => [DarcsFlag] -> PatchSet p C(Origin x) -> IO ()+writeInventoryAndPatches opts ps = do writeInventory "." ps+ sequence_ $ mapRL (writePatch opts . hopefully) $ newset2RL ps++addToInventory :: FilePath -> [PatchInfo] -> IO ()+addToInventory dir pinfos = appendDocBinFile (dir++"/"++darcsdir++"/inventory") $ text "\n" <> pidocs pinfos where pidocs [] = text "" pidocs (p:ps) = showPatchInfo p $$ pidocs ps -add_to_tentative_inventory :: forall p C(x y). RepoPatch p => [DarcsFlag] -> Named p C(x y) -> IO FilePath-add_to_tentative_inventory opts p =+addToTentativeInventory :: forall p C(x y). RepoPatch p => [DarcsFlag] -> Named p C(x y) -> IO FilePath+addToTentativeInventory opts p = do appendDocBinFile (darcsdir++"/tentative_inventory") $ text "\n" <> showPatchInfo (patch2patchinfo p)- when (is_tag $ patch2patchinfo p) $+ res <- writePatch opts p+ when (isTag $ patch2patchinfo p) $ do debugMessage "Optimizing the tentative inventory, since we're adding a tag." realdir <- toPath `fmap` ioAbsoluteOrRemote "." let k = "Reading tentative inventory" beginTedious k- Sealed ps <- read_repo_private k opts realdir "tentative_inventory"- :: IO (SealedPatchSet p)- simply_write_inventory "tentative_inventory" "." $ slightly_optimize_patchset ps- write_patch opts p+ Sealed ps <- readRepoPrivate k realdir "tentative_inventory"+ :: IO (SealedPatchSet p C(Origin) )+ simplyWriteInventory "tentative_inventory" "." $ slightlyOptimizePatchset ps+ return res -add_to_tentative_pristine :: Effect p => p C(x y) -> IO ()-add_to_tentative_pristine p =+addToTentativePristine :: Effect p => p C(x y) -> IO ()+addToTentativePristine p = do -- Sealed p <- (fst . fromJust . readPatchCarefully) `fmap` gzReadFilePS fp appendDocBinFile (darcsdir++"/tentative_pristine") $ showPatch (effect p) -- FIXME: this is inefficient! appendBinFile (darcsdir++"/tentative_pristine") "\n" -remove_from_tentative_inventory :: RepoPatch p => Bool -> [DarcsFlag] -> FL (Named p) C(x y) -> IO ()-remove_from_tentative_inventory update_pristine opts to_remove =- do finalize_tentative_changes- Sealed allpatches <- read_repo opts "."- skipped :< unmodified <- return $ commute_to_end (unsafeCoerceP to_remove) allpatches- sequence_ $ mapFL (write_patch opts) skipped- write_inventory "." $ deep_optimize_patchset- $ mapRL_RL n2pia (reverseFL skipped) :<: unmodified- remove_from_checkpoint_inventory to_remove+removeFromTentativeInventory :: RepoPatch p => Bool -> [DarcsFlag]+ -> FL (PatchInfoAnd p) C(x y) -> IO ()+removeFromTentativeInventory update_pristine opts to_remove =+ do finalizeTentativeChanges+ Sealed allpatches <- readRepo opts "."+ unmodified :>> skipped <- return $ commuteToEnd+ (reverseFL $ unsafeCoerceP to_remove) allpatches+ sequence_ $ mapRL (writePatch opts . hopefully) skipped+ let newpatches = case unmodified of+ PatchSet ps ts -> PatchSet (skipped+<+ps) ts+ writeInventory "." $ deepOptimizePatchset newpatches when update_pristine $ do pris <- identifyPristine repairable $ applyPristine pris $ progressFL "Applying inverse to pristine" $ invert to_remove- revert_tentative_changes+ revertTentativeChanges -finalize_tentative_changes :: IO ()-finalize_tentative_changes = renameFile (darcsdir++"/tentative_inventory") (darcsdir++"/inventory")+finalizeTentativeChanges :: IO ()+finalizeTentativeChanges = renameFile (darcsdir++"/tentative_inventory") (darcsdir++"/inventory") -finalize_pristine_changes :: IO ()-finalize_pristine_changes =+finalizePristineChanges :: IO ()+finalizePristineChanges = do Sealed ps <- read_patches $ darcsdir++"/tentative_pristine" pris <- identifyPristine repairable $ applyPristine pris ps- where + where read_patches :: String -> IO (Sealed (FL Prim C(x))) read_patches f = do ps <- B.readFile f return $ case readPatch ps of@@ -225,109 +243,109 @@ ["Your repository is now in an inconsistent state.", "This must be fixed by running darcs repair."] -revert_tentative_changes :: IO ()-revert_tentative_changes =+revertTentativeChanges :: IO ()+revertTentativeChanges = do cloneFile (darcsdir++"/inventory") (darcsdir++"/tentative_inventory") writeBinFile (darcsdir++"/tentative_pristine") "" -copy_patches :: [DarcsFlag] -> FilePath -> FilePath -> [PatchInfo] -> IO ()-copy_patches opts dir out patches = do+copyPatches :: [DarcsFlag] -> FilePath -> FilePath -> [PatchInfo] -> IO ()+copyPatches opts dir out patches = do realdir <- toPath `fmap` ioAbsoluteOrRemote dir- copyFilesOrUrls opts (realdir++"/"++darcsdir++"/patches") (map make_filename patches)+ copyFilesOrUrls opts (realdir++"/"++darcsdir++"/patches") (map makeFilename patches) (out++"/"++darcsdir++"/patches") Cachable -read_repo :: RepoPatch p => [DarcsFlag] -> String -> IO (SealedPatchSet p)-read_repo opts d = do+readRepo :: RepoPatch p => [DarcsFlag] -> String -> IO (SealedPatchSet p C(Origin))+readRepo _ d = do realdir <- toPath `fmap` ioAbsoluteOrRemote d let k = "Reading inventory of repository "++d beginTedious k- read_repo_private k opts realdir "inventory" `catch`+ readRepoPrivate k realdir "inventory" `catch` (\e -> do hPutStrLn stderr ("Invalid repository: " ++ realdir) ioError e) -read_tentative_repo :: RepoPatch p => [DarcsFlag] -> String -> IO (SealedPatchSet p)-read_tentative_repo opts d = do+readTentativeRepo :: RepoPatch p => [DarcsFlag] -> String -> IO (SealedPatchSet p C(Origin))+readTentativeRepo _ d = do realdir <- toPath `fmap` ioAbsoluteOrRemote d let k = "Reading tentative inventory of repository "++d beginTedious k- read_repo_private k opts realdir "tentative_inventory" `catch`+ readRepoPrivate k realdir "tentative_inventory" `catch` (\e -> do hPutStrLn stderr ("Invalid repository: " ++ realdir) ioError e) -read_repo_private :: RepoPatch p => String -> [DarcsFlag] -> FilePath -> FilePath -> IO (SealedPatchSet p)-read_repo_private k opts d iname = do- i <- gzFetchFilePS (d++"/"++darcsdir++"/"++iname) Uncachable+readRepoPrivate :: RepoPatch p => String -> FilePath -> FilePath -> IO (SealedPatchSet p C(Origin))+readRepoPrivate k d iname = do+ i <- gzFetchFilePS (d </> "_darcs" </> iname) Uncachable finishedOneIO k iname- (rest,str) <- case BC.break ((==) '\n') i of- (swt,pistr) | swt == BC.pack "Starting with tag:" ->- do r <- rr $ head $ read_patch_ids pistr- return (r,pistr)- _ -> do endTedious k- return (seal NilRL,i)- pis <- return $ reverse $ read_patch_ids str- isdir <- doesDirectoryExist d- let parse f = let fn = d ++ "/"++darcsdir++"/patches/" ++ make_filename f- in if isdir then parse_local fn- else parse_remote fn- lift2Sealed (:<:) (return rest) (read_patches parse pis)- where rr pinfo = unsafeInterleaveIO $ read_repo_private k opts d $- "inventories/"++make_filename pinfo- -- parse_remote should really download to a temporary file removed- -- at exit- parse_remote, parse_local :: RepoPatch p => String -> IO (Sealed (Hopefully (Named p) C(x)))- parse_remote fn = do ps <- gzFetchFilePS fn Cachable- return $ hopefullyNoParseError fn (readPatch ps)- parse_local fn = do ps <- gzReadFilePS fn- return $ hopefullyNoParseError fn (readPatch ps)- hopefullyNoParseError :: String -> Maybe (Sealed (a C(x)), b) -> Sealed (Hopefully a C(x))+ let parse inf = parse2 inf $ d </> "_darcs/patches" </> makeFilename inf+ (mt, is) = case BC.break ((==) '\n') i of+ (swt,pistr) | swt == BC.pack "Starting with tag:" ->+ case readPatchIds pistr of+ (t:ids) -> (Just t,reverse ids)+ [] -> bug "bad inventory in readRepoPrivate"+ _ -> (Nothing, reverse $ readPatchIds i)+ Sealed ts <- unseal seal `fmap` unsafeInterleaveIO (read_ts parse mt)+ Sealed ps <- unseal seal `fmap` unsafeInterleaveIO (read_patches parse is)+ return $ seal (PatchSet ps ts)+ where read_ts :: RepoPatch p =>+ (FORALL(b) PatchInfo -> IO (Sealed (PatchInfoAnd p C(b))))+ -> Maybe PatchInfo -> IO (Sealed (RL (Tagged p) C(Origin)))+ read_ts _ Nothing = do endTedious k+ return $ seal NilRL+ read_ts parse (Just tag0) =+ do debugMessage $ "Looking for inventory for:\n"++ show tag0+ i <- unsafeInterleaveIO $+ do x <- gzFetchFilePS (d</>"_darcs/inventories"</>makeFilename tag0) Uncachable+ finishedOneIO k (show tag0)+ return x+ let (mt, is) = case BC.break ((==) '\n') i of+ (swt,pistr) | swt == BC.pack "Starting with tag:" ->+ case readPatchIds pistr of+ (t:ids) -> (Just t,reverse ids)+ [] -> bug "bad inventory in readRepoPrivate"+ _ -> (Nothing, reverse $ readPatchIds i)+ Sealed ts <- fmap (unseal seal) $ unsafeInterleaveIO $ read_ts parse mt+ Sealed ps <- unseal seal `fmap` unsafeInterleaveIO (read_patches parse is)+ Sealed tag00 <- parse tag0 `catch`+ \e -> return $ seal $+ patchInfoAndPatch tag0 $ unavailable $ show e+ return $ seal $ Tagged tag00 Nothing ps :<: ts+ parse2 :: RepoPatch p => PatchInfo -> FilePath+ -> IO (Sealed (PatchInfoAnd p C(x)))+ parse2 i fn = do ps <- unsafeInterleaveIO $ gzFetchFilePS fn Cachable+ return $ patchInfoAndPatch i+ `mapSeal` hopefullyNoParseError (toPath fn) (readPatch ps)+ hopefullyNoParseError :: String -> Maybe (Sealed (Named a1dr C(x)), b)+ -> Sealed (Hopefully (Named a1dr) C(x)) hopefullyNoParseError _ (Just (Sealed x, _)) = seal $ actually x hopefullyNoParseError s Nothing = seal $ unavailable $ "Couldn't parse file "++s- read_patches :: RepoPatch p => (FORALL(b) PatchInfo -> IO (Sealed (Hopefully (Named p) C(b))))+ read_patches :: RepoPatch p =>+ (FORALL(b) PatchInfo -> IO (Sealed (PatchInfoAnd p C(b)))) -> [PatchInfo] -> IO (Sealed (RL (PatchInfoAnd p) C(x))) read_patches _ [] = return $ seal NilRL read_patches parse (i:is) =- lift2Sealed (\p rest -> i `patchInfoAndPatch` p :<: rest)+ lift2Sealed (:<:) (read_patches parse is)- (parse i `catch` \e -> return $ seal $ unavailable $ show e)- lift2Sealed :: (FORALL(y z) q C(y z) -> p C(x y) -> r C(x z))- -> IO (Sealed (p C(x))) -> (FORALL(b) IO (Sealed (q C(b)))) -> IO (Sealed (r C(x)))+ (parse i `catch` \e ->+ return $ seal $ patchInfoAndPatch i $ unavailable $ show e)+ lift2Sealed :: (FORALL(y z) q C(y z) -> pp C(y) -> r C(z))+ -> IO (Sealed pp) -> (FORALL(b) IO (Sealed (q C(b)))) -> IO (Sealed r) lift2Sealed f iox ioy = do Sealed x <- unseal seal `fmap` unsafeInterleaveIO iox Sealed y <- unseal seal `fmap` unsafeInterleaveIO ioy return $ seal $ f y x -read_patch_ids :: B.ByteString -> [PatchInfo]-read_patch_ids inv | B.null inv = []-read_patch_ids inv = case readPatchInfo inv of- Just (pinfo,r) -> pinfo : read_patch_ids r+readPatchIds :: B.ByteString -> [PatchInfo]+readPatchIds inv | B.null inv = []+readPatchIds inv = case readPatchInfo inv of+ Just (pinfo,r) -> pinfo : readPatchIds r Nothing -> [] -read_checkpoints :: String -> IO [(PatchInfo, Maybe Slurpy)]-read_checkpoints d = do+readCheckpoints :: String -> IO [PatchInfo]+readCheckpoints d = do realdir <- toPath `fmap` ioAbsoluteOrRemote d pistr <- fetchFilePS (realdir++"/"++darcsdir++"/checkpoints/inventory") Uncachable `catchall` return B.empty- pis <- return $ reverse $ read_patch_ids pistr- slurpies <- sequence $ map (fetch_checkpoint realdir) pis- return $ zip pis slurpies- where fetch_checkpoint r pinfo =- unsafeInterleaveIO $ do- pstr <- gzFetchFilePS- (r++"/"++darcsdir++"/checkpoints/"++make_filename pinfo) Cachable- case fst `liftM` readPatch_ pstr of- Nothing -> return Nothing- Just (Sealed p) -> return $ applyToSlurpy p empty_slurpy- readPatch_ :: B.ByteString -> Maybe (Sealed (Named Patch C(x)), B.ByteString)- readPatch_ = readPatch--remove_from_checkpoint_inventory :: RepoPatch p => FL (Named p) C(x y) -> IO ()-remove_from_checkpoint_inventory ps = do- -- only tags can be checkpoints- let pinfos = filter is_tag $ mapFL patch2patchinfo ps- unless (null pinfos) $ do- createDirectoryIfMissing False (darcsdir++"/checkpoints")- cpi <- (map fst) `liftM` read_checkpoints "."- writeDocBinFile (darcsdir++"/checkpoints/inventory") $- format_inventory $ reverse $ filter (`notElem` pinfos) cpi+ pis <- return $ reverse $ readPatchIds pistr+ return pis \end{code} The \verb!_darcs! directory also contains a directory called
src/Darcs/Repository/Format.hs view
@@ -107,7 +107,7 @@ readfromAndWritetoProblem :: RepoFormat -> RepoFormat -> Maybe String readfromAndWritetoProblem inrf outrf | formatHas Darcs2 inrf /= formatHas Darcs2 outrf- = Just "Cannot mix darcs-2 repositories with older formats" + = Just "Cannot mix darcs-2 repositories with older formats" | otherwise = msum [readProblem inrf, writeProblem outrf]
src/Darcs/Repository/HashedIO.hs view
@@ -19,41 +19,42 @@ #include "gadts.h" -module Darcs.Repository.HashedIO ( HashedIO, applyHashed,- copyHashed, copyPartialsHashed, listHashedContents,- slurpHashedPristine, writeHashedPristine,- clean_hashdir ) where+module Darcs.Repository.HashedIO ( HashedIO,+ copyHashed, copyPartialsHashed,+ cleanHashdir ) where import Darcs.Global ( darcsdir ) import qualified Data.Set as Set-import qualified Data.Map as Map import System.Directory ( getDirectoryContents, createDirectoryIfMissing ) import Control.Monad.State ( StateT, runStateT, modify, get, put, gets, lift ) import Control.Monad ( when ) import Control.Applicative ( (<$>) ) import Data.Maybe ( isJust ) import System.IO.Unsafe ( unsafeInterleaveIO )+import System.IO ( hPutStrLn, stderr ) -import Darcs.SlurpDirectory.Internal ( Slurpy(..), SlurpyContents(..), map_to_slurpies, slurpies_to_map )-import Darcs.SlurpDirectory ( withSlurpy, undefined_size )-import Darcs.Repository.Cache ( Cache, fetchFileUsingCache, writeFileUsingCache,+import Darcs.Repository.Cache ( Cache(..), fetchFileUsingCache, writeFileUsingCache, peekInCache, speculateFileUsingCache, okayHash, cleanCachesWithHint, HashedDir(..), hashedDir ) import Darcs.Patch ( Patchy, apply ) import Darcs.RepoPath ( FilePathLike, toFilePath ) import Darcs.IO ( ReadableDirectory(..), WriteableDirectory(..) )-import Darcs.Flags ( DarcsFlag, Compression( .. ), compression )+import Darcs.Flags ( DarcsFlag, Compression( .. ) ) import Darcs.Lock ( writeAtomicFilePS, removeFileMayNotExist ) import Darcs.Utils ( withCurrentDirectory )-import Progress ( debugMessage, beginTedious, endTedious, tediousSize, finishedOneIO, progress )-import Darcs.Patch.FileName ( FileName, norm_path, fp2fn, fn2fp, fn2niceps, niceps2fn,- break_on_dir, own_name, superName )+import Progress ( debugMessage, tediousSize, finishedOneIO )+import Darcs.Patch.FileName ( FileName, normPath, fp2fn, fn2fp, fn2niceps, niceps2fn,+ breakOnDir, ownName, superName ) import ByteStringUtils ( linesPS, unlinesPS ) import qualified Data.ByteString as B (ByteString, length, empty) import qualified Data.ByteString.Char8 as BC (unpack, pack) -import SHA1 ( sha1PS )+import Storage.Hashed.Darcs( hashedTreeIO, readDarcsHashedNosize, readDarcsHashed,+ writeDarcsHashed, readDarcsHashedDir, darcsLocation,+ decodeDarcsHash, decodeDarcsSize )+import Storage.Hashed.Tree( treeHash, ItemType(..) )+import Storage.Hashed.Hash( encodeBase16, Hash(..) ) -- | @readHashFile c subdir hash@ reads the file with hash @hash@ in dir subdir, -- fetching it from 'Cache' @c@ if needed.@@ -62,19 +63,6 @@ do debugMessage $ "Reading hash file "++hash++" from "++(hashedDir subdir)++"/" fetchFileUsingCache c subdir hash -applyHashed :: Patchy q => Cache -> [DarcsFlag] -> String -> q C(x y) -> IO String-applyHashed c fs h p = do s <- slurpHashedPristine c (compression fs) h- let ms = withSlurpy s $ apply fs p- case ms of- Left e -> fail e- Right (s', ()) -> writeHashedPristine c (compression fs) s'-{--applyHashed c fs h p = do (_,hd) <- runStateT (apply fs p) $- HashDir { permissions = RW, cache = c,- options = fs, rootHash = h }- return $ rootHash hd--}- data HashDir r p = HashDir { permissions :: !r, cache :: !Cache, compress :: !Compression, rootHash :: !String } type HashedIO r p = StateT (HashDir r p) IO@@ -99,7 +87,7 @@ _ -> return False mInCurrentDirectory fn j | fn' == fp2fn "" = j | otherwise =- case break_on_dir fn' of+ case breakOnDir fn' of Nothing -> do c <- readroot case geta D fn' c of Nothing -> fail "dir doesn't exist mInCurrentDirectory..."@@ -108,11 +96,11 @@ case geta D d c of Nothing -> fail "dir doesn't exist..." Just h -> inh h $ mInCurrentDirectory fn'' j- where fn' = norm_path fn+ where fn' = normPath fn mGetDirectoryContents = map (\ (_,f,_) -> f) `fmap` readroot mReadFilePS fn = mInCurrentDirectory (superName fn) $ do c <- readroot- case geta F (own_name fn) c of+ case geta F (ownName fn) c of Nothing -> fail $ " file don't exist... "++ fn2fp fn Just h -> readhash h @@ -120,7 +108,7 @@ mWithCurrentDirectory fn j | fn' == fp2fn "" = j | otherwise =- case break_on_dir fn' of+ case breakOnDir fn' of Nothing -> do c <- readroot case geta D fn' c of Nothing -> fail "dir doesn't exist in mWithCurrentDirectory..."@@ -133,7 +121,7 @@ Just h -> do (h',x) <- withh h $ mWithCurrentDirectory fn'' j writeroot $ seta D d h' c return x- where fn' = norm_path fn+ where fn' = normPath fn mSetFileExecutable _ _ = return () mWriteFilePS fn ps = do mexists <- identifyThing fn case mexists of@@ -160,22 +148,22 @@ identifyThing :: FileName -> HashedIO r p (Maybe (ObjType,String)) identifyThing fn | fn' == fp2fn "" = do h <- gets rootHash return $ Just (D, h)- | otherwise = case break_on_dir fn' of+ | otherwise = case breakOnDir fn' of Nothing -> getany fn' `fmap` readroot Just (d,fn'') -> do c <- readroot case geta D d c of Nothing -> return Nothing Just h -> inh h $ identifyThing fn''- where fn' = norm_path fn+ where fn' = normPath fn makeThing :: FileName -> (ObjType,String) -> HashedIO RW p ()-makeThing fn (o,h) = mWithCurrentDirectory (superName $ norm_path fn) $- seta o (own_name $ norm_path fn) h `fmap` readroot >>= writeroot+makeThing fn (o,h) = mWithCurrentDirectory (superName $ normPath fn) $+ seta o (ownName $ normPath fn) h `fmap` readroot >>= writeroot rmThing :: FileName -> HashedIO RW p ()-rmThing fn = mWithCurrentDirectory (superName $ norm_path fn) $+rmThing fn = mWithCurrentDirectory (superName $ normPath fn) $ do c <- readroot- let c' = filter (\(_,x,_)->x/= own_name (norm_path fn)) c+ let c' = filter (\(_,x,_)->x/= ownName (normPath fn)) c if length c' == length c - 1 then writeroot c' else fail "obj doesn't exist in rmThing"@@ -186,10 +174,6 @@ let (_,out) = z return out -readTediousHash :: String -> String -> HashedIO r p B.ByteString-readTediousHash k h = do lift $ finishedOneIO k h- readhash h- withh :: String -> HashedIO RW p a -> HashedIO RW p (String,a) withh h j = do hd <- get put $ hd { rootHash = h }@@ -205,13 +189,6 @@ put hd return x -safeInterleave :: HashedIO RO p a -> HashedIO r p a-safeInterleave job = do HashDir _ c compr h <- get- z <- lift $ unsafeInterleaveIO $ runStateT job- (HashDir { permissions = RO, cache = c, compress = compr, rootHash = h })- let (x,_) = z- return x- readroot :: HashedIO r p [(ObjType, FileName, String)] readroot = do haveitalready <- peekroot cc <- gets rootHash >>= readdir@@ -268,59 +245,6 @@ compr <- gets compress lift $ writeFileUsingCache c compr HashedPristineDir ps --- |Create a Slurpy representing the pristine content determined by the--- supplied root hash (which uniquely determines the pristine tree)-slurpHashedPristine :: Cache -> Compression -> String -> IO Slurpy-slurpHashedPristine c compr h = fst `fmap` runStateT slh- (HashDir { permissions = RO, cache = c,- compress = compr, rootHash = h })--slh :: HashedIO r p Slurpy-slh = do c <- readroot- hroot <- gets rootHash- lift $ beginTedious k- safeInterleave $ (Slurpy rootdir . SlurpDir (Just hroot) . slurpies_to_map) `fmap` mapM sl c- where sl (F,n,h) = do ps <- safeInterleave $ readTediousHash k h- let len = if length h == 75 then read (take 10 h)- else undefined_size- return $ Slurpy n $ SlurpFile (Just h, 0, len) ps- sl (D,n,h) = inh h $ do c <- readroot- lift $ tediousSize k (length c)- lift $ finishedOneIO k h- (Slurpy n . SlurpDir (Just h) . slurpies_to_map) `fmap` mapM sl c- k = "Reading pristine"--rootdir :: FileName-rootdir = fp2fn "."---- |Write contents of a Slurpy into hashed pristine. Only files that have not--- not yet been hashed (that is, the hash corresponding to their content is--- already present in hashed pristine) will be written out, so it is efficient--- to use this function to update existing pristine cache. Note that the--- pristine root hash will *not* be updated. You need to do that manually.-writeHashedPristine :: Cache -> Compression -> Slurpy -> IO String-writeHashedPristine c compr sl =- do beginTedious k- h <- fst `fmap` runStateT (hsl sl)- (HashDir { permissions = RW, cache = c,- compress = compr, rootHash = sha1PS B.empty })- endTedious k- return h- where hsl (Slurpy _ (SlurpDir (Just h) _)) = return h- hsl (Slurpy _ (SlurpDir Nothing ss)) = do lift $ tediousSize k (Map.size ss)- mapM hs (map_to_slurpies ss) >>= writedir- hsl (Slurpy _ (SlurpFile (Just h,_,_) _)) = return h- hsl (Slurpy _ (SlurpFile _ x)) = writeHashFile x- hs (Slurpy d (SlurpDir (Just h) _)) = progress k $ return (D, d, h)- hs s@(Slurpy d (SlurpDir Nothing _)) = do h <- hsl s- lift $ finishedOneIO k h- return (D, d, h)- hs (Slurpy f (SlurpFile (Just h,_,_) _)) = progress k $ return (F, f, h)- hs s@(Slurpy f (SlurpFile _ _)) = do h <- hsl s- lift $ finishedOneIO k h- return (F, f, h)- k = "Writing pristine"- copyHashed :: String -> Cache -> Compression -> String -> IO () copyHashed k c compr z = do runStateT cph $ HashDir { permissions = RO, cache = c, compress = compr, rootHash = z }@@ -355,28 +279,18 @@ lift $ writeAtomicFilePS (fn2fp f) ps Nothing -> return () --- | Seems to list all hashes reachable from "root".-listHashedContents :: String -> Cache -> String -> IO [String]-listHashedContents k c root =- do beginTedious k- tediousSize k 1- x <- fst `fmap` runStateT (lhc (D,fp2fn ".",root)) (HashDir RO c NoCompression root)- endTedious k- return x- where lhc :: (ObjType, FileName, String) -> HashedIO r a [String]- lhc (D,dname,d) = do xs <- inh d $ readroot- lift $ finishedOneIO k (fn2fp dname)- lift $ tediousSize k (length $ filter (\(x,_,_) -> x == D) xs)- hcxs <- mapM lhc xs- return (d:concat hcxs)- lhc (F,_,h) = return [h]--clean_hashdir :: Cache -> HashedDir -> [String] -> IO ()-clean_hashdir c dir_ hashroots =+cleanHashdir :: Cache -> HashedDir -> [String] -> IO ()+cleanHashdir c dir_ hashroots = do -- we'll remove obsolete bits of "dir" debugMessage $ "Cleaning out " ++ (hashedDir dir_) ++ "..." let hashdir = darcsdir ++ "/" ++ (hashedDir dir_) ++ "/"- hs <- set . concat <$> mapM (listHashedContents "cleaning up..." c) hashroots+ listone h = do let size = decodeDarcsSize $ BC.pack h+ hash = decodeDarcsHash $ BC.pack h+ x <- readDarcsHashedDir hashdir (size, hash)+ let subs = [ fst $ darcsLocation "" (s, h) | (TreeType, _, s, h) <- x ]+ hashes = h : [ fst $ darcsLocation "" (s, h) | (_, _, s, h) <- x ]+ (hashes++) . concat <$> mapM listone subs+ hs <- set . concat <$> mapM listone hashroots fs <- set . filter okayHash <$> getDirectoryContents hashdir mapM_ (removeFileMayNotExist . (hashdir++)) (unset $ fs `Set.difference` hs) -- and also clean out any global caches.
src/Darcs/Repository/HashedRepo.hs view
@@ -19,42 +19,46 @@ #include "gadts.h" -module Darcs.Repository.HashedRepo ( revert_tentative_changes, finalize_tentative_changes,- slurp_pristine, clean_pristine,- copy_pristine, copy_partials_pristine,- apply_to_tentative_pristine,- add_to_tentative_inventory, remove_from_tentative_inventory,- read_repo, read_tentative_repo, write_and_read_patch,- write_tentative_inventory, copy_repo, slurp_all_but_darcs,- readHashedPristineRoot, pris2inv+module Darcs.Repository.HashedRepo ( revertTentativeChanges, finalizeTentativeChanges,+ cleanPristine,+ copyPristine, copyPartialsPristine,+ applyToTentativePristine,+ addToTentativeInventory, removeFromTentativeInventory,+ readRepo, readTentativeRepo, writeAndReadPatch,+ writeTentativeInventory, copyRepo,+ readHashedPristineRoot, pris2inv, copySources ) where -import System.Directory ( doesFileExist, createDirectoryIfMissing )+import System.Directory ( createDirectoryIfMissing )+import System.FilePath.Posix( (</>) ) import System.IO.Unsafe ( unsafeInterleaveIO ) import System.IO ( stderr, hPutStrLn )-import Data.List ( delete )+import Data.List ( delete, filter ) import Control.Monad ( unless ) import Workaround ( renameFile ) import Darcs.Flags ( DarcsFlag, Compression )-import Darcs.Patch.Set ( PatchSet, SealedPatchSet )+import Darcs.Patch.Set ( PatchSet(..), Tagged(..), SealedPatchSet )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif import Darcs.RepoPath ( FilePathLike, ioAbsoluteOrRemote, toPath )-import Darcs.Repository.Cache ( Cache, fetchFileUsingCache, speculateFileUsingCache,- writeFileUsingCache,+import Darcs.Repository.Cache ( Cache(..), CacheLoc(..), fetchFileUsingCache,+ speculateFilesUsingCache, writeFileUsingCache, unionCaches, repo2cache, okayHash, takeHash,- HashedDir(..), hashedDir )-import Darcs.Repository.HashedIO ( applyHashed, slurpHashedPristine,- copyHashed, copyPartialsHashed,- clean_hashdir )-import Darcs.Repository.InternalTypes ( Repository(..), extractCache )-import Darcs.Hopefully ( PatchInfoAnd, patchInfoAndPatch, n2pia, info,+ HashedDir(..),WritableOrNot(..), hashedDir,+ peekInCache )+import qualified Darcs.Repository.Cache as DarcsCache+import Darcs.Repository.HashedIO ( copyHashed, copyPartialsHashed,+ cleanHashdir )+import Darcs.Repository.InternalTypes ( Repository(..), extractCache, modifyCache )+import Darcs.Repository.Prefs ( globalCacheDir )+import Darcs.Hopefully ( PatchInfoAnd, patchInfoAndPatch, info, extractHash, createHashed )-import Darcs.SlurpDirectory ( Slurpy, empty_slurpy, slurp_remove, slurp )-import Darcs.Patch ( RepoPatch, Patchy, Named, showPatch, patch2patchinfo, readPatch )-import Darcs.Patch.Depends ( commute_to_end, slightly_optimize_patchset )-import Darcs.Patch.Info ( PatchInfo, showPatchInfo, human_friendly, readPatchInfo )-import Darcs.Witnesses.Ordered ( unsafeCoerceP, (:<)(..) )-import Darcs.Patch.FileName ( fp2fn )+import Darcs.Patch ( RepoPatch, Patchy, showPatch, readPatch, apply )+import Darcs.Patch.Depends ( commuteToEnd, slightlyOptimizePatchset )+import Darcs.Patch.Info ( PatchInfo, showPatchInfo, humanFriendly, readPatchInfo )+import Darcs.Witnesses.Ordered ( unsafeCoerceP, reverseRL, reverseFL, (+<+) ) import ByteStringUtils ( gzReadFilePS, dropSpace ) import qualified Data.ByteString as B (null, length, empty@@ -62,30 +66,62 @@ import qualified Data.ByteString.Char8 as BC (unpack, dropWhile, break, pack) import Printer ( Doc, hcat, (<>), ($$), renderString, renderPS, text, invisiblePS )-import SHA1 ( sha1PS )+import Crypt.SHA256 ( sha256sum ) import Darcs.External ( copyFileOrUrl, cloneFile, fetchFilePS, Cachable( Uncachable ) ) import Darcs.Lock ( writeBinFile, writeDocBinFile, writeAtomicFilePS, appendBinFile, appendDocBinFile ) import Darcs.Utils ( withCurrentDirectory )-import Progress ( beginTedious, tediousSize, endTedious, debugMessage, finishedOneIO )+import Progress ( beginTedious, endTedious, debugMessage, finishedOneIO ) #include "impossible.h"-import Darcs.Witnesses.Ordered ( FL(..), RL(..),- mapRL, mapFL, lengthRL )-import Darcs.Witnesses.Sealed ( Sealed(..), seal, unseal )+import Darcs.Witnesses.Ordered ( FL(..), RL(..), (:>>)(..), mapRL, mapFL )+import Darcs.Witnesses.Sealed ( Sealed(..), seal, unseal, mapSeal ) import Darcs.Global ( darcsdir ) -revert_tentative_changes :: IO ()-revert_tentative_changes =+import Storage.Hashed.Darcs( hashedTreeIO, readDarcsHashedNosize, readDarcsHashed,+ writeDarcsHashed, readDarcsHashedDir, darcsLocation,+ decodeDarcsHash, decodeDarcsSize )+import Storage.Hashed.Tree( treeHash, ItemType(..) )+import Storage.Hashed.Hash( encodeBase16, Hash(..) )+++applyHashed' fs root p = do case root of+ (SHA256 _) -> return ()+ _ -> fail $ "Cannot handle hash: " ++ show root+ s <- readDarcsHashedNosize "_darcs/pristine.hashed" root+ (_, t) <- (hashedTreeIO (apply fs p) s "_darcs/pristine.hashed")+ return $ BC.unpack . encodeBase16 $ treeHash t++applyHashed :: Patchy q => [DarcsFlag] -> String -> q C(x y) -> IO String+applyHashed fs h p = applyHashed' fs hash p `catch` \_ -> do+ hPutStrLn stderr warn+ inv <- gzReadFilePS invpath+ let oldroot = BC.pack $ inv2pris inv+ oldroot_hash = decodeDarcsHash oldroot+ oldroot_size = decodeDarcsSize oldroot+ old <- readDarcsHashed "_darcs/pristine.hashed" (oldroot_size, oldroot_hash)+ root <- writeDarcsHashed old "_darcs/pristine.hashed"+ let newroot = BC.unpack $ encodeBase16 root+ writeDocBinFile invpath $ pris2inv newroot inv+ cleanHashdir (Ca []) HashedPristineDir [newroot]+ hPutStrLn stderr "Pristine conversion done..."+ applyHashed' fs root p+ where invpath = darcsdir ++ "/hashed_inventory"+ hash = decodeDarcsHash $ BC.pack h+ warn = "WARNING: Doing a one-time conversion of pristine format.\n" +++ "This may take a while. The new format is backwards-compatible."++revertTentativeChanges :: IO ()+revertTentativeChanges = do cloneFile (darcsdir++"/hashed_inventory") (darcsdir++"/tentative_hashed_inventory") i <- gzReadFilePS (darcsdir++"/hashed_inventory") writeBinFile (darcsdir++"/tentative_pristine") $ "pristine:" ++ inv2pris i -finalize_tentative_changes :: RepoPatch p => Repository p C(r u t) -> Compression -> IO ()-finalize_tentative_changes r compr =+finalizeTentativeChanges :: RepoPatch p => Repository p C(r u t) -> Compression -> IO ()+finalizeTentativeChanges r compr = do let t = darcsdir++"/tentative_hashed_inventory" -- first let's optimize it... debugMessage "Optimizing the inventory..."- ps <- read_tentative_repo r "."- write_tentative_inventory (extractCache r) compr ps+ ps <- readTentativeRepo r "."+ writeTentativeInventory (extractCache r) compr ps -- then we'll add in the pristine cache, i <- gzReadFilePS t p <- gzReadFilePS $ darcsdir++"/tentative_pristine"@@ -101,80 +137,84 @@ i <- (Just `fmap` gzReadFilePS (darcsdir++"/hashed_inventory")) `catch` (\_ -> return Nothing) return $ inv2pris `fmap` i -clean_pristine :: Repository p C(r u t) -> IO ()-clean_pristine r@(Repo d _ _ _) = withCurrentDirectory d $+cleanPristine :: Repository p C(r u t) -> IO ()+cleanPristine r@(Repo d _ _ _) = withCurrentDirectory d $ do -- we'll remove obsolete bits of our pristine cache debugMessage "Cleaning out the pristine cache..." i <- gzReadFilePS (darcsdir++"/hashed_inventory")- clean_hashdir (extractCache r) HashedPristineDir [inv2pris i]+ cleanHashdir (extractCache r) HashedPristineDir [inv2pris i] -add_to_tentative_inventory :: RepoPatch p => Cache -> Compression -> PatchInfoAnd p C(x y) -> IO FilePath-add_to_tentative_inventory c compr p =- do hash <- snd `fmap` write_patch_if_necesary c compr p+addToTentativeInventory :: RepoPatch p => Cache -> Compression+ -> PatchInfoAnd p C(x y) -> IO FilePath+addToTentativeInventory c compr p =+ do hash <- snd `fmap` writePatchIfNecesary c compr p appendDocBinFile (darcsdir++"/tentative_hashed_inventory") $ showPatchInfo $ info p appendBinFile (darcsdir++"/tentative_hashed_inventory") $ "\nhash: " ++ hash ++ "\n" return $ darcsdir++"/patches/" ++ hash -remove_from_tentative_inventory :: RepoPatch p => Repository p C(r u t) -> Compression- -> FL (Named p) C(x t) -> IO ()-remove_from_tentative_inventory repo compr to_remove =+removeFromTentativeInventory :: RepoPatch p => Repository p C(r u t) -> Compression+ -> FL (PatchInfoAnd p) C(x t) -> IO ()+removeFromTentativeInventory repo compr to_remove = -- FIXME: This algorithm should be *far* simpler. All we need do is -- to to remove the patches from a patchset and then write that -- patchset. The commutation behavior of PatchInfoAnd should track -- which patches need to be rewritten for us.- do allpatches <- read_tentative_repo repo "."- skipped :< _ <- return $ commute_to_end to_remove allpatches- okay <- simple_remove_from_tentative_inventory repo compr- (mapFL patch2patchinfo to_remove ++ mapFL patch2patchinfo skipped)- unless okay $ bug "bug in HashedRepo.remove_from_tentative_inventory"- sequence_ $ mapFL (add_to_tentative_inventory (extractCache repo) compr . n2pia) skipped+ do allpatches <- readTentativeRepo repo "."+ _ :>> skipped <- return $ commuteToEnd (reverseFL to_remove) allpatches+ okay <- simpleRemoveFromTentativeInventory repo compr+ (mapFL info to_remove ++ mapRL info skipped)+ unless okay $ bug "bug in HashedRepo.removeFromTentativeInventory"+ sequence_ $ mapFL (addToTentativeInventory (extractCache repo) compr) (reverseRL skipped) -simple_remove_from_tentative_inventory :: forall p C(r u t). RepoPatch p =>+simpleRemoveFromTentativeInventory :: forall p C(r u t). RepoPatch p => Repository p C(r u t) -> Compression -> [PatchInfo] -> IO Bool-simple_remove_from_tentative_inventory repo compr pis = do- inv <- read_tentative_repo repo "."+simpleRemoveFromTentativeInventory repo compr pis = do+ inv <- readTentativeRepo repo "." case cut_inv pis inv of Nothing -> return False- Just (Sealed inv') -> do write_tentative_inventory (extractCache repo) compr inv'+ Just (Sealed inv') -> do writeTentativeInventory (extractCache repo) compr inv' return True- where cut_inv :: [PatchInfo] -> PatchSet p C(x) -> Maybe (SealedPatchSet p)+ where cut_inv :: [PatchInfo] -> PatchSet p C(start x) -> Maybe (SealedPatchSet p C(start)) cut_inv [] x = Just $ seal x- cut_inv x (NilRL:<:rs) = cut_inv x rs- cut_inv xs ((hp:<:r):<:rs) | info hp `elem` xs = cut_inv (info hp `delete` xs) (r:<:rs)+ cut_inv x (PatchSet NilRL (Tagged t _ ps :<: ts))+ = cut_inv x (PatchSet (t :<: ps) ts)+ cut_inv xs (PatchSet (hp:<:r) ts)+ | info hp `elem` xs = cut_inv (info hp `delete` xs) (PatchSet r ts) cut_inv _ _ = Nothing writeHashFile :: Cache -> Compression -> HashedDir -> Doc -> IO String writeHashFile c compr subdir d = do debugMessage $ "Writing hash file to "++(hashedDir subdir) writeFileUsingCache c compr subdir $ renderPS d -read_repo :: RepoPatch p => Repository p C(r u t) -> String -> IO (PatchSet p C(r))-read_repo repo d = do+readRepo :: RepoPatch p => Repository p C(r u t) -> String -> IO (PatchSet p C(Origin r))+readRepo repo d = do realdir <- toPath `fmap` ioAbsoluteOrRemote d- Sealed ps <- read_repo_private repo realdir "hashed_inventory" `catch`+ Sealed ps <- readRepoPrivate (extractCache repo) realdir "hashed_inventory" `catch` (\e -> do hPutStrLn stderr ("Invalid repository: " ++ realdir) ioError e) return $ unsafeCoerceP ps -read_tentative_repo :: RepoPatch p => Repository p C(r u t) -> String -> IO (PatchSet p C(t))-read_tentative_repo repo d = do+readTentativeRepo :: RepoPatch p => Repository p C(r u t) -> String -> IO (PatchSet p C(Origin t))+readTentativeRepo repo d = do realdir <- toPath `fmap` ioAbsoluteOrRemote d- Sealed ps <- read_repo_private repo realdir "tentative_hashed_inventory" `catch`+ Sealed ps <- readRepoPrivate (extractCache repo) realdir "tentative_hashed_inventory" `catch` (\e -> do hPutStrLn stderr ("Invalid repository: " ++ realdir) ioError e) return $ unsafeCoerceP ps -read_repo_private :: RepoPatch p => Repository p C(r u t)- -> FilePath -> FilePath -> IO (SealedPatchSet p)-read_repo_private repo d iname =- do inventories <- read_inventory_private repo (d++"/"++darcsdir) iname- parseinvs inventories- where read_patches :: RepoPatch p => [(PatchInfo, String)] -> IO (Sealed (RL (PatchInfoAnd p) C(x)))+readRepoPrivate :: RepoPatch p => Cache -> FilePath -> FilePath -> IO (SealedPatchSet p C(Origin))+readRepoPrivate cache d iname =+ do inventory <- readInventoryPrivate cache (d </> "_darcs") iname+ parseinvs inventory+ where read_patches :: RepoPatch p => [(PatchInfo, String)]+ -> IO (Sealed (RL (PatchInfoAnd p) C(x))) read_patches [] = return $ seal NilRL read_patches allis@((i1,h1):is1) = lift2Sealed (\p rest -> i1 `patchInfoAndPatch` p :<: rest) (rp is1) (createHashed h1 (const $ speculate h1 allis >> parse i1 h1))- where rp :: RepoPatch p => [(PatchInfo, String)] -> IO (Sealed (RL (PatchInfoAnd p) C(x)))+ where rp :: RepoPatch p => [(PatchInfo, String)]+ -> IO (Sealed (RL (PatchInfoAnd p) C(x))) rp [] = return $ seal NilRL rp [(i,h),(il,hl)] = lift2Sealed (\p rest -> i `patchInfoAndPatch` p :<: rest)@@ -183,85 +223,145 @@ rp ((i,h):is) = lift2Sealed (\p rest -> i `patchInfoAndPatch` p :<: rest) (rp is) (createHashed h (parse i))+ read_tag :: RepoPatch p => (PatchInfo, String) -> IO (Sealed (PatchInfoAnd p C(x)))+ read_tag (i,h) = mapSeal (patchInfoAndPatch i) `fmap` createHashed h (parse i) speculate :: String -> [(PatchInfo, String)] -> IO ()- speculate h is = do already_got_one <- doesFileExist (d++"/"++darcsdir++"/patches/"++h)- unless already_got_one $- mapM_ (speculateFileUsingCache (extractCache repo) HashedPatchesDir . snd) is+ speculate h is =+ do already_got_one <- peekInCache cache HashedPatchesDir h+ unless already_got_one $+ speculateFilesUsingCache cache HashedPatchesDir (map snd is) parse :: Patchy p => PatchInfo -> String -> IO (Sealed (p C(x)))- parse i h = do debugMessage ("Reading patch file: "++ show (human_friendly i))- (fn,ps) <- fetchFileUsingCache (extractCache repo) HashedPatchesDir h+ parse i h = do debugMessage ("Reading patch file: "++ show (humanFriendly i))+ (fn,ps) <- fetchFileUsingCache cache HashedPatchesDir h case readPatch ps of Just (p,_) -> return p Nothing -> fail $ unlines ["Couldn't parse file "++fn, "which is patch",- renderString $ human_friendly i]- parseinvs :: RepoPatch p => [[(PatchInfo, String)]] -> IO (SealedPatchSet p)- parseinvs [] = return $ seal NilRL- parseinvs (i:is) = lift2Sealed (:<:) (parseinvs is) (read_patches i)+ renderString $ humanFriendly i]+ parseinvs :: RepoPatch p => (Maybe String, [(PatchInfo, String)])+ -> IO (SealedPatchSet p C(Origin))+ parseinvs (Nothing, ris) = mapSeal (\ps -> PatchSet ps NilRL)+ `fmap` (read_patches $ reverse ris)+ parseinvs (Just h, []) = bug $ "bad inventory "++h++" (no tag) in parseinvs!"+ parseinvs (Just h, t:ris) = do Sealed ts <- unseal seal `fmap`+ unsafeInterleaveIO (read_ts t h)+ Sealed ps <- unseal seal `fmap`+ unsafeInterleaveIO (read_patches $+ reverse ris)+ return $ seal $ PatchSet ps ts+ read_ts :: RepoPatch p => (PatchInfo, String) -> String -> IO (Sealed (RL (Tagged p) C(Origin)))+ read_ts tag0 h0 =+ do contents <- unsafeInterleaveIO $ readTaggedInventory cache h0+ let is = reverse $ case contents of (Just _, _:ris0) -> ris0+ (Nothing, ris0) -> ris0+ (Just _, []) -> bug "inventory without tag!!!!"+ Sealed ts <- fmap (unseal seal) $ unsafeInterleaveIO $+ case contents of+ (Just h', t':_) -> read_ts t' h'+ (Just _, []) -> bug "inventory without tag!!!!"+ (Nothing, _) -> return $ seal NilRL+ Sealed ps <- unseal seal `fmap` unsafeInterleaveIO (read_patches is)+ Sealed tag00 <- read_tag tag0+ return $ seal $ Tagged tag00 (Just h0) ps :<: ts lift2Sealed :: (FORALL(y z) q C(y z) -> p C(x y) -> r C(x z))- -> IO (Sealed (p C(x))) -> (FORALL(b) IO (Sealed (q C(b)))) -> IO (Sealed (r C(x)))- lift2Sealed f iox ioy = do Sealed x <- unseal seal `fmap` unsafeInterleaveIO iox- Sealed y <- unseal seal `fmap` unsafeInterleaveIO ioy+ -> IO (Sealed (p C(x))) -> (FORALL(b) IO (Sealed (q C(b))))+ -> IO (Sealed (r C(x)))+ lift2Sealed f iox ioy = do Sealed x <- unseal seal `fmap`+ unsafeInterleaveIO iox+ Sealed y <- unseal seal `fmap`+ unsafeInterleaveIO ioy return $ seal $ f y x -write_and_read_patch :: RepoPatch p => Cache -> Compression -> PatchInfoAnd p C(x y)+readTaggedInventory :: Cache -> String -> IO (Maybe String, [(PatchInfo, String)])+readTaggedInventory cache ihash = do+ (fn,i_and_p) <- fetchFileUsingCache cache HashedInventoriesDir ihash+ let i = skipPristine i_and_p+ (rest,str) <- case BC.break ((==)'\n') i of+ (swt,pistr) | swt == BC.pack "Starting with inventory:" ->+ case BC.break ((==)'\n') $ B.tail pistr of+ (h,thisinv) | okayHash hash -> return (Just hash, thisinv)+ where hash = BC.unpack h+ _ -> fail $ "Bad hash in file " ++ fn+ _ -> return (Nothing,i)+ return (rest, readPatchIds str)++copyRepo :: RepoPatch p => Repository p C(r u t) -> [DarcsFlag] -> String -> IO ()+copyRepo repo@(Repo outr _ _ _) opts inr = do+ createDirectoryIfMissing False (outr++"/"++darcsdir++"/inventories")+ copyFileOrUrl opts (inr++"/"++darcsdir++"/hashed_inventory") (outr++"/"++darcsdir++"/hashed_inventory")+ Uncachable -- no need to copy anything but hashed_inventory!+ copySources repo inr+ debugMessage "Done copying hashed inventory."++copySources :: RepoPatch p => Repository p C(r u t) -> String -> IO ()+copySources repo@(Repo outr _ _ _) inr = do+ let repoCache = extractCache $ modifyCache repo dropGlobalCaches+ appendBinFile (outr++"/"++darcsdir++"/prefs/sources") (show $ repo2cache inr `unionCaches` repoCache )+ where+ dropGlobalCaches (Ca cache) = Ca $ filter notGlobalCache cache+ notGlobalCache xs = case xs of+ Cache DarcsCache.Directory _ _ -> False+ _ -> True++writeAndReadPatch :: RepoPatch p => Cache -> Compression -> PatchInfoAnd p C(x y) -> IO (PatchInfoAnd p C(x y))-write_and_read_patch c compr p = do (i,h) <- write_patch_if_necesary c compr p+writeAndReadPatch c compr p = do (i,h) <- writePatchIfNecesary c compr p unsafeInterleaveIO $ readp h i- where parse i h = do debugMessage ("Rereading patch file: "++ show (human_friendly i))+ where parse i h = do debugMessage ("Rereading patch file: "++ show (humanFriendly i)) (fn,ps) <- fetchFileUsingCache c HashedPatchesDir h case readPatch ps of Just (x,_) -> return x Nothing -> fail $ unlines ["Couldn't parse patch file "++fn, "which is",- renderString $ human_friendly i]+ renderString $ humanFriendly i] readp h i = do Sealed x <- createHashed h (parse i) return $ patchInfoAndPatch i $ unsafeCoerceP x -write_tentative_inventory :: RepoPatch p => Cache -> Compression -> PatchSet p C(x) -> IO ()-write_tentative_inventory c compr = write_either_inventory c compr "tentative_hashed_inventory"--copy_repo :: RepoPatch p => Repository p C(r u t) -> [DarcsFlag] -> String -> IO ()-copy_repo repo@(Repo outr _ _ _) opts inr = do- createDirectoryIfMissing False (outr++"/"++darcsdir++"/inventories")- copyFileOrUrl opts (inr++"/"++darcsdir++"/hashed_inventory") (outr++"/"++darcsdir++"/hashed_inventory")- Uncachable -- no need to copy anything but hashed_inventory!- appendBinFile (outr++"/"++darcsdir++"/prefs/sources") (show $ repo2cache inr `unionCaches` extractCache repo)- debugMessage "Done copying hashed inventory."+writeTentativeInventory :: RepoPatch p => Cache -> Compression -> PatchSet p C(Origin x) -> IO ()+writeTentativeInventory cache compr = writeEitherInventory cache compr "tentative_hashed_inventory" -write_either_inventory :: RepoPatch p => Cache -> Compression -> String -> PatchSet p C(x) -> IO ()-write_either_inventory c compr iname x =- do createDirectoryIfMissing False $ darcsdir++"/inventories"+writeEitherInventory :: RepoPatch p => Cache -> Compression -> String -> PatchSet p C(Origin x) -> IO ()+writeEitherInventory cache compr iname x =+ do debugMessage "in writeEitherInventory..."+ createDirectoryIfMissing False $ "_darcs/inventories" let k = "Writing inventory" beginTedious k- tediousSize k (lengthRL x)- hsh <- write_inventory_private k c compr $ slightly_optimize_patchset x+ hsh <- writeInventoryPrivate cache k compr $ slightlyOptimizePatchset x endTedious k+ debugMessage "still in writeEitherInventory..." case hsh of- Nothing -> writeBinFile (darcsdir++"/"++iname) ""- Just h -> gzReadFilePS (darcsdir++"/inventories/"++h) >>= writeAtomicFilePS (darcsdir++"/"++iname)+ Nothing -> writeBinFile ("_darcs" </> iname) ""+ Just h -> fmap snd (fetchFileUsingCache cache HashedInventoriesDir h)+ >>= writeAtomicFilePS ("_darcs" </> iname) -write_inventory_private :: RepoPatch p => String -> Cache -> Compression- -> PatchSet p C(x) -> IO (Maybe String)-write_inventory_private _ _ _ NilRL = return Nothing-write_inventory_private _ _ _ (NilRL:<:NilRL) = return Nothing-write_inventory_private _ _ _ (NilRL:<:_) = -- This shouldn't be possible, so best to check...- bug "malformed PatchSet in HashedRepo.write_inventory_private"-write_inventory_private k c compr (x:<:xs) =- do resthash <- write_inventory_private k c compr xs- finishedOneIO k (case resthash of Nothing -> ""; Just h -> h)- inventory <- sequence $ mapRL (write_patch_if_necesary c compr) x+writeInventoryPrivate :: RepoPatch p => Cache -> String -> Compression+ -> PatchSet p C(Origin x) -> IO (Maybe String)+writeInventoryPrivate _ _ _ (PatchSet NilRL NilRL) = return Nothing+writeInventoryPrivate cache _ compr (PatchSet x NilRL) =+ do inventory <- sequence $ mapRL (writePatchIfNecesary cache compr) x let inventorylist = hcat (map pihash $ reverse inventory)- inventorycontents = case resthash of- Just lasthash -> text ("Starting with inventory:\n"++lasthash) $$- inventorylist- _ -> inventorylist- hash <- writeHashFile c compr HashedInventoriesDir inventorycontents+ hash <- writeHashFile cache compr HashedInventoriesDir inventorylist return $ Just hash+writeInventoryPrivate cache k compr (PatchSet x xs@(Tagged t _ _ :<: _)) =+ do resthash <- write_ts xs+ finishedOneIO k $ maybe "" id resthash+ inventory <- sequence $ mapRL (writePatchIfNecesary cache compr) (x+<+t:<:NilRL)+ let inventorylist = hcat (map pihash $ reverse inventory)+ inventorycontents =+ case resthash of+ Just h -> text ("Starting with inventory:\n"++h) $$ inventorylist+ Nothing -> inventorylist+ hash <- writeHashFile cache compr HashedInventoriesDir inventorycontents+ return $ Just hash+ where write_ts :: RepoPatch p => RL (Tagged p) C(Origin x) -> IO (Maybe String)+ write_ts (Tagged _ (Just h) _ :<: _) = return (Just h) -- already written!+ write_ts (Tagged _ Nothing pps :<: tts) =+ writeInventoryPrivate cache k compr $ PatchSet pps tts+ write_ts NilRL = return Nothing -write_patch_if_necesary :: RepoPatch p => Cache -> Compression+writePatchIfNecesary :: RepoPatch p => Cache -> Compression -> PatchInfoAnd p C(x y) -> IO (PatchInfo, String)-write_patch_if_necesary c compr hp =+writePatchIfNecesary c compr hp = seq infohp $ case extractHash hp of Right h -> return (infohp, h) Left p -> (\h -> (infohp, h)) `fmap`@@ -271,44 +371,41 @@ pihash :: (PatchInfo,String) -> Doc pihash (pinf,hash) = showPatchInfo pinf $$ text ("hash: " ++ hash ++ "\n") -read_inventory_private :: Repository p C(r u t) -> String -> String- -> IO [[(PatchInfo, String)]]-read_inventory_private repo d iname = do- i <- skip_pristine `fmap` fetchFilePS (d++"/"++iname) Uncachable+readInventoryPrivate :: Cache -> String -> String -> IO (Maybe String, [(PatchInfo, String)])+readInventoryPrivate _ d iname = do+ i <- skipPristine `fmap` fetchFilePS (d </> iname) Uncachable (rest,str) <- case BC.break ((==)'\n') i of (swt,pistr) | swt == BC.pack "Starting with inventory:" -> case BC.break ((==)'\n') $ B.tail pistr of- (h,thisinv) | okayHash $ BC.unpack h ->- do r <- unsafeInterleaveIO $ read_inventories- (extractCache repo) (BC.unpack h) -- don't unpack twice!- return (r,thisinv)- _ -> fail $ "Bad hash in " ++ d ++ "/"++darcsdir++"/" ++ iname- _ -> return ([],i)- return $ reverse (read_patch_ids str) : rest+ (h,thisinv) | okayHash hash -> return (Just hash, thisinv)+ where hash = BC.unpack h+ _ -> fail $ "Bad hash in " ++ toPath d ++ "/_darcs/" ++ iname+ _ -> return (Nothing, i)+ return (rest, readPatchIds str) -read_inventories :: Cache -> String -> IO [[(PatchInfo, String)]]-read_inventories cache ihash = do+readInventories :: Cache -> String -> IO [[(PatchInfo, String)]]+readInventories cache ihash = do (fn,i_and_p) <- fetchFileUsingCache cache HashedInventoriesDir ihash- let i = skip_pristine i_and_p+ let i = skipPristine i_and_p (rest,str) <- case BC.break ((==)'\n') i of (swt,pistr) | swt == BC.pack "Starting with inventory:" -> case BC.break ((==)'\n') $ B.tail pistr of (h,thisinv) | okayHash $ BC.unpack h -> do r <- unsafeInterleaveIO $- read_inventories cache (BC.unpack h) -- again. no.+ readInventories cache (BC.unpack h) -- again. no. return (r,thisinv) _ -> fail $ "Bad hash in file " ++ fn _ -> return ([],i)- return $ reverse (read_patch_ids str) : rest+ return $ reverse (readPatchIds str) : rest -read_patch_ids :: B.ByteString -> [(PatchInfo, String)]-read_patch_ids inv | B.null inv = []-read_patch_ids inv = case readPatchInfo inv of+readPatchIds :: B.ByteString -> [(PatchInfo, String)]+readPatchIds inv | B.null inv = []+readPatchIds inv = case readPatchInfo inv of Nothing -> [] Just (pinfo,r) -> case readHash r of Nothing -> []- Just (h,r') -> (pinfo,h) : read_patch_ids r'+ Just (h,r') -> (pinfo,h) : readPatchIds r' readHash :: B.ByteString -> Maybe (String, B.ByteString) readHash s = let s' = dropSpace s@@ -318,27 +415,17 @@ then Nothing else Just (BC.unpack $ B.tail h,r) -apply_pristine :: Patchy q => Cache -> [DarcsFlag] -> String -> String -> q C(x y) -> IO ()-apply_pristine c opts d iname p =+applyPristine :: Patchy q => [DarcsFlag] -> String -> String -> q C(x y) -> IO ()+applyPristine opts d iname p = do i <- gzReadFilePS (d++"/"++iname)- h <- applyHashed c opts (inv2pris i) p+ h <- applyHashed opts (inv2pris i) p writeDocBinFile (d++"/"++iname) $ pris2inv h i -apply_to_tentative_pristine :: Patchy q => Cache -> [DarcsFlag] -> q C(x y) -> IO ()-apply_to_tentative_pristine c opts p = apply_pristine c opts "." (darcsdir++"/tentative_pristine") p--slurp_pristine :: Cache -> Compression -> String -> String -> IO Slurpy-slurp_pristine c compr d iname = do- i <- fetchFilePS (d++"/"++iname) Uncachable- slurp_pristine_private c compr i--slurp_pristine_private :: Cache -> Compression -> B.ByteString -> IO Slurpy-slurp_pristine_private c compr inv = case inv2pris inv of- h | h == sha1PS B.empty -> return empty_slurpy- | otherwise -> slurpHashedPristine c compr h+applyToTentativePristine :: Patchy q => [DarcsFlag] -> q C(x y) -> IO ()+applyToTentativePristine opts p = applyPristine opts "." (darcsdir++"/tentative_pristine") p -copy_pristine :: Cache -> Compression -> String -> String -> IO ()-copy_pristine c compr d iname = do+copyPristine :: Cache -> Compression -> String -> String -> IO ()+copyPristine c compr d iname = do i <- fetchFilePS (d++"/"++iname) Uncachable debugMessage $ "Copying hashed pristine tree: "++inv2pris i let k = "Copying pristine"@@ -346,36 +433,30 @@ copyHashed k c compr $ inv2pris i endTedious k -copy_partials_pristine :: FilePathLike fp =>+copyPartialsPristine :: FilePathLike fp => Cache -> Compression -> String -> String -> [fp] -> IO ()-copy_partials_pristine c compr d iname fps =+copyPartialsPristine c compr d iname fps = do i <- fetchFilePS (d++"/"++iname) Uncachable copyPartialsHashed c compr (inv2pris i) fps inv2pris :: B.ByteString -> String-inv2pris inv | B.take pristine_name_length inv == pristine_name =- case takeHash $ B.drop pristine_name_length inv of+inv2pris inv | B.take pristineNameLength inv == pristineName =+ case takeHash $ B.drop pristineNameLength inv of Just (h,_) -> h Nothing -> error "Bad hash in inventory!"- | otherwise = sha1PS B.empty+ | otherwise = sha256sum B.empty pris2inv :: String -> B.ByteString -> Doc-pris2inv h inv = invisiblePS pristine_name <> text h $$ invisiblePS (skip_pristine inv)+pris2inv h inv = invisiblePS pristineName <> text h $$ invisiblePS (skipPristine inv) -pristine_name :: B.ByteString-pristine_name = BC.pack "pristine:"+pristineName :: B.ByteString+pristineName = BC.pack "pristine:" -skip_pristine :: B.ByteString -> B.ByteString-skip_pristine ps- | B.take pristine_name_length ps == pristine_name = B.drop 1 $ BC.dropWhile (/= '\n') $- B.drop pristine_name_length ps+skipPristine :: B.ByteString -> B.ByteString+skipPristine ps+ | B.take pristineNameLength ps == pristineName = B.drop 1 $ BC.dropWhile (/= '\n') $+ B.drop pristineNameLength ps | otherwise = ps -pristine_name_length :: Int-pristine_name_length = B.length pristine_name--slurp_all_but_darcs :: FilePath -> IO Slurpy-slurp_all_but_darcs d = do s <- slurp d- case slurp_remove (fp2fn $ "./"++darcsdir) s of- Nothing -> return s- Just s' -> return s'+pristineNameLength :: Int+pristineNameLength = B.length pristineName
src/Darcs/Repository/Internal.hs view
@@ -23,13 +23,13 @@ module Darcs.Repository.Internal ( Repository(..), RepoType(..), RIO(unsafeUnRIO), ($-), maybeIdentifyRepository, identifyDarcs1Repository, identifyRepositoryFor,+ IdentifyRepo(..), findRepository, amInRepository, amNotInRepository,- slurp_pending, revertRepositoryChanges,- slurp_recorded, slurp_recorded_and_unrecorded,- announce_merge_conflicts, setTentativePending,- check_unrecorded_conflicts,+ revertRepositoryChanges,+ announceMergeConflicts, setTentativePending,+ checkUnrecordedConflicts, withRecorded,- read_repo,+ readRepo, readTentativeRepo, prefsUrl, makePatchLazy, withRepoLock, withRepoReadLock, withRepository, withRepositoryDirectory, withGutsOf,@@ -47,29 +47,27 @@ getRepository, rIO, testTentative, testRecorded, UpdatePristine(..), MakeChanges(..), applyToTentativePristine,- make_new_pending+ makeNewPending ) where import Printer ( putDocLn, (<+>), text, ($$) ) -import Data.Maybe ( isJust, isNothing ) import Darcs.Repository.Prefs ( getPrefval )-import Darcs.Repository.State ( readRecorded )-import Darcs.Repository.LowLevel ( read_pending, pendingName, readPrims, read_pendingfile )+import Darcs.Repository.State ( readRecorded, readWorking )+import Darcs.Repository.LowLevel ( readPending, pendingName, readPrims, readPendingfile ) import System.Exit ( ExitCode(..), exitWith ) import System.Cmd ( system ) import Darcs.External ( clonePartialsTree ) import Darcs.IO ( runTolerantly, runSilently ) import Darcs.Repository.Pristine ( identifyPristine, nopristine,- easyCreatePristineDirectoryTree, slurpPristine,+ easyCreatePristineDirectoryTree, easyCreatePartialsPristineDirectoryTree ) -import Data.List ( (\\) ) import Darcs.SignalHandler ( withSignalsBlocked ) import Darcs.Repository.Format ( RepoFormat, RepoProperty( Darcs2, HashedInventory ), identifyRepoFormat, formatHas, writeProblem, readProblem, readfromAndWritetoProblem )-import System.Directory ( doesDirectoryExist, setCurrentDirectory, removeFile,+import System.Directory ( doesDirectoryExist, setCurrentDirectory, createDirectoryIfMissing ) import Control.Monad ( liftM, when, unless ) import Workaround ( getCurrentDirectory, renameFile, setExecutable )@@ -80,66 +78,69 @@ import Darcs.Patch ( Patch, RealPatch, Effect, primIsHunk, primIsBinary, description, - tryToShrink, commuteFL, commute )-import Darcs.Patch.Prim ( try_shrinking_inverse, Conflict )-import Darcs.Patch.Bundle ( scan_bundle, make_bundle )-import Darcs.SlurpDirectory ( Slurpy, mmap_slurp, co_slurp, list_slurpy_files )-import Darcs.Hopefully ( PatchInfoAnd, info, n2pia,+ tryToShrink, commuteFLorComplain, commute )+import Darcs.Patch.Prim ( tryShrinkingInverse )+import Darcs.Patch.Bundle ( scanBundle, makeBundleN )+import Darcs.Hopefully ( PatchInfoAnd, info, hopefully, hopefullyM )-import Darcs.Repository.ApplyPatches ( apply_patches )+import Darcs.Repository.ApplyPatches ( applyPatches ) import qualified Darcs.Repository.HashedRepo as HashedRepo- ( revert_tentative_changes, finalize_tentative_changes,- remove_from_tentative_inventory,- copy_pristine, copy_partials_pristine, slurp_pristine,- apply_to_tentative_pristine,- write_tentative_inventory, write_and_read_patch,- add_to_tentative_inventory,- read_repo, read_tentative_repo, clean_pristine,- slurp_all_but_darcs )+ ( revertTentativeChanges, finalizeTentativeChanges,+ removeFromTentativeInventory,+ copyPristine, copyPartialsPristine,+ applyToTentativePristine,+ writeTentativeInventory, writeAndReadPatch,+ addToTentativeInventory,+ readRepo, readTentativeRepo, cleanPristine ) import qualified Darcs.Repository.DarcsRepo as DarcsRepo import Darcs.Flags ( DarcsFlag(Verbose, Quiet, MarkConflicts, AllowConflicts, NoUpdateWorking, WorkRepoUrl, WorkRepoDir, UMask, Test, LeaveTestDir, SetScriptsExecutable, DryRun ),- want_external_merge, compression )+ wantExternalMerge, compression ) import Darcs.Witnesses.Ordered ( FL(..), RL(..), EqCheck(..), unsafeCoerceP, (:\/:)(..), (:/\:)(..), (:>)(..), (+>+), lengthFL,- allFL, filterFL,- reverseRL, reverseFL, concatRL, mapFL,- mapFL_FL, concatFL )+ allFL, filterFLFL,+ reverseFL, mapFL_FL, concatFL ) import Darcs.Patch ( RepoPatch, Patchy, Prim, merge, joinPatches, listConflictedFiles, listTouchedFiles, Named, patchcontents, commuteRL, fromPrims,- patch2patchinfo, readPatch,+ readPatch, writePatch, effect, invert, primIsAddfile, primIsAdddir, primIsSetpref,- apply, applyToSlurpy,+ apply, applyToTree, emptyMarkedupFile, MarkedUpFile )-import Darcs.Patch.Patchy ( Invert(..) ) import Darcs.Patch.Permutations ( commuteWhatWeCanFL, removeFL ) import Darcs.Patch.Info ( PatchInfo )-import Darcs.Patch.Set ( PatchSet, SealedPatchSet )+import Darcs.Patch.Set ( PatchSet(..), SealedPatchSet, newset2FL )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif import Darcs.Patch.Apply ( markupFile, LineMark(None) )-import Darcs.Patch.Depends ( get_common_and_uncommon, deep_optimize_patchset )+import Darcs.Patch.Depends ( deepOptimizePatchset, removeFromPatchSet, mergeThem ) import Darcs.RepoPath ( FilePathLike, AbsolutePath, toFilePath, ioAbsoluteOrRemote, toPath ) import Darcs.Utils ( promptYorn, catchall, withCurrentDirectory, withUMask, nubsort ) import Progress ( debugMessage ) import Darcs.ProgressPatches (progressFL)-import Darcs.URL ( is_file )+import Darcs.URL ( isFile ) import Darcs.Repository.Prefs ( getCaches )-import Darcs.Lock ( withLock, writeDocBinFile, withDelayedDir, removeFileMayNotExist,+import Darcs.Lock ( withLock, writeDocBinFile, removeFileMayNotExist, withTempDir, withPermDir ) import Darcs.Witnesses.Sealed ( Sealed(Sealed), seal, FlippedSeal(FlippedSeal), flipSeal ) import Darcs.Repository.InternalTypes( Repository(..), RepoType(..) ) import Darcs.Global ( darcsdir )+ import System.Mem( performGC ) +import qualified Storage.Hashed.Tree as Tree+import Storage.Hashed.AnchoredPath( anchorPath )+ #include "impossible.h" -- | Repository IO monad. This monad-like datatype is responsible for@@ -189,36 +190,42 @@ getRepository :: RIO p C(r u t t) (Repository p C(r u t)) getRepository = RIO return -maybeIdentifyRepository :: [DarcsFlag] -> String -> IO (Either String (Repository p C(r u t)))+-- | The status of a given directory: is it a darcs repository?+data IdentifyRepo p C(r u t) = BadRepository String -- ^ looks like a repository with some error+ | NonRepository String -- ^ safest guess+ | GoodRepository (Repository p C(r u t))++maybeIdentifyRepository :: [DarcsFlag] -> String -> IO (IdentifyRepo p C(r u t)) maybeIdentifyRepository opts "." = do darcs <- doesDirectoryExist darcsdir rf_or_e <- identifyRepoFormat "." here <- toPath `fmap` ioAbsoluteOrRemote "." case rf_or_e of- Left err -> return $ Left err+ Left err -> return $ NonRepository err Right rf -> case readProblem rf of- Just err -> return $ Left err+ Just err -> return $ BadRepository err Nothing -> if darcs then do pris <- identifyPristine cs <- getCaches opts here- return $ Right $ Repo here opts rf (DarcsRepository pris cs)- else return (Left "Not a repository")+ return $ GoodRepository $ Repo here opts rf (DarcsRepository pris cs)+ else return (NonRepository "Not a repository") maybeIdentifyRepository opts url' = do url <- toPath `fmap` ioAbsoluteOrRemote url' rf_or_e <- identifyRepoFormat url case rf_or_e of- Left e -> return $ Left e+ Left e -> return $ NonRepository e Right rf -> case readProblem rf of- Just err -> return $ Left err+ Just err -> return $ BadRepository err Nothing -> do cs <- getCaches opts url- return $ Right $ Repo url opts rf (DarcsRepository nopristine cs)+ return $ GoodRepository $ Repo url opts rf (DarcsRepository nopristine cs) identifyDarcs1Repository :: [DarcsFlag] -> String -> IO (Repository Patch C(r u t)) identifyDarcs1Repository opts url = do er <- maybeIdentifyRepository opts url case er of- Left s -> fail s- Right r -> return r+ BadRepository s -> fail s+ NonRepository s -> fail s+ GoodRepository r -> return r identifyRepositoryFor :: forall p C(r u t). RepoPatch p => Repository p C(r u t) -> String -> IO (Repository p C(r u t)) identifyRepositoryFor (Repo _ opts rf _) url =@@ -228,20 +235,14 @@ Just e -> fail $ "Incompatibility with repository " ++ url ++ ":\n" ++ e Nothing -> return $ Repo absurl opts rf_ t' -isRight :: Either a b -> Bool-isRight (Right _) = True-isRight _ = False--currentDirIsRepository :: IO Bool-currentDirIsRepository = isRight `liftM` maybeIdentifyRepository [] "."- amInRepository :: [DarcsFlag] -> IO (Either String ()) amInRepository (WorkRepoDir d:_) = do setCurrentDirectory d `catchall` (fail $ "can't set directory to "++d)- air <- currentDirIsRepository- if air- then return (Right ())- else return (Left "You need to be in a repository directory to run this command.")+ status <- maybeIdentifyRepository [] "."+ case status of+ GoodRepository _ -> return (Right ())+ BadRepository e -> return (Left $ "While " ++ d ++ " looks like a repository directory, we have a problem with it:\n" ++ e)+ NonRepository _ -> return (Left "You need to be in a repository directory to run this command.") amInRepository (_:fs) = amInRepository fs amInRepository [] = seekRepo (Left "You need to be in a repository directory to run this command.")@@ -255,10 +256,12 @@ -> IO (Either String ()) seekRepo onFail = getCurrentDirectory >>= helper where helper startpwd = do- air <- currentDirIsRepository- if air- then return (Right ())- else do cd <- toFilePath `fmap` getCurrentDirectory+ status <- maybeIdentifyRepository [] "."+ case status of+ GoodRepository _ -> return (Right ())+ BadRepository e -> return (Left e)+ NonRepository _ ->+ do cd <- toFilePath `fmap` getCurrentDirectory setCurrentDirectory ".." cd' <- toFilePath `fmap` getCurrentDirectory if cd' /= cd@@ -277,12 +280,14 @@ amNotInRepository [] amNotInRepository (_:f) = amNotInRepository f amNotInRepository [] =- do air <- currentDirIsRepository- if air then return (Left $ "You may not run this command in a repository.")- else return $ Right ()+ do status <- maybeIdentifyRepository [] "."+ case status of+ GoodRepository _ -> return (Left $ "You may not run this command in a repository.")+ BadRepository e -> return (Left $ "You may not run this command in a repository.\nBy the way, we have a problem with it:\n" ++ e)+ NonRepository _ -> return (Right ()) findRepository :: [DarcsFlag] -> IO (Either String ())-findRepository (WorkRepoUrl d:_) | is_file d =+findRepository (WorkRepoUrl d:_) | isFile d = do setCurrentDirectory d `catchall` (fail $ "can't set directory to "++d) findRepository [] findRepository (WorkRepoDir d:_) =@@ -291,52 +296,21 @@ findRepository (_:fs) = findRepository fs findRepository [] = seekRepo (Right ()) -slurp_pending :: RepoPatch p => Repository p C(r u t) -> IO Slurpy-slurp_pending repo@(Repo _ _ _ rt) = do- cur <- slurp_recorded repo- Sealed pend <- read_pending repo- case applyToSlurpy pend cur of- Just pendcur -> return pendcur- Nothing -> do putStrLn "Yikes, pending has conflicts. Renaming file as_darcs/patches/pending_buggy"- renameFile (pendingName rt) (pendingName rt++"_buggy")- return cur--slurp_recorded :: RepoPatch p => Repository p C(r u t) -> IO Slurpy-slurp_recorded (Repo dir opts rf (DarcsRepository _ c))- | formatHas HashedInventory rf =- HashedRepo.slurp_pristine c (compression opts) dir $ darcsdir++"/hashed_inventory"-slurp_recorded repository@(Repo dir _ _ (DarcsRepository p _)) = do- mc <- withCurrentDirectory dir $ slurpPristine p- case mc of (Just slurpy) -> return slurpy- Nothing -> withDelayedDir "pristine.temp" $ \abscd ->- do let cd = toFilePath abscd- createPristineDirectoryTree repository cd- mmap_slurp cd--slurp_recorded_and_unrecorded :: RepoPatch p => Repository p C(r u t) -> IO (Slurpy, Slurpy)-slurp_recorded_and_unrecorded repo@(Repo r _ _ _) = do- cur <- slurp_recorded repo- Sealed pend <- read_pending repo- withCurrentDirectory r $- case applyToSlurpy pend cur of- Nothing -> fail "Yikes, pending has conflicts!"- Just pendslurp -> do unrec <- co_slurp pendslurp "."- return (cur, unrec)--make_new_pending :: forall p C(r u t y). RepoPatch p => Repository p C(r u t) -> FL Prim C(r y) -> IO ()-make_new_pending (Repo _ opts _ _) _ | NoUpdateWorking `elem` opts = return ()-make_new_pending repo@(Repo r _ _ tp) origp =+makeNewPending :: forall p C(r u t y). RepoPatch p+ => Repository p C(r u t) -> FL Prim C(t y) -> IO ()+makeNewPending (Repo _ opts _ _) _ | NoUpdateWorking `elem` opts = return ()+makeNewPending repo@(Repo r _ _ tp) origp = withCurrentDirectory r $ do let newname = pendingName tp ++ ".new" debugMessage $ "Writing new pending: " ++ newname- Sealed sfp <- return $ sift_for_pending origp+ Sealed sfp <- return $ siftForPending origp writeSealedPatch newname $ seal $ fromPrims $ sfp- cur <- slurp_recorded repo- Sealed p <- read_pendingfile newname- when (isNothing $ applyToSlurpy p cur) $ do+ cur <- readRecorded repo+ Sealed p <- readPendingfile newname+ catch (applyToTree p cur) $ \err -> do let buggyname = pendingName tp ++ "_buggy" renameFile newname buggyname- bugDoc $ text "There was an attempt to write an invalid pending!"+ bugDoc $ text ("There was an attempt to write an invalid pending! " ++ show err) $$ text "If possible, please send the contents of" <+> text buggyname $$ text "along with a bug report."@@ -345,21 +319,21 @@ where writeSealedPatch :: FilePath -> Sealed (Patch C(x)) -> IO () writeSealedPatch fp (Sealed p) = writePatch fp p -sift_for_pending :: FL Prim C(x y) -> Sealed (FL Prim C(x))-sift_for_pending simple_ps =- let oldps = maybe simple_ps id $ try_shrinking_inverse $ crude_sift simple_ps+siftForPending :: FL Prim C(x y) -> Sealed (FL Prim C(x))+siftForPending simple_ps =+ let oldps = maybe simple_ps id $ tryShrinkingInverse $ crudeSift simple_ps in if allFL (\p -> primIsAddfile p || primIsAdddir p) $ oldps then seal oldps else fromJust $ do Sealed x <- return $ sfp NilFL $ reverseFL oldps return (case tryToShrink x of- ps | lengthFL ps < lengthFL oldps -> sift_for_pending ps+ ps | lengthFL ps < lengthFL oldps -> siftForPending ps | otherwise -> seal ps) where sfp :: FL Prim C(a b) -> RL Prim C(c a) -> Sealed (FL Prim C(c)) sfp sofar NilRL = seal sofar sfp sofar (p:<:ps) | primIsHunk p || primIsBinary p- = case commuteFL (p :> sofar) of+ = case commuteFLorComplain (p :> sofar) of Right (sofar' :> _) -> sfp sofar' ps Left _ -> sfp (p:>:sofar) ps sfp sofar (p:<:ps) = sfp (p:>:sofar) ps@@ -368,24 +342,24 @@ -- seal it. Instead, update this function to work with type witnesses -- by fixing DarcsRepo to match HashedRepo in the handling of -- Repository state.-read_repo :: RepoPatch p => Repository p C(r u t) -> IO (PatchSet p C(r))-read_repo repo@(Repo r opts rf _)- | formatHas HashedInventory rf = do ps <- HashedRepo.read_repo repo r+readRepo :: RepoPatch p => Repository p C(r u t) -> IO (PatchSet p C(Origin r))+readRepo repo@(Repo r opts rf _)+ | formatHas HashedInventory rf = do ps <- HashedRepo.readRepo repo r return ps- | otherwise = do Sealed ps <- DarcsRepo.read_repo opts r+ | otherwise = do Sealed ps <- DarcsRepo.readRepo opts r return $ unsafeCoerceP ps -readTentativeRepo :: RepoPatch p => Repository p C(r u t) -> IO (PatchSet p C(t))+readTentativeRepo :: RepoPatch p => Repository p C(r u t) -> IO (PatchSet p C(Origin t)) readTentativeRepo repo@(Repo r opts rf _)- | formatHas HashedInventory rf = do ps <- HashedRepo.read_tentative_repo repo r+ | formatHas HashedInventory rf = do ps <- HashedRepo.readTentativeRepo repo r return ps- | otherwise = do Sealed ps <- DarcsRepo.read_tentative_repo opts r+ | otherwise = do Sealed ps <- DarcsRepo.readTentativeRepo opts r return $ unsafeCoerceP ps makePatchLazy :: RepoPatch p => Repository p C(r u t) -> PatchInfoAnd p C(x y) -> IO (PatchInfoAnd p C(x y)) makePatchLazy (Repo r opts rf (DarcsRepository _ c)) p- | formatHas HashedInventory rf = withCurrentDirectory r $ HashedRepo.write_and_read_patch c (compression opts) p- | otherwise = withCurrentDirectory r $ DarcsRepo.write_and_read_patch opts p+ | formatHas HashedInventory rf = withCurrentDirectory r $ HashedRepo.writeAndReadPatch c (compression opts) p+ | otherwise = withCurrentDirectory r $ DarcsRepo.writeAndReadPatch opts p prefsUrl :: Repository p C(r u t) -> String prefsUrl (Repo r _ _ (DarcsRepository _ _)) = r ++ "/"++darcsdir++"/prefs"@@ -393,19 +367,20 @@ unrevertUrl :: Repository p C(r u t) -> String unrevertUrl (Repo r _ _ (DarcsRepository _ _)) = r ++ "/"++darcsdir++"/patches/unrevert" -applyToWorking :: Patchy p => Repository p1 C(r u t) -> [DarcsFlag] -> p C(u y) -> IO ()-applyToWorking (Repo r _ _ (DarcsRepository _ _)) opts patch =- withCurrentDirectory r $ if Quiet `elem` opts- then runSilently $ apply opts patch- else runTolerantly $ apply opts patch+applyToWorking :: Patchy p => Repository p1 C(r u t) -> [DarcsFlag] -> p C(u y) -> IO (Repository p1 C(r y t))+applyToWorking (Repo r ropts rf (DarcsRepository t c)) opts patch =+ do withCurrentDirectory r $ if Quiet `elem` opts+ then runSilently $ apply opts patch+ else runTolerantly $ apply opts patch+ return (Repo r ropts rf (DarcsRepository t c)) -handle_pend_for_add :: forall p q C(r u t x y). (RepoPatch p, Effect q)+handlePendForAdd :: forall p q C(r u t x y). (RepoPatch p, Effect q) => Repository p C(r u t) -> q C(x y) -> IO ()-handle_pend_for_add (Repo _ opts _ _) _ | NoUpdateWorking `elem` opts = return ()-handle_pend_for_add (Repo _ _ _ rt) p =+handlePendForAdd (Repo _ opts _ _) _ | NoUpdateWorking `elem` opts = return ()+handlePendForAdd (Repo _ _ _ rt) p = do let pn = pendingName rt ++ ".tentative" Sealed pend <- (readPrims `fmap` gzReadFilePS pn) `catchall` (return $ Sealed NilFL)- let effectp = if allFL is_simple pend then crude_sift $ effect p+ let effectp = if allFL isSimple pend then crudeSift $ effect p else effect p Sealed newpend <- return $ rmpend (progressFL "Removing from pending:" effectp) pend writePatch pn $ fromPrims_ newpend@@ -427,11 +402,11 @@ fromPrims_ :: FL Prim C(a b) -> Patch C(a b) fromPrims_ = fromPrims -is_simple :: Prim C(x y) -> Bool-is_simple x = primIsHunk x || primIsBinary x || primIsSetpref x+isSimple :: Prim C(x y) -> Bool+isSimple x = primIsHunk x || primIsBinary x || primIsSetpref x -crude_sift :: FL Prim C(x y) -> FL Prim C(x y)-crude_sift xs = if allFL is_simple xs then filterFL ishunkbinary xs else xs+crudeSift :: FL Prim C(x y) -> FL Prim C(x y)+crudeSift xs = if allFL isSimple xs then filterFLFL ishunkbinary xs else xs where ishunkbinary :: Prim C(x y) -> EqCheck C(x y) ishunkbinary x | primIsHunk x || primIsBinary x = unsafeCoerceP IsEq | otherwise = NotEq@@ -445,12 +420,12 @@ data MakeChanges = MakeChanges | DontMakeChanges deriving ( Eq ) -announce_merge_conflicts :: String -> [DarcsFlag] -> FL Prim C(x y) -> IO Bool-announce_merge_conflicts cmd opts resolved_pw =+announceMergeConflicts :: String -> [DarcsFlag] -> FL Prim C(x y) -> IO Bool+announceMergeConflicts cmd opts resolved_pw = case nubsort $ listTouchedFiles $ resolved_pw of [] -> return False cfs -> if MarkConflicts `elem` opts || AllowConflicts `elem` opts- || want_external_merge opts /= Nothing+ || wantExternalMerge opts /= Nothing then do putStrLn "We have conflicts in the following files:" putStrLn $ unwords cfs return True@@ -463,13 +438,13 @@ " "++cmd++" mark-conflicts\n"++ "to "++darcsdir++"/prefs/defaults in the target repo. " -check_unrecorded_conflicts :: forall p C(r y). RepoPatch p => [DarcsFlag] -> FL (Named p) C(r y) -> IO Bool-check_unrecorded_conflicts opts _ | NoUpdateWorking `elem` opts = return False-check_unrecorded_conflicts opts pc =+checkUnrecordedConflicts :: forall p C(t y). RepoPatch p => [DarcsFlag] -> FL (Named p) C(t y) -> IO Bool+checkUnrecordedConflicts opts _ | NoUpdateWorking `elem` opts = return False+checkUnrecordedConflicts opts pc = do repository <- identifyDarcs1Repository opts "." cuc repository where cuc :: Repository Patch C(r u t) -> IO Bool- cuc r = do Sealed mpend <- read_pending r :: IO (Sealed (FL Prim C(r)))+ cuc r = do Sealed mpend <- readPending r :: IO (Sealed (FL Prim C(t))) case mpend of NilFL -> return False pend ->@@ -488,32 +463,33 @@ fromPrims_ = fromPrims tentativelyAddPatch :: RepoPatch p- => Repository p C(r u t) -> [DarcsFlag] -> PatchInfoAnd p C(r y) -> IO ()+ => Repository p C(r u t) -> [DarcsFlag] -> PatchInfoAnd p C(t y) -> IO (Repository p C(r u y)) tentativelyAddPatch = tentativelyAddPatch_ UpdatePristine data UpdatePristine = UpdatePristine | DontUpdatePristine deriving Eq tentativelyAddPatch_ :: RepoPatch p => UpdatePristine -> Repository p C(r u t) -> [DarcsFlag]- -> PatchInfoAnd p C(r y) -> IO ()+ -> PatchInfoAnd p C(t y) -> IO (Repository p C(r u y)) tentativelyAddPatch_ _ _ opts _ | DryRun `elem` opts = bug "tentativelyAddPatch_ called when --dry-run is specified"-tentativelyAddPatch_ up r@(Repo dir _ rf (DarcsRepository _ c)) opts p =+tentativelyAddPatch_ up r@(Repo dir ropts rf (DarcsRepository t c)) opts p = withCurrentDirectory dir $ do decideHashedOrNormal rf $ HvsO {- hashed = HashedRepo.add_to_tentative_inventory c (compression opts) p,- old = DarcsRepo.add_to_tentative_inventory opts (hopefully p) }+ hashed = HashedRepo.addToTentativeInventory c (compression opts) p,+ old = DarcsRepo.addToTentativeInventory opts (hopefully p) } when (up == UpdatePristine) $ do debugMessage "Applying to pristine cache..." applyToTentativePristine r p debugMessage "Updating pending..."- handle_pend_for_add r p+ handlePendForAdd r p+ return (Repo dir ropts rf (DarcsRepository t c)) -applyToTentativePristine :: (Effect q, Patchy q) => Repository p C(r u t) -> q C(r y) -> IO ()+applyToTentativePristine :: (Effect q, Patchy q) => Repository p C(r u t) -> q C(t y) -> IO () applyToTentativePristine (Repo dir opts rf (DarcsRepository _ c)) p = withCurrentDirectory dir $ do when (Verbose `elem` opts) $ putDocLn $ text "Applying to pristine..." <+> description p- decideHashedOrNormal rf $ HvsO {hashed = HashedRepo.apply_to_tentative_pristine c opts p,- old = DarcsRepo.add_to_tentative_pristine p}+ decideHashedOrNormal rf $ HvsO {hashed = HashedRepo.applyToTentativePristine opts p,+ old = DarcsRepo.addToTentativePristine p} -- | This fuction is unsafe because it accepts a patch that works on the tentative -- pending and we don't currently track the state of the tentative pending.@@ -540,7 +516,7 @@ setTentativePending :: forall p C(r u t x y). RepoPatch p => Repository p C(r u t) -> FL Prim C(x y) -> IO () setTentativePending (Repo _ opts _ _) _ | NoUpdateWorking `elem` opts = return () setTentativePending (Repo dir _ _ rt) patch = do- Sealed prims <- return $ sift_for_pending patch+ Sealed prims <- return $ siftForPending patch withCurrentDirectory dir $ writePatch (pendingName rt ++ ".tentative") $ fromPrims_ prims where fromPrims_ :: FL Prim C(a b) -> Patch C(a b)@@ -555,7 +531,7 @@ do let pn = pendingName rt ++ ".tentative" Sealed pend <- readPrims `liftM` (gzReadFilePS pn `catchall` (return B.empty)) Sealed newpend_ <- return $ newpend pend patch- writePatch pn $ fromPrims_ (crude_sift newpend_)+ writePatch pn $ fromPrims_ (crudeSift newpend_) where newpend :: FL Prim C(b c) -> FL Prim C(a b) -> Sealed (FL Prim C(a)) newpend NilFL patch_ = seal patch_ newpend p patch_ = seal $ patch_ +>+ p@@ -563,50 +539,48 @@ fromPrims_ = fromPrims tentativelyRemovePatches :: RepoPatch p => Repository p C(r u t) -> [DarcsFlag]- -> FL (Named p) C(x t) -> IO ()+ -> FL (PatchInfoAnd p) C(x t) -> IO (Repository p C(r u x)) tentativelyRemovePatches = tentativelyRemovePatches_ UpdatePristine tentativelyRemovePatches_ :: forall p C(r u t x). RepoPatch p => UpdatePristine -> Repository p C(r u t) -> [DarcsFlag]- -> FL (Named p) C(x t) -> IO ()-tentativelyRemovePatches_ up repository@(Repo dir _ rf (DarcsRepository _ c)) opts ps =+ -> FL (PatchInfoAnd p) C(x t) -> IO (Repository p C(r u x))+tentativelyRemovePatches_ up repository@(Repo dir ropts rf (DarcsRepository t c)) opts ps = withCurrentDirectory dir $ do when (up == UpdatePristine) $ do debugMessage "Adding changes to pending..." prepend repository $ effect ps- remove_from_unrevert_context repository ps+ removeFromUnrevertContext repository ps debugMessage "Removing changes from tentative inventory..." if formatHas HashedInventory rf- then do HashedRepo.remove_from_tentative_inventory repository (compression opts) ps+ then do HashedRepo.removeFromTentativeInventory repository (compression opts) ps when (up == UpdatePristine) $- HashedRepo.apply_to_tentative_pristine c opts $+ HashedRepo.applyToTentativePristine opts $ progressFL "Applying inverse to pristine" $ invert ps- else DarcsRepo.remove_from_tentative_inventory (up==UpdatePristine) opts ps+ else DarcsRepo.removeFromTentativeInventory (up==UpdatePristine) opts ps+ return (Repo dir ropts rf (DarcsRepository t c)) tentativelyReplacePatches :: forall p C(r u t x). RepoPatch p => Repository p C(r u t) -> [DarcsFlag]- -> FL (Named p) C(x t) -> IO ()-tentativelyReplacePatches repository@(Repo x y z w) opts ps =- -- tentativelyRemovePatches_ leaves the repository in state C(x u t)- do tentativelyRemovePatches_ DontUpdatePristine repository opts ps- -- Now we add the patches back so that the repo again has state C(r u t)- sequence_ $ mapAdd ((Repo x y z w) :: Repository p C(x u t)) ps- where mapAdd :: Repository p C(i l m) -> FL (Named p) C(i j) -> [IO ()]- mapAdd _ NilFL = []- mapAdd r@(Repo dir df rf dr) (a:>:as) =- -- we construct a new Repository object on the recursive case so that the- -- recordedstate of the repository can match the fact that we just wrote a patch- tentativelyAddPatch_ DontUpdatePristine r opts (n2pia a) : mapAdd (Repo dir df rf dr) as+ -> FL (PatchInfoAnd p) C(x t) -> IO (Repository p C(r u t))+tentativelyReplacePatches repository opts ps =+ do repository' <- tentativelyRemovePatches_ DontUpdatePristine repository opts ps+ mapAdd repository' ps+ where mapAdd :: Repository p C(m l i) -> FL (PatchInfoAnd p) C(i j) -> IO (Repository p C(m l j))+ mapAdd r NilFL = return r+ mapAdd r (a:>:as) =+ do r' <- tentativelyAddPatch_ DontUpdatePristine r opts a+ mapAdd r' as -finalize_pending :: RepoPatch p => Repository p C(r u t) -> IO ()-finalize_pending (Repo dir opts _ rt)+finalizePending :: RepoPatch p => Repository p C(r u t) -> IO ()+finalizePending (Repo dir opts _ rt) | NoUpdateWorking `elem` opts = withCurrentDirectory dir $ removeFileMayNotExist $ (pendingName rt)-finalize_pending repository@(Repo dir _ _ rt) = do+finalizePending repository@(Repo dir _ _ rt) = do withCurrentDirectory dir $ do let pn = pendingName rt tpn = pn ++ ".tentative" tpfile <- gzReadFilePS tpn `catchall` (return B.empty) Sealed tpend <- return $ readPrims tpfile- Sealed new_pending <- return $ sift_for_pending tpend- make_new_pending repository new_pending+ Sealed new_pending <- return $ siftForPending tpend+ makeNewPending repository new_pending finalizeRepositoryChanges :: RepoPatch p => Repository p C(r u t) -> IO () finalizeRepositoryChanges (Repo _ opts _ _)@@ -616,16 +590,16 @@ withCurrentDirectory dir $ do debugMessage "Considering whether to test..." testTentative repository debugMessage "Finalizing changes..."- withSignalsBlocked $ do HashedRepo.finalize_tentative_changes repository (compression opts)- finalize_pending repository+ withSignalsBlocked $ do HashedRepo.finalizeTentativeChanges repository (compression opts)+ finalizePending repository debugMessage "Done finalizing changes..." finalizeRepositoryChanges repository@(Repo dir _ _ (DarcsRepository _ _)) = withCurrentDirectory dir $ do debugMessage "Considering whether to test..." testTentative repository debugMessage "Finalizing changes..."- withSignalsBlocked $ do DarcsRepo.finalize_pristine_changes- DarcsRepo.finalize_tentative_changes- finalize_pending repository+ withSignalsBlocked $ do DarcsRepo.finalizePristineChanges+ DarcsRepo.finalizeTentativeChanges+ finalizePending repository testTentative :: RepoPatch p => Repository p C(r u t) -> IO () testTentative = testAny withTentative@@ -661,14 +635,14 @@ revertRepositoryChanges r@(Repo dir opts rf dr@(DarcsRepository _ _)) = withCurrentDirectory dir $ do removeFileMayNotExist (pendingName dr ++ ".tentative")- Sealed x <- read_pending r+ Sealed x <- readPending r setTentativePending r $ effect x when (NoUpdateWorking `elem` opts) $ removeFileMayNotExist $ pendingName dr- decideHashedOrNormal rf $ HvsO { hashed = HashedRepo.revert_tentative_changes,- old = DarcsRepo.revert_tentative_changes }+ decideHashedOrNormal rf $ HvsO { hashed = HashedRepo.revertTentativeChanges,+ old = DarcsRepo.revertTentativeChanges } -patchSetToPatches :: RepoPatch p => RL (RL (PatchInfoAnd p)) C(x y) -> FL (Named p) C(x y)-patchSetToPatches patchSet = mapFL_FL hopefully $ reverseRL $ concatRL patchSet+patchSetToPatches :: RepoPatch p => PatchSet p C(x y) -> FL (Named p) C(x y)+patchSetToPatches patchSet = mapFL_FL hopefully $ newset2FL patchSet getUMask :: [DarcsFlag] -> Maybe String getUMask [] = Nothing@@ -731,43 +705,44 @@ then job repository else withLock name (revertRepositoryChanges repository >> job repository) -remove_from_unrevert_context :: forall p C(r u t x). RepoPatch p- => Repository p C(r u t) -> FL (Named p) C(x t) -> IO ()-remove_from_unrevert_context repository ps = do- Sealed bundle <- unrevert_patch_bundle `catchall` (return $ seal (NilRL:<:NilRL))+removeFromUnrevertContext :: forall p C(r u t x). RepoPatch p+ => Repository p C(r u t) -> FL (PatchInfoAnd p) C(x t) -> IO ()+removeFromUnrevertContext repository ps = do+ Sealed bundle <- unrevert_patch_bundle `catchall` (return $ seal (PatchSet NilRL NilRL)) remove_from_unrevert_context_ bundle- where unrevert_impossible unrevert_loc =- do putStrLn "This operation will make unrevert impossible!"- yorn <- promptYorn "Proceed?"+ where unrevert_impossible =+ do yorn <- promptYorn "This operation will make unrevert impossible!\nProceed?" case yorn of 'n' -> fail "Cancelled."- 'y' -> removeFile unrevert_loc `catchall` return ()+ 'y' -> removeFileMayNotExist (unrevertUrl repository) _ -> impossible- pis = mapFL patch2patchinfo ps- unrevert_patch_bundle :: IO (SealedPatchSet p)+ unrevert_patch_bundle :: IO (SealedPatchSet p C(Origin)) unrevert_patch_bundle = do pf <- B.readFile (unrevertUrl repository)- case scan_bundle pf of+ case scanBundle pf of Right foo -> return foo Left err -> fail $ "Couldn't parse unrevert patch:\n" ++ err- remove_from_unrevert_context_ :: PatchSet p C(z) -> IO ()- remove_from_unrevert_context_ (NilRL :<: NilRL) = return ()- remove_from_unrevert_context_ bundle = do- let unrevert_loc = unrevertUrl repository- debugMessage "Adjusting the context of the unrevert changes..."+ remove_from_unrevert_context_ :: PatchSet p C(Origin z) -> IO ()+ remove_from_unrevert_context_ (PatchSet NilRL NilRL) = return ()+ remove_from_unrevert_context_ bundle =+ do debugMessage "Adjusting the context of the unrevert changes..."+ debugMessage $ "Removing "++ show (lengthFL ps) +++ " patches in removeFromUnrevertContext!" ref <- readTentativeRepo repository- case get_common_and_uncommon (bundle, ref) of- (common,(h_us:<:NilRL) :\/: NilRL) ->- case commuteRL (reverseFL ps :> hopefully h_us) of- Nothing -> unrevert_impossible unrevert_loc- Just (us' :> _) -> do- s <- readRecorded repository- bundle' <- make_bundle [] s (common \\ pis) (us' :>: NilFL)- writeDocBinFile unrevert_loc bundle'- (common,(x:<:NilRL):\/:_)- | isr && any (`elem` common) pis -> unrevert_impossible unrevert_loc- | isr -> return ()- where isr = isJust $ hopefullyM x- _ -> unrevert_impossible unrevert_loc+ let withSinglet :: Sealed (FL ppp C(xxx))+ -> (FORALL(yyy) ppp C(xxx yyy) -> IO ()) -> IO ()+ withSinglet (Sealed (x :>: NilFL)) j = j x+ withSinglet _ _ = return ()+ withSinglet (mergeThem ref bundle) $ \h_us ->+ case commuteRL (reverseFL ps :> h_us) of+ Nothing -> unrevert_impossible+ Just (us' :> _) ->+ case removeFromPatchSet ps ref of+ Nothing -> unrevert_impossible+ Just common ->+ do debugMessage "Have now found the new context..."+ bundle <- makeBundleN Nothing common (hopefully us':>:NilFL)+ writeDocBinFile (unrevertUrl repository) bundle+ debugMessage "Done adjusting the context of the unrevert changes!" -- | Writes out a fresh copy of the inventory that minimizes the -- amount of inventory that need be downloaded when people pull from@@ -782,31 +757,31 @@ -- important in large repositories. optimizeInventory :: RepoPatch p => Repository p C(r u t) -> IO () optimizeInventory repository@(Repo r opts rf (DarcsRepository _ c)) =- do ps <- read_repo repository+ do ps <- readRepo repository decideHashedOrNormal rf $ HvsO { hashed = do revertRepositoryChanges repository- HashedRepo.write_tentative_inventory c (compression opts) $ deep_optimize_patchset ps+ HashedRepo.writeTentativeInventory c (compression opts) $ deepOptimizePatchset ps finalizeRepositoryChanges repository,- old = DarcsRepo.write_inventory r $ deep_optimize_patchset ps+ old = DarcsRepo.writeInventory r $ deepOptimizePatchset ps } cleanRepository :: RepoPatch p => Repository p C(r u t) -> IO () cleanRepository repository@(Repo _ _ rf _) = decideHashedOrNormal rf $- HvsO { hashed = HashedRepo.clean_pristine repository,+ HvsO { hashed = HashedRepo.cleanPristine repository, old = return () } createPristineDirectoryTree :: RepoPatch p => Repository p C(r u t) -> FilePath -> IO () createPristineDirectoryTree repo@(Repo r opts rf (DarcsRepository pris c)) reldir | formatHas HashedInventory rf = do createDirectoryIfMissing True reldir- withCurrentDirectory reldir $ HashedRepo.copy_pristine c (compression opts) r (darcsdir++"/hashed_inventory")+ withCurrentDirectory reldir $ HashedRepo.copyPristine c (compression opts) r (darcsdir++"/hashed_inventory") | otherwise = do dir <- toPath `fmap` ioAbsoluteOrRemote reldir done <- withCurrentDirectory r $ easyCreatePristineDirectoryTree pris dir- unless done $ do Sealed patches <- (seal . reverseRL . concatRL) `liftM` read_repo repo+ unless done $ do Sealed patches <- (seal . newset2FL) `liftM` readRepo repo createDirectoryIfMissing True dir- withCurrentDirectory dir $ apply_patches [] patches+ withCurrentDirectory dir $ applyPatches [] patches -- fp below really should be FileName createPartialsPristineDirectoryTree :: (FilePathLike fp, RepoPatch p) => Repository p C(r u t) -> [fp] -> FilePath -> IO ()@@ -814,7 +789,7 @@ | formatHas HashedInventory rf = do createDirectoryIfMissing True dir withCurrentDirectory dir $- HashedRepo.copy_partials_pristine c (compression opts) r (darcsdir++"/hashed_inventory") prefs+ HashedRepo.copyPartialsPristine c (compression opts) r (darcsdir++"/hashed_inventory") prefs createPartialsPristineDirectoryTree r@(Repo rdir _ _ (DarcsRepository pris _)) prefs dir = withCurrentDirectory rdir $ do done <- easyCreatePartialsPristineDirectoryTree prefs pris dir@@ -832,7 +807,7 @@ -> (AbsolutePath -> IO a) -> IO a withTentative (Repo dir opts rf (DarcsRepository _ c)) mk_dir f | formatHas HashedInventory rf =- mk_dir $ \d -> do HashedRepo.copy_pristine c (compression opts) dir (darcsdir++"/tentative_pristine")+ mk_dir $ \d -> do HashedRepo.copyPristine c (compression opts) dir (darcsdir++"/tentative_pristine") f d withTentative repository@(Repo dir opts _ _) mk_dir f = withRecorded repository mk_dir $ \d ->@@ -848,20 +823,20 @@ getMarkedupFile :: RepoPatch p => Repository p C(r u t) -> PatchInfo -> FilePath -> IO MarkedUpFile getMarkedupFile repository pinfo f = do Sealed (FlippedSeal patches) <- (seal . dropWhileFL ((/= pinfo) . info)- . reverseRL . concatRL) `liftM` read_repo repository- return $ snd $ do_mark_all patches (f, emptyMarkedupFile)+ . newset2FL) `liftM` readRepo repository+ return $ snd $ doMarkAll patches (f, emptyMarkedupFile) where dropWhileFL :: (FORALL(x y) a C(x y) -> Bool) -> FL a C(r v) -> FlippedSeal (FL a) C(v) dropWhileFL _ NilFL = flipSeal NilFL dropWhileFL p xs@(x:>:xs') | p x = dropWhileFL p xs' | otherwise = flipSeal xs-do_mark_all :: RepoPatch p => FL (PatchInfoAnd p) C(x y)+doMarkAll :: RepoPatch p => FL (PatchInfoAnd p) C(x y) -> (FilePath, MarkedUpFile) -> (FilePath, MarkedUpFile)-do_mark_all (hp:>:pps) (f, mk) =+doMarkAll (hp:>:pps) (f, mk) = case hopefullyM hp of- Just p -> do_mark_all pps $ markupFile (info hp) (patchcontents p) (f, mk)+ Just p -> doMarkAll pps $ markupFile (info hp) (patchcontents p) (f, mk) Nothing -> (f, [(BC.pack "Error reading a patch!",None)])-do_mark_all NilFL (f, mk) = (f, mk)+doMarkAll NilFL (f, mk) = (f, mk) -- | Sets scripts in or below the current directory executable. A script is any file that starts -- with the bytes '#!'. This is used sometimes for --set-scripts-executable, but at other times@@ -870,10 +845,11 @@ setScriptsExecutable = do debugMessage "Making scripts executable" myname <- getCurrentDirectory- c <- list_slurpy_files `fmap` (HashedRepo.slurp_all_but_darcs myname)- let setExecutableIfScript f =+ tree <- readWorking+ let paths = [ anchorPath "." p | (p, Tree.File _) <- Tree.list tree ]+ setExecutableIfScript f = do contents <- B.readFile f when (BC.pack "#!" `B.isPrefixOf` contents) $ do debugMessage ("Making executable: " ++ f) setExecutable f True- mapM_ setExecutableIfScript c+ mapM_ setExecutableIfScript paths
src/Darcs/Repository/InternalTypes.hs view
@@ -21,12 +21,14 @@ #include "gadts.h" module Darcs.Repository.InternalTypes ( Repository(..), RepoType(..), Pristine(..)- , extractCache+ , extractCache, extractOptions, modifyCache ) where -import Darcs.Repository.Cache ( Cache )+import Data.List ( nub, sortBy )+import Darcs.Repository.Cache ( Cache (..) , compareByLocality ) import Darcs.Flags ( DarcsFlag ) import Darcs.Repository.Format ( RepoFormat )+import Darcs.Patch ( RepoPatch ) data Pristine = NoPristine !String@@ -40,3 +42,13 @@ extractCache :: Repository p C(r u t) -> Cache extractCache (Repo _ _ _ (DarcsRepository _ c)) = c++extractOptions :: Repository p C(r u t) -> [DarcsFlag]+extractOptions (Repo _ opts _ _) = opts++-- | 'modifyCache' @repository function@ modifies the cache of+-- @repository@ with @function@, remove duplicates and sort the results with 'compareByLocality'.+modifyCache :: FORALL(p r u t) (RepoPatch p) => Repository p C(r u t) -> (Cache -> Cache) -> Repository p C(r u t)+modifyCache (Repo dir opts rf (DarcsRepository pristine cache)) f = Repo dir opts rf dr+ where dr = DarcsRepository pristine . cmap ( sortBy compareByLocality . nub ) $ f cache+ cmap f (Ca c) = Ca (f c)
src/Darcs/Repository/LowLevel.hs view
@@ -21,7 +21,7 @@ #include "gadts.h" -module Darcs.Repository.LowLevel ( read_pending, read_pendingfile, pendingName, readPrims ) where+module Darcs.Repository.LowLevel ( readPending, readPendingfile, pendingName, readPrims ) where import Darcs.Repository.InternalTypes ( RepoType(..), Repository(..) ) import Darcs.Patch ( readPatch, Prim, Patch, RepoPatch, effect )@@ -35,12 +35,12 @@ pendingName :: RepoType p -> String pendingName (DarcsRepository _ _) = darcsdir++"/patches/pending" -read_pending :: RepoPatch p => Repository p C(r u t) -> IO (Sealed (FL Prim C(r)))-read_pending (Repo r _ _ tp) =- withCurrentDirectory r (read_pendingfile (pendingName tp))+readPending :: RepoPatch p => Repository p C(r u t) -> IO (Sealed (FL Prim C(t)))+readPending (Repo r _ _ tp) =+ withCurrentDirectory r (readPendingfile (pendingName tp)) -read_pendingfile :: String -> IO (Sealed (FL Prim C(x)))-read_pendingfile name = do+readPendingfile :: String -> IO (Sealed (FL Prim C(x)))+readPendingfile name = do pend <- gzReadFilePS name `catchall` return BS.empty return $ readPrims pend
src/Darcs/Repository/Merge.hs view
@@ -21,19 +21,20 @@ module Darcs.Repository.Merge where -import Darcs.Resolution ( standard_resolution, external_resolution )+import Darcs.Resolution ( standardResolution, externalResolution ) import Darcs.External ( backupByCopying )-import Control.Monad ( when )+import Control.Monad ( when, unless ) import Darcs.Patch ( Effect ) import Darcs.Hopefully ( PatchInfoAnd, n2pia, hopefully ) import Darcs.Flags- ( DarcsFlag( AllowConflicts ), want_external_merge )+ ( DarcsFlag( AllowConflicts, NoAllowConflicts ), wantExternalMerge ) import Darcs.Witnesses.Ordered ( FL(..), (:\/:)(..), (:/\:)(..), (+>+), mapFL_FL ) import Darcs.Patch ( RepoPatch, Prim, merge, joinPatches, listTouchedFiles , patchcontents, anonymous, fromPrims, effect )+import Darcs.Patch.Depends( merge2FL ) import Progress( debugMessage ) import Darcs.ProgressPatches( progressFL ) import Darcs.Witnesses.Sealed( Sealed(Sealed), seal )@@ -42,72 +43,73 @@ import Darcs.Repository.State( unrecordedChanges, readUnrecorded ) import Darcs.Repository.Internal- ( announce_merge_conflicts, check_unrecorded_conflicts+ ( announceMergeConflicts, checkUnrecordedConflicts , MakeChanges(..), setTentativePending , tentativelyAddPatch_, applyToTentativePristine, UpdatePristine(..) ) tentativelyMergePatches_ :: forall p C(r u t y x). RepoPatch p => MakeChanges -> Repository p C(r u t) -> String -> [DarcsFlag]- -> FL (PatchInfoAnd p) C(x r) -> FL (PatchInfoAnd p) C(x y)+ -> FL (PatchInfoAnd p) C(x t) -> FL (PatchInfoAnd p) C(x y) -> IO (Sealed (FL Prim C(u))) tentativelyMergePatches_ mc r cmd opts usi themi = do let us = mapFL_FL hopefully usi them = mapFL_FL hopefully themi- _ :/\: pc <- return $ merge (progressFL "Merging them" them :\/: progressFL "Merging us" us)+ Sealed pc <- return $ merge2FL (progressFL "Merging us" usi) (progressFL "Merging them" themi) pend <- unrecordedChanges opts r []- anonpend <- anonymous (fromPrims pend)+ anonpend <- n2pia `fmap` anonymous (fromPrims pend) pend' :/\: pw <- return $ merge (pc :\/: anonpend :>: NilFL)- let pwprim = joinPatches $ progressFL "Examining patches for conflicts" $ mapFL_FL patchcontents pw- Sealed standard_resolved_pw <- return $ standard_resolution pwprim+ let pwprim = joinPatches $ progressFL "Examining patches for conflicts" $+ mapFL_FL (patchcontents . hopefully) pw+ Sealed standard_resolved_pw <- return $ standardResolution pwprim debugMessage "Checking for conflicts..."- mapM_ backupByCopying $ listTouchedFiles standard_resolved_pw+ unless (AllowConflicts `elem` opts || NoAllowConflicts `elem` opts) $+ mapM_ backupByCopying $ listTouchedFiles standard_resolved_pw debugMessage "Announcing conflicts..."- have_conflicts <- announce_merge_conflicts cmd opts standard_resolved_pw+ have_conflicts <- announceMergeConflicts cmd opts standard_resolved_pw debugMessage "Checking for unrecorded conflicts..."- have_unrecorded_conflicts <- check_unrecorded_conflicts opts pc+ have_unrecorded_conflicts <- checkUnrecordedConflicts opts $ mapFL_FL hopefully pc debugMessage "Reading working directory..."- working <- readUnrecorded r+ working <- readUnrecorded r [] debugMessage "Working out conflicts in actual working directory..." Sealed pw_resolution <-- case (want_external_merge opts, have_conflicts || have_unrecorded_conflicts) of+ case (wantExternalMerge opts, have_conflicts || have_unrecorded_conflicts) of (Nothing,_) -> return $ if AllowConflicts `elem` opts then seal NilFL else seal standard_resolved_pw (_,False) -> return $ seal standard_resolved_pw- (Just c, True) -> external_resolution working c+ (Just c, True) -> externalResolution working c (effect us +>+ pend) (effect them) pwprim debugMessage "Applying patches to the local directories..." when (mc == MakeChanges) $- do let doChanges :: FL (PatchInfoAnd p) C(x r) -> IO ()+ do let doChanges :: FL (PatchInfoAnd p) C(x t) -> IO () doChanges NilFL = applyps r themi- doChanges _ = applyps r (mapFL_FL n2pia pc)+ doChanges _ = applyps r pc doChanges usi setTentativePending r (effect pend' +>+ pw_resolution) return $ seal (effect pwprim +>+ pw_resolution)- where mapAdd :: Repository p C(i l m) -> FL (PatchInfoAnd p) C(i j) -> [IO ()]- mapAdd _ NilFL = []- mapAdd r'@(Repo dir df rf dr) (a:>:as) =- -- we construct a new Repository object on the recursive case so that the- -- recordedstate of the repository can match the fact that we just wrote a patch- tentativelyAddPatch_ DontUpdatePristine r' opts a : mapAdd (Repo dir df rf dr) as- applyps :: Repository p C(i l m) -> FL (PatchInfoAnd p) C(i j) -> IO ()+ where mapAdd :: Repository p C(m l i) -> FL (PatchInfoAnd p) C(i j) -> IO (Repository p C(m l j))+ mapAdd repo NilFL = return repo+ mapAdd repo (a:>:as) =+ do repo' <- tentativelyAddPatch_ DontUpdatePristine repo opts a+ mapAdd repo' as+ applyps :: Repository p C(m l i) -> FL (PatchInfoAnd p) C(i j) -> IO () applyps repo ps = do debugMessage "Adding patches to inventory..."- sequence_ $ mapAdd repo ps+ mapAdd repo ps debugMessage "Applying patches to pristine..." applyToTentativePristine repo ps tentativelyMergePatches :: RepoPatch p => Repository p C(r u t) -> String -> [DarcsFlag]- -> FL (PatchInfoAnd p) C(x r) -> FL (PatchInfoAnd p) C(x y)+ -> FL (PatchInfoAnd p) C(x t) -> FL (PatchInfoAnd p) C(x y) -> IO (Sealed (FL Prim C(u))) tentativelyMergePatches = tentativelyMergePatches_ MakeChanges considerMergeToWorking :: RepoPatch p => Repository p C(r u t) -> String -> [DarcsFlag]- -> FL (PatchInfoAnd p) C(x r) -> FL (PatchInfoAnd p) C(x y)+ -> FL (PatchInfoAnd p) C(x t) -> FL (PatchInfoAnd p) C(x y) -> IO (Sealed (FL Prim C(u))) considerMergeToWorking = tentativelyMergePatches_ DontMakeChanges
src/Darcs/Repository/Motd.lhs view
@@ -17,7 +17,7 @@ \begin{code}-module Darcs.Repository.Motd (get_motd, show_motd) where+module Darcs.Repository.Motd (getMotd, showMotd) where import Control.Monad ( unless ) import Darcs.Flags ( DarcsFlag( Quiet, XMLOutput ) ) import Darcs.External ( fetchFilePS, Cachable(..) )@@ -34,15 +34,15 @@ \begin{code} -- | Fetch and return the message of the day for a given repository.-get_motd :: String -> IO B.ByteString-get_motd repo = fetchFilePS (repo++"/"++darcsdir++"/prefs/motd") (MaxAge 600)+getMotd :: String -> IO B.ByteString+getMotd repo = fetchFilePS (repo++"/"++darcsdir++"/prefs/motd") (MaxAge 600) `catchall` return B.empty -- | Display the message of the day for a given repository, -- unless either the 'XMLOutput' or the 'Quiet' flags are passed in-show_motd :: [DarcsFlag] -> String -> IO ()-show_motd opts repo = unless (Quiet `elem` opts || XMLOutput `elem` opts) $ do- motd <- get_motd repo+showMotd :: [DarcsFlag] -> String -> IO ()+showMotd opts repo = unless (Quiet `elem` opts || XMLOutput `elem` opts) $ do+ motd <- getMotd repo unless (B.null motd) $ do B.hPut stdout motd putStrLn "**********************"
src/Darcs/Repository/Prefs.lhs view
@@ -31,7 +31,8 @@ boringRegexps, boringFileFilter, darcsdirFilter, FileType(..), filetypeFunction, getCaches,- binariesFileHelp+ binariesFileHelp,+ globalCacheDir ) where import System.IO.Error ( isDoesNotExistError )@@ -39,7 +40,7 @@ import Text.Regex ( Regex, mkRegex, matchRegex, ) import Data.Char ( toUpper ) import Data.Maybe ( isJust, catMaybes )-import Data.List ( nub, isPrefixOf, union )+import Data.List ( nub, isPrefixOf, union, sortBy ) import System.Directory ( getAppUserDataDirectory ) import System.FilePath ( (</>) ) import System.Environment ( getEnvironment )@@ -54,8 +55,8 @@ import qualified Data.ByteString as B ( empty ) import Darcs.Global ( darcsdir ) import Darcs.Repository.Cache ( Cache(..), CacheType(..), CacheLoc(..),- WritableOrNot(..) )-import Darcs.URL ( is_file )+ WritableOrNot(..), compareByLocality )+import Darcs.URL ( isFile ) \end{code} \section{prefs}@@ -116,7 +117,7 @@ \verb!_darcs/prefs/boring!, so be sure to copy that file to the boringfile. You can also set up a ``boring'' regexps-file in your home directory, named \verb!~/.darcs/boring!, +file in your home directory, named \verb!~/.darcs/boring!, on MS Windows~\ref{ms_win}, which will be used with all of your darcs repositories. @@ -237,7 +238,8 @@ isDarcsdir "" = True isDarcsdir ".." = True isDarcsdir "../" = True-isDarcsdir fp = darcsdir `isPrefixOf` fp+isDarcsdir "_darcs" = True+isDarcsdir fp = "_darcs/" `isPrefixOf` fp -- | The path of the global preference directory; @~/.darcs@ on Unix, -- and @%APPDATA%/darcs@ on Windows.@@ -433,7 +435,7 @@ defaultrepo :: [DarcsFlag] -> AbsolutePath -> [String] -> IO [String] defaultrepo opts _ [] =- do let fixR r | not (is_file r) = return r+ do let fixR r | not (isFile r) = return r | otherwise = do absr <- ioAbsolute r return $ toFilePath absr case [r | RemoteRepo r <- opts] of@@ -445,17 +447,26 @@ defaultrepo _ _ r = return r setDefaultrepo :: String -> [DarcsFlag] -> IO ()-setDefaultrepo r opts = do doit <- if (NoSetDefault `notElem` opts && DryRun `notElem` opts && r_is_not_tmp)- then return True- else do olddef <-- getPreflist "defaultrepo"- return (olddef == [])- when doit- (setPreflist "defaultrepo" [r])+setDefaultrepo r opts = do olddef <- getPreflist "defaultrepo"+ let doit = NoSetDefault `notElem` opts && greenLight+ greenLight = wetRun+ && not rIsTmp+ && (olddef /= [r] || olddef == [])+ if doit+ then setPreflist "defaultrepo" [r]+ else when greenLight $ putStr . unlines $+ -- the nuance here is that we should only notify when the+ -- reason we're not setting default is the --no-set-default+ -- flag, not the various automatic show stoppers+ [ "Note: if you want to change the default remote repository to"+ , r ++ ","+ , "quit now and issue the same command with the --set-default flag."+ ] addToPreflist "repos" r `catchall` return () -- we don't care if this fails! where- r_is_not_tmp = not $ r `elem` [x | RemoteRepo x <- opts]+ wetRun = DryRun `notElem` opts+ rIsTmp = r `elem` [x | RemoteRepo x <- opts] \end{code} \paragraph{email}@@ -485,21 +496,16 @@ downloading patches multiple times when you pull from a remote repository to more than one local repository. -We strongly advise that you enable a global cache directory, which will-allow darcs to avoid re-downloading patches (for example, when doing a+A global cache is enabled by default in your home directory. The cache+allows darcs to avoid re-downloading patches (for example, when doing a second darcs get of the same repository), and also allows darcs to use hard-links to reduce disk usage. To do this, simply-\begin{verbatim}-mkdir -p $HOME/.darcs/cache-echo cache:$HOME/.darcs/cache > $HOME/.darcs/sources-\end{verbatim}+links to reduce disk usage.+ Note that the cache directory should reside on the same filesystem as your repositories, so you may need to vary this. You can also use multiple cache directories on different filesystems, if you have several filesystems on which you use darcs. -On MS Windows~\ref{ms_win})- \begin{code} getCaches :: [DarcsFlag] -> String -> IO Cache getCaches opts repodir =@@ -517,9 +523,10 @@ let thisrepo = if Ephemeral `elem` opts then [Cache Repo NotWritable $ toFilePath thisdir] else [Cache Repo Writable $ toFilePath thisdir]- return $ Ca $ nub $ thisrepo ++ globalcache ++ globalsources +++ let tempCache = nub $ thisrepo ++ globalcache ++ globalsources ++ here ++ [Cache Repo NotWritable repodir] ++ there- where + return $ Ca $ sortBy compareByLocality tempCache+ where parsehs = catMaybes . map readln . noncomments readln l | take 5 l == "repo:" = Just (Cache Repo NotWritable (drop 5 l)) | take 9 l == "thisrepo:" = Just (Cache Repo Writable (drop 9 l))@@ -528,6 +535,6 @@ | take 9 l == "readonly:" = Just (Cache Directory NotWritable (drop 9 l)) | otherwise = Nothing nocache = NoCache `elem` opts- + \end{code}
src/Darcs/Repository/Pristine.hs view
@@ -23,7 +23,6 @@ module Darcs.Repository.Pristine ( Pristine, flagsToPristine, nopristine, createPristine, removePristine, identifyPristine,- slurpPristine, applyPristine, createPristineFromWorking, getPristinePop, pristineDirectory, pristineToFlagString,@@ -34,9 +33,7 @@ import Data.Maybe ( isJust ) import Control.Monad ( when, liftM ) import System.Directory ( createDirectory, doesDirectoryExist, doesFileExist, removeFile )-import Darcs.Lock ( rm_recursive, writeBinFile )-import Workaround ( getCurrentDirectory )-import Darcs.SlurpDirectory ( Slurpy, mmap_slurp )+import Darcs.Lock ( rmRecursive, writeBinFile ) import Darcs.PopulationData ( Population, getPopFrom ) import Darcs.Flags ( DarcsFlag( PristinePlain, PristineNone ) )@@ -46,12 +43,12 @@ import Darcs.Patch ( Patchy, apply ) import Darcs.Patch.Info ( PatchInfo ) import Darcs.Patch.FileName ( fp2fn )-import qualified Data.ByteString as B (empty) import Darcs.RepoPath ( FilePathLike, toFilePath )-import SHA1 ( sha1PS ) import Darcs.External ( cloneTree, cloneTreeExcept, clonePartialsTree ) import Darcs.Repository.InternalTypes ( Pristine(..) ) import Darcs.Global ( darcsdir )+import Storage.Hashed.Darcs( writeDarcsHashed )+import Storage.Hashed.Tree( emptyTree ) #include "impossible.h" nopristine :: Pristine@@ -67,7 +64,7 @@ Just pristine -> return pristine reallyIdentifyPristine :: IO (Maybe Pristine)-reallyIdentifyPristine = +reallyIdentifyPristine = do dir <- findpristine doesDirectoryExist "" none <- findpristine doesFileExist ".none" hashinv <- doesFileExist $ darcsdir++"/hashed_inventory"@@ -100,14 +97,15 @@ flagsToPristine [] rf = flagsToPristine [PristinePlain] rf createPristine :: Pristine -> IO Pristine-createPristine p = +createPristine p = do oldpristine <- reallyIdentifyPristine when (isJust oldpristine) $ fail "Pristine tree already exists." case p of NoPristine n -> writeBinFile n "Do not delete this file.\n" PlainPristine n -> createDirectory n HashedPristine -> do createDirectory hashedPristineDirectory- writeFile (hashedPristineDirectory++"/"++sha1PS B.empty) ""+ writeDarcsHashed emptyTree "_darcs/pristine.hashed"+ return () return p hashedPristineDirectory :: String@@ -115,16 +113,8 @@ removePristine :: Pristine -> IO () removePristine (NoPristine n) = removeFile n-removePristine (PlainPristine n) = rm_recursive n-removePristine HashedPristine = rm_recursive hashedPristineDirectory--slurpPristine :: Pristine -> IO (Maybe Slurpy)-slurpPristine (PlainPristine n) = do cwd <- getCurrentDirectory- slurpy <- mmap_slurp (cwd ++ "/" ++ n)- return (Just slurpy)-slurpPristine (NoPristine _) = return Nothing-slurpPristine HashedPristine =- bug "HashedPristine is not implemented yet."+removePristine (PlainPristine n) = rmRecursive n+removePristine HashedPristine = rmRecursive hashedPristineDirectory applyPristine :: Patchy p => Pristine -> p C(x y) -> IO () applyPristine (NoPristine _) _ = return ()
src/Darcs/Repository/Repair.hs view
@@ -1,10 +1,10 @@ {-# OPTIONS_GHC -cpp #-}-{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP, PatternGuards #-} module Darcs.Repository.Repair ( replayRepository, checkIndex , RepositoryConsistency(..) ) where- + import Control.Monad ( when, unless ) import Control.Monad.Trans ( liftIO ) import Control.Applicative( (<$>) )@@ -13,24 +13,30 @@ import Data.List ( sort, (\\) ) import System.Directory ( createDirectoryIfMissing ) -import Darcs.Lock( rm_recursive )-import Darcs.Hopefully ( PatchInfoAnd, info )+import Darcs.Lock( rmRecursive )+import Darcs.Hopefully ( PatchInfoAnd, info, winfo, WPatchInfo, unWPatchInfo, compareWPatchInfo ) -import Darcs.Witnesses.Ordered ( FL(..), RL(..), lengthFL, reverseFL, reverseRL, concatRL,- mapRL )+import Darcs.Witnesses.Ordered ( FL(..), RL(..), lengthFL, reverseFL,+ mapRL, nullFL, (:||:)(..), EqCheck(..) )+import Darcs.Witnesses.Sealed ( Sealed2(..), Sealed(..), unFreeLeft ) import Darcs.Patch.Patchy ( applyAndTryToFix )-import Darcs.Patch.Info ( PatchInfo( .. ), human_friendly )-import Darcs.Patch.Set ( PatchSet )-import Darcs.Patch ( RepoPatch )+import Darcs.Hopefully( hopefully )+import Darcs.Patch.Info ( humanFriendly )+import Darcs.Patch.Set ( PatchSet(..), newset2FL, newset2RL )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif+import Darcs.Patch ( RepoPatch, isInconsistent )+import Darcs.Patch.Core ( patchcontents ) -import Darcs.Repository.Format ( identifyRepoFormat, +import Darcs.Repository.Format ( identifyRepoFormat, RepoProperty ( HashedInventory ), formatHas ) import Darcs.Repository.Cache ( HashedDir( HashedPristineDir ) )-import Darcs.Repository.HashedIO ( clean_hashdir )+import Darcs.Repository.HashedIO ( cleanHashdir ) import Darcs.Repository.HashedRepo ( readHashedPristineRoot ) import Darcs.Repository.InternalTypes ( extractCache ) import Darcs.Repository.Prefs ( filetypeFunction )-import Darcs.Repository ( Repository, read_repo, makePatchLazy+import Darcs.Repository ( Repository, readRepo, makePatchLazy , readRecorded, readIndex, readRecordedAndPending ) import Progress ( debugMessage, beginTedious, endTedious, tediousSize, finishedOneIO )@@ -53,83 +59,86 @@ import qualified Data.ByteString.Char8 as BS #include "impossible.h"+#include "gadts.h" -replaceInFL :: FL (PatchInfoAnd a)- -> [(PatchInfo, PatchInfoAnd a)]- -> FL (PatchInfoAnd a)+replaceInFL :: FL (PatchInfoAnd a) C(x y)+ -> [Sealed2 (WPatchInfo :||: PatchInfoAnd a)]+ -> FL (PatchInfoAnd a) C(x y) replaceInFL orig [] = orig replaceInFL NilFL _ = impossible-replaceInFL (o:>:orig) ch@((o',c):ch_rest)- | info o == o' = c:>:replaceInFL orig ch_rest+replaceInFL (o:>:orig) ch@(Sealed2 (o':||:c):ch_rest)+ | IsEq <- winfo o `compareWPatchInfo` o' = c:>:replaceInFL orig ch_rest | otherwise = o:>:replaceInFL orig ch -applyAndFix :: forall p. RepoPatch p => Repository p -> FL (PatchInfoAnd p) -> TreeIO (FL (PatchInfoAnd p), Bool)+applyAndFix :: forall p C(r u t x y). RepoPatch p => Repository p C(r u t) -> FL (PatchInfoAnd p) C(Origin r) -> TreeIO (FL (PatchInfoAnd p) C(Origin r), Bool) applyAndFix _ NilFL = return (NilFL, True) applyAndFix r psin = do liftIO $ beginTedious k liftIO $ tediousSize k $ lengthFL psin (repaired, ok) <- aaf psin liftIO $ endTedious k- orig <- liftIO $ (reverseRL . concatRL) `fmap` read_repo r+ orig <- liftIO $ newset2FL `fmap` readRepo r return (replaceInFL orig repaired, ok) where k = "Replaying patch"- aaf :: FL (PatchInfoAnd p) -> TreeIO ([(PatchInfo, PatchInfoAnd p)], Bool)+ aaf :: FL (PatchInfoAnd p) C(w z) -> TreeIO ([Sealed2 (WPatchInfo :||: PatchInfoAnd p)], Bool) aaf NilFL = return ([], True) aaf (p:>:ps) = do mp' <- applyAndTryToFix p- let !infp = info p -- assure that 'p' can be garbage collected.- liftIO $ finishedOneIO k $ show $ human_friendly $ infp+ case isInconsistent . patchcontents . hopefully $ p of+ Just err -> liftIO $ putDocLn err+ Nothing -> return ()+ let !winfp = winfo p -- assure that 'p' can be garbage collected.+ liftIO $ finishedOneIO k $ show $ humanFriendly $ unWPatchInfo winfp (ps', restok) <- aaf ps case mp' of Nothing -> return (ps', restok) Just (e,pp) -> do liftIO $ putStrLn e p' <- liftIO $ makePatchLazy r pp- return ((infp, p'):ps', False)+ return (Sealed2 (winfp :||: p'):ps', False) -data RepositoryConsistency p =+data RepositoryConsistency p C(x) = RepositoryConsistent | BrokenPristine (Tree IO)- | BrokenPatches (Tree IO) (PatchSet p)+ | BrokenPatches (Tree IO) (PatchSet p C(Origin x)) -check_uniqueness :: RepoPatch p => (Doc -> IO ()) -> (Doc -> IO ()) -> Repository p -> IO ()-check_uniqueness putVerbose putInfo repository =+checkUniqueness :: RepoPatch p => (Doc -> IO ()) -> (Doc -> IO ()) -> Repository p C(r u t) -> IO ()+checkUniqueness putVerbose putInfo repository = do putVerbose $ text "Checking that patch names are unique..."- r <- read_repo repository- case has_duplicate $ mapRL info $ concatRL r of+ r <- readRepo repository+ case hasDuplicate $ mapRL info $ newset2RL r of Nothing -> return () Just pinf -> do putInfo $ text "Error! Duplicate patch name:"- putInfo $ human_friendly pinf+ putInfo $ humanFriendly pinf fail "Duplicate patches found." -has_duplicate :: Ord a => [a] -> Maybe a-has_duplicate li = hd $ sort li+hasDuplicate :: Ord a => [a] -> Maybe a+hasDuplicate li = hd $ sort li where hd [_] = Nothing hd [] = Nothing hd (x1:x2:xs) | x1 == x2 = Just x1 | otherwise = hd (x2:xs)-replayRepository' :: (RepoPatch p) => Repository p -> [DarcsFlag] -> IO (RepositoryConsistency p)+replayRepository' :: (RepoPatch p)+ => Repository p C(r u t) -> [DarcsFlag] -> IO (RepositoryConsistency p C(r)) replayRepository' repo opts = do let putVerbose s = when (Verbose `elem` opts) $ putDocLn s putInfo s = when (not $ Quiet `elem` opts) $ putDocLn s- check_uniqueness putVerbose putInfo repo+ checkUniqueness putVerbose putInfo repo createDirectoryIfMissing False $ darcsdir ++ "/pristine.hashed" putVerbose $ text "Reading recorded state..." pris <- readRecorded repo `catch` \_ -> return emptyTree putVerbose $ text "Applying patches..."- patches <- read_repo repo+ patches <- readRepo repo debugMessage "Fixing any broken patches..."- let psin = reverseRL $ concatRL patches+ let psin = newset2FL patches repair = applyAndFix repo psin ((ps, patches_ok), newpris) <- hashedTreeIO repair emptyTree "_darcs/pristine.hashed" debugMessage "Done fixing broken patches..."- let newpatches = reverseFL ps :<: NilRL+ let newpatches = PatchSet (reverseFL ps) NilRL debugMessage "Checking pristine against slurpy" ftf <- filetypeFunction- is_same <- do diff <- treeDiff ftf pris newpris- return $ case diff of- NilFL -> True- _ -> False+ is_same <- do Sealed diff <- unFreeLeft `fmap` treeDiff ftf pris newpris+ return $ nullFL diff `catchall` return False -- TODO is the latter condition needed? Does a broken patch imply pristine -- difference? Why, or why not?@@ -139,25 +148,25 @@ then BrokenPristine newpris else BrokenPatches newpris newpatches) -cleanupRepositoryReplay :: Repository p -> IO ()+cleanupRepositoryReplay :: Repository p C(r u t) -> IO () cleanupRepositoryReplay r = do let c = extractCache r rf_or_e <- identifyRepoFormat "." rf <- case rf_or_e of Left e -> fail e Right x -> return x unless (formatHas HashedInventory rf) $- rm_recursive $ darcsdir ++ "/pristine.hashed" + rmRecursive $ darcsdir ++ "/pristine.hashed" when (formatHas HashedInventory rf) $ do current <- readHashedPristineRoot r- clean_hashdir c HashedPristineDir $ catMaybes [current]+ cleanHashdir c HashedPristineDir $ catMaybes [current] -replayRepository :: (RepoPatch p) => Repository p -> [DarcsFlag] -> (RepositoryConsistency p -> IO a) -> IO a+replayRepository :: (RepoPatch p) => Repository p C(r u t) -> [DarcsFlag] -> (RepositoryConsistency p C(r) -> IO a) -> IO a replayRepository r opt f = run `finally` cleanupRepositoryReplay r where run = do st <- replayRepository' r opt f st -checkIndex :: (RepoPatch p) => Repository p -> Bool -> IO Bool+checkIndex :: (RepoPatch p) => Repository p C(r u t) -> Bool -> IO Bool checkIndex repo quiet = do index <- updateIndex =<< readIndex repo pristine <- expand =<< readRecordedAndPending repo
src/Darcs/Repository/State.hs view
@@ -24,9 +24,9 @@ -- SOFTWARE. module Darcs.Repository.State- ( restrictSubpaths, restrictBoring+ ( restrictSubpaths, restrictBoring, TreeFilter(..) -- * Diffs.- , unrecordedChanges, readPending, pendingChanges+ , unrecordedChanges, readPending -- * Trees. , readRecorded, readUnrecorded, readRecordedAndPending, readWorking -- * Index.@@ -46,16 +46,17 @@ import Darcs.Patch ( RepoPatch, Prim, invert, applyToTree, applyToFilepaths , sortCoalesceFL )-import Darcs.Patch.TouchesFiles ( choose_touching )+import Darcs.Patch.TouchesFiles ( chooseTouching ) import Darcs.Witnesses.Ordered ( FL(..), (+>+) )-import Darcs.Witnesses.Sealed ( Sealed(Sealed), seal )+import Darcs.Witnesses.Ordered ( unsafeCoerceP, EqCheck(IsEq) )+import Darcs.Witnesses.Sealed ( Sealed(Sealed), seal, unFreeLeft ) import Darcs.Diff ( treeDiff ) import Darcs.Flags ( DarcsFlag( LookForAdds ), willIgnoreTimes ) import Darcs.Global ( darcsdir ) import Darcs.Utils ( filterPaths ) import Darcs.Repository.InternalTypes ( Repository )-import Darcs.Repository.LowLevel( read_pending )+import qualified Darcs.Repository.LowLevel as LowLevel import Darcs.Repository.Prefs ( filetypeFunction, boringRegexps ) import Darcs.Patch.FileName ( fn2fp )@@ -71,6 +72,8 @@ #include "gadts.h" +newtype TreeFilter m = TreeFilter { applyTreeFilter :: forall tr . FilterTree tr m => tr m -> tr m }+ -- | From a repository and a list of SubPath's, construct a filter that can be -- used on a Tree (recorded or unrecorded state) of this repository. This -- constructed filter will take pending into account, so the subpaths will be@@ -78,15 +81,15 @@ -- convenience, if the subpath list is empty, the filter constructed is an -- identity. restrictSubpaths :: (RepoPatch p) => Repository p C(r u t) -> [SubPath]- -> IO (forall t m. FilterTree t m => t m -> t m)+ -> IO (TreeFilter m) restrictSubpaths repo subpaths = do- Sealed pending <- read_pending repo+ Sealed pending <- LowLevel.readPending repo let paths = map (fn2fp . sp2fn) subpaths paths' = paths `union` applyToFilepaths pending paths anchored = map floatPath paths' restrictPaths :: FilterTree t m => t m -> t m restrictPaths = if null subpaths then id else filter (filterPaths anchored)- return restrictPaths+ return (TreeFilter restrictPaths) -- | Construct a Tree filter that removes any boring files the Tree might have -- contained. Additionally, you should (in most cases) pass an (expanded) Tree@@ -99,7 +102,7 @@ -- full working copy of the repository, including untracked -- files. Cf. whatsnew, record --look-for-adds. NB. Assumes that our CWD is -- the repository root.-restrictBoring :: forall t m. Tree m -> IO (FilterTree t m => t m -> t m)+restrictBoring :: forall m . Tree m -> IO (TreeFilter m) restrictBoring guide = do boring <- boringRegexps let boring' (AnchoredPath (Name x:_)) | x == BSC.pack darcsdir = False@@ -109,14 +112,17 @@ restrictTree = filter $ \p _ -> case find guide p of Nothing -> boring' p _ -> True- return restrictTree+ return (TreeFilter restrictTree) -- | For a repository and a list of paths (when empty, take everything) compute -- a (forward) list of prims (i.e. a patch) going from the recorded state of -- the repository (pristine) to the unrecorded state of the repository (the--- working copy + pending). When a non-empty list of paths is given, exactly+-- working copy + pending). When a non-empty list of paths is given, at least -- the files that live under any of these paths in either recorded or--- unrecorded will be included in the resulting patch.+-- unrecorded will be included in the resulting patch. NB. More patches may be+-- included in this list, eg. the full contents of the pending patch. This is+-- usually not a problem, since selectChanges will properly filter the results+-- anyway. -- -- This also depends on the options given: with LookForAdds, we will include -- any non-boring files (i.e. also those that do not exist in the "recorded"@@ -128,29 +134,30 @@ -- is very inefficient, although in extremely rare cases, the index could go -- out of sync (file is modified, index is updated and file is modified again -- within a single second).-unrecordedChanges :: (RepoPatch p) => [DarcsFlag] -> Repository p C(r u t)- -> [SubPath] -> IO (FL Prim C(r y))+unrecordedChanges :: FORALL(p r u t) (RepoPatch p)+ => [DarcsFlag] -> Repository p C(r u t)+ -> [SubPath] -> IO (FL Prim C(t u)) unrecordedChanges opts repo paths = do- (all_current, _) <- readPending repo- Sealed pending <- pendingChanges repo paths+ (all_current, Sealed (pending :: FL Prim C(t x))) <- readPending repo relevant <- restrictSubpaths repo paths- let getIndex = I.updateIndex =<< (relevant <$> readIndex repo)- current = relevant all_current+ let getIndex = I.updateIndex =<< (applyTreeFilter relevant <$> readIndex repo)+ current = applyTreeFilter relevant all_current working <- case (LookForAdds `elem` opts, willIgnoreTimes opts) of (False, False) -> getIndex (False, True) -> do guide <- expand current- relevant <$> restrict guide <$> readPlainTree "."+ applyTreeFilter relevant <$> restrict guide <$> readPlainTree "." (True, ignoretimes) -> do index <- getIndex nonboring <- restrictBoring index- plain <- relevant <$> nonboring <$> readPlainTree "."+ plain <- applyTreeFilter relevant <$> applyTreeFilter nonboring <$> readPlainTree "." return $ if ignoretimes then plain else plain `overlay` index ft <- filetypeFunction- diff <- treeDiff ft current working+ Sealed (diff :: FL Prim C(x y)) <- (unFreeLeft `fmap` treeDiff ft current working) :: IO (Sealed (FL Prim C(x)))+ IsEq <- return (unsafeCoerceP IsEq) :: IO (EqCheck C(y u)) return $ sortCoalesceFL (pending +>+ diff) -- | Obtains a Tree corresponding to the "recorded" state of the repository:@@ -183,9 +190,16 @@ (_, _) -> fail "No pristine tree is available!" -- | Obtains a Tree corresponding to the "unrecorded" state of the repository:--- the working tree plus the "pending" patch.-readUnrecorded :: (RepoPatch p) => Repository p C(r u t) -> IO (Tree IO)-readUnrecorded repo = readIndex repo >>= I.updateIndex+-- the working tree plus the "pending" patch. The optional list of paths (it is+-- ignored if empty) allows to restrict the query to a subtree.+--+-- Limiting the query may be more efficient, since hashes on the uninteresting+-- parts of the index do not need to go through an up-to-date check (which+-- involves a relatively expensive lstat(2) per file.+readUnrecorded :: (RepoPatch p) => Repository p C(r u t) -> [SubPath] -> IO (Tree IO)+readUnrecorded repo paths = do+ relevant <- restrictSubpaths repo paths+ readIndex repo >>= I.updateIndex . applyTreeFilter relevant -- | Obtains a Tree corresponding to the working copy of the -- repository. NB. Almost always, using readUnrecorded is the right@@ -198,12 +212,12 @@ readRecordedAndPending :: (RepoPatch p) => Repository p C(r u t) -> IO (Tree IO) readRecordedAndPending repo = do pristine <- readRecorded repo- Sealed pending <- pendingChanges repo []+ Sealed pending <- snd `fmap` readPending repo applyToTree pending pristine -readPending :: (RepoPatch p) => Repository p C(r u t) -> IO (Tree IO, Sealed (FL Prim C(r)))+readPending :: (RepoPatch p) => Repository p C(r u t) -> IO (Tree IO, Sealed (FL Prim C(t))) readPending repo =- do Sealed pending <- read_pending repo+ do Sealed pending <- LowLevel.readPending repo pristine <- readRecorded repo catch ((\t -> (t, seal pending)) `fmap` applyToTree pending pristine) $ \ err -> do putStrLn $ "Yikes, pending has conflicts! " ++ show err@@ -211,17 +225,6 @@ renameFile "_darcs/patches/pending" "_darcs/patches/pending_buggy" return (pristine, seal NilFL)--pendingChanges :: (RepoPatch p) => Repository p C(r u t)- -> [SubPath] -> IO (Sealed (FL Prim C(r)))-pendingChanges repo paths = do- Sealed pending <- snd `fmap` readPending repo- let files = map (fn2fp . sp2fn) paths- pre_files = applyToFilepaths (invert pending) files- relevant = case paths of- [] -> seal pending- _ -> choose_touching pre_files pending- return relevant -- | Mark the existing index as invalid. This has to be called whenever the -- listing of pristine changes and will cause darcs to update the index next
src/Darcs/Resolution.lhs view
@@ -21,9 +21,9 @@ #include "gadts.h" -module Darcs.Resolution ( standard_resolution,- external_resolution,- patchset_conflict_resolutions,+module Darcs.Resolution ( standardResolution,+ externalResolution,+ patchsetConflictResolutions, ) where import System.FilePath.Posix ( (</>) )@@ -44,9 +44,11 @@ import CommandLine ( parseCmd ) import Darcs.Hopefully ( hopefully ) import Darcs.Utils ( askUser, filterFilePaths )-import Darcs.Patch.Set ( PatchSet )-import Darcs.Witnesses.Sealed ( seal )-import Darcs.Witnesses.Sealed ( Sealed(..) )+import Darcs.Patch.Set ( PatchSet(..) )+#ifdef GADT_WITNESSES+import Darcs.Patch.Set ( Origin )+#endif+import Darcs.Witnesses.Sealed ( Sealed(..), unFreeLeft ) import Darcs.Repository.Prefs ( filetypeFunction ) import Exec ( exec, Redirect(..) ) import Darcs.Lock ( withTempDir )@@ -59,11 +61,11 @@ --import Printer ( greenText, ($$), Doc ) --import Darcs.Patch ( showPatch ) -standard_resolution :: RepoPatch p => p C(x y) -> Sealed (FL Prim C(y))-standard_resolution p = merge_list $ map head $ resolveConflicts p+standardResolution :: RepoPatch p => p C(x y) -> Sealed (FL Prim C(y))+standardResolution p = mergeList $ map head $ resolveConflicts p -merge_list :: [Sealed (FL Prim C(x))] -> Sealed (FL Prim C(x))-merge_list patches = doml NilFL patches+mergeList :: [Sealed (FL Prim C(x))] -> Sealed (FL Prim C(x))+mergeList patches = doml NilFL patches where doml :: FL Prim C(x y) -> [Sealed (FL Prim C(x))] -> Sealed (FL Prim C(x)) doml mp (Sealed p:ps) = case commute (invert p :> mp) of@@ -103,7 +105,7 @@ Note that the command is split into space-separated words and the first one is \verb!exec!ed with the rest as arguments---it is not a shell command. In particular, on Windows this means that the first command path should not contain spaces and-you should make sure the command is in your \verb!PATH!. +you should make sure the command is in your \verb!PATH!. The substitution of the \verb!%! escapes is done everywhere. If you need to prevent substitution you can use a double percentage sign, i.e. \verb!%%a! is substituted with@@ -144,7 +146,7 @@ Note that if you do use an external merge tool, most likely you will want to add to your defaults file-(\verb!_darcs/prefs/defaults! or \verb!~/.darcs/prefs!, see \ref{defaults}, +(\verb!_darcs/prefs/defaults! or \verb!~/.darcs/prefs!, see \ref{defaults}, on MS Windows~\ref{ms_win}) a line such as \begin{verbatim}@@ -158,10 +160,10 @@ Note that the defaults file does not want quotes around the command. \begin{code}-external_resolution :: RepoPatch p => Tree.Tree IO -> String -> FL Prim C(x y) -> FL Prim C(x z)+externalResolution :: RepoPatch p => Tree.Tree IO -> String -> FL Prim C(x y) -> FL Prim C(x z) -> p C(y a) -> IO (Sealed (FL Prim C(a)))-external_resolution s1 c p1 p2 pmerged = do+externalResolution s1 c p1 p2 pmerged = do sa <- applyToTree (invert p1) s1 sm <- applyToTree pmerged s1 s2 <- applyToTree p2 sa@@ -192,16 +194,16 @@ withTempDir "version2" $ \absd2 -> do let d2 = toFilePath absd2 write_files s2 n2s- mapM_ (externally_resolve_file c da d1 d2 dm) ns+ mapM_ (externallyResolveFile c da d1 d2 dm) ns sc <- readPlainTree dc sfixed <- readPlainTree dm ftf <- filetypeFunction- seal `fmap` treeDiff ftf sc sfixed+ unFreeLeft `fmap` treeDiff ftf sc sfixed -externally_resolve_file :: String -> String -> String -> String -> String+externallyResolveFile :: String -> String -> String -> String -> String -> (FilePath, FilePath, FilePath, FilePath) -> IO ()-externally_resolve_file c da d1 d2 dm (fa, f1, f2, fm) = do+externallyResolveFile c 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) $@@ -219,16 +221,13 @@ exec command args (Null,Null,Null) rr [] = return ExitSuccess -patchset_conflict_resolutions :: RepoPatch p => PatchSet p C(x) -> Sealed (FL Prim C(x))-patchset_conflict_resolutions (NilRL:<:_) = --traceDoc (greenText "no conflicts A") $- Sealed NilFL-patchset_conflict_resolutions NilRL = --traceDoc (greenText "no conflicts B") $- Sealed NilFL-patchset_conflict_resolutions (xs:<:_)+patchsetConflictResolutions :: RepoPatch p => PatchSet p C(Origin x) -> Sealed (FL Prim C(x))+patchsetConflictResolutions (PatchSet NilRL _) = Sealed NilFL+patchsetConflictResolutions (PatchSet xs _) = --traceDoc (greenText "looking at resolutions" $$ -- (sh $ resolveConflicts $ joinPatches $ -- mapFL_FL (patchcontents . hopefully) $ reverseRL xs )) $- merge_list $ map head $ resolveConflicts $ joinPatches $+ mergeList $ map head $ resolveConflicts $ joinPatches $ mapFL_FL (patchcontents . hopefully) $ reverseRL xs --where sh :: [[Sealed (FL Prim)]] -> Doc -- sh [] = greenText "no more conflicts"
src/Darcs/RunCommand.hs view
@@ -16,7 +16,7 @@ -- Boston, MA 02110-1301, USA. {-# LANGUAGE CPP #-}-module Darcs.RunCommand ( run_the_command ) where+module Darcs.RunCommand ( runTheCommand ) where import Control.Monad ( unless, when ) import System.Console.GetOpt( ArgOrder( Permute, RequireOrder ),@@ -26,10 +26,12 @@ import Darcs.Arguments ( DarcsFlag(..), help,- optionFromDarcsoption,- listOptions )-import Darcs.ArgumentDefaults ( get_default_flags )+ fixUrlFlag,+ optionFromDarcsOption,+ listOptions, nubOptions )+import Darcs.ArgumentDefaults ( getDefaultFlags ) import Darcs.Commands ( CommandArgs( CommandOnly, SuperCommandOnly, SuperCommandSub ),+ CommandControl, DarcsCommand, commandName, commandCommand,@@ -47,56 +49,55 @@ subusage, chompNewline ) import Darcs.Commands.GZCRCs ( doCRCWarnings ) import Darcs.Global ( atexit )-import Darcs.Commands.Help ( commandControlList ) import Darcs.External ( viewDoc ) import Darcs.Global ( setDebugMode, setSshControlMasterDisabled, setTimingsMode, setVerboseMode ) import Darcs.Match ( checkMatchSyntax ) import Progress ( setProgressMode ) import Darcs.RepoPath ( getCurrentDirectory )-import Darcs.Test ( run_posthook, run_prehook )+import Darcs.Test ( runPosthook, runPrehook ) import Darcs.Utils ( formatPath ) import Data.List ( intersperse ) import Printer ( text )-import URL ( setDebugHTTP, setHTTPPipelining )+import URL ( setDebugHTTP, disableHTTPPipelining ) -run_the_command :: String -> [String] -> IO ()-run_the_command cmd args =+runTheCommand :: [CommandControl] -> String -> [String] -> IO ()+runTheCommand commandControlList cmd args = either fail rtc $ disambiguateCommands commandControlList cmd args where- rtc (CommandOnly c, as) = run_command Nothing c as- rtc (SuperCommandOnly c, as) = run_raw_supercommand c as- rtc (SuperCommandSub c s, as) = run_command (Just c) s as+ rtc (CommandOnly c, as) = runCommand Nothing c as+ rtc (SuperCommandOnly c, as) = runRawSupercommand c as+ rtc (SuperCommandSub c s, as) = runCommand (Just c) s as -- This is the actual heavy lifter code, which is responsible for parsing the -- arguments and then running the command itself. -run_command :: Maybe DarcsCommand -> DarcsCommand -> [String] -> IO ()+runCommand :: Maybe DarcsCommand -> DarcsCommand -> [String] -> IO () -run_command _ _ args -- Check for "dangerous" typoes...+runCommand _ _ args -- Check for "dangerous" typoes... | "-all" `elem` args = -- -all indicates --all --look-for-adds!- fail $ "Are you sure you didn't mean -" ++ "-all rather than -all?"-run_command msuper cmd args = do+ fail "Are you sure you didn't mean --all rather than -all?"+runCommand msuper cmd args = do cwd <- getCurrentDirectory let options = opts1 ++ opts2 (opts1, opts2) = commandOptions cwd cmd case getOpt Permute- (optionFromDarcsoption cwd listOptions++options) args of+ (optionFromDarcsOption cwd listOptions++options) args of (opts,extra,[]) | Help `elem` opts -> viewDoc $ text $ getCommandHelp msuper cmd | ListOptions `elem` opts -> do setProgressMode False commandPrereq cmd opts file_args <- commandGetArgPossibilities cmd- putStrLn $ get_options_options (opts1++opts2) ++ unlines file_args- | otherwise -> consider_running msuper cmd (addVerboseIfDebug opts) extra+ putStrLn $ unlines $ getOptionsOptions (opts1++opts2) : file_args+ | otherwise -> considerRunning msuper cmd (addVerboseIfDebug opts) extra (_,_,ermsgs) -> do fail $ chompNewline(unlines ermsgs) where addVerboseIfDebug opts | DebugVerbose `elem` opts = Debug:Verbose:opts | otherwise = opts -consider_running :: Maybe DarcsCommand -> DarcsCommand+considerRunning :: Maybe DarcsCommand -> DarcsCommand -> [DarcsFlag] -> [String] -> IO ()-consider_running msuper cmd opts old_extra = do+considerRunning msuper cmd opts old_extra = do cwd <- getCurrentDirectory location <- commandPrereq cmd opts case location of@@ -104,7 +105,7 @@ formatPath ("darcs " ++ superName msuper ++ commandName cmd) ++ " here.\n\n" ++ complaint Right () -> do- specops <- add_command_defaults cmd opts+ specops <- nubopts `fmap` addCommandDefaults cmd opts extra <- (commandArgdefaults cmd) specops cwd old_extra when (Disable `elem` specops) $ fail $ "Command "++commandName cmd++" disabled with --disable option!"@@ -118,7 +119,8 @@ nth_arg (length extra + 1) ++ "\n" ++ getCommandMiniHelp msuper cmd else runWithHooks specops extra- where nth_arg n = nth_of n (commandExtraArgHelp cmd)+ 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"@@ -131,39 +133,39 @@ when (DebugHTTP `elem` os) setDebugHTTP when (Verbose `elem` os) setVerboseMode when (Quiet `elem` os) $ setProgressMode False- when (HTTPPipelining `elem` os) $ setHTTPPipelining True- when (NoHTTPPipelining `elem` os) $ setHTTPPipelining False+ when (NoHTTPPipelining `elem` os) $ disableHTTPPipelining unless (SSHControlMaster `elem` os) setSshControlMasterDisabled unless (Quiet `elem` os) $ atexit $ doCRCWarnings (Verbose `elem` os) -- actually run the command and its hooks- preHookExitCode <- run_prehook os here+ preHookExitCode <- runPrehook os here if preHookExitCode /= ExitSuccess then exitWith preHookExitCode else do let fixFlag = FixFilePath here cwd- (commandCommand cmd) (fixFlag : os) ex- postHookExitCode <- run_posthook os here+ fixedOs <- mapM (fixUrlFlag [fixFlag]) os+ (commandCommand cmd) (fixFlag : fixedOs) ex+ postHookExitCode <- runPosthook os here exitWith postHookExitCode -add_command_defaults :: DarcsCommand -> [DarcsFlag] -> IO [DarcsFlag]-add_command_defaults cmd already = do+addCommandDefaults :: DarcsCommand -> [DarcsFlag] -> IO [DarcsFlag]+addCommandDefaults cmd already = do let (opts1, opts2) = commandAlloptions cmd- defaults <- get_default_flags (commandName cmd) (opts1 ++ opts2) already+ defaults <- getDefaultFlags (commandName cmd) (opts1 ++ opts2) already return $ already ++ defaults -get_options_options :: [OptDescr DarcsFlag] -> String-get_options_options = concat . intersperse "\n" . concatMap goo+getOptionsOptions :: [OptDescr DarcsFlag] -> String+getOptionsOptions = concat . intersperse "\n" . concatMap goo where goo (Option _ os _ _) = map ("--"++) os -run_raw_supercommand :: DarcsCommand -> [String] -> IO ()-run_raw_supercommand super [] =+runRawSupercommand :: DarcsCommand -> [String] -> IO ()+runRawSupercommand super [] = fail $ "Command '"++ commandName super ++"' requires subcommand!\n\n" ++ subusage super-run_raw_supercommand super args = do+runRawSupercommand super args = do cwd <- getCurrentDirectory case getOpt RequireOrder- (optionFromDarcsoption cwd help++- optionFromDarcsoption cwd listOptions) args of+ (optionFromDarcsOption cwd help+++ optionFromDarcsOption cwd listOptions) args of (opts,_,[]) | Help `elem` opts -> viewDoc $ text $ getCommandHelp Nothing super
src/Darcs/SelectChanges.hs view
@@ -21,647 +21,840 @@ #include "gadts.h" -module Darcs.SelectChanges ( with_selected_changes',- with_selected_changes_to_files',- with_selected_last_changes_to_files',- with_selected_last_changes_reversed',- with_selected_changes,- with_selected_changes_to_files,- with_selected_changes_reversed,- with_selected_last_changes_to_files,- with_selected_last_changes_to_files_reversed,- with_selected_last_changes_reversed,- view_changes,- with_selected_patch_from_repo,- filterOutConflicts,- ) where-import System.IO-import Data.List ( intersperse )-import Data.Maybe ( catMaybes, isJust )-import Data.Char ( toUpper )-import Control.Monad ( when )-import System.Exit ( exitWith, ExitCode(ExitSuccess) )--import English ( Noun(..), englishNum )-import Darcs.Arguments ( showFriendly )-import Darcs.Hopefully ( PatchInfoAnd, hopefully, n2pia )-import Darcs.Repository ( Repository, read_repo, unrecordedChanges )-import Darcs.Patch ( RepoPatch, Patchy, Prim, summary,- invert, listTouchedFiles,- commuteFL, fromPrims, anonymous )-import qualified Darcs.Patch ( thing, things )-import Darcs.Patch.Split ( Splitter(..) )-import Darcs.Witnesses.Ordered ( FL(..), RL(..), (:>)(..), (:||:)(..),- (+>+), lengthFL, lengthRL, concatRL, mapFL_FL,- spanFL, reverseFL, (+<+), mapFL,- unsafeCoerceP )-import Darcs.Patch.Choices ( PatchChoices, patchChoices, patchChoicesTps,- patchChoicesTpsSub,- forceFirst, forceLast, makeUncertain, tag,- getChoices,- separateFirstMiddleFromLast,- separateFirstFromMiddleLast,- patchSlot,- selectAllMiddles,- forceMatchingLast,- forceMatchingFirst, makeEverythingLater,- TaggedPatch, tpPatch, Slot(..),- substitute,- )-import Darcs.Patch.Permutations ( partitionConflictingFL, selfCommuter, commuterIdRL )-import Darcs.Patch.TouchesFiles ( deselect_not_touching, select_not_touching )-import Darcs.PrintPatch ( printFriendly, printPatch, printPatchPager )-import Darcs.Match ( haveNonrangeMatch, matchAPatch, matchAPatchread )-import Darcs.Flags ( DarcsFlag( Summary, DontGrabDeps, Verbose, DontPromptForDependencies, SkipConflicts), isInteractive )-import Darcs.Witnesses.Sealed ( FlippedSeal(..), flipSeal, seal2, unseal2, Sealed(..) )-import Darcs.Utils ( askUser, promptCharFancy )-import Darcs.Lock ( editText )-import Printer ( prefix, putDocLn )-#include "impossible.h"--data WhichChanges = Last | LastReversed | First | FirstReversed deriving (Eq, Show)--type MatchCriterion p = FORALL(u v) WhichChanges -> [DarcsFlag] -> (p C(u v)) -> Bool--type WithPatches p a C(x y) =- String -- jobname- -> [DarcsFlag] -- opts- -> Maybe (Splitter p) -- for interactive editing- -> FL p C(x y) -- patches to select among- -> ((FL p :> FL p) C(x y) -> IO a) -- job- -> IO a -- result of running job---- | The only difference with 'WithPatches' is the [FilePath] argument-type WithPatchesToFiles p a C(x y) =- String -- jobname- -> [DarcsFlag] -- opts- -> Maybe (Splitter p) -- for interactive editing- -> [FilePath] -- files- -> FL p C(x y) -- patches to select among- -> ((FL p :> FL p) C(x y) -> IO a) -- job- -> IO a -- result of running job--with_selected_changes'- :: WithPatches Prim a C(x y)-with_selected_changes_to_files'- :: WithPatchesToFiles Prim a C(x y)-with_selected_last_changes_to_files'- :: WithPatchesToFiles Prim a C(x y)-with_selected_last_changes_reversed'- :: WithPatches Prim a C(x y)---- Common match criteria-triv :: MatchCriterion p-triv _ _ _ = True--iswanted :: Patchy p => MatchCriterion (PatchInfoAnd p)-iswanted First opts p = matchAPatch opts . hopefully $ p-iswanted LastReversed opts p = matchAPatch opts . hopefully . invert $ p-iswanted Last _ _ = bug "don't support patch matching with Last in wasp"-iswanted FirstReversed _ _ = bug "don't support patch matching with FirstReversed in wasp"--with_selected_changes' = wasc First triv-with_selected_changes_to_files' = wasc_ First triv-with_selected_last_changes_to_files' = wasc_ Last triv-with_selected_last_changes_reversed' = wasc LastReversed triv--with_selected_changes :: RepoPatch p => WithPatches (PatchInfoAnd p) a C(x y)-with_selected_changes_to_files :: RepoPatch p => WithPatchesToFiles (PatchInfoAnd p) a C(x y)-with_selected_changes_reversed :: RepoPatch p => WithPatches (PatchInfoAnd p) a C(x y)-with_selected_last_changes_to_files :: RepoPatch p => WithPatchesToFiles (PatchInfoAnd p) a C(x y)-with_selected_last_changes_to_files_reversed :: RepoPatch p => WithPatchesToFiles (PatchInfoAnd p) a C(x y)-with_selected_last_changes_reversed :: RepoPatch p => WithPatches (PatchInfoAnd p) a C(x y)--with_selected_changes = wasc First iswanted-with_selected_changes_to_files = wasc_ First iswanted-with_selected_changes_reversed = wasc FirstReversed iswanted-with_selected_last_changes_to_files = wasc_ Last iswanted-with_selected_last_changes_to_files_reversed = wasc_ LastReversed iswanted-with_selected_last_changes_reversed = wasc LastReversed iswanted---- | wasc and wasc_ are just shorthand for with_any_selected_changes-wasc :: Patchy p => WhichChanges -> MatchCriterion p -> WithPatches p a C(x y)-wasc mwch crit j o spl = wasc_ mwch crit j o spl []-wasc_ :: Patchy p => WhichChanges -> MatchCriterion p -> WithPatchesToFiles p a C(x y)-wasc_ = with_any_selected_changes--with_any_selected_changes :: Patchy p => WhichChanges -> MatchCriterion p -> WithPatchesToFiles p a C(x y)-with_any_selected_changes Last crit jn opts splitter fs =- with_any_selected_changes_last- (patches_to_consider_last' fs opts crit)- crit jn opts splitter fs-with_any_selected_changes First crit jn opts splitter fs =- with_any_selected_changes_first- (patches_to_consider_first' fs opts crit)- crit jn opts splitter fs-with_any_selected_changes FirstReversed crit jn opts splitter fs =- with_any_selected_changes_first_reversed- (patches_to_consider_first_reversed' fs opts crit)- crit jn opts splitter fs-with_any_selected_changes LastReversed crit jn opts splitter fs =- with_any_selected_changes_last_reversed- (patches_to_consider_last_reversed' fs opts crit)- crit jn opts splitter fs---view_changes :: RepoPatch p => [DarcsFlag] -> FL (PatchInfoAnd p) C(x y) -> IO ()-view_changes opts ps = do- text_view opts Nothing 0 NilRL init_tps init_pc- return ()- where (init_pc, init_tps) = patchChoicesTps ps--data KeyPress a = KeyPress { kp :: Char- , kpHelp :: String }--helpFor :: String -> [[KeyPress a]] -> String-helpFor jobname options =- unlines $ [ "How to use "++jobname++":" ]- ++ (concat $ intersperse [""] $ map (map help) options)- ++ [ ""- , "?: show this help"- , ""- , "<Space>: accept the current default (which is capitalized)"- ]- where help i = kp i:(": "++kpHelp i)--keysFor :: [[KeyPress a]] -> [Char]-keysFor = concatMap (map kp)--with_selected_patch_from_repo :: forall p C(r u t). RepoPatch p => String -> Repository p C(r u t) -> [DarcsFlag]- -> (FORALL(a) (FL (PatchInfoAnd p) :> PatchInfoAnd p) C(a r) -> IO ()) -> IO ()-with_selected_patch_from_repo jn repository opts job = do- p_s <- read_repo repository- sp <- wspfr jn (matchAPatchread opts)- (concatRL p_s) NilFL- case sp of- Just (FlippedSeal (skipped :> selected)) -> job (skipped :> selected)- Nothing -> do 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.-wspfr :: RepoPatch p => 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- | not $ matches p = wspfr jn matches pps (p:>:skipped)- | otherwise =- case commuteFL (p :> skipped) of- Left _ -> do putStrLn "\nSkipping depended-upon patch:"- printFriendly [] p- wspfr jn matches pps (p:>:skipped)- Right (skipped' :> p') -> do- printFriendly [] p- let repeat_this = wspfr jn matches (p:<:pps) skipped- options = [[ KeyPress 'y' (jn++" this patch")- , KeyPress 'n' ("don't "++jn++" it")- , 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)- ]]- let prompt = "Shall I "++jn++" this patch?"- yorn <- promptCharFancy prompt (keysFor options) (Just 'n') "?h"- case yorn of- 'y' -> return $ Just $ flipSeal $ skipped' :> p'- 'n' -> wspfr jn matches pps (p:>: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 options- repeat_this- where jn_cap = (toUpper $ head jn) : tail jn---- After selecting with a splitter, the results may not be canonical-canonizeWith :: Maybe (Splitter p) -> (FL p :> FL p) C(x y) -> (FL p :> FL p) C(x y)-canonizeWith Nothing xy = xy-canonizeWith (Just spl) (x :> y) = canonizeSplit spl x :> canonizeSplit spl y--with_any_selected_changes_last :: forall p a C(x y). Patchy p- => (FL p C(x y) -> (FL p :> FL p) C(x y))- -> MatchCriterion p- -> WithPatchesToFiles p a C(x y)-with_any_selected_changes_last p2c crit jobname opts splitter _ ps job =- case p2c ps of- ps_to_consider :> other_ps ->- if not $ isInteractive opts- then job $ ps_to_consider :> other_ps- else do pc <- tentatively_text_select splitter "" jobname (Noun "patch") Last crit- opts ps_len 0 NilRL init_tps init_pc- job $ canonizeWith splitter $ selected_patches_last rejected_ps pc- where rejected_ps = ps_to_consider- ps_len = lengthFL init_tps- (init_pc, init_tps) = patchChoicesTps $ other_ps--with_any_selected_changes_first :: forall p a C(x y). Patchy p- => (FL p C(x y) -> (FL p :> FL p) C(x y))- -> MatchCriterion p- -> WithPatchesToFiles p a C(x y)-with_any_selected_changes_first p2c crit jobname opts splitter _ ps job =- case p2c ps of- ps_to_consider :> other_ps ->- if not $ isInteractive opts- then job $ ps_to_consider :> other_ps- else do pc <- tentatively_text_select splitter "" jobname (Noun "patch") First crit- opts ps_len 0 NilRL init_tps init_pc- job $ canonizeWith splitter $ selected_patches_first rejected_ps pc- where rejected_ps = other_ps- ps_len = lengthFL init_tps- (init_pc, init_tps) = patchChoicesTps $ ps_to_consider--with_any_selected_changes_first_reversed :: forall p a C(x y). Patchy p- => (FL p C(x y) -> (FL p :> FL p) C(y x))- -> MatchCriterion p- -> WithPatchesToFiles p a C(x y)-with_any_selected_changes_first_reversed p2c crit jobname opts splitter _ ps job =- case p2c ps of- ps_to_consider :> other_ps ->- if not $ isInteractive opts- then job $ invert other_ps :> invert ps_to_consider- else do pc <- tentatively_text_select splitter- "" jobname (Noun "patch") FirstReversed crit- opts ps_len 0 NilRL init_tps init_pc- job $ canonizeWith splitter $ selected_patches_first_reversed rejected_ps pc- where rejected_ps = ps_to_consider- ps_len = lengthFL init_tps- (init_pc, init_tps) = patchChoicesTps other_ps--with_any_selected_changes_last_reversed :: forall p a C(x y). Patchy p- => (FL p C(x y) -> (FL p :> FL p) C(y x))- -> MatchCriterion p- -> WithPatchesToFiles p a C(x y)-with_any_selected_changes_last_reversed p2c crit jobname opts splitter _ ps job =- case p2c ps of- ps_to_consider :> other_ps ->- if not $ isInteractive opts- then job $ invert other_ps :> invert ps_to_consider- else do pc <- tentatively_text_select splitter- "" jobname (Noun "patch") LastReversed crit- opts ps_len 0 NilRL init_tps init_pc- job $ canonizeWith splitter $ selected_patches_last_reversed rejected_ps pc- where rejected_ps = other_ps- ps_len = lengthFL init_tps- (init_pc, init_tps) = patchChoicesTps ps_to_consider---patches_to_consider_first' :: Patchy p- => [FilePath] -- ^ files- -> [DarcsFlag] -- ^ opts- -> MatchCriterion p- -> FL p C(x y) -- ^ patches- -> (FL p :> FL p) C(x y)-patches_to_consider_first' fs opts crit ps =- let deselect_unwanted pc =- if haveNonrangeMatch opts- then if DontGrabDeps `elem` opts- then forceMatchingLast (not.iswanted_) pc- else makeEverythingLater $ forceMatchingFirst iswanted_ pc- else pc- iswanted_ = crit First opts . tpPatch- in if null fs && not (haveNonrangeMatch opts)- then ps :> NilFL- else tp_patches $ separateFirstMiddleFromLast $ deselect_not_touching fs- $ deselect_unwanted $ patchChoices ps--patches_to_consider_last' :: Patchy p- => [FilePath] -- ^ files- -> [DarcsFlag] -- ^ opts- -> MatchCriterion p- -> FL p C(x y) -- ^ patches- -> (FL p :> FL p) C(x y)-patches_to_consider_last' fs opts crit ps =- let deselect_unwanted pc =- if haveNonrangeMatch opts- then if DontGrabDeps `elem` opts- then forceMatchingLast (not.iswanted_) pc- else makeEverythingLater $ forceMatchingFirst iswanted_ pc- else pc- iswanted_ = crit Last opts . tpPatch- in if null fs && not (haveNonrangeMatch opts)- then NilFL :> ps- else case getChoices $ select_not_touching fs $ deselect_unwanted $ patchChoices ps of- fc :> mc :> lc -> tp_patches $ fc :> mc +>+ lc--patches_to_consider_first_reversed' :: Patchy p- => [FilePath] -- ^ files- -> [DarcsFlag] -- ^ opts- -> MatchCriterion p- -> FL p C(x y) -- ^ patches- -> (FL p :> FL p) C(y x)-patches_to_consider_first_reversed' fs opts crit ps =- let deselect_unwanted pc =- if haveNonrangeMatch opts- then if DontGrabDeps `elem` opts- then forceMatchingLast (not.iswanted_) pc- else makeEverythingLater $ forceMatchingFirst iswanted_ pc- else pc- iswanted_ = crit FirstReversed opts . tpPatch- in if null fs && not (haveNonrangeMatch opts)- then NilFL :> (invert ps)- else case getChoices $ select_not_touching fs $ deselect_unwanted $ patchChoices $ invert ps of- fc :> mc :> lc -> tp_patches $ fc :> mc +>+ lc--patches_to_consider_last_reversed' :: Patchy p- => [FilePath] -- ^ files- -> [DarcsFlag] -- ^ opts- -> MatchCriterion p- -> FL p C(x y) -- ^ patches- -> (FL p :> FL p) C(y x)-patches_to_consider_last_reversed' fs opts crit ps =- let deselect_unwanted pc =- if haveNonrangeMatch opts- then if DontGrabDeps `elem` opts- then forceMatchingLast (not.iswanted_) pc- else makeEverythingLater $ forceMatchingFirst iswanted_ pc- else pc- iswanted_ = crit LastReversed opts . tpPatch- in- if null fs && not (haveNonrangeMatch opts)- then (invert ps) :> NilFL- else tp_patches $ separateFirstMiddleFromLast $ deselect_not_touching fs- $ deselect_unwanted $ patchChoices $ invert ps---- | Returns the results of a patch selection user interaction-selected_patches_last :: Patchy p => FL p C(x y) -> PatchChoices p C(y z)- -> (FL p :> FL p) C(x z)-selected_patches_last other_ps pc =- case getChoices pc of- fc :> mc :> lc -> other_ps +>+ mapFL_FL tpPatch (fc +>+ mc) :> mapFL_FL tpPatch lc--selected_patches_first :: Patchy p => FL p C(y z) -> PatchChoices p C(x y)- -> (FL p :> FL p) C(x z)-selected_patches_first other_ps pc =- case separateFirstFromMiddleLast pc of- xs :> ys -> mapFL_FL tpPatch xs :> mapFL_FL tpPatch ys +>+ other_ps--selected_patches_last_reversed :: Patchy p => FL p C(y x) -> PatchChoices p C(z y)- -> (FL p :> FL p) C(x z)-selected_patches_last_reversed other_ps pc =- case separateFirstFromMiddleLast pc of- xs :> ys -> invert (mapFL_FL tpPatch ys +>+ other_ps) :> invert (mapFL_FL tpPatch xs)--selected_patches_first_reversed :: Patchy p => FL p C(z y) -> PatchChoices p C(y x)- -> (FL p :> FL p) C(x z)-selected_patches_first_reversed other_ps pc =- case getChoices pc of- fc :> mc :> lc -> invert (mapFL_FL tpPatch lc) :> invert (other_ps +>+ mapFL_FL tpPatch (fc +>+ mc))--text_select :: forall p C(x y z). Patchy p => Maybe (Splitter p) -> String -> WhichChanges- -> MatchCriterion p -> [DarcsFlag] -> Int -> Int- -> RL (TaggedPatch p) C(x y) -> FL (TaggedPatch p) C(y z) -> PatchChoices p C(x z)- -> IO ((PatchChoices p) C(x z))--text_select _ _ _ _ _ _ _ _ NilFL pc = return pc-text_select splitter jn whichch crit opts n_max n- tps_done tps_todo@(tp:>:tps_todo') pc = do- (printFriendly opts) `unseal2` viewp- repeat_this -- prompt the user- where- do_next_action ja je = tentatively_text_select splitter ja jn je whichch crit opts- n_max- (n+1) (tp:<:tps_done) tps_todo'- do_next = do_next_action "" (Noun "patch")- helper :: PatchChoices p C(a b) -> p C(a b)- helper = undefined- thing = Darcs.Patch.thing (helper pc)- things = Darcs.Patch.things (helper pc)- split = splitter >>= flip applySplitter (tpPatch tp)- options_basic =- [ KeyPress 'y' (jn++" this "++thing)- , KeyPress 'n' ("don't "++jn++" it")- , KeyPress 'w' ("wait and decide later, defaulting to no") ]- options_file =- [ KeyPress 's' ("don't "++jn++" the rest of the changes to this file")- , KeyPress 'f' (jn++" the rest of the changes to this file") ]- options_view =- [ KeyPress 'v' ("view this "++thing++" in full")- , KeyPress 'p' ("view this "++thing++" in full with pager")- , KeyPress 'l' ("list all selected "++things) ]- options_summary =- [ KeyPress 'x' ("view a summary of this "++thing) ]- options_quit =- [ KeyPress 'd' (jn++" selected "++things++", skipping all the remaining "++things)- , KeyPress 'a' (jn++" all the remaining "++things)- , KeyPress 'q' ("cancel "++jn) ]- options_nav =- [ KeyPress 'j' ("skip to next "++thing)- , KeyPress 'k' ("back up to previous "++thing) ]- options_split- | Just _ <- split- = [ KeyPress 'e' ("interactively edit this "++thing) ]- | otherwise = []- options = [options_basic]- ++ [options_split]- ++ (if is_single_file_patch then [options_file] else [])- ++ [options_view ++- if Summary `elem` opts then [] else options_summary]- ++ [options_quit]- ++ [options_nav ]- prompt = "Shall I "++jn++" this "++thing++"? "- ++ "(" ++ show (n+1) ++ "/" ++ show n_max ++ ") "- repeat_this :: IO ((PatchChoices p) C(x z))- repeat_this = do- yorn <- promptCharFancy prompt (keysFor options) (Just the_default) "?h"- case yorn of- 'y' -> do_next $ force_yes (tag tp) pc- 'n' -> do_next $ force_no (tag tp) pc- 'w' -> do_next $ makeUncertain (tag tp) pc- 'e' | Just (text, parse) <- split- -> do newText <- editText "darcs-patch-edit" text- case parse newText of- Nothing -> repeat_this- Just ps -> do let tps_new = snd $ patchChoicesTpsSub (Just (tag tp)) ps- text_select splitter- jn whichch crit opts (n_max + lengthFL tps_new - 1) n- tps_done (tps_new+>+tps_todo')- (substitute (seal2 (tp :||: tps_new)) pc)-- 's' -> do_next_action "Skipped" (Noun "change") $ skip_file- 'f' -> do_next_action "Included" (Noun "change") $ do_file- 'v' -> printPatch `unseal2` viewp >> repeat_this- 'p' -> printPatchPager `unseal2` viewp >> repeat_this- 'l' -> do let selected = case getChoices pc of- (first_chs:>_:>last_chs) ->- if whichch == Last || whichch == FirstReversed- then map_patches last_chs- else map_patches first_chs- map_patches = mapFL (\a ->- (showFriendly opts) `unseal2` (seal2 $ tpPatch a))- putStrLn $ "---- Already selected "++things++" ----"- mapM_ putDocLn $ selected- putStrLn $ "---- end of already selected "++things++" ----"- (printFriendly opts) `unseal2` viewp- repeat_this- 'x' -> do (putDocLn . prefix " " . summary) `unseal2` viewp- repeat_this- 'd' -> return pc- 'a' -> do ask_confirmation- return $ selectAllMiddles (whichch == Last || whichch == FirstReversed) pc- 'q' -> do putStrLn $ jn_cap++" cancelled."- exitWith $ ExitSuccess- 'j' -> case tps_todo' of- NilFL -> -- May as well work out the length now we have all- -- the patches in memory- text_select splitter jn whichch crit opts- n_max n tps_done tps_todo pc- _ -> text_select splitter jn whichch crit opts- n_max (n+1) (tp:<:tps_done) tps_todo' pc- 'k' -> case tps_done of- NilRL -> repeat_this- (tp':<:tps_done') ->- text_select splitter jn whichch crit opts- n_max (n-1) tps_done' (tp':>:tps_todo) pc- 'c' -> text_select splitter jn whichch crit opts- n_max n tps_done tps_todo pc- _ -> do putStrLn $ helpFor jn options- repeat_this- force_yes = if whichch == Last || whichch == FirstReversed then forceLast else forceFirst- force_no = if whichch == Last || whichch == FirstReversed then forceFirst else forceLast- patches_to_skip = (tag tp:) $ catMaybes- $ mapFL (\tp' -> if listTouchedFiles tp' == touched_files- then Just (tag tp')- else Nothing) tps_todo'- skip_file = foldr force_no pc patches_to_skip- do_file = foldr force_yes pc patches_to_skip- the_default = get_default (whichch == Last || whichch == FirstReversed) $ patchSlot tp pc- jn_cap = (toUpper $ head jn) : tail jn- touched_files = listTouchedFiles $ tpPatch tp- is_single_file_patch = length touched_files == 1- viewp = if whichch == LastReversed || whichch == FirstReversed then seal2 $ invert (tpPatch tp) else seal2 $ tpPatch tp- ask_confirmation =- if jn `elem` ["unpull", "unrecord", "obliterate"]- then do yorn <- askUser $ "Really " ++ jn ++ " all undecided patches? "- case yorn of- ('y':_) -> return ()- _ -> exitWith $ ExitSuccess- else return ()--text_view :: forall p C(x y u r s). Patchy p => [DarcsFlag] -> Maybe Int -> Int- -> RL (TaggedPatch p) C(x y) -> FL (TaggedPatch p) C(y u) -> PatchChoices p C(r s)- -> IO ((PatchChoices p) C(r s))-text_view _ _ _ _ NilFL _ = return $ patchChoices $ unsafeCoerceP NilFL --return pc-text_view opts n_max n- tps_done tps_todo@(tp:>:tps_todo') pc = do- printFriendly opts (tpPatch tp)- putStr "\n"- repeat_this -- prompt the user- where- prev_patch :: IO ((PatchChoices p) C(r s))- prev_patch = case tps_done of- NilRL -> repeat_this- (tp':<:tps_done') ->- text_view opts- n_max (n-1) tps_done' (tp':>:tps_todo) pc- next_patch :: IO ((PatchChoices p) C(r s))- next_patch = case tps_todo' of- NilFL -> -- May as well work out the length now we have all- -- the patches in memory- text_view opts n_max- n tps_done NilFL pc- _ -> text_view opts n_max- (n+1) (tp:<:tps_done) tps_todo' pc- options_yn =- [ KeyPress 'y' "view this patch and go to the next"- , KeyPress 'n' "skip to the next patch" ]- options_view =- [ KeyPress 'v' "view this patch in full"- , KeyPress 'p' "view this patch in full with pager" ]- options_summary =- [ KeyPress 'x' "view a summary of this patch" ]- options_nav =- [ KeyPress 'q' ("quit view changes")- , KeyPress 'k' "back up to previous patch"- , KeyPress 'j' "skip to next patch"- , KeyPress 'c' "count total patch number" ]- options = [ options_yn ]- ++ [ options_view ++- if Summary `elem` opts then [] else options_summary ]- ++ [ options_nav ]- prompt = "Shall I view this patch? "- ++ "(" ++ show (n+1) ++ "/" ++ maybe "?" show n_max ++ ")"- repeat_this :: IO ((PatchChoices p) C(r s))- repeat_this = do- yorn <- promptCharFancy prompt (keysFor options) (Just 'n') "?h"- case yorn of- 'y' -> printPatch (tpPatch tp) >> next_patch- 'n' -> next_patch- 'v' -> printPatch (tpPatch tp) >> repeat_this- 'p' -> printPatchPager (tpPatch tp) >> repeat_this- 'x' -> do putDocLn $ prefix " " $ summary (tpPatch tp)- repeat_this- 'q' -> exitWith ExitSuccess- 'k' -> prev_patch- 'j' -> next_patch- 'c' -> text_view opts- count_n_max n tps_done tps_todo pc- _ -> do putStrLn $ helpFor "view changes" options- repeat_this- count_n_max | isJust n_max = n_max- | otherwise = Just $ lengthFL tps_todo + lengthRL tps_done-tentatively_text_select :: Patchy p => Maybe (Splitter p) -> String -> String -> Noun -> WhichChanges- -> MatchCriterion p -> [DarcsFlag]- -> Int -> Int -> RL (TaggedPatch p) C(x y) -> FL (TaggedPatch p) C(y z)- -> PatchChoices p C(x z)- -> IO ((PatchChoices p) C(x z))-tentatively_text_select _ _ _ _ _ _ _ _ _ _ NilFL pc = return pc-tentatively_text_select splitter jobaction jobname jobelement whichch crit- opts n_max n ps_done ps_todo pc =- case spanFL (\p -> decided $ patchSlot p pc) ps_todo of- skipped :> unskipped -> do- when (numSkipped > 0) show_skipped- let (boringThenInteresting) =- if DontPromptForDependencies `elem` opts- then spanFL (not.(crit whichch opts).tpPatch) unskipped- else NilFL :> unskipped- case boringThenInteresting of- boring :> interesting -> do- let numNotConsidered = lengthFL boring + numSkipped- text_select splitter jobname whichch crit opts n_max (n + numNotConsidered)- (reverseFL boring +<+ reverseFL skipped +<+ ps_done) interesting pc- where- numSkipped = lengthFL skipped- show_skipped = do putStrLn $ _doing_ ++ _with_ ++ "."- when (Verbose `elem` opts) $ showskippedpatch skipped- where- _doing_ = _action_ ++ " " ++ jobname- _with_ = " of " ++ show numSkipped ++ " " ++ _elem_ ""- _action_ = if (length jobaction) == 0 then "Skipped" else jobaction- _elem_ = englishNum numSkipped jobelement- showskippedpatch :: Patchy p => FL (TaggedPatch p) C(y t) -> IO ()- showskippedpatch (tp:>:tps) = (putDocLn $ prefix " " $ summary (tpPatch tp)) >> showskippedpatch tps- showskippedpatch NilFL = return ()--decided :: Slot -> Bool-decided InMiddle = False-decided _ = True--get_default :: Bool -> Slot -> Char-get_default _ InMiddle = 'w'-get_default True InFirst = 'n'-get_default True InLast = 'y'-get_default False InFirst = 'y'-get_default False InLast = 'n'--tp_patches :: (FL (TaggedPatch p) :> FL (TaggedPatch p)) C(x y)- -> (FL p :> FL p) C(x y)-tp_patches (x:>y) = mapFL_FL tpPatch x :> mapFL_FL tpPatch y---- |Optionally remove any patches (+dependencies) from a sequence that--- conflict with the recorded or unrecorded changes in a repo-filterOutConflicts :: RepoPatch p- => [DarcsFlag] -- ^Command-line options. Only 'SkipConflicts' is- -- significant; filtering will happen iff it is present- -> RL (PatchInfoAnd p) C(x r) -- ^Recorded patches from repository, starting from- -- same context as the patches to filter- -> Repository p C(r u t) -- ^Repository itself, used for grabbing unrecorded changes- -> FL (PatchInfoAnd p) C(x z) -- ^Patches to filter- -> IO (Bool, Sealed (FL (PatchInfoAnd p) C(x))) -- ^(True iff any patches were removed, possibly filtered patches)-filterOutConflicts opts us repository them- | SkipConflicts `elem` opts+module Darcs.SelectChanges (selectChanges, WhichChanges(..), viewChanges, withSelectedPatchFromRepo,+ filterOutConflicts, runSelection, selectionContextPrim,+ selectionContext+ ) where+import System.IO+import Data.List ( intersperse )+import Data.Maybe ( catMaybes, isJust, fromJust )+import Data.Char ( toUpper )+import Control.Monad ( when, (>=>) )+import Control.Monad.Trans ( liftIO )+import Control.Monad.Reader ( ReaderT, Reader, asks, runReader, runReaderT )+import Control.Monad.State ( State(..), StateT, modify, gets, execStateT )+import System.Exit ( exitWith, ExitCode(ExitSuccess) )++import English ( Noun(..), englishNum )+import Darcs.Arguments ( showFriendly )+import Darcs.Hopefully ( PatchInfoAnd, hopefully, n2pia )+import Darcs.Repository ( Repository, readRepo, unrecordedChanges )+import Darcs.Patch ( RepoPatch, Patchy, Prim, summary,+ invert, listTouchedFiles,+ commuteFLorComplain, fromPrims, anonymous )+import Darcs.Patch.Set ( newset2RL )+import qualified Darcs.Patch ( thing, things )+import Darcs.Patch.Split ( Splitter(..) )+import Darcs.Witnesses.Ordered ( FL(..), RL(..), (:>)(..), (:||:)(..),+ (+>+), lengthFL, concatRL, mapFL_FL,+ spanFL, spanFL_M, reverseFL, (+<+), mapFL, filterFL )+import Darcs.Witnesses.WZipper( FZipper(..), left, right+ , rightmost, lengthFZ, focus+ , toEnd)+import Darcs.Patch.Choices ( PatchChoices, patchChoices,+ patchChoicesTpsSub,+ forceFirst, forceLast, makeUncertain, tag,+ getChoices, refineChoices,+ separateFirstFromMiddleLast,+ patchSlot',+ selectAllMiddles,+ forceMatchingLast,+ forceMatchingFirst, makeEverythingLater,+ makeEverythingSooner,+ TaggedPatch, tpPatch, Slot(..),+ substitute+ )+import Darcs.Patch.Permutations ( partitionConflictingFL, selfCommuter, commuterIdRL )+import qualified Darcs.Patch.TouchesFiles as TouchesFiles+import Darcs.PrintPatch ( printFriendly, printPatch, printPatchPager )+import Darcs.Match ( haveNonrangeMatch, matchAPatch, matchAPatchread )+import Darcs.Flags ( DarcsFlag( Summary, DontGrabDeps, Verbose, DontPromptForDependencies, SkipConflicts), isInteractive )+import Darcs.Witnesses.Sealed ( FlippedSeal(..), flipSeal, seal2, unseal2, Sealed(..), Sealed2(..) )+import Darcs.Utils ( askUser, promptChar, PromptConfig(..) )+import Darcs.Lock ( editText )+import Printer ( prefix, putDocLn )++-- | When asking about patches, we either ask about them in+-- oldest-first or newest first (with respect to the current ordering+-- of the repository), and we either want an initial segment or a+-- final segment of the poset of patches.+--+-- @First@: ask for an initial+-- segment, first patches first (default for all pull-like commands)+--+-- @FirstReversed@: ask for an initial segment, last patches first+-- (used to ask about dependencies in record, and for pull-like+-- commands with the @--reverse@ flag).+--+-- @LastReversed@: ask for a final segment, last patches first. (default+-- for unpull-like commands, except for selecting *primitive* patches in+-- rollback)+--+-- @Last@: ask for a final segment, first patches first. (used for selecting+-- primitive patches in rollback, and for unpull-like commands with the+-- @--reverse@ flag+data WhichChanges = Last | LastReversed | First | FirstReversed deriving (Eq, Show)++-- | A @WhichChanges@ is backwards if the order in which patches are presented+-- is the opposite of the order of dependencies for that operation.+backward :: WhichChanges -> Bool+backward w = w == Last || w == FirstReversed++-- | The type of the function we use to filter patches when @--match@ is+-- given.+type MatchCriterion p = WhichChanges -> [DarcsFlag] -> Sealed2 p -> Bool++-- | A @PatchSelectionContext@ contains all the static settings for selecting+-- patches. See "PatchSelectionM"+data PatchSelectionContext p = PSC { opts :: [DarcsFlag]+ , splitter :: Maybe (Splitter p)+ , files :: [FilePath]+ , matchCriterion :: MatchCriterion p+ , jobname :: String }++-- | A 'PatchSelectionContext' for selecting 'Prim' patches.+selectionContextPrim :: String -> [DarcsFlag] -> Maybe (Splitter Prim)+ -> [FilePath] -> PatchSelectionContext Prim+selectionContextPrim jn o spl fs =+ PSC { opts = o+ , splitter = spl+ , files = fs+ , matchCriterion = triv+ , jobname = jn }++-- | A 'PatchSelectionContext' for selecting full patches ('PatchInfoAnd' patches)+selectionContext :: (RepoPatch p) => String -> [DarcsFlag] -> Maybe (Splitter (PatchInfoAnd p))+ -> [FilePath] -> PatchSelectionContext (PatchInfoAnd p)+selectionContext jn o spl fs =+ PSC { opts = o+ , splitter = spl+ , files = fs+ , matchCriterion = iswanted+ , jobname = jn }++-- | The dynamic parameters for interactive selection of patches.+data InteractiveSelectionContext p C(x y) = ISC { total :: Int+ -- ^ total number of patches+ , current :: Int+ -- ^ number of already-seen patches+ , tps :: FZipper (TaggedPatch p) C(x y)+ -- ^ the patches we offer+ , choices :: PatchChoices p C(x y)+ -- ^ the user's choices+ }++type PatchSelectionM p a = ReaderT (PatchSelectionContext p) a++type InteractiveSelectionM p C(x y) a =+ StateT (InteractiveSelectionContext p C(x y))+ (PatchSelectionM p IO) a++type PatchSelection p C(x y) =+ PatchSelectionM p IO ((FL p :> FL p) C(x y))++-- Common match criteria++-- | For commands without @--match@, 'triv' matches all patches+triv :: MatchCriterion p+triv = \ _ _ _ -> True++-- | 'iswanted' selects patches according to the @--match@ flag in+-- opts'+iswanted :: Patchy p => MatchCriterion (PatchInfoAnd p)+iswanted whch opts' =+ unseal2 (iw whch opts')+ where+ iw First o = matchAPatch o . hopefully+ iw Last o = matchAPatch o . hopefully+ iw LastReversed o = matchAPatch o . hopefully . invert+ iw FirstReversed o = matchAPatch o . hopefully . invert++liftR :: Monad m => Reader r a -> ReaderT r m a+liftR = asks . runReader++-- | runs a 'PatchSelection' action in the given 'PatchSelectionContext'.+runSelection :: (Patchy p) => PatchSelection p C(x y) -> PatchSelectionContext p+ -> IO ((FL p :> FL p) C(x y))+runSelection = runReaderT++-- | Select patches from a @FL@.+selectChanges :: forall p C(x y) . Patchy p =>+ WhichChanges -> FL p C(x y)+ -> PatchSelection p C(x y)+selectChanges First = sc1 First+selectChanges Last = sc1 Last+selectChanges FirstReversed = return . invert+ >=> sc1 FirstReversed+ >=> return . invertC++selectChanges LastReversed = return . invert+ >=> sc1 LastReversed+ >=> return . invertC++sc1 :: forall p C(x y) . Patchy p =>+ WhichChanges -> FL p C(x y)+ -> PatchSelection p C(x y)+sc1 whch =+ ((liftR . patchesToConsider whch)+ >=> realSelectChanges whch+ >=> return . selectedPatches whch+ >=> (liftR . canonizeAfterSplitter))++-- | inverses the choices that have been made+invertC :: (Patchy p) => (FL p :> FL p) C(x y) -> (FL p :> FL p) C(y x)+invertC (a :> b) = (invert b) :> (invert a)++-- | Shows the patch that is actually being selected the way the user+-- should see it.+repr :: (Patchy p) => WhichChanges -> Sealed2 p -> Sealed2 p+repr First (Sealed2 p) = Sealed2 p+repr LastReversed (Sealed2 p) = Sealed2 (invert p)+repr Last (Sealed2 p) = Sealed2 p+repr FirstReversed (Sealed2 p) = Sealed2 (invert p)++-- | The equivalent of 'selectChanges' for the @darcs changes@ command+viewChanges :: Patchy p => [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+-- They are found in a [[KeyPress]] bunch, each list representing a set of+-- answers which belong together+data KeyPress = KeyPress { kp :: Char+ , kpHelp :: String }++-- | Generates the help for a set of basic and advanced 'KeyPress' groups.+helpFor :: String -> [[KeyPress]] -> [[KeyPress]] -> String+helpFor jn basicKeypresses advancedKeyPresses =+ unlines $ [ "How to use "++jn++":" ]+ ++ (concat $ intersperse [""] $ map (map help) keypresses)+ ++ [ ""+ , "?: show this help"+ , ""+ , "<Space>: accept the current default (which is capitalized)"+ ]+ where help i = kp i:(": "++kpHelp i)+ keypresses = basicKeypresses ++ advancedKeyPresses++-- | The keys used by a list of 'keyPress' groups.+keysFor :: [[KeyPress]] -> [Char]+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 => String -> 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+ case sp of+ Just (FlippedSeal (skipped :> selected)) -> job (skipped :> selected)+ Nothing -> do 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.+wspfr :: RepoPatch p => 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+ | not $ matches p = wspfr jn matches pps (p:>:skipped)+ | otherwise =+ case commuteFLorComplain (p :> skipped) of+ Left _ -> do putStrLn "\nSkipping depended-upon patch:"+ printFriendly [] 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")+ ]]+ advanced_options =+ [[ 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)+ ]]+ 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)+ '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))+canonizeAfterSplitter (x :> y) =+ do spl' <- asks splitter+ case spl' of+ Nothing -> return (x:>y)+ Just spl -> return $ canonizeSplit spl x :> canonizeSplit spl y++realSelectChanges :: forall p C(x y). Patchy p+ => WhichChanges+ -> PatchChoices p C(x y)+ -> PatchSelectionM p IO (PatchChoices p C(x y))+realSelectChanges whch autoChoices =+ do+ o <- asks opts+ if not $ isInteractive o+ then return $ promote autoChoices+ else flip refineChoices autoChoices $ textSelect whch+ where forward = not $ backward whch+ promote = if forward+ then makeEverythingSooner+ else makeEverythingLater++-- | When using @--match@, remove unmatched patches not depended upon by matched+-- patches.+deselectUnwanted :: forall p C(x y) . Patchy p => WhichChanges ->+ PatchChoices p C(x y) ->+ Reader (PatchSelectionContext p) (PatchChoices p C(x y))+deselectUnwanted whichch pc =+ do+ o <- asks opts+ c <- (asks matchCriterion)+ let iswanted_ = c whichch o . seal2 . tpPatch+ select = if forward+ then forceMatchingFirst iswanted_+ else forceMatchingLast iswanted_+ deselect = if forward+ then forceMatchingLast (not . iswanted_)+ else forceMatchingFirst (not . iswanted_)+ if haveNonrangeMatch o+ then if DontGrabDeps `elem` o+ then return $ deselect pc+ else do+ return . demote $ select pc+ else return pc+ where+ forward = not $ backward whichch+ demote = if forward+ then makeEverythingLater+ else makeEverythingSooner++-- | Selects the patches matching the match criterion, and puts them first or last+-- according to whch, while respecting any dependencies.+patchesToConsider :: Patchy p+ => WhichChanges+ -> FL p C(x y)+ -> Reader (PatchSelectionContext p) (PatchChoices p C(x y))+patchesToConsider whch ps =+ do+ fs <- asks files+ o <- asks opts+ let deselectNotTouching =+ case whch of+ First -> TouchesFiles.deselectNotTouching+ Last -> TouchesFiles.selectNotTouching+ FirstReversed -> TouchesFiles.selectNotTouching+ LastReversed -> TouchesFiles.deselectNotTouching+ everything = patchChoices ps+ if null fs && not (haveNonrangeMatch o)+ then return everything+ else do notUnwanted <- deselectUnwanted whch everything+ return $ deselectNotTouching fs notUnwanted++-- | Returns the results of a patch selection user interaction+selectedPatches :: Patchy p => WhichChanges -> PatchChoices p C(y z)+ -> (FL p :> FL p) C(y z)+selectedPatches Last pc =+ case getChoices pc of+ fc :> mc :> lc -> mapFL_FL tpPatch (fc +>+ mc) :> mapFL_FL tpPatch lc++selectedPatches First pc =+ case separateFirstFromMiddleLast pc of+ xs :> ys -> mapFL_FL tpPatch xs :> mapFL_FL tpPatch ys++selectedPatches LastReversed pc =+ case separateFirstFromMiddleLast pc of+ xs :> ys -> mapFL_FL tpPatch (xs) :> (mapFL_FL tpPatch (ys))++selectedPatches FirstReversed pc =+ case getChoices pc of+ fc :> mc :> lc -> (mapFL_FL tpPatch (fc +>+ mc)) :> (mapFL_FL tpPatch lc)++-- | Runs a function on the underlying @PatchChoices@ object+liftChoices :: forall p a C(x y) . Patchy p =>+ State (PatchChoices p C(x y)) a+ -> InteractiveSelectionM p C(x y) a+liftChoices act = do+ ch <- gets choices+ let (result, ch') = runState act ch+ modify $ \isc -> isc {choices = ch}+ return result++-- | @justDone n@ notes that @n@ patches have just been processed+justDone :: Patchy p => Int -> InteractiveSelectionM p C(x y) ()+justDone n = modify $ \isc -> isc{ current = current isc + n}++-- | The actual interactive selection process.+textSelect :: forall p C(x y) . Patchy p => 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) $+ ISC { total = lengthFL tps+ , current = 0+ , tps = FZipper NilRL tps+ , choices = pcs }+ return $ choices userSelection++textSelect' :: Patchy p => WhichChanges ->+ InteractiveSelectionM p C(x y) ()+textSelect' whch = do+ z <- gets tps+ when (not $ rightmost z) $+ do+ textSelectOne whch+ textSelect' whch++optionsBasic :: [Char] -> [Char] -> [KeyPress]+optionsBasic jn aThing =+ [ KeyPress 'y' (jn++" this "++aThing)+ , KeyPress 'n' ("don't "++jn++" it")+ , KeyPress 'w' ("wait and decide later, defaulting to no") ]++optionsFile :: [Char] -> [KeyPress]+optionsFile jn =+ [ KeyPress 's' ("don't "++jn++" the rest of the changes to this file")+ , KeyPress 'f' (jn++" the rest of the changes to this file") ]++optionsView :: String -> String -> [KeyPress]+optionsView aThing someThings =+ [ KeyPress 'v' ("view this "++aThing++" in full")+ , KeyPress 'p' ("view this "++aThing++" in full with pager")+ , KeyPress 'l' ("list all selected "++someThings) ]++optionsSummary :: String -> [KeyPress]+optionsSummary aThing =+ [ KeyPress 'x' ("view a summary of this "++aThing) ]++optionsQuit :: String -> String -> [KeyPress]+optionsQuit jn someThings =+ [ KeyPress 'd' (jn++" selected "++someThings++", skipping all the remaining "++someThings)+ , KeyPress 'a' (jn++" all the remaining "++someThings)+ , KeyPress 'q' ("cancel "++jn) ]++optionsNav :: String -> [KeyPress]+optionsNav aThing =+ [ KeyPress 'j' ("skip to next "++ aThing)+ , KeyPress 'k' ("back up to previous "++ aThing) ]++optionsSplit :: Maybe (Splitter a) -> String -> [KeyPress]+optionsSplit split aThing+ | Just _ <- split+ = [ KeyPress 'e' ("interactively edit this "++ aThing) ]+ | otherwise = []++options :: forall p C(x y) . (Patchy p) => Bool ->+ InteractiveSelectionM p C(x y) ([[KeyPress]],[[KeyPress]])+options single = do+ split <- asks splitter+ jn <- asks jobname+ aThing <- thing+ someThings <- things+ o <- asks opts+ return $+ ([optionsBasic jn aThing]+ ,[optionsSplit split aThing]+ ++ (if single then+ [optionsFile jn ] else [])+ ++ [optionsView aThing someThings +++ if Summary `elem` o then []+ else optionsSummary aThing]+ ++ [optionsQuit jn someThings]+ ++ [optionsNav aThing]+ )++-- | Returns a @Sealed2@ version of the patch we are asking the user+-- about.+currentPatch :: forall p C(x y) . Patchy p =>+ InteractiveSelectionM p C(x y)+ (Maybe (Sealed2 (TaggedPatch p)))+currentPatch = do+ (FZipper _ tps_todo) :: FZipper (TaggedPatch p) C(x y) <- gets tps+ case tps_todo of+ NilFL -> return Nothing+ (tp:>:_) -> return $ Just (Sealed2 tp)++-- | Returns the patches we have yet to ask the user about.+todo :: forall p C(x y) . Patchy p+ => InteractiveSelectionM p C(x y)+ (FlippedSeal (FL (TaggedPatch p)) C(y))+todo = do+ (FZipper _ tps_todo) <- gets tps+ return (FlippedSeal tps_todo)++-- | Modify the underlying @PatchChoices@ by some function+modChoices :: forall p C(x y) . Patchy p =>+ (PatchChoices p C(x y) -> PatchChoices p C(x y))+ -> InteractiveSelectionM p C(x y) ()+modChoices f = modify $ \isc -> isc{choices = f $ choices isc}++-- | returns @Just f@ if the 'currentPatch' only modifies @f@,+-- @Nothing@ otherwise.+currentFile :: forall p C(x y) . Patchy p+ => InteractiveSelectionM p C(x y) (Maybe FilePath)+currentFile = do+ c <- currentPatch+ return $ case c of+ Nothing -> Nothing+ Just (Sealed2 tp) ->+ case listTouchedFiles tp of+ [f] -> Just f+ _ -> Nothing++-- | @decide True@ selects the current patch, and @decide False@ deselects+-- it.+decide :: forall p C(x y t u) . Patchy p => WhichChanges -> Bool+ -> TaggedPatch p C(t u)+ -> InteractiveSelectionM p C(x y) ()+decide whch takeOrDrop tp =+ if backward whch == takeOrDrop -- we go backward xor we are dropping+ then modChoices $ forceLast (tag tp)+ else modChoices $ forceFirst (tag tp)++-- | like 'decide', but for all patches touching @file@+decideWholeFile :: forall p C(x y). Patchy p => WhichChanges ->+ FilePath -> Bool -> InteractiveSelectionM p C(x y) ()+decideWholeFile whch file takeOrDrop =+ do+ FlippedSeal tps_todo <- todo+ let patches_to_skip =+ filterFL (\tp' -> listTouchedFiles tp' == [file]) tps_todo+ mapM_ (unseal2 $ decide whch takeOrDrop) patches_to_skip++-- | Undecide the current patch.+postponeNext :: forall p C(x y) . Patchy p => InteractiveSelectionM p C(x y) ()+postponeNext =+ do+ Just (Sealed2 tp) <- currentPatch+ modChoices $ makeUncertain (tag tp)++-- | Focus the next patch.+skipOne :: forall p C(x y) . Patchy p => InteractiveSelectionM p C(x y) ()+skipOne = modify so+ where so x = x{tps = right (tps x), current = current x +1}++-- | Focus the previous patch.+backOne :: forall p C(x y) . Patchy p => InteractiveSelectionM p C(x y) ()+backOne = modify so+ where so isc = isc{tps = left (tps isc), current = max (current isc-1) 0}++-- | Split the current patch (presumably a hunk), and add the replace it+-- with its parts.+splitCurrent :: forall p C(x y) . Patchy p => Splitter p+ -> InteractiveSelectionM p C(x y) ()+splitCurrent s = do+ FZipper tps_done (tp:>:tps_todo) <- gets tps+ case (applySplitter s (tpPatch tp)) of+ Nothing -> return ()+ Just (text, parse) ->+ do+ newText <- liftIO $ editText "darcs-patch-edit" text+ case parse newText of+ Nothing -> return ()+ Just ps -> do+ tps_new <- liftIO . return . snd+ $ patchChoicesTpsSub (Just (tag tp)) ps+ modify $ \isc -> isc { total = ( total isc+ + lengthFL tps_new - 1 )+ , tps = (FZipper tps_done+ (tps_new +>+ tps_todo))+ , choices = (substitute+ (seal2 (tp :||: tps_new))+ (choices isc))+ }++-- | Returns a list of the currently selected patches, in+-- their original context, i.e., not commuted past unselected+-- patches.+selected :: forall p C(x y). Patchy p => WhichChanges ->+ InteractiveSelectionM p C(x y) [Sealed2 p]+selected whichch = do+ c <- gets choices+ (first_chs :> _ :> last_chs) <- return $ getChoices c+ return $ if backward whichch+ then+ mapFL (repr whichch . Sealed2 . tpPatch) $ last_chs+ else+ mapFL (repr whichch . Sealed2 . tpPatch) $ first_chs++-- | Prints the list of the selected patches. See 'selected'.+printSelected :: Patchy p => WhichChanges ->+ InteractiveSelectionM p C(x y) ()+printSelected whichch = do+ someThings <- things+ o <- asks opts+ s <- selected whichch+ liftIO $ do+ putStrLn $ "---- Already selected "++someThings++" ----"+ mapM_ (putDocLn . unseal2 (showFriendly o)) s+ putStrLn $ "---- end of already selected "++someThings++" ----"++printSummary :: forall p C(x y) . Patchy p => p C(x y) -> IO ()+printSummary =+ putDocLn . prefix " " . summary++-- | Skips all remaining patches.+skipAll :: forall p C(x y) . Patchy p =>+ InteractiveSelectionM p C(x y) ()+skipAll = do+ modify $ \isc -> isc {tps = toEnd $ tps isc}++isSingleFile :: Patchy p => p C(x y) -> Bool+isSingleFile p = length (listTouchedFiles p) == 1++askConfirmation :: forall p C(x y) . Patchy p =>+ InteractiveSelectionM p C(x y) ()+askConfirmation = do+ jn <- asks jobname+ liftIO $ if jn `elem` ["unpull", "unrecord", "obliterate"]+ then do+ yorn <- askUser $ "Really " ++ jn ++ " all undecided patches? "+ case yorn of+ ('y':_) -> return ()+ _ -> exitWith $ ExitSuccess+ else return ()++-- | The singular form of the noun for items of type @p@.+thing :: Patchy p => InteractiveSelectionM p C(x y) String+thing = gets choices >>= return . Darcs.Patch.thing . helper+ where+ helper :: PatchChoices p C(a b) -> p C(a b)+ helper = undefined++-- | The plural form of the noun for items of type @p@.+things :: Patchy p => InteractiveSelectionM p C(x y) String+things = gets choices >>= return . Darcs.Patch.things . helper+ where+ helper :: PatchChoices p C(a b) -> p C(a b)+ helper = undefined++-- | The question to ask about one patch.+prompt :: Patchy p => InteractiveSelectionM p C(x y) String+prompt = do+ jn <- asks jobname+ aThing <- thing+ n <- gets current+ n_max <- gets total+ return $ "Shall I "++jn++" this "++aThing++"? "+ ++ "(" ++ show (n+1) ++ "/" ++ show n_max ++ ") "++-- | Asks the user about one patch, returns their answer.+promptUser :: forall p C(x y) . Patchy p => Bool -> Char+ -> InteractiveSelectionM p C(x y) Char+promptUser single def = do+ thePrompt <- prompt+ (basicOptions,advancedOptions) <- options single+ liftIO $ promptChar $ PromptConfig { pPrompt = thePrompt+ , pBasicCharacters = keysFor basicOptions+ , pAdvancedCharacters = keysFor advancedOptions+ , pDefault = Just def+ , pHelp = "?h"+ }++-- | Ask the user what to do with the next patch.+textSelectOne :: forall p C(x y). Patchy p => WhichChanges+ -> InteractiveSelectionM p C(x y) ()+textSelectOne whichch = do+ c <- currentPatch+ case c of+ Nothing -> return ()+ Just (Sealed2 tp) ->+ do+ jn <- asks jobname+ spl <- asks splitter+ o <- asks opts+ let singleFile = isSingleFile (tpPatch tp)+ reprCur = repr whichch (Sealed2 (tpPatch tp))+ (basicOptions,advancedOptions) <- options singleFile+ theSlot <- liftChoices $ patchSlot' tp+ let+ the_default = getDefault (whichch == Last || whichch == FirstReversed) theSlot+ jn_cap = (toUpper $ head jn) : tail jn+ yorn <- promptUser singleFile the_default+ let nextPatch = skipMundane whichch >> showCur whichch+ case yorn of+ 'y' -> decide whichch True tp >> skipOne >> nextPatch+ 'n' -> decide whichch False tp >> skipOne >> nextPatch+ 'w' -> postponeNext >> skipOne >> nextPatch+ 'e' | (Just s) <- spl -> splitCurrent s >> showCur whichch+ 's' -> currentFile >>= maybe+ (return ())+ (\f -> decideWholeFile whichch f False) >> nextPatch+ 'f' -> currentFile >>= maybe+ (return ())+ (\f -> decideWholeFile whichch f True) >> nextPatch+ 'v' -> liftIO $ unseal2 printPatch reprCur+ 'p' -> liftIO $ unseal2 printPatchPager reprCur+ 'l' -> printSelected whichch >> showCur whichch+ 'x' -> liftIO $ unseal2 printSummary reprCur+ 'd' -> skipAll+ 'a' ->+ do+ askConfirmation+ modChoices $ selectAllMiddles (whichch == Last || whichch == FirstReversed)+ skipAll+ 'q' -> liftIO $+ do putStrLn $ jn_cap++" cancelled."+ exitWith $ ExitSuccess+ 'j' -> skipOne >> showCur whichch+ 'k' -> backOne >> showCur whichch+ _ -> 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+ -> InteractiveSelectionM p C(x y) ()+showCur whichch = do+ o <- asks opts+ c <- currentPatch+ case c of+ Nothing -> return ()+ Just (Sealed2 tp) -> do+ let reprCur = repr whichch (Sealed2 (tpPatch tp))+ liftIO . (unseal2 (printFriendly o)) $ reprCur++-- | The interactive part of @darcs changes@+textView :: forall p C(x y u r s). Patchy p => [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+ where+ prev_patch :: IO ()+ prev_patch = case ps_done of+ [] -> repeat_this+ (p':ps_done') ->+ textView o+ n_max (n-1) ps_done' (p':ps_todo)+ next_patch :: IO ()+ next_patch = case ps_todo' of+ [] -> -- May as well work out the length now we have all+ -- the patches in memory+ textView o n_max+ n ps_done []+ _ -> textView o n_max+ (n+1) (p:ps_done) ps_todo'+ options_yn =+ [ KeyPress 'y' "view this patch and go to the next"+ , KeyPress 'n' "skip to the next patch" ]+ optionsView =+ [ KeyPress 'v' "view this patch in full"+ , KeyPress 'p' "view this patch in full with pager" ]+ optionsSummary =+ [ KeyPress 'x' "view a summary of this patch" ]+ optionsNav =+ [ KeyPress 'q' "quit view changes"+ , KeyPress 'k' "back up to previous patch"+ , KeyPress 'j' "skip to next patch"+ , KeyPress 'c' "count total patch number" ]+ basicOptions = [ options_yn ]+ advancedOptions =+ [ optionsView +++ if Summary `elem` o then [] else optionsSummary ]+ ++ [ optionsNav ]+ prompt = "Shall I view this patch? "+ ++ "(" ++ show (n+1) ++ "/" ++ maybe "?" show n_max ++ ")"+ repeat_this :: IO ()+ repeat_this = 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+ 'x' -> do putDocLn $ prefix " " $ unseal2 summary p+ repeat_this+ 'q' -> exitWith ExitSuccess+ 'k' -> prev_patch+ 'j' -> next_patch+ 'c' -> textView o+ count_n_max n ps_done ps_todo+ _ -> do putStrLn $ helpFor "view changes" basicOptions advancedOptions+ repeat_this+ count_n_max | isJust n_max = n_max+ | otherwise = Just $ length ps_done + length ps_todo++-- | Skips patches we should not ask the user about+skipMundane :: Patchy p => WhichChanges ->+ InteractiveSelectionM p C(x y) ()+skipMundane whichch = do+ (FZipper tps_done tps_todo) <- gets tps+ o <- asks opts+ crit <- asks matchCriterion+ jn <- asks jobname+ (skipped :> unskipped) <- liftChoices $ spanFL_M+ (patchSlot' >=> return . decided)+ tps_todo+ let numSkipped = lengthFL skipped+ when (numSkipped > 0) . liftIO $ show_skipped o jn numSkipped skipped+ let boringThenInteresting =+ if DontPromptForDependencies `elem` o+ then spanFL (not.(crit whichch o) . seal2 . tpPatch) $+ unskipped+ else NilFL :> unskipped+ case boringThenInteresting of+ boring :> interesting ->+ do+ justDone $ lengthFL boring + numSkipped+ modify $ \isc -> isc {tps = (FZipper (reverseFL boring +<+ reverseFL skipped +<+ tps_done) interesting)}+ where+ show_skipped o jn n ps = do putStrLn $ _nevermind_ jn ++ _these_ n ++ "."+ when (Verbose `elem` o) $+ showskippedpatch ps+ _nevermind_ jn = "Will not ask whether to " ++ jn ++ " "+ _these_ n = show n ++ " already decided " ++ _elem_ n ""+ _elem_ n = englishNum n (Noun "patch")+ showskippedpatch :: Patchy p => FL (TaggedPatch p) C(y t) -> IO ()+ showskippedpatch =+ sequence_ . mapFL (printSummary . tpPatch)++decided :: Slot -> Bool+decided InMiddle = False+decided _ = True++-- | The action bound to space, depending on the current status of the+-- patch.+getDefault :: Bool -> Slot -> Char+getDefault _ InMiddle = 'w'+getDefault True InFirst = 'n'+getDefault True InLast = 'y'+getDefault False InFirst = 'y'+getDefault False InLast = 'n'++-- |Optionally remove any patches (+dependencies) from a sequence that+-- conflict with the recorded or unrecorded changes in a repo+filterOutConflicts :: RepoPatch p+ => [DarcsFlag] -- ^Command-line options. Only 'SkipConflicts' is+ -- significant; filtering will happen iff it is present+ -> RL (PatchInfoAnd p) C(x t) -- ^Recorded patches from repository, starting from+ -- same context as the patches to filter+ -> Repository p C(r u t) -- ^Repository itself, used for grabbing unrecorded changes+ -> FL (PatchInfoAnd p) C(x z) -- ^Patches to filter+ -> IO (Bool, Sealed (FL (PatchInfoAnd p) C(x))) -- ^(True iff any patches were removed, possibly filtered patches)+filterOutConflicts o us repository them+ | SkipConflicts `elem` o = do let commuter = commuterIdRL selfCommuter unrec <- fmap n2pia . (anonymous . fromPrims) =<< unrecordedChanges [] repository [] them' :> rest <- return $ partitionConflictingFL commuter them (unrec :<: us)
src/Darcs/SignalHandler.hs view
@@ -21,16 +21,20 @@ module Darcs.SignalHandler ( withSignalsHandled, withSignalsBlocked, catchInterrupt, catchNonSignal,- tryNonSignal, stdout_is_a_pipe ) where+ tryNonSignal, stdoutIsAPipe ) where +import Prelude hiding ( catch )+ import System.IO.Error ( isUserError, ioeGetErrorString, ioeGetFileName )-import Control.Exception ( dynExceptions, ioErrors, catchJust, Exception ( IOException ) ) import System.Exit ( exitWith, ExitCode ( ExitFailure ) ) import Control.Concurrent ( ThreadId, myThreadId )-import Control.Exception ( catchDyn, throwDyn, throwDynTo, block )+import Control.Exception.Extensible+ ( catch, throw, throwTo, block,+ Exception(..), SomeException(..), IOException ) import System.Posix.Files ( getFdStatus, isNamedPipe ) import System.Posix.IO ( stdOutput )-import Data.Dynamic ( Typeable, fromDynamic )+import Data.Typeable ( Typeable, cast )+import Data.List ( isPrefixOf ) import System.IO ( hPutStrLn, stderr ) import Control.Monad ( when ) @@ -40,21 +44,25 @@ import CtrlC ( withCtrlCHandler ) #endif -stdout_is_a_pipe :: IO Bool-stdout_is_a_pipe- = catchJust ioErrors+stdoutIsAPipe :: IO Bool+stdoutIsAPipe+ = catch (do stat <- getFdStatus stdOutput return (isNamedPipe stat))- (\_ -> return False)+ (\(_ :: IOException) -> return False) withSignalsHandled :: IO a -> IO a-newtype SignalException = SignalException Signal deriving (Typeable)+newtype SignalException = SignalException Signal deriving (Show, Typeable) +instance Exception SignalException where+ toException e = SomeException e+ fromException (SomeException e) = cast e+ withSignalsHandled job = do thid <- myThreadId mapM_ (ih thid) [sigINT, sigHUP, sigABRT, sigTERM, sigPIPE]- catchJust just_usererrors (job' thid `catchSignal` defaults)- die_with_string+ catchUserErrors (job' thid `catchSignal` defaults)+ die_with_string where defaults s | s == sigINT = ew s "Interrupted!" | s == sigHUP = ew s "HUP" | s == sigABRT = ew s "ABRT"@@ -65,8 +73,8 @@ resethandler sig raiseSignal sig -- ensure that our caller knows how we died exitWith $ ExitFailure $ 1- die_with_string e | take 6 e == "STDOUT" =- do is_pipe <- stdout_is_a_pipe+ die_with_string e | "STDOUT" `isPrefixOf` e =+ do is_pipe <- stdoutIsAPipe when (not is_pipe) $ hPutStrLn stderr $ "\ndarcs failed: "++drop 6 e exitWith $ ExitFailure $ 2@@ -74,7 +82,7 @@ exitWith $ ExitFailure $ 2 #ifdef WIN32 job' thid =- withCtrlCHandler (throwDynTo thid $ SignalException sigINT) job+ withCtrlCHandler (throwTo thid $ SignalException sigINT) job #else job' _ = job #endif@@ -85,39 +93,39 @@ ih :: ThreadId -> Signal -> IO () ih thid s =- do installHandler s (Catch $ throwDynTo thid $ SignalException s) Nothing+ do installHandler s (Catch $ throwTo thid $ SignalException s) Nothing return () catchSignal :: IO a -> (Signal -> IO a) -> IO a catchSignal job handler =- job `Control.Exception.catchDyn` (\(SignalException sig) -> handler sig)+ job `catch` (\(SignalException sig) -> handler sig) -- catchNonSignal is a drop-in replacement for Control.Exception.catch, which allows -- us to catch anything but a signal. Useful for situations where we want -- don't want to inhibit ctrl-C. -catchNonSignal :: IO a -> (Control.Exception.Exception -> IO a) -> IO a-catchNonSignal = Control.Exception.catchJust notSig- where notSig x = case dynExceptions x of- Nothing -> Just x- Just d -> case fromDynamic d :: Maybe SignalException of- Just _ -> Nothing- Nothing -> Just x+catchNonSignal :: IO a -> (SomeException -> IO a) -> IO a+catchNonSignal comp handler = catch comp handler'+ where handler' se =+ case fromException se :: Maybe SignalException of+ Nothing -> handler se+ Just _ -> throw se catchInterrupt :: IO a -> IO a -> IO a catchInterrupt job handler = job `catchSignal` h where h s | s == sigINT = handler- | otherwise = throwDyn (SignalException s)+ | otherwise = throw (SignalException s) -tryNonSignal :: IO a -> IO (Either Control.Exception.Exception a)+tryNonSignal :: IO a -> IO (Either SomeException a) tryNonSignal j = (Right `fmap` j) `catchNonSignal` \e -> return (Left e) -just_usererrors :: Control.Exception.Exception -> Maybe String-just_usererrors (IOException e) | isUserError e = Just $ ioeGetErrorString e-just_usererrors (IOException e) | ioeGetFileName e == Just "<stdout>"- = Just $ "STDOUT"++ioeGetErrorString e-just_usererrors _ = Nothing+catchUserErrors :: IO a -> (String -> IO a) -> IO a+catchUserErrors comp handler = catch comp handler'+ where handler' ioe+ | isUserError ioe = handler (ioeGetErrorString ioe)+ | ioeGetFileName ioe == Just "<stdout>" = handler ("STDOUT" ++ ioeGetErrorString ioe)+ | otherwise = throw ioe withSignalsBlocked :: IO () -> IO () withSignalsBlocked job = (block job) `catchSignal` couldnt_do
− src/Darcs/SlurpDirectory.hs
@@ -1,35 +0,0 @@--- Copyright (C) 2002-2004 David Roundy------ This program is free software; you can redistribute it and/or modify--- it under the terms of the GNU General Public License as published by--- the Free Software Foundation; either version 2, or (at your option)--- any later version.------ This program is distributed in the hope that it will be useful,--- but WITHOUT ANY WARRANTY; without even the implied warranty of--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the--- GNU General Public License for more details.------ You should have received a copy of the GNU General Public License--- along with this program; see the file COPYING. If not, write to--- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,--- Boston, MA 02110-1301, USA.--module Darcs.SlurpDirectory- (Slurpy, empty_slurpy, slurp,- mmap_slurp, co_slurp, slurp_unboring,- FileContents,- undefined_time, undefined_size,- doesFileReallyExist, doesDirectoryReallyExist, isFileReallySymlink,- is_dir, is_file,- get_slurp, slurp_name,- slurp_has, slurp_has_anycase, slurp_hasfile, slurp_hasdir,- list_slurpy, list_slurpy_files, list_slurpy_dirs, get_path_list,- get_filecontents, get_dircontents, get_mtime, get_length,- slurp_modfile, slurp_remove, slurp_removefile, slurp_removedir,- write_files,- SlurpMonad, withSlurpy, writeSlurpy, syncSlurpy- )- where--import Darcs.SlurpDirectory.Internal
− src/Darcs/SlurpDirectory/Internal.hs
@@ -1,661 +0,0 @@--- Copyright (C) 2002-2004 David Roundy------ This program is free software; you can redistribute it and/or modify--- it under the terms of the GNU General Public License as published by--- the Free Software Foundation; either version 2, or (at your option)--- any later version.------ This program is distributed in the hope that it will be useful,--- but WITHOUT ANY WARRANTY; without even the implied warranty of--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the--- GNU General Public License for more details.------ You should have received a copy of the GNU General Public License--- along with this program; see the file COPYING. If not, write to--- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,--- Boston, MA 02110-1301, USA.--{-# OPTIONS_GHC -cpp #-}-{-# LANGUAGE CPP #-}---- | SlurpDirectory is intended to give a nice lazy way of traversing directory--- trees.-module Darcs.SlurpDirectory.Internal- ( Slurpy(..), SlurpyContents(..), slurpies_to_map, map_to_slurpies,- FileContents, empty_slurpy,- slurp, mmap_slurp, slurp_unboring, co_slurp,- slurp_name, is_file, is_dir,- get_filecontents, get_dircontents, get_mtime,- get_length, get_slurp,- slurp_removefile, slurp_removedir,- slurp_remove,- slurp_modfile, slurp_hasfile, slurp_hasdir,- slurp_has_anycase, undefined_time,- undefined_size,- slurp_has, list_slurpy, list_slurpy_files,- get_path_list,- list_slurpy_dirs,- isFileReallySymlink,- doesFileReallyExist, doesDirectoryReallyExist,- SlurpMonad, withSlurpy, write_files,- writeSlurpy, syncSlurpy- ) where--import System.IO-import System.Directory hiding ( getCurrentDirectory, renameFile )-import Workaround ( getCurrentDirectory )-import Darcs.Utils ( withCurrentDirectory, formatPath )-import Darcs.RepoPath ( FilePathLike, toPath )-import System.IO.Unsafe ( unsafeInterleaveIO )-import Data.List ( isPrefixOf )-import Control.Monad ( MonadPlus(..), when )-import Data.Char ( toLower )-import System.Posix.Types ( EpochTime )-import System.Posix.Files- ( getSymbolicLinkStatus, modificationTime,- fileSize,- isRegularFile, isDirectory, isSymbolicLink- )-import Data.Maybe ( catMaybes, isJust, maybeToList )-import Data.Map (Map)-import qualified Data.Map as Map--import Darcs.SignalHandler ( tryNonSignal )-import Darcs.CheckFileSystem ( can_I_use_mmap )-import Darcs.IO ( ReadableDirectory(..), WriteableDirectory(..) )--import ByteStringUtils-import qualified Data.ByteString as B--import Darcs.Patch.FileName ( FileName, fn2fp, fp2fn, norm_path, break_on_dir,- own_name, superName )-import System.Posix.Types ( FileOffset )--#include "impossible.h"--data Slurpy = Slurpy !FileName !SlurpyContents--slurpy_to_pair :: Slurpy -> (FileName, SlurpyContents)-slurpy_to_pair (Slurpy fn sc) = (fn, sc)--pair_to_slurpy :: (FileName, SlurpyContents) -> Slurpy-pair_to_slurpy = uncurry Slurpy--type SlurpyMap = Map FileName SlurpyContents--slurpies_to_map :: [Slurpy] -> SlurpyMap-slurpies_to_map = Map.fromList . map slurpy_to_pair--map_to_slurpies :: SlurpyMap -> [Slurpy]-map_to_slurpies = map pair_to_slurpy . Map.toList--data SlurpyContents = SlurpDir (Maybe String) SlurpyMap- | SlurpFile (Maybe String,EpochTime,FileOffset) FileContents-type FileContents = B.ByteString--instance Show Slurpy where- show (Slurpy fn (SlurpDir _ l)) =- "Dir " ++ (fn2fp fn) ++ "\n" ++- concat (map show $ map_to_slurpies l) ++ "End Dir " ++ (fn2fp fn) ++ "\n"- show (Slurpy fn (SlurpFile _ _)) = "File " ++ (fn2fp fn) ++ "\n"--mapSlurpyNames :: (FileName -> FileName) -> Slurpy -> Slurpy-mapSlurpyNames f = onSlurpy- where onSlurpy (Slurpy fn sc) = Slurpy (f fn) (onSlurpyContents sc)- onSlurpyContents sf@(SlurpFile _ _) = sf- onSlurpyContents (SlurpDir x sm) = SlurpDir x . slurpies_to_map . map onSlurpy . map_to_slurpies $ sm--slurp :: FilePathLike p => p -> IO Slurpy-mmap_slurp :: FilePath -> IO Slurpy-slurp_unboring :: (FilePath->Bool) -> FilePath -> IO Slurpy-empty_slurpy :: Slurpy-empty_slurpy = Slurpy (fp2fn ".") (SlurpDir Nothing Map.empty)-slurp_name :: Slurpy -> FilePath-is_file :: Slurpy -> Bool-is_dir :: Slurpy -> Bool--get_filecontents :: Slurpy -> FileContents-get_dircontents :: Slurpy -> [Slurpy]-get_mtime :: Slurpy -> EpochTime-get_length :: Slurpy -> FileOffset--instance Eq Slurpy where- s1 == s2 = (slurp_name s1) == (slurp_name s2)-instance Ord Slurpy where- s1 <= s2 = (slurp_name s1) <= (slurp_name s2)--data SlurpMonad a = SM ((Either String Slurpy)- -> Either String (Slurpy, a))-mksm :: (Slurpy -> Either String (Slurpy, a)) -> SlurpMonad a-mksm x = SM sm where sm (Left e) = Left e- sm (Right s) = x s--instance Functor SlurpMonad where- fmap f m = m >>= return . f--instance Monad SlurpMonad where- (SM p) >>= k = SM sm- where sm e = case p e of- Left er -> Left er- Right (s, a) -> case k a of- (SM q) -> q (Right s)- return a = SM ( \s -> case s of- Left e -> Left e- Right x -> Right (x, a) )- fail e = SM ( \s -> case s of- Left x -> Left x- _ -> Left e )--instance MonadPlus SlurpMonad where- mzero = fail "SlurpMonad mzero"- (SM p) `mplus` (SM q) = SM sm- where sm e = case p e of- Left _ -> q e- okay -> okay--instance ReadableDirectory SlurpMonad where- mDoesDirectoryExist d = smDoesDirectoryExist d- mDoesFileExist f = smDoesFileExist f- mInCurrentDirectory = smInSlurpy- mGetDirectoryContents = smGetDirContents- mReadFilePS = smReadFilePS- mReadFilePSs = smReadFilePSs--instance WriteableDirectory SlurpMonad where- mWithCurrentDirectory = modifySubSlurpy- mSetFileExecutable _ _ = return ()- mWriteFilePS = smWriteFilePS- mCreateDirectory = smCreateDirectory- mRename = smRename- mRemoveDirectory = smRemoveDirectory- mRemoveFile = smRemoveFile--write_file :: Slurpy -> FileName -> IO ()-write_file s fn = case withSlurpy s $ smReadFilePS fn of- Left err -> fail err- Right (_, c) -> do- ensureDirectories (superName fn)- mWriteFilePS fn c- -try_write_file :: Slurpy -> FilePath -> IO ()-try_write_file s fp = let fn = fp2fn fp in- if slurp_hasfile fn s- then write_file s fn- else if slurp_hasdir fn s- then ensureDirectories fn- else return ()--ensureDirectories :: WriteableDirectory m => FileName -> m ()-ensureDirectories d = do- isPar <- mDoesDirectoryExist d- if isPar - then return ()- else ensureDirectories (superName d) >> (mCreateDirectory d)--write_files :: Slurpy -> [FilePath] -> IO ()-write_files s fps = mapM_ (try_write_file s) fps---- don't overwrite non-empty directories unless explicitly asked by--- being passed "." (which always exists)-writeSlurpy :: Slurpy -> FilePath -> IO ()-writeSlurpy s d = do- when (d /= ".") $ createDirectory d- withCurrentDirectory d $ write_files s (list_slurpy s)--withSlurpy :: Slurpy -> SlurpMonad a -> Either String (Slurpy, a)-withSlurpy s (SM f) = f (Right s)--smDoesDirectoryExist :: FileName -> SlurpMonad Bool-smDoesDirectoryExist d = mksm $ \s -> (Right (s, slurp_hasdir d s))--smDoesFileExist :: FileName -> SlurpMonad Bool-smDoesFileExist f = mksm $ \s -> (Right (s, slurp_hasfile f s))---- smInSlurpy doesn't make any changes to the subdirectory.-smInSlurpy :: FileName -> SlurpMonad a -> SlurpMonad a-smInSlurpy d job = mksm sm- where sm s = case get_slurp d s of- Just s' | is_dir s' -> case withSlurpy s' job of- Left e -> Left e- Right (_,a) -> Right (s, a)- _ -> Left $ "smInSlurpy: Couldn't find directory " ++- formatPath (fn2fp d)--fromSlurpFile :: FileName -> (Slurpy -> a) -> SlurpMonad a-fromSlurpFile f job = mksm sm- where sm s = case get_slurp f s of- Just s' | is_file s' -> Right (s, job s')- _ -> Left $ "fromSlurpFile: Couldn't find file " ++- formatPath (fn2fp f)--modifySubSlurpy :: FileName -> SlurpMonad a -> SlurpMonad a-modifySubSlurpy d job = mksm sm- where sm s = case get_slurp_context d s of- Just (ctx, sub@(Slurpy _ (SlurpDir _ _))) ->- case withSlurpy sub job of- Left e -> Left e- Right (sub',a) -> Right (ctx sub', a)- _ -> Left $ "modifySubSlurpy: Couldn't find directory " ++- formatPath (fn2fp d)--modifyFileSlurpy :: FileName -> (Slurpy -> Slurpy) -> SlurpMonad ()-modifyFileSlurpy f job = mksm sm- where sm s = case get_slurp_context f s of- Just (ctx, sf@(Slurpy _ (SlurpFile _ _))) -> Right (ctx $ job sf, ())- _ -> Left $ "modifyFileSlurpy: Couldn't find file " ++- formatPath (fn2fp f)--insertSlurpy :: FileName -> Slurpy -> SlurpMonad ()-insertSlurpy f news = mksm $ \s ->- if slurp_hasfile f s || slurp_hasdir f s || not (slurp_hasdir (superName f) s)- then Left $ "Error creating file "++fn2fp f- else Right (addslurp f news s, ())--smReadFilePS :: FileName -> SlurpMonad B.ByteString-smReadFilePS f = fromSlurpFile f get_filecontents--smReadFilePSs :: FileName -> SlurpMonad [B.ByteString]-smReadFilePSs f = fromSlurpFile f (linesPS . get_filecontents)--smGetDirContents :: SlurpMonad [FileName]-smGetDirContents = mksm $ \s -> Right (s, map slurp_fn $ get_dircontents s)--smWriteFilePS :: FileName -> B.ByteString -> SlurpMonad ()-smWriteFilePS f ps = -- this implementation could be made rather more direct- -- and limited to a single pass down the Slurpy- modifyFileSlurpy f (\_ -> sl)- `mplus` insertSlurpy f sl- where sl = Slurpy (own_name f) (SlurpFile undef_time_size ps)--smCreateDirectory :: FileName -> SlurpMonad ()-smCreateDirectory a = mksm sm- where sm s = case slurp_adddir a s of- Just s' -> Right (s', ())- Nothing -> Left $ "Error creating directory "++fn2fp a--smRename :: FileName -> FileName -> SlurpMonad ()-smRename a b = mksm sm- where sm s = case slurp_move a b s of- Just s' -> Right (s', ())- Nothing -> - -- Workaround for some old patches having moves when the source file doesn't exist.- if (slurp_has (fn2fp a) s)- then Left $ "Error moving "++fn2fp a++" to "++fn2fp b- else Right (s, ())--smRemove :: FileName -> SlurpMonad ()-smRemove f = mksm sm- where sm s = case slurp_remove f s of- Nothing -> Left $ fn2fp f++" does not exist."- Just s' -> Right (s', ())--smRemoveFile :: FileName -> SlurpMonad ()-smRemoveFile f =- do exists <- mDoesFileExist f- if exists then smRemove f- else fail $ "File "++fn2fp f++" does not exist."--smRemoveDirectory :: FileName -> SlurpMonad ()-smRemoveDirectory f =- do exists <- mDoesDirectoryExist f- if exists then smRemove f- else fail $ "Directory "++fn2fp f++" does not exist."---- | Here are a few access functions.-slurp_name (Slurpy n _) = fn2fp n-slurp_fn :: Slurpy -> FileName-slurp_fn (Slurpy n _) = n-slurp_setname :: FileName -> Slurpy -> Slurpy-slurp_setname f (Slurpy _ s) = Slurpy f s--is_file (Slurpy _ (SlurpDir _ _)) = False-is_file (Slurpy _ (SlurpFile _ _)) = True--is_dir (Slurpy _ (SlurpDir _ _)) = True-is_dir (Slurpy _ (SlurpFile _ _)) = False--get_filecontents (Slurpy _ (SlurpFile _ c)) = c-get_filecontents _ = bug "Can't get_filecontents on SlurpDir."--get_dircontents (Slurpy _ (SlurpDir _ c)) = map_to_slurpies c-get_dircontents _ = bug "Can't get_dircontents on SlurpFile."--get_mtime (Slurpy _ (SlurpFile (_,t,_) _)) = t-get_mtime _ = bug "can't get_mtime on SlurpDir."-get_length (Slurpy _ (SlurpFile (_,_,l) _)) = l-get_length _ = bug "can't get_length on SlurpDir."--undefined_time :: EpochTime-undefined_time = -1-undefined_size :: FileOffset-undefined_size = -1-undef_time_size :: (Maybe String, EpochTime, FileOffset)-undef_time_size = (Nothing, undefined_time, undefined_size)--isFileReallySymlink :: FilePath -> IO Bool-isFileReallySymlink f = do fs <- getSymbolicLinkStatus f- return (isSymbolicLink fs)--doesFileReallyExist :: FilePath -> IO Bool-doesFileReallyExist f = do fs <- getSymbolicLinkStatus f- return (isRegularFile fs)--doesDirectoryReallyExist :: FilePath -> IO Bool-doesDirectoryReallyExist f = do fs <- getSymbolicLinkStatus f- return (isDirectory fs)---- |slurp is how we get a slurpy in the first place\ldots-slurp = slurp_unboring (\_->True) . toPath-mmap_slurp d = do canmmap <- can_I_use_mmap- if canmmap then genslurp True (\_->True) d- else genslurp False (\_->True) d-slurp_unboring = genslurp False-genslurp :: Bool -> (FilePath -> Bool)- -> FilePath -> IO Slurpy-genslurp usemm nb dirname = do- isdir <- doesDirectoryExist dirname- ms <- if isdir- then withCurrentDirectory dirname $- do actualname <- getCurrentDirectory- genslurp_helper usemm nb (reverse actualname) "" "."- else do former_dir <- getCurrentDirectory- genslurp_helper usemm nb (reverse former_dir) "" dirname- case ms of- Just s -> return s- Nothing -> fail $ "Unable to read directory " ++ dirname ++- " (it appears to be neither file nor directory)"--unsafeInterleaveMapIO :: (a -> IO b) -> [a] -> IO [b]-unsafeInterleaveMapIO _ [] = return []-unsafeInterleaveMapIO f (x:xs)- = do x' <- f x- xs' <- unsafeInterleaveIO $ unsafeInterleaveMapIO f xs- return (x':xs')--genslurp_helper :: Bool -> (FilePath -> Bool)- -> FilePath -> String -> String -> IO (Maybe Slurpy)-genslurp_helper usemm nb formerdir fullpath dirname = do- fs <- getSymbolicLinkStatus fulldirname- if isRegularFile fs- then do let mtime = (Nothing, modificationTime fs, fileSize fs)- ls <- unsafeInterleaveIO $ myReadFileLinesPSetc fulldirname- return $ Just $ Slurpy (fp2fn dirname) $ SlurpFile mtime ls- else if isDirectory fs || (isSymbolicLink fs && dirname == ".")- then do sl <- unsafeInterleaveIO $- do fnames <- getDirectoryContents fulldirname- unsafeInterleaveMapIO- (\f -> genslurp_helper usemm nb fulldirname'- (fullpath///f) f)- $ filter (nb . (fullpath///)) $ filter not_hidden fnames- return $ Just $ Slurpy (fp2fn dirname) $ SlurpDir Nothing $ slurpies_to_map $ catMaybes sl- else return Nothing- where fulldirname' = formerdir\\\dirname- fulldirname = reverse fulldirname'- myReadFileLinesPSetc = if usemm then mmapFilePS- else B.readFile--not_hidden :: FilePath -> Bool-not_hidden "." = False-not_hidden ".." = False-not_hidden _ = True--(\\\) :: FilePath -> FilePath -> FilePath-(\\\) "" d = d-(\\\) d "." = d-(\\\) d subdir = reverse subdir ++ "/" ++ d--(///) :: FilePath -> FilePath -> FilePath-(///) "" d = d-(///) d "." = d-(///) d subdir = d ++ "/" ++ subdir--co_slurp :: Slurpy -> FilePath -> IO Slurpy-co_slurp guide dirname = do- isdir <- doesDirectoryExist dirname- if isdir- then withCurrentDirectory dirname $ do- actualname <- getCurrentDirectory- Just slurpy <- co_slurp_helper (reverse actualname) guide- return slurpy- else error "Error coslurping!!! Please report this."--co_slurp_helper :: FilePath -> Slurpy -> IO (Maybe Slurpy)-co_slurp_helper former_dir (Slurpy d (SlurpDir _ c)) = unsafeInterleaveIO $ do- let d' = fn2fp d- fn' = former_dir\\\d'- fn = reverse fn'- efs <- tryNonSignal $ getSymbolicLinkStatus fn- case efs of- Right fs- | isDirectory fs || (isSymbolicLink fs && d' == ".") ->- do sl <- unsafeInterleaveIO- $ unsafeInterleaveMapIO (co_slurp_helper fn') (map_to_slurpies c)- return $ Just $ Slurpy d $ SlurpDir Nothing $ slurpies_to_map $ catMaybes sl- _ -> return Nothing-co_slurp_helper former_dir (Slurpy f (SlurpFile _ _)) = unsafeInterleaveIO $ do- let fn' = former_dir\\\fn2fp f- fn = reverse fn'- efs <- tryNonSignal $ getSymbolicLinkStatus fn- case efs of- Right fs- | isRegularFile fs ->- do let mtime = (Nothing, modificationTime fs, fileSize fs)- ls <- unsafeInterleaveIO $ B.readFile fn- return $ Just $ Slurpy f $ SlurpFile mtime ls- _ -> return Nothing--get_slurp_context_generic :: (Slurpy -> a) -> (a -> [Slurpy]) -> FileName -> Slurpy -> Maybe (a -> a, Slurpy)-get_slurp_context_generic h1 h2 fn0 s0 =- let norm_fn0 = norm_path fn0 in- if norm_fn0 == empty- then Just (id, s0)- else slurp_context_private norm_fn0 id s0- where- slurp_context_private f ctx s@(Slurpy f' (SlurpFile _ _)) =- if f == f' then Just (ctx, s)- else Nothing- slurp_context_private f ctx s@(Slurpy d (SlurpDir _ c))- | f == d = Just (ctx, s)- | d == dot =- case break_on_dir f of- Just (dn,fn) | dn == dot ->- descend fn- _ ->- descend f- | otherwise =- case break_on_dir f of- Just (dn,fn) ->- if dn == d- then descend fn- else Nothing- _ -> Nothing- where- descend fname =- case findSubSlurpy fname c of- Nothing -> Nothing- Just this -> slurp_context_private- fname- (ctx . h1 . Slurpy d . SlurpDir Nothing . foldr (uncurry Map.insert) (Map.delete (slurp_fn this) c) . map slurpy_to_pair . h2)- this-- dot = fp2fn "."- empty = fp2fn ""---- |get_slurp_context navigates to a specified filename in the given slurpy,--- and returns the child slurpy at that point together with a update function that can be used--- to reconstruct the original slurpy from a replacement value for the child slurpy.-get_slurp_context :: FileName -> Slurpy -> Maybe (Slurpy -> Slurpy, Slurpy)-get_slurp_context = get_slurp_context_generic id return---- |A variant of 'get_slurp_context' that allows for removing the child slurpy--- altogether by passing in 'Nothing' to the update function.--- If the child slurpy happened to be at the top level and 'Nothing' was passed in,--- then the result of the update function will also be 'Nothing', otherwise it will always--- be a 'Just' value.-get_slurp_context_maybe :: FileName -> Slurpy -> Maybe (Maybe Slurpy -> Maybe Slurpy, Slurpy)-get_slurp_context_maybe = get_slurp_context_generic Just maybeToList---- |A variant of 'get_slurp_context' that allows for replacing the child slurpy by--- a list of slurpies. The result of the update function will always be a singleton--- list unless the child slurpy was at the top level.--- Currently unused.--- get_slurp_context_list :: FileName -> Slurpy -> Maybe ([Slurpy] -> [Slurpy], Slurpy)--- get_slurp_context_list = get_slurp_context_generic return id---- | It is important to be able to readily modify a slurpy.-slurp_remove :: FileName -> Slurpy -> Maybe Slurpy-slurp_remove fname s@(Slurpy _ (SlurpDir _ _)) =- case get_slurp_context_maybe fname s of- Just (ctx, _) -> ctx Nothing- Nothing -> Nothing-slurp_remove _ _ = bug "slurp_remove only acts on SlurpDirs"--slurp_removefile :: FileName -> Slurpy -> Maybe Slurpy-slurp_removefile f s =- if slurp_hasfile f s- then case slurp_remove f s of- s'@(Just (Slurpy _ (SlurpDir _ _))) -> s'- _ -> impossible- else Nothing--slurp_move :: FileName -> FileName -> Slurpy -> Maybe Slurpy-slurp_move f f' s =- if not (slurp_has (fn2fp f') s) && slurp_hasdir (superName f') s- then case get_slurp f s of- Nothing -> Nothing- Just sf ->- case slurp_remove f s of- Nothing -> Nothing- Just (s'@(Slurpy _ (SlurpDir _ _))) ->- Just $ addslurp f' (slurp_setname (own_name f') sf) s'- _ -> impossible- else Nothing--addslurp :: FileName -> Slurpy -> Slurpy -> Slurpy-addslurp fname s s' =- case get_slurp_context (superName fname) s' of- Just (ctx, Slurpy d (SlurpDir _ c)) -> ctx (Slurpy d (SlurpDir Nothing (uncurry Map.insert (slurpy_to_pair s) c)))- _ -> s'--get_slurp :: FileName -> Slurpy -> Maybe Slurpy-get_slurp f s = fmap snd (get_slurp_context f s)--slurp_removedir :: FileName -> Slurpy -> Maybe Slurpy-slurp_removedir f s =- case get_slurp f s of- Just (Slurpy _ (SlurpDir _ l)) | Map.null l ->- case slurp_remove f s of- s'@(Just (Slurpy _ (SlurpDir _ _))) -> s'- _ -> impossible- _ -> Nothing--slurp_adddir :: FileName -> Slurpy -> Maybe Slurpy-slurp_adddir f s =- if slurp_hasfile f s || slurp_hasdir f s || not (slurp_hasdir (superName f) s)- then Nothing- else Just $ addslurp f (Slurpy (own_name f) (SlurpDir Nothing Map.empty)) s---- |Code to modify a given file in a slurpy.-slurp_modfile :: FileName -> (FileContents -> Maybe FileContents)- -> Slurpy -> Maybe Slurpy-slurp_modfile fname modify sl =- case get_slurp_context fname sl of- Just (ctx, Slurpy ff (SlurpFile _ c)) ->- case modify c of- Nothing -> Nothing- Just c' -> Just (ctx (Slurpy ff (SlurpFile undef_time_size c')))- _ -> - Nothing--slurp_hasfile :: FileName -> Slurpy -> Bool-slurp_hasfile f s =- case get_slurp f s of- Just s' | is_file s' -> True- _ -> False--slurp_has :: FilePath -> Slurpy -> Bool-slurp_has f s = isJust (get_slurp (fp2fn f) s)--slurp_has_anycase :: FilePath -> Slurpy -> Bool-slurp_has_anycase fname s =- seq normed_name $ isJust $ get_slurp normed_name $ mapSlurpyNames tolower s- where normed_name = norm_path $ fp2fn $ map toLower fname--tolower :: FileName -> FileName-tolower = fp2fn . (map toLower) . fn2fp--findSubSlurpy :: FileName -> SlurpyMap -> Maybe Slurpy-findSubSlurpy fn sm =- let topname = case break_on_dir fn of- Just (dn, _) -> dn- Nothing -> fn- in fmap (Slurpy topname) (Map.lookup topname sm)--slurp_hasdir :: FileName -> Slurpy -> Bool-slurp_hasdir d _ | norm_path d == fp2fn "" = True-slurp_hasdir f (Slurpy _ (SlurpDir _ c)) =- seq f $ let f' = norm_path f- in case findSubSlurpy f' c of- Just s -> slurp_hasdir_private f' s- Nothing -> False-slurp_hasdir _ _ = False--slurp_hasdir_private :: FileName -> Slurpy -> Bool-slurp_hasdir_private _ (Slurpy _ (SlurpFile _ _)) = False-slurp_hasdir_private f (Slurpy d (SlurpDir _ c))- | f == d = True- | otherwise =- case break_on_dir f of- Just (dn,fn) ->- if dn == d- then case findSubSlurpy fn c of- Just s -> slurp_hasdir_private fn s- Nothing -> False- else False- _ -> False--get_path_list :: Slurpy -> FilePath -> [FilePath]-get_path_list s fp = get_path_list' s ("./" ++ fp)--get_path_list' :: Slurpy -> FilePath -> [FilePath]-get_path_list' s "" = list_slurpy s-get_path_list' (Slurpy f (SlurpFile _ _)) fp- | f' == fp = [f']- where f' = fn2fp f-get_path_list' (Slurpy d (SlurpDir _ ss)) fp- | (d' ++ "/") `isPrefixOf` (fp ++ "/")- = let fp' = drop (length d' + 1) fp- in map (d' ///) $ concatMap (\s -> get_path_list' s fp') $ map_to_slurpies ss- where d' = fn2fp d-get_path_list' _ _ = []--list_slurpy :: Slurpy -> [FilePath]-list_slurpy (Slurpy f (SlurpFile _ _)) = [fn2fp f]-list_slurpy (Slurpy dd (SlurpDir _ ss)) = d : map (d ///) (concatMap list_slurpy (map_to_slurpies ss))- where d = fn2fp dd--list_slurpy_files :: Slurpy -> [FilePath]-list_slurpy_files (Slurpy f (SlurpFile _ _)) = [fn2fp f]-list_slurpy_files (Slurpy dd (SlurpDir _ ss)) =- map ((fn2fp dd) ///) (concatMap list_slurpy_files (map_to_slurpies ss))--list_slurpy_dirs :: Slurpy -> [FilePath]-list_slurpy_dirs (Slurpy _ (SlurpFile _ _)) = []-list_slurpy_dirs (Slurpy dd (SlurpDir _ ss)) =- d : map (d ///) (concatMap list_slurpy_dirs (map_to_slurpies ss))- where d = fn2fp dd--unsyncedSlurpySize :: Slurpy -> Int-unsyncedSlurpySize (Slurpy _ (SlurpFile (_,_,size) ps))- | size == undefined_size = B.length ps- | otherwise = 0-unsyncedSlurpySize (Slurpy _ (SlurpDir _ ss)) =- sum $ map unsyncedSlurpySize (map_to_slurpies ss)--slurp_sync_size :: Int-slurp_sync_size = 100 * 1000000--syncSlurpy :: (Slurpy -> IO Slurpy) -> Slurpy -> IO Slurpy-syncSlurpy put s = if unsyncedSlurpySize s > slurp_sync_size- then put s- else return s
src/Darcs/Test.lhs view
@@ -16,8 +16,8 @@ % Boston, MA 02110-1301, USA. \begin{code}-module Darcs.Test ( get_test,- run_posthook, run_prehook )+module Darcs.Test ( getTest,+ runPosthook, runPrehook ) where import Darcs.RepoPath ( AbsolutePath ) import Darcs.Utils ( withCurrentDirectory )@@ -57,8 +57,8 @@ section~\ref{defaults}). \begin{code}-get_test :: [DarcsFlag] -> IO (IO ExitCode)-get_test opts =+getTest :: [DarcsFlag] -> IO (IO ExitCode)+getTest opts = let putInfo s = when (not $ Quiet `elem` opts) $ putStr s in do testline <- getPrefval "test"@@ -73,12 +73,12 @@ else putInfo "Test failed!\n" return ec -run_posthook :: [DarcsFlag] -> AbsolutePath -> IO ExitCode-run_posthook opts repodir = do ph <- get_posthook opts- withCurrentDirectory repodir $ run_hook opts "Posthook" ph+runPosthook :: [DarcsFlag] -> AbsolutePath -> IO ExitCode+runPosthook opts repodir = do ph <- getPosthook opts+ withCurrentDirectory repodir $ runHook opts "Posthook" ph -get_posthook :: [DarcsFlag] -> IO (Maybe String)-get_posthook opts = case getPosthookCmd opts of+getPosthook :: [DarcsFlag] -> IO (Maybe String)+getPosthook opts = case getPosthookCmd opts of Nothing -> return Nothing Just command -> if AskPosthook `elem` opts@@ -91,12 +91,12 @@ return Nothing else return $ Just command -run_prehook :: [DarcsFlag] -> AbsolutePath -> IO ExitCode-run_prehook opts repodir = do ph <- get_prehook opts- withCurrentDirectory repodir $ run_hook opts "Prehook" ph+runPrehook :: [DarcsFlag] -> AbsolutePath -> IO ExitCode+runPrehook opts repodir = do ph <- getPrehook opts+ withCurrentDirectory repodir $ runHook opts "Prehook" ph -get_prehook :: [DarcsFlag] -> IO (Maybe String)-get_prehook opts = case getPrehookCmd opts of+getPrehook :: [DarcsFlag] -> IO (Maybe String)+getPrehook opts = case getPrehookCmd opts of Nothing -> return Nothing Just command -> if AskPrehook `elem` opts@@ -109,9 +109,9 @@ return Nothing else return $ Just command -run_hook :: [DarcsFlag] -> String -> Maybe String -> IO ExitCode-run_hook _ _ Nothing = return ExitSuccess-run_hook opts cname (Just command) =+runHook :: [DarcsFlag] -> String -> Maybe String -> IO ExitCode+runHook _ _ Nothing = return ExitSuccess+runHook opts cname (Just command) = do ec <- system command when (Quiet `notElem` opts) $ if ec == ExitSuccess
src/Darcs/Test/Email.hs view
@@ -21,9 +21,9 @@ -- These tests check whether the emails generated by darcs meet a few criteria. -- We check for line length and non-ASCII characters. We apparently do not have -- to check for CR-LF newlines because that's handled by sendmail.-module Darcs.Test.Email ( email_parsing, email_header_no_long_lines,- email_header_ascii_chars, email_header_lines_start,- email_header_no_empty_lines+module Darcs.Test.Email ( emailParsing, emailHeaderNoLongLines,+ emailHeaderAsciiChars, emailHeaderLinesStart,+ emailHeaderNoEmptyLines ) where import Data.Char ( isPrint ) import qualified Data.ByteString as B ( length, unpack, null, head, filter,@@ -32,55 +32,55 @@ import Test.Framework ( Test ) import Test.Framework.Providers.QuickCheck2 ( testProperty ) import Printer ( text, renderPS )-import Darcs.Email ( make_email, read_email, formatHeader )+import Darcs.Email ( makeEmail, readEmail, formatHeader ) -- | Checks that darcs can read the emails it generates-email_parsing :: Test-email_parsing = testProperty "Checking that email can be parsed" $ \s ->+emailParsing :: Test+emailParsing = testProperty "Checking that email can be parsed" $ \s -> unlines ("":s++["", ""]) ==- BC.unpack (read_email (renderPS- $ make_email "reponame" [] (Just (text "contents\n"))+ BC.unpack (readEmail (renderPS+ $ makeEmail "reponame" [] (Just (text "contents\n")) (text $ unlines s) (Just "filename"))) -- | Check that formatHeader never creates lines longer than 78 characters -- (excluding the carriage return and line feed)-email_header_no_long_lines :: Test-email_header_no_long_lines =+emailHeaderNoLongLines :: Test+emailHeaderNoLongLines = testProperty "Checking email header line length" $ \field value ->- let cleanField = clean_field_string field- in not $ any (>78) $ map B.length $ bs_lines $ formatHeader cleanField value+ let cleanField = cleanFieldString field+ in not $ any (>78) $ map B.length $ bsLines $ formatHeader cleanField value -- Check that an email header does not contain non-ASCII characters -- formatHeader doesn't escape field names, there is no such thing as non-ascii -- field names afaik-email_header_ascii_chars :: Test-email_header_ascii_chars =+emailHeaderAsciiChars :: Test+emailHeaderAsciiChars = testProperty "Checking email for illegal characters" $ \field value ->- let cleanField = clean_field_string field+ let cleanField = cleanFieldString field in not (any (>127) (B.unpack (formatHeader cleanField value))) -- Check that header the second and later lines of a header start with a space-email_header_lines_start :: Test-email_header_lines_start =+emailHeaderLinesStart :: Test+emailHeaderLinesStart = testProperty "Checking for spaces at start of folded email header lines" $ \field value ->- let headerLines = bs_lines (formatHeader cleanField value)- cleanField = clean_field_string field+ let headerLines = bsLines (formatHeader cleanField value)+ cleanField = cleanFieldString field in all (\l -> B.null l || B.head l == 32) (tail headerLines) -- Checks that there are no lines in email headers with only whitespace-email_header_no_empty_lines :: Test-email_header_no_empty_lines =+emailHeaderNoEmptyLines :: Test+emailHeaderNoEmptyLines = testProperty "Checking that there are no empty lines in email headers" $ \field value ->- let headerLines = bs_lines (formatHeader cleanField value)- cleanField = clean_field_string field- in all (not . B.null . B.filter (not . (`elem` [10, 32, 9]))) headerLines+ let headerLines = bsLines (formatHeader cleanField value)+ cleanField = cleanFieldString field+ in all (not . B.null) headerLines --(not . B.null . B.filter (not . (`elem` [10, 32, 9]))) headerLines -bs_lines :: B.ByteString -> [B.ByteString]-bs_lines = finalizeFold . B.foldr splitAtLines (B.empty, [])+bsLines :: B.ByteString -> [B.ByteString]+bsLines = finalizeFold . B.foldr splitAtLines (B.empty, []) where splitAtLines 10 (thisLine, prevLines) = (B.empty, thisLine:prevLines) splitAtLines c (thisLine, prevLines) = (B.cons c thisLine, prevLines) finalizeFold (lastLine, otherLines) = lastLine : otherLines -clean_field_string :: String -> String-clean_field_string = filter (\c -> isPrint c && c < '\x80' && c /= ':')+cleanFieldString :: String -> String+cleanFieldString = filter (\c -> isPrint c && c < '\x80' && c /= ':')
src/Darcs/Test/Patch/Check.hs view
@@ -15,11 +15,11 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -module Darcs.Test.Patch.Check ( PatchCheck(), do_check, file_exists, dir_exists,- remove_file, remove_dir, create_file, create_dir,- insert_line, delete_line, is_valid, do_verbose_check,- file_empty,- check_move, modify_file, FileContents(..)+module Darcs.Test.Patch.Check ( PatchCheck(), doCheck, fileExists, dirExists,+ removeFile, removeDir, createFile, createDir,+ insertLine, deleteLine, isValid, doVerboseCheck,+ fileEmpty,+ checkMove, modifyFile, FileContents(..) ) where import System.IO.Unsafe ( unsafePerformIO )@@ -38,8 +38,8 @@ -- We must also store the greatest line number that is known to exist in a -- file, to be able to exclude the possibility of it being empty without -- knowing its contents.-data FileContents = FC { fc_lines :: Map Int B.ByteString- , fc_maxline :: Int+data FileContents = FC { fcLines :: Map Int B.ByteString+ , fcMaxline :: Int } deriving (Eq, Show) data Prop = FileEx String | DirEx String | NotEx String | FileLines String FileContents@@ -62,115 +62,115 @@ type PatchCheck = State KnownState -- | The @FileContents@ structure for an empty file-empty_filecontents :: FileContents-empty_filecontents = FC M.empty 0+emptyFilecontents :: FileContents+emptyFilecontents = FC M.empty 0 -- | Returns a given value if the repository state is inconsistent, and performs -- a given action otherwise.-handle_inconsistent :: a -- ^ The value to return if the state is inconsistent+handleInconsistent :: a -- ^ The value to return if the state is inconsistent -> PatchCheck a -- ^ The action to perform otherwise -> PatchCheck a-handle_inconsistent v a = do state <- get+handleInconsistent v a = do state <- get case state of Inconsistent -> return v _ -> a -do_check :: PatchCheck a -> a-do_check p = evalState p (P [] [])+doCheck :: PatchCheck a -> a+doCheck p = evalState p (P [] []) -- | Run a check, and print the final repository state-do_verbose_check :: PatchCheck a -> a-do_verbose_check p =+doVerboseCheck :: PatchCheck a -> a+doVerboseCheck p = case runState p (P [] []) of (b, pc) -> unsafePerformIO $ do putStrLn $ show pc return b -- | Returns true if the current repository state is not inconsistent-is_valid :: PatchCheck Bool-is_valid = handle_inconsistent False (return True)+isValid :: PatchCheck Bool+isValid = handleInconsistent False (return True) has :: Prop -> [Prop] -> Bool has _ [] = False has k (k':ks) = k == k' || has k ks -modify_file :: String+modifyFile :: String -> (Maybe FileContents -> Maybe FileContents) -> PatchCheck Bool-modify_file f change = do- file_exists f- c <- file_contents f+modifyFile f change = do+ fileExists f+ c <- fileContents f case change c of- Nothing -> assert_not $ FileEx f -- shorthand for "FAIL"- Just c' -> do set_contents f c'- is_valid+ Nothing -> assertNot $ FileEx f -- shorthand for "FAIL"+ Just c' -> do setContents f c'+ isValid -insert_line :: String -> Int -> B.ByteString -> PatchCheck Bool-insert_line f n l = do- c <- file_contents f+insertLine :: String -> Int -> B.ByteString -> PatchCheck Bool+insertLine f n l = do+ c <- fileContents f case c of- Nothing -> assert_not $ FileEx f -- in this case, the repo is inconsistent+ Nothing -> assertNot $ FileEx f -- in this case, the repo is inconsistent Just c' -> do- let lines' = M.mapKeys (\k -> if k >= n then k+1 else k) (fc_lines c')+ let lines' = M.mapKeys (\k -> if k >= n then k+1 else k) (fcLines c') lines'' = M.insert n l lines'- maxline' = max n (fc_maxline c')- set_contents f (FC lines'' maxline')+ maxline' = max n (fcMaxline c')+ setContents f (FC lines'' maxline') return True -- deletes a line from a hunk patch (third argument) in the given file (first -- argument) at the given line number (second argument)-delete_line :: String -> Int -> B.ByteString -> PatchCheck Bool-delete_line f n l = do- c <- file_contents f+deleteLine :: String -> Int -> B.ByteString -> PatchCheck Bool+deleteLine f n l = do+ c <- fileContents f case c of- Nothing -> assert_not $ FileEx f+ Nothing -> assertNot $ FileEx f Just c' ->- let flines = fc_lines c'+ let flines = fcLines c' flines' = M.mapKeys (\k -> if k > n then k-1 else k) (M.delete n flines)- maxlinenum' | n <= fc_maxline c' = fc_maxline c' - 1+ maxlinenum' | n <= fcMaxline c' = fcMaxline c' - 1 | otherwise = n - 1 c'' = FC flines' maxlinenum' do_delete = do- set_contents f c''- is_valid+ setContents f c''+ isValid in case M.lookup n flines of Nothing -> do_delete Just l' -> if l == l' then do_delete- else assert_not $ FileEx f+ else assertNot $ FileEx f -set_contents :: String -> FileContents -> PatchCheck ()-set_contents f c = handle_inconsistent () $ do+setContents :: String -> FileContents -> PatchCheck ()+setContents f c = handleInconsistent () $ do P ks nots <- get let ks' = FileLines f c : filter (not . is_file_lines_for f) ks put (P ks' nots)- where is_file_lines_for file prop = case prop of + where is_file_lines_for file prop = case prop of FileLines f' _ -> file == f' _ -> False -- | Get (as much as we know about) the contents of a file in the current state. -- Returns Nothing if the state is inconsistent.-file_contents :: String -> PatchCheck (Maybe FileContents)-file_contents f = handle_inconsistent Nothing $ do+fileContents :: String -> PatchCheck (Maybe FileContents)+fileContents f = handleInconsistent Nothing $ do P ks _ <- get return (fic ks) where fic (FileLines f' c:_) | f == f' = Just c fic (_:ks) = fic ks- fic [] = Just empty_filecontents+ fic [] = Just emptyFilecontents -- | Checks if a file is empty-file_empty :: String -- ^ Name of the file to check+fileEmpty :: String -- ^ Name of the file to check -> PatchCheck Bool-file_empty f = do- c <- file_contents f+fileEmpty f = do+ c <- fileContents f let empty = case c of- Just c' -> fc_maxline c' == 0 && M.null (fc_lines c')+ Just c' -> fcMaxline c' == 0 && M.null (fcLines c') Nothing -> True if empty- then do set_contents f empty_filecontents- is_valid+ then do setContents f emptyFilecontents+ isValid -- Crude way to make it inconsistent and return false:- else assert_not $ FileEx f+ else assertNot $ FileEx f return empty movedirfilename :: String -> String -> String -> String@@ -181,8 +181,8 @@ -- | Replaces a filename by another in all paths. Returns True if the repository -- is consistent, False if it is not.-do_swap :: String -> String -> PatchCheck Bool-do_swap f f' = handle_inconsistent False $ do+doSwap :: String -> String -> PatchCheck Bool+doSwap f f' = handleInconsistent False $ do modify (\(P ks nots) -> P (map sw ks) (map sw nots)) return True where sw (FileEx a) | f `is_soe` a = FileEx $ movedirfilename f f' a@@ -203,7 +203,7 @@ -- the property is already in the list of properties that do not hold for the -- repo, the state becomes inconsistent, and the function returns false. assert :: Prop -> PatchCheck Bool-assert p = handle_inconsistent False $ do+assert p = handleInconsistent False $ do P ks nots <- get if has p nots then do@@ -217,8 +217,8 @@ -- | Like @assert@, but negatively: state that some property must not hold for -- the current repo.-assert_not :: Prop -> PatchCheck Bool-assert_not p = handle_inconsistent False $ do+assertNot :: Prop -> PatchCheck Bool+assertNot p = handleInconsistent False $ do P ks nots <- get if has p ks then do@@ -233,76 +233,76 @@ -- | Remove a property from the list of properties that do not hold for this -- repo (if it's there), and add it to the list of properties that hold. -- Returns False if the repo is inconsistent, True otherwise.-change_to_true :: Prop -> PatchCheck Bool-change_to_true p = handle_inconsistent False $ do+changeToTrue :: Prop -> PatchCheck Bool+changeToTrue p = handleInconsistent False $ do modify (\(P ks nots) -> P (p:ks) (filter (p /=) nots)) return True -- | Remove a property from the list of properties that hold for this repo (if -- it's in there), and add it to the list of properties that do not hold. -- Returns False if the repo is inconsistent, True otherwise.-change_to_false :: Prop -> PatchCheck Bool-change_to_false p = handle_inconsistent False $ do+changeToFalse :: Prop -> PatchCheck Bool+changeToFalse p = handleInconsistent False $ do modify (\(P ks nots) -> P (filter (p /=) ks) (p:nots)) return True -assert_file_exists :: String -> PatchCheck Bool-assert_file_exists f = do assert_not $ NotEx f- assert_not $ DirEx f+assertFileExists :: String -> PatchCheck Bool+assertFileExists f = do assertNot $ NotEx f+ assertNot $ DirEx f assert $ FileEx f-assert_dir_exists :: String -> PatchCheck Bool-assert_dir_exists d = do assert_not $ NotEx d- assert_not $ FileEx d+assertDirExists :: String -> PatchCheck Bool+assertDirExists d = do assertNot $ NotEx d+ assertNot $ FileEx d assert $ DirEx d-assert_exists :: String -> PatchCheck Bool-assert_exists f = assert_not $ NotEx f+assertExists :: String -> PatchCheck Bool+assertExists f = assertNot $ NotEx f -assert_no_such :: String -> PatchCheck Bool-assert_no_such f = do assert_not $ FileEx f- assert_not $ DirEx f+assertNoSuch :: String -> PatchCheck Bool+assertNoSuch f = do assertNot $ FileEx f+ assertNot $ DirEx f assert $ NotEx f -create_file :: String -> PatchCheck Bool-create_file fn = do- superdirs_exist fn- assert_no_such fn- change_to_true (FileEx fn)- change_to_false (NotEx fn)+createFile :: String -> PatchCheck Bool+createFile fn = do+ superdirsExist fn+ assertNoSuch fn+ changeToTrue (FileEx fn)+ changeToFalse (NotEx fn) -create_dir :: String -> PatchCheck Bool-create_dir fn = do- substuff_dont_exist fn- superdirs_exist fn- assert_no_such fn- change_to_true (DirEx fn)- change_to_false (NotEx fn)+createDir :: String -> PatchCheck Bool+createDir fn = do+ substuffDontExist fn+ superdirsExist fn+ assertNoSuch fn+ changeToTrue (DirEx fn)+ changeToFalse (NotEx fn) -remove_file :: String -> PatchCheck Bool-remove_file fn = do- superdirs_exist fn- assert_file_exists fn- file_empty fn- change_to_false (FileEx fn)- change_to_true (NotEx fn)+removeFile :: String -> PatchCheck Bool+removeFile fn = do+ superdirsExist fn+ assertFileExists fn+ fileEmpty fn+ changeToFalse (FileEx fn)+ changeToTrue (NotEx fn) -remove_dir :: String -> PatchCheck Bool-remove_dir fn = do- substuff_dont_exist fn- superdirs_exist fn- assert_dir_exists fn- change_to_false (DirEx fn)- change_to_true (NotEx fn)+removeDir :: String -> PatchCheck Bool+removeDir fn = do+ substuffDontExist fn+ superdirsExist fn+ assertDirExists fn+ changeToFalse (DirEx fn)+ changeToTrue (NotEx fn) -check_move :: String -> String -> PatchCheck Bool-check_move f f' = do- superdirs_exist f- superdirs_exist f'- assert_exists f- assert_no_such f'- do_swap f f'+checkMove :: String -> String -> PatchCheck Bool+checkMove f f' = do+ superdirsExist f+ superdirsExist f'+ assertExists f+ assertNoSuch f'+ doSwap f f' -substuff_dont_exist :: String -> PatchCheck Bool-substuff_dont_exist d = handle_inconsistent False $ do+substuffDontExist :: String -> PatchCheck Bool+substuffDontExist d = handleInconsistent False $ do P ks _ <- get if all noss ks then return True@@ -317,17 +317,17 @@ -- the init and tail calls dump the final init (which is just the path itself -- again), the first init (which is empty), and the initial "." from -- splitDirectories-superdirs_exist :: String -> PatchCheck Bool-superdirs_exist fn = and `fmap` mapM assert_dir_exists superdirs- where superdirs = map (("./"++) . joinPath) +superdirsExist :: String -> PatchCheck Bool+superdirsExist fn = and `fmap` mapM assertDirExists superdirs+ where superdirs = map (("./"++) . joinPath) (init (tail (inits (tail (splitDirectories fn))))) -file_exists :: String -> PatchCheck Bool-file_exists fn = do- superdirs_exist fn- assert_file_exists fn+fileExists :: String -> PatchCheck Bool+fileExists fn = do+ superdirsExist fn+ assertFileExists fn -dir_exists :: String -> PatchCheck Bool-dir_exists fn = do- superdirs_exist fn- assert_dir_exists fn+dirExists :: String -> PatchCheck Bool+dirExists fn = do+ superdirsExist fn+ assertDirExists fn
+ src/Darcs/Test/Patch/Info.hs view
@@ -0,0 +1,158 @@+-- Copyright (C) 2009 Reinier Lamers+--+-- 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.++-- | This module contains tests for the code in Darcs.Patch.Info. Most of them+-- are about the UTF-8-encoding of patch metadata.+module Darcs.Test.Patch.Info ( metadataDecodingTest, metadataEncodingTest,+ packUnpackTest+ ) where++import Prelude hiding ( pi )+import Data.ByteString ( ByteString )+import qualified Data.ByteString as B ( split, pack )+import qualified Data.ByteString.Char8 as BC ( unpack )+import Data.List ( sort )+import Data.Maybe ( isNothing )+import Data.Text as T ( findBy, any )+import Data.Text.Encoding ( decodeUtf8With )+import Data.Text.Encoding.Error ( lenientDecode )+import Foreign ( unsafePerformIO )+import Test.QuickCheck ( Arbitrary(arbitrary), oneof, listOf, choose, shrink+ , Gen )+import Test.Framework.Providers.QuickCheck2 ( testProperty )+import Test.Framework (Test)+import Data.List ( isPrefixOf )++import Darcs.Patch.Info ( PatchInfo(..), patchinfo,+ piLog, piAuthor, piName )+import ByteStringUtils ( decodeLocale, packStringToUTF8, unpackPSFromUTF8 )++-- | A newtype wrapping String so we can make our own random generator for it.+newtype UnicodeString = UnicodeString { asString :: String }+ deriving (Show, Eq, Ord)++-- | A newtype wrapping PatchInfo that has a random generator that generates+-- both UTF-8-encoded and non-encoded PatchInfo's.+newtype UTF8OrNotPatchInfo = UTF8OrNotPatchInfo PatchInfo deriving (Eq, Ord)++-- | A newtype wrapping PatchInfo, which has a random generator that generates+-- only UTF-8-encoded PatchInfo's.+newtype UTF8PatchInfo = UTF8PatchInfo PatchInfo deriving (Eq, Ord)++instance Arbitrary UnicodeString where+ -- 0x10ffff is the highest Unicode code point ; 0xd800 - 0xdfff are+ -- surrogates. '\xfffd' is excluded because it is used as a marker+ -- for UTF-8 test failure.+ arbitrary = UnicodeString `fmap` listOf (oneof [choose ('\0', '\xd799')+ ,choose ('\xe000', '\xfffc')+ ,choose ('\xfffe', '\x10ffff')])++instance Show UTF8PatchInfo where+ showsPrec _ = withUTF8PatchInfo rawPatchInfoShow+instance Show UTF8OrNotPatchInfo where+ showsPrec _ = withUTF8OrNotPatchInfo rawPatchInfoShow++-- | Shows a PatchInfo, outputting every byte and clearly marking what is what+rawPatchInfoShow :: PatchInfo -> String -> String+rawPatchInfoShow pi = ("PatchInfo: \n"++)+ . ("date: "++) . shows (_piDate pi) . ('\n':)+ . ("author: "++) . shows (_piAuthor pi) . ('\n':)+ . ("name: "++) . shows (_piName pi) . ('\n':)+ . ("log: "++) . shows (_piLog pi) . ('\n':)++instance Arbitrary UTF8PatchInfo where+ arbitrary = UTF8PatchInfo `fmap` arbitraryUTF8Patch+ shrink upi = flip withUTF8PatchInfo upi $ \pi -> do+ sn <- shrink (piName pi)+ sa <- shrink (piAuthor pi)+ sl <- shrink (filter (not . isPrefixOf "Ignore-this:") (piLog pi))+ return (UTF8PatchInfo+ (unsafePerformIO $ patchinfo sn+ (BC.unpack (_piDate pi)) sa sl))++instance Arbitrary UTF8OrNotPatchInfo where+ arbitrary = UTF8OrNotPatchInfo `fmap` oneof ([arbitraryUTF8Patch,+ arbitraryUnencodedPatch])++-- | Generate arbitrary patch metadata that uses the metadata creation function+-- 'patchinfo' from Darcs.Patch.Info.+arbitraryUTF8Patch :: Gen PatchInfo+arbitraryUTF8Patch =+ do n <- asString `fmap` arbitrary+ d <- arbitrary+ a <- asString `fmap` arbitrary+ l <- (lines . asString) `fmap` arbitrary+ return $ unsafePerformIO $ patchinfo n d a l++-- | Generate arbitrary patch metadata that has totally arbitrary byte strings+-- as its name, date, author and log.+arbitraryUnencodedPatch :: Gen PatchInfo+arbitraryUnencodedPatch = do+ n <- arbitraryByteString+ d <- arbitraryByteString+ a <- arbitraryByteString+ -- split 10 is the ByteString equivalent of 'lines'+ l <- B.split 10 `fmap` arbitraryByteString+ i <- arbitrary+ return (PatchInfo d n a l i)++arbitraryByteString :: Gen ByteString+arbitraryByteString = (B.pack . map fromIntegral)+ `fmap` listOf (choose (0, 255) :: Gen Int)++-- | Test that anything produced by the 'patchinfo' function is valid UTF-8+metadataEncodingTest :: Test+metadataEncodingTest = testProperty "Testing patch metadata encoding" $+ withUTF8PatchInfo $+ \patchInfo -> encodingOK (_piAuthor patchInfo)+ && encodingOK (_piName patchInfo)+ && all encodingOK (_piLog patchInfo)+ where encodingOK = isNothing . T.findBy (=='\xfffd') . decodeUtf8With lenientDecode++-- | Test that metadata in patches are decoded as UTF-8 or locale depending on+-- whether they're valid UTF-8.+metadataDecodingTest :: Test+metadataDecodingTest = testProperty "Testing patch metadata decoding" $+ withUTF8OrNotPatchInfo $+ \patchInfo -> utf8OrLocale (_piAuthor patchInfo) == piAuthor patchInfo+ && utf8OrLocale (_piName patchInfo) == piName patchInfo+ && map utf8OrLocale (_piLog patchInfo) `superset` piLog patchInfo+ where utf8OrLocale bs = if isValidUTF8 bs+ then unpackPSFromUTF8 bs+ else decodeLocale bs++isValidUTF8 :: ByteString -> Bool+isValidUTF8 = not . T.any (=='\xfffd') . decodeUtf8With lenientDecode++packUnpackTest :: Test+packUnpackTest = testProperty "Testing UTF-8 packing and unpacking" $+ \uString -> asString uString == (unpackPSFromUTF8 . packStringToUTF8) (asString uString)++superset :: (Eq a, Ord a) => [a] -> [a] -> Bool+superset a b = sorted_superset (sort a) (sort b)+ where sorted_superset (x:xs) (y:ys) | x == y = sorted_superset xs ys+ | x < y = sorted_superset xs (y:ys)+ | y < x = False+ sorted_superset [] (_:_) = False+ sorted_superset _ [] = True++withUTF8PatchInfo :: (PatchInfo -> a) -> UTF8PatchInfo -> a+withUTF8PatchInfo f mpi = case mpi of+ UTF8PatchInfo pinf -> f pinf+withUTF8OrNotPatchInfo :: (PatchInfo -> a) -> UTF8OrNotPatchInfo -> a+withUTF8OrNotPatchInfo f mpi = case mpi of+ UTF8OrNotPatchInfo pinf -> f pinf
src/Darcs/Test/Patch/QuickCheck.hs view
@@ -3,23 +3,21 @@ #include "gadts.h" module Darcs.Test.Patch.QuickCheck ( WithStartState, RepoModel, Tree,-#ifndef GADT_WITNESSES- merge_examples, commute_examples, triple_examples,-#endif- prop_consistent_tree_flattenings, prop_fail,- prop_is_mergeable,+ mergeExamples, commuteExamples, tripleExamples,+ propConsistentTreeFlattenings, propFail,+ propIsMergeable, flattenOne, testConditional, commutePairFromTree, mergePairFromTree, commuteTripleFromTree, commutePairFromTWFP, mergePairFromTWFP, getPairs, getTriples, patchFromTree,- quickCheck, real_patch_loop_examples, shrink+ quickCheck, realPatchLoopExamples, shrink ) where import Control.Arrow ( (***) ) import Control.Monad ( liftM, replicateM, mplus, mzero )-import qualified Data.ByteString.Char8 as BC (pack)-import qualified Data.ByteString as B (ByteString)+import qualified Data.ByteString.Char8 as BC (pack, head)+import qualified Data.ByteString as B (ByteString, length) import Test.QuickCheck import Test.Framework ( Test ) import Test.Framework.Providers.QuickCheck2 ( testProperty )@@ -28,8 +26,9 @@ import Darcs.Witnesses.Ordered import Darcs.Patch.Patchy (--showPatch, Invert(..), Commute(..))-import Darcs.Patch.Prim (Prim(..), Effect(..), FilePatchType(..), FromPrim(..), is_identity )+import Darcs.Patch.Prim (Prim(..), Effect(..), FilePatchType(..), FromPrim(..), isIdentity ) import Darcs.Patch.Real ( RealPatch, prim2real )+import Darcs.Patch.Set ( Origin ) --import Darcs.ColorPrinter ( errorDoc ) --import Darcs.ColorPrinter ( traceDoc ) import Darcs.Witnesses.Show@@ -38,25 +37,15 @@ #include "impossible.h" --#ifndef GADT_WITNESSES-instance Eq (a C(x y)) => Eq (Sealed2 a) where- (Sealed2 x) == (Sealed2 y) = x == y-#endif--instance Show2 Patch where- show2 = show--#ifndef GADT_WITNESSES-triple_examples :: (FromPrim p, Commute p, Invert p) => [p :> p :> p]-triple_examples = [commuteTripleFromTree id $+tripleExamples :: (FromPrim p, Commute p, Invert p) => [Sealed2 (p :> p :> p)]+tripleExamples = [commuteTripleFromTree seal2 $ WithStartState (RepoModel { rmFileName = fp2fn "./file", rmFileContents = [] }) (ParTree (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "g"])) (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "j"])) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "s"])) NilTree))) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "e"])) NilTree))- ,commuteTripleFromTree id $+ ,commuteTripleFromTree seal2 $ WithStartState (RepoModel { rmFileName = fp2fn "./file", rmFileContents = [BC.pack "j"] }) (ParTree@@ -68,12 +57,12 @@ ] -merge_examples :: (FromPrim p, Commute p, Invert p) => [p :\/: p]-merge_examples = map (mergePairFromCommutePair id) commute_examples+mergeExamples :: (FromPrim p, Commute p, Invert p) => [Sealed2 (p :\/: p)]+mergeExamples = map (unseal2 (mergePairFromCommutePair seal2)) commuteExamples -commute_examples :: (FromPrim p, Commute p) => [p :> p]-commute_examples = [- commutePairFromTWFP id $+commuteExamples :: (FromPrim p, Commute p) => [Sealed2 (p :> p)]+commuteExamples = [+ commutePairFromTWFP seal2 $ WithStartState (RepoModel { rmFileName = fp2fn "./file", rmFileContents = [] }) (TWFP 3 (ParTree@@ -82,7 +71,7 @@ (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "f"])) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "v"])) (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "f"] [])) NilTree)))))),- commutePairFromTWFP id $+ commutePairFromTWFP seal2 $ WithStartState (RepoModel { rmFileName = fp2fn "./file", rmFileContents = [BC.pack "f",BC.pack "s",BC.pack "d"] })@@ -94,7 +83,7 @@ (SeqTree (FP (fp2fn "./file") (Hunk 0 [BC.pack "f"] [])) (SeqTree (FP (fp2fn "./file") (Hunk 0 [BC.pack "s",BC.pack "d"] [])) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "v"])) NilTree)))))),-{- commutePairFromTWFP id $+{- commutePairFromTWFP seal2 $ WithStartState (RepoModel { rmFileName = fp2fn "./file", rmFileContents = [BC.pack "f",BC.pack "u",@@ -109,7 +98,7 @@ (SeqTree (FP(fp2fn "./file") (Hunk 0 [BC.pack "u",BC.pack "s",BC.pack "d"] [])) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "a"])) (SeqTree (FP (fp2fn "./file") (Hunk 0 [BC.pack "a"] [])) NilTree))))))),-}- commutePairFromTree id $+ commutePairFromTree seal2 $ WithStartState (RepoModel { rmFileName = fp2fn "./file", rmFileContents = [BC.pack "n",BC.pack "t",BC.pack "h"] }) (ParTree@@ -118,13 +107,13 @@ (SeqTree (FP (fp2fn "./file") (Hunk 2 [BC.pack "h"] [])) (SeqTree (FP (fp2fn "./file") (Hunk 0 [BC.pack "n"] [])) (SeqTree (FP (fp2fn "./file") (Hunk 0 [BC.pack "t"] [])) NilTree)))),- commutePairFromTree id $+ commutePairFromTree seal2 $ WithStartState (RepoModel { rmFileName = fp2fn "./file", rmFileContents = [] }) (ParTree (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "n"])) NilTree) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "i"])) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "i"])) NilTree))),- commutePairFromTree id $+ commutePairFromTree seal2 $ WithStartState (RepoModel { rmFileName = fp2fn "./file", rmFileContents = [] }) (ParTree (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "c"]))@@ -133,7 +122,7 @@ (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "h"])) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "d"])) NilTree)))) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "f"])) NilTree)),- commutePairFromTWFP id $+ commutePairFromTWFP seal2 $ WithStartState (RepoModel { rmFileName = fp2fn "./file", rmFileContents = [] }) (TWFP 1 (ParTree@@ -141,7 +130,7 @@ (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "t"])) NilTree) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "t"])) NilTree)) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "f"])) NilTree))),- commutePairFromTWFP id $+ commutePairFromTWFP seal2 $ WithStartState (RepoModel { rmFileName = fp2fn "./file", rmFileContents = [BC.pack "f",BC.pack " r", BC.pack "c",BC.pack "v"] })@@ -154,7 +143,7 @@ (SeqTree (FP (fp2fn "./file") (Hunk 0 [BC.pack "f",BC.pack "r"] [])) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "y"])) NilTree)))) (SeqTree (FP (fp2fn "./file") (Hunk 3 [BC.pack "v"] [])) NilTree))),- commutePairFromTree id $+ commutePairFromTree seal2 $ WithStartState (RepoModel { rmFileName = fp2fn "./file", rmFileContents = [] }) (ParTree (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "z"])) NilTree)@@ -163,7 +152,7 @@ (ParTree (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "r"])) NilTree) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "d"])) NilTree))))- , commutePairFromTree id $+ , commutePairFromTree seal2 $ WithStartState (RepoModel { rmFileName = fp2fn "./file", rmFileContents = [BC.pack "t",BC.pack "r", BC.pack "h"] })@@ -174,7 +163,7 @@ (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "o"])) NilTree)) (SeqTree (FP (fp2fn "./file") (Hunk 0 [BC.pack "t"] [])) (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "h"] [])) NilTree)))- , commutePairFromTWFP id $+ , commutePairFromTWFP seal2 $ WithStartState (RepoModel { rmFileName = fp2fn "./file", rmFileContents = [] }) $ TWFP 2 (ParTree@@ -182,26 +171,26 @@ (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "y"])) (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "m"])) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "v"])) NilTree))))- , commutePairFromTree id $+ , commutePairFromTree seal2 $ WithStartState (RepoModel {rmFileName = fp2fn "./file",rmFileContents = [] }) (ParTree (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "p"])) (SeqTree (FP (fp2fn "./file") (Hunk 0 [BC.pack "p"] [])) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "c"])) NilTree))) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "z"])) NilTree))- , commutePairFromTree id $+ , commutePairFromTree seal2 $ WithStartState (RepoModel { rmFileName = fp2fn "./file", rmFileContents = [] }) (ParTree (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "j" ])) (SeqTree (FP (fp2fn "./file") (Hunk 0 [BC.pack "j"] [])) NilTree)) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "v"])) NilTree))- , commutePairFromTree id $+ , commutePairFromTree seal2 $ WithStartState (RepoModel { rmFileName = fp2fn "./file", rmFileContents = [] }) (ParTree (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "v"])) NilTree) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "j" ])) (SeqTree (FP (fp2fn "./file") (Hunk 0 [BC.pack "j"] [])) NilTree)))- , commutePairFromTree id $+ , commutePairFromTree seal2 $ WithStartState (RepoModel { rmFileName = fp2fn "./file", rmFileContents = [BC.pack "x",BC.pack "c"] }) (ParTree@@ -211,7 +200,7 @@ (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "x"] [])) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "j"])) NilTree)))) (SeqTree (FP (fp2fn "./file") (Hunk 0 [] [BC.pack "l"])) NilTree))- , commutePairFromTree id $+ , commutePairFromTree seal2 $ WithStartState (RepoModel { rmFileName = fp2fn "./file", rmFileContents = [] }) (ParTree (SeqTree (FP (fp2fn "./file") (Hunk 0 [] (packStringLetters "s"))) NilTree)@@ -220,7 +209,6 @@ (SeqTree (FP (fp2fn "./file") (Hunk 0 [] (packStringLetters "m"))) (SeqTree (FP (fp2fn "./file") (Hunk 0 (packStringLetters "m") [])) NilTree))))) ]-#endif -- | Turns a condition and a test function into a conditional quickcheck -- property that can be run by test-framework.@@ -240,23 +228,21 @@ } deriving (Eq) instance Show (RepoModel C(x)) where- showsPrec d rm = showParen (d > app_prec) $+ showsPrec d rm = showParen (d > appPrec) $ showString "RepoModel { rmFileName = " . showsPrec 0 (rmFileName rm) .- showString ", rmFileContents = " . showsPrec 0 (rmFileContents rm) .+ showString ", rmFileContents = " . showsPrec' 0 (rmFileContents rm) . showString " }"+ where showsPrec' n lines | all ((==1) . B.length) lines = showsPrecC n lines+ | otherwise = showsPrec n lines+ showsPrecC _ [] = showString "[]"+ showsPrecC n ss = showParen (n > 0) $ showString "packStringLetters " . showsPrec (appPrec + 1) (map BC.head ss) instance Show1 RepoModel where- showsPrec1 = showsPrec--#ifdef GADT_WITNESSES--- | The initial repository state, to be used as an argument to the @RepoModel@--- data type.-data InitRepoModel -- this ought to be defined somewhere central as the unique starting state-#endif+ showDict1 = ShowDictClass -- | The initial repository model. The repository contains a single file named -- @./file@, which is empty.-initRepoModel :: RepoModel C(InitRepoModel)+initRepoModel :: RepoModel C(Origin) initRepoModel = RepoModel { rmFileName = fp2fn "./file", rmFileContents = [] } rebuildRepoModel :: RepoModel C(x) -> RepoModel C(y) rebuildRepoModel rm = RepoModel { rmFileName = rmFileName rm, rmFileContents = rmFileContents rm }@@ -285,41 +271,41 @@ arbitrary = do Sealed2 ps1 <- liftM (unseal (seal2 . wesPatch)) $ arbitraryState initRepoModel return $ Sealed2 $ mapFL_FL make_identity_identity ps1 where make_identity_identity :: Prim C(x y) -> Prim C(x y)- make_identity_identity p | IsEq <- is_identity p = identity+ make_identity_identity p | IsEq <- isIdentity p = identity | otherwise = p instance Arbitrary (Sealed2 (FL (WithState RepoModel Prim))) where arbitrary = liftM (unseal (seal2 . wesPatch)) $ arbitraryState initRepoModel -prop_consistent_tree_flattenings :: Sealed (WithStartState RepoModel (Tree Prim)) -> Bool-prop_consistent_tree_flattenings (Sealed (WithStartState start t))+propConsistentTreeFlattenings :: Sealed (WithStartState RepoModel (Tree Prim)) -> Bool+propConsistentTreeFlattenings (Sealed (WithStartState start t)) = fromJust $ do Sealed (G2 flat) <- return $ flattenTree $ mapTree prim2real t rms <- return $ map (applyPatch start) flat- return $ and $ zipWith assert_equal_fst (zip rms flat) (tail $ zip rms flat)+ return $ and $ zipWith assertEqualFst (zip rms flat) (tail $ zip rms flat) -assert_equal_fst :: (Eq a, Show a, Show b, Show c) => (a, b) -> (a, c) -> Bool-assert_equal_fst (x,bx) (y,by)+assertEqualFst :: (Eq a, Show a, Show b, Show c) => (a, b) -> (a, c) -> Bool+assertEqualFst (x,bx) (y,by) | x == y = True | otherwise = error ("Not really equal:\n" ++ show x ++ "\nand\n" ++ show y ++ "\ncoming from\n" ++ show bx ++ "\nand\n" ++ show by) --- WithState and prop_fail are handy for debugging arbitrary code+-- WithState and propFail are handy for debugging arbitrary code data WithState s p C(x y) = WithState (s C(x)) (p C(x y)) (s C(y)) deriving Show data WithStartState s p C(x) = WithStartState (s C(x)) (p C(x)) instance (Show1 s, Show1 p) => Show (WithStartState s p C(x)) where- showsPrec d (WithStartState s p) = showParen (d > app_prec) $ showString "WithStartState " .- showsPrec1 (app_prec + 1) s . showString " " .- showsPrec1 (app_prec + 1) p+ showsPrec d (WithStartState s p) = showParen (d > appPrec) $ showString "WithStartState " .+ showsPrec1 (appPrec + 1) s . showString " " .+ showsPrec1 (appPrec + 1) p instance (Show1 s, Show1 p) => Show1 (WithStartState s p) where- showsPrec1 = showsPrec+ showDict1 = ShowDictClass -prop_fail :: Int -> Tree Prim C(x) -> Bool-prop_fail n xs = sizeTree xs < n+propFail :: Int -> Tree Prim C(x) -> Bool+propFail n xs = sizeTree xs < n instance ArbitraryState s p => ArbitraryState s (WithState s p) where arbitraryState rm = do xandrm' <- arbitraryState rm@@ -348,15 +334,15 @@ instance Show2 p => Show (Tree p C(x)) where showsPrec _ NilTree = showString "NilTree"- showsPrec d (SeqTree a t) = showParen (d > app_prec) $ showString "SeqTree " .- showsPrec2 (app_prec + 1) a . showString " " .- showsPrec (app_prec + 1) t- showsPrec d (ParTree t1 t2) = showParen (d > app_prec) $ showString "ParTree " .- showsPrec (app_prec + 1) t1 . showString " " .- showsPrec (app_prec + 1) t2+ showsPrec d (SeqTree a t) = showParen (d > appPrec) $ showString "SeqTree " .+ showsPrec2 (appPrec + 1) a . showString " " .+ showsPrec (appPrec + 1) t+ showsPrec d (ParTree t1 t2) = showParen (d > appPrec) $ showString "ParTree " .+ showsPrec (appPrec + 1) t1 . showString " " .+ showsPrec (appPrec + 1) t2 instance Show2 p => Show1 (Tree p) where- showsPrec1 = showsPrec+ showDict1 = ShowDictClass sizeTree :: Tree p C(x) -> Int sizeTree NilTree = 0@@ -511,10 +497,10 @@ return $ Sealed $ WithStartState rm (canonizeTree tree) shrink = shrinkWSSTree -prop_is_mergeable :: forall p C(x) . (FromPrim p, Commute p)+propIsMergeable :: forall p C(x) . (FromPrim p, Commute p) => Sealed (WithStartState RepoModel (Tree Prim)) -> Maybe (Tree p C(x))-prop_is_mergeable (Sealed (WithStartState _ t))+propIsMergeable (Sealed (WithStartState _ t)) = case flattenOne t of Sealed ps -> let _ = seal2 ps :: Sealed2 (FL p) in case lengthFL ps of@@ -630,12 +616,12 @@ instance Show2 p => Show (TreeWithFlattenPos p C(x)) where- showsPrec d (TWFP n t) = showParen (d > app_prec) $ showString "TWFP " .- showsPrec (app_prec + 1) n . showString " " .- showsPrec1 (app_prec + 1) t+ showsPrec d (TWFP n t) = showParen (d > appPrec) $ showString "TWFP " .+ showsPrec (appPrec + 1) n . showString " " .+ showsPrec1 (appPrec + 1) t instance Show1 (TreeWithFlattenPos Prim) where- show1 = show+ showDict1 = ShowDictClass instance Arbitrary (Sealed (WithStartState RepoModel (TreeWithFlattenPos Prim))) where arbitrary = do Sealed (WithStartState rm t) <- arbitrary@@ -718,8 +704,8 @@ packStringLetters :: String -> [B.ByteString] packStringLetters = map (BC.pack . (:[])) -real_patch_loop_examples :: [Sealed (WithStartState RepoModel (Tree Prim))]-real_patch_loop_examples =+realPatchLoopExamples :: [Sealed (WithStartState RepoModel (Tree Prim))]+realPatchLoopExamples = [Sealed (WithStartState (RepoModel { rmFileName = fx, rmFileContents = [] }) $ canonizeTree (ParTree
src/Darcs/Test/Patch/Test.hs view
@@ -21,22 +21,22 @@ #include "gadts.h" module Darcs.Test.Patch.Test- ( prop_read_show,- prop_inverse_composition, prop_commute_twice,- prop_inverse_valid, prop_other_inverse_valid,- prop_commute_equivalency, prop_commute_either_order,- prop_commute_either_way, prop_merge_is_commutable_and_correct,- prop_merge_is_swapable, prop_merge_valid,- prop_unravel_three_merge, prop_unravel_seq_merge,- prop_unravel_order_independent,- prop_simple_smart_merge_good_enough,- prop_elegant_merge_good_enough,- prop_patch_and_inverse_is_identity,- quickmerge, check_patch, check_a_patch, verbose_check_a_patch,- prop_resolve_conflicts_valid,- test_patch, prop_commute_inverse,- subcommutes_inverse, subcommutes_nontrivial_inverse,- subcommutes_failure,+ ( propReadShow,+ propInverseComposition, propCommuteTwice,+ propInverseValid, propOtherInverseValid,+ propCommuteEquivalency, propCommuteEitherOrder,+ propCommuteEitherWay, propMergeIsCommutableAndCorrect,+ propMergeIsSwapable, propMergeValid,+ propUnravelThreeMerge, propUnravelSeqMerge,+ propUnravelOrderIndependent,+ propSimpleSmartMergeGoodEnough,+ propElegantMergeGoodEnough,+ propPatchAndInverseIsIdentity,+ quickmerge, checkPatch, checkAPatch, verboseCheckAPatch,+ propResolveConflictsValid,+ testPatch, propCommuteInverse,+ subcommutesInverse, subcommutesNontrivialInverse,+ subcommutesFailure, join_patches ) where @@ -47,10 +47,10 @@ import Darcs.Patch.Info ( PatchInfo, patchinfo ) import Darcs.Test.Patch.Check ( PatchCheck,- check_move, remove_dir, create_dir,- is_valid, insert_line, file_empty, file_exists,- delete_line, modify_file, create_file, remove_file,- do_check, do_verbose_check, FileContents(..)+ checkMove, removeDir, createDir,+ isValid, insertLine, fileEmpty, fileExists,+ deleteLine, modifyFile, createFile, removeFile,+ doCheck, doVerboseCheck, FileContents(..) ) import Darcs.Patch.RegChars ( regChars ) import ByteStringUtils ( linesPS )@@ -65,8 +65,9 @@ changepref, isMerger, invert, commute, merge, readPatch, resolveConflicts, effect, fromPrims,- unravel, merger, elegantMerge )+ elegantMerge ) import Darcs.Patch.Core ( Patch(..) )+import Darcs.Patch.Commute ( unravel, merger ) import Darcs.Patch.Prim ( Prim(..), DirPatchType(..), FilePatchType(..), CommuteFunction, Perhaps(..), subcommutes )@@ -117,7 +118,7 @@ twofilegen p = do n1 <- filepathgen n2 <- filepathgen- if n1 /= n2 && (check_a_patch $ fromPrims $ (p n1 n2 :>: NilFL))+ if n1 /= n2 && (checkAPatch $ fromPrims $ (p n1 n2 :>: NilFL)) then return $ p n1 n2 else twofilegen p @@ -151,40 +152,44 @@ arbpatch n = frequency [(3,PP `fmap` onepatchgen), -- (1,compgen n), (2,flatcompgen n),- (0,raw_merge_gen n),+ (0,rawMergeGen n), (0,mergegen n), (1,PP `fmap` onepatchgen) ] +-- | Generate an arbitrary list of at least one element unempty :: Arbitrary a => Gen [a] unempty = do+ a <- arbitrary as <- arbitrary- case as of- [] -> unempty- _ -> return as+ return (a:as) join_patches :: [Patch] -> Patch join_patches = joinPatches . unsafeFL+ where+ unsafeFL :: [a] -> FL a+ unsafeFL [] = NilFL+ unsafeFL (a:as) = a :>: unsafeFL as -raw_merge_gen :: Int -> Gen Patch-raw_merge_gen n = do p1 <- arbpatch len+rawMergeGen :: Int -> Gen Patch+rawMergeGen n = do p1 <- arbpatch len p2 <- arbpatch len- if (check_a_patch $ join_patches [invert p1,p2]) &&- (check_a_patch $ join_patches [invert p2,p1])+ if (checkAPatch $ join_patches [invert p1,p2]) &&+ (checkAPatch $ join_patches [invert p2,p1]) then case merge (p2 :\/: p1) of _ :/\: p2' -> return p2'- else raw_merge_gen n+ else rawMergeGen n where len = if n < 15 then n`div`3 else 3 mergegen :: Int -> Gen Patch mergegen n = do p1 <- norecursgen len p2 <- norecursgen len- if (check_a_patch $ join_patches [invert p1,p2]) &&- (check_a_patch $ join_patches [invert p2,p1])+ if (checkAPatch $ join_patches [invert p1,p2]) &&+ (checkAPatch $ join_patches [invert p2,p1]) then case merge (p2:\/:p1) of p1' :/\: p2' ->- if check_a_patch $ join_patches [p1,p2']+ if checkAPatch $ join_patches [p1,p2'] then return $ join_patches [p1,p2'] else return $ join_patches [PP $ addfile "Error_in_mergegen", PP $ addfile "Error_in_mergegen",@@ -221,7 +226,7 @@ size <- choose (0,n) myp <- liftM join_patches $ plistgen size ((n+1) `div` (size+1)) -- here I assume we only want to consider valid patches...- if check_a_patch myp+ if checkAPatch myp then return myp else compgen n -}@@ -231,8 +236,8 @@ flatcompgen :: Int -> Gen Patch flatcompgen n = do- myp <- liftM (join_patches . regularize_patches) $ flatlistgen n- if check_a_patch myp+ myp <- liftM (join_patches . regularizePatches) $ flatlistgen n+ if checkAPatch myp then return myp else flatcompgen n @@ -286,88 +291,88 @@ -} -- coarbitrary c = coarbitrary (ord c) -check_patch :: Patch -> PatchCheck Bool-check_a_patch :: Patch -> Bool-check_a_patch p = do_check $ do check_patch p- check_patch $ invert p-verbose_check_a_patch :: Patch -> Bool-verbose_check_a_patch p = do_verbose_check $ do check_patch p- check_patch $ invert p+checkPatch :: Patch -> PatchCheck Bool+checkAPatch :: Patch -> Bool+checkAPatch p = doCheck $ do checkPatch p+ checkPatch $ invert p+verboseCheckAPatch :: Patch -> Bool+verboseCheckAPatch p = doVerboseCheck $ do checkPatch p+ checkPatch $ invert p -check_patch p | isMerger p = do- check_patch $ fromPrims $ effect p-check_patch (Merger _ _ _ _) = impossible-check_patch (Regrem _ _ _ _) = impossible-check_patch (ComP NilFL) = is_valid-check_patch (ComP (p:>:ps)) =- check_patch p >> check_patch (ComP ps)-check_patch (PP Identity) = is_valid-check_patch (PP (Split NilFL)) = is_valid-check_patch (PP (Split (p:>:ps))) =- check_patch (PP p) >> check_patch (PP (Split ps))+checkPatch p | isMerger p = do+ checkPatch $ fromPrims $ effect p+checkPatch (Merger _ _ _ _) = impossible+checkPatch (Regrem _ _ _ _) = impossible+checkPatch (ComP NilFL) = isValid+checkPatch (ComP (p:>:ps)) =+ checkPatch p >> checkPatch (ComP ps)+checkPatch (PP Identity) = isValid+checkPatch (PP (Split NilFL)) = isValid+checkPatch (PP (Split (p:>:ps))) =+ checkPatch (PP p) >> checkPatch (PP (Split ps)) -check_patch (PP (FP f RmFile)) = remove_file $ fn2fp f-check_patch (PP (FP f AddFile)) = create_file $ fn2fp f-check_patch (PP (FP f (Hunk line old new))) = do- file_exists $ fn2fp f- mapM_ (delete_line (fn2fp f) line) old- mapM_ (insert_line (fn2fp f) line) (reverse new)- is_valid-check_patch (PP (FP f (TokReplace t old new))) =- modify_file (fn2fp f) (try_tok_possibly t old new)+checkPatch (PP (FP f RmFile)) = removeFile $ fn2fp f+checkPatch (PP (FP f AddFile)) = createFile $ fn2fp f+checkPatch (PP (FP f (Hunk line old new))) = do+ fileExists $ fn2fp f+ mapM_ (deleteLine (fn2fp f) line) old+ mapM_ (insertLine (fn2fp f) line) (reverse new)+ isValid+checkPatch (PP (FP f (TokReplace t old new))) =+ modifyFile (fn2fp f) (tryTokPossibly t old new) -- note that the above isn't really a sure check, as it leaves PSomethings -- and PNothings which may have contained new...-check_patch (PP (FP f (Binary o n))) = do- file_exists $ fn2fp f- mapM_ (delete_line (fn2fp f) 1) (linesPS o)- file_empty $ fn2fp f- mapM_ (insert_line (fn2fp f) 1) (reverse $ linesPS n)- is_valid+checkPatch (PP (FP f (Binary o n))) = do+ fileExists $ fn2fp f+ mapM_ (deleteLine (fn2fp f) 1) (linesPS o)+ fileEmpty $ fn2fp f+ mapM_ (insertLine (fn2fp f) 1) (reverse $ linesPS n)+ isValid -check_patch (PP (DP d AddDir)) = create_dir $ fn2fp d-check_patch (PP (DP d RmDir)) = remove_dir $ fn2fp d+checkPatch (PP (DP d AddDir)) = createDir $ fn2fp d+checkPatch (PP (DP d RmDir)) = removeDir $ fn2fp d -check_patch (PP (Move f f')) = check_move (fn2fp f) (fn2fp f')-check_patch (PP (ChangePref _ _ _)) = return True+checkPatch (PP (Move f f')) = checkMove (fn2fp f) (fn2fp f')+checkPatch (PP (ChangePref _ _ _)) = return True -regularize_patches :: [Patch] -> [Patch]-regularize_patches patches = rpint [] patches+regularizePatches :: [Patch] -> [Patch]+regularizePatches patches = rpint [] patches where rpint ok_ps [] = ok_ps rpint ok_ps (p:ps) =- if check_a_patch (join_patches $ p:ok_ps)+ if checkAPatch (join_patches $ p:ok_ps) then rpint (p:ok_ps) ps else rpint ok_ps ps -prop_inverse_composition :: Patch -> Patch -> Bool-prop_inverse_composition p1 p2 =+propInverseComposition :: Patch -> Patch -> Bool+propInverseComposition p1 p2 = invert (join_patches [p1,p2]) == join_patches [invert p2, invert p1]-prop_inverse_valid :: Patch -> Bool-prop_inverse_valid p1 = check_a_patch $ join_patches [invert p1,p1]-prop_other_inverse_valid :: Patch -> Bool-prop_other_inverse_valid p1 = check_a_patch $ join_patches [p1,invert p1]+propInverseValid :: Patch -> Bool+propInverseValid p1 = checkAPatch $ join_patches [invert p1,p1]+propOtherInverseValid :: Patch -> Bool+propOtherInverseValid p1 = checkAPatch $ join_patches [p1,invert p1] -prop_commute_twice :: Patch -> Patch -> Property-prop_commute_twice p1 p2 =- (does_commute p1 p2) ==> (Just (p1:>p2) == (commute (p1:>p2) >>= commute))-does_commute :: Patch -> Patch -> Bool-does_commute p1 p2 =- commute (p1:>p2) /= Nothing && (check_a_patch $ join_patches [p1,p2])-prop_commute_equivalency :: Patch -> Patch -> Property-prop_commute_equivalency p1 p2 =- (does_commute p1 p2) ==>+propCommuteTwice :: Patch -> Patch -> Property+propCommuteTwice p1 p2 =+ (doesCommute p1 p2) ==> (Just (p1:>p2) == (commute (p1:>p2) >>= commute))+doesCommute :: Patch -> Patch -> Bool+doesCommute p1 p2 =+ commute (p1:>p2) /= Nothing && (checkAPatch $ join_patches [p1,p2])+propCommuteEquivalency :: Patch -> Patch -> Property+propCommuteEquivalency p1 p2 =+ (doesCommute p1 p2) ==> case commute (p1:>p2) of- Just (p2':>p1') -> check_a_patch $ join_patches [p1,p2,invert p1',invert p2']+ Just (p2':>p1') -> checkAPatch $ join_patches [p1,p2,invert p1',invert p2'] _ -> impossible -prop_commute_either_way :: Patch -> Patch -> Property-prop_commute_either_way p1 p2 =- does_commute p1 p2 ==> does_commute (invert p2) (invert p1)+propCommuteEitherWay :: Patch -> Patch -> Property+propCommuteEitherWay p1 p2 =+ doesCommute p1 p2 ==> doesCommute (invert p2) (invert p1) -prop_commute_either_order :: Patch -> Patch -> Patch -> Property-prop_commute_either_order p1 p2 p3 =- check_a_patch (join_patches [p1,p2,p3]) &&- does_commute p1 (join_patches [p2,p3]) &&- does_commute p2 p3 ==>+propCommuteEitherOrder :: Patch -> Patch -> Patch -> Property+propCommuteEitherOrder p1 p2 p3 =+ checkAPatch (join_patches [p1,p2,p3]) &&+ doesCommute p1 (join_patches [p2,p3]) &&+ doesCommute p2 p3 ==> case commute (p1:>p2) of Nothing -> False Just (p2':>p1') ->@@ -384,9 +389,9 @@ Just (p3''a:>_) -> p3''a == p3'' Nothing -> False -prop_patch_and_inverse_is_identity :: Patch -> Patch -> Property-prop_patch_and_inverse_is_identity p1 p2 =- (check_a_patch $ join_patches [p1,p2]) && (commute (p1:>p2) /= Nothing) ==>+propPatchAndInverseIsIdentity :: Patch -> Patch -> Property+propPatchAndInverseIsIdentity p1 p2 =+ (checkAPatch $ join_patches [p1,p2]) && (commute (p1:>p2) /= Nothing) ==> case commute (p1:>p2) of Just (p2':>_) -> case commute (invert p1:>p2') of Nothing -> True -- This is a subtle distinction.@@ -397,40 +402,40 @@ quickmerge (p1:\/:p2) = case merge (p1:\/:p2) of _ :/\: p1' -> p1' -prop_merge_is_commutable_and_correct :: Patch -> Patch -> Property-prop_merge_is_commutable_and_correct p1 p2 =- (check_a_patch $ join_patches [invert p1,p2]) ==>+propMergeIsCommutableAndCorrect :: Patch -> Patch -> Property+propMergeIsCommutableAndCorrect p1 p2 =+ (checkAPatch $ join_patches [invert p1,p2]) ==> case merge (p2:\/:p1) of p1' :/\: p2' -> case commute (p1:>p2') of Nothing -> False Just (p2'':>p1'') -> p2'' == p2 && p1' == p1''-prop_merge_is_swapable :: Patch -> Patch -> Property-prop_merge_is_swapable p1 p2 =- (check_a_patch $ join_patches [invert p1,p2]) ==>+propMergeIsSwapable :: Patch -> Patch -> Property+propMergeIsSwapable p1 p2 =+ (checkAPatch $ join_patches [invert p1,p2]) ==> case merge (p2:\/:p1) of p1' :/\: p2' -> case merge (p1:\/:p2) of p2''' :/\: p1''' -> p1' == p1''' && p2' == p2''' -prop_merge_valid :: Patch -> Patch -> Property-prop_merge_valid p1 p2 =- (check_a_patch $ join_patches [invert p1,p2]) ==>+propMergeValid :: Patch -> Patch -> Property+propMergeValid p1 p2 =+ (checkAPatch $ join_patches [invert p1,p2]) ==> case merge (p2:\/:p1) of _ :/\: p2' ->- check_a_patch $ join_patches [invert p1,p2,invert p2,p1,p2']+ checkAPatch $ join_patches [invert p1,p2,invert p2,p1,p2'] -prop_simple_smart_merge_good_enough :: Patch -> Patch -> Property-prop_simple_smart_merge_good_enough p1 p2 =- (check_a_patch $ join_patches [invert p1,p2]) ==>- smart_merge (p2:\/:p1) == simple_smart_merge (p2:\/:p1)+propSimpleSmartMergeGoodEnough :: Patch -> Patch -> Property+propSimpleSmartMergeGoodEnough p1 p2 =+ (checkAPatch $ join_patches [invert p1,p2]) ==>+ smartMerge (p2:\/:p1) == simpleSmartMerge (p2:\/:p1) -smart_merge :: (Patch :\/: Patch) -> Maybe (Patch :< Patch)-smart_merge (p1 :\/: p2) =- case simple_smart_merge (p1:\/:p2) of+smartMerge :: (Patch :\/: Patch) -> Maybe (Patch :< Patch)+smartMerge (p1 :\/: p2) =+ case simpleSmartMerge (p1:\/:p2) of Nothing -> Nothing Just (p1'a:<p2a) ->- case simple_smart_merge (p2 :\/: p1) of+ case simpleSmartMerge (p2 :\/: p1) of Nothing -> Nothing Just (x:<y) -> case commute (y:>x) of@@ -439,8 +444,8 @@ if p1'a == p1'b && p2a == p2b && p2a == p2 then Just (p1'a :< p2) else Nothing-simple_smart_merge :: (Patch :\/: Patch) -> Maybe (Patch :< Patch)-simple_smart_merge (p1 :\/: p2) =+simpleSmartMerge :: (Patch :\/: Patch) -> Maybe (Patch :< Patch)+simpleSmartMerge (p1 :\/: p2) = case commute (invert p2 :> p1) of Just (p1':>_) -> case commute (p2:>p1') of@@ -450,10 +455,10 @@ Nothing -> Nothing Nothing -> Nothing -prop_elegant_merge_good_enough :: Patch -> Patch -> Property-prop_elegant_merge_good_enough p1 p2 =- (check_a_patch $ join_patches [invert p1,p2]) ==>- (fst' `fmap` smart_merge (p2:\/:p1)) ==+propElegantMergeGoodEnough :: Patch -> Patch -> Property+propElegantMergeGoodEnough p1 p2 =+ (checkAPatch $ join_patches [invert p1,p2]) ==>+ (fst' `fmap` smartMerge (p2:\/:p1)) == (snd'' `fmap` elegantMerge (p2:\/:p1)) fst' :: p :< p -> p@@ -474,42 +479,42 @@ instance Show p => Show (p :> p) where show (x :> y) = show x ++ " :> " ++ show y -test_patch :: String-test_patch = test_str ++ test_note+testPatch :: String+testPatch = testStr ++ testNote tp1, tp2 :: Patch tp1 = unsafeUnseal . fst . fromJust . readPatch $ BC.pack "\nmove ./test/test ./hello\n" tp2 = unsafeUnseal . fst . fromJust . readPatch $ BC.pack "\nmove ./test ./hello\n" tp1', tp2' :: Patch tp2' = quickmerge (tp2:\/:tp1) tp1' = quickmerge (tp1:\/:tp2)-test_note :: String-test_note = (if commute (tp1:>tp2') == Just (tp2:>tp1')+testNote :: String+testNote = (if commute (tp1:>tp2') == Just (tp2:>tp1') then "At least they commutex right.\n" else "Argh! they don't even commutex right.\n")- ++(if check_a_patch $ tp2+ ++(if checkAPatch $ tp2 then "tp2 itself is valid!\n" else "Oh my! tp2 isn't even valid!\n")- ++(if check_a_patch $ tp2'+ ++(if checkAPatch $ tp2' then "tp2' itself is valid!\n" else "Aaack! tp2' itself is invalid!\n")- ++(if check_a_patch $ join_patches [tp1, tp2']+ ++(if checkAPatch $ join_patches [tp1, tp2'] then "Valid merge tp2'!\n" else "Bad merge tp2'!\n")- ++ (if check_a_patch $ join_patches [tp2, tp1']+ ++ (if checkAPatch $ join_patches [tp2, tp1'] then "Valid merge tp1'!\n" else "Bad merge tp1'!\n")- ++ (if check_a_patch $ join_patches [tp2,tp1',invert tp2',invert tp1]+ ++ (if checkAPatch $ join_patches [tp2,tp1',invert tp2',invert tp1] then "Both agree!\n" else "The two merges don't agree!\n")- ++ (if check_a_patch $ join_patches [invert tp2, tp1]+ ++ (if checkAPatch $ join_patches [invert tp2, tp1] then "They should be mergable!\n" else "Wait a minute, these guys can't be merged!\n") tp :: Patch tp = tp1' -test_str :: String-test_str = "Patches are:\n"++(show tp)- ++(if check_a_patch tp+testStr :: String+testStr = "Patches are:\n"++(show tp)+ ++(if checkAPatch tp then "At least the patch itself is valid.\n" else "The patch itself is bad!\n") ++"commute of tp2 and tp1' is "++show (commute (tp2:>tp1'))++"\n"@@ -518,13 +523,13 @@ ++ (show $ mapFL (joinPatches.flattenFL.merger_equivalent) $ flattenFL tp) ++ "\n\nUnravelled, it gives:\n" ++ (show $ map unravel $ flatten tp) ++ "\n\nUnwound, it gives:\n" ++ (show $ mapFL unwind $ flattenFL tp)- ++(if check_a_patch (join_patches$ reverse $ unwind tp)+ ++(if checkAPatch (join_patches$ reverse $ unwind tp) then "Unwinding is valid.\n" else "Bad unwinding!\n")- ++(if check_a_patch $ join_patches [tp,invert tp]+ ++(if checkAPatch $ join_patches [tp,invert tp] then "Inverse is valid.\n" else "Bad inverse!\n")- ++(if check_a_patch $ join_patches [invert tp, tp]+ ++(if checkAPatch $ join_patches [invert tp, tp] then "Other inverse is valid.\n" else "Bad other inverse!\n")-} @@ -538,56 +543,56 @@ -- unravel the merger, almost any function of the unravelled merger satisfies -- the two constraints mentioned above that the conflict resolution code must -- satisfy.-prop_unravel_three_merge :: Patch -> Patch -> Patch -> Property-prop_unravel_three_merge p1 p2 p3 =- (check_a_patch $ join_patches [invert p1,p2,invert p2,p3]) ==>- (unravel $ merger "0.0" (merger "0.0" p2 p3) (merger "0.0" p2 p1)) ==- (unravel $ merger "0.0" (merger "0.0" p1 p3) (merger "0.0" p1 p2))+propUnravelThreeMerge :: Patch -> Patch -> Patch -> Property+propUnravelThreeMerge p1 p2 p3 =+ (checkAPatch $ join_patches [invert p1,p2,invert p2,p3]) ==>+ (unravel $ unsafeUnseal $ merger "0.0" (unsafeUnseal (merger "0.0" p2 p3)) (unsafeUnseal (merger "0.0" p2 p1))) ==+ (unravel $ unsafeUnseal $ merger "0.0" (unsafeUnseal (merger "0.0" p1 p3)) (unsafeUnseal (merger "0.0" p1 p2))) -prop_unravel_seq_merge :: Patch -> Patch -> Patch -> Property-prop_unravel_seq_merge p1 p2 p3 =- (check_a_patch $ join_patches [invert p1,p2,p3]) ==>- (unravel $ merger "0.0" p3 $ merger "0.0" p2 p1) ==- (unravel $ merger "0.0" (merger "0.0" p2 p1) p3)+propUnravelSeqMerge :: Patch -> Patch -> Patch -> Property+propUnravelSeqMerge p1 p2 p3 =+ (checkAPatch $ join_patches [invert p1,p2,p3]) ==>+ (unravel $ unsafeUnseal $ merger "0.0" p3 $ unsafeUnseal $ merger "0.0" p2 p1) ==+ (unravel $ unsafeUnseal $ merger "0.0" (unsafeUnseal $ merger "0.0" p2 p1) p3) -prop_unravel_order_independent :: Patch -> Patch -> Property-prop_unravel_order_independent p1 p2 =- (check_a_patch $ join_patches [invert p1,p2]) ==>- (unravel $ merger "0.0" p2 p1) == (unravel $ merger "0.0" p1 p2)+propUnravelOrderIndependent :: Patch -> Patch -> Property+propUnravelOrderIndependent p1 p2 =+ (checkAPatch $ join_patches [invert p1,p2]) ==>+ (unravel $ unsafeUnseal $ merger "0.0" p2 p1) == (unravel $ unsafeUnseal $ merger "0.0" p1 p2) -prop_resolve_conflicts_valid :: Patch -> Patch -> Property-prop_resolve_conflicts_valid p1 p2 =- (check_a_patch $ join_patches [invert p1,p2]) ==>- and $ map (check_a_patch.(\l-> join_patches [p,merge_list l]))+propResolveConflictsValid :: Patch -> Patch -> Property+propResolveConflictsValid p1 p2 =+ (checkAPatch $ join_patches [invert p1,p2]) ==>+ and $ map (checkAPatch.(\l-> join_patches [p,mergeList l])) $ resolveConflicts p where p = case merge (p1:\/:p2) of _ :/\: p1' -> join_patches [p2,p1'] -merge_list :: [Sealed (FL Prim C(x))] -> Patch-merge_list patches = fromPrims `unseal` doml NilFL patches+mergeList :: [Sealed (FL Prim C(x))] -> Patch+mergeList patches = fromPrims `unseal` doml NilFL patches where doml mp (Sealed p:ps) = case commute (invert p :> mp) of Just (mp' :> _) -> doml (effect p +>+ effect mp') ps Nothing -> doml mp ps -- This shouldn't happen for "good" resolutions. doml mp [] = Sealed mp -try_tok_possibly :: String -> String -> String+tryTokPossibly :: String -> String -> String -> (Maybe FileContents) -> (Maybe FileContents)-try_tok_possibly t o n = liftM $ \contents ->+tryTokPossibly t o n = liftM $ \contents -> let lines' = M.mapMaybe (liftM B.concat- . try_tok_internal t (BC.pack o) (BC.pack n))- (fc_lines contents)- in contents { fc_lines = lines' }+ . tryTokInternal t (BC.pack o) (BC.pack n))+ (fcLines contents)+ in contents { fcLines = lines' } -try_tok_internal :: String -> B.ByteString -> B.ByteString+tryTokInternal :: String -> B.ByteString -> B.ByteString -> B.ByteString -> Maybe [B.ByteString]-try_tok_internal _ _ _ s | B.null s = Just []-try_tok_internal t o n s =+tryTokInternal _ _ _ s | B.null s = Just []+tryTokInternal t o n s = case BC.break (regChars t) s of (before,s') -> case BC.break (not . regChars t) s' of (tok,after) ->- case try_tok_internal t o n after of+ case tryTokInternal t o n after of Nothing -> Nothing Just rest -> if tok == o@@ -596,25 +601,25 @@ then Nothing else Just $ before : tok : rest -prop_read_show :: Patch -> Bool-prop_read_show p = case readPatch $ renderPS $ showPatch p of+propReadShow :: Patch -> Bool+propReadShow p = case readPatch $ renderPS $ showPatch p of Just (Sealed p',_) -> p' == p Nothing -> False -- |In order for merges to work right with commuted patches, inverting a patch -- past a patch and its inverse had golly well better give you the same patch -- back again.-prop_commute_inverse :: Patch -> Patch -> Property-prop_commute_inverse p1 p2 =- does_commute p1 p2 ==> case commute (p1 :> p2) of+propCommuteInverse :: Patch -> Patch -> Property+propCommuteInverse p1 p2 =+ doesCommute p1 p2 ==> case commute (p1 :> p2) of Nothing -> impossible Just (_ :> p1') -> case commute (p1' :> invert p2) of Nothing -> False Just (_ :> p1'') -> p1'' == p1 -subcommutes_inverse :: [(String, Prim -> Prim -> Property)]-subcommutes_inverse = zip names (map prop_subcommute cs)+subcommutesInverse :: [(String, Prim -> Prim -> Property)]+subcommutesInverse = zip names (map prop_subcommute cs) where (names, cs) = unzip subcommutes prop_subcommute c p1 p2 = does c p1 p2 ==>@@ -634,8 +639,8 @@ _ -> False _ -> False -subcommutes_nontrivial_inverse :: [(String, Prim -> Prim -> Property)]-subcommutes_nontrivial_inverse = zip names (map prop_subcommute cs)+subcommutesNontrivialInverse :: [(String, Prim -> Prim -> Property)]+subcommutesNontrivialInverse = zip names (map prop_subcommute cs) where (names, cs) = unzip subcommutes prop_subcommute c p1 p2 = nontrivial c p1 p2 ==>@@ -655,28 +660,28 @@ _ -> False _ -> False -subcommutes_failure :: [(String, Prim -> Prim -> Property)]-subcommutes_failure = zip names (map prop cs)+subcommutesFailure :: [(String, Prim -> Prim -> Property)]+subcommutesFailure = zip names (map prop cs) where (names, cs) = unzip subcommutes prop c p1 p2 =- does_fail c p1 p2 ==> case c (invert p1 :< invert p2) of+ doesFail c p1 p2 ==> case c (invert p1 :< invert p2) of Failed -> True _ -> False -does_fail :: CommuteFunction -> Prim -> Prim -> Bool-does_fail c p1 p2 =- fails (c (p2:<p1)) && (check_a_patch $ fromPrims $ unsafeFL [p1,p2])+doesFail :: CommuteFunction -> Prim -> Prim -> Bool+doesFail c p1 p2 =+ fails (c (p2:<p1)) && (checkAPatch $ fromPrims (p1 :>: p2 :>: NilFL)) where fails Failed = True fails _ = False does :: CommuteFunction -> Prim -> Prim -> Bool does c p1 p2 =- succeeds (c (p2:<p1)) && (check_a_patch $ fromPrims $ unsafeFL [p1,p2])+ succeeds (c (p2:<p1)) && (checkAPatch $ fromPrims (p1 :>: p2 :>: NilFL)) where succeeds (Succeeded _) = True succeeds _ = False nontrivial :: CommuteFunction -> Prim -> Prim -> Bool nontrivial c p1 p2 =- succeeds (c (p2:<p1)) && (check_a_patch $ fromPrims $ unsafeFL [p1,p2])+ succeeds (c (p2:<p1)) && (checkAPatch $ fromPrims (p1 :>: p2 :>: NilFL)) where succeeds (Succeeded (p1' :< p2')) = p1' /= p1 || p2' /= p2 succeeds _ = False
src/Darcs/Test/Patch/Unit.hs view
@@ -20,21 +20,21 @@ #include "gadts.h" -module Darcs.Test.Patch.Unit ( patch_unit_tests ) where+module Darcs.Test.Patch.Unit ( patchUnitTests ) where import Data.Maybe ( catMaybes, isNothing ) import qualified Data.ByteString.Char8 as BC ( pack ) import Darcs.Witnesses.Sealed import Darcs.Patch import Darcs.Patch.Patchy ( mergeFL, Invert )-import Darcs.Patch.Real ( RealPatch, prim2real, is_consistent, is_forward, is_duplicate )+import Darcs.Patch.Real ( RealPatch, prim2real, isConsistent, isForward, isDuplicate ) import Darcs.Test.Patch.Test () -- for instance Eq Patch import Darcs.Witnesses.Ordered-import Darcs.Patch.Properties ( recommute, commute_inverses, permutivity, partial_permutivity,- inverse_doesnt_commute, patch_and_inverse_commute,- merge_commute, merge_consistent, merge_arguments_consistent,- merge_either_way, show_read,- join_inverses, join_commute )+import Darcs.Patch.Properties ( recommute, commuteInverses, permutivity, partialPermutivity,+ inverseDoesntCommute, patchAndInverseCommute,+ mergeCommute, mergeConsistent, mergeArgumentsConsistent,+ mergeEitherWay, show_read,+ joinInverses, joinCommute ) import Darcs.Patch.Prim ( join ) import Darcs.Test.Patch.QuickCheck import Printer ( Doc, redText, ($$) )@@ -51,56 +51,56 @@ -- #include "impossible.h" -- | The unit tests defined about patches-patch_unit_tests :: [Test]-patch_unit_tests = [--do putStr "Checking with quickcheck that real patches have consistent flattenings... "- -- quickCheck (not . isBottomTimeOut (Just 10) . prop_consistent_tree_flattenings) >> return 0- run_primitive_tests "prim join inverses" (\ (a:\/:_) -> join_inverses join a) mergeables,- testProperty "Checking prim join inverses using QuickCheck... " (isNothing . join_inverses join),- run_primitive_tests "prim inverse doesn't commute" (\ (a:\/:_) -> inverse_doesnt_commute a) mergeables,+patchUnitTests :: [Test]+patchUnitTests = [--do putStr "Checking with quickcheck that real patches have consistent flattenings... "+ -- quickCheck (not . isBottomTimeOut (Just 10) . propConsistentTreeFlattenings) >> return 0+ runPrimitiveTests "prim join inverses" (\ (a:\/:_) -> joinInverses join a) mergeables,+ testProperty "Checking prim join inverses using QuickCheck... " (isNothing . joinInverses join),+ runPrimitiveTests "prim inverse doesn't commute" (\ (a:\/:_) -> inverseDoesntCommute a) mergeables, -- The following fails because of setpref patches... --,do putStr "Checking prim inverse doesn't commute using QuickCheck... "- -- simpleCheck (inverse_doesnt_commute :: Prim -> Maybe Doc)- run_primitive_tests "join commute" (join_commute join) prim_permutables,- testProperty "Checking prim join commute using QuickCheck... " (unseal2 (isNothing . join_commute join)),- run_primitive_tests "prim recommute" (recommute commute) $ map mergeable2commutable mergeables,- run_primitive_tests "prim patch and inverse commute" (patch_and_inverse_commute commute) $ map mergeable2commutable mergeables,- run_primitive_tests "prim inverses commute" (commute_inverses commute) $ map mergeable2commutable mergeables,+ -- simpleCheck (inverseDoesntCommute :: Prim -> Maybe Doc)+ runPrimitiveTests "join commute" (joinCommute join) primPermutables,+ testProperty "Checking prim join commute using QuickCheck... " (unseal2 (isNothing . joinCommute join)),+ runPrimitiveTests "prim recommute" (recommute commute) $ map mergeable2commutable mergeables,+ runPrimitiveTests "prim patch and inverse commute" (patchAndInverseCommute commute) $ map mergeable2commutable mergeables,+ runPrimitiveTests "prim inverses commute" (commuteInverses commute) $ map mergeable2commutable mergeables, --,do putStr "Checking prim recommute using QuickCheck... " -- simpleCheck (recommute -- (commute :: Prim :> Prim -- -> Maybe (Prim :> Prim)))- run_primitive_tests "FL prim recommute" (recommute commute) $ map mergeable2commutable mergeablesFL,- run_primitive_tests "FL prim patch and inverse commute" (patch_and_inverse_commute commute) $ map mergeable2commutable mergeablesFL,- run_primitive_tests "FL prim inverses commute" (commute_inverses commute) $ map mergeable2commutable mergeablesFL,- run_primitive_tests "fails" (commute_fails commute) ([] :: [Prim :> Prim]),- run_primitive_tests "read and show work on Prim" show_read prim_patches,- run_primitive_tests "read and show work on RealPatch" show_read real_patches,+ runPrimitiveTests "FL prim recommute" (recommute commute) $ map mergeable2commutable mergeablesFL,+ runPrimitiveTests "FL prim patch and inverse commute" (patchAndInverseCommute commute) $ map mergeable2commutable mergeablesFL,+ runPrimitiveTests "FL prim inverses commute" (commuteInverses commute) $ map mergeable2commutable mergeablesFL,+ runPrimitiveTests "fails" (commuteFails commute) ([] :: [Prim :> Prim]),+ runPrimitiveTests "read and show work on Prim" show_read primPatches,+ runPrimitiveTests "read and show work on RealPatch" show_read realPatches, testProperty "Checking that readPatch and showPatch work on RealPatch... " (isNothing . (unseal $ patchFromTree $ (show_read :: RealPatch -> Maybe Doc))), testProperty "Checking that readPatch and showPatch work on FL RealPatch... " (isNothing . (unseal2 $ (show_read :: FL RealPatch -> Maybe Doc))),- run_primitive_tests "example flattenings work"- (\x -> if prop_consistent_tree_flattenings x+ runPrimitiveTests "example flattenings work"+ (\x -> if propConsistentTreeFlattenings x then Nothing else Just $ redText "oops")- real_patch_loop_examples,- testProperty "Checking that tree flattenings are consistent... " prop_consistent_tree_flattenings,+ realPatchLoopExamples,+ testProperty "Checking that tree flattenings are consistent... " propConsistentTreeFlattenings, testProperty "Checking with quickcheck that real patches are consistent... "- (isNothing . (unseal $ patchFromTree $ is_consistent)),- run_primitive_tests "real merge input consistent" (merge_arguments_consistent is_consistent) real_mergeables,- run_primitive_tests "real merge input is forward" (merge_arguments_consistent is_forward) real_mergeables,- run_primitive_tests "real merge output is forward" (merge_consistent is_forward) real_mergeables,- run_primitive_tests "real merge output consistent" (merge_consistent is_consistent) real_mergeables,- run_primitive_tests "real merge either way" merge_either_way real_mergeables,- run_primitive_tests "real merge and commute" merge_commute real_mergeables,+ (isNothing . (unseal $ patchFromTree $ isConsistent)),+ runPrimitiveTests "real merge input consistent" (mergeArgumentsConsistent isConsistent) realMergeables,+ runPrimitiveTests "real merge input is forward" (mergeArgumentsConsistent isForward) realMergeables,+ runPrimitiveTests "real merge output is forward" (mergeConsistent isForward) realMergeables,+ runPrimitiveTests "real merge output consistent" (mergeConsistent isConsistent) realMergeables,+ runPrimitiveTests "real merge either way" mergeEitherWay realMergeables,+ runPrimitiveTests "real merge and commute" mergeCommute realMergeables, - run_primitive_tests "real recommute" (recommute commute) real_commutables,- run_primitive_tests "real inverses commute" (commute_inverses commute) real_commutables,- run_primitive_tests "real permutivity" (permutivity commute) $ filter (not_duplicatestriple) real_triples,- run_primitive_tests "real partial permutivity" (partial_permutivity commute) $ filter (not_duplicatestriple) real_triples,+ runPrimitiveTests "real recommute" (recommute commute) realCommutables,+ runPrimitiveTests "real inverses commute" (commuteInverses commute) realCommutables,+ runPrimitiveTests "real permutivity" (permutivity commute) $ filter (notDuplicatestriple) realTriples,+ runPrimitiveTests "real partial permutivity" (partialPermutivity commute) $ filter (notDuplicatestriple) realTriples, testProperty "Checking we can do merges using QuickCheck"- (isNothing . (prop_is_mergeable ::+ (isNothing . (propIsMergeable :: Sealed (WithStartState RepoModel (Tree Prim)) -> Maybe (Tree RealPatch C(x)))), testProperty "Checking recommute using QuickCheck Tree generator"@@ -114,13 +114,13 @@ (commute :: RealPatch :> RealPatch -> Maybe (RealPatch :> RealPatch))))), testConditional "Checking nontrivial recommute"- (unseal $ commutePairFromTree $ nontrivial_reals)+ (unseal $ commutePairFromTree $ nontrivialReals) (unseal $ commutePairFromTree $ (recommute (commute :: RealPatch :> RealPatch -> Maybe (RealPatch :> RealPatch)))), testConditional "Checking nontrivial recommute using TWFP"- (unseal $ commutePairFromTWFP $ nontrivial_reals)+ (unseal $ commutePairFromTWFP $ nontrivialReals) (unseal $ commutePairFromTWFP $ (recommute (commute :: RealPatch :> RealPatch@@ -128,67 +128,67 @@ testProperty "Checking inverses commute using QuickCheck Tree generator" (isNothing . (unseal $ commutePairFromTree $- (commute_inverses+ (commuteInverses (commute :: RealPatch :> RealPatch -> Maybe (RealPatch :> RealPatch))))), testProperty "Checking inverses commute using QuickCheck TWFP generator" (isNothing . (unseal $ commutePairFromTWFP $- (commute_inverses+ (commuteInverses (commute :: RealPatch :> RealPatch -> Maybe (RealPatch :> RealPatch))))), testConditional "Checking nontrivial inverses commute"- (unseal $ commutePairFromTree $ nontrivial_reals)+ (unseal $ commutePairFromTree $ nontrivialReals) (unseal $ commutePairFromTree $- (commute_inverses+ (commuteInverses (commute :: RealPatch :> RealPatch -> Maybe (RealPatch :> RealPatch)))), testConditional "Checking nontrivial inverses commute using TWFP"- (unseal $ commutePairFromTWFP $ nontrivial_reals)+ (unseal $ commutePairFromTWFP $ nontrivialReals) (unseal $ commutePairFromTWFP $- (commute_inverses+ (commuteInverses (commute :: RealPatch :> RealPatch -> Maybe (RealPatch :> RealPatch)))), testProperty "Checking merge either way using QuickCheck TWFP generator" (isNothing . (unseal $ mergePairFromTWFP $- (merge_either_way :: RealPatch :\/: RealPatch -> Maybe Doc))),+ (mergeEitherWay :: RealPatch :\/: RealPatch -> Maybe Doc))), testProperty "Checking merge either way using QuickCheck Tree generator" (isNothing . (unseal $ mergePairFromTree $- (merge_either_way :: RealPatch :\/: RealPatch -> Maybe Doc))),+ (mergeEitherWay :: RealPatch :\/: RealPatch -> Maybe Doc))), testConditional "Checking nontrivial merge either way"- (unseal $ mergePairFromTree $ nontrivial_merge_reals)+ (unseal $ mergePairFromTree $ nontrivialMergeReals) (unseal $ mergePairFromTree $- (merge_either_way :: RealPatch :\/: RealPatch -> Maybe Doc)),+ (mergeEitherWay :: RealPatch :\/: RealPatch -> Maybe Doc)), testConditional "Checking nontrivial merge either way using TWFP"- (unseal $ mergePairFromTWFP $ nontrivial_merge_reals)+ (unseal $ mergePairFromTWFP $ nontrivialMergeReals) (unseal $ mergePairFromTWFP $- (merge_either_way :: RealPatch :\/: RealPatch -> Maybe Doc)),+ (mergeEitherWay :: RealPatch :\/: RealPatch -> Maybe Doc)), testConditional "Checking permutivity"- (unseal $ commuteTripleFromTree not_duplicatestriple)+ (unseal $ commuteTripleFromTree notDuplicatestriple) (unseal $ commuteTripleFromTree $ permutivity (commute :: RealPatch :> RealPatch -> Maybe (RealPatch :> RealPatch))), testConditional "Checking partial permutivity"- (unseal $ commuteTripleFromTree not_duplicatestriple)- (unseal $ commuteTripleFromTree $ partial_permutivity+ (unseal $ commuteTripleFromTree notDuplicatestriple)+ (unseal $ commuteTripleFromTree $ partialPermutivity (commute :: RealPatch :> RealPatch -> Maybe (RealPatch :> RealPatch))), testConditional "Checking nontrivial permutivity" (unseal $ commuteTripleFromTree- (\t -> nontrivial_triple t && not_duplicatestriple t))+ (\t -> nontrivialTriple t && notDuplicatestriple t)) (unseal $ commuteTripleFromTree $ (permutivity (commute :: RealPatch :> RealPatch -> Maybe (RealPatch :> RealPatch)))) ] -not_duplicatestriple :: RealPatch :> RealPatch :> RealPatch -> Bool-not_duplicatestriple (a :> b :> c) = not $ any is_duplicate [a,b,c]+notDuplicatestriple :: RealPatch :> RealPatch :> RealPatch -> Bool+notDuplicatestriple (a :> b :> c) = not $ any isDuplicate [a,b,c] --not_duplicates_pair :: RealPatch :> RealPatch -> Bool---not_duplicates_pair (a :> b) = not $ any is_duplicate [a,b]+--not_duplicates_pair (a :> b) = not $ any isDuplicate [a,b] -nontrivial_triple :: RealPatch :> RealPatch :> RealPatch -> Bool-nontrivial_triple (a :> b :> c) =+nontrivialTriple :: RealPatch :> RealPatch :> RealPatch -> Bool+nontrivialTriple (a :> b :> c) = case commute (a :> b) of Nothing -> False Just (b' :> a') ->@@ -201,30 +201,30 @@ (not (c' `unsafeCompare` c) || not (b'' `unsafeCompare` b)) && (not (c'' `unsafeCompare` c) || not (a'' `unsafeCompare` a')) -nontrivial_reals :: RealPatch :> RealPatch -> Bool-nontrivial_reals = nontrivial_commute+nontrivialReals :: RealPatch :> RealPatch -> Bool+nontrivialReals = nontrivialCommute -nontrivial_commute :: Patchy p => p :> p -> Bool-nontrivial_commute (x :> y) = case commute (x :> y) of+nontrivialCommute :: Patchy p => p :> p -> Bool+nontrivialCommute (x :> y) = case commute (x :> y) of Just (y' :> x') -> not (y' `unsafeCompare` y) || not (x' `unsafeCompare` x) Nothing -> False -nontrivial_merge_reals :: RealPatch :\/: RealPatch -> Bool-nontrivial_merge_reals = nontrivial_merge+nontrivialMergeReals :: RealPatch :\/: RealPatch -> Bool+nontrivialMergeReals = nontrivialMerge -nontrivial_merge :: Patchy p => p :\/: p -> Bool-nontrivial_merge (x :\/: y) = case merge (x :\/: y) of+nontrivialMerge :: Patchy p => p :\/: p -> Bool+nontrivialMerge (x :\/: y) = case merge (x :\/: y) of y' :/\: x' -> not (y' `unsafeCompare` y) || not (x' `unsafeCompare` x) -- | Run a test function on a set of data, using HUnit. The test function should -- return @Nothing@ upon success and a @Just x@ upon failure.-run_primitive_tests :: (Show a, Show b) => String -- ^ The test name+runPrimitiveTests :: (Show a, Show b) => String -- ^ The test name -> (a -> Maybe b) -- ^ The test function -> [a] -- ^ The test data -> Test-run_primitive_tests name test datas = testCase name (assertBool assertName res)+runPrimitiveTests name test datas = testCase name (assertBool assertName res) where assertName = "Boolean assertion for \"" ++ name ++ "\"" res = and $ map (isNothing . test) datas @@ -232,8 +232,8 @@ quickhunk l o n = hunk "test" l (map (\c -> BC.pack [c]) o) (map (\c -> BC.pack [c]) n) -prim_permutables :: [Prim :> Prim :> Prim]-prim_permutables =+primPermutables :: [Prim :> Prim :> Prim]+primPermutables = [quickhunk 0 "e" "bo" :> quickhunk 3 "" "x" :> quickhunk 2 "f" "qljo"] mergeables :: [Prim :\/: Prim]@@ -255,17 +255,18 @@ mergeable2commutable :: Invert p => p :\/: p -> p :> p mergeable2commutable (x :\/: y) = invert x :> y -prim_patches :: [Prim]-prim_patches = concatMap mergeable2patches mergeables+primPatches :: [Prim]+primPatches = concatMap mergeable2patches mergeables where mergeable2patches (x:\/:y) = [x,y] -real_patches :: [RealPatch]-real_patches = concatMap commutable2patches real_commutables+realPatches :: [RealPatch]+realPatches = concatMap commutable2patches realCommutables where commutable2patches (x:>y) = [x,y] -real_triples :: [RealPatch :> RealPatch :> RealPatch]-real_triples = [ob' :> oa2 :> a2'',- oa' :> oa2 :> a2''] ++ triple_examples+realTriples :: [RealPatch :> RealPatch :> RealPatch]+realTriples = [ob' :> oa2 :> a2'',+ oa' :> oa2 :> a2'']+ ++ map unsafeUnseal2 tripleExamples ++ map unsafeUnseal2 (concatMap getTriples realFLs) where oa = prim2real $ quickhunk 1 "o" "aa" oa2 = oa@@ -280,18 +281,19 @@ where oa = prim2real $ quickhunk 1 "o" "a" ps :/\: _ = merge (oa :>: invert oa :>: NilFL :\/: oa :>: invert oa :>: NilFL) -real_commutables :: [RealPatch :> RealPatch]-real_commutables = commute_examples ++ map mergeable2commutable real_mergeables+++realCommutables :: [RealPatch :> RealPatch]+realCommutables = map unsafeUnseal2 commuteExamples+++ map mergeable2commutable realMergeables++ [invert oa :> ob'] ++ map unsafeUnseal2 (concatMap getPairs realFLs) where oa = prim2real $ quickhunk 1 "o" "a" ob = prim2real $ quickhunk 1 "o" "b" _ :/\: ob' = mergeFL (ob :\/: oa :>: invert oa :>: NilFL) -real_mergeables :: [RealPatch :\/: RealPatch]-real_mergeables = map (\ (x :\/: y) -> prim2real x :\/: prim2real y) mergeables- ++ real_igloo_mergeables- ++ real_quickcheck_mergeables- ++ merge_examples+realMergeables :: [RealPatch :\/: RealPatch]+realMergeables = map (\ (x :\/: y) -> prim2real x :\/: prim2real y) mergeables+ ++ realIglooMergeables+ ++ realQuickcheckMergeables+ ++ map unsafeUnseal2 mergeExamples ++ catMaybes (map pair2m (concatMap getPairs realFLs)) ++ [(oa :\/: od), (oa :\/: a2'),@@ -335,8 +337,8 @@ pair2m (Sealed2 (xx :> y)) = do y' :> _ <- commute (xx :> y) return (xx :\/: y') -real_igloo_mergeables :: [RealPatch :\/: RealPatch]-real_igloo_mergeables = [(a :\/: b),+realIglooMergeables :: [RealPatch :\/: RealPatch]+realIglooMergeables = [(a :\/: b), (b :\/: c), (a :\/: c), (x :\/: a),@@ -356,8 +358,8 @@ y' :/\: _ = merge (b :\/: y) z' :/\: _ = merge (c :\/: z) -real_quickcheck_mergeables :: [RealPatch :\/: RealPatch]-real_quickcheck_mergeables = [-- invert k1 :\/: n1+realQuickcheckMergeables :: [RealPatch :\/: RealPatch]+realQuickcheckMergeables = [-- invert k1 :\/: n1 --, invert k2 :\/: n2 hb :\/: k , b' :\/: b'@@ -388,9 +390,9 @@ d3 :/\: _ = merge (xi :\/: d) _ :/\: k3 = mergeFL (k :\/: i :>: x :>: xi :>: d3 :>: NilFL) -commute_fails :: (MyEq p, Patchy p) => (p :> p -> Maybe (p :> p)) -> p :> p+commuteFails :: (MyEq p, Patchy p) => (p :> p -> Maybe (p :> p)) -> p :> p -> Maybe Doc-commute_fails c (x :> y) = do y' :> x' <- c (x :> y)+commuteFails c (x :> y) = do y' :> x' <- c (x :> y) return $ redText "x" $$ showPatch x $$ redText ":> y" $$ showPatch y $$ redText "y'" $$ showPatch y' $$
+ src/Darcs/Test/Unit.lhs view
@@ -0,0 +1,766 @@+% Copyright (C) 2002-2005,2007 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.++\documentclass{report}+\usepackage{color}++\usepackage{verbatim}+\newenvironment{code}{\color{blue}\verbatim}{\endverbatim}++\begin{document}++% Definition of title page:+\title{+ Unit Testing for darcs in Haskell+}+\author{+ David Roundy % insert author(s) here+}++\maketitle++\tableofcontents % Table of Contents++\chapter{Introduction}++This is a unit testing program, which is intended to make sure that all the+functions of my darcs code work properly.++\begin{code}+{-# OPTIONS_GHC -cpp -fno-warn-orphans -fno-warn-deprecations -fglasgow-exts #-}+{-# LANGUAGE CPP #-}++module Darcs.Test.Unit (main) where++import System.IO.Unsafe ( unsafePerformIO )+import ByteStringUtils hiding ( intercalate )+import qualified Data.ByteString.Char8 as BC ( unpack, pack )+import qualified Data.ByteString as B ( concat, empty )+import Darcs.Patch+import Darcs.Test.Patch.Test+import Darcs.Test.Patch.Unit ( patchUnitTests )+import Darcs.Test.Email ( emailParsing, emailHeaderNoLongLines,+ emailHeaderAsciiChars, emailHeaderLinesStart,+ emailHeaderNoEmptyLines )+import Darcs.Test.Patch.Info ( metadataDecodingTest, metadataEncodingTest,+ packUnpackTest )+import Lcs ( shiftBoundaries )+import Test.QuickCheck+import Printer ( renderPS )+import Darcs.Patch.Commute+import Data.Array.Base+import Data.Array.Unboxed+import Control.Monad.ST+import Darcs.Witnesses.Ordered+import Darcs.Witnesses.Sealed ( Sealed(Sealed), unsafeUnseal )+import Test.HUnit ( assertBool, assertFailure )+import Test.Framework.Providers.QuickCheck2 ( testProperty )+import Test.Framework.Providers.HUnit ( testCase )+import Test.Framework.Runners.Console ( defaultMain )+import Test.Framework ( Test )++#include "impossible.h"+\end{code}++\chapter{Main body of code}++\begin{code}+main :: IO ()+main = do+ putStr ("There are a total of "++(show (length primitiveTestPatches))+ ++" primitive patches.\n")+ putStr ("There are a total of "+++ (show (length testPatches))++" patches.\n")+ defaultMain tests++-- | Utility function to run bools with test-framework+testBool :: String -> Bool -> Test+testBool name test = testCase name (assertBool assertName test)+ where assertName = "boolean test \"" ++ name ++ "\" should return True"++-- | Utility function to run old tests that return a list of error messages,+-- with the empty list meaning success.+testStringList :: String -> [String] -> Test+testStringList name test = testCase name $ mapM_ assertFailure test++-- | This is the big list of tests that will be run using testrunner.+tests :: [Test]+tests = patchUnitTests +++ [testBool "Checking that UTF-8 packing and unpacking preserves 'hello world'"+ (unpackPSFromUTF8 (BC.pack "hello world") == "hello world"),+ testBool "Checking that hex packing and unpacking preserves 'hello world'"+ (BC.unpack (fromHex2PS $ fromPS2Hex $ BC.pack "hello world")+ == "hello world"),+ emailParsing,+ emailHeaderNoLongLines,+ emailHeaderAsciiChars,+ emailHeaderLinesStart,+ emailHeaderNoEmptyLines,+ testProperty "Checking that B.concat works" propConcatPS,+ testProperty "Checking that hex conversion works" propHexConversion,+ testProperty "Checking that show and read work right" propReadShow,+ testStringList "Checking known commutes" commuteTests,+ testStringList "Checking known merges" mergeTests,+ testStringList "Checking known canons" canonizationTests]+ ++ checkSubcommutes subcommutesInverse "patch and inverse both commute"+ ++ checkSubcommutes subcommutesNontrivialInverse "nontrivial commutes are correct"+ ++ checkSubcommutes subcommutesFailure "inverses fail"+ +++ [testProperty "Checking that commuting by patch and its inverse is ok" propCommuteInverse,+ --putStr "Checking that conflict resolution is valid... "+ --runQuickCheckTest returnval propResolveConflictsValid+ testProperty "Checking that a patch followed by its inverse is identity" propPatchAndInverseIsIdentity,+ -- The following tests are "wrong" with the Conflictor code.+ --putStr "Checking that a simple smart_merge is sufficient... "+ --runQuickCheckTest returnval propSimpleSmartMergeGoodEnough+ --putStr "Checking that an elegant merge is sufficient... "+ --runQuickCheckTest returnval propElegantMergeGoodEnough+ testProperty "Checking that commutes are equivalent" propCommuteEquivalency,+ testProperty "Checking that merges are valid" propMergeValid,+ testProperty "Checking inverses being valid" propInverseValid,+ testProperty "Checking other inverse being valid" propOtherInverseValid,+ testStringList "Checking merge swaps" mergeSwapTests,+ -- The patch generator isn't smart enough to generate correct test+ -- cases for the following: (which will be obsoleted soon, anyhow)+ --putStr "Checking the order dependence of unravel... "+ --runQuickCheckTest returnval propUnravelOrderIndependent+ --putStr "Checking the unravelling of three merges... "+ --runQuickCheckTest returnval propUnravelThreeMerge+ --putStr "Checking the unravelling of a merge of a sequence... "+ --runQuickCheckTest returnval propUnravelSeqMerge+ testProperty "Checking inverse of inverse" propInverseComposition,+ testProperty "Checking the order of commutes" propCommuteEitherOrder,+ testProperty "Checking commute either way" propCommuteEitherWay,+ testProperty "Checking the double commute" propCommuteTwice,+ testProperty "Checking that merges commute and are well behaved" propMergeIsCommutableAndCorrect,+ testProperty "Checking that merges can be swapped" propMergeIsSwapable,+ testProperty "Checking again that merges can be swapped (I'm paranoid) " propMergeIsSwapable,+ testStringList "Checking that the patch validation works" testCheck,+ testStringList "Checking commute/recommute" commuteRecommuteTests,+ testStringList "Checking merge properties" genericMergeTests,+ testStringList "Testing the lcs code" showLcsTests,+ testStringList "Checking primitive patch IO functions" primitiveShowReadTests,+ testStringList "Checking IO functions" showReadTests,+ testStringList "Checking primitive commute/recommute" primitiveCommuteRecommuteTests,+ metadataDecodingTest,+ metadataEncodingTest,+ packUnpackTest+ ]+\end{code}++\chapter{Unit Tester}++The unit tester function is really just a glorified map for functions that+return lists, in which the lists get concatenated (where map would end up+with a list of lists).++\begin{code}+type PatchUnitTest p = p -> [String]+type TwoPatchUnitTest = Patch -> Patch -> [String]++parallelPairUnitTester :: TwoPatchUnitTest -> [(Patch:\/:Patch)] -> [String]+parallelPairUnitTester _ [] = []+parallelPairUnitTester thetest ((p1:\/:p2):ps)+ = (thetest p1 p2)++(parallelPairUnitTester thetest ps)++pairUnitTester :: TwoPatchUnitTest -> [(Patch:<Patch)] -> [String]+pairUnitTester _ [] = []+pairUnitTester thetest ((p1:<p2):ps)+ = (thetest p1 p2)++(pairUnitTester thetest ps)+\end{code}++\chapter{LCS}++Here are a few quick tests of the shiftBoundaries function.++\begin{code}+showLcsTests :: [String]+showLcsTests = concatMap checkKnownShifts knownShifts+checkKnownShifts :: ([Int],[Int],String,String,[Int],[Int])+ -> [String]+checkKnownShifts (ca, cb, sa, sb, ca', cb') = runST (+ do ca_arr <- newListArray (0, length ca) $ toBool (0:ca)+ cb_arr <- newListArray (0, length cb) $ toBool (0:cb)+ let p_a = listArray (0, length sa) $ B.empty:(toPS sa)+ p_b = listArray (0, length sb) $ B.empty:(toPS sb)+ shiftBoundaries ca_arr cb_arr p_a 1 1+ shiftBoundaries cb_arr ca_arr p_b 1 1+ ca_res <- fmap (fromBool . tail) $ getElems ca_arr+ cb_res <- fmap (fromBool . tail) $ getElems cb_arr+ return $ if ca_res == ca' && cb_res == cb' then []+ else ["shiftBoundaries failed on "++sa++" and "++sb++" with "+ ++(show (ca,cb))++" expected "++(show (ca', cb'))+ ++" got "++(show (ca_res, cb_res))++"\n"])+ where toPS = map (\c -> if c == ' ' then B.empty else BC.pack [c])+ toBool = map (>0)+ fromBool = map (\b -> if b then 1 else 0)++knownShifts :: [([Int],[Int],String,String,[Int],[Int])]+knownShifts =+ [([0,0,0],[0,1,0,1,0],"aaa","aaaaa",+ [0,0,0],[0,0,0,1,1]),+ ([0,1,0],[0,1,1,0],"cd ","c a ",+ [0,1,0],[0,1,1,0]),+ ([1,0,0,0,0,0,0,0,0],[1,0,0,0,0,0,1,1,1,1,1,0,0,0], "fg{} if{}","dg{} ih{} if{}",+ [1,0,0,0,0,0,0,0,0],[1,0,0,0,0,1,1,1,1,1,0,0,0,0]), -- prefer empty line at end+ ([0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,1,1,1,1,1,0,0,0], "fg{} if{}","fg{} ih{} if{}",+ [0,0,0,0,0,0,0,0,0],[0,0,0,0,0,1,1,1,1,1,0,0,0,0]), -- prefer empty line at end+ ([],[1,1],"","aa",[],[1,1]),+ ([1,1],[],"aa","",[1,1],[])]+++\end{code}++\chapter{Show/Read tests}++This test involves calling ``show'' to print a string describing a patch,+and then using readPatch to read it back in, and making sure the patch we+read in is the same as the original. Useful for making sure that I don't+have any stupid IO bugs.++\begin{code}+showReadTests :: [String]+showReadTests = concatMap tShowRead testPatches +++ concatMap tShowRead testPatchesNamed+primitiveShowReadTests :: [String]+primitiveShowReadTests = concatMap tShowRead primitiveTestPatches+tShowRead :: (Eq p, Show p, Patchy p) => PatchUnitTest p+tShowRead p =+ case readPatch $ renderPS $ showPatch p of+ Just (Sealed p',_) -> if p' == p then []+ else ["Failed to read shown: "++(show p)++"\n"]+ Nothing -> ["Failed to read at all: "++(show p)++"\n"]++instance MyEq p => Eq (Named p) where+ (==) = unsafeCompare+\end{code}++\chapter{Canonization tests}++This is a set of known correct canonizations, to make sure that I'm+canonizing as I ought.++\begin{code}+canonizationTests :: [String]+canonizationTests = concatMap checkKnownCanon knownCanons+checkKnownCanon :: (Patch, Patch) -> [String]+checkKnownCanon (p1,p2) =+ if (fromPrims $ concatFL $ mapFL_FL canonize $ sortCoalesceFL $ effect p1) == p2+ then []+ else ["Canonization failed:\n"++show p1++"canonized is\n"+ ++show (fromPrims $ concatFL $ mapFL_FL canonize $ sortCoalesceFL $ effect p1 :: Patch)+ ++"which is not\n"++show p2]+knownCanons :: [(Patch,Patch)]+knownCanons =+ [(quickhunk 1 "abcde" "ab", quickhunk 3 "cde" ""),+ (quickhunk 1 "abcde" "bd", join_patches [quickhunk 1 "a" "",+ quickhunk 2 "c" "",+ quickhunk 3 "e" ""]),+ (join_patches [quickhunk 4 "a" "b",+ quickhunk 1 "c" "d"],+ join_patches [quickhunk 1 "c" "d",+ quickhunk 4 "a" "b"]),+ (join_patches [quickhunk 1 "a" "",+ quickhunk 1 "" "b"],+ quickhunk 1 "a" "b"),+ (join_patches [quickhunk 1 "ab" "c",+ quickhunk 1 "cd" "e"],+ quickhunk 1 "abd" "e"),+ (quickhunk 1 "abcde" "cde", quickhunk 1 "ab" ""),+ (quickhunk 1 "abcde" "acde", quickhunk 2 "b" "")]+quickhunk :: Int -> String -> String -> Patch+quickhunk l o n = fromPrim $ hunk "test" l (map (\c -> BC.pack [c]) o)+ (map (\c -> BC.pack [c]) n)+\end{code}++\chapter{Merge/unmgerge tests}++It should always be true that if two patches can be unmerged, then merging+the resulting patches should give them back again.+\begin{code}+genericMergeTests :: [String]+genericMergeTests =+ case take 400 [(p1:\/:p2)|+ i <- [0..(length testPatches)-1],+ p1<-[testPatches!!i],+ p2<-drop i testPatches,+ checkAPatch $ join_patches [invert p2,p1]] of+ merge_pairs -> (parallelPairUnitTester tMergeEitherWayValid merge_pairs) +++ (parallelPairUnitTester tMergeSwapMerge merge_pairs)+tMergeEitherWayValid :: TwoPatchUnitTest+tMergeEitherWayValid p1 p2 =+ case join_patches [p2, quickmerge (p1:\/: p2)] of+ combo2 ->+ case join_patches [p1, quickmerge (p2:\/: p1)] of+ combo1 ->+ if not $ checkAPatch $ join_patches [combo1]+ then ["oh my combo1 invalid:\n"++show p1++"and...\n"++show p2++show combo1]+ else+ if checkAPatch $ join_patches [invert combo1, combo2]+ then []+ else ["merge both ways invalid:\n"++show p1++"and...\n"++show p2+++ show combo1+++ show combo2]+tMergeSwapMerge :: TwoPatchUnitTest+tMergeSwapMerge p1 p2 =+ if (swapp $ merge (p2:\/: p1)) == merge (p1:\/:p2)+ then []+ else ["Failed to swap merges:\n"++show p1++"and...\n"++show p2+ ++"merged:\n"++show (merge (p1:\/:p2))++"\n"+ ++"merged and swapped:\n"++show (swapp $ merge (p2:\/: p1))++"\n"]+ where swapp (x :/\: y) = y :/\: x++instance Show p => Show (p :/\: p) where+ show (x :/\: y) = show x ++ " :/\\: " ++ show y+instance Eq p => Eq (p :/\: p) where+ (x :/\: y) == (x' :/\: y') = x == x' && y == y'+\end{code}++\chapter{Commute/recommute tests}++Here we test to see if commuting patch A and patch B and then commuting the+result gives us patch A and patch B again. The set of patches (A,B) is+chosen from the set of all pairs of test patches by selecting those which+commute with one another.++\begin{code}+commuteRecommuteTests :: [String]+commuteRecommuteTests =+ case take 200 [(p2:<p1)|+ p1<-testPatches,+ p2<-filter (\p->checkseq [p1,p]) testPatches,+ commute (p1:>p2) /= Nothing] of+ commute_pairs -> pairUnitTester tCommuteRecommute commute_pairs+ where checkseq ps = checkAPatch $ join_patches ps+primitiveCommuteRecommuteTests :: [String]+primitiveCommuteRecommuteTests =+ pairUnitTester tCommuteRecommute+ [(p1:<p2)|+ p1<-primitiveTestPatches,+ p2<-primitiveTestPatches,+ commute (p2:>p1) /= Nothing,+ checkAPatch $ join_patches [p2,p1]]+tCommuteRecommute :: TwoPatchUnitTest+tCommuteRecommute p1 p2 =+ if (commute (p2:>p1) >>= commute) == Just (p2:>p1)+ then []+ else ["Failed to recommute:\n"++(show p2)++(show p1)+++ "we saw it as:\n"++show (commute (p2:>p1))+++ "\nAnd recommute was:\n"++show (commute (p2:>p1) >>= commute)+ ++ "\n"]+\end{code}++\chapter{Commute tests}++Here we provide a set of known interesting commutes.+\begin{code}+commuteTests :: [String]+commuteTests =+ concatMap checkKnownCommute knownCommutes+++ concatMap checkCantCommute knownCantCommute+checkKnownCommute :: (Patch:< Patch, Patch:< Patch) -> [String]+checkKnownCommute (p1:<p2,p2':<p1') =+ case commute (p2:>p1) of+ Just (p1a:>p2a) ->+ if (p2a:< p1a) == (p2':< p1')+ then []+ else ["Commute gave wrong value!\n"++show p1++"\n"++show p2+ ++"should be\n"++show p2'++"\n"++show p1'+ ++"but is\n"++show p2a++"\n"++show p1a]+ Nothing -> ["Commute failed!\n"++show p1++"\n"++show p2]+ +++ case commute (p1':>p2') of+ Just (p2a:>p1a) ->+ if (p1a:< p2a) == (p1:< p2)+ then []+ else ["Commute gave wrong value!\n"++show p2a++"\n"++show p1a+ ++"should have been\n"++show p2'++"\n"++show p1']+ Nothing -> ["Commute failed!\n"++show p2'++"\n"++show p1']+knownCommutes :: [(Patch:<Patch,Patch:<Patch)]+knownCommutes = [+ (testhunk 1 [] ["A"]:<+ testhunk 2 [] ["B"],+ testhunk 3 [] ["B"]:<+ testhunk 1 [] ["A"]),+ (fromPrim (tokreplace "test" "A-Za-z_" "old" "new"):<+ testhunk 2+ ["hello world all that is old is good old_"]+ ["I don't like old things"],+ testhunk 2+ ["hello world all that is new is good old_"]+ ["I don't like new things"]:<+ fromPrim (tokreplace "test" "A-Za-z_" "old" "new")),+ (testhunk 1 ["A"] ["B"]:<+ testhunk 2 ["C"] ["D"],+ testhunk 2 ["C"] ["D"]:<+ testhunk 1 ["A"] ["B"]),+ (fromPrim (rmfile "NwNSO"):<+ (quickmerge (fromPrim (addfile "hello"):\/:fromPrim (addfile "hello"))),+ (quickmerge (fromPrim (addfile "hello"):\/:fromPrim (addfile "hello"))):<+ fromPrim (rmfile "NwNSO")),++ (quickmerge (testhunk 3 ["o"] ["n"]:\/:+ testhunk 3 ["o"] ["v"]):<+ testhunk 1 [] ["a"],+ testhunk 1 [] ["a"]:<+ quickmerge (testhunk 2 ["o"] ["n"]:\/:+ testhunk 2 ["o"] ["v"])),++ (testhunk 1 ["A"] []:<+ testhunk 3 ["B"] [],+ testhunk 2 ["B"] []:<+ testhunk 1 ["A"] []),++ (testhunk 1 ["A"] ["B"]:<+ testhunk 2 ["B"] ["C"],+ testhunk 2 ["B"] ["C"]:<+ testhunk 1 ["A"] ["B"]),++ (testhunk 1 ["A"] ["B"]:<+ testhunk 3 ["B"] ["C"],+ testhunk 3 ["B"] ["C"]:<+ testhunk 1 ["A"] ["B"]),++ (testhunk 1 ["A"] ["B","C"]:<+ testhunk 2 ["B"] ["C","D"],+ testhunk 3 ["B"] ["C","D"]:<+ testhunk 1 ["A"] ["B","C"])]+ where testhunk l o n = fromPrim $ hunk "test" l (map BC.pack o) (map BC.pack n)++checkCantCommute :: (Patch:< Patch) -> [String]+checkCantCommute (p1:<p2) =+ case commute (p2:>p1) of+ Nothing -> []+ _ -> [show p1 ++ "\n\n" ++ show p2 +++ "\nArgh, these guys shouldn't commute!\n"]+knownCantCommute :: [(Patch:< Patch)]+knownCantCommute = [+ (testhunk 2 ["o"] ["n"]:<+ testhunk 1 [] ["A"]),+ (testhunk 1 [] ["A"]:<+ testhunk 1 ["o"] ["n"]),+ (quickmerge (testhunk 2 ["o"] ["n"]:\/:+ testhunk 2 ["o"] ["v"]):<+ testhunk 1 [] ["a"]),+ (fromPrim (hunk "test" 1 ([BC.pack "a"]) ([BC.pack "b"])):<+ fromPrim (addfile "test"))]+ where testhunk l o n = fromPrim $ hunk "test" l (map BC.pack o) (map BC.pack n)+\end{code}++\chapter{Merge tests}++Here we provide a set of known interesting merges.+\begin{code}+mergeTests :: [String]+mergeTests =+ concatMap checkKnownMergeEquiv knownMergeEquivs+++ concatMap checkKnownMerge knownMerges+checkKnownMerge :: (Patch:\/: Patch, Patch) -> [String]+checkKnownMerge (p1:\/:p2,p1') =+ case merge (p1:\/:p2) of+ _ :/\: p1a ->+ if p1a == p1'+ then []+ else ["Merge gave wrong value!\n"++show p1++show p2+ ++"I expected\n"++show p1'+ ++"but found instead\n"++show p1a]+knownMerges :: [(Patch:\/:Patch,Patch)]+knownMerges = [+ (testhunk 2 [BC.pack "c"] [BC.pack "d",BC.pack "e"]:\/:+ testhunk 1 [BC.pack "x"] [BC.pack "a",BC.pack "b"],+ testhunk 3 [BC.pack "c"] [BC.pack "d",BC.pack "e"]),+ (testhunk 1 [BC.pack "x"] [BC.pack "a",BC.pack "b"]:\/:+ testhunk 2 [BC.pack "c"] [BC.pack "d",BC.pack "e"],+ testhunk 1 [BC.pack "x"] [BC.pack "a",BC.pack "b"]),+ (testhunk 3 [BC.pack "A"] []:\/:+ testhunk 1 [BC.pack "B"] [],+ testhunk 2 [BC.pack "A"] []),+ (fromPrim (rmdir "./test/world"):\/:+ fromPrim (hunk "./world" 3 [BC.pack "A"] []),+ fromPrim (rmdir "./test/world")),++ (join_patches [quickhunk 1 "a" "bc",+ quickhunk 6 "d" "ef"]:\/:+ join_patches [quickhunk 3 "a" "bc",+ quickhunk 8 "d" "ef"],+ join_patches [quickhunk 1 "a" "bc",+ quickhunk 7 "d" "ef"]),++ (testhunk 1 [BC.pack "A"] [BC.pack "B"]:\/:+ testhunk 2 [BC.pack "B"] [BC.pack "C"],+ testhunk 1 [BC.pack "A"] [BC.pack "B"]),++ (testhunk 2 [BC.pack "A"] [BC.pack "B",BC.pack "C"]:\/:+ testhunk 1 [BC.pack "B"] [BC.pack "C",BC.pack "D"],+ testhunk 3 [BC.pack "A"] [BC.pack "B",BC.pack "C"])]+ where testhunk l o n = fromPrim $ hunk "test" l o n+checkKnownMergeEquiv :: (Patch:\/:Patch,Patch) -> [String]+checkKnownMergeEquiv (p1:\/: p2, pe) =+ case quickmerge (p1:\/:p2) of+ p1' -> if checkAPatch $ join_patches [invert p1, p2, p1', invert pe]+ then []+ else ["Oh no, merger isn't equivalent...\n"++show p1++"\n"++show p2+ ++"in other words\n" ++ show (p1 :\/: p2)+ ++"merges as\n" ++ show (merge $ p1 :\/: p2)+ ++"merges to\n" ++ show (quickmerge $ p1 :\/: p2)+ ++"which is equivalent to\n" ++ show (effect p1')+ ++ "should all work out to\n"+ ++ show pe]+knownMergeEquivs :: [(Patch:\/: Patch, Patch)]+knownMergeEquivs = [++ -- The following tests are going to be failed by the+ -- Conflictor code as a cleanup.++ --(addfile "test":\/:+ -- adddir "test",+ -- join_patches [adddir "test",+ -- addfile "test-conflict"]),+ --(move "silly" "test":\/:+ -- adddir "test",+ -- join_patches [adddir "test",+ -- move "silly" "test-conflict"]),+ --(addfile "test":\/:+ -- move "old" "test",+ -- join_patches [addfile "test",+ -- move "old" "test-conflict"]),+ --(move "a" "test":\/:+ -- move "old" "test",+ -- join_patches [move "a" "test",+ -- move "old" "test-conflict"]),+ (fromPrim (hunk "test" 1 [] [BC.pack "A"]):\/:+ fromPrim (hunk "test" 1 [] [BC.pack "B"]),+ fromPrim (hunk "test" 1 [] [BC.pack "A", BC.pack "B"])),+ (fromPrim (hunk "test" 1 [] [BC.pack "a"]):\/:+ fromPrim (hunk "test" 1 [BC.pack "b"] []),+ identity),+ --hunk "test" 1 [] [BC.pack "v v v v v v v",+ -- BC.pack "*************",+ -- BC.pack "a",+ -- BC.pack "b",+ -- BC.pack "^ ^ ^ ^ ^ ^ ^"]),+ (quickhunk 4 "a" "":\/:+ quickhunk 3 "a" "",+ quickhunk 3 "aa" ""),+ (join_patches [quickhunk 1 "a" "bc",+ quickhunk 6 "d" "ef"]:\/:+ join_patches [quickhunk 3 "a" "bc",+ quickhunk 8 "d" "ef"],+ join_patches [quickhunk 3 "a" "bc",+ quickhunk 8 "d" "ef",+ quickhunk 1 "a" "bc",+ quickhunk 7 "d" "ef"]),+ (quickmerge (quickhunk 2 "" "bd":\/:quickhunk 2 "" "a"):\/:+ quickmerge (quickhunk 2 "" "c":\/:quickhunk 2 "" "a"),+ quickhunk 2 "" "abdc")+ ]+\end{code}++It also is useful to verify that it doesn't matter which order we specify+the patches when we merge.++\begin{code}+mergeSwapTests :: [String]+mergeSwapTests =+ concat+ [checkMergeSwap p1 p2 |+ p1<-primitiveTestPatches,+ p2<-primitiveTestPatches,+ checkAPatch $ join_patches [invert p1,p2]+ ]+checkMergeSwap :: Patch -> Patch -> [String]+checkMergeSwap p1 p2 =+ case merge (p2:\/:p1) of+ _ :/\: p2' ->+ case merge (p1:\/:p2) of+ _ :/\: p1' ->+ case commute (p1:>p2') of+ Just (_:>p1'b) ->+ if p1'b /= p1'+ then ["Merge swapping problem with...\np1 "+++ show p1++"merged with\np2 "+++ show p2++"p1' is\np1' "+++ show p1'++"p1'b is\np1'b "+++ show p1'b+ ]+ else []+ Nothing -> ["Merge commuting problem with...\np1 "+++ show p1++"merged with\np2 "+++ show p2++"gives\np2' "+++ show p2'++"which doesn't commute with p1.\n"+ ]+\end{code}++\chapter{Patch test data}++This is where we define the set of patches which we run our tests on. This+should be kept up to date with as many interesting permutations of patch+types as possible.++\begin{code}+testPatches :: [Patch]+testPatchesNamed :: [Named Patch]+testPatchesAddfile :: [Patch]+testPatchesRmfile :: [Patch]+testPatchesHunk :: [Patch]+primitiveTestPatches :: [Patch]+testPatchesBinary :: [Patch]+testPatchesCompositeNocom :: [Patch]+testPatchesComposite :: [Patch]+testPatchesTwoCompositeHunks :: [Patch]+testPatchesCompositeHunks :: [Patch]+testPatchesCompositeFourHunks :: [Patch]+testPatchesMerged :: [Patch]+validPatches :: [Patch]++testPatchesNamed = [unsafePerformIO $+ namepatch "date is" "patch name" "David Roundy" []+ (fromPrim $ addfile "test"),+ unsafePerformIO $+ namepatch "Sat Oct 19 08:31:13 EDT 2002"+ "This is another patch" "David Roundy"+ ["This log file has","two lines in it"]+ (fromPrim $ rmfile "test")]+testPatchesAddfile = map fromPrim+ [addfile "test",adddir "test",addfile "test/test"]+testPatchesRmfile = map invert testPatchesAddfile+testPatchesHunk =+ [fromPrim $ hunk file line old new |+ file <- ["test"],+ line <- [1,2],+ old <- map (map BC.pack) partials,+ new <- map (map BC.pack) partials,+ old /= new+ ]+ where partials = [["A"],["B"],[],["B","B2"]]++primitiveTestPatches = testPatchesAddfile +++ testPatchesRmfile +++ testPatchesHunk +++ [unsafeUnseal.fst.fromJust.readPatch $+ BC.pack "move ./test/test ./hello",+ unsafeUnseal.fst.fromJust.readPatch $+ BC.pack "move ./test ./hello"] +++ testPatchesBinary++testPatchesBinary =+ [fromPrim $ binary "./hello"+ (BC.pack $ "agadshhdhdsa75745457574asdgg" +++ "a326424677373735753246463gadshhdhdsaasdgg" +++ "a326424677373735753246463gadshhdhdsaasdgg" +++ "a326424677373735753246463gadshhdhdsaasdgg")+ (BC.pack $ "adafjttkykrehhtrththrthrthre" +++ "a326424677373735753246463gadshhdhdsaasdgg" +++ "a326424677373735753246463gadshhdhdsaasdgg" +++ "a326424677373735753246463gadshhdhdsaagg"),+ fromPrim $ binary "./hello"+ B.empty+ (BC.pack "adafjttkykrere")]++testPatchesCompositeNocom =+ take 50 [join_patches [p1,p2]|+ p1<-primitiveTestPatches,+ p2<-filter (\p->checkseq [p1,p]) primitiveTestPatches,+ commute (p1:>p2) == Nothing]+ where checkseq ps = checkAPatch $ join_patches ps++testPatchesComposite =+ take 100 [join_patches [p1,p2]|+ p1<-primitiveTestPatches,+ p2<-filter (\p->checkseq [p1,p]) primitiveTestPatches,+ commute (p1:>p2) /= Nothing,+ commute (p1:>p2) /= Just (p2:>p1)]+ where checkseq ps = checkAPatch $ join_patches ps++testPatchesTwoCompositeHunks =+ take 100 [join_patches [p1,p2]|+ p1<-testPatchesHunk,+ p2<-filter (\p->checkseq [p1,p]) testPatchesHunk]+ where checkseq ps = checkAPatch $ join_patches ps++testPatchesCompositeHunks =+ take 100 [join_patches [p1,p2,p3]|+ p1<-testPatchesHunk,+ p2<-filter (\p->checkseq [p1,p]) testPatchesHunk,+ p3<-filter (\p->checkseq [p1,p2,p]) testPatchesHunk]+ where checkseq ps = checkAPatch $ join_patches ps++testPatchesCompositeFourHunks =+ take 100 [join_patches [p1,p2,p3,p4]|+ p1<-testPatchesHunk,+ p2<-filter (\p->checkseq [p1,p]) testPatchesHunk,+ p3<-filter (\p->checkseq [p1,p2,p]) testPatchesHunk,+ p4<-filter (\p->checkseq [p1,p2,p3,p]) testPatchesHunk]+ where checkseq ps = checkAPatch $ join_patches ps++testPatchesMerged =+ take 200+ [joinPatches $ flattenFL p2+>+flattenFL (quickmerge (p1:\/:p2)) |+ p1<-take 10 (drop 15 testPatchesCompositeHunks)++primitiveTestPatches+ ++take 10 (drop 15 testPatchesTwoCompositeHunks)+ ++ take 2 (drop 4 testPatchesCompositeFourHunks),+ p2<-take 10 testPatchesCompositeHunks++primitiveTestPatches+ ++take 10 testPatchesTwoCompositeHunks+ ++take 2 testPatchesCompositeFourHunks,+ checkAPatch $ join_patches [invert p1, p2],+ commute (p2:>p1) /= Just (p1:>p2)+ ]++testPatches = primitiveTestPatches +++ testPatchesComposite +++ testPatchesCompositeNocom +++ testPatchesMerged+\end{code}++\chapter{Check patch test}+Check patch is supposed to verify that a patch is valid.++\begin{code}+validPatches = [(join_patches [quickhunk 4 "a" "b",+ quickhunk 1 "c" "d"]),+ (join_patches [quickhunk 1 "a" "bc",+ quickhunk 1 "b" "d"]),+ (join_patches [quickhunk 1 "a" "b",+ quickhunk 1 "b" "d"])]++testPatches++testCheck :: [String]+testCheck = concatMap tTestCheck validPatches+tTestCheck :: PatchUnitTest Patch+tTestCheck p = if checkAPatch p+ then []+ else ["Failed the check: "++show p++"\n"]++propHexConversion :: String -> Bool+propHexConversion s =+ fromHex2PS (fromPS2Hex $ BC.pack s) == BC.pack s+propConcatPS :: [String] -> Bool+propConcatPS ss = concat ss == BC.unpack (B.concat $ map BC.pack ss)++-- | Groups a set of tests by giving them the same prefix in their description.+-- When this is called as @checkSubcommutes subcoms expl@, the prefix for a+-- test becomes @"Checking " ++ expl ++ " for subcommute "@.+checkSubcommutes :: Testable a => [(String, a)] -> String+ -> [Test]+checkSubcommutes subcoms expl = map check_subcommute subcoms+ where check_subcommute (name, test) =+ let testName = "Checking" ++ expl ++ " for subcommute " ++ name+ in testProperty testName test+\end{code}++\end{document}++
src/Darcs/TheCommands.hs view
@@ -35,7 +35,7 @@ import Darcs.Commands.MarkConflicts ( markconflicts, resolve ) import Darcs.Commands.Move ( move, mv ) import Darcs.Commands.Optimize ( optimize )-import Darcs.Commands.Pull ( pull )+import Darcs.Commands.Pull ( pull, fetch ) import Darcs.Commands.Push ( push ) import Darcs.Commands.Put ( put ) import Darcs.Commands.Record ( record, commit )@@ -84,6 +84,7 @@ HiddenCommand transferMode, GroupName "Copying patches between repositories with working copy update:", CommandData pull,+ CommandData fetch, CommandData obliterate, HiddenCommand unpull, CommandData rollback, CommandData push,
src/Darcs/URL.hs view
@@ -48,34 +48,34 @@ -} module Darcs.URL (- is_file, is_url, is_ssh, is_relative, is_absolute,- is_ssh_nopath+ isFile, isUrl, isSsh, isRelative, isAbsolute,+ isSshNopath ) where #include "impossible.h" -is_relative :: String -> Bool-is_relative (_:':':_) = False-is_relative f@(c:_) = is_file f && c /= '/' && c /= '~'-is_relative "" = bug "Empty filename in is_relative"+isRelative :: String -> Bool+isRelative (_:':':_) = False+isRelative f@(c:_) = isFile f && c /= '/' && c /= '~'+isRelative "" = bug "Empty filename in isRelative" -is_absolute :: String -> Bool-is_absolute "" = bug "is_absolute called with empty filename"-is_absolute f = is_file f && (not $ is_relative f)+isAbsolute :: String -> Bool+isAbsolute "" = bug "isAbsolute called with empty filename"+isAbsolute f = isFile f && (not $ isRelative f) -is_file :: String -> Bool-is_file (_:_:fou) = ':' `notElem` fou-is_file _ = True+isFile :: String -> Bool+isFile (_:_:fou) = ':' `notElem` fou+isFile _ = True -is_url :: String -> Bool-is_url (':':'/':'/':_:_) = True-is_url (_:x) = is_url x-is_url "" = False+isUrl :: String -> Bool+isUrl (':':'/':'/':_:_) = True+isUrl (_:x) = isUrl x+isUrl "" = False -is_ssh :: String -> Bool-is_ssh s = not (is_file s || is_url s)+isSsh :: String -> Bool+isSsh s = not (isFile s || isUrl s) -is_ssh_nopath :: String -> Bool-is_ssh_nopath s = case reverse s of+isSshNopath :: String -> Bool+isSshNopath s = case reverse s of ':':x@(_:_:_) -> ':' `notElem` x _ -> False
src/Darcs/Utils.hs view
@@ -7,22 +7,25 @@ putStrLnError, putDocLnError, withCurrentDirectory, withUMask, askUser, stripCr,- showHexLen, add_to_error_loc,+ showHexLen, addToErrorLoc, maybeGetEnv, firstNotBlank, firstJustM, firstJustIO,- isUnsupportedOperationError, isHardwareFaultError,- get_viewer, edit_file, run_editor,- promptYorn, promptCharFancy,+ getViewer, editFile, runEditor,+ PromptConfig(..), promptYorn, promptChar, environmentHelpEditor, environmentHelpPager,- formatPath+ formatPath,+ isFileReallySymlink, doesDirectoryReallyExist, doesFileReallyExist+ -- * Tree filtering. , filterFilePaths, filterPaths+ -- * Tree lookup.+ , treeHas, treeHasDir, treeHasFile, treeHasAnycase ) where import Prelude hiding ( catch )-import Control.Exception ( bracket, bracket_, catch, Exception(IOException), try )-import GHC.IOBase ( IOException(ioe_location),- IOErrorType(UnsupportedOperation, HardwareFault) )-import System.IO.Error ( isUserError, ioeGetErrorType, ioeGetErrorString )+import Control.Exception.Extensible+ ( bracket, bracket_, catch, try,+ IOException, SomeException, Exception(fromException) )+import System.IO.Error ( annotateIOError, isUserError, ioeGetErrorString ) import Darcs.SignalHandler ( catchNonSignal ) import Numeric ( showHex )@@ -30,49 +33,41 @@ import System.Exit ( exitWith, ExitCode(..) ) import System.Environment ( getEnv ) import System.IO ( hPutStrLn, stderr )-import Data.Char ( toUpper )+import Data.Char ( toUpper, toLower ) import Darcs.RepoPath ( FilePathLike, getCurrentDirectory, setCurrentDirectory, toFilePath ) import Data.Maybe ( listToMaybe, isJust ) import Data.List ( group, sort )-import Control.Monad ( when )-import Exec ( exec_interactive )+import Control.Monad ( when, forM )+import Control.Monad.Error( catchError, MonadError )+import Exec ( execInteractive ) import Printer ( Doc, hPutDocLn ) import Foreign.C.String ( CString, withCString ) import Foreign.C.Error ( throwErrno ) import Foreign.C.Types ( CInt ) -import Progress ( withoutProgress )+import qualified Data.ByteString.Char8 as BSC -import Storage.Hashed.AnchoredPath( AnchoredPath, isPrefix, floatPath )+import System.Posix.Files( getSymbolicLinkStatus, isRegularFile, isDirectory, isSymbolicLink ) +import Progress ( withoutProgress )+ import System.Console.Haskeline ( runInputT, defaultSettings, getInputLine, getInputChar, outputStrLn )-import System.Console.Haskeline.Encoding ( encode ) import qualified Data.ByteString as B ( readFile )-import qualified Data.ByteString.Char8 as B ( unpack ) +import Control.Monad.State.Strict( gets )+import Storage.Hashed.AnchoredPath( AnchoredPath(..), Name(..), isPrefix, floatPath )+import Storage.Hashed.Monad( withDirectory, fileExists, exists, directoryExists+ , virtualTreeMonad, currentDirectory+ , TreeMonad, tree )+import Storage.Hashed.Tree( Tree, listImmediate, findTree )+ showHexLen :: (Integral a) => Int -> a -> String showHexLen n x = let s = showHex x "" in replicate (n - length s) ' ' ++ s -add_to_error_loc :: Exception -> String -> Exception-add_to_error_loc (IOException ioe) s- = IOException $ ioe { ioe_location = s ++ ": " ++ ioe_location ioe }-add_to_error_loc e _ = e--isUnsupportedOperationError :: IOError -> Bool-isUnsupportedOperationError = isUnsupportedOperationErrorType . ioeGetErrorType--isUnsupportedOperationErrorType :: IOErrorType -> Bool-isUnsupportedOperationErrorType UnsupportedOperation = True-isUnsupportedOperationErrorType _ = False--isHardwareFaultError :: IOError -> Bool-isHardwareFaultError = isHardwareFaultErrorType . ioeGetErrorType--isHardwareFaultErrorType :: IOErrorType -> Bool-isHardwareFaultErrorType HardwareFault = True-isHardwareFaultErrorType _ = False+addToErrorLoc :: IOException -> String -> IOException+addToErrorLoc ioe s = annotateIOError ioe s Nothing Nothing catchall :: IO a -> IO a -> IO a a `catchall` b = a `catchNonSignal` (\_ -> b)@@ -100,8 +95,8 @@ clarifyErrors :: IO a -> String -> IO a clarifyErrors a e = a `catch` (\x -> fail $ unlines [prettyException x,e]) -prettyException :: Control.Exception.Exception -> String-prettyException (IOException e) | isUserError e = ioeGetErrorString e+prettyException :: SomeException -> String+prettyException e | Just ioe <- fromException e, isUserError ioe = ioeGetErrorString ioe prettyException e = show e prettyError :: IOError -> String@@ -120,7 +115,7 @@ (Right (ExitFailure 126)) -> b -- command not executable (Right (ExitFailure 127)) -> b -- command not found (Right x) -> return x -- legitimate success/failure- (Left _) -> b -- an exception+ (Left (_ :: SomeException)) -> b -- an exception putStrLnError :: String -> IO () putStrLnError = hPutStrLn stderr@@ -151,13 +146,12 @@ (reset_umask rc) job -askUser :: String -> IO String+-- | Ask the user for a line of input.+askUser :: String -- ^ The prompt to display+ -> IO String -- ^ The string the user entered. askUser prompt = withoutProgress $ runInputT defaultSettings $ getInputLine prompt >>= maybe (error "askUser: unexpected end of input") return- -- Return the input as encoded, 8-bit Chars (same as the- -- non-Haskeline backend).- >>= fmap B.unpack . encode stripCr :: String -> String stripCr "" = ""@@ -188,10 +182,10 @@ nubsort = map head . group . sort -edit_file :: FilePathLike p => p -> IO ExitCode-edit_file ff = do+editFile :: FilePathLike p => p -> IO ExitCode+editFile ff = do old_content <- file_content- ec <- run_editor f+ ec <- runEditor f new_content <- file_content when (new_content == old_content) $ do yorn <- promptYorn "File content did not change. Continue anyway?"@@ -205,19 +199,19 @@ return $ Just content else return Nothing -run_editor :: FilePath -> IO ExitCode-run_editor f = do- ed <- get_editor- exec_interactive ed f- `ortryrunning` exec_interactive "emacs" f- `ortryrunning` exec_interactive "emacs -nw" f- `ortryrunning` exec_interactive "nano" f+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 #ifdef WIN32- `ortryrunning` exec_interactive "edit" f+ `ortryrunning` execInteractive "edit" f #endif -get_editor :: IO String-get_editor = getEnv "DARCS_EDITOR" `catchall`+getEditor :: IO String+getEditor = getEnv "DARCS_EDITOR" `catchall` getEnv "DARCSEDITOR" `catchall` getEnv "VISUAL" `catchall` getEnv "EDITOR" `catchall` return "vi"@@ -231,8 +225,8 @@ "found in your PATH, emacs, emacs -nw, nano and (on Windows) edit are", "each tried in turn."]) -get_viewer :: IO String-get_viewer = getEnv "DARCS_PAGER" `catchall`+getViewer :: IO String+getViewer = getEnv "DARCS_PAGER" `catchall` getEnv "PAGER" `catchall` return "less" environmentHelpPager :: ([String], [String])@@ -241,26 +235,36 @@ "to fit onscreen. Darcs will use the pager specified by $DARCS_PAGER", "or $PAGER. If neither are set, `less' will be used."]) +data PromptConfig = PromptConfig { pPrompt :: String+ , pBasicCharacters :: [Char]+ , pAdvancedCharacters :: [Char] -- ^ only shown on help+ , pDefault :: Maybe Char+ , pHelp :: [Char]+ }+ promptYorn :: [Char] -> IO Char-promptYorn p = promptCharFancy p "yn" Nothing []+promptYorn p = promptChar (PromptConfig p "yn" [] Nothing []) -promptCharFancy :: String -> [Char] -> Maybe Char -> [Char] -> IO Char-promptCharFancy p chs md help_chs = withoutProgress $ runInputT defaultSettings $- loopChar+promptChar :: PromptConfig -> IO Char+promptChar (PromptConfig p basic_chs adv_chs md help_chs) =+ withoutProgress $ runInputT defaultSettings $ loopChar where+ chs = basic_chs ++ adv_chs loopChar = do- let prompt = p ++ " [" ++ setDefault chs ++ "]" ++ helpStr- a <- getInputChar prompt >>= maybe (error "promptCharFancy: unexpected end of input")+ let chars = setDefault (basic_chs ++ (if null adv_chs then "" else "..."))+ prompt = p ++ " [" ++ chars ++ "]" ++ helpStr+ a <- getInputChar prompt >>= maybe (error "promptChar: unexpected end of input") return- case () of + case () of _ | a `elem` chs -> return a- | a == ' ' -> case md of Nothing -> tryAgain + | a == ' ' -> case md of Nothing -> tryAgain Just d -> return d | a `elem` help_chs -> return a | otherwise -> tryAgain helpStr = case help_chs of- [] -> ""- (h:_) -> ", or " ++ (h:" for help: ")+ [] -> ""+ (h:_) | null adv_chs -> ", or " ++ (h:" for help: ")+ | otherwise -> ", or " ++ (h:" for more options: ") tryAgain = do outputStrLn "Invalid response, try again!" loopChar setDefault s = case md of Nothing -> s@@ -279,3 +283,39 @@ filterFilePaths :: [FilePath] -> AnchoredPath -> t -> Bool filterFilePaths = filterPaths . map floatPath +-- Huh?+isFileReallySymlink :: FilePath -> IO Bool+isFileReallySymlink f = do fs <- getSymbolicLinkStatus f+ return (isSymbolicLink fs)++doesFileReallyExist :: FilePath -> IO Bool+doesFileReallyExist f = do fs <- getSymbolicLinkStatus f+ return (isRegularFile fs)++doesDirectoryReallyExist :: FilePath -> IO Bool+doesDirectoryReallyExist f = do fs <- getSymbolicLinkStatus f+ return (isDirectory fs)++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)) =+ do wd <- currentDirectory+ Just tree <- gets (flip findTree wd . tree)+ let subs = [ AnchoredPath [Name n] | (Name n, _) <- listImmediate tree,+ BSC.map toLower n == BSC.map toLower x ]+ or `fmap` forM subs (\path -> do+ file <- fileExists path+ 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 (exists $ floatPath path) tree++treeHasDir :: (MonadError e m, Functor m, Monad m) => Tree m -> FilePath -> m Bool+treeHasDir tree path = fst `fmap` virtualTreeMonad (directoryExists $ floatPath path) tree++treeHasFile :: (MonadError e m, Functor m, Monad m) => Tree m -> FilePath -> m Bool+treeHasFile tree path = fst `fmap` virtualTreeMonad (fileExists $ floatPath path) tree
src/Darcs/Witnesses/Ordered.hs view
@@ -23,24 +23,25 @@ module Darcs.Witnesses.Ordered ( EqCheck(..), isEq, (:>)(..), (:<)(..), (:\/:)(..), (:/\:)(..), (:||:)(..), FL(..), RL(..),Proof(..),-#ifndef GADT_WITNESSES- unsafeUnFL, unsafeFL, unsafeRL, unsafeUnRL,-#endif- lengthFL, mapFL, mapFL_FL, spanFL, foldlFL, allFL,- splitAtFL, bunchFL, foldlRL,+ lengthFL, mapFL, mapFL_FL, spanFL, foldlFL, allFL, anyFL,+ filterFL,+ splitAtFL, splitAtRL, bunchFL, foldlRL, lengthRL, isShorterThanRL, mapRL, mapRL_RL, zipWithFL,- unsafeMap_l2f, filterE, filterFL,+ unsafeMap_l2f, filterE, filterFLFL,+ filterRL, reverseFL, reverseRL, (+>+), (+<+), nullFL, concatFL, concatRL, concatReverseFL, headRL, MyEq, unsafeCompare, (=\/=), (=/\=),- consRLSealed, nullRL,- unsafeCoerceP, unsafeCoerceP2+ consRLSealed, nullRL, toFL,+ (:>>)(..),+ unsafeCoercePStart, unsafeCoercePEnd,+ unsafeCoerceP, unsafeCoerceP2, spanFL_M ) where #include "impossible.h" import GHC.Base (unsafeCoerce#) import Darcs.Witnesses.Show-import Darcs.Witnesses.Sealed ( FlippedSeal(..), flipSeal )+import Darcs.Witnesses.Sealed ( FlippedSeal(..), flipSeal, Sealed(..), FreeLeft, unFreeLeft, Sealed2(..) ) data EqCheck C(a b) where IsEq :: EqCheck C(a a)@@ -70,6 +71,10 @@ data (a1 :\/: a2) C(x y) = FORALL(z) (a1 C(z x)) :\/: (a2 C(z y)) data (a1 :/\: a2) C(x y) = FORALL(z) (a1 C(x z)) :/\: (a2 C(y z)) data (a1 :||: a2) C(x y) = (a1 C(x y)) :||: (a2 C(x y))++data (a1 :>> a2) C(y) = FORALL(z) (a1 C(z)) :>> (a2 C(z y))+infixr 1 :>>+ class MyEq p where -- Minimal definition defines any one of unsafeCompare, =\/= and =/\=. unsafeCompare :: p C(a b) -> p C(c d) -> Bool@@ -86,21 +91,39 @@ unsafeCoerceP :: a C(x y) -> a C(b c) unsafeCoerceP = unsafeCoerce# +unsafeCoercePStart :: a C(x1 y) -> a C(x2 y)+unsafeCoercePStart = unsafeCoerce#++unsafeCoercePEnd :: a C(x y1) -> a C(x y2)+unsafeCoercePEnd = unsafeCoerce#+ unsafeCoerceP2 :: t C(w x y z) -> t C(a b c d) unsafeCoerceP2 = unsafeCoerce# instance (Show2 a, Show2 b) => Show ( (a :> b) C(x y) ) where showsPrec d (x :> y) = showOp2 1 ":>" d x y +instance (MyEq a, MyEq b) => MyEq (a :> b) where+ (a1 :> b1) =\/= (a2 :> b2) | IsEq <- a1 =\/= a2 = b1 =\/= b2+ | otherwise = NotEq++instance (MyEq a, MyEq b) => MyEq (a :< b) where+ (a1 :< b1) =\/= (a2 :< b2) | IsEq <- b1 =\/= b2 = a1 =\/= a2+ | otherwise = NotEq+ instance (Show2 a, Show2 b) => Show2 (a :> b) where- showsPrec2 = showsPrec+ showDict2 = ShowDictClass instance (Show2 a, Show2 b) => Show ( (a :\/: b) C(x y) ) where showsPrec d (x :\/: y) = showOp2 9 ":\\/:" d x y instance (Show2 a, Show2 b) => Show2 (a :\/: b) where- showsPrec2 = showsPrec+ showDict2 = ShowDictClass +instance MyEq a => Eq (Sealed (a C(x))) where+ Sealed x == Sealed y | IsEq <- x =\/= y = True+ | otherwise = False+ infixr 5 :>:, :<:, +>+, +<+ -- forward list@@ -115,7 +138,7 @@ where prec = 5 instance Show2 a => Show2 (FL a) where- showsPrec2 = showsPrec+ showDict2 = ShowDictClass -- reverse list data RL a C(x z) where@@ -130,11 +153,16 @@ nullRL NilRL = True nullRL _ = False -filterFL :: (FORALL(x y) p C(x y) -> EqCheck C(x y)) -> FL p C(w z) -> FL p C(w z)-filterFL _ NilFL = NilFL-filterFL f (x:>:xs) | IsEq <- f x = filterFL f xs- | otherwise = x :>: filterFL f xs+filterFLFL :: (FORALL(x y) p C(x y) -> EqCheck C(x y)) -> FL p C(w z) -> FL p C(w z)+filterFLFL _ NilFL = NilFL+filterFLFL f (x:>:xs) | IsEq <- f x = filterFLFL f xs+ | otherwise = x :>: filterFLFL f xs +filterRL :: (FORALL(x y) p C(x y) -> Bool) -> RL p C(x y) -> [Sealed2 p]+filterRL _ NilRL = []+filterRL f (x :<: xs) | f x = Sealed2 x : (filterRL f xs)+ | otherwise = filterRL f xs+ filterE :: (a -> EqCheck C(x y)) -> [a] -> [Proof a C(x y)] filterE _ [] = [] filterE p (x:xs)@@ -174,12 +202,31 @@ ys :> zs -> (x:>:ys) :> zs spanFL _ xs = NilFL :> xs +spanFL_M :: forall a m C(x z). Monad m =>+ (FORALL(w y) a C(w y) -> m Bool) -> FL a C(x z)+ -> m ((FL a :> FL a) C(x z))+spanFL_M f (x:>:xs) =+ do+ continue <- f x+ if continue+ then do (ys :> zs) <- spanFL_M f xs+ return $ (x :>: ys) :> zs+ else return $ NilFL :> (x :>: xs)++spanFL_M _ (NilFL) = return $ NilFL :> NilFL+ splitAtFL :: Int -> FL a C(x z) -> (FL a :> FL a) C(x z) splitAtFL 0 xs = NilFL :> xs splitAtFL _ NilFL = NilFL :> NilFL splitAtFL n (x:>:xs) = case splitAtFL (n-1) xs of (xs':>xs'') -> (x:>:xs' :> xs'') +splitAtRL :: Int -> RL a C(x z) -> (RL a :< RL a) C(x z)+splitAtRL 0 xs = NilRL :< xs+splitAtRL _ NilRL = NilRL :< NilRL+splitAtRL n (x:<:xs) = case splitAtRL (n-1) xs of+ (xs':<xs'') -> (x:<:xs' :< xs'')+ -- 'bunchFL n' groups patches into batches of n, except that it always puts -- the first patch in its own group, this being a recognition that the -- first patch is often *very* large.@@ -196,6 +243,9 @@ allFL :: (FORALL(x y) a C(x y) -> Bool) -> FL a C(w z) -> Bool allFL f xs = and $ mapFL f xs +anyFL :: (FORALL(x y) a C(x y) -> Bool) -> FL a C(w z) -> Bool+anyFL f xs = or $ mapFL f xs+ foldlFL :: (FORALL(w y) a -> b C(w y) -> a) -> a -> FL b C(x z) -> a foldlFL _ x NilFL = x foldlFL f x (y:>:ys) = foldlFL f (f x y) ys@@ -222,6 +272,12 @@ mapFL _ NilFL = [] mapFL f (a :>: b) = f a : mapFL f b +filterFL :: (FORALL(x y) a C(x y) -> Bool) -> FL a C(x y) -> [Sealed2 a]+filterFL _ NilFL = []+filterFL f (a :>: b) = if f a+ then (Sealed2 a):(filterFL f b)+ else filterFL f b+ mapRL :: (FORALL(w z) a C(w z) -> b) -> RL a C(x y) -> [b] mapRL _ NilRL = [] mapRL f (a :<: b) = f a : mapRL f b@@ -257,21 +313,7 @@ consRLSealed :: a C(y z) -> FlippedSeal (RL a) C(y) -> FlippedSeal (RL a) C(z) consRLSealed a (FlippedSeal as) = flipSeal $ a :<: as -#ifndef GADT_WITNESSES--- These are useful for interfacing with modules which do not yet use type witnesses-unsafeUnFL :: FL a -> [a]-unsafeUnFL NilFL = []-unsafeUnFL (a:>:as) = a : unsafeUnFL as--unsafeUnRL :: RL a -> [a]-unsafeUnRL NilRL = []-unsafeUnRL (a:<:as) = a : unsafeUnRL as--unsafeFL :: [a] -> FL a-unsafeFL [] = NilFL-unsafeFL (a:as) = a :>: unsafeFL as+toFL :: [FreeLeft a] -> Sealed (FL a C(x))+toFL [] = Sealed NilFL+toFL (x:xs) = case unFreeLeft x of Sealed y -> case toFL xs of Sealed ys -> Sealed (y :>: ys) -unsafeRL :: [a] -> RL a-unsafeRL [] = NilRL-unsafeRL (a:as) = a :<: unsafeRL as-#endif
src/Darcs/Witnesses/Sealed.hs view
@@ -1,4 +1,4 @@--- Copyright (C) 2007 David Roundy+-- Copyright (C) 2007 David Roundy, 2009 Ganesh Sittampalam -- -- 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@@ -22,12 +22,11 @@ #include "gadts.h" module Darcs.Witnesses.Sealed ( Sealed(..), seal, unseal, mapSeal,-#ifndef GADT_WITNESSES- unsafeUnseal, unsafeUnflippedseal, unsafeUnseal2,-#endif+ unsafeUnseal, unsafeUnsealFlipped, unsafeUnseal2, Sealed2(..), seal2, unseal2, mapSeal2, FlippedSeal(..), flipSeal, unsealFlipped, mapFlipped,- unsealM, liftSM+ unsealM, liftSM,+ Gap(..), FreeLeft, unFreeLeft, FreeRight, unFreeRight ) where import GHC.Base ( unsafeCoerce# )@@ -51,22 +50,17 @@ flipSeal :: a C(x y) -> FlippedSeal a C(y) flipSeal = FlippedSeal -#ifndef GADT_WITNESSES-unsafeUnseal :: Sealed a -> a-unsafeUnseal (Sealed a) = a--unsafeUnflippedseal :: FlippedSeal a -> a-unsafeUnflippedseal (FlippedSeal a) = a+unsafeUnseal :: Sealed a -> a C(x)+unsafeUnseal (Sealed a) = unsafeCoerce# a -unsafeUnseal2 :: Sealed2 a -> a-unsafeUnseal2 (Sealed2 a) = a-#endif+unsafeUnsealFlipped :: FlippedSeal a C(y) -> a C(x y)+unsafeUnsealFlipped (FlippedSeal a) = unsafeCoerce# a -seriouslyUnsafeUnseal :: Sealed a -> a C(())-seriouslyUnsafeUnseal (Sealed a) = unsafeCoerce# a+unsafeUnseal2 :: Sealed2 a -> a C(x y)+unsafeUnseal2 (Sealed2 a) = unsafeCoerce# a unseal :: (FORALL(x) a C(x ) -> b) -> Sealed a -> b-unseal f x = f (seriouslyUnsafeUnseal x)+unseal f x = f (unsafeUnseal x) -- laziness property: -- unseal (const True) undefined == True@@ -85,11 +79,8 @@ mapFlipped :: (FORALL(x) a C(x y) -> b C(x z)) -> FlippedSeal a C(y) -> FlippedSeal b C(z) mapFlipped f (FlippedSeal x) = FlippedSeal (f x) -seriouslyUnsafeUnseal2 :: Sealed2 a -> a C(() ())-seriouslyUnsafeUnseal2 (Sealed2 a) = unsafeCoerce# a- unseal2 :: (FORALL(x y) a C(x y ) -> b) -> Sealed2 a -> b-unseal2 f a = f (seriouslyUnsafeUnseal2 a)+unseal2 f a = f (unsafeUnseal2 a) mapSeal2 :: (FORALL(x y) a C(x y ) -> b C(x y )) -> Sealed2 a -> Sealed2 b mapSeal2 f = unseal2 (seal2 . f)@@ -98,6 +89,61 @@ unsealFlipped f (FlippedSeal a) = f a instance Show1 a => Show (Sealed a) where- showsPrec d (Sealed x) = showParen (d > app_prec) $ showString "Sealed " . showsPrec1 (app_prec + 1) x+ showsPrec d (Sealed x) = showParen (d > appPrec) $ showString "Sealed " . showsPrec1 (appPrec + 1) x instance Show2 a => Show (Sealed2 a) where- showsPrec d (Sealed2 x) = showParen (d > app_prec) $ showString "Sealed2 " . showsPrec2 (app_prec + 1) x+ showsPrec d (Sealed2 x) = showParen (d > appPrec) $ showString "Sealed2 " . showsPrec2 (appPrec + 1) x++-- |'Poly' is similar to 'Sealed', but the type argument is+-- universally quantified instead of being existentially quantified.+newtype Poly a = Poly { unPoly :: FORALL(x) a C(x) }++-- |'Stepped' is a type level composition operator.+-- For example, 'Stepped Sealed p' is equivalent to 'lambda x . Sealed (p x)'+newtype Stepped f a C(x) = Stepped { unStepped :: f (a C(x)) }++-- |'FreeLeft p' is '\forall x . \exists y . p x y'+-- In other words the caller is free to specify the left witness,+-- and then the right witness is an existential.+-- Note that the order of the type constructors is important for ensuring+-- that 'y' is dependent on the 'x' that is supplied.+-- This is why 'Stepped' is needed, rather than writing the more obvious+-- 'Sealed (Poly p)' which would notionally have the same quantification+-- of the type witnesses.+newtype FreeLeft p = FLInternal (Poly (Stepped Sealed p))++-- |'FreeLeft p' is '\forall y . \exists x . p x y'+-- In other words the caller is free to specify the right witness,+-- and then the left witness is an existential.+-- Note that the order of the type constructors is important for ensuring+-- that 'x' is dependent on the 'y' that is supplied.+newtype FreeRight p = FRInternal (Poly (FlippedSeal p))++-- |Unwrap a 'FreeLeft' value+unFreeLeft :: FreeLeft p -> Sealed (p C(x))+unFreeLeft (FLInternal x) = unStepped (unPoly x)++-- |Unwrap a 'FreeRight' value+unFreeRight :: FreeRight p -> FlippedSeal p C(x)+unFreeRight (FRInternal x) = unPoly x++-- |'Gap' abstracts over 'FreeLeft' and 'FreeRight' for code constructing these values+class Gap w where+ -- |An empty 'Gap', e.g. 'NilFL' or 'NilRL'+ emptyGap :: (FORALL(x) p C(x x)) -> w p+ -- |A 'Gap' constructed from a completely polymorphic value, for example the constructors+ -- for primitive patches+ freeGap :: (FORALL(x y) p C(x y)) -> w p+ -- |Compose two 'Gap' values together, e.g. 'joinGap (+>+)' or 'joinGap (:>:)'+ joinGap :: (FORALL(x y z) p C(x y) -> q C(y z) -> r C(x z)) -> w p -> w q -> w r++instance Gap FreeLeft where+ emptyGap e = FLInternal (Poly (Stepped (Sealed e)))+ freeGap e = FLInternal (Poly (Stepped (Sealed e)))+ joinGap op (FLInternal p) (FLInternal q)+ = FLInternal (Poly (case unPoly p of Stepped (Sealed p') -> case unPoly q of Stepped (Sealed q') -> Stepped (Sealed (p' `op` q'))))++instance Gap FreeRight where+ emptyGap e = FRInternal (Poly (FlippedSeal e))+ freeGap e = FRInternal (Poly (FlippedSeal e))+ joinGap op (FRInternal p) (FRInternal q)+ = FRInternal (Poly (case unPoly q of FlippedSeal q' -> case unPoly p of FlippedSeal p' -> FlippedSeal (p' `op` q')))
src/Darcs/Witnesses/Show.hs view
@@ -1,25 +1,48 @@ {-# OPTIONS_GHC -cpp -fglasgow-exts #-} {-# LANGUAGE CPP #-} -module Darcs.Witnesses.Show(Show1(..), Show2(..), showOp2, app_prec) where+module Darcs.Witnesses.Show(ShowDict(..), showD, showListD, showsPrecD,+ Show1(..), Show2(..), show1, showsPrec1, show2, showsPrec2, showOp2, appPrec) where #include "gadts.h" +data ShowDict a where+ ShowDictClass :: Show a => ShowDict a+ ShowDictRecord :: (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> ShowDict a++showsPrecD :: ShowDict a -> Int -> a -> ShowS+showsPrecD ShowDictClass = showsPrec+showsPrecD (ShowDictRecord showsPrecR _ _) = showsPrecR++showD :: ShowDict a -> a -> String+showD ShowDictClass = show+showD (ShowDictRecord _ showR _) = showR++showListD :: ShowDict a -> [a] -> ShowS+showListD ShowDictClass = showList+showListD (ShowDictRecord _ _ showListR) = showListR+ class Show1 a where- show1 :: a C(x) -> String- show1 x = showsPrec1 0 x ""- showsPrec1 :: Int -> a C(x) -> ShowS- showsPrec1 _ x s = show1 x ++ s+ showDict1 :: ShowDict (a C(x)) +showsPrec1 :: Show1 a => Int -> a C(x) -> ShowS+showsPrec1 = showsPrecD showDict1++show1 :: Show1 a => a C(x) -> String+show1 = showD showDict1+ class Show2 a where- show2 :: a C(x y) -> String- show2 x = showsPrec2 0 x ""- showsPrec2 :: Int -> a C(x y) -> ShowS- showsPrec2 _ x s = show2 x ++ s+ showDict2 :: ShowDict (a C(x y)) +showsPrec2 :: Show2 a => Int -> a C(x y) -> ShowS+showsPrec2 = showsPrecD showDict2++show2 :: Show2 a => a C(x y) -> String+show2 = showD showDict2+ showOp2 :: (Show2 a, Show2 b) => Int -> String -> Int -> a C(w x) -> b C(y z) -> String -> String showOp2 prec opstr d x y = showParen (d > prec) $ showsPrec2 (prec + 1) x . showString opstr . showsPrec2 (prec + 1) y -app_prec :: Int-app_prec = 10+appPrec :: Int+appPrec = 10
+ src/Darcs/Witnesses/WZipper.hs view
@@ -0,0 +1,77 @@+-- Copyright (C) 2009 Florent Becker+--+-- 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.++{-# OPTIONS_GHC -cpp -fglasgow-exts #-}+{-# LANGUAGE CPP #-}++#include "gadts.h"+module Darcs.Witnesses.WZipper ( FZipper(..), focus, leftmost, left+ , rightmost, right, jokers, clowns+ , flToZipper, lengthFZ, nullFZ+ , toEnd+ )+where+import Darcs.Witnesses.Ordered ( FL(..), RL(..), nullFL, nullRL+ , lengthFL, lengthRL, (+<+)+ , reverseFL+ )+import Darcs.Witnesses.Sealed(Sealed2(..), Sealed(..), FlippedSeal(..))++-- forward zipper+data FZipper a C(x z) where+ FZipper :: RL a C(x y) -> FL a C(y z) -> FZipper a C(x z)++-- Constructors+flToZipper :: FL a C(x y) -> FZipper a C(x y)+flToZipper l = FZipper NilRL l++--destructors+nullFZ :: FZipper a C(x y) -> Bool+nullFZ (FZipper l r) = nullRL l && nullFL r++lengthFZ :: FZipper a C(x y) -> Int+lengthFZ (FZipper l r) = lengthRL l + lengthFL r++focus :: FZipper a C(x y) -> Maybe (Sealed2 a)+focus (FZipper _ (x :>: _)) = Just $ Sealed2 x+focus _ = Nothing++clowns :: FZipper a C(x y) -> Sealed ((RL a) C(x))+clowns (FZipper l _) = Sealed l++jokers :: FZipper a C(x y) -> FlippedSeal (FL a) C(y)+jokers (FZipper _ r) = FlippedSeal r++rightmost :: FZipper p C(x y) -> Bool+rightmost (FZipper _ NilFL) = True+rightmost _ = False++right :: FZipper p C(x y) -> FZipper p C(x y)+right (FZipper l (b:>:r)) = FZipper (b :<: l) r+right x@(FZipper _ NilFL) = x++leftmost :: FZipper p C(x y) -> Bool+leftmost (FZipper NilRL _) = True+leftmost _ = False++left :: FZipper p C(x y) -> FZipper p C(x y)+left (FZipper (b :<: l) r) = FZipper l (b :>: r)+left x@(FZipper NilRL _) = x++toEnd :: FZipper p C(x y) -> FZipper p C(x y)+toEnd (FZipper l r) = FZipper (reverseFL r +<+ l) NilFL+
src/DateMatcher.hs view
@@ -23,10 +23,12 @@ -- for debugging only , DateMatcher(..), getMatchers ) where -import Control.Exception ( catchJust, userErrors )+import Prelude hiding ( catch )+import Control.Exception.Extensible ( catch, throw )+import System.IO.Error ( isUserError, ioeGetErrorString ) import Data.Maybe ( isJust ) import System.Time-import IsoDate ( parseDate, englishDateTime, englishInterval, englishLast, iso8601_interval,+import IsoDate ( parseDate, englishDateTime, englishInterval, englishLast, iso8601Interval, resetCalendar, subtractFromMCal, getLocalTz, MCalendarTime(..), toMCalendarTime, unsafeToCalendarTime, unsetTime,@@ -107,7 +109,7 @@ -- | 'parseDateMatcher' @s@ return the first matcher in -- 'getMatchers' that can parse 's' parseDateMatcher :: String -> IO (CalendarTime -> Bool)-parseDateMatcher d = +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.@@ -119,7 +121,8 @@ then error "Can't handle dates that far back!" else error e where- catchUserError = catchJust userErrors+ catchUserError comp handler+ = catch comp (\e -> if isUserError e then handler (ioeGetErrorString e) else throw e) -- | 'getMatchers' @d@ returns the list of matchers that will be -- applied on @d@. If you wish to extend the date parsing code,@@ -144,7 +147,7 @@ (parseDateWith $ englishInterval rightNow) (uncurry cDateRange) , DM "ISO 8601 interval"- (parseDateWith $ iso8601_interval tzNow)+ (parseDateWith $ iso8601Interval tzNow) matchIsoInterval , DM "CVS, ISO 8601, or old style date" (parseDate tzNow d)
src/English.hs view
@@ -74,3 +74,7 @@ intersperseLast _ _ [clause] = clause intersperseLast sep sepLast clauses = concat (intersperse sep $ init clauses) ++ sepLast ++ last clauses++presentParticiple :: String -> String+presentParticiple v | last v == 'e' = init v ++ "ing"+ | otherwise = v ++ "ing"
src/Exec.hs view
@@ -20,21 +20,21 @@ {-# LANGUAGE CPP, ForeignFunctionInterface #-} -- , DeriveDataTypeable #-} -module Exec ( exec, exec_interactive,+module Exec ( exec, execInteractive, withoutNonBlock, Redirects, Redirect(..), ExecException(..) ) where -import Data.Typeable ( Typeable )+import Data.Typeable ( Typeable, cast ) #ifndef WIN32-import Control.Exception ( bracket )+import Control.Exception.Extensible ( bracket ) import System.Posix.Env ( setEnv, getEnv, unsetEnv ) import System.Posix.IO ( queryFdOption, setFdOption, FdOption(..), stdInput ) import System.IO ( stdin ) #else-import Control.Exception ( catchJust, Exception(IOException) )+import Control.Exception.Extensible ( catchJust, IOException ) import Data.List ( isInfixOf ) #endif @@ -44,7 +44,7 @@ import System.Process ( runProcess, terminateProcess, waitForProcess ) import GHC.Handle ( hDuplicate ) -- urgh. hDuplicate isn't available from a standard place.-import Control.Exception ( bracketOnError )+import Control.Exception.Extensible ( bracketOnError, Exception(..), SomeException(..) ) import Darcs.Global ( whenDebugMode ) import Progress ( withoutProgress )@@ -81,12 +81,15 @@ data ExecException = ExecException String [String] Redirects String deriving (Typeable,Show) +instance Exception ExecException where+ toException e = SomeException e+ fromException (SomeException e) = cast e -_dev_null :: FilePath+_devNull :: FilePath #ifdef WIN32-_dev_null = "NUL"+_devNull = "NUL" #else-_dev_null = "/dev/null"+_devNull = "/dev/null" #endif {-@@ -107,14 +110,14 @@ (waitForProcess) where redirect AsIs _ = return Nothing- redirect Null mode = Just `fmap` openBinaryFile _dev_null mode+ 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 redirect Stdout _ = Just `fmap` hDuplicate stdout -- hDuplicate stdout rather than passing stdout itself, -- because runProcess closes the Handles we pass it. -exec_interactive :: String -> String -> IO ExitCode+execInteractive :: String -> String -> IO ExitCode #ifndef WIN32 {-@@ -123,7 +126,7 @@ the argument in any way, so we set an environment variable and call cmd "$DARCS_ARGUMENT" -}-exec_interactive cmd arg = withoutProgress $ do+execInteractive cmd arg = withoutProgress $ do let var = "DARCS_ARGUMENT" stdin `seq` return () withoutNonBlock $ bracket@@ -138,7 +141,7 @@ #else -exec_interactive cmd arg = withoutProgress $ do+execInteractive cmd arg = withoutProgress $ do system $ cmd ++ " " ++ arg #endif @@ -172,8 +175,8 @@ #ifdef WIN32 withExit127 a = catchJust notFoundError a (const $ return $ ExitFailure 127) -notFoundError :: Exception -> Maybe ()-notFoundError (IOException e) | "runProcess: does not exist" `isInfixOf` show e = Just ()+notFoundError :: IOException -> Maybe ()+notFoundError e | "runProcess: does not exist" `isInfixOf` show e = Just () notFoundError _ = Nothing #else withExit127 = id
src/HTTP.hs view
@@ -1,9 +1,10 @@ {-# OPTIONS_GHC -cpp #-} {-# LANGUAGE CPP #-} -module HTTP( fetchUrl, postUrl, request_url, wait_next_url ) where+module HTTP( fetchUrl, postUrl, requestUrl, waitNextUrl ) where import Darcs.Global ( debugFail )+import Version ( version ) #ifdef HAVE_HTTP import Control.Monad ( when )@@ -25,13 +26,13 @@ -> String -- ^ mime type -> IO () -- ^ result -request_url :: String -> FilePath -> a -> IO String-wait_next_url :: IO (String, String)+requestUrl :: String -> FilePath -> a -> IO String+waitNextUrl :: IO (String, String) #ifdef HAVE_HTTP headers :: [Header]-headers = [Header HdrUserAgent $ "darcs-HTTP/" ++ PACKAGE_VERSION]+headers = [Header HdrUserAgent $ "darcs-HTTP/" ++ version] fetchUrl url = case parseURI url of Nothing -> fail $ "Invalid URI: " ++ url@@ -75,14 +76,14 @@ requestedUrl :: IORef (String, FilePath) requestedUrl = unsafePerformIO $ newIORef ("", "") -request_url u f _ = do+requestUrl u f _ = do (u', _) <- readIORef requestedUrl if null u' then do writeIORef requestedUrl (u, f) return "" else return "URL already requested" -wait_next_url = do+waitNextUrl = do (u, f) <- readIORef requestedUrl if null u then return ("", "No URL requested")@@ -101,7 +102,7 @@ fetchUrl _ = debugFail "Network.HTTP does not exist" postUrl _ _ _ = debugFail "Cannot use http POST because darcs was not compiled with Network.HTTP." -request_url _ _ _ = debugFail "Network.HTTP does not exist"-wait_next_url = debugFail "Network.HTTP does not exist"+requestUrl _ _ _ = debugFail "Network.HTTP does not exist"+waitNextUrl = debugFail "Network.HTTP does not exist" #endif
src/IsoDate.hs view
@@ -20,7 +20,7 @@ module IsoDate ( getIsoDateTime, readLocalDate, readUTCDate, parseDate, getLocalTz, englishDateTime, englishInterval, englishLast,- iso8601_interval, iso8601_duration,+ iso8601Interval, iso8601Duration, cleanLocalDate, resetCalendar, MCalendarTime(..), subtractFromMCal, addToMCal, toMCalendarTime, unsafeToCalendarTime,@@ -71,7 +71,7 @@ -- | Parse a date string, assuming a default timezone if -- the date string does not specify one. The date formats--- understood are those of 'showIsoDateTime' and 'date_time'+-- understood are those of 'showIsoDateTime' and 'dateTime' parseDate :: Int -> String -> Either ParseError MCalendarTime parseDate tz d = if length d >= 14 && B.all isDigit bd@@ -84,7 +84,7 @@ (readI $ B.take 2 $ B.drop 12 bd) -- Second 0 Sunday 0 -- Picosecond, weekday and day of year unknown "GMT" 0 False- else let dt = do { x <- date_time tz; eof; return x }+ else let dt = do { x <- dateTime tz; eof; return x } in parse dt "" d where bd = B.pack (take 14 d) readI s = fst $ fromMaybe (error "parseDate: invalid date") (B.readInt s)@@ -138,54 +138,54 @@ -- | Try each of these date parsers in the following order ----- (1) 'cvs_date_time'+-- (1) 'cvsDateTime' ----- (2) 'iso8601_date_time'+-- (2) 'iso8601DateTime' ----- (3) 'old_date_time-date_time :: Int -> CharParser a MCalendarTime-date_time tz =- choice [try $ toMCalendarTime `fmap` cvs_date_time tz,- try $ iso8601_date_time tz,- toMCalendarTime `fmap` old_date_time]+-- (3) 'oldDateTime+dateTime :: Int -> CharParser a MCalendarTime+dateTime tz =+ choice [try $ toMCalendarTime `fmap` cvsDateTime tz,+ try $ iso8601DateTime tz,+ toMCalendarTime `fmap` oldDateTime] -- | CVS-style date/times, e.g. -- 2007/08/25 14:25:39 GMT -- Note that time-zones are optional here.-cvs_date_time :: Int -> CharParser a CalendarTime-cvs_date_time tz =+cvsDateTime :: Int -> CharParser a CalendarTime+cvsDateTime tz = do y <- year char '/'- mon <- month_num + mon <- monthNum char '/' d <- day- my_spaces+ mySpaces h <- hour char ':' m <- minute char ':' s <- second- z <- option tz $ my_spaces >> zone+ z <- option tz $ mySpaces >> zone return (CalendarTime y mon d h m s 0 Monday 0 "" z False) -- | \"Old\"-style dates, e.g. -- Tue Jan 3 14:08:07 EST 1999 -- darcs-doc: Question (what does the "old" stand for really?)-old_date_time :: CharParser a CalendarTime-old_date_time = do wd <- day_name- my_spaces- mon <- month_name- my_spaces+oldDateTime :: CharParser a CalendarTime+oldDateTime = do wd <- dayName+ mySpaces+ mon <- monthName+ mySpaces d <- day- my_spaces+ mySpaces h <- hour char ':' m <- minute char ':' s <- second- my_spaces+ mySpaces z <- zone- my_spaces+ mySpaces y <- year return (CalendarTime y mon d h m s 0 wd 0 "" z False) @@ -195,7 +195,7 @@ -- -- * years > 9999 ----- * truncated representations with implied century (89 for 1989) +-- * truncated representations with implied century (89 for 1989) -- -- I have not implemented: --@@ -209,17 +209,17 @@ -- -- * the difference between 24h and 0h ----- * allows stuff like 2005-1212; either you use the hyphen all the way +-- * allows stuff like 2005-1212; either you use the hyphen all the way -- (2005-12-12) or you don't use it at all (20051212), but you don't use -- it halfway, likewise with time -- -- * No bounds checking whatsoever on intervals! -- (next action: read iso doc to see if bounds-checking required?) -}-iso8601_date_time :: Int -> CharParser a MCalendarTime-iso8601_date_time localTz = try $ - do d <- iso8601_date- t <- option id $ try $ do optional $ oneOf " T" - iso8601_time +iso8601DateTime :: Int -> CharParser a MCalendarTime+iso8601DateTime localTz = try $+ do d <- iso8601Date+ t <- option id $ try $ do optional $ oneOf " T"+ iso8601Time return $ t $ d { mctTZ = Just localTz } -- | Three types of ISO 8601 date:@@ -229,14 +229,14 @@ -- * week+day in year, e.g., 1997-W32-4 -- -- * day in year, e.g, 1997-273-iso8601_date :: CharParser a MCalendarTime-iso8601_date = +iso8601Date :: CharParser a MCalendarTime+iso8601Date = do d <- calendar_date <|> week_date <|> ordinal_date return $ foldr ($) nullMCalendar d- where + where calendar_date = -- yyyy-mm-dd try $ do d <- optchain year_ [ (dash, month_), (dash, day_) ]- -- allow other variants to be parsed correctly + -- allow other variants to be parsed correctly notFollowedBy (digit <|> char 'W') return d week_date = --yyyy-Www-d@@ -244,8 +244,8 @@ optional dash char 'W' -- offset human 'week 1' -> computer 'week 0'- w' <- (\x -> x-1) `liftM` two_digits- mwd <- option Nothing $ do { optional dash; Just `fmap` n_digits 1 }+ w' <- (\x -> x-1) `liftM` twoDigits+ mwd <- option Nothing $ do { optional dash; Just `fmap` nDigits 1 } let y = resetCalendar . unsafeToCalendarTime . yfn $ nullMCalendar { mctDay = Just 1 } firstDay = ctWDay y -- things that make this complicated@@ -263,33 +263,33 @@ ordinal_date = -- yyyy-ddd try $ optchain year_ [ (dash, yearDay_) ] --- year_ = try $ do y <- four_digits <?> "year (0000-9999)"+ year_ = try $ do y <- fourDigits <?> "year (0000-9999)" return $ \c -> c { mctYear = Just y }- month_ = try $ do m <- two_digits <?> "month (1 to 12)"+ month_ = try $ do m <- twoDigits <?> "month (1 to 12)" return $ \c -> c { mctMonth = Just $ intToMonth m }- day_ = try $ do d <- two_digits <?> "day in month (1 to 31)"+ day_ = try $ do d <- twoDigits <?> "day in month (1 to 31)" return $ \c -> c { mctDay = Just d }- yearDay_ = try $ do d <- n_digits 3 <?> "day in year (001 to 366)"+ yearDay_ = try $ do d <- nDigits 3 <?> "day in year (001 to 366)" return $ \c -> c { mctDay = Just d , mctYDay = Just (d - 1) } dash = char '-' -- | Note that this returns a function which sets the time on--- another calendar (see 'iso8601_date_time' for a list of+-- another calendar (see 'iso8601DateTime' for a list of -- flaws-iso8601_time :: CharParser a (MCalendarTime -> MCalendarTime)-iso8601_time = try $+iso8601Time :: CharParser a (MCalendarTime -> MCalendarTime)+iso8601Time = try $ do ts <- optchain hour_ [ (colon , min_) , (colon , sec_)- , (oneOf ",.", pico_) ] + , (oneOf ",.", pico_) ] z <- option id $ choice [ zulu , offset ] return $ foldr (.) id (z:ts)- where - hour_ = do h <- two_digits+ where+ hour_ = do h <- twoDigits return $ \c -> c { mctHour = Just h }- min_ = do m <- two_digits+ min_ = do m <- twoDigits return $ \c -> c { mctMin = Just m }- sec_ = do s <- two_digits+ sec_ = do s <- twoDigits return $ \c -> c { mctSec = Just s } pico_ = do digs <- many digit let picoExp = 12@@ -301,8 +301,8 @@ zulu = do { char 'Z'; return (\c -> c { mctTZ = Just 0 }) } offset = do sign <- choice [ do { char '+' >> return 1 } , do { char '-' >> return (-1) } ]- h <- two_digits- m <- option 0 $ do { optional colon; two_digits }+ h <- twoDigits+ m <- option 0 $ do { optional colon; twoDigits } return $ \c -> c { mctTZ = Just $ sign * 60 * ((h*60)+m) } colon = char ':' @@ -314,24 +314,24 @@ -- -- * P2Y11MT16H30M/2012-08-17T16:30 ----- See 'iso8601_duration'-iso8601_interval :: Int -> CharParser a (Either TimeDiff (MCalendarTime, MCalendarTime))-iso8601_interval localTz = leftDur <|> rightDur where- leftDur = - do dur <- iso8601_duration +-- See 'iso8601Duration'+iso8601Interval :: Int -> CharParser a (Either TimeDiff (MCalendarTime, MCalendarTime))+iso8601Interval localTz = leftDur <|> rightDur where+ leftDur =+ do dur <- iso8601Duration end <- option Nothing $ do { char '/'; Just `liftM` isoDt }- return $ case end of + return $ case end of Nothing -> Left dur Just e -> Right (dur `subtractFromMCal` e, e) rightDur = do start <- isoDt char '/'- durOrEnd <- Left `liftM` iso8601_duration <|> Right `liftM` isoDt+ durOrEnd <- Left `liftM` iso8601Duration <|> Right `liftM` isoDt return $ case durOrEnd of Left dur -> Right (start, dur `addToMCal` start) Right end -> Right (start, end)- isoDt = iso8601_date_time localTz- + isoDt = iso8601DateTime localTz+ -- | Durations in ISO 8601, e.g., -- -- * P4Y (four years)@@ -341,19 +341,19 @@ -- * P4Y5M (four years and five months) -- -- * P4YT3H6S (four years, three hours and six seconds)-iso8601_duration :: CharParser a TimeDiff-iso8601_duration = +iso8601Duration :: CharParser a TimeDiff+iso8601Duration = do char 'P' y <- block 0 'Y' mon <- block 0 'M' d <- block 0 'D'- (h,m,s) <- option (0,0,0) $ + (h,m,s) <- option (0,0,0) $ do char 'T' h' <- block (-1) 'H' m' <- block (-1) 'M' s' <- block (-1) 'S' let unset = (== (-1))- if all unset [h',m',s'] + if all unset [h',m',s'] then fail "T should be omitted if time is unspecified" else let clear x = if (unset x) then 0 else x in return (clear h', clear m', clear s')@@ -361,7 +361,7 @@ return $ TimeDiff y mon d h m s 0 where block d c = option d $ try $ do n <- many1 digit- char c + char c return $ read n -- | 'optchain' @p xs@ parses a string with the obligatory@@ -378,29 +378,29 @@ -- the year and month), 200707 (only the year and month -- with no separator), 2007-07-19 (year, month and day). optchain :: CharParser a b -> [(CharParser a c, CharParser a b)] -> CharParser a [b]-optchain p next = try $ +optchain p next = try $ do r1 <- p- r2 <- case next of + r2 <- case next of [] -> return [] ((sep,p2):next2) -> option [] $ do { optional sep; optchain p2 next2 } return (r1:r2) -n_digits :: Int -> CharParser a Int -n_digits n = read `liftM` count n digit+nDigits :: Int -> CharParser a Int+nDigits n = read `liftM` count n digit -two_digits, four_digits :: CharParser a Int-two_digits = n_digits 2-four_digits = n_digits 4+twoDigits, fourDigits :: CharParser a Int+twoDigits = nDigits 2+fourDigits = nDigits 4 -- | One or more space. -- WARNING! This only matches on the space character, not on -- whitespace in general-my_spaces :: CharParser a String-my_spaces = manyN 1 $ char ' '+mySpaces :: CharParser a String+mySpaces = manyN 1 $ char ' ' -- | English three-letter day abbreviations (e.g. Mon, Tue, Wed)-day_name :: CharParser a Day-day_name = choice+dayName :: CharParser a Day+dayName = choice [ caseString "Mon" >> return Monday , try (caseString "Tue") >> return Tuesday , caseString "Wed" >> return Wednesday@@ -412,11 +412,11 @@ -- | Four-digit year year :: CharParser a Int-year = four_digits+year = fourDigits -- | One or two digit month (e.g. 3 for March, 11 for November)-month_num :: CharParser a Month-month_num = do mn <- manyNtoM 1 2 digit +monthNum :: CharParser a Month+monthNum = do mn <- manyNtoM 1 2 digit return $ intToMonth $ (read mn :: Int) -- | January is 1, February is 2, etc@@ -436,8 +436,8 @@ intToMonth _ = error "invalid month!" -- | English three-letter month abbreviations (e.g. Jan, Feb, Mar)-month_name :: CharParser a Month-month_name = choice+monthName :: CharParser a Month+monthName = choice [ try (caseString "Jan") >> return January , caseString "Feb" >> return February , try (caseString "Mar") >> return March@@ -459,15 +459,15 @@ -- | hour in two-digit notation hour :: CharParser a Int-hour = two_digits+hour = twoDigits -- | minute in two-digit notation minute :: CharParser a Int-minute = two_digits+minute = twoDigits -- | second in two-digit notation second :: CharParser a Int-second = two_digits+second = twoDigits -- | limited timezone support --@@ -552,18 +552,18 @@ -- Uses its first argument as "now", i.e. the time relative to which -- "yesterday", "today" etc are to be interpreted englishDateTime :: CalendarTime -> CharParser a CalendarTime-englishDateTime now = - try $ dateMaybeAtTime <|> timeThenDate - where +englishDateTime now =+ try $ dateMaybeAtTime <|> timeThenDate+ where -- yesterday (at) noon- dateMaybeAtTime = try $ + dateMaybeAtTime = try $ do ed <- englishDate now t <- option Nothing $ try $ do { space; optional $ caseString "at "; Just `liftM` englishTime } return $ fromMaybe id t $ ed -- tea time 2005-12-04 timeThenDate = try $- do t <- englishTime + do t <- englishTime optional $ char ',' space ed <- englishDate now@@ -581,7 +581,7 @@ -- -- The first argument is "now". englishDate :: CalendarTime -> CharParser a CalendarTime-englishDate now = try $ +englishDate now = try $ (caseString "today" >> (return $ resetCalendar now)) <|> (caseString "yesterday" >> (return $ oneDay `subtractFromCal` now) ) <|> fst `fmap` englishLast now@@ -606,7 +606,7 @@ space d <- englishDate now <|> fst `fmap` englishLast now- <|> unsafeToCalendarTime `fmap` iso8601_date_time (ctTZ now)+ <|> unsafeToCalendarTime `fmap` iso8601DateTime (ctTZ now) return (m,d) return $ multiplyDiff m p `addToCal` ref where@@ -626,27 +626,27 @@ -- * 4 months ago (i.e. till now; see 'englishAgo') englishInterval :: CalendarTime -> CharParser a TimeInterval englishInterval now = twixt <|> before <|> after <|> inTheLast <|> lastetc- where - englishDT = (unsafeToCalendarTime `fmap` iso8601_date_time (ctTZ now)+ where+ englishDT = (unsafeToCalendarTime `fmap` iso8601DateTime (ctTZ now) <|> englishDateTime now) before = try $ do caseString "before" space- end <- englishDT + end <- englishDT return (Just theBeginning, Just end) after = try $ do caseString "after" space start <- englishDT return (Just start, Nothing)- twixt = try $ + twixt = try $ do caseString "between" space start <- englishDT space- caseString "and" + caseString "and" space- end <- englishDT + end <- englishDT return (Just start, Just end) inTheLast = try $ do caseString "in the last"@@ -668,17 +668,17 @@ d <- englishDuration return (d `subtractFromCal` now, now) --- | Either an 'iso8601_time' or one of several common+-- | Either an 'iso8601Time' or one of several common -- English time expressions like 'noon' or 'tea time' englishTime :: CharParser a (CalendarTime->CalendarTime)-englishTime = try $ - choice [ wrapM `fmap` iso8601_time- , namedTime "noon" 12 0 +englishTime = try $+ choice [ wrapM `fmap` iso8601Time+ , namedTime "noon" 12 0 , namedTime "midnight" 0 0 , namedTime "tea time" 16 30- , namedTime "bed time" 2 30 + , namedTime "bed time" 2 30 , namedTime "proper bed time" 21 30 ]- where namedTime name h m = try $ + where namedTime name h m = try $ do caseString name return $ \c -> c { ctHour = h, ctMin = m } wrapM f = unsafeToCalendarTime . f . toMCalendarTime@@ -702,11 +702,11 @@ b <- base optional (caseString "es" <|> caseString "s") let current = multiplyDiff n b- next <- option noTimeDiff $ try $ do + next <- option noTimeDiff $ try $ do { optional space; char ',' ; optional space ; englishDuration }- return $ addDiff current next - where - base = choice + return $ addDiff current next+ where+ base = choice [ try $ caseString "score" >> (return $ TimeDiff 20 0 0 0 0 0 0) -- why not? , caseString "year" >> (return $ TimeDiff 1 0 0 0 0 0 0) , try $ caseString "month" >> (return $ TimeDiff 0 1 0 0 0 0 0)@@ -714,9 +714,9 @@ , caseString "week" >> (return $ TimeDiff 0 0 7 0 0 0 0) , caseString "day" >> (return $ TimeDiff 0 0 1 0 0 0 0) , caseString "hour" >> (return $ TimeDiff 0 0 0 1 0 0 0)- , caseString "minute" >> (return $ TimeDiff 0 0 0 0 1 0 0) + , caseString "minute" >> (return $ TimeDiff 0 0 0 0 1 0 0) , caseString "second" >> (return $ TimeDiff 0 0 0 0 0 1 0) ]- + ----- Calendar and TimeDiff manipulation --------------------------------------------- -- | The very beginning of time, i.e. 1970-01-01
src/Lcs.hs view
@@ -196,7 +196,7 @@ (xmid, ymid, _) <- findDiag 1 h_a h_b p_a p_b m_a m_b v vrev off_a' off_b' l_a' l_b' del dodd when ((xmid == 0 && ymid == 0) || (xmid == l_a' && ymid == l_b')- || (xmid < 0 || ymid < 0 || xmid > l_a' || ymid > l_b')) + || (xmid < 0 || ymid < 0 || xmid > l_a' || ymid > l_b')) impossible c1 <- cmpseq h_a h_b p_a p_b m_a m_b c_a c_b off_a' off_b' xmid ymid@@ -246,7 +246,7 @@ -- | find position on diag d with one more insert/delete going forward findOne :: HArray -> HArray -> PArray -> PArray -> MapArray -> MapArray- -> VSTArray s -> Int -> Int -> Int -> Int -> Int -> ST s Int + -> VSTArray s -> Int -> Int -> Int -> Int -> Int -> ST s Int findOne h_a h_b p_a p_b m_a m_b v d off_a off_b l_a l_b = do x0 <- do xbelow <- readArray v (d - 1) xover <- readArray v (d + 1)@@ -268,7 +268,7 @@ -- | find position on diag d with one more insert/delete going backward findOneRev :: HArray -> HArray -> PArray -> PArray -> MapArray -> MapArray- -> VSTArray s -> Int -> Int -> Int -> Int -> ST s Int + -> VSTArray s -> Int -> Int -> Int -> Int -> ST s Int findOneRev h_a h_b p_a p_b m_a m_b v d del off_a off_b = do x0 <- do xbelow <- readArray v (d - 1) xover <- readArray v (d + 1)@@ -359,7 +359,7 @@ when ((not b1) || b2) impossible when (p_a!(i-1) /= p_a!(start-1)) impossible writeArray c_a (i-1) False- writeArray c_a (start-1) True + writeArray c_a (start-1) True j' <- prevUnchanged c_b (j-1) moveCorr (start-1) (i-1) j' corr
src/Preproc.hs view
@@ -12,7 +12,7 @@ -- * Some nonstandard pseudo-LaTeX commands are expanded into actual -- LaTeX text. In particular, \\darcsCommand{foo} is replaced by -- LaTeX markup describing the command @foo@.-module Preproc ( preproc_main ) where+module Preproc ( preprocMain ) where import qualified Ratified( readFile ) import System.FilePath ( (</>) ) import System.Environment ( getArgs )@@ -29,15 +29,15 @@ import English ( andClauses ) import Version ( version ) -the_commands :: [DarcsCommand]-the_commands = extractCommands commandControlList+theCommands :: [DarcsCommand]+theCommands = extractCommands commandControlList -- | The entry point for this program. The path to the TeX master -- file is supplied as the first argument. Bootstrapping into -- 'preproc' then happens by passing it a pseudo-document that -- contains a single input (include) line.-preproc_main :: [String] -> IO ()-preproc_main args = do+preprocMain :: [String] -> IO ()+preprocMain args = do if length args < 1 then exitWith $ ExitFailure 1 else return ()@@ -48,9 +48,9 @@ -- | Depending on whether pdflatex or htlatex is to be used, the LaTeX -- output of this program must vary subtly. This procedure returns -- true iff the command-line arguments contain @--html@.-am_html :: IO Bool-am_html = do args <- getArgs- return $ elem "--html" args+amHtml :: IO Bool+amHtml = do args <- getArgs+ return $ elem "--html" args -- | Given a list of input lines in pseudo-LaTeX, return the same -- document in LaTeX. The pseudo-LaTeX lines are replaced, other@@ -59,7 +59,7 @@ preproc [] = return [] -- Empty input, empty output. preproc ("\\usepackage{html}":ss) = -- only use html package with latex2html do rest <- preproc ss- ah <- am_html+ ah <- amHtml if ah then return $ "\\usepackage{html}" : rest else return $ "\\usepackage{hyperref}" : rest preproc ("\\begin{code}":ss) = ignore ss@@ -69,13 +69,13 @@ ignore [] = return [] preproc ("\\begin{options}":ss) = do rest <- preproc ss- ah <- am_html+ ah <- amHtml if ah then return $ "\\begin{rawhtml}" : "<div class=\"cmd-opt-hdr\">" : rest else return $ ("\\begin{Verbatim}[frame=lines,xleftmargin=1cm," ++ "xrightmargin=1cm]") : rest preproc ("\\end{options}":ss) = do rest <- preproc ss- ah <- am_html+ ah <- amHtml if ah then return $ "</div>" : "\\end{rawhtml}" : rest else return $ "\\end{Verbatim}" : rest preproc ("\\darcsVersion":ss) = do@@ -101,36 +101,36 @@ latexCommandHelp :: String -> String latexCommandHelp command = section ++ "{darcs " ++ command ++ "}\n" ++ "\\label{" ++ command ++ "}\n" ++- gh ++ get_options command ++ gd+ gh ++ getOptions command ++ gd where section = if ' ' `elem` command then "\\subsubsection" else "\\subsection" -- | Given a Darcs command name as a string, return that command's (multi-line) help string. gh :: String- gh = escape_latex_specials $ command_property commandHelp the_commands command+ gh = escapeLatexSpecials $ commandProperty commandHelp theCommands command -- | Given a Darcs command name as a string, return that command's (one-line) description string. gd :: String- gd = command_property commandDescription the_commands command+ gd = commandProperty commandDescription theCommands command -get_options :: String -> String-get_options comm = get_com_options $ get_c names the_commands+getOptions :: String -> String+getOptions comm = getComOptions $ getC names theCommands where names = words comm -get_c :: [String] -> [DarcsCommand] -> [DarcsCommand]-get_c (name:ns) commands =+getC :: [String] -> [DarcsCommand] -> [DarcsCommand]+getC (name:ns) commands = case ns of [] -> [get name commands] _ -> case get name commands of c@SuperCommand { } ->- c:(get_c ns $ extractCommands $ commandSubCommands c)+ c:(getC ns $ extractCommands $ commandSubCommands c) _ -> error $ "Not a supercommand: " ++ name where get n (c:cs) | commandName c == n = c | otherwise = get n cs get n [] = error $ "No such command: "++n-get_c [] _ = error "no command specified"+getC [] _ = error "no command specified" -get_com_options :: [DarcsCommand] -> String-get_com_options c =+getComOptions :: [DarcsCommand] -> String+getComOptions c = "\\par\\verb!Usage: darcs " ++ cmd ++ " [OPTION]... " ++ args ++ "!\n\n" ++ "Options:\n\n" ++ optionsLatex opts1 ++ (if null opts2 then "" else "\n\n" ++ "Advanced options:\n\n" ++ optionsLatex opts2)@@ -139,21 +139,21 @@ opts1 = commandBasicOptions $ last c opts2 = commandAdvancedOptions $ last c -command_property :: (DarcsCommand -> String) -> [DarcsCommand] -> String+commandProperty :: (DarcsCommand -> String) -> [DarcsCommand] -> String -> String-command_property property commands name =+commandProperty property commands name = property $ last c where names = words name- c = get_c names commands+ c = getC names commands envHelp :: String -> String envHelp var = unlines $ render $ entry environmentHelp where render (ks, ds) =- ("\\paragraph{" ++ escape_latex_specials (andClauses ks) ++ "}") :+ ("\\paragraph{" ++ escapeLatexSpecials (andClauses ks) ++ "}") : ("\\label{env:" ++ var ++ "}") :- map escape_latex_specials ds+ map escapeLatexSpecials ds entry [] = undefined entry (x:xs) | elem var $ fst x = x | otherwise = entry xs@@ -161,9 +161,9 @@ -- | LaTeX treats a number of characters or sequences specially. -- Therefore when including ordinary help text in a LaTeX document, it -- is necessary to escape these characters in the way LaTeX expects.-escape_latex_specials :: String -> String+escapeLatexSpecials :: String -> String -- Order is important-escape_latex_specials =+escapeLatexSpecials = (bs2 . amp . percent . carrot . dollar . underscore . rbrace . lbrace . bs1) where amp = replace "&" "\\&"
src/Printer.lhs view
@@ -43,9 +43,9 @@ hPutDocWith, hPutDocLnWith, putDocWith, putDocLnWith, renderString, renderStringWith, renderPS, renderPSWith, renderPSs, renderPSsWith, lineColor,- prefix, insert_before_lastline, colorText, invisibleText, + prefix, insertBeforeLastline, colorText, invisibleText, hiddenText, hiddenPrefix, userchunk, text,- printable, wrap_text,+ printable, wrapText, blueText, redText, greenText, magentaText, cyanText, unsafeText, unsafeBoth, unsafeBothText, unsafeChar, invisiblePS, packedString, unsafePackedString, userchunkPS,@@ -68,13 +68,13 @@ | PS !B.ByteString | Both !String !B.ByteString --- | 'space_p' is the 'Printable' representation of a space.-space_p :: Printable-space_p = Both " " (BC.singleton ' ')+-- | 'spaceP' is the 'Printable' representation of a space.+spaceP :: Printable+spaceP = Both " " (BC.singleton ' ') --- | 'newline_p' is the 'Printable' representation of a newline.-newline_p :: Printable-newline_p = S "\n"+-- | 'newlineP' is the 'Printable' representation of a newline.+newlineP :: Printable+newlineP = S "\n" -- | Minimal 'Doc's representing the common characters 'space', 'newline' -- 'minus', 'plus', and 'backslash'.@@ -154,7 +154,7 @@ -- | The State associated with a doc. Contains a set of printers for each -- hanlde, and the current prefix of the document. data St = St { printers :: !Printers',- current_prefix :: !([Printable] -> [Printable]) }+ currentPrefix :: !([Printable] -> [Printable]) } type Printers = Handle -> Printers' -- | A set of printers to print different types of text to a handle.@@ -214,24 +214,24 @@ -- printers. Each item of the list corresponds to a string that was -- added to the doc. renderWith :: Printers' -> Doc -> [Printable]-renderWith ps (Doc d) = case d (init_state ps) of+renderWith ps (Doc d) = case d (initState ps) of Empty -> [] Document f -> f [] -init_state :: Printers' -> St-init_state prs = St { printers = prs, current_prefix = id }+initState :: Printers' -> St+initState prs = St { printers = prs, currentPrefix = id } prefix :: String -> Doc -> Doc prefix s (Doc d) = Doc $ \st -> let p = S s- st' = st { current_prefix = current_prefix st . (p:) } in+ st' = st { currentPrefix = currentPrefix st . (p:) } in case d st' of Document d'' -> Document $ (p:) . d'' Empty -> Empty- -insert_before_lastline :: Doc -> Doc -> Doc-insert_before_lastline a b =++insertBeforeLastline :: Doc -> Doc -> Doc+insertBeforeLastline a b = case reverse $ map packedString $ linesPS $ renderPS a of (ll:ls) -> vcat (reverse ls) $$ b $$ ll [] -> error "empty Doc given as first argument of Printer.insert_before_last_line"@@ -245,7 +245,7 @@ hiddenPrefix s (Doc d) = Doc $ \st -> let pr = printers st p = S (renderStringWith pr $ hiddenText s)- st' = st { current_prefix = current_prefix st . (p:) }+ st' = st { currentPrefix = currentPrefix st . (p:) } in case d st' of Document d'' -> Document $ (p:) . d'' Empty -> Empty@@ -304,9 +304,9 @@ colorText :: Color -> String -> Doc colorText c = mkColorPrintable c . S --- | @'wrap_text' n s@ is a 'Doc' representing @s@ line-wrapped at 'n' characters-wrap_text :: Int -> String -> Doc-wrap_text n s =+-- | @'wrapText' n s@ is a 'Doc' representing @s@ line-wrapped at 'n' characters+wrapText :: Int -> String -> Doc+wrapText n s = vcat $ map text $ reverse $ "": (foldl add_to_line [] $ words s) where add_to_line [] a = [a] add_to_line ("":d) a = (a:d)@@ -393,7 +393,7 @@ Document af -> Document (\s -> af $ case b st of Empty -> s Document bf ->- space_p:bf s)+ spaceP:bf s) -- a above b Doc a $$ Doc b =@@ -402,8 +402,8 @@ Document af -> Document (\s -> af $ case b st of Empty -> s- Document bf -> sf (newline_p:pf (bf s)))- where pf = current_prefix st+ Document bf -> sf (newlineP:pf (bf s)))+ where pf = currentPrefix st sf = lineColorS $ printers st -- | 'vcat' piles vertically a list of 'Doc's.
src/Progress.hs view
@@ -11,9 +11,9 @@ finishedOneIO, progressList, minlist, setProgressMode) where -import Prelude hiding (lookup, catch)+import Prelude hiding (lookup) -import Control.Exception ( catch, throw )+import Control.Exception.Extensible ( onException ) import Control.Monad ( when ) import System.IO ( stdout, stderr, hFlush, hPutStr, hPutStrLn, hSetBuffering, hIsTerminalDevice,@@ -172,7 +172,7 @@ withoutProgress :: IO a -> IO a withoutProgress j = withProgressMode $ \m -> do debugMessage "Disabling progress reports..." setProgressMode False- a <- j `catch` \e -> setProgressMode m >> throw e+ a <- j `onException` setProgressMode m if m then debugMessage "Reenabling progress reports." else debugMessage "Leaving progress reports off." setProgressMode m
src/SHA1.hs view
@@ -15,7 +15,7 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# OPTIONS_GHC -fno-warn-name-shadowing -cpp #-}+{-# OPTIONS_GHC -fno-warn-name-shadowing #-} {-# LANGUAGE CPP #-} @@ -40,52 +40,52 @@ sha1PS :: B.ByteString -> String sha1PS s = s5- where s1_2 = sha1_step_1_2_pad_length s- abcde = sha1_step_3_init+ where s1_2 = sha1Step12PadLength s+ abcde = sha1Step3Init abcde' = unsafePerformIO $ unsafeWithInternals s1_2 (\ptr len -> do let ptr' = castPtr ptr #ifndef BIGENDIAN- fiddle_endianness ptr' len+ fiddleEndianness ptr' len #endif- sha1_step_4_main abcde ptr' len)- s5 = sha1_step_5_display abcde'+ sha1Step4Main abcde ptr' len)+ s5 = sha1Step5Display abcde' -fiddle_endianness :: Ptr Word32 -> Int -> IO ()-fiddle_endianness p 0 = p `seq` return ()-fiddle_endianness p n+fiddleEndianness :: Ptr Word32 -> Int -> IO ()+fiddleEndianness p 0 = p `seq` return ()+fiddleEndianness p n = do x <- peek p poke p $ shiftL x 24 .|. shiftL (x .&. 0xff00) 8 .|. (shiftR x 8 .&. 0xff00) .|. shiftR x 24- fiddle_endianness (p `advancePtr` 1) (n - 4)+ fiddleEndianness (p `advancePtr` 1) (n - 4) --- sha1_step_1_2_pad_length assumes the length is at most 2^61.+-- sha1Step12PadLength assumes the length is at most 2^61. -- This seems reasonable as the Int used to represent it is normally 32bit, -- but obviously could go wrong with large inputs on 64bit machines. -- The B.ByteString library should probably move to Word64s if this is an -- issue, though. -sha1_step_1_2_pad_length :: B.ByteString -> B.ByteString-sha1_step_1_2_pad_length s+sha1Step12PadLength :: B.ByteString -> B.ByteString+sha1Step12PadLength s = let len = B.length s num_nuls = (55 - len) `mod` 64 padding = 128:replicate num_nuls 0- len_w8s = reverse $ size_split 8 (fromIntegral len*8)+ len_w8s = reverse $ sizeSplit 8 (fromIntegral len*8) in B.concat [s, B.pack padding, B.pack len_w8s] -size_split :: Int -> Integer -> [Word8]-size_split 0 _ = []-size_split p n = fromIntegral d:size_split (p-1) n'+sizeSplit :: Int -> Integer -> [Word8]+sizeSplit 0 _ = []+sizeSplit p n = fromIntegral d:sizeSplit (p-1) n' where (n', d) = divMod n 256 -sha1_step_3_init :: ABCDE-sha1_step_3_init = ABCDE 0x67452301 0xefcdab89 0x98badcfe 0x10325476 0xc3d2e1f0+sha1Step3Init :: ABCDE+sha1Step3Init = ABCDE 0x67452301 0xefcdab89 0x98badcfe 0x10325476 0xc3d2e1f0 -sha1_step_4_main :: ABCDE -> Ptr Word32 -> Int -> IO ABCDE-sha1_step_4_main abcde _ 0 = return $! abcde-sha1_step_4_main (ABCDE a0@a b0@b c0@c d0@d e0@e) s len+sha1Step4Main :: ABCDE -> Ptr Word32 -> Int -> IO ABCDE+sha1Step4Main abcde _ 0 = return $! abcde+sha1Step4Main (ABCDE a0@a b0@b c0@c d0@d e0@e) s len = do (e, b) <- doit f1 0x5a827999 (x 0) a b c d e (d, a) <- doit f1 0x5a827999 (x 1) e a b c d@@ -168,7 +168,7 @@ (b, d) <- doit f2 0xca62c1d6 (m 78) c d e a b (a, c) <- doit f2 0xca62c1d6 (m 79) b c d e a let abcde' = ABCDE (a0 + a) (b0 + b) (c0 + c) (d0 + d) (e0 + e)- sha1_step_4_main abcde' (s `advancePtr` 16) (len - 64)+ sha1Step4Main abcde' (s `advancePtr` 16) (len - 64) where {-# INLINE f1 #-} f1 (XYZ x y z) = (x .&. y) .|. ((complement x) .&. z) {-# INLINE f2 #-}@@ -192,8 +192,8 @@ return (rotateL a 5 + f (XYZ b c d) + e + i' + k, rotateL b 30) -sha1_step_5_display :: ABCDE -> String-sha1_step_5_display (ABCDE a b c d e)+sha1Step5Display :: ABCDE -> String+sha1Step5Display (ABCDE a b c d e) = concatMap showAsHex [a, b, c, d, e] showAsHex :: Word32 -> String
src/Ssh.hs view
@@ -22,13 +22,14 @@ import Control.Monad ( when ) import System.Process ( runInteractiveProcess ) +import Data.List ( isPrefixOf ) import Data.Map ( Map, empty, insert, lookup ) import Data.IORef ( IORef, newIORef, readIORef, modifyIORef ) import Darcs.SignalHandler ( catchNonSignal ) import Darcs.Utils ( withCurrentDirectory, breakCommand, prettyException, catchall ) import Darcs.Global ( atexit, sshControlMasterDisabled, darcsdir, withDebugMode )-import Darcs.Lock ( withTemp, withOpenTemp, tempdir_loc, removeFileMayNotExist )+import Darcs.Lock ( withTemp, withOpenTemp, tempdirLoc, removeFileMayNotExist ) import Exec ( exec, Redirects, Redirect(..), ) import Progress ( withoutProgress, debugMessage, debugFail, progressList ) @@ -85,7 +86,7 @@ modifyIORef sshConnections (insert (cleanrepourl x) Nothing) cleanrepourl :: String -> String-cleanrepourl zzz | take (length dd) zzz == dd = ""+cleanrepourl zzz | dd `isPrefixOf` zzz = "" where dd = darcsdir++"/" cleanrepourl (z:zs) = z : cleanrepourl zs cleanrepourl "" = ""@@ -97,7 +98,7 @@ grabSSH x c = do let dir = drop 1 $ dropWhile (/= ':') x dd = darcsdir++"/"- clean zzz | take (length dd) zzz == dd = drop (length dd) zzz+ clean zzz | dd `isPrefixOf` zzz = drop (length dd) zzz clean (_:zs) = clean zs clean "" = bug $ "Buggy path in grabSSH: "++x file = clean dir@@ -159,7 +160,7 @@ then (take 5 ns) ++ ["and " ++ (show (length ns - 5)) ++ " more"] else ns- hint = if take 1 path == "~"+ hint = if "~" `isPrefixOf` path then ["sftp doesn't expand ~, use path/ instead of ~/path/"] else [] when (r /= ExitSuccess) $ do@@ -310,7 +311,7 @@ -> IO FilePath controlMasterPath rawAddr = do let addr = takeWhile (/= ':') rawAddr- tmp <- (fmap (/// ".darcs") $ getEnv "HOME") `catchall` tempdir_loc+ tmp <- (fmap (/// ".darcs") $ getEnv "HOME") `catchall` tempdirLoc #ifdef WIN32 r <- randomIO let suffix = (showHexLen 6 (r .&. 0xFFFFFF :: Int))
src/URL.hs view
@@ -1,8 +1,8 @@ {-# LANGUAGE CPP, ForeignFunctionInterface #-} -module URL ( copyUrl, copyUrlFirst, pipeliningEnabledByDefault,- setDebugHTTP, setHTTPPipelining, waitUrl,- Cachable(Cachable, Uncachable, MaxAge),+module URL ( copyUrl, copyUrlFirst, setDebugHTTP,+ disableHTTPPipelining, maxPipelineLength,+ waitUrl, Cachable(Cachable, Uncachable, MaxAge), environmentHelpProxy, environmentHelpProxyPassword ) where @@ -31,23 +31,21 @@ #ifdef HAVE_CURL import Foreign.C.String ( withCString, peekCString, CString ) #else-import qualified HTTP ( request_url, wait_next_url )+import qualified HTTP ( requestUrl, waitNextUrl ) #endif #include "impossible.h" data UrlRequest = UrlRequest { url :: String , file :: FilePath , cachable :: Cachable- , priority :: Priority- , notifyVar :: MVar String }+ , priority :: Priority } data Cachable = Cachable | Uncachable | MaxAge !CInt deriving (Show, Eq) data UrlState = UrlState { inProgress :: Map String ( FilePath , [FilePath]- , Cachable- , (MVar String) )+ , Cachable ) , waitToStart :: Q String , pipeLength :: Int , randomJunk :: String }@@ -80,26 +78,19 @@ data Priority = High | Low deriving Eq -#if defined(CURL_PIPELINING) || defined(CURL_PIPELINING_DEFAULT)-pipeliningLimit :: Int-pipeliningLimit = 100-#endif--pipeliningEnabledByDefault :: Bool-#ifdef CURL_PIPELINING_DEFAULT-pipeliningEnabledByDefault = True-#else-pipeliningEnabledByDefault = False+{-# NOINLINE maxPipelineLengthRef #-}+maxPipelineLengthRef :: IORef Int+maxPipelineLengthRef = unsafePerformIO $ do+ enabled <- pipeliningEnabled+#ifdef HAVE_CURL+ when (not enabled) (debugMessage $+ "Warning: pipelining is disabled, because libcurl "+++ "version darcs was compiled with is too old (< 7.19.1)") #endif+ newIORef $ if enabled then 100 else 1 -{-# NOINLINE maxPipeLength #-}-maxPipeLength :: IORef Int-maxPipeLength = unsafePerformIO $ newIORef $-#ifdef CURL_PIPELINING_DEFAULT- pipeliningLimit-#else- 1-#endif+maxPipelineLength :: IO Int+maxPipelineLength = readIORef maxPipelineLengthRef {-# NOINLINE urlNotifications #-} urlNotifications :: MVar (Map String (MVar String))@@ -148,23 +139,23 @@ st <- get let p = inProgress st w = waitToStart st- e = (f, [], c, notifyVar r)+ e = (f, [], c) new_w = case priority r of High -> pushQ u w Low -> insertQ u w new_st = st { inProgress = Map.insert u e p , waitToStart = new_w } case Map.lookup u p of- Just (f', fs', c', v) -> do+ Just (f', fs', c') -> do let new_c = minCachable c c'- when (c /= c') $ let new_p = Map.insert u (f', fs', new_c, v) p+ when (c /= c') $ let new_p = Map.insert u (f', fs', new_c) p in do modify (\s -> s { inProgress = new_p }) dbg $ "Changing "++u++" request cachability from "++show c++" to "++show new_c when (u `elemQ` w && priority r == High) $ do modify (\s -> s { waitToStart = pushQ u (deleteQ u w) }) dbg $ "Moving "++u++" to head of download queue." if f `notElem` (f':fs')- then let new_p = Map.insert u (f', f:fs', new_c, v) p+ then let new_p = Map.insert u (f', f:fs', new_c) p in do modify (\s -> s { inProgress = new_p }) dbg "Adding new file to existing UrlRequest." else dbg "Ignoring UrlRequest of file that's already queued."@@ -179,25 +170,25 @@ checkWaitToStart = do st <- get let l = pipeLength st- mpl <- liftIO $ readIORef maxPipeLength+ mpl <- liftIO maxPipelineLength when (l < mpl) $ do let w = waitToStart st case readQ w of Just (u,rest) -> do case Map.lookup u (inProgress st) of- Just (f, _, c, v) -> do- dbg ("URL.request_url ("++u++"\n"+++ Just (f, _, c) -> do+ dbg ("URL.requestUrl ("++u++"\n"++ " -> "++f++")") let f_new = f++"-new_"++randomJunk st- err <- liftIO $ request_url u f_new c+ err <- liftIO $ requestUrl u f_new c if null err- then do dbg "URL.request_url succeeded"+ then do dbg "URL.requestUrl succeeded" liftIO $ atexit (removeFileMayNotExist f_new) put $ st { waitToStart = rest , pipeLength = l + 1 } else do dbg $ "Failed to start download URL "++u++": "++err liftIO $ do removeFileMayNotExist f_new- putMVar v err+ downloadComplete u err put $ st { waitToStart = rest } _ -> bug $ "Possible bug in URL.checkWaitToStart "++u checkWaitToStart@@ -216,7 +207,7 @@ v <- newEmptyMVar let fn _ old_val = old_val modifyMVar_ urlNotifications (return . (Map.insertWith fn u v))- let r = UrlRequest u f c p v+ let r = UrlRequest u f c p writeChan urlChan r waitNextUrl :: StateT UrlState IO ()@@ -225,22 +216,22 @@ let l = pipeLength st when (l > 0) $ do dbg "URL.waitNextUrl start"- (u, e) <- liftIO $ wait_next_url+ (u, e) <- liftIO $ waitNextUrl' let p = inProgress st new_st = st { inProgress = Map.delete u p , pipeLength = l - 1 } liftIO $ if null e then case Map.lookup u p of- Just (f, fs, _, v) -> do+ Just (f, fs, _) -> do renameFile (f++"-new_"++randomJunk st) f mapM_ (safeCopyFile st f) fs- putMVar v e+ downloadComplete u e debugMessage $ "URL.waitNextUrl succeeded: "++u++" "++f Nothing -> bug $ "Possible bug in URL.waitNextUrl: "++u else case Map.lookup u p of- Just (f, _, _, v) -> do+ Just (f, _, _) -> do removeFileMayNotExist (f++"-new_"++randomJunk st)- putMVar v e+ downloadComplete u e debugMessage $ "URL.waitNextUrl failed: "++ u++" "++f++" "++e Nothing -> bug $ "Another possible bug in URL.waitNextUrl: "++u++" "++e@@ -249,6 +240,13 @@ in do copyFile f new_t renameFile new_t t +downloadComplete :: String -> String -> IO ()+downloadComplete u e = do+ r <- withMVar urlNotifications (return . (Map.lookup u))+ case r of+ Just notifyVar -> putMVar notifyVar e+ Nothing -> debugMessage $ "downloadComplete URL '"++u++"' downloaded several times"+ waitUrl :: String -> IO () waitUrl u = do debugMessage $ "URL.waitUrl "++u r <- withMVar urlNotifications (return . (Map.lookup u))@@ -277,35 +275,33 @@ cachableToInt (MaxAge n) = n #endif -setHTTPPipelining :: Bool -> IO ()-setHTTPPipelining False = writeIORef maxPipeLength 1-setHTTPPipelining True = writeIORef maxPipeLength-#ifdef CURL_PIPELINING- pipeliningLimit-#else- 1 >> (putStrLn $ "Warning: darcs is compiled without HTTP pipelining "++- "support, '--http-pipelining' argument is ignored.")-#endif+disableHTTPPipelining :: IO ()+disableHTTPPipelining = writeIORef maxPipelineLengthRef 1 setDebugHTTP :: IO ()-request_url :: String -> FilePath -> Cachable -> IO String-wait_next_url :: IO (String, String)+requestUrl :: String -> FilePath -> Cachable -> IO String+waitNextUrl' :: IO (String, String)+pipeliningEnabled :: IO Bool #ifdef HAVE_CURL setDebugHTTP = curl_enable_debug -request_url u f cache =+requestUrl u f cache = withCString u $ \ustr -> withCString f $ \fstr -> do err <- curl_request_url ustr fstr (cachableToInt cache) >>= peekCString return err -wait_next_url = do+waitNextUrl' = do e <- curl_wait_next_url >>= peekCString u <- curl_last_url >>= peekCString return (u, e) +pipeliningEnabled = do+ r <- curl_pipelining_enabled+ return $ r /= 0+ foreign import ccall "hscurl.h curl_request_url" curl_request_url :: CString -> CString -> CInt -> IO CString @@ -318,17 +314,22 @@ foreign import ccall "hscurl.h curl_enable_debug" curl_enable_debug :: IO () +foreign import ccall "hscurl.h curl_pipelining_enabled"+ curl_pipelining_enabled :: IO CInt+ #elif defined(HAVE_HTTP) setDebugHTTP = return ()-request_url = HTTP.request_url-wait_next_url = HTTP.wait_next_url+requestUrl = HTTP.requestUrl+waitNextUrl' = HTTP.waitNextUrl+pipeliningEnabled = return False #else setDebugHTTP = debugMessage "URL.setDebugHttp works only with libcurl"-request_url _ _ _ = debugFail "URL.request_url: there is no libcurl!"-wait_next_url = debugFail "URL.wait_next_url: there is no libcurl!"+requestUrl _ _ _ = debugFail "URL.requestUrl: there is no libcurl!"+waitNextUrl' = debugFail "URL.waitNextUrl': there is no libcurl!"+pipeliningEnabled = return False #endif
src/building_darcs.tex view
@@ -24,7 +24,7 @@ This will require the following build dependencies: \begin{itemize}-\item GHC 6.8 or higher; and+\item GHC 6.10 or higher; and \item Cabal 1.6 or higher. \end{itemize} @@ -70,7 +70,7 @@ \item http 3000 or 3001.1; \item network 2.2; \item terminfo 0.3.- \item utf8-string 0.3; and+ \item text 0.3; and \item zlib 0.5. \end{itemize} \end{itemize}
src/darcs.hs view
@@ -20,26 +20,24 @@ module Main (main) where -import System.IO ( hSetBinaryMode)-import System.IO ( stdin, stdout )+import Prelude hiding ( catch )++import System.IO ( stdin, stdout, stderr, hSetBinaryMode )+import Control.Monad ( forM_ ) import System.Exit ( exitWith, ExitCode(..) ) import System.Environment ( getArgs )-import Control.Exception ( Exception( AssertionFailed ), handleJust, catchDyn )+import Control.Exception.Extensible ( AssertionFailed(..), handle, catch ) -import Darcs.RunCommand ( run_the_command )+import Darcs.RunCommand ( runTheCommand ) import Darcs.Flags ( DarcsFlag(Verbose) )-import Darcs.Commands.Help ( helpCmd, listAvailableCommands, printVersion )+import Darcs.Commands.Help ( helpCmd, listAvailableCommands, printVersion, commandControlList ) import Darcs.SignalHandler ( withSignalsHandled )-import Version ( version, context )-import Darcs.Global ( with_atexit )-import Preproc( preproc_main )+import Version ( version, context, builddeps )+import Darcs.Global ( withAtexit )+import Preproc( preprocMain ) import Exec ( ExecException(..) ) #include "impossible.h" -assertions :: Control.Exception.Exception -> Maybe String-assertions (AssertionFailed s) = Just s-assertions _ = Nothing- execExceptionHandler :: ExecException -> IO a execExceptionHandler (ExecException cmd args redirects reason) = do putStrLn $ "Failed to execute external command: " ++ unwords (cmd:args) ++ "\n"@@ -48,9 +46,9 @@ exitWith $ ExitFailure 3 main :: IO ()-main = with_atexit $ withSignalsHandled $- flip catchDyn execExceptionHandler $- handleJust assertions bug $ do+main = withAtexit $ withSignalsHandled $+ flip catch execExceptionHandler $+ handle (\(AssertionFailed e) -> bug e) $ do argv <- getArgs case argv of -- User called "darcs" without arguments.@@ -65,9 +63,10 @@ ["--exact-version"] -> do putStrLn $ "darcs compiled on "++__DATE__++", at "++__TIME__ putStrLn context- ("--preprocess-manual":rest) -> preproc_main rest+ putStrLn $ "Compiled with:\n"+ putStr builddeps+ ("--preprocess-manual":rest) -> preprocMain rest -- User called a normal darcs command, "darcs foo [args]". _ -> do- hSetBinaryMode stdin True- hSetBinaryMode stdout True- run_the_command (head argv) (tail argv)+ forM_ [stdout, stdin, stderr] $ \h -> hSetBinaryMode h True+ runTheCommand commandControlList (head argv) (tail argv)
src/fpstring.c view
@@ -20,6 +20,7 @@ #include <string.h> #include <stdio.h> + #ifdef _WIN32 #include <windows.h> #else@@ -65,3 +66,4 @@ return; }+
src/fpstring.h view
@@ -5,4 +5,3 @@ void conv_to_hex(unsigned char *dest, unsigned char *from, int num_chars); void conv_from_hex(unsigned char *dest, unsigned char *from, int num_chars);-
src/hscurl.c view
@@ -1,5 +1,3 @@-#ifdef HAVE_CURL- #include "hscurl.h" #include <curl/curl.h>@@ -8,6 +6,11 @@ #include <stdlib.h> #include <string.h> +#if LIBCURL_VERSION_NUM >= 0x071301+/* enable pipelining for libcurl >= 7.19.1 */+#define ENABLE_PIPELINING+#endif+ enum RESULT_CODES { RESULT_OK = 0,@@ -16,7 +19,6 @@ RESULT_MULTI_INIT_FAIL, RESULT_EASY_INIT_FAIL, RESULT_SLIST_APPEND_FAIL,- RESULT_NO_RUNNING_HANDLES, RESULT_MULTI_INFO_READ_FAIL, RESULT_UNKNOWN_MESSAGE, RESULT_FILE_OPEN_FAIL@@ -30,7 +32,6 @@ "curl_multi_init() failed", "curl_easy_init() failed", "curl_slist_append() failed",- "curl_multi_perform() - no running handles", "curl_multi_info_read() failed", "curl_multi_info_read() returned unknown message", "fopen() failed"@@ -44,8 +45,14 @@ }; static int debug = 0;+#ifndef _WIN32 static const char user_agent[] = "darcs/" PACKAGE_VERSION " libcurl/" LIBCURL_VERSION;+#else+static const char user_agent[] =+ "darcs/unknown libcurl/" LIBCURL_VERSION;+#endif+ static const char *proxypass; static int init_done = 0; static CURLM *multi = NULL;@@ -64,11 +71,9 @@ error = curl_multi_perform(multi, &running_handles); if (error != CURLM_OK && error != CURLM_CALL_MULTI_PERFORM) return curl_multi_strerror(error);- if (running_handles == 0)- return error_strings[RESULT_NO_RUNNING_HANDLES]; running_handles_last = running_handles;- while (1)+ while (running_handles_last > 0) { while (error == CURLM_CALL_MULTI_PERFORM) error = curl_multi_perform(multi, &running_handles);@@ -133,7 +138,7 @@ multi = curl_multi_init(); if (multi == NULL) return error_strings[RESULT_MULTI_INIT_FAIL];-#ifdef CURL_PIPELINING+#ifdef ENABLE_PIPELINING error = curl_multi_setopt(multi, CURLMOPT_PIPELINING, 1); if (error != CURLM_OK && error != CURLM_CALL_MULTI_PERFORM) return curl_multi_strerror(error);@@ -302,4 +307,11 @@ debug = 1; } +int curl_pipelining_enabled()+{+#ifdef ENABLE_PIPELINING+ return 1;+#else+ return 0; #endif+}
src/hscurl.h view
@@ -7,3 +7,5 @@ const char *curl_last_url(); void curl_enable_debug();++int curl_pipelining_enabled();
+ src/unit.hs view
@@ -0,0 +1,3 @@+module Main ( main ) where++import Darcs.Test.Unit ( main )
− src/unit.lhs
@@ -1,770 +0,0 @@-% Copyright (C) 2002-2005,2007 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.--\documentclass{report}-\usepackage{color}--\usepackage{verbatim}-\newenvironment{code}{\color{blue}\verbatim}{\endverbatim}--\begin{document}--% Definition of title page:-\title{- Unit Testing for darcs in Haskell-}-\author{- David Roundy % insert author(s) here-}--\maketitle--\tableofcontents % Table of Contents--\chapter{Introduction}--This is a unit testing program, which is intended to make sure that all the-functions of my darcs code work properly.--\begin{code}-{-# OPTIONS_GHC -cpp -fno-warn-orphans -fno-warn-deprecations -fglasgow-exts #-}-{-# LANGUAGE CPP #-}--module Main (main) where--import System.IO.Unsafe ( unsafePerformIO )-import ByteStringUtils hiding ( intercalate )-import Codec.Binary.UTF8.Generic ( toString )-import qualified Data.ByteString.Char8 as BC ( unpack, pack )-import qualified Data.ByteString as B ( concat, empty )-import Darcs.Patch-import Darcs.Test.Patch.Test-import Darcs.Test.Patch.Unit ( patch_unit_tests )-import Darcs.Test.Email ( email_parsing, email_header_no_long_lines,- email_header_ascii_chars, email_header_lines_start,- email_header_no_empty_lines )-import Lcs ( shiftBoundaries )-import Test.QuickCheck-import Printer ( renderPS )-import Darcs.Patch.Commute-import Data.Array.Base-import Data.Array.Unboxed-import Control.Monad.ST-import Darcs.Witnesses.Ordered-import Darcs.Witnesses.Sealed ( Sealed(Sealed), unsafeUnseal )-import Test.HUnit ( assertBool, assertFailure )-import Test.Framework.Providers.QuickCheck2 ( testProperty )-import Test.Framework.Providers.HUnit ( testCase )-import Test.Framework.Runners.Console ( defaultMain )-import Test.Framework ( Test )--#include "impossible.h"-\end{code}--\chapter{Main body of code}--\begin{code}-main :: IO ()-main = do- putStr ("There are a total of "++(show (length primitive_test_patches))- ++" primitive patches.\n")- putStr ("There are a total of "++- (show (length test_patches))++" patches.\n")- defaultMain tests---- | Utility function to run bools with test-framework-testBool :: String -> Bool -> Test-testBool name test = testCase name (assertBool assertName test)- where assertName = "boolean test \"" ++ name ++ "\" should return True"---- | Utility function to run old tests that return a list of error messages,--- with the empty list meaning success.-testStringList :: String -> [String] -> Test-testStringList name test = testCase name $ mapM_ assertFailure test---- | This is the big list of tests that will be run using testrunner.-tests :: [Test]-tests = patch_unit_tests ++- [testBool "Checking that UTF-8 packing and unpacking preserves 'hello world'"- (toString (BC.pack "hello world") == "hello world"),- testBool "Checking that hex packing and unpacking preserves 'hello world'"- (BC.unpack (fromHex2PS $ fromPS2Hex $ BC.pack "hello world")- == "hello world"),- email_parsing,- email_header_no_long_lines,- email_header_ascii_chars,- email_header_lines_start,- email_header_no_empty_lines,- testProperty "Checking that B.concat works" prop_concatPS,- testProperty "Checking that hex conversion works" prop_hex_conversion,- testProperty "Checking that show and read work right" prop_read_show,- testStringList "Checking known commutes" commute_tests,- testStringList "Checking known merges" merge_tests,- testStringList "Checking known canons" canonization_tests]- ++ check_subcommutes subcommutes_inverse "patch and inverse both commute"- ++ check_subcommutes subcommutes_nontrivial_inverse "nontrivial commutes are correct"- ++ check_subcommutes subcommutes_failure "inverses fail"- ++- [testProperty "Checking that commuting by patch and its inverse is ok" prop_commute_inverse,- --putStr "Checking that conflict resolution is valid... "- --runQuickCheckTest returnval prop_resolve_conflicts_valid- testProperty "Checking that a patch followed by its inverse is identity" prop_patch_and_inverse_is_identity,- -- The following tests are "wrong" with the Conflictor code.- --putStr "Checking that a simple smart_merge is sufficient... "- --runQuickCheckTest returnval prop_simple_smart_merge_good_enough- --putStr "Checking that an elegant merge is sufficient... "- --runQuickCheckTest returnval prop_elegant_merge_good_enough- testProperty "Checking that commutes are equivalent" prop_commute_equivalency,- testProperty "Checking that merges are valid" prop_merge_valid,- testProperty "Checking inverses being valid" prop_inverse_valid,- testProperty "Checking other inverse being valid" prop_other_inverse_valid,- testStringList "Checking merge swaps" merge_swap_tests,- -- The patch generator isn't smart enough to generate correct test- -- cases for the following: (which will be obsoleted soon, anyhow)- --putStr "Checking the order dependence of unravel... "- --runQuickCheckTest returnval prop_unravel_order_independent- --putStr "Checking the unravelling of three merges... "- --runQuickCheckTest returnval prop_unravel_three_merge- --putStr "Checking the unravelling of a merge of a sequence... "- --runQuickCheckTest returnval prop_unravel_seq_merge- testProperty "Checking inverse of inverse" prop_inverse_composition,- testProperty "Checking the order of commutes" prop_commute_either_order,- testProperty "Checking commute either way" prop_commute_either_way,- testProperty "Checking the double commute" prop_commute_twice,- testProperty "Checking that merges commute and are well behaved" prop_merge_is_commutable_and_correct,- testProperty "Checking that merges can be swapped" prop_merge_is_swapable,- testProperty "Checking again that merges can be swapped (I'm paranoid) " prop_merge_is_swapable,- testStringList "Checking that the patch validation works" test_check,- testStringList "Checking commute/recommute" commute_recommute_tests,- testStringList "Checking merge properties" generic_merge_tests,- testStringList "Testing the lcs code" show_lcs_tests,- testStringList "Checking primitive patch IO functions" primitive_show_read_tests,- testStringList "Checking IO functions" show_read_tests,- testStringList "Checking primitive commute/recommute" primitive_commute_recommute_tests- ]-\end{code}--\chapter{Unit Tester}--The unit tester function is really just a glorified map for functions that-return lists, in which the lists get concatenated (where map would end up-with a list of lists).--\begin{code}-type PatchUnitTest p = p -> [String]-type TwoPatchUnitTest = Patch -> Patch -> [String]--parallel_pair_unit_tester :: TwoPatchUnitTest -> [(Patch:\/:Patch)] -> [String]-parallel_pair_unit_tester _ [] = []-parallel_pair_unit_tester thetest ((p1:\/:p2):ps)- = (thetest p1 p2)++(parallel_pair_unit_tester thetest ps)--pair_unit_tester :: TwoPatchUnitTest -> [(Patch:<Patch)] -> [String]-pair_unit_tester _ [] = []-pair_unit_tester thetest ((p1:<p2):ps)- = (thetest p1 p2)++(pair_unit_tester thetest ps)-\end{code}--\chapter{LCS}--Here are a few quick tests of the shiftBoundaries function.--\begin{code}-show_lcs_tests :: [String]-show_lcs_tests = concatMap check_known_shifts known_shifts-check_known_shifts :: ([Int],[Int],String,String,[Int],[Int])- -> [String]-check_known_shifts (ca, cb, sa, sb, ca', cb') = runST (- do ca_arr <- newListArray (0, length ca) $ toBool (0:ca)- cb_arr <- newListArray (0, length cb) $ toBool (0:cb)- let p_a = listArray (0, length sa) $ B.empty:(toPS sa)- p_b = listArray (0, length sb) $ B.empty:(toPS sb)- shiftBoundaries ca_arr cb_arr p_a 1 1- shiftBoundaries cb_arr ca_arr p_b 1 1- ca_res <- fmap (fromBool . tail) $ getElems ca_arr- cb_res <- fmap (fromBool . tail) $ getElems cb_arr- return $ if ca_res == ca' && cb_res == cb' then []- else ["shiftBoundaries failed on "++sa++" and "++sb++" with "- ++(show (ca,cb))++" expected "++(show (ca', cb'))- ++" got "++(show (ca_res, cb_res))++"\n"])- where toPS = map (\c -> if c == ' ' then B.empty else BC.pack [c])- toBool = map (>0)- fromBool = map (\b -> if b then 1 else 0)--known_shifts :: [([Int],[Int],String,String,[Int],[Int])]-known_shifts =- [([0,0,0],[0,1,0,1,0],"aaa","aaaaa",- [0,0,0],[0,0,0,1,1]),- ([0,1,0],[0,1,1,0],"cd ","c a ",- [0,1,0],[0,1,1,0]),- ([1,0,0,0,0,0,0,0,0],[1,0,0,0,0,0,1,1,1,1,1,0,0,0], "fg{} if{}","dg{} ih{} if{}",- [1,0,0,0,0,0,0,0,0],[1,0,0,0,0,1,1,1,1,1,0,0,0,0]), -- prefer empty line at end- ([0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,1,1,1,1,1,0,0,0], "fg{} if{}","fg{} ih{} if{}",- [0,0,0,0,0,0,0,0,0],[0,0,0,0,0,1,1,1,1,1,0,0,0,0]), -- prefer empty line at end- ([],[1,1],"","aa",[],[1,1]),- ([1,1],[],"aa","",[1,1],[])]---\end{code}--\chapter{Show/Read tests}--This test involves calling ``show'' to print a string describing a patch,-and then using readPatch to read it back in, and making sure the patch we-read in is the same as the original. Useful for making sure that I don't-have any stupid IO bugs.--\begin{code}-show_read_tests :: [String]-show_read_tests = concatMap t_show_read test_patches ++- concatMap t_show_read test_patches_named-primitive_show_read_tests :: [String]-primitive_show_read_tests = concatMap t_show_read primitive_test_patches-t_show_read :: (Eq p, Show p, Patchy p) => PatchUnitTest p-t_show_read p =- case readPatch $ renderPS $ showPatch p of- Just (Sealed p',_) -> if p' == p then []- else ["Failed to read shown: "++(show p)++"\n"]- Nothing -> ["Failed to read at all: "++(show p)++"\n"]--instance MyEq p => Eq (Named p) where- (==) = unsafeCompare-\end{code}--\chapter{Canonization tests}--This is a set of known correct canonizations, to make sure that I'm-canonizing as I ought.--\begin{code}-canonization_tests :: [String]-canonization_tests = concatMap check_known_canon known_canons-check_known_canon :: (Patch, Patch) -> [String]-check_known_canon (p1,p2) =- if (fromPrims $ concatFL $ mapFL_FL canonize $ sortCoalesceFL $ effect p1) == p2- then []- else ["Canonization failed:\n"++show p1++"canonized is\n"- ++show (fromPrims $ concatFL $ mapFL_FL canonize $ sortCoalesceFL $ effect p1 :: Patch)- ++"which is not\n"++show p2]-known_canons :: [(Patch,Patch)]-known_canons =- [(quickhunk 1 "abcde" "ab", quickhunk 3 "cde" ""),- (quickhunk 1 "abcde" "bd", join_patches [quickhunk 1 "a" "",- quickhunk 2 "c" "",- quickhunk 3 "e" ""]),- (join_patches [quickhunk 4 "a" "b",- quickhunk 1 "c" "d"],- join_patches [quickhunk 1 "c" "d",- quickhunk 4 "a" "b"]),- (join_patches [quickhunk 1 "a" "",- quickhunk 1 "" "b"],- quickhunk 1 "a" "b"),- (join_patches [quickhunk 1 "ab" "c",- quickhunk 1 "cd" "e"],- quickhunk 1 "abd" "e"),- (quickhunk 1 "abcde" "cde", quickhunk 1 "ab" ""),- (quickhunk 1 "abcde" "acde", quickhunk 2 "b" "")]-quickhunk :: Int -> String -> String -> Patch-quickhunk l o n = fromPrim $ hunk "test" l (map (\c -> BC.pack [c]) o)- (map (\c -> BC.pack [c]) n)-\end{code}--\chapter{Merge/unmgerge tests}--It should always be true that if two patches can be unmerged, then merging-the resulting patches should give them back again.-\begin{code}-generic_merge_tests :: [String]-generic_merge_tests =- case take 400 [(p1:\/:p2)|- i <- [0..(length test_patches)-1],- p1<-[test_patches!!i],- p2<-drop i test_patches,- check_a_patch $ join_patches [invert p2,p1]] of- merge_pairs -> (parallel_pair_unit_tester t_merge_either_way_valid merge_pairs) ++- (parallel_pair_unit_tester t_merge_swap_merge merge_pairs)-t_merge_either_way_valid :: TwoPatchUnitTest-t_merge_either_way_valid p1 p2 =- case join_patches [p2, quickmerge (p1:\/: p2)] of- combo2 ->- case join_patches [p1, quickmerge (p2:\/: p1)] of- combo1 ->- if not $ check_a_patch $ join_patches [combo1]- then ["oh my combo1 invalid:\n"++show p1++"and...\n"++show p2++show combo1]- else- if check_a_patch $ join_patches [invert combo1, combo2]- then []- else ["merge both ways invalid:\n"++show p1++"and...\n"++show p2++- show combo1++- show combo2]-t_merge_swap_merge :: TwoPatchUnitTest-t_merge_swap_merge p1 p2 =- if (swapp $ merge (p2:\/: p1)) == merge (p1:\/:p2)- then []- else ["Failed to swap merges:\n"++show p1++"and...\n"++show p2- ++"merged:\n"++show (merge (p1:\/:p2))++"\n"- ++"merged and swapped:\n"++show (swapp $ merge (p2:\/: p1))++"\n"]- where swapp (x :/\: y) = y :/\: x--instance Show p => Show (p :/\: p) where- show (x :/\: y) = show x ++ " :/\\: " ++ show y-instance Eq p => Eq (p :/\: p) where- (x :/\: y) == (x' :/\: y') = x == x' && y == y'-\end{code}--\chapter{Commute/recommute tests}--Here we test to see if commuting patch A and patch B and then commuting the-result gives us patch A and patch B again. The set of patches (A,B) is-chosen from the set of all pairs of test patches by selecting those which-commute with one another.--\begin{code}-commute_recommute_tests :: [String]-commute_recommute_tests =- case take 200 [(p2:<p1)|- p1<-test_patches,- p2<-filter (\p->checkseq [p1,p]) test_patches,- commute (p1:>p2) /= Nothing] of- commute_pairs -> pair_unit_tester t_commute_recommute commute_pairs- where checkseq ps = check_a_patch $ join_patches ps-primitive_commute_recommute_tests :: [String]-primitive_commute_recommute_tests =- pair_unit_tester t_commute_recommute- [(p1:<p2)|- p1<-primitive_test_patches,- p2<-primitive_test_patches,- commute (p2:>p1) /= Nothing,- check_a_patch $ join_patches [p2,p1]]-t_commute_recommute :: TwoPatchUnitTest-t_commute_recommute p1 p2 =- if (commute (p2:>p1) >>= commute) == Just (p2:>p1)- then []- else ["Failed to recommute:\n"++(show p2)++(show p1)++- "we saw it as:\n"++show (commute (p2:>p1))++- "\nAnd recommute was:\n"++show (commute (p2:>p1) >>= commute)- ++ "\n"]-\end{code}--\chapter{Commute tests}--Here we provide a set of known interesting commutes.-\begin{code}-commute_tests :: [String]-commute_tests =- concatMap check_known_commute known_commutes++- concatMap check_cant_commute known_cant_commute-check_known_commute :: (Patch:< Patch, Patch:< Patch) -> [String]-check_known_commute (p1:<p2,p2':<p1') =- case commute (p2:>p1) of- Just (p1a:>p2a) ->- if (p2a:< p1a) == (p2':< p1')- then []- else ["Commute gave wrong value!\n"++show p1++"\n"++show p2- ++"should be\n"++show p2'++"\n"++show p1'- ++"but is\n"++show p2a++"\n"++show p1a]- Nothing -> ["Commute failed!\n"++show p1++"\n"++show p2]- ++- case commute (p1':>p2') of- Just (p2a:>p1a) ->- if (p1a:< p2a) == (p1:< p2)- then []- else ["Commute gave wrong value!\n"++show p2a++"\n"++show p1a- ++"should have been\n"++show p2'++"\n"++show p1']- Nothing -> ["Commute failed!\n"++show p2'++"\n"++show p1']-known_commutes :: [(Patch:<Patch,Patch:<Patch)]-known_commutes = [- (testhunk 1 [] ["A"]:<- testhunk 2 [] ["B"],- testhunk 3 [] ["B"]:<- testhunk 1 [] ["A"]),- (fromPrim (tokreplace "test" "A-Za-z_" "old" "new"):<- testhunk 2- ["hello world all that is old is good old_"]- ["I don't like old things"],- testhunk 2- ["hello world all that is new is good old_"]- ["I don't like new things"]:<- fromPrim (tokreplace "test" "A-Za-z_" "old" "new")),- (testhunk 1 ["A"] ["B"]:<- testhunk 2 ["C"] ["D"],- testhunk 2 ["C"] ["D"]:<- testhunk 1 ["A"] ["B"]),- (fromPrim (rmfile "NwNSO"):<- (quickmerge (fromPrim (addfile "hello"):\/:fromPrim (addfile "hello"))),- (quickmerge (fromPrim (addfile "hello"):\/:fromPrim (addfile "hello"))):<- fromPrim (rmfile "NwNSO")),-- (quickmerge (testhunk 3 ["o"] ["n"]:\/:- testhunk 3 ["o"] ["v"]):<- testhunk 1 [] ["a"],- testhunk 1 [] ["a"]:<- quickmerge (testhunk 2 ["o"] ["n"]:\/:- testhunk 2 ["o"] ["v"])),-- (testhunk 1 ["A"] []:<- testhunk 3 ["B"] [],- testhunk 2 ["B"] []:<- testhunk 1 ["A"] []),-- (testhunk 1 ["A"] ["B"]:<- testhunk 2 ["B"] ["C"],- testhunk 2 ["B"] ["C"]:<- testhunk 1 ["A"] ["B"]),-- (testhunk 1 ["A"] ["B"]:<- testhunk 3 ["B"] ["C"],- testhunk 3 ["B"] ["C"]:<- testhunk 1 ["A"] ["B"]),-- (testhunk 1 ["A"] ["B","C"]:<- testhunk 2 ["B"] ["C","D"],- testhunk 3 ["B"] ["C","D"]:<- testhunk 1 ["A"] ["B","C"])]- where testhunk l o n = fromPrim $ hunk "test" l (map BC.pack o) (map BC.pack n)--check_cant_commute :: (Patch:< Patch) -> [String]-check_cant_commute (p1:<p2) =- case commute (p2:>p1) of- Nothing -> []- _ -> [show p1 ++ "\n\n" ++ show p2 ++- "\nArgh, these guys shouldn't commute!\n"]-known_cant_commute :: [(Patch:< Patch)]-known_cant_commute = [- (testhunk 2 ["o"] ["n"]:<- testhunk 1 [] ["A"]),- (testhunk 1 [] ["A"]:<- testhunk 1 ["o"] ["n"]),- (quickmerge (testhunk 2 ["o"] ["n"]:\/:- testhunk 2 ["o"] ["v"]):<- testhunk 1 [] ["a"]),- (fromPrim (hunk "test" 1 ([BC.pack "a"]) ([BC.pack "b"])):<- fromPrim (addfile "test"))]- where testhunk l o n = fromPrim $ hunk "test" l (map BC.pack o) (map BC.pack n)-\end{code}--\chapter{Merge tests}--Here we provide a set of known interesting merges.-\begin{code}-merge_tests :: [String]-merge_tests =- concatMap check_known_merge_equiv known_merge_equivs++- concatMap check_known_merge known_merges-check_known_merge :: (Patch:\/: Patch, Patch:< Patch) -> [String]-check_known_merge (p1:\/:p2,p1':<p2') =- case merge (p1:\/:p2) of- _ :/\: p1a ->- if (p1a:< p2) == (p1':< p2')- then []- else ["Merge gave wrong value!\n"++show p1++show p2- ++"I expected\n"++show p1'++show p2'- ++"but found instead\n"++show p1a]-known_merges :: [(Patch:\/:Patch,Patch:<Patch)]-known_merges = [- (testhunk 2 [BC.pack "c"] [BC.pack "d",BC.pack "e"]:\/:- testhunk 1 [BC.pack "x"] [BC.pack "a",BC.pack "b"],- testhunk 3 [BC.pack "c"] [BC.pack "d",BC.pack "e"]:<- testhunk 1 [BC.pack "x"] [BC.pack "a",BC.pack "b"]),- (testhunk 1 [BC.pack "x"] [BC.pack "a",BC.pack "b"]:\/:- testhunk 2 [BC.pack "c"] [BC.pack "d",BC.pack "e"],- testhunk 1 [BC.pack "x"] [BC.pack "a",BC.pack "b"]:<- testhunk 2 [BC.pack "c"] [BC.pack "d",BC.pack "e"]),- (testhunk 3 [BC.pack "A"] []:\/:- testhunk 1 [BC.pack "B"] [],- testhunk 2 [BC.pack "A"] []:<- testhunk 1 [BC.pack "B"] []),- (fromPrim (rmdir "./test/world"):\/:- fromPrim (hunk "./world" 3 [BC.pack "A"] []),- fromPrim (rmdir "./test/world"):<- fromPrim (hunk "./world" 3 [BC.pack "A"] [])),-- (join_patches [quickhunk 1 "a" "bc",- quickhunk 6 "d" "ef"]:\/:- join_patches [quickhunk 3 "a" "bc",- quickhunk 8 "d" "ef"],- join_patches [quickhunk 1 "a" "bc",- quickhunk 7 "d" "ef"]:<- join_patches [quickhunk 3 "a" "bc",- quickhunk 8 "d" "ef"]),-- (testhunk 1 [BC.pack "A"] [BC.pack "B"]:\/:- testhunk 2 [BC.pack "B"] [BC.pack "C"],- testhunk 1 [BC.pack "A"] [BC.pack "B"]:<- testhunk 2 [BC.pack "B"] [BC.pack "C"]),-- (testhunk 2 [BC.pack "A"] [BC.pack "B",BC.pack "C"]:\/:- testhunk 1 [BC.pack "B"] [BC.pack "C",BC.pack "D"],- testhunk 3 [BC.pack "A"] [BC.pack "B",BC.pack "C"]:<- testhunk 1 [BC.pack "B"] [BC.pack "C",BC.pack "D"])]- where testhunk l o n = fromPrim $ hunk "test" l o n-check_known_merge_equiv :: (Patch:\/:Patch,Patch) -> [String]-check_known_merge_equiv (p1:\/: p2, pe) =- case quickmerge (p1:\/:p2) of- p1' -> if check_a_patch $ join_patches [invert p1, p2, p1', invert pe]- then []- else ["Oh no, merger isn't equivalent...\n"++show p1++"\n"++show p2- ++"in other words\n" ++ show (p1 :\/: p2)- ++"merges as\n" ++ show (merge $ p1 :\/: p2)- ++"merges to\n" ++ show (quickmerge $ p1 :\/: p2)- ++"which is equivalent to\n" ++ show (effect p1')- ++ "should all work out to\n"- ++ show pe]-known_merge_equivs :: [(Patch:\/: Patch, Patch)]-known_merge_equivs = [-- -- The following tests are going to be failed by the- -- Conflictor code as a cleanup.-- --(addfile "test":\/:- -- adddir "test",- -- join_patches [adddir "test",- -- addfile "test-conflict"]),- --(move "silly" "test":\/:- -- adddir "test",- -- join_patches [adddir "test",- -- move "silly" "test-conflict"]),- --(addfile "test":\/:- -- move "old" "test",- -- join_patches [addfile "test",- -- move "old" "test-conflict"]),- --(move "a" "test":\/:- -- move "old" "test",- -- join_patches [move "a" "test",- -- move "old" "test-conflict"]),- (fromPrim (hunk "test" 1 [] [BC.pack "A"]):\/:- fromPrim (hunk "test" 1 [] [BC.pack "B"]),- fromPrim (hunk "test" 1 [] [BC.pack "A", BC.pack "B"])),- (fromPrim (hunk "test" 1 [] [BC.pack "a"]):\/:- fromPrim (hunk "test" 1 [BC.pack "b"] []),- identity),- --hunk "test" 1 [] [BC.pack "v v v v v v v",- -- BC.pack "*************",- -- BC.pack "a",- -- BC.pack "b",- -- BC.pack "^ ^ ^ ^ ^ ^ ^"]),- (quickhunk 4 "a" "":\/:- quickhunk 3 "a" "",- quickhunk 3 "aa" ""),- (join_patches [quickhunk 1 "a" "bc",- quickhunk 6 "d" "ef"]:\/:- join_patches [quickhunk 3 "a" "bc",- quickhunk 8 "d" "ef"],- join_patches [quickhunk 3 "a" "bc",- quickhunk 8 "d" "ef",- quickhunk 1 "a" "bc",- quickhunk 7 "d" "ef"]),- (quickmerge (quickhunk 2 "" "bd":\/:quickhunk 2 "" "a"):\/:- quickmerge (quickhunk 2 "" "c":\/:quickhunk 2 "" "a"),- quickhunk 2 "" "abdc")- ]-\end{code}--It also is useful to verify that it doesn't matter which order we specify-the patches when we merge.--\begin{code}-merge_swap_tests :: [String]-merge_swap_tests =- concat- [check_merge_swap p1 p2 |- p1<-primitive_test_patches,- p2<-primitive_test_patches,- check_a_patch $ join_patches [invert p1,p2]- ]-check_merge_swap :: Patch -> Patch -> [String]-check_merge_swap p1 p2 =- case merge (p2:\/:p1) of- _ :/\: p2' ->- case merge (p1:\/:p2) of- _ :/\: p1' ->- case commute (p1:>p2') of- Just (_:>p1'b) ->- if p1'b /= p1'- then ["Merge swapping problem with...\np1 "++- show p1++"merged with\np2 "++- show p2++"p1' is\np1' "++- show p1'++"p1'b is\np1'b "++- show p1'b- ]- else []- Nothing -> ["Merge commuting problem with...\np1 "++- show p1++"merged with\np2 "++- show p2++"gives\np2' "++- show p2'++"which doesn't commute with p1.\n"- ]-\end{code}--\chapter{Patch test data}--This is where we define the set of patches which we run our tests on. This-should be kept up to date with as many interesting permutations of patch-types as possible.--\begin{code}-test_patches :: [Patch]-test_patches_named :: [Named Patch]-test_patches_addfile :: [Patch]-test_patches_rmfile :: [Patch]-test_patches_hunk :: [Patch]-primitive_test_patches :: [Patch]-test_patches_binary :: [Patch]-test_patches_composite_nocom :: [Patch]-test_patches_composite :: [Patch]-test_patches_two_composite_hunks :: [Patch]-test_patches_composite_hunks :: [Patch]-test_patches_composite_four_hunks :: [Patch]-test_patches_merged :: [Patch]-valid_patches :: [Patch]--test_patches_named = [unsafePerformIO $- namepatch "date is" "patch name" "David Roundy" []- (fromPrim $ addfile "test"),- unsafePerformIO $- namepatch "Sat Oct 19 08:31:13 EDT 2002"- "This is another patch" "David Roundy"- ["This log file has","two lines in it"]- (fromPrim $ rmfile "test")]-test_patches_addfile = map fromPrim- [addfile "test",adddir "test",addfile "test/test"]-test_patches_rmfile = map invert test_patches_addfile-test_patches_hunk =- [fromPrim $ hunk file line old new |- file <- ["test"],- line <- [1,2],- old <- map (map BC.pack) partials,- new <- map (map BC.pack) partials,- old /= new- ]- where partials = [["A"],["B"],[],["B","B2"]]--primitive_test_patches = test_patches_addfile ++- test_patches_rmfile ++- test_patches_hunk ++- [unsafeUnseal.fst.fromJust.readPatch $- BC.pack "move ./test/test ./hello",- unsafeUnseal.fst.fromJust.readPatch $- BC.pack "move ./test ./hello"] ++- test_patches_binary--test_patches_binary =- [fromPrim $ binary "./hello"- (BC.pack $ "agadshhdhdsa75745457574asdgg" ++- "a326424677373735753246463gadshhdhdsaasdgg" ++- "a326424677373735753246463gadshhdhdsaasdgg" ++- "a326424677373735753246463gadshhdhdsaasdgg")- (BC.pack $ "adafjttkykrehhtrththrthrthre" ++- "a326424677373735753246463gadshhdhdsaasdgg" ++- "a326424677373735753246463gadshhdhdsaasdgg" ++- "a326424677373735753246463gadshhdhdsaagg"),- fromPrim $ binary "./hello"- B.empty- (BC.pack "adafjttkykrere")]--test_patches_composite_nocom =- take 50 [join_patches [p1,p2]|- p1<-primitive_test_patches,- p2<-filter (\p->checkseq [p1,p]) primitive_test_patches,- commute (p1:>p2) == Nothing]- where checkseq ps = check_a_patch $ join_patches ps--test_patches_composite =- take 100 [join_patches [p1,p2]|- p1<-primitive_test_patches,- p2<-filter (\p->checkseq [p1,p]) primitive_test_patches,- commute (p1:>p2) /= Nothing,- commute (p1:>p2) /= Just (p2:>p1)]- where checkseq ps = check_a_patch $ join_patches ps--test_patches_two_composite_hunks =- take 100 [join_patches [p1,p2]|- p1<-test_patches_hunk,- p2<-filter (\p->checkseq [p1,p]) test_patches_hunk]- where checkseq ps = check_a_patch $ join_patches ps--test_patches_composite_hunks =- take 100 [join_patches [p1,p2,p3]|- p1<-test_patches_hunk,- p2<-filter (\p->checkseq [p1,p]) test_patches_hunk,- p3<-filter (\p->checkseq [p1,p2,p]) test_patches_hunk]- where checkseq ps = check_a_patch $ join_patches ps--test_patches_composite_four_hunks =- take 100 [join_patches [p1,p2,p3,p4]|- p1<-test_patches_hunk,- p2<-filter (\p->checkseq [p1,p]) test_patches_hunk,- p3<-filter (\p->checkseq [p1,p2,p]) test_patches_hunk,- p4<-filter (\p->checkseq [p1,p2,p3,p]) test_patches_hunk]- where checkseq ps = check_a_patch $ join_patches ps--test_patches_merged =- take 200- [joinPatches $ flattenFL p2+>+flattenFL (quickmerge (p1:\/:p2)) |- p1<-take 10 (drop 15 test_patches_composite_hunks)++primitive_test_patches- ++take 10 (drop 15 test_patches_two_composite_hunks)- ++ take 2 (drop 4 test_patches_composite_four_hunks),- p2<-take 10 test_patches_composite_hunks++primitive_test_patches- ++take 10 test_patches_two_composite_hunks- ++take 2 test_patches_composite_four_hunks,- check_a_patch $ join_patches [invert p1, p2],- commute (p2:>p1) /= Just (p1:>p2)- ]--test_patches = primitive_test_patches ++- test_patches_composite ++- test_patches_composite_nocom ++- test_patches_merged-\end{code}--\chapter{Check patch test}-Check patch is supposed to verify that a patch is valid.--\begin{code}-valid_patches = [(join_patches [quickhunk 4 "a" "b",- quickhunk 1 "c" "d"]),- (join_patches [quickhunk 1 "a" "bc",- quickhunk 1 "b" "d"]),- (join_patches [quickhunk 1 "a" "b",- quickhunk 1 "b" "d"])]++test_patches--test_check :: [String]-test_check = concatMap t_test_check valid_patches-t_test_check :: PatchUnitTest Patch-t_test_check p = if check_a_patch p- then []- else ["Failed the check: "++show p++"\n"]--prop_hex_conversion :: String -> Bool-prop_hex_conversion s =- fromHex2PS (fromPS2Hex $ BC.pack s) == BC.pack s-prop_concatPS :: [String] -> Bool-prop_concatPS ss = concat ss == BC.unpack (B.concat $ map BC.pack ss)---- | Groups a set of tests by giving them the same prefix in their description.--- When this is called as @check_subcommutes subcoms expl@, the prefix for a--- test becomes @"Checking " ++ expl ++ " for subcommute "@.-check_subcommutes :: Testable a => [(String, a)] -> String- -> [Test]-check_subcommutes subcoms expl = map check_subcommute subcoms- where check_subcommute (name, test) =- let testName = "Checking" ++ expl ++ " for subcommute " ++ name- in testProperty testName test-\end{code}--\end{document}--
src/witnesses.hs view
@@ -1,6 +1,6 @@ import Version--- import Preproc -- imports Darcs.Commands.Help--- import Darcs.ArgumentDefaults -- imports Darcs.Commands.Help+import Preproc+import Darcs.ArgumentDefaults import Darcs.Patch.Real import Darcs.Patch.Properties import Darcs.Patch.Bundle@@ -10,45 +10,46 @@ import Darcs.Repository.HashedRepo import Darcs.Resolution import Darcs.Test.Patch.Check+import Darcs.Test.Patch.QuickCheck import Darcs.Repository.Pristine import Darcs.Repository.DarcsRepo import Darcs.Repository.Internal--- import Darcs.Commands.Add+import Darcs.Commands.Add import Darcs.Commands.Annotate--- import Darcs.Commands.AmendRecord -- depends on Darcs.Commands.Record+import Darcs.Commands.AmendRecord import Darcs.Commands.Apply--- import Darcs.Commands.Changes -- does lots of nasty filtering of patch lists--- import Darcs.Commands.Check--- import Darcs.Commands.Convert+import Darcs.Commands.Changes+import Darcs.Commands.Check+import Darcs.Commands.Convert import Darcs.Commands.Diff import Darcs.Commands.Dist--- import Darcs.Commands.Get+import Darcs.Commands.Get import Darcs.Commands.GZCRCs--- import Darcs.Commands.Help -- depends on Darcs.TheCommands+import Darcs.Commands.Help import Darcs.Commands.Init--- import Darcs.Commands.MarkConflicts--- import Darcs.Commands.Move--- import Darcs.Commands.Optimize+import Darcs.Commands.MarkConflicts+import Darcs.Commands.Move+import Darcs.Commands.Optimize import Darcs.Commands.Pull import Darcs.Commands.Push--- import Darcs.Commands.Put--- import Darcs.Commands.Record--- import Darcs.Commands.Remove -- depends on Darcs.Commands.Add--- import Darcs.Commands.Repair--- import Darcs.Commands.Replace--- import Darcs.Commands.Revert--- import Darcs.Commands.Rollback -- depends on Darcs.Commands.Rollback+import Darcs.Commands.Put+import Darcs.Commands.Record+import Darcs.Commands.Remove+import Darcs.Commands.Repair+import Darcs.Commands.Replace+import Darcs.Commands.Revert+import Darcs.Commands.Rollback import Darcs.Commands.Send import Darcs.Commands.SetPref import Darcs.Commands.Show--- import Darcs.Commands.Tag -- depends on Darcs.Commands.Tag+import Darcs.Commands.Tag import Darcs.Commands.TrackDown import Darcs.Commands.TransferMode import Darcs.Commands.Unrevert import Darcs.Commands.Unrecord import Darcs.Commands.WhatsNew --- import Darcs.RunCommand -- imports Darcs.Commands.Help--- import Darcs.TheCommands -- pulls in all other commands+import Darcs.RunCommand+import Darcs.TheCommands main = return ()
tests/amend-record.sh view
@@ -44,6 +44,35 @@ echo "another line" >> foo echo y | darcs amend -a -m new_name -A new_author foo | grep -i 'amending changes' darcs changes --last=1 | grep new_author++# check that normally the date changes when we amend+echo "another line" >> foo+darcs changes --last=1 | head -n 1 > old_date+sleep 1+echo y | darcs amend -a foo -A new_author | grep -i 'amending changes'+darcs changes --last=1 | head -n 1 > new_date+not cmp old_date new_date++# check that --keep-date works+echo "another line" >> foo+darcs changes --last=1 | head -n 1 > old_date+sleep 1+echo y | darcs amend -a foo -A new_author --keep-date | grep -i 'amending changes'+darcs changes --last=1 | head -n 1 > new_date+cmp old_date new_date+ cd .. -rm -rf temp1+# check that the identity changes with --keep-date+darcs get temp1 temp2+cd temp2++echo "another line" >> foo+darcs changes --last=1 | head -n 1 > old_date+echo y | darcs amend -a foo -A new_author --keep-date | grep -i 'amending changes'+darcs pull ../temp1 -a --skip-conflicts | grep -i "Skipping some"++cd ..+++rm -rf temp1 temp2
tests/check.sh view
@@ -28,7 +28,7 @@ rm -rf R # Another script may have left a mess. darcs init --repo R darcs setpref --repo R test 'grep hello f'-not darcs record --repo R -am 'true test'+not darcs record --repo R -am 'true test' --test darcs record --repo R -am 'true test' --no-test touch R/f@@ -36,7 +36,7 @@ darcs tag --repo R -m 'got f?' echo hello > R/f-darcs record --repo R -lam 'hellofoo'+darcs record --repo R -lam 'hellofoo' --test darcs chec --repo R --test rm -rf R # Clean up after ourselves.
tests/dist.sh view
@@ -1,15 +1,35 @@ #!/usr/bin/env bash +# run darcs dist, then extract the resulting archive+# and compare it to the original repository content+ . lib rm -rf temp1 mkdir temp1 cd temp1 darcs init-touch foo-darcs add foo++for (( i=0 ; i < 5; i=i+1 )); do+ echo $i >> file-$i;+ mkdir dir-$i;+ echo $i >> dir-$i/file-$i;+ darcs add file-$i;+ darcs add dir-$i/file-$i+done+ darcs record -a -m add_foo | grep -i "finished recording" darcs dist++mv temp1.tar.gz ..+ cd .. -rm -rf temp1+rm -rf temp1/_darcs+mv temp1 temp_orig++tar xzf temp1.tar.gz++diff -r temp_orig temp1++rm -rf temp_orig temp1
tests/external-resolution.sh view
@@ -26,6 +26,17 @@ echo | darcs pull -a ../temp2 --external-merge 'cp %2 %o' cd .. +grep "Part 2" temp1/child_of_conflict diff -u temp1/child_of_conflict temp2/child_of_conflict++cd temp1+darcs wh+darcs rev -a+echo y | darcs unpull --last 1 -a+echo | darcs pull -a ../temp2 --external-merge 'cp %1 %o'+cd ..++cat temp1/child_of_conflict+grep "Part 1" temp1/child_of_conflict rm -rf temp1 temp2
tests/failed-amend-should-not-break-repo.sh view
@@ -37,17 +37,17 @@ darcs record -a -m 'A' # second patch: mv A to B darcs mv A B-echo y | darcs record -a -m 'move'+darcs record -a -m 'move' # third patch: modify B echo "content" > B-echo y | darcs record -a -m 'add content'+darcs record -a -m 'add content' # amending 'move' results in commuting 'move' patch # to the end for removal. The commute changes the "add content" # patch to modify A instead of B. But the amend is interrupted # because of test failure. Check the consitency after the operation. darcs setpref test false-echo yy | not darcs amend -p move+echo yy | not darcs amend -p move --test darcs check # Note: Amend-record in case of test failure is broken as described in issue1406,
tests/failing-issue1014_identical_patches.sh view
@@ -57,4 +57,6 @@ darcs get ac abc2 cd abc2 darcs pull -a ../bc+darcs changes +test `darcs changes | fgrep -c '* C'` -eq 1
− tests/failing-issue1337_darcs_changes_false_positives.sh
@@ -1,37 +0,0 @@-#!/usr/bin/env bash-## Test for issue1337 - darcs changes shows unrelated patches-## Asking "darcs changes" about an unrecorded file d/f will list the-## patch that creates the parent directory d/ (instead of no patches).-##-## Copyright (C) 2009 Trent W. Buck-##-## 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.--. ../tests/lib--rm -rf temp1-darcs init --repodir temp1-cd temp1-mkdir d-darcs record -lam d d-# We use --match 'touch d/f' instead of simply d/f because the latter-# prints "Changes to d/f:\n" before the count.-test 0 -eq "$(darcs changes --count --match 'touch d/f')"
+ tests/failing-issue1363-mark-conflicts.sh view
@@ -0,0 +1,62 @@+#!/usr/bin/env bash+## Test for issue1363 - mark-conflicts should report that there is a+## conflict to mark if apply/push say there are+##+## Copyright (C) 2010 Eric Kow+## Copyright (C) 2009 Thorkil Naur+##+## 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.+rm -rf R S T # Another script may have left a mess.+darcs init --repo R # Create our test repos.+darcs init --repo S+darcs init --repo T++cd R+ touch f.txt+ darcs add f.txt+ darcs record -am "Empty f.txt"+ echo f.txt contents >> f.txt+ darcs record -am "Contents of f.txt"+cd ..++cd S+ echo yn | darcs pull ../R+ rm f.txt+ darcs record -am 'Remove f.txt in S'+cd ..++cd T+ echo ynn | darcs pull ../R+ rm f.txt+ darcs record -am 'Remove f.txt in T'+ not darcs push -a ../R > log # should fail because of conflict+ grep "There are conflicts" log+cd ..++cd R+ darcs pull -a ../S+ darcs revert -a+ darcs pull -a ../T+ echo y | darcs mark-conflicts > log+ not grep "No conflicts" log+cd ..
tests/failing-issue1461_case_folding.sh view
@@ -25,6 +25,10 @@ ## SOFTWARE. . ../tests/lib # Load some portability helpers.++touch casetest+test -e CASETEST || exit 200+ rm -rf lower upper joint # Another script may have left a mess. mkdir lower upper
+ tests/failing-issue1727_move_current_directory.sh view
@@ -0,0 +1,49 @@+#!/usr/bin/env bash+## Test for issue1727 - darcs move . target' fails as an attempt to+## write an 'invalid pending.+##+## Copyright (C) 2009 Sean Erle Johnson +##+## 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.++. ../tests/lib # Load some portability helpers.+rm -rf R # Another script may have left a mess.++# Create repository R+darcs init --repo R # Create the test repo.+cd R++mkdir d # Change the working tree.++# darcs move empty current directory to existing directory d+darcs move . d++# darcs move empty current directory to non-existing directory e+darcs move . e++# Make file to be copied+echo 'main = putStrLn "Hello World"' > hello.hs++# darcs move non-empty current directory to existing directory d+darcs move . d++# darcs move non-empty current directory to non-existing directory e+darcs move . e
+ tests/failing-issue1737-move_args.sh view
@@ -0,0 +1,38 @@+#!/usr/bin/env bash+## Test for issue1737 - command line filtering on darcs move+## yields suprising results with shell globs+##+## Copyright (C) 2010 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.+rm -rf R # Another script may have left a mess.+darcs init --repo R # Create our test repos.++cd R+mkdir d+touch d.txt+darcs record -lam 'Add d and d.txt'+darcs move d d.txt d 2> log || : # this can happen if eg. you darcs move d* d+# d.txt isn't a directory; we should probably just say that you+# can't move a directory on to itself+not grep "target directory d.txt" log
+ tests/failing-issue1740-mv-dir.sh view
@@ -0,0 +1,36 @@+#!/usr/bin/env bash+## Test for issue1740 - darcs mv on directories should work after the fact+##+## Copyright (C) 2009 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.+rm -rf R # Another script may have left a mess.+darcs init --repo R # Create our test repos.++cd R+mkdir d+echo 'Example content.' > d/f+darcs record -lam 'Add d/f'+mv d d2+darcs mv d d2 # oops, I meant to darcs mv that+darcs what | grep move ./d ./d2
+ tests/failing-issue1845-paths-working-copy.sh view
@@ -0,0 +1,37 @@+#!/usr/bin/env bash+## Test for issue1845 - darcs wants file paths from root of working copy+##+## Copyright (C) 2010 Guillaume Hoffmann+##+## 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.+rm -rf R S # Another script may have left a mess.+darcs init --repo R # Create our test repos.+cd R+mkdir subdir+touch subdir/subfile+darcs add subdir subdir/subfile+darcs record -am"add subdir and subfile"+cd subdir+rm subfile+darcs record subfile -am"delete file in subdirectory" # fails because darcs wants subdir/subfile +
+ tests/failing-issue1848-rollback-p.sh view
@@ -0,0 +1,36 @@+#!/usr/bin/env bash+## Test for issue1848 - interactive selection of primitive patches+## should still work with rollback -p+##+## Copyright (C) 2010 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.+rm -rf R S # Another script may have left a mess.+darcs init --repo R # Create our test repos.++cd R+echo 'f' > f+echo 'g' > g+darcs record -lam 'Add f and g'+echo ynq | darcs rollback -p 'f and g'+cd ..
+ tests/failing-issue1909-unrecord-O-misses-tag.sh view
@@ -0,0 +1,20 @@+#!/usr/bin/env bash+## issue1909: unrecord -O in tagged repo makes a busted bundle++. lib++rm -rf R+mkdir R+darcs init --repo R+echo a > R/a+darcs rec -lam a --repo R --ignore-times+darcs tag -m T --repo R+echo b > R/a+darcs rec -lam b --repo R --ignore-times+echo c > R/a+darcs rec -lam c --repo R --ignore-times++darcs unpull -p c -a --repo R -O+cat c.dpatch+grep '^\[b' c.dpatch+grep TAG c.dpatch
tests/filepath.sh view
@@ -103,7 +103,7 @@ # can handle .. path cd temp3-darcs pull ../temp2 -p1 --all | grep -i 'Finished pulling'+darcs pull ../temp2 --set-default -p1 --all | grep -i 'Finished pulling' darcs pull --dry-run | grep hello2 cd a/b #[issue268] repodir with subdir
tests/get.sh view
@@ -15,4 +15,5 @@ cd .. rm -rf temp2 darcs get temp1 --context="${abs_to_context}" temp2-rm -rf temp1 temp2+darcs changes --context --repo temp2 > repo2_context+diff -u "${abs_to_context}" repo2_context
+ tests/issue121.sh view
@@ -0,0 +1,45 @@+#!/usr/bin/env bash+## Test for issue121 - amend-record --ask-deps+##+## Copyright (C) 2009 Ganesh Sittampalam+##+## 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.++. ../tests/lib # Load some portability helpers.+rm -rf R+darcs init --repo R # Create our test repos.++cd R+touch a+darcs add a+darcs rec --ignore-times -am 'add a'+(echo '1' ; echo '1' ; echo '1') > a+darcs rec --ignore-times -am 'patch X'+(echo '2' ; echo '1' ; echo '1') > a+darcs rec --ignore-times -am 'patch Y'+(echo '2' ; echo '1' ; echo '2') > a+darcs rec --ignore-times -am 'patch Z'++darcs obliterate --dry-run --patch 'patch Y' | not grep 'patch Z'++echo 'yy' | darcs amend-rec --ask-deps++darcs obliterate --dry-run --patch 'patch Y' | grep 'patch Z'
+ tests/issue1210-no-global-cache-in-sources.sh view
@@ -0,0 +1,36 @@+#!/usr/bin/env bash+## Test for issue1210 - 'global cache gets recorded in _darcs/prefs/sources'+##+## Copyright (C) 2010 Adolfo Builes+##+## 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.++set -ev++. lib++cacheDir=$HOME/.darcs/cache++rm -rf R S+darcs init --repo R+darcs get R S+not grep "$cacheDir" S/_darcs/prefs/sources+not grep "cache:" S/_darcs/prefs/sources
+ tests/issue1232_convert_forgets_prefs.sh view
@@ -0,0 +1,62 @@+#!/usr/bin/env bash+## Test for issue1232 - When converting a repo to darcs2 format,+## the prefs file isn't copied.+##+## Copyright (C) 2010 Dino Morelli+##+## 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+++# Test set-up where src repo DOES NOT already have a+# _darcs/prefs/prefs file++# Another script may have left a mess+rm -rf R S++# Create source repo+darcs init --old-fashioned-inventory --repo R++# Perform the d1 to d2 conversion+echo "I understand the consequences of my action" | darcs convert R S++# Check that the new repo is d2+test -f S/_darcs/hashed_inventory+++# Test set-up where src repo DOES have a _darcs/prefs/prefs file++# Another script may have left a mess+rm -rf R S++# Create source repo+darcs init --old-fashioned-inventory --repo R++# Do something easy that will create a prefs file+darcs setpref test 'true' --repo R++# Perform the d1 to d2 conversion+echo "I understand the consequences of my action" | darcs convert R S++# Check that the prefs file was copied over+prefsPath="_darcs/prefs/prefs"+diff R/$prefsPath S/$prefsPath
+ tests/issue1277-repo-format.sh view
@@ -0,0 +1,46 @@+#!/usr/bin/env bash+## Test for issue1277 - repository format errors should be reported+## correctly (ie. not as some totally unrelated error)+##+## Copyright (C) 2010 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.+rm -rf R # Another script may have left a mess.+darcs init --repo R # Create our test repos.+cd R+ darcs init --repo R2 # Protect the darcs darcs repo with R+ cd R2+ echo impossible >> _darcs/format+ echo 'Example content.' > f+ not darcs add f > log 2>&1+ grep "Can't understand repository format" log+ not darcs whatsnew > log 2>&1+ grep "Can't understand repository format" log+ not darcs init > log 2>&1+ grep "You may not run this command in a repository" log+ grep "Can't understand repository format" log+ cd ..+ not darcs whatsnew --repodir R2 > log 2>&1+ grep "R2 looks like a repository directory," log+ grep "Can't understand repository format" log+cd ..
+ tests/issue1290-diff-index.sh view
@@ -0,0 +1,22 @@+#!/usr/bin/env bash+## Test for issue1290 - darcs diff --index+##+## Public domain - 2010 Eric Kow++. lib # Load some portability helpers.+rm -rf R # Another script may have left a mess.+darcs init --repo R # Create our test repos.++cd R+ echo '1' > f+ darcs record -lam 'one'+ echo '2' > f+ darcs record -lam 'two'+ echo '3' > f+ darcs record -lam 'three'+ echo '4' > f+ darcs record -lam 'four'+ darcs diff --from-patch one --to-patch two > d1+ darcs diff --index=3-4 > d2 # the numbers go backwards+ diff -q d1 d2+cd ..
+ tests/issue1337_darcs_changes_false_positives.sh view
@@ -0,0 +1,37 @@+#!/usr/bin/env bash+## Test for issue1337 - darcs changes shows unrelated patches+## Asking "darcs changes" about an unrecorded file d/f will list the+## patch that creates the parent directory d/ (instead of no patches).+##+## Copyright (C) 2009 Trent W. Buck+##+## 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.++. ../tests/lib++rm -rf temp1+darcs init --repodir temp1+cd temp1+mkdir d+darcs record -lam d d+# We use --match 'touch d/f' instead of simply d/f because the latter+# prints "Changes to d/f:\n" before the count.+test 0 -eq "$(darcs changes --count --match 'touch d/f')"
+ tests/issue1427_apply_gz.sh view
@@ -0,0 +1,41 @@+#!/usr/bin/env bash+set -ev++rm -rf temp1 temp2+mkdir temp1 temp2++cd temp2+darcs init++cd ../temp1+darcs init+touch foo bar+darcs add foo bar+darcs record -a -m add_foo_bar -A x+darcs mv foo zig+darcs mv bar foo+darcs mv zig bar+darcs record -a -m swap_foo_bar -A x+darcs send --author=me --output=funpatch --dont-sign -a ../temp2++gzip funpatch++cd ../temp2+darcs apply ../temp1/funpatch.gz+cd ..+cmp temp1/bar temp2/bar+rm -rf temp2++mkdir temp2+cd temp2+darcs init+darcs apply ../temp1/funpatch.gz+## Also test that "darcs apply" can accept a patch on stdin.+darcs obl -a+darcs apply < ../temp1/funpatch.gz+cd ..+cmp temp1/bar temp2/bar+++rm -rf temp1 temp2+
+ tests/issue1473.sh view
@@ -0,0 +1,42 @@+#!/usr/bin/env bash+## Test for issue1473 - check that darcs annotate works with and without+## repodir and with "." argument. It should fail with the empty string as+## a single argument and without any arguments.+##+## 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.++rm -rf R S # Another script may have left a mess.+darcs init --repo R # Create our test repos.++cd R+echo 'Example content.' > f+darcs record -lam 'Added f.'+darcs annotate .+darcs annotate f+not darcs annotate+not darcs annotate ''++cd ..+darcs annotate --repodir=R .+darcs annotate --repodir=R f+not darcs annotate --repodir=R+not darcs annotate --repodir=R ''
tests/issue1618-amend-preserve-logfile.sh view
@@ -32,7 +32,7 @@ darcs setpref test false darcs record -am foo --no-test export DARCS_EDITOR="echo 'new log' > "-echo y | not darcs amend -p foo --edit-long-comment 2> out+echo y | not darcs amend -p foo --edit-long-comment --test 2> out # the msg has the format: "Logfile left in filenamehere." LOGFILE=`grep "Logfile left in" out | sed "s/Logfile left in //" | sed s/.$//`
tests/issue1620-record-lies-about-leaving-logfile.sh view
@@ -30,7 +30,7 @@ export DARCS_EDITOR="echo 'a log' > " darcs init darcs setpref test false-echo yy | not darcs record -m foo -a --edit-long-comment 2> out+echo yy | not darcs record -m foo -a --edit-long-comment --test 2> out # the msg has the format: "Logfile left in filenamehere." LOGFILE=`grep "Logfile left in" out | sed "s/Logfile left in //" | sed s/.$//`
+ tests/issue1726_darcs_always-boring.sh view
@@ -0,0 +1,73 @@+#!/usr/bin/env bash+## Test for issue1726 - Files whose names start with "_darcs" are considered+## boring, even if they don't match anything in the boring file, and even if+## you pass --boring to the command.+##+## Copyright (C) 2009 Daniel Dickison <danieldickison@gmail.com>+##+## 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.++. ../tests/lib # Load some portability helpers.+rm -rf R # Another script may have left a mess.+darcs init --repo R # Create our test repos.++cd R+++## First test expected failures with actual _darcs files/directories++function bad_add {+ filename="$1"+ touch "$filename"+ not darcs whatsnew -ls --boring+ not darcs whatsnew -ls+ not darcs add --boring "$filename"+}++touch _darcs/foo+bad_add _darcs+bad_add _darcs/+bad_add _darcs/foo+bad_add ./_darcs+bad_add ./_darcs/+bad_add ./_darcs/foo+bad_add "$PWD/_darcs"+bad_add "$PWD/_darcs/"+bad_add "$PWD/_darcs/foo"+bad_add "../${PWD##*/}/_darcs"+bad_add "../${PWD##*/}/_darcs/"+bad_add "../${PWD##*/}/_darcs/foo"++++## Then test expected successes with files that aren't in _darcs++# Passing --boring should definitely succeed.+touch _darcsfoo+darcs whatsnew -ls --boring+darcs add --boring _darcsfoo+darcs record -am 'add _darcsfoo' _darcsfoo++# Without --boring, this tests the default boring file.+touch _darcsbar+darcs whatsnew -ls+darcs add _darcsbar+darcs record -am 'add _darcsbar' _darcsbar
+ tests/issue1739-escape-multibyte-chars-correctly.sh view
@@ -0,0 +1,71 @@+#!/usr/bin/env bash+## Test for issue1739 - "Char.intToDigit: not a digit" in darcs changes +##+## Copyright (C) 2010 Reinier Lamers+##+## 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.++. ../tests/lib++# First, try to see if character set is UTF-8. If we can't find out or if it+# isn't, we skip this test.+if ! which locale ; then+ echo "no locale command"+ exit 200 # skip test+fi++charmap=`locale charmap`+if [ $? -ne 0 ]; then+ echo "couldn't determine locale character set"+ exit 200 # skip test+fi++if [ "$charmap" != "UTF-8" ]; then+ echo "locale character set is not UTF-8, skipping"+ exit 200+fi++# we want escaping, otherwise output of non-ASCII characters is unreliable+export DARCS_DONT_ESCAPE_ANYTHING=0++rm -rf R+mkdir R+cd R+darcs init++echo garbelbolf > aargh+darcs add aargh+echo -e '\xe2\x80\x9e\x54\x61\x20\x4d\xc3\xa8\x72\x65\xe2\x80\x9d' > message.txt+darcs record --logfile=message.txt -A 'Petra Testa van der Test <test@example.com>' -a > rec.txt+darcs changes > log.txt+cat log.txt+grep '<U+201E>' log.txt+grep '<U+201D>' log.txt+grep '<U+00E8>' log.txt++# locale should not matter+LC_ALL=C darcs changes > log.txt+grep '<U+201E>' log.txt+grep '<U+201D>' log.txt+grep '<U+00E8>' log.txt++cd ..+
+ tests/issue1749-rmdir.sh view
@@ -0,0 +1,42 @@+#!/usr/bin/env bash+## Test for issueXXX - darcs remove <dir> corrupts the patch sequence+##+## Copyright (C) 2009 Eric Kow <kowey@darcs.net>+##+## 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.++. ../tests/lib # Load some portability helpers.+rm -rf R # Another script may have left a mess.+darcs init --repo R+cd R++mkdir dir+touch dir/file++darcs add dir/file # adds dir too (which is fine)+darcs rec -a -m"add dir and file"++darcs remove dir+darcs rec -a -m"remove dir" # removed dir but didn't remove file++darcs obliterate -a --patch "remove dir"+darcs check+
+ tests/issue1756_moves_index.sh view
@@ -0,0 +1,50 @@+#!/usr/bin/env bash+## Test for issue1756 - moving files between directories breaks index+##+## Copyright (C) 2010 Petr Rockai+##+## 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.+rm -rf R S # Another script may have left a mess.+darcs init --repo R # Create our test repos.++cd R+mkdir d e # Change the working tree.+echo 'a' > d/a+echo 'b' > d/b+echo 'c' > e/c+darcs record -lam '.'+darcs mv d/a e/+darcs check --no-ignore-times+cd ..+rm -rf R++darcs init --repo R+cd R+mkdir d e # Change the working tree.+echo 'a' > d/a+echo 'b' > e/b+darcs record -lam '.'+darcs mv d/a e/+darcs check --no-ignore-times+cd ..+rm -rf R
+ tests/issue1763-pull-fails-on-non-ascii-filenames.sh view
@@ -0,0 +1,77 @@+#!/usr/bin/env bash+## Test for issue1763 - When you pull in a conflicting hunk+## patch to a file with a non-ASCII name, and then pull from the same+## repo again, darcs crashes.+##+## Copyright (C) 2010 Reinier Lamers+##+## 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++abort_windows # FIXME! We should figure out what's going on here++rm -rf R S+darcs init --repo R++export LC_ALL=C++function check_consistent_filename {+ count=`darcs changes -v | grep 'hunk .*\.lisp' | sed -e 's/.*hunk //' -e 's/.lisp.*//' | sort | uniq | wc -l`+ test $count -eq 1+}++# Set up a repo with 3 patches to a non-ASCII-named file+cd R+touch kitöltés.lisp+darcs rec -l -a -m "Add"+echo hi >> kitöltés.lisp+darcs record -a -m "First edit"+cd ..++rm -rf S S2 S3+darcs get R S+darcs get R S2+darcs get R S3++cd R+echo hi >> kitöltés.lisp+darcs record -a -m "Second edit"+cd ..++# From another repo, pull the first two, edit, pull the third to get a+# conflict, pull again to get the crash+cd S+echo hello >> kitöltés.lisp+darcs record -a -m "My edit"+darcs pull -a ../R+darcs pull -a ../R+check_consistent_filename+cd ..++# duplicates+cd S2+echo hi >> kitöltés.lisp+darcs record -a -m "My duplicate edit"+darcs pull -a ../R+darcs pull -a ../R+check_consistent_filename+cd ..
+ tests/issue1825-remove-pending.sh view
@@ -0,0 +1,43 @@+#!/usr/bin/env bash+## Test for issue1825 - buggy pending when reverting a removed+## directory and file.+##+## Copyright (C) 2010 Ferenc Wagner+## Copyright (C) 2010 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.+rm -rf R # Another script may have left a mess.+darcs init --repo R # Create our test repos.++cd R+mkdir -p a+echo foo >a/b+darcs add -r a+darcs rec -am "add a/b"+rm -r a+darcs add # NB. not darcs add per se, but any operation which+ # causes the pending patch to be detected (eg. remove,+ # record + unrecord)++# We have a legitimate pending patch so far. Now what?+darcs revert -a a/b
+ tests/issue183_mv_order.sh view
@@ -0,0 +1,45 @@+#!/usr/bin/env bash+## Test for issue183 - mv and other patches should be in replayable+## order+##+## Copyright (C) 2010 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.+rm -rf R S # Another script may have left a mess.+darcs init --repo R # Create our test repos.++cd R+touch f+darcs add f+darcs record -am 'Create f'+mkdir d+darcs add d+darcs mv f d+darcs record -am 'Create d and mv f in it'+cat > expected << EOF+ A ./d/+ ./f -> ./d/f+EOF+darcs changes -s -p 'Create d' | grep "d/" > log+diff -q expected log+cd ..
+ tests/issue1845-record-removed.sh view
@@ -0,0 +1,16 @@+#!/usr/bin/env bash+## Test for issue1871 - darcs record f, for f a removed file should work+##+## Public domain - 2010 Petr Rockai++. lib # Load some portability helpers.+rm -rf R # Another script may have left a mess.+darcs init --repo R # Create our test repos.++cd R+echo 'Example content.' > f+darcs rec -lam "first"+rm -f f+echo n | darcs record f 2>&1 | tee log+not grep "None of the files" log+cd ..
+ tests/issue1857-pristine-conversion.sh view
@@ -0,0 +1,37 @@+#!/usr/bin/env bash+## Test for issue1857 - upgrading the pristine format should either+## work or have no effect) even it happens before a failing operation+##+## Copyright (C) 2010 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.+rm -rf minimal-darcs-2.4+tar zxf ../tests/repos/minimal-darcs-2.4.tgz++cd minimal-darcs-2.4+darcs check+darcs setpref test false+echo 'hi' > README+not darcs record -a -m argh --test+darcs check+cd ..
+ tests/issue1860-incomplete-pristine.sh view
@@ -0,0 +1,45 @@+#!/usr/bin/env bash+## Copyright (C) 2010 Petr Rockai+##+## 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.+rm -rf R S # Another script may have left a mess.+darcs init --repo R # Create our test repos.++cd R+mkdir tools+darcs rec -lam "add tools"+mkdir tools/cgi+echo bar > tools/cgi/README+darcs rec -lam "add cgi"+darcs move tools contrib+darcs rec -lam "rename tools/ to contrib/"+rm -rf contrib/cgi+darcs rec -lam "drop cgi"+cd ..++darcs get R S+cd S+darcs show pristine+darcs unpull --last 2 -a+darcs show pristine+cd ..
+ tests/issue1865-get-context.sh view
@@ -0,0 +1,20 @@+#!/usr/bin/env bash++. lib++rm -rf temp1 temp2+mkdir temp1+cd temp1+darcs init+touch t.t+darcs add t.t+darcs record -am "initial add"+darcs tag -m tt+echo x > x+darcs rec -lam "x" x+darcs changes --context > my_context+abs_to_context="$(pwd)/my_context"+cd ..+darcs get temp1 --context="${abs_to_context}" temp2+darcs changes --context --repo temp2 > repo2_context+diff -u "${abs_to_context}" repo2_context
+ tests/issue1871-record-dot.sh view
@@ -0,0 +1,18 @@+#!/usr/bin/env bash+## Test for issue1871 - darcs record . should work for tracked changes+## in a subdirectory even if the subdirectory itself is not known yet.+##+## Public domain - 2010 Eric Kow++. lib # Load some portability helpers.+rm -rf R # Another script may have left a mess.+darcs init --repo R # Create our test repos.++cd R+mkdir d # Change the working tree.+echo 'Example content.' > d/f+darcs add d/f+echo n | darcs record+echo n | darcs record . > log+not grep "None of the files" log+cd ..
+ tests/issue1873-apply-failed-to-read-patch.sh view
@@ -0,0 +1,55 @@+#!/usr/bin/env bash+## Test for issue1873 - apply should complain about the right+## patches if it says some are missing+##+## Copyright (C) 2010 Petr Rockai+##+## 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 S+mkdir R+darcs init --repo R++cd R+echo a > a+darcs rec -lam a+echo b > a+darcs rec -lam b+echo x > x+darcs rec -lam x+echo c > a+darcs rec -lam c+echo y > y+darcs rec -lam y+echo d > a+darcs rec -lam d+cd ..++darcs get R S+darcs unpull -p x -a --repo R+darcs send -p x -a --repo S -o R/x.dpatch+darcs unpull -p y -a --repo R+not darcs apply --repo R R/x.dpatch 2>&1 | tee log++not grep '^ \* d' log # does not complain about an unrelated patch+ grep '^ \* y' log # complains about the offending one instead
+ tests/issue1875-honor-no-set-default.sh view
@@ -0,0 +1,39 @@+#!/usr/bin/env bash+## Test for issue1875 - corner cases in which darcs may accidentally+## set default even though it's not supposed to+##+## Copyright (C) 2010 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.+rm -rf R S # Another script may have left a mess.+darcs init --repo R # Create our test repos.+darcs get R S --no-set-default+not find S/_darcs/prefs/defaultrepo+rm -rf S++darcs init --repo S+cd S+darcs push ../R --dry-run+not grep '/R$' _darcs/prefs/defaultrepo+darcs push ../R+cd ..
+ tests/issue1877_noisy_xml_output.sh view
@@ -0,0 +1,47 @@+#!/usr/bin/env bash+## Test for issue1877 - pull --dry-run --xml-output is noisy+##+## Copyright (C) 2010 Lele Gaifax <lele@nautilus.homeip.net>+##+## 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.+rm -rf R S # Another script may have left a mess.+darcs init --repo R # Create our test repos.+darcs init --repo S++cd R+echo 'Example content.' > foo+darcs record -lam 'Add foo.'+cd ..++cd S++# This does the right thing... at least until the second command is executed+darcs pull --dry-run --xml-output 2>&1 ../R | not grep "Would pull"++# This does not, never+darcs pull --summary --dry-run --xml-output 2>&1 ../R | not grep "Would pull"++# From now on, this fails too!+darcs pull --dry-run --xml-output 2>&1 ../R | not grep "Would pull"++cd ..
+ tests/issue1879-same-patchinfo-uncommon.sh view
@@ -0,0 +1,38 @@+#!/usr/bin/env bash+## Test for issue1879 - we should at least notice that when a patch claims+## to have the same identity (patchinfo) as one of ours, then it should not+## depend on anything we don't have.+##+## Public domain - 2010 Eric Kow++. lib # Load some portability helpers.+rm -rf R S # Another script may have left a mess.+darcs init --repo R # Create our test repos.+darcs init --repo S++cd R+touch x1+darcs add x1+darcs record -am 'x1'+darcs changes --context > ctx+echo hello > f+echo world > x1+darcs add f+darcs record -am 'hello world'+darcs send -a --context ctx -o foo.dpatch ../S+cd ..++cd S+touch x2+darcs add x2+darcs record -am 'x2'+darcs changes --context > ctx+# create an evil wrong patch+sed -e '/Context:/,$d' -e 's/x1/x2/g' ../R/foo.dpatch > foo.dpatch+cat ctx >> foo.dpatch+darcs apply foo.dpatch+cd ..++cd R+not darcs pull -a ../S > log 2>&1+cd ..
+ tests/issue1888-changes-context.sh view
@@ -0,0 +1,43 @@+#!/usr/bin/env bash+## Test for issue1888 - changes --context is broken when topmost patch is a+## clean tag.++## Copyright (C) 2010 Petr Rockai+##+## 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.+rm -rf R # Another script may have left a mess.+darcs init --repo R # Create our test repos.++cd R++echo a > a ; darcs rec -lam "patch_a"+darcs changes --context | grep patch_a++darcs tag -m "tag_a"+darcs changes --context | not grep patch_a+darcs changes --context | grep tag_a++echo b > a; darcs rec -lam "patch_b"+darcs changes --context | not grep patch_a+darcs changes --context | grep tag_a+darcs changes --context | grep patch_b
+ tests/issue1898-set-default-notification.sh view
@@ -0,0 +1,54 @@+#!/usr/bin/env bash+## Test for issue1898 - set-default mechanism+##+## Copyright (C) 2010 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.+rm -rf R0 R1 R2 S # Another script may have left a mess.+darcs init --repo R0 # Create our test repos.++darcs get R0 R1+darcs get R0 R2+darcs get R0 S++cd S+# default to no-set-default+darcs push ../R1 > log+grep '/R0$' _darcs/prefs/defaultrepo+# notification when using no-set-default+grep "set-default" log+# set-default works+darcs push ../R1 --set-default > log+grep '/R1$' _darcs/prefs/defaultrepo+# no notification when already using --set-default+not grep "set-default" log+# no notification when already pushing to the default repo+darcs push > log+not grep "set-default" log+# no notification when it's just the --remote-repo+darcs push --remote-repo ../R1 > log+not grep "set-default" log+# but... notification still works in presence of remote-repo+darcs push --remote-repo ../R1 ../R2 > log+grep "set-default" log+cd ..
+ tests/issue1913-diffing.sh view
@@ -0,0 +1,39 @@+#!/usr/bin/env bash+## Test for issue1913 - test for directory diffing+##+## Copyright (C) 2010 Ian Lynagh+##+## 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.+rm -rf R # Another script may have left a mess.+darcs init --repo R # Create our test repos.++cd R+mkdir foo+touch foo/foofile+darcs rec -l -a -m "foo patch"+mkdir bar+touch bar/barfile+rm -r foo+darcs rec -l -a -m "bar patch"+not darcs whatsnew -l+cd ..
+ tests/issue1951-add-outside-repo.sh view
@@ -0,0 +1,151 @@+#!/usr/bin/env bash+## Test for issue1951 - darcs should refuse adds from outside the+## current repository+##+## Copyright (C) 2010 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.++rm -rf R R2 R3 R4 R5 R6 R7 R8 R9 R10+darcs init --repo R # Create our test repos.+darcs init --repo R2+darcs init --repo R3+darcs init --repo R4+darcs init --repo R5+darcs init --repo R6+darcs init --repo R7+darcs init --repo R8+darcs init --repo R9+darcs init --repo R10++# test if adding files outside the repository fails++OUTSIDE=`pwd`++cd R+echo 'Example content.' > f+echo 'Bad' > ../junk-for-issue1951+darcs add f+not darcs add ../junk-for-issue1951+not darcs add $OUTSIDE/junk-for-issue1951+darcs whatsnew > log+not grep junk-for-issue1951 log+cd ..++# test adding a file that:+# * is in a subdirectory of the repository root+# * is referred to with a path relative to the cwd, when the cwd is the+# directory that the file is in+cd R2+mkdir subdir+cd subdir+touch f+darcs add f+darcs whatsnew > log+grep 'subdir/f' log+cd ../..++# same as above, but now the relative path is valid both from the cwd and from+# the repository root. Darcs should add the file in the cwd, not the one in the+# repository root+cd R3+touch f+mkdir subdir+cd subdir+touch f+darcs add f+darcs whatsnew > log+grep 'subdir/f' log+cd ../..++# now test that adding fails on a file that+# * is in the repository root+# * is referred to with a path relative to the repository root, when the cwd is+# not the repository root+cd R4+touch myfilename+mkdir subdir+cd subdir+not darcs add myfilename+cd ../..++# test adding a file by relative path from the repo root, when the cwd is+# outside the repo+# It may seem counterintuitive that this succeeds and the cases above and below+# do not, but that's the way it is. We use this feature ourselves in our test+# suite.++touch R5/myfilename+darcs add --repo R5 myfilename+darcs whatsnew --repo R5 > log+grep 'myfilename' log+not grep '\.\./myfilename' log++# The case below makes the R4 case (of using a repo-root-relative path in a+# subdir of the repo) look even more like the R5 case (of using a+# repo-root-relative path outside the repo) by usign the --repo flag. It still+# failed on darcs 2.4.++cd R6+touch myfilename+mkdir subdir+cd subdir+not darcs add --repo .. myfilename+cd ../..++# Test adding a file by relative path from the repo root, when the cwd is+# outside the repo, and the relative path also exists from the cwd++touch myfilename+touch R7/myfilename+darcs add --repo R7 myfilename+darcs whatsnew --repo R7 > log+grep 'myfilename' log+not grep '\.\.myfilename' log++# Like the R4 case: try to use a path relative to the repo root from a cwd that+# is a subdir of the repo root. In this case the path relative to the repo root+# also includes a directory however.++cd R8+mkdir subdir1+mkdir subdir2+touch subdir2/myfilename+cd subdir1+not darcs add subdir2/myfilename+cd ../..++# Try adding a non-repository file using a non-existent repo subdirectory name+# followed by two ..'s+touch myfilename+cd R9+not darcs add nonexistentsubdir/../../myfilename+cd ..++# Try adding a non-repository file using an existing repo subdirectory name+# followed by two ..'s+touch myfilename+cd R10+mkdir subdir+not darcs add subdir/../../myfilename+cd ..
tests/issue538.sh view
@@ -23,7 +23,7 @@ make_repo_with_test touch blaat darcs add blaat-if darcs record --set-scripts-executable -A test@test -am blaat ; then+if darcs record --set-scripts-executable -A test@test -am blaat --test; then echo "ok 1" else echo "not ok 1 recording second patch failed (because test failed?)"@@ -36,7 +36,7 @@ make_repo_with_test touch blaat darcs add blaat-if darcs record --dont-set-scripts-executable -A test@test -am blaat ; then+if darcs record --dont-set-scripts-executable -A test@test -am blaat --test; then echo "not ok 2 recording second patch succeeded though test script should not be executable" exit 1 else@@ -49,7 +49,7 @@ make_repo_with_test touch blaat darcs add blaat-if echo y | darcs amend-record --set-scripts-executable -A test@test -a ; then+if echo y | darcs amend-record --set-scripts-executable -A test@test -a --test; then echo "ok 3" else echo "not ok 3 amending patch failed (because test failed?)"@@ -62,7 +62,7 @@ make_repo_with_test touch blaat darcs add blaat-if echo y | darcs amend-record --dont-set-scripts-executable -A test@test -a /dev/null ; then+if echo y | darcs amend-record --dont-set-scripts-executable -A test@test -a /dev/null --test; then echo "not ok 4 amending patch succeeded even though --dont-set-scripts-executable specified" exit 1 else
tests/lib view
@@ -1,5 +1,5 @@ # This is a -*- sh -*- library.-set -vex+set -vex -o pipefail ## I would use the builtin !, but that has the wrong semantics. not () { "$@" && exit 1 || :; }@@ -25,11 +25,8 @@ exit 200 # skip test fi - latin9_locale=`locale -a | grep @euro | head -n 1`- if [ -z "$latin9_locale" ]; then- echo "no latin9 locale found"- exit 200 # skip, we can't switch away from UTF-8- fi+ latin9_locale=`locale -a | grep @euro | head -n 1` || exit 200+ test -n "$latin9_locale" || exit 200 echo "Using locale $latin9_locale" export LC_ALL=$latin9_locale
tests/match-date.sh view
@@ -5,6 +5,17 @@ rm -rf temp1 # Some tests for the '--match' flag, specifically the date-matching +fmt_offset() {+ if date -d "now${1}days" >& /dev/null; then+ date -d "now${1}days" +"%Y%m%d"+ elif date -v ${1}d >& /dev/null; then+ date -v ${1}d +"%Y%m%d"+ else+ echo "Can't do date arithmetic on this system :(" >&2+ return 1+ fi+}+ reset_repo () { cd .. rm -rf temp1@@ -122,8 +133,10 @@ match_date 'last week' match_date 'last month' +fmt_offset -1 || exit 200+ reset_repo-create_entry "$(($year-1))-$mm-$dd"+create_entry "$(fmt_offset -350)" # english dates - new possibilities nomatch_date 'yesterday at 14:00:00' match_date 'last 3 years'
tests/mergeresolved.sh view
@@ -53,5 +53,3 @@ # At this point, tempB and tempA should agree since we have pulled both ways. cmp tempB/foo tempA/foo -rm -rf fooOld tempA tempB-
+ tests/network/issue1503_prefer_local_caches_to_remote_one.sh view
@@ -0,0 +1,33 @@+#!/usr/bin/env bash+## Test for issue1503 - 'Prefer local caches to remote ones'+##+## Copyright (C) 2010 Adolfo Builes+##+## 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.++. ../tests/lib++rm -rf S T+darcs get --lazy http://darcs.net/testing/repo1 S+darcs tag --repo S -m 2+darcs get --lazy http://darcs.net/testing/repo1 T+darcs pull --repo T S -a --debug --verbose 2>&1 | tee log+not grep repo1 log
tests/output.sh view
@@ -30,7 +30,7 @@ diff -u ../patchfile ../correct rm ../patchfile-darcs send -a --dont-edit-description -o - ../../temp2 > ../patchfile+darcs send -a --set-default --dont-edit-description -o - ../../temp2 > ../patchfile grep -v Creating ../patchfile | diff -u ../correct - darcs apply --repodir=../../temp2 --dry-run ../patchfile > out
tests/posthook.sh view
@@ -2,6 +2,8 @@ set -ev +. lib+ rm -rf temp1 mkdir temp1 cd temp1@@ -20,7 +22,35 @@ test -f posthook-ran rm posthook-ran -echo Successful.- cd .. rm -rf temp1++# POSIX-only section+# ----------------------------------------------------------------------+# Things below this section do not appear to work on Windows.+# Pending further investigation at http://bugs.darcs.net/issue1813++if echo $OS | grep -i windows; then+ exit 0+fi++# Check that DARCS_PATCHES_XML works+rm -rf R S # another script may have left a mess+darcs init --repo R # Create our test repos.+darcs init --repo S # Create our test repos.++cd R+echo 'echo $DARCS_PATCHES_XML' > hook+darcs record -lam 'hook'+chmod u+x hook+cat > _darcs/prefs/defaults << END+apply run-posthook+apply posthook ./hook+END+cd ..++cd S+echo 'Example content.' > f+darcs record -lam 'Add f'+darcs push -a ../R | grep 'patch author'+cd ..
tests/printer.sh view
@@ -63,21 +63,21 @@ test_line $(printf '\x1E') '[_^^_]' test_line $(printf '\x1F') '[_^__]' test_line $(printf '\x7F') '[_^?_]'-# other chars are escaped with \xXX+# other chars are escaped with <U+XXXX> test_line $(printf '\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F')\- '[_\80_][_\81_][_\82_][_\83_][_\84_][_\85_][_\86_][_\87_][_\88_][_\89_][_\8a_][_\8b_][_\8c_][_\8d_][_\8e_][_\8f_]'+ '[_<U+0080>_][_<U+0081>_][_<U+0082>_][_<U+0083>_][_<U+0084>_][_<U+0085>_][_<U+0086>_][_<U+0087>_][_<U+0088>_][_<U+0089>_][_<U+008A>_][_<U+008B>_][_<U+008C>_][_<U+008D>_][_<U+008E>_][_<U+008F>_]' test_line $(printf '\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F')\- '[_\90_][_\91_][_\92_][_\93_][_\94_][_\95_][_\96_][_\97_][_\98_][_\99_][_\9a_][_\9b_][_\9c_][_\9d_][_\9e_][_\9f_]'+ '[_<U+0090>_][_<U+0091>_][_<U+0092>_][_<U+0093>_][_<U+0094>_][_<U+0095>_][_<U+0096>_][_<U+0097>_][_<U+0098>_][_<U+0099>_][_<U+009A>_][_<U+009B>_][_<U+009C>_][_<U+009D>_][_<U+009E>_][_<U+009F>_]' test_line $(printf '\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF')\- '[_\a0_][_\a1_][_\a2_][_\a3_][_\a4_][_\a5_][_\a6_][_\a7_][_\a8_][_\a9_][_\aa_][_\ab_][_\ac_][_\ad_][_\ae_][_\af_]'+ '[_<U+00A0>_][_<U+00A1>_][_<U+00A2>_][_<U+00A3>_][_<U+00A4>_][_<U+00A5>_][_<U+00A6>_][_<U+00A7>_][_<U+00A8>_][_<U+00A9>_][_<U+00AA>_][_<U+00AB>_][_<U+00AC>_][_<U+00AD>_][_<U+00AE>_][_<U+00AF>_]' test_line $(printf '\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF')\- '[_\b0_][_\b1_][_\b2_][_\b3_][_\b4_][_\b5_][_\b6_][_\b7_][_\b8_][_\b9_][_\ba_][_\bb_][_\bc_][_\bd_][_\be_][_\bf_]'+ '[_<U+00B0>_][_<U+00B1>_][_<U+00B2>_][_<U+00B3>_][_<U+00B4>_][_<U+00B5>_][_<U+00B6>_][_<U+00B7>_][_<U+00B8>_][_<U+00B9>_][_<U+00BA>_][_<U+00BB>_][_<U+00BC>_][_<U+00BD>_][_<U+00BE>_][_<U+00BF>_]' test_line $(printf '\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF')\- '[_\c0_][_\c1_][_\c2_][_\c3_][_\c4_][_\c5_][_\c6_][_\c7_][_\c8_][_\c9_][_\ca_][_\cb_][_\cc_][_\cd_][_\ce_][_\cf_]'+ '[_<U+00C0>_][_<U+00C1>_][_<U+00C2>_][_<U+00C3>_][_<U+00C4>_][_<U+00C5>_][_<U+00C6>_][_<U+00C7>_][_<U+00C8>_][_<U+00C9>_][_<U+00CA>_][_<U+00CB>_][_<U+00CC>_][_<U+00CD>_][_<U+00CE>_][_<U+00CF>_]' test_line $(printf '\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF')\- '[_\d0_][_\d1_][_\d2_][_\d3_][_\d4_][_\d5_][_\d6_][_\d7_][_\d8_][_\d9_][_\da_][_\db_][_\dc_][_\dd_][_\de_][_\df_]'+ '[_<U+00D0>_][_<U+00D1>_][_<U+00D2>_][_<U+00D3>_][_<U+00D4>_][_<U+00D5>_][_<U+00D6>_][_<U+00D7>_][_<U+00D8>_][_<U+00D9>_][_<U+00DA>_][_<U+00DB>_][_<U+00DC>_][_<U+00DD>_][_<U+00DE>_][_<U+00DF>_]' test_line $(printf '\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF')\- '[_\e0_][_\e1_][_\e2_][_\e3_][_\e4_][_\e5_][_\e6_][_\e7_][_\e8_][_\e9_][_\ea_][_\eb_][_\ec_][_\ed_][_\ee_][_\ef_]'+ '[_<U+00E0>_][_<U+00E1>_][_<U+00E2>_][_<U+00E3>_][_<U+00E4>_][_<U+00E5>_][_<U+00E6>_][_<U+00E7>_][_<U+00E8>_][_<U+00E9>_][_<U+00EA>_][_<U+00EB>_][_<U+00EC>_][_<U+00ED>_][_<U+00EE>_][_<U+00EF>_]' test_line $(printf '\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF')\- '[_\f0_][_\f1_][_\f2_][_\f3_][_\f4_][_\f5_][_\f6_][_\f7_][_\f8_][_\f9_][_\fa_][_\fb_][_\fc_][_\fd_][_\fe_][_\ff_]'+ '[_<U+00F0>_][_<U+00F1>_][_<U+00F2>_][_<U+00F3>_][_<U+00F4>_][_<U+00F5>_][_<U+00F6>_][_<U+00F7>_][_<U+00F8>_][_<U+00F9>_][_<U+00FA>_][_<U+00FB>_][_<U+00FC>_][_<U+00FD>_][_<U+00FE>_][_<U+00FF>_]' rm -rf temp1
tests/pull-dont-prompt-deps.sh view
@@ -22,10 +22,12 @@ echo bar2 > b darcs record -Ax -alm bar2 cd ../temp2-echo y | darcs pull ../temp1 --dont-prompt-for-dependencies -p foo2 --dry-run > toto-#on the previous line, we don't get asked about foo1.-grep foo toto | wc -l | grep 2-#but we send it anyway.+echo y | darcs pull ../temp1 -i --dont-prompt-for-dependencies -p foo2 --dry-run > toto+#on the previous line, we get asked about foo2, and we take it+grep foo2 toto | wc -l | grep 2+#we don't get asked about foo1, but we take it anyway, so +grep foo1 toto | wc -l | grep 1+#and we don't send bar grep bar toto | wc -l | grep 0 cd .. rm -rf temp1 temp2
tests/pull.sh view
@@ -25,7 +25,7 @@ darcs add one; darcs record --patch-name uno --all cd .. # now outside of any repo-darcs pull --repodir temp1 --all temp2 | grep -i 'Finished pulling.' # temp2 is not relative to temp1+darcs pull --set-default --repodir temp1 --all temp2 | grep -i 'Finished pulling.' # temp2 is not relative to temp1 # set up server repo date > temp2/one/date.t@@ -119,7 +119,7 @@ darcs record -am newdir cd ../temp2 mkdir newdir-darcs pull -a ../temp1 &> out2+darcs pull -a --set-default ../temp1 &> out2 cat out grep Backing out2 grep 'Finished pulling' out2
tests/pull_binary.sh view
@@ -19,7 +19,7 @@ mkdir temp2 cd temp2 darcs init-echo yn | darcs pull ../temp1+echo yn | darcs pull --set-default ../temp1 rm foo darcs pull -a cd ..
tests/push-dont-prompt-deps.sh view
@@ -21,10 +21,12 @@ darcs record -Ax -alm foo2 echo bar2 > b darcs record -Ax -alm bar2-echo y | darcs push ../temp2 --dont-prompt-for-dependencies -p foo2 --dry-run > toto-#on the previous line, we don't get asked about foo1.-grep foo toto | wc -l | grep 2-#but we send it anyway.+echo y | darcs push ../temp2 --dont-prompt-for-dependencies -p foo2 --dry-run -i > toto+#on the previous line, we get asked about foo2, and we take it+grep foo2 toto | wc -l | grep 2+#we don't get asked about foo1, but we take it anyway, so +grep foo1 toto | wc -l | grep 1+#and we don't send bar grep bar toto | wc -l | grep 0 cd .. rm -rf temp1 temp2
tests/query_manifest.sh view
@@ -13,12 +13,13 @@ echo "./$x" >> dirs.tmp echo "./$x" >> files-dirs.tmp done- darcs query manifest $3 --files --no-directories | sort > darcs-files.tmp- darcs query manifest $3 --no-files --directories | sort > darcs-dirs.tmp- darcs query manifest $3 --files --directories | sort > darcs-files-dirs.tmp+ darcs query manifest $3 --files --no-directories > darcsraw-files.tmp+ darcs query manifest $3 --no-files --directories > darcsraw-dirs.tmp+ darcs query manifest $3 --files --directories > darcsraw-files-dirs.tmp for x in files dirs files-dirs ; do- sort $x.tmp >sorted-$x.tmp- diff sorted-$x.tmp darcs-$x.tmp+ sort $x.tmp | sed -e 's,\\,/,' > expected-$x.tmp+ sort darcsraw-$x.tmp | sed -e 's,\\,/,' > darcs-$x.tmp+ diff -u expected-$x.tmp darcs-$x.tmp done } rm -rf temp
tests/record.sh view
@@ -24,12 +24,13 @@ # RT#231 - special message is given for nonexistent directories not darcs record -am foo not_there.txt > log-grep -i 'non existent' log+grep -i 'not exist' log # RT#231 - a nonexistent file before an existing file is handled correctly touch b.t-darcs record -am foo a.t b.t > log-grep -i 'non existent files or directories: "a.t"' log+darcs record -lam foo a.t b.t > log+grep -i 'WARNING:.*a.t' log+grep -iv 'WARNING:.*b.t' log DIR="`pwd`" touch date.t
+ tests/repos/minimal-darcs-2.4.tgz view
binary file changed (absent → 3443 bytes)
tests/send-dont-prompt-deps.sh view
@@ -21,10 +21,12 @@ darcs record -Ax -alm foo2 echo bar2 > b darcs record -Ax -alm bar2-echo y | darcs send ../temp2 --dont-prompt-for-dependencies -p foo2 --dry-run > toto-#on the previous line, we don't get asked about foo1.-grep foo toto | wc -l | grep 2-#but we send it anyway.+echo y | darcs send ../temp2 -i --dont-prompt-for-dependencies -p foo2 --dry-run > toto+#on the previous line, we get asked about foo2, and we take it+grep foo2 toto | wc -l | grep 2+#we don't get asked about foo1, but we take it anyway, so +grep foo1 toto | wc -l | grep 1+#and we don't send bar grep bar toto | wc -l | grep 0 cd .. rm -rf temp1 temp2
tests/tentative_revert.sh view
@@ -33,13 +33,13 @@ darcs record --repo R -lam 'bar' echo "this change should stay uncommitted" >> R/foo darcs setpref --repo R test false-echo 'y' | not darcs amend --repo R -am 'change everything' R/foo+echo 'y' | not darcs amend --repo R -am 'change everything' R/foo --test darcs setpref --repo R test true # if tentative state was not cleared, the previous changes # from failed transaction would piggy back on the next echo "xx" >> R/bar-echo 'y' | darcs amend --repo R -am 'bar2' R/bar+echo 'y' | darcs amend --repo R -am 'bar2' R/bar --test # should have uncommitted changes darcs wh --repo R > changes
+ tests/trackdown-bisect-helper.hs view
@@ -0,0 +1,38 @@+{-++Tool for construction of testing repository for trackdown testing+with bisect option. Written by Matthias Fischmann.++Usage:++./trackdown-bisect-helper '[0,1,1,1,0,0,0]'++This will generate a repository in which `grep -q 1 j` will first fail+three times, then succeed three times, then fail once if you unapply+patches with the linear implementation.++-}+++import Control.Monad+import System.IO+import System+import System.Random+import Data.List+import Control.Exception+++stamp i j = system ("echo " ++ show i ++ " > ./i") >>+ system ("echo " ++ show j ++ " > ./j") >>+ -- system ("sleep 1") >>+ hFlush stdout >>+ system ("darcs record --ignore-times -am '" ++ show i ++ "'")++generate :: [Int] -> IO ()+generate = mapM_ (uncurry stamp) . zip [1..]++main :: IO ()+main = do+ args <- getArgs+ let js = (read (head args)) :: [Int]+ generate js
+ tests/trackdown-bisect.sh view
@@ -0,0 +1,141 @@+#!/bin/env bash+# A test for trackdown --bisect option.+# In general it construct various repositories and try+# to find the last recent failing patch and match it with+# expected position.+#+# It runs the same tests also for the trackdown without+# the --bisect.+################################################################++set -ev++if echo $OS | grep -i windows; then+ echo I do not know how to run a test program under windows+ exit 0+fi++ghc -o trackdown-bisect-helper trackdown-bisect-helper.hs++function make_repo_with_test {+ rm -fr temp1+ mkdir temp1 ; cd temp1 ; darcs init+ touch ./i+ touch ./j+ darcs add ./i+ darcs add ./j+ ../trackdown-bisect-helper $1+}++function cleanup_repo_after {+ cd ..+ rm -fr temp1+}++# You can remove --bisect for compare with linear trackdown+trackdown_args='--bisect' ++# Function return true if given patch was found.+# It expects that last line has finish with <SPACE><patchname>+# For the linear it is second last from the end, and last line+# is sentence if trackdown failed or succeed.+function is_found_good_patch {+ if [ -z "$trackdown_args" ]; then+ tail -n 2 | grep " $1\$"+ else + tail -n 1 | grep " $1\$"+ fi+}++# Test command - Success condition is that file 'j' have one inside (1)+# That means if it has zero (0) it is failing test. +test_cmd='grep -q 1 j'++#############################################################################+# Section with test-cases+#############################################################################++# TEST01: Repo with success in the half+testTrackdown() {+make_repo_with_test $1+if darcs trackdown $trackdown_args "$test_cmd" | is_found_good_patch $2; then+ echo "ok 1"+else+ echo "not ok 1. the trackdown should find last failing patch = $2."+ exit 1+fi+cleanup_repo_after+}++# TEST01: Repo with success in the half+test01() {+testTrackdown '[1,1,0,0,0]' 3+}++# TEST02: Repo with without success condition+test02() { +testTrackdown '[0,0,0,0,0]' 1+}++# TEST03: Repo with with success condition at before last patch+test03() {+testTrackdown '[1,1,1,1,0]' 5+}++# TEST04: Repo with with success condition as first patch ever+test04() {+testTrackdown '[1,0,0,0,0]' 2+}++# TEST05: Long repo with with success condition as first patch ever+test05() {+testTrackdown '[1,0,0,0,0,0,0,0,0,0,0]' 2+}++# TEST06: Long repo with with success condition as sixth patch+test06() {+testTrackdown '[1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0]' 7+}++# TEST07: Long repo with with success condition very nead the head+test07() {+testTrackdown '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0]' 54+}++# TEST08: Long repo with with success condition very nead the head+test08() {+testTrackdown '[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0]' 55+}++# TEST09: Long repo with non-monotone errors / success distribution+# This test only tests that it will not crash... +test09() {+testTrackdown '[1,1,1,1,1,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0]' 7+}++#############################################+# call test-cases for trackdown linear+#############################################+trackdown_args=''+test01+test02+test03+test04+test05+test06+test07+test08+#############################################+# Call test-cases for trackdown bisect+#############################################+trackdown_args='--bisect' +test01+test02+test03+test04+test05+test06+test07+test08+test09 # only for --bisect+
+ tests/utf8.sh view
@@ -0,0 +1,189 @@+#!/usr/bin/env bash+## Test for issue64 - Should store patch metadata in UTF-8+##+## Copyright (C) 2009 Reinier Lamers+##+## 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.++. ../tests/lib ++# Helper function: do a darcs changes --xml and grep the result for the first+# argument. If it is not found, exit with status 1. Otherwise, continue. The+# second argument is a text that describes what we're grepping for.+# If a third argument is given, it is used as the value for a --last option for+# darcs changes.+grep_changes () {+ if [ -z "$3" ]; then+ last=""+ else+ last="--last $3"+ fi++ darcs changes $last --xml > changes.xml+ if grep "$1" changes.xml ; then+ echo "$2 OK"+ else+ echo "$2 not UTF-8-encoded!"+ exit 1+ fi+}+++# This file is encoded in ISO-8859-15 aka latin9. It was crafted with a hex editor.+# Please leave it this way :-)+switch_to_latin9_locale++rm -rf temp1+mkdir temp1+cd temp1++darcs init++# Test recording non-UTF-8-encoded non-latin1 ("funny") metadata from+# interactive input++echo 'Selbstverständlich überraschend' > something.txt+darcs add something.txt++echo 'l33tking¸0r@example.org' > interaction_script.txt+echo y >> interaction_script.txt+echo y >> interaction_script.txt+echo '¤uroh4xx0rz' >> interaction_script.txt+echo n >> interaction_script.txt++unset DARCSEMAIL+unset DARCS_TESTING_PREFS_DIR+unset EMAIL+set+darcs record -i < interaction_script.txt+grep_changes 'l33tkingž0r@example.org' 'patch author from interactive prompt'+grep_changes 'â¬uroh4xx0rz' 'patch name from interactive prompt'++# Test recording funny metadata from command line ++echo 'Sogar überraschender' >> something.txt++darcs record -a -A 'Jérôme Leb½uf' -m 'that will be ¤ 15, sir'++grep_changes 'that will be ⬠15, sir' 'patch name from command line'+grep_changes 'Jérôme LebÅuf' 'patch author from command line'++# Test recording funny metadata from a log file++echo 'Am allerüberraschendsten' >> something.txt++echo 'darcs is soms wat naïef aangaande tekstcodering' > log.txt+echo 'en zulke naïviteit is tegenwoordig passé, aldus ´i¸ek' >> log.txt+darcs record -a -A 'Jérôme Leb½uf' --logfile=log.txt++grep_changes 'darcs is soms wat naïef aangaande tekstcodering' 'patch name from log file'+grep_changes 'en zulke naïviteit is tegenwoordig passé, aldus Žižek' 'patch log from log file'++# Test recording funny metadata from environment, +export EMAIL='Slavoj ´i¸ek <zizek@example.edu>'+rm _darcs/prefs/author+echo 'La la la, the more lines the better!' >> something.txt+darcs record -a -m 'Patch names are overrated'++grep_changes 'Slavoj Žižek' 'author name from environment'++# Test recording funny metadata from prefs files+echo '´ed is dead' > _darcs/prefs/author+echo '483 bottles of beer on the wall' >> something.txt+darcs record -a -m 'Patch by ´ed'++grep_changes 'Žed is dead' 'author name from prefs file'++# Test amend-recording funny metadata+echo 'No, it is really 484' >> something.txt+echo y | darcs amend-record -p 'Patch by ' -A '´ed is even deader' -a+grep_changes 'Žed is even deader' 'author name from amend-record command line flag'++echo '#!/usr/bin/env bash' > editor+echo 'echo All my ¤s are gone > $1' >> editor # create an 'editor' that writes latin9+chmod +x editor+export EDITOR="`pwd`/editor"+printf "y\ny\n" | darcs amend --edit -p 'Patch by '+grep_changes 'All my â¬s are gone' 'description edited from amend-record'+grep_changes 'Žed is even deader' 'author name taken from draft in amend'++# Test rollback recording funny metadata+printf "y\ny\n" | darcs rollback -p 's are gone' -A '´ee´ee' -m "No patch¤s by ´ed!"+grep_changes 'ŽeeŽee' 'Author name from rollback command line'+grep_changes 'No patchâ¬s by Žed' 'Patch name from rollback command line'+grep_changes 'All my â¬s are gone' 'Patch name of rolled back patch' 1+grep_changes 'Žed is even deader' 'Author name of rolled back patch' 1++# Test tag recording funny metadata+rm _darcs/prefs/author # Make tag be taken from EMAIL env variable+darcs tag -m '´ is my favorite letter'+grep_changes 'Slavoj Žižek' 'author name from environment with tag command' 1+grep_changes 'Ž is my favorite letter' 'Tag name from command line'++unset EMAIL+printf "´ors\ninitialcomment\n" | darcs tag --edit-long-comment+grep_changes Žors 'Author name from interactive prompt from tag command'+grep_changes 'All my â¬s are gone' 'Tag name from editor from tag command' 1++if grep ´ors _darcs/prefs/author ; then+ echo 'Author name stored locale-encoded in prefs file after tag command, OK'+else+ echo 'No locale-encoded author in prefs file after tag command!'+ exit 1+fi++darcs tag -A Ad¸e -m 'La¸t call'+grep_changes Adže 'Author name from tag command line' 1+grep_changes 'Lažt call' 'Tag name from tag command line (take 2)' 1++cd ..++# test that UTF-8 metadata doesn't get mangled on get+rm -rf temp2+darcs get temp1 temp2+darcs changes --repodir temp1 --xml > temp1/changes.xml+darcs changes --repodir temp2 --xml > temp2/changes.xml+diff temp1/changes.xml temp2/changes.xml++# and that it doesn't get mangled on push+rm -rf temp2+mkdir temp2; darcs init --repodir temp2+darcs push --repodir temp1 -a temp2 --set-default+darcs changes --repodir temp1 --xml > temp1/changes.xml+darcs changes --repodir temp2 --xml > temp2/changes.xml+diff temp1/changes.xml temp2/changes.xml++# and that it doesn't get mangled on pull+rm -rf temp2+mkdir temp2; darcs init --repodir temp2+darcs pull --repodir temp2 -a temp1+darcs changes --repodir temp1 --xml > temp1/changes.xml+darcs changes --repodir temp2 --xml > temp2/changes.xml+diff temp1/changes.xml temp2/changes.xml++# and that it doesn't get mangled on send+rm -rf temp2+mkdir temp2; darcs init --repodir temp2+darcs send --repodir temp1 -a -o temp2/patch.dpatch+darcs apply --repodir temp2 -a temp2/patch.dpatch+darcs changes --repodir temp1 --xml > temp1/changes.xml+darcs changes --repodir temp2 --xml > temp2/changes.xml+diff temp1/changes.xml temp2/changes.xml
+ tests/whatsnew-pending.sh view
@@ -0,0 +1,15 @@+#!/usr/bin/env bash+## Ensure that darcs whatsnew <paths> only lists relevant bits.+## Public Domain, 2010, Petr Rockai++. lib # Load some portability helpers.+rm -rf R # Another script may have left a mess.+darcs init --repo R # Create our test repos.++cd R+mkdir d e # Change the working tree.+echo 'Example content.' > d/f+darcs record -lam 'Add d/f and e.'+darcs remove d/f+not darcs wh e # | not grep f+cd ..