darcs 2.10.3 → 2.12.0
raw patch · 471 files changed
+16324/−21971 lines, 471 filesdep +asyncdep +fgldep +graphvizdep −deepseqdep −lcsdep ~HTTPdep ~HUnitdep ~QuickChecksetup-changedbinary-added
Dependencies added: async, fgl, graphviz
Dependencies removed: deepseq, lcs
Dependency ranges changed: HTTP, HUnit, QuickCheck, Win32, array, base, binary, bytestring, containers, cryptohash, directory, filepath, hashable, html, mmap, mtl, network, old-locale, process, random, sandi, split, test-framework, test-framework-hunit, test-framework-quickcheck2, time, unix, unix-compat, vector, zip-archive, zlib
Files
- CHANGELOG +32/−0
- README.md +9/−0
- Setup.lhs +0/−3
- containers-0.5.2.1/Darcs/Data/Map/Base.hs +0/−2791
- containers-0.5.2.1/Darcs/Data/Map/Strict.hs +0/−1180
- containers-0.5.2.1/Darcs/Data/Set/Base.hs +0/−1526
- containers-0.5.2.1/Darcs/Data/StrictPair.hs +0/−13
- contrib/darcs-errors.hlint +0/−25
- contrib/runHLint.sh +12/−0
- darcs.cabal +92/−226
- harness/Darcs/Test/Patch.hs +57/−55
- harness/Darcs/Test/Patch/Arbitrary/Generic.hs +3/−3
- harness/Darcs/Test/Patch/Arbitrary/PatchV1.hs +0/−290
- harness/Darcs/Test/Patch/Arbitrary/PrimFileUUID.hs +259/−0
- harness/Darcs/Test/Patch/Arbitrary/PrimV1.hs +5/−1
- harness/Darcs/Test/Patch/Arbitrary/PrimV3.hs +0/−259
- harness/Darcs/Test/Patch/Arbitrary/Real.hs +0/−69
- harness/Darcs/Test/Patch/Arbitrary/RepoPatchV1.hs +291/−0
- harness/Darcs/Test/Patch/Arbitrary/RepoPatchV2.hs +69/−0
- harness/Darcs/Test/Patch/Examples/Set1.hs +50/−50
- harness/Darcs/Test/Patch/Examples/Set2Unwitnessed.hs +72/−72
- harness/Darcs/Test/Patch/FileUUIDModel.hs +214/−0
- harness/Darcs/Test/Patch/Properties/Check.hs +2/−2
- harness/Darcs/Test/Patch/Properties/GenericUnwitnessed.hs +3/−3
- harness/Darcs/Test/Patch/Properties/Real.hs +0/−35
- harness/Darcs/Test/Patch/Properties/RepoPatchV2.hs +35/−0
- harness/Darcs/Test/Patch/Properties/V1Set1.hs +15/−17
- harness/Darcs/Test/Patch/Properties/V1Set2.hs +23/−23
- harness/Darcs/Test/Patch/Rebase.hs +3/−3
- harness/Darcs/Test/Patch/Selection.hs +60/−0
- harness/Darcs/Test/Patch/V1Model.hs +8/−7
- harness/Darcs/Test/Patch/V3Model.hs +0/−215
- harness/Darcs/Test/Patch/WSub.hs +4/−4
- harness/Darcs/Test/Util/QuickCheck.hs +3/−1
- harness/Storage/Hashed/Test.hs +542/−0
- harness/hstestdata.zip binary
- harness/test.hs +13/−4
- hashed-storage/Bundled/Posix.hsc +0/−116
- hashed-storage/LICENSE +0/−26
- hashed-storage/Storage/Hashed.hs +0/−51
- hashed-storage/Storage/Hashed/AnchoredPath.hs +0/−110
- hashed-storage/Storage/Hashed/Darcs.hs +0/−310
- hashed-storage/Storage/Hashed/Diff.hs +0/−136
- hashed-storage/Storage/Hashed/Hash.hs +0/−80
- hashed-storage/Storage/Hashed/Index.hs +0/−525
- hashed-storage/Storage/Hashed/Monad.hs +0/−277
- hashed-storage/Storage/Hashed/Packed.hs +0/−227
- hashed-storage/Storage/Hashed/Plain.hs +0/−78
- hashed-storage/Storage/Hashed/Test.hs +0/−621
- hashed-storage/Storage/Hashed/Tree.hs +0/−464
- hashed-storage/Storage/Hashed/Utils.hs +0/−148
- hashed-storage/test.hs +0/−17
- hashed-storage/testdata.zip binary
- release/distributed-context +1/−1
- src/Bundled/Posix.hsc +116/−0
- src/Darcs/Patch.hs +12/−7
- src/Darcs/Patch/Annotate.hs +10/−11
- src/Darcs/Patch/Apply.hs +15/−8
- src/Darcs/Patch/ApplyMonad.hs +74/−55
- src/Darcs/Patch/ApplyPatches.hs +2/−2
- src/Darcs/Patch/Bracketed.hs +2/−0
- src/Darcs/Patch/Bracketed/Instances.hs +22/−0
- src/Darcs/Patch/Bundle.hs +58/−45
- src/Darcs/Patch/Choices.hs +18/−12
- src/Darcs/Patch/Commute.hs +6/−22
- src/Darcs/Patch/CommuteFn.hs +7/−4
- src/Darcs/Patch/Conflict.hs +105/−17
- src/Darcs/Patch/ConflictMarking.hs +0/−97
- src/Darcs/Patch/Depends.hs +219/−164
- src/Darcs/Patch/Dummy.hs +4/−7
- src/Darcs/Patch/Effect.hs +5/−0
- src/Darcs/Patch/FileHunk.hs +3/−0
- src/Darcs/Patch/Index/Monad.hs +12/−10
- src/Darcs/Patch/Index/Types.hs +3/−0
- src/Darcs/Patch/Info.hs +4/−0
- src/Darcs/Patch/Inspect.hs +3/−0
- src/Darcs/Patch/Invert.hs +5/−2
- src/Darcs/Patch/Match.hs +124/−118
- src/Darcs/Patch/Matchable.hs +1/−3
- src/Darcs/Patch/MaybeInternal.hs +0/−33
- src/Darcs/Patch/Merge.hs +7/−18
- src/Darcs/Patch/MonadProgress.hs +5/−3
- src/Darcs/Patch/Named.hs +7/−9
- src/Darcs/Patch/Named/Wrapped.hs +344/−0
- src/Darcs/Patch/OldDate.hs +3/−0
- src/Darcs/Patch/PatchInfoAnd.hs +57/−44
- src/Darcs/Patch/Permutations.hs +18/−15
- src/Darcs/Patch/Prim/Class.hs +6/−1
- src/Darcs/Patch/Prim/FileUUID.hs +25/−0
- src/Darcs/Patch/Prim/FileUUID/Apply.hs +81/−0
- src/Darcs/Patch/Prim/FileUUID/Coalesce.hs +18/−0
- src/Darcs/Patch/Prim/FileUUID/Commute.hs +73/−0
- src/Darcs/Patch/Prim/FileUUID/Core.hs +151/−0
- src/Darcs/Patch/Prim/FileUUID/Details.hs +12/−0
- src/Darcs/Patch/Prim/FileUUID/ObjectMap.hs +43/−0
- src/Darcs/Patch/Prim/FileUUID/Read.hs +58/−0
- src/Darcs/Patch/Prim/FileUUID/Show.hs +70/−0
- src/Darcs/Patch/Prim/V1.hs +3/−0
- src/Darcs/Patch/Prim/V1/Apply.hs +7/−4
- src/Darcs/Patch/Prim/V1/Coalesce.hs +28/−26
- src/Darcs/Patch/Prim/V1/Commute.hs +77/−74
- src/Darcs/Patch/Prim/V1/Core.hs +3/−0
- src/Darcs/Patch/Prim/V1/Details.hs +3/−1
- src/Darcs/Patch/Prim/V1/Read.hs +3/−0
- src/Darcs/Patch/Prim/V1/Show.hs +5/−2
- src/Darcs/Patch/Prim/V3.hs +0/−22
- src/Darcs/Patch/Prim/V3/Apply.hs +0/−63
- src/Darcs/Patch/Prim/V3/Coalesce.hs +0/−15
- src/Darcs/Patch/Prim/V3/Commute.hs +0/−70
- src/Darcs/Patch/Prim/V3/Core.hs +0/−148
- src/Darcs/Patch/Prim/V3/Details.hs +0/−12
- src/Darcs/Patch/Prim/V3/ObjectMap.hs +0/−40
- src/Darcs/Patch/Prim/V3/Read.hs +0/−55
- src/Darcs/Patch/Prim/V3/Show.hs +0/−67
- src/Darcs/Patch/Progress.hs +18/−15
- src/Darcs/Patch/Read.hs +3/−2
- src/Darcs/Patch/ReadMonads.hs +4/−1
- src/Darcs/Patch/Rebase.hs +44/−471
- src/Darcs/Patch/Rebase/Container.hs +209/−0
- src/Darcs/Patch/Rebase/Fixup.hs +8/−2
- src/Darcs/Patch/Rebase/Item.hs +183/−0
- src/Darcs/Patch/Rebase/Name.hs +10/−0
- src/Darcs/Patch/Rebase/NameHack.hs +0/−23
- src/Darcs/Patch/Rebase/Recontext.hs +0/−45
- src/Darcs/Patch/Rebase/Viewing.hs +31/−33
- src/Darcs/Patch/RegChars.hs +3/−0
- src/Darcs/Patch/Repair.hs +6/−3
- src/Darcs/Patch/RepoPatch.hs +0/−4
- src/Darcs/Patch/RepoType.hs +48/−0
- src/Darcs/Patch/Set.hs +36/−29
- src/Darcs/Patch/Show.hs +6/−3
- src/Darcs/Patch/Split.hs +16/−13
- src/Darcs/Patch/Summary.hs +11/−3
- src/Darcs/Patch/SummaryData.hs +3/−0
- src/Darcs/Patch/TokenReplace.hs +15/−0
- src/Darcs/Patch/TouchesFiles.hs +5/−2
- src/Darcs/Patch/Type.hs +4/−2
- src/Darcs/Patch/V1.hs +5/−5
- src/Darcs/Patch/V1/Apply.hs +7/−4
- src/Darcs/Patch/V1/Commute.hs +125/−109
- src/Darcs/Patch/V1/Core.hs +27/−31
- src/Darcs/Patch/V1/Read.hs +6/−3
- src/Darcs/Patch/V1/Show.hs +6/−3
- src/Darcs/Patch/V1/Viewing.hs +6/−3
- src/Darcs/Patch/V2.hs +4/−4
- src/Darcs/Patch/V2/Non.hs +5/−7
- src/Darcs/Patch/V2/Real.hs +0/−952
- src/Darcs/Patch/V2/RepoPatch.hs +945/−0
- src/Darcs/Patch/Viewing.hs +12/−14
- src/Darcs/Patch/Witnesses/Eq.hs +3/−0
- src/Darcs/Patch/Witnesses/Ordered.hs +32/−40
- src/Darcs/Patch/Witnesses/Sealed.hs +3/−0
- src/Darcs/Patch/Witnesses/Show.hs +3/−0
- src/Darcs/Patch/Witnesses/WZipper.hs +7/−3
- src/Darcs/Prelude.hs +54/−0
- src/Darcs/Repository.hs +42/−639
- src/Darcs/Repository/ApplyPatches.hs +11/−7
- src/Darcs/Repository/Cache.hs +9/−9
- src/Darcs/Repository/Clone.hs +507/−0
- src/Darcs/Repository/Compat.hs +0/−141
- src/Darcs/Repository/Diff.hs +3/−1
- src/Darcs/Repository/External.hs +0/−297
- src/Darcs/Repository/Flags.hs +16/−0
- src/Darcs/Repository/Format.hs +16/−12
- src/Darcs/Repository/HashedIO.hs +10/−6
- src/Darcs/Repository/HashedRepo.hs +81/−117
- src/Darcs/Repository/Internal.hs +163/−113
- src/Darcs/Repository/InternalTypes.hs +7/−4
- src/Darcs/Repository/Job.hs +146/−75
- src/Darcs/Repository/Lock.hs +0/−382
- src/Darcs/Repository/LowLevel.hs +0/−126
- src/Darcs/Repository/Match.hs +16/−22
- src/Darcs/Repository/Merge.hs +31/−24
- src/Darcs/Repository/Motd.hs +4/−1
- src/Darcs/Repository/Old.hs +20/−17
- src/Darcs/Repository/Packs.hs +117/−0
- src/Darcs/Repository/PatchIndex.hs +38/−40
- src/Darcs/Repository/Pending.hs +129/−0
- src/Darcs/Repository/Prefs.hs +6/−4
- src/Darcs/Repository/Read.hs +0/−25
- src/Darcs/Repository/Rebase.hs +85/−87
- src/Darcs/Repository/Repair.hs +33/−34
- src/Darcs/Repository/Resolution.hs +15/−11
- src/Darcs/Repository/Ssh.hs +0/−233
- src/Darcs/Repository/State.hs +303/−166
- src/Darcs/Repository/Test.hs +8/−5
- src/Darcs/Repository/Util.hs +0/−340
- src/Darcs/UI/ApplyPatches.hs +25/−20
- src/Darcs/UI/Commands.hs +7/−15
- src/Darcs/UI/Commands/Add.hs +8/−6
- src/Darcs/UI/Commands/Amend.hs +41/−109
- src/Darcs/UI/Commands/Annotate.hs +24/−69
- src/Darcs/UI/Commands/Apply.hs +17/−16
- src/Darcs/UI/Commands/Clone.hs +11/−12
- src/Darcs/UI/Commands/Convert.hs +202/−95
- src/Darcs/UI/Commands/Diff.hs +19/−13
- src/Darcs/UI/Commands/Dist.hs +9/−17
- src/Darcs/UI/Commands/GZCRCs.hs +5/−2
- src/Darcs/UI/Commands/Help.hs +8/−7
- src/Darcs/UI/Commands/Init.hs +13/−10
- src/Darcs/UI/Commands/Log.hs +66/−75
- src/Darcs/UI/Commands/MarkConflicts.hs +6/−3
- src/Darcs/UI/Commands/Move.hs +25/−15
- src/Darcs/UI/Commands/Optimize.hs +68/−34
- src/Darcs/UI/Commands/Pull.hs +25/−25
- src/Darcs/UI/Commands/Push.hs +69/−39
- src/Darcs/UI/Commands/Rebase.hs +79/−69
- src/Darcs/UI/Commands/Record.hs +70/−209
- src/Darcs/UI/Commands/Remove.hs +6/−5
- src/Darcs/UI/Commands/Repair.hs +6/−6
- src/Darcs/UI/Commands/Replace.hs +7/−15
- src/Darcs/UI/Commands/Revert.hs +18/−20
- src/Darcs/UI/Commands/Rollback.hs +18/−17
- src/Darcs/UI/Commands/Send.hs +26/−25
- src/Darcs/UI/Commands/SetPref.hs +3/−0
- src/Darcs/UI/Commands/Show.hs +12/−23
- src/Darcs/UI/Commands/ShowAuthors.hs +3/−0
- src/Darcs/UI/Commands/ShowBug.hs +3/−0
- src/Darcs/UI/Commands/ShowContents.hs +13/−10
- src/Darcs/UI/Commands/ShowDependencies.hs +148/−0
- src/Darcs/UI/Commands/ShowFiles.hs +38/−38
- src/Darcs/UI/Commands/ShowIndex.hs +7/−7
- src/Darcs/UI/Commands/ShowPatchIndex.hs +23/−82
- src/Darcs/UI/Commands/ShowRepo.hs +16/−10
- src/Darcs/UI/Commands/ShowTags.hs +4/−2
- src/Darcs/UI/Commands/Tag.hs +20/−23
- src/Darcs/UI/Commands/Test.hs +28/−27
- src/Darcs/UI/Commands/TransferMode.hs +5/−5
- src/Darcs/UI/Commands/Unrecord.hs +45/−38
- src/Darcs/UI/Commands/Unrevert.hs +14/−12
- src/Darcs/UI/Commands/Util.hs +46/−37
- src/Darcs/UI/Commands/Util/Tree.hs +7/−5
- src/Darcs/UI/Commands/WhatsNew.hs +30/−22
- src/Darcs/UI/CommandsAux.hs +3/−0
- src/Darcs/UI/Defaults.hs +5/−2
- src/Darcs/UI/Email.hs +3/−0
- src/Darcs/UI/External.hs +6/−12
- src/Darcs/UI/Flags.hs +35/−30
- src/Darcs/UI/Message/Send.hs +5/−3
- src/Darcs/UI/Options.hs +3/−0
- src/Darcs/UI/Options/All.hs +22/−14
- src/Darcs/UI/Options/Core.hs +2/−2
- src/Darcs/UI/Options/Flags.hs +3/−0
- src/Darcs/UI/Options/Iso.hs +4/−0
- src/Darcs/UI/Options/Markdown.hs +3/−0
- src/Darcs/UI/Options/Matching.hs +7/−6
- src/Darcs/UI/Options/Util.hs +3/−0
- src/Darcs/UI/PatchHeader.hs +295/−0
- src/Darcs/UI/PrintPatch.hs +5/−2
- src/Darcs/UI/RemoteApply.hs +0/−65
- src/Darcs/UI/RunCommand.hs +16/−15
- src/Darcs/UI/SelectChanges.hs +74/−75
- src/Darcs/UI/TheCommands.hs +28/−27
- src/Darcs/UI/Usage.hs +4/−0
- src/Darcs/Util/AtExit.hs +2/−2
- src/Darcs/Util/Bug.hs +3/−0
- src/Darcs/Util/ByteString.hs +35/−4
- src/Darcs/Util/CommandLine.hs +3/−0
- src/Darcs/Util/Compat.hs +141/−0
- src/Darcs/Util/Crypt/SHA1.hs +3/−0
- src/Darcs/Util/Crypt/SHA256.hs +3/−0
- src/Darcs/Util/DateMatcher.hs +3/−0
- src/Darcs/Util/DateTime.hs +3/−0
- src/Darcs/Util/Diff.hs +3/−0
- src/Darcs/Util/Diff/Myers.hs +3/−0
- src/Darcs/Util/Diff/Patience.hs +85/−88
- src/Darcs/Util/Download.hs +3/−0
- src/Darcs/Util/Download/Curl.hs +3/−1
- src/Darcs/Util/Download/HTTP.hs +2/−1
- src/Darcs/Util/Download/Request.hs +3/−0
- src/Darcs/Util/Encoding.hs +3/−0
- src/Darcs/Util/Encoding/Win32.hs +3/−0
- src/Darcs/Util/English.hs +21/−10
- src/Darcs/Util/Environment.hs +2/−2
- src/Darcs/Util/Exception.hs +11/−3
- src/Darcs/Util/Exec.hs +3/−0
- src/Darcs/Util/External.hs +216/−0
- src/Darcs/Util/File.hs +2/−1
- src/Darcs/Util/Global.hs +7/−1
- src/Darcs/Util/Hash.hs +63/−0
- src/Darcs/Util/Index.hs +583/−0
- src/Darcs/Util/IsoDate.hs +3/−0
- src/Darcs/Util/Lock.hs +384/−0
- src/Darcs/Util/Path.hs +133/−24
- src/Darcs/Util/Printer.hs +129/−56
- src/Darcs/Util/Printer/Color.hs +3/−1
- src/Darcs/Util/Progress.hs +3/−0
- src/Darcs/Util/Prompt.hs +2/−1
- src/Darcs/Util/Show.hs +3/−0
- src/Darcs/Util/SignalHandler.hs +2/−1
- src/Darcs/Util/Ssh.hs +244/−7
- src/Darcs/Util/Text.hs +17/−1
- src/Darcs/Util/Tree.hs +470/−0
- src/Darcs/Util/Tree/Hashed.hs +245/−0
- src/Darcs/Util/Tree/Monad.hs +280/−0
- src/Darcs/Util/Tree/Plain.hs +76/−0
- src/Darcs/Util/URL.hs +16/−9
- src/Darcs/Util/Workaround.hs +2/−1
- src/win32/System/Posix.hs +0/−1
- src/win32/System/Posix/Files.hsc +0/−1
- tests/add-formerly-pl.sh +0/−58
- tests/add.sh +96/−0
- tests/add_in_subdir.sh +0/−22
- tests/addexitval.sh +0/−33
- tests/addmv.sh +0/−20
- tests/addrace.sh +0/−18
- tests/addrm.sh +0/−18
- tests/amend-cancelling.sh +0/−19
- tests/amend-record-back-up.sh +0/−39
- tests/amend-unrecord.sh +0/−57
- tests/amend.sh +69/−1
- tests/apply_skip_conflicts.sh +59/−0
- tests/ask_deps.sh +9/−7
- tests/bad_pending_after_pull.sh +0/−51
- tests/changes-duplicate.sh +0/−49
- tests/changes.sh +0/−71
- tests/changes_send_context.sh +0/−18
- tests/changes_with_move.sh +0/−34
- tests/conflict-reporting.sh +97/−0
- tests/dist-v.sh +0/−18
- tests/double-unrevert.sh +0/−29
- tests/failing-haskell_policy.sh +0/−41
- tests/failing-index-argument.sh +1/−2
- tests/failing-issue1013_either_dependency.sh +0/−53
- tests/failing-issue1196_whatsnew_falsely_lists_all_changes.sh +0/−21
- tests/failing-issue1316-2.sh +1/−1
- tests/failing-issue1332_add_r_boring.sh +0/−45
- tests/failing-issue1401_bug_in_get_extra.sh +1/−1
- tests/failing-issue1609_ot_convergence.sh +0/−104
- tests/failing-issue1632_changes_nonexisting.sh +0/−40
- tests/failing-issue1702-optimize-relink-vs-cache.sh +0/−77
- tests/failing-issue1829-inconsistent-conflictor.sh +2/−1
- tests/failing-issue1928-file-dir-replace.sh +0/−36
- tests/failing-issue2017-missing-tag.sh +0/−85
- tests/failing-issue2047_duplicate_conflictor_recommute_fail.sh +2/−0
- tests/failing-issue2086-index-permissions.sh +0/−38
- tests/failing-issue2138-whatsnew-s.sh +0/−59
- tests/failing-issue2186-apply--reply-conflict.sh +3/−3
- tests/failing-issue2186-apply--reply-ok.sh +3/−3
- tests/failing-issue2219-no-working.sh +11/−10
- tests/failing-issue2238-unadded-files-showing-added.sh +0/−37
- tests/failing-issue2242-rollback-mv.sh +0/−46
- tests/failing-issue2293-laziness-amend.sh +1/−1
- tests/failing-issue2308-changes-missing-amend-edit.sh +0/−54
- tests/failing-issue2380-rename-to-deleted-file.sh +0/−47
- tests/failing-issue2480-display-unicode-in-patch-content.sh +9/−0
- tests/failing-issue612_repo_not_writable.sh +0/−35
- tests/failing-issue68_broken_pipe.sh +0/−27
- tests/failing-look_for_moves.sh +0/−39
- tests/failing-merging_newlines.sh +0/−1
- tests/get-http-packed-detect.sh +0/−40
- tests/get-http-packed.sh +0/−29
- tests/get-http.sh +0/−41
- tests/git_import_delete_empty_directories.sh +33/−0
- tests/git_rename_and_copy_files.sh +63/−0
- tests/git_rename_dir.sh +62/−0
- tests/hijack.sh +112/−0
- tests/illegal_mv.sh +0/−21
- tests/impossible_unrevert.sh +0/−33
- tests/issue1012_unrecord_remove.sh +0/−18
- tests/issue1111-pull-intersection.sh +0/−66
- tests/issue1139-diff-last.sh +0/−1
- tests/issue1196_whatsnew_falsely_lists_all_changes.sh +15/−0
- tests/issue1332_add_r_boring.sh +45/−0
- tests/issue1337_darcs_changes_false_positives.sh +0/−37
- tests/issue142_record-log.sh +0/−34
- tests/issue1599-automatically-expire-unused-caches.sh +0/−55
- tests/issue1702-optimize-relink-vs-cache.sh +77/−0
- tests/issue1765_recursive-remove-on-root.sh +0/−36
- tests/issue1825-remove-pending.sh +1/−5
- tests/issue1845-record-removed.sh +0/−16
- tests/issue184_add.sh +0/−19
- tests/issue1888-changes-context.sh +0/−43
- tests/issue1923-cache-warning.sh +0/−53
- tests/issue1928-file-dir-replace.sh +36/−0
- tests/issue1932-colon-breaks-add.sh +3/−10
- tests/issue2017-missing-tag.sh +85/−0
- tests/issue2086-index-permissions.sh +40/−0
- tests/issue2136-changes_created_as_for_multiple_files.sh +0/−86
- tests/issue2136-log_created_as_for_multiple_files.sh +86/−0
- tests/issue2138-whatsnew-s.sh +59/−0
- tests/issue2139-mv-to-dir.sh +0/−54
- tests/issue2209-look_for_replaces.sh +45/−3
- tests/issue2242-rollback-mv.sh +46/−0
- tests/issue2270-changes-interactive-only-to-files.sh +0/−139
- tests/issue2270-log-interactive-only-to-files.sh +139/−0
- tests/issue2378-moving-directory-to-file.sh +14/−0
- tests/issue2380-rename-to-deleted-file.sh +47/−0
- tests/issue2388-optimize-fails-no-changes.sh +0/−30
- tests/issue244_changes.sh +0/−18
- tests/issue2479-mv-list-files.sh +14/−0
- tests/issue2494-output-of-record-with-file-arguments.sh +79/−0
- tests/issue612_repo_not_writable.sh +35/−0
- tests/issue68_broken_pipe.sh +25/−0
- tests/lib +7/−0
- tests/log-duplicate.sh +46/−0
- tests/log.sh +161/−0
- tests/log_send_context.sh +18/−0
- tests/look_for_moves.sh +29/−24
- tests/merge_three_patches.sh +2/−2
- tests/merging_newlines.sh +0/−1
- tests/mv-formerly-pl.sh +0/−123
- tests/mv-test-suite.sh +0/−28
- tests/mv.sh +238/−14
- tests/mv_then_add.sh +0/−25
- tests/network/clone-http-packed-detect.sh +40/−0
- tests/network/clone-http-packed.sh +29/−0
- tests/network/clone-http.sh +41/−0
- tests/network/clone.sh +4/−3
- tests/network/issue1503_prefer_local_caches_to_remote_one.sh +2/−0
- tests/network/issue1599-automatically-expire-unused-caches.sh +55/−0
- tests/network/issue1923-cache-warning.sh +53/−0
- tests/network/issue1932-remote.sh +41/−0
- tests/network/issue2090-transfer-mode.sh +6/−3
- tests/network/lazy-clone.sh +5/−2
- tests/network/log.sh +5/−0
- tests/network/show_tags-remote.sh +46/−0
- tests/network/ssh.sh +34/−28
- tests/obliterate-add.sh +0/−19
- tests/obliterate-formerly-pl.sh +0/−32
- tests/obliterate.sh +69/−14
- tests/optimize.sh +8/−0
- tests/patch-index-creation.sh +10/−10
- tests/patch-index-enabled-and-disabled.sh +2/−2
- tests/patch-index-rename.sh +1/−1
- tests/patch-index-spans.sh +3/−3
- tests/patch-index-sync.sh +11/−11
- tests/pull-union.sh +0/−42
- tests/pull.sh +193/−26
- tests/pull_binary.sh +0/−50
- tests/pull_compl.sh +0/−145
- tests/pull_complement.sh +145/−0
- tests/pull_conflicts.sh +0/−57
- tests/pull_two.sh +0/−34
- tests/push-dont-prompt-deps.sh +0/−32
- tests/push-dry-run.sh +3/−8
- tests/push-formerly-pl.sh +0/−57
- tests/push.sh +95/−15
- tests/push_conflicts.sh +0/−59
- tests/push_lock.sh +0/−26
- tests/query_manifest.sh +0/−91
- tests/rebase-nochanges.sh +1/−0
- tests/rebase-warns-lost-deps.sh +54/−0
- tests/record-interactive.sh +0/−27
- tests/record-misc.sh +0/−61
- tests/record.sh +211/−0
- tests/record_editor.sh +0/−79
- tests/recordrace.sh +0/−21
- tests/remove.sh +14/−0
- tests/replace.sh +33/−0
- tests/replace_after_pending_add.sh +0/−21
- tests/replace_after_pending_mv.sh +0/−21
- tests/show_files.sh +91/−0
- tests/show_tags-remote.sh +0/−46
- tests/tricky_unrecord.sh +0/−27
- tests/unpull-formerly-pl.sh +0/−51
- tests/unpull.sh +0/−23
- tests/unrecord-add.sh +0/−23
- tests/unrecord-dont-prompt.sh +0/−28
- tests/unrecord-remove.sh +0/−28
- tests/unrecord-setpref.sh +0/−24
- tests/unrecord.sh +126/−0
- tests/unrevert-add.sh +0/−26
- tests/unrevert-replace-moved.sh +0/−36
- tests/unrevert.sh +117/−4
- tests/unrevert_cancel.sh +0/−22
- tests/what_sl.sh +0/−19
- tests/whatsnew-file.sh +0/−61
- tests/whatsnew-interactive.sh +0/−46
- tests/whatsnew-pending.sh +0/−38
- tests/whatsnew.sh +114/−1
CHANGELOG view
@@ -1,3 +1,35 @@+Darcs 2.12.0, 29 April 2016++ * `darcs show dependencies`: export patch dependency graph as dot file (Ale Gadea)+ * improvements in `record` output with irrelevant files (Ben Franksen)+ * `darcs log -v --machine-readable`: show internal representation of+ patches (including explicit dependencies). Remove patch viewing via the+ `annotate` command. (Guillaume Hoffmann)+ * `whatsnew -s` (and `status`) show conflicting files (Guillaume Hoffmann)+ * honor "quiet" flag in command outputs (Ben Franksen)+ * a single `show patch-index` command (Guillaume Hoffmann)+ * remove deprecated aliases of show (Guillaume Hoffmann)+ * handle file moves natively when importing from git (Owen Stephens)+ * require GHC 7.6 (base > 4.6) and support GHC 8 (Ganesh Sittampalam)+ * switch to sandi from dataenc (Daniil Frumin)+ * remove hack to enable arbitrary protocols via env variables (Guillaume Hoffmann)+ * fixed the following bugs:++ * 1807: clarify help of PAGER, DARCS_PAGER (Guillaume Hoffmann)+ * 2258: improve patch index error message with suggestion (Guillaume Hoffmann)+ * 2269: push hijack test to suspend time (Eric Kow)+ * 2276: Keep track of patch hijack decisions (Eric Kow)+ * 2138: report conflicting files in whatsnew -s (Guillaume Hoffmann)+ * 2393: remove whatsnew functionality from annotate (Guillaume Hoffmann)+ * 2400: use async package to keep track of unpack threads (Ben Franksen)+ * 2459: fall back to writing the file if createLink fails (Ben Franksen)+ * 2479: root dir most not be among the sources of a move (Ben Franksen)+ * 2481: expose API for 'darcs diff' command (Ganesh Sittampalam)+ * 2486: obliterate --not-in-remote -q should be more quiet (Ben Franksen)+ * 2489: dequote filepaths while importing from git (Guillaume Hoffmann)+ * 2494: output of darcs record with file arguments (Ben Franksen)++ Darcs 2.10.3, 29 January 2016 * implement weak repository hash and show it in "darcs show repo"
README.md view
@@ -46,3 +46,12 @@ Please send bug reports to <bugs@darcs.net>. +Hacking+=======++Please consult <http://darcs.net/Development/GettingStarted> for+up-to-date information about contributing to Darcs.++The wiki can be downloaded with the command:++ $ darcs clone --lazy http://darcs.net/darcs-wiki
Setup.lhs view
@@ -4,9 +4,6 @@ -- portions copyright (c) 2008 David Roundy -- portions copyright (c) 2007-2009 Judah Jacobson -import Prelude hiding ( catch )-import qualified Prelude- import qualified Distribution.InstalledPackageInfo as Installed import Distribution.Simple ( defaultMainWithHooks, UserHooks(..), simpleUserHooks )
− containers-0.5.2.1/Darcs/Data/Map/Base.hs
@@ -1,2791 +0,0 @@-{-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__-{-# LANGUAGE DeriveDataTypeable, StandaloneDeriving #-}-#endif-#if !defined(TESTING) && __GLASGOW_HASKELL__ >= 703-{-# LANGUAGE Trustworthy #-}-#endif--------------------------------------------------------------------------------- |--- Module : Data.Map.Base--- Copyright : (c) Daan Leijen 2002--- (c) Andriy Palamarchuk 2008--- License : BSD-style--- Maintainer : libraries@haskell.org--- Stability : provisional--- Portability : portable------ An efficient implementation of maps from keys to values (dictionaries).------ Since many function names (but not the type name) clash with--- "Prelude" names, this module is usually imported @qualified@, e.g.------ > import Data.Map (Map)--- > import qualified Data.Map as Map------ The implementation of 'Map' is based on /size balanced/ binary trees (or--- trees of /bounded balance/) as described by:------ * Stephen Adams, \"/Efficient sets: a balancing act/\",--- Journal of Functional Programming 3(4):553-562, October 1993,--- <http://www.swiss.ai.mit.edu/~adams/BB/>.------ * J. Nievergelt and E.M. Reingold,--- \"/Binary search trees of bounded balance/\",--- SIAM journal of computing 2(1), March 1973.------ Note that the implementation is /left-biased/ -- the elements of a--- first argument are always preferred to the second, for example in--- 'union' or 'insert'.------ Operation comments contain the operation time complexity in--- the Big-O notation <http://en.wikipedia.org/wiki/Big_O_notation>.---------------------------------------------------------------------------------- [Note: Using INLINABLE]--- ~~~~~~~~~~~~~~~~~~~~~~~--- It is crucial to the performance that the functions specialize on the Ord--- type when possible. GHC 7.0 and higher does this by itself when it sees th--- unfolding of a function -- that is why all public functions are marked--- INLINABLE (that exposes the unfolding).----- [Note: Using INLINE]--- ~~~~~~~~~~~~~~~~~~~~--- For other compilers and GHC pre 7.0, we mark some of the functions INLINE.--- We mark the functions that just navigate down the tree (lookup, insert,--- delete and similar). That navigation code gets inlined and thus specialized--- when possible. There is a price to pay -- code growth. The code INLINED is--- therefore only the tree navigation, all the real work (rebalancing) is not--- INLINED by using a NOINLINE.------ All methods marked INLINE have to be nonrecursive -- a 'go' function doing--- the real work is provided.----- [Note: Type of local 'go' function]--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--- If the local 'go' function uses an Ord class, it sometimes heap-allocates--- the Ord dictionary when the 'go' function does not have explicit type.--- In that case we give 'go' explicit type. But this slightly decrease--- performance, as the resulting 'go' function can float out to top level.----- [Note: Local 'go' functions and capturing]--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--- As opposed to IntMap, when 'go' function captures an argument, increased--- heap-allocation can occur: sometimes in a polymorphic function, the 'go'--- floats out of its enclosing function and then it heap-allocates the--- dictionary and the argument. Maybe it floats out too late and strictness--- analyzer cannot see that these could be passed on stack.------ For example, change 'member' so that its local 'go' function is not passing--- argument k and then look at the resulting code for hedgeInt.----- [Note: Order of constructors]--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--- The order of constructors of Map matters when considering performance.--- Currently in GHC 7.0, when type has 2 constructors, a forward conditional--- jump is made when successfully matching second constructor. Successful match--- of first constructor results in the forward jump not taken.--- On GHC 7.0, reordering constructors from Tip | Bin to Bin | Tip--- improves the benchmark by up to 10% on x86.--module Darcs.Data.Map.Base (- -- * Map type- Map(..) -- instance Eq,Show,Read-- -- * Operators- , (!), (\\)-- -- * Query- , null- , size- , member- , notMember- , lookup- , findWithDefault- , lookupLT- , lookupGT- , lookupLE- , lookupGE-- -- * Construction- , empty- , singleton-- -- ** Insertion- , insert- , insertWith- , insertWithKey- , insertLookupWithKey-- -- ** Delete\/Update- , delete- , adjust- , adjustWithKey- , update- , updateWithKey- , updateLookupWithKey- , alter-- -- * Combine-- -- ** Union- , union- , unionWith- , unionWithKey- , unions- , unionsWith-- -- ** Difference- , difference- , differenceWith- , differenceWithKey-- -- ** Intersection- , intersection- , intersectionWith- , intersectionWithKey-- -- ** Universal combining function- , mergeWithKey-- -- * Traversal- -- ** Map- , map- , mapWithKey- , traverseWithKey- , mapAccum- , mapAccumWithKey- , mapAccumRWithKey- , mapKeys- , mapKeysWith- , mapKeysMonotonic-- -- * Folds- , foldr- , foldl- , foldrWithKey- , foldlWithKey- -- ** Strict folds- , foldr'- , foldl'- , foldrWithKey'- , foldlWithKey'-- -- * Conversion- , elems- , keys- , assocs- , keysSet- , fromSet-- -- ** Lists- , toList- , fromList- , fromListWith- , fromListWithKey-- -- ** Ordered lists- , toAscList- , toDescList- , fromAscList- , fromAscListWith- , fromAscListWithKey- , fromDistinctAscList-- -- * Filter- , filter- , filterWithKey- , partition- , partitionWithKey-- , mapMaybe- , mapMaybeWithKey- , mapEither- , mapEitherWithKey-- , split- , splitLookup-- -- * Submap- , isSubmapOf, isSubmapOfBy- , isProperSubmapOf, isProperSubmapOfBy-- -- * Indexed- , lookupIndex- , findIndex- , elemAt- , updateAt- , deleteAt-- -- * Min\/Max- , findMin- , findMax- , deleteMin- , deleteMax- , deleteFindMin- , deleteFindMax- , updateMin- , updateMax- , updateMinWithKey- , updateMaxWithKey- , minView- , maxView- , minViewWithKey- , maxViewWithKey-- -- * Debugging- , showTree- , showTreeWith- , valid-- -- Used by the strict version- , bin- , balance- , balanced- , balanceL- , balanceR- , delta- , join- , insertMax- , merge- , glue- , trim- , trimLookupLo- , foldlStrict- , MaybeS(..)- , filterGt- , filterLt- ) where--import Control.Applicative (Applicative(..), (<$>))-import Control.DeepSeq (NFData(rnf))-import Data.Bits (shiftL, shiftR)-import qualified Data.Foldable as Foldable-import Data.Monoid (Monoid(..))-import Darcs.Data.StrictPair-import Data.Traversable (Traversable(traverse))-import Data.Typeable-import Prelude hiding (lookup, map, filter, foldr, foldl, null)--import qualified Darcs.Data.Set.Base as Set--#if __GLASGOW_HASKELL__-import GHC.Exts ( build )-import Text.Read-import Data.Data-#endif---- Use macros to define strictness of functions.--- STRICT_x_OF_y denotes an y-ary function strict in the x-th parameter.--- We do not use BangPatterns, because they are not in any standard and we--- want the compilers to be compiled by as many compilers as possible.-#define STRICT_1_OF_2(fn) fn arg _ | arg `seq` False = undefined-#define STRICT_1_OF_3(fn) fn arg _ _ | arg `seq` False = undefined-#define STRICT_2_OF_3(fn) fn _ arg _ | arg `seq` False = undefined-#define STRICT_1_OF_4(fn) fn arg _ _ _ | arg `seq` False = undefined-#define STRICT_2_OF_4(fn) fn _ arg _ _ | arg `seq` False = undefined--{--------------------------------------------------------------------- Operators---------------------------------------------------------------------}-infixl 9 !,\\ ------ | /O(log n)/. Find the value at a key.--- Calls 'error' when the element can not be found.------ > fromList [(5,'a'), (3,'b')] ! 1 Error: element not in the map--- > fromList [(5,'a'), (3,'b')] ! 5 == 'a'--(!) :: Ord k => Map k a -> k -> a-(!) m k = find k m-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE (!) #-}-#endif---- | Same as 'difference'.-(\\) :: Ord k => Map k a -> Map k b -> Map k a-m1 \\ m2 = difference m1 m2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE (\\) #-}-#endif--{--------------------------------------------------------------------- Size balanced trees.---------------------------------------------------------------------}--- | A Map from keys @k@ to values @a@.---- See Note: Order of constructors-data Map k a = Bin {-# UNPACK #-} !Size !k a !(Map k a) !(Map k a)- | Tip--type Size = Int--instance (Ord k) => Monoid (Map k v) where- mempty = empty- mappend = union- mconcat = unions--#if __GLASGOW_HASKELL__--{--------------------------------------------------------------------- A Data instance---------------------------------------------------------------------}---- This instance preserves data abstraction at the cost of inefficiency.--- We provide limited reflection services for the sake of data abstraction.--instance (Data k, Data a, Ord k) => Data (Map k a) where- gfoldl f z m = z fromList `f` toList m- toConstr _ = fromListConstr- gunfold k z c = case constrIndex c of- 1 -> k (z fromList)- _ -> error "gunfold"- dataTypeOf _ = mapDataType- dataCast2 f = gcast2 f--fromListConstr :: Constr-fromListConstr = mkConstr mapDataType "fromList" [] Prefix--mapDataType :: DataType-mapDataType = mkDataType "Data.Map.Base.Map" [fromListConstr]--#endif--{--------------------------------------------------------------------- Query---------------------------------------------------------------------}--- | /O(1)/. Is the map empty?------ > Data.Map.null (empty) == True--- > Data.Map.null (singleton 1 'a') == False--null :: Map k a -> Bool-null Tip = True-null (Bin {}) = False-{-# INLINE null #-}---- | /O(1)/. The number of elements in the map.------ > size empty == 0--- > size (singleton 1 'a') == 1--- > size (fromList([(1,'a'), (2,'c'), (3,'b')])) == 3--size :: Map k a -> Int-size Tip = 0-size (Bin sz _ _ _ _) = sz-{-# INLINE size #-}----- | /O(log n)/. Lookup the value at a key in the map.------ The function will return the corresponding value as @('Just' value)@,--- or 'Nothing' if the key isn't in the map.------ An example of using @lookup@:------ > import Prelude hiding (lookup)--- > import Data.Map--- >--- > employeeDept = fromList([("John","Sales"), ("Bob","IT")])--- > deptCountry = fromList([("IT","USA"), ("Sales","France")])--- > countryCurrency = fromList([("USA", "Dollar"), ("France", "Euro")])--- >--- > employeeCurrency :: String -> Maybe String--- > employeeCurrency name = do--- > dept <- lookup name employeeDept--- > country <- lookup dept deptCountry--- > lookup country countryCurrency--- >--- > main = do--- > putStrLn $ "John's currency: " ++ (show (employeeCurrency "John"))--- > putStrLn $ "Pete's currency: " ++ (show (employeeCurrency "Pete"))------ The output of this program:------ > John's currency: Just "Euro"--- > Pete's currency: Nothing-lookup :: Ord k => k -> Map k a -> Maybe a-lookup = go- where- STRICT_1_OF_2(go)- go _ Tip = Nothing- go k (Bin _ kx x l r) = case compare k kx of- LT -> go k l- GT -> go k r- EQ -> Just x-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE lookup #-}-#else-{-# INLINE lookup #-}-#endif---- | /O(log n)/. Is the key a member of the map? See also 'notMember'.------ > member 5 (fromList [(5,'a'), (3,'b')]) == True--- > member 1 (fromList [(5,'a'), (3,'b')]) == False-member :: Ord k => k -> Map k a -> Bool-member = go- where- STRICT_1_OF_2(go)- go _ Tip = False- go k (Bin _ kx _ l r) = case compare k kx of- LT -> go k l- GT -> go k r- EQ -> True-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE member #-}-#else-{-# INLINE member #-}-#endif---- | /O(log n)/. Is the key not a member of the map? See also 'member'.------ > notMember 5 (fromList [(5,'a'), (3,'b')]) == False--- > notMember 1 (fromList [(5,'a'), (3,'b')]) == True--notMember :: Ord k => k -> Map k a -> Bool-notMember k m = not $ member k m-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE notMember #-}-#else-{-# INLINE notMember #-}-#endif---- | /O(log n)/. Find the value at a key.--- Calls 'error' when the element can not be found.-find :: Ord k => k -> Map k a -> a-find = go- where- STRICT_1_OF_2(go)- go _ Tip = error "Map.!: given key is not an element in the map"- go k (Bin _ kx x l r) = case compare k kx of- LT -> go k l- GT -> go k r- EQ -> x-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE find #-}-#else-{-# INLINE find #-}-#endif---- | /O(log n)/. The expression @('findWithDefault' def k map)@ returns--- the value at key @k@ or returns default value @def@--- when the key is not in the map.------ > findWithDefault 'x' 1 (fromList [(5,'a'), (3,'b')]) == 'x'--- > findWithDefault 'x' 5 (fromList [(5,'a'), (3,'b')]) == 'a'-findWithDefault :: Ord k => a -> k -> Map k a -> a-findWithDefault = go- where- STRICT_2_OF_3(go)- go def _ Tip = def- go def k (Bin _ kx x l r) = case compare k kx of- LT -> go def k l- GT -> go def k r- EQ -> x-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE findWithDefault #-}-#else-{-# INLINE findWithDefault #-}-#endif---- | /O(log n)/. Find largest key smaller than the given one and return the--- corresponding (key, value) pair.------ > lookupLT 3 (fromList [(3,'a'), (5,'b')]) == Nothing--- > lookupLT 4 (fromList [(3,'a'), (5,'b')]) == Just (3, 'a')-lookupLT :: Ord k => k -> Map k v -> Maybe (k, v)-lookupLT = goNothing- where- STRICT_1_OF_2(goNothing)- goNothing _ Tip = Nothing- goNothing k (Bin _ kx x l r) | k <= kx = goNothing k l- | otherwise = goJust k kx x r-- STRICT_1_OF_4(goJust)- goJust _ kx' x' Tip = Just (kx', x')- goJust k kx' x' (Bin _ kx x l r) | k <= kx = goJust k kx' x' l- | otherwise = goJust k kx x r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE lookupLT #-}-#else-{-# INLINE lookupLT #-}-#endif---- | /O(log n)/. Find smallest key greater than the given one and return the--- corresponding (key, value) pair.------ > lookupGT 4 (fromList [(3,'a'), (5,'b')]) == Just (5, 'b')--- > lookupGT 5 (fromList [(3,'a'), (5,'b')]) == Nothing-lookupGT :: Ord k => k -> Map k v -> Maybe (k, v)-lookupGT = goNothing- where- STRICT_1_OF_2(goNothing)- goNothing _ Tip = Nothing- goNothing k (Bin _ kx x l r) | k < kx = goJust k kx x l- | otherwise = goNothing k r-- STRICT_1_OF_4(goJust)- goJust _ kx' x' Tip = Just (kx', x')- goJust k kx' x' (Bin _ kx x l r) | k < kx = goJust k kx x l- | otherwise = goJust k kx' x' r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE lookupGT #-}-#else-{-# INLINE lookupGT #-}-#endif---- | /O(log n)/. Find largest key smaller or equal to the given one and return--- the corresponding (key, value) pair.------ > lookupLE 2 (fromList [(3,'a'), (5,'b')]) == Nothing--- > lookupLE 4 (fromList [(3,'a'), (5,'b')]) == Just (3, 'a')--- > lookupLE 5 (fromList [(3,'a'), (5,'b')]) == Just (5, 'b')-lookupLE :: Ord k => k -> Map k v -> Maybe (k, v)-lookupLE = goNothing- where- STRICT_1_OF_2(goNothing)- goNothing _ Tip = Nothing- goNothing k (Bin _ kx x l r) = case compare k kx of LT -> goNothing k l- EQ -> Just (kx, x)- GT -> goJust k kx x r-- STRICT_1_OF_4(goJust)- goJust _ kx' x' Tip = Just (kx', x')- goJust k kx' x' (Bin _ kx x l r) = case compare k kx of LT -> goJust k kx' x' l- EQ -> Just (kx, x)- GT -> goJust k kx x r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE lookupLE #-}-#else-{-# INLINE lookupLE #-}-#endif---- | /O(log n)/. Find smallest key greater or equal to the given one and return--- the corresponding (key, value) pair.------ > lookupGE 3 (fromList [(3,'a'), (5,'b')]) == Just (3, 'a')--- > lookupGE 4 (fromList [(3,'a'), (5,'b')]) == Just (5, 'b')--- > lookupGE 6 (fromList [(3,'a'), (5,'b')]) == Nothing-lookupGE :: Ord k => k -> Map k v -> Maybe (k, v)-lookupGE = goNothing- where- STRICT_1_OF_2(goNothing)- goNothing _ Tip = Nothing- goNothing k (Bin _ kx x l r) = case compare k kx of LT -> goJust k kx x l- EQ -> Just (kx, x)- GT -> goNothing k r-- STRICT_1_OF_4(goJust)- goJust _ kx' x' Tip = Just (kx', x')- goJust k kx' x' (Bin _ kx x l r) = case compare k kx of LT -> goJust k kx x l- EQ -> Just (kx, x)- GT -> goJust k kx' x' r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE lookupGE #-}-#else-{-# INLINE lookupGE #-}-#endif--{--------------------------------------------------------------------- Construction---------------------------------------------------------------------}--- | /O(1)/. The empty map.------ > empty == fromList []--- > size empty == 0--empty :: Map k a-empty = Tip-{-# INLINE empty #-}---- | /O(1)/. A map with a single element.------ > singleton 1 'a' == fromList [(1, 'a')]--- > size (singleton 1 'a') == 1--singleton :: k -> a -> Map k a-singleton k x = Bin 1 k x Tip Tip-{-# INLINE singleton #-}--{--------------------------------------------------------------------- Insertion---------------------------------------------------------------------}--- | /O(log n)/. Insert a new key and value in the map.--- If the key is already present in the map, the associated value is--- replaced with the supplied value. 'insert' is equivalent to--- @'insertWith' 'const'@.------ > insert 5 'x' (fromList [(5,'a'), (3,'b')]) == fromList [(3, 'b'), (5, 'x')]--- > insert 7 'x' (fromList [(5,'a'), (3,'b')]) == fromList [(3, 'b'), (5, 'a'), (7, 'x')]--- > insert 5 'x' empty == singleton 5 'x'---- See Note: Type of local 'go' function-insert :: Ord k => k -> a -> Map k a -> Map k a-insert = go- where- go :: Ord k => k -> a -> Map k a -> Map k a- STRICT_1_OF_3(go)- go kx x Tip = singleton kx x- go kx x (Bin sz ky y l r) =- case compare kx ky of- LT -> balanceL ky y (go kx x l) r- GT -> balanceR ky y l (go kx x r)- EQ -> Bin sz kx x l r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE insert #-}-#else-{-# INLINE insert #-}-#endif---- Insert a new key and value in the map if it is not already present.--- Used by `union`.---- See Note: Type of local 'go' function-insertR :: Ord k => k -> a -> Map k a -> Map k a-insertR = go- where- go :: Ord k => k -> a -> Map k a -> Map k a- STRICT_1_OF_3(go)- go kx x Tip = singleton kx x- go kx x t@(Bin _ ky y l r) =- case compare kx ky of- LT -> balanceL ky y (go kx x l) r- GT -> balanceR ky y l (go kx x r)- EQ -> t-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE insertR #-}-#else-{-# INLINE insertR #-}-#endif---- | /O(log n)/. Insert with a function, combining new value and old value.--- @'insertWith' f key value mp@--- will insert the pair (key, value) into @mp@ if key does--- not exist in the map. If the key does exist, the function will--- insert the pair @(key, f new_value old_value)@.------ > insertWith (++) 5 "xxx" (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "xxxa")]--- > insertWith (++) 7 "xxx" (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "a"), (7, "xxx")]--- > insertWith (++) 5 "xxx" empty == singleton 5 "xxx"--insertWith :: Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a-insertWith f = insertWithKey (\_ x' y' -> f x' y')-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE insertWith #-}-#else-{-# INLINE insertWith #-}-#endif---- | /O(log n)/. Insert with a function, combining key, new value and old value.--- @'insertWithKey' f key value mp@--- will insert the pair (key, value) into @mp@ if key does--- not exist in the map. If the key does exist, the function will--- insert the pair @(key,f key new_value old_value)@.--- Note that the key passed to f is the same key passed to 'insertWithKey'.------ > let f key new_value old_value = (show key) ++ ":" ++ new_value ++ "|" ++ old_value--- > insertWithKey f 5 "xxx" (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "5:xxx|a")]--- > insertWithKey f 7 "xxx" (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "a"), (7, "xxx")]--- > insertWithKey f 5 "xxx" empty == singleton 5 "xxx"---- See Note: Type of local 'go' function-insertWithKey :: Ord k => (k -> a -> a -> a) -> k -> a -> Map k a -> Map k a-insertWithKey = go- where- go :: Ord k => (k -> a -> a -> a) -> k -> a -> Map k a -> Map k a- STRICT_2_OF_4(go)- go _ kx x Tip = singleton kx x- go f kx x (Bin sy ky y l r) =- case compare kx ky of- LT -> balanceL ky y (go f kx x l) r- GT -> balanceR ky y l (go f kx x r)- EQ -> Bin sy kx (f kx x y) l r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE insertWithKey #-}-#else-{-# INLINE insertWithKey #-}-#endif---- | /O(log n)/. Combines insert operation with old value retrieval.--- The expression (@'insertLookupWithKey' f k x map@)--- is a pair where the first element is equal to (@'lookup' k map@)--- and the second element equal to (@'insertWithKey' f k x map@).------ > let f key new_value old_value = (show key) ++ ":" ++ new_value ++ "|" ++ old_value--- > insertLookupWithKey f 5 "xxx" (fromList [(5,"a"), (3,"b")]) == (Just "a", fromList [(3, "b"), (5, "5:xxx|a")])--- > insertLookupWithKey f 7 "xxx" (fromList [(5,"a"), (3,"b")]) == (Nothing, fromList [(3, "b"), (5, "a"), (7, "xxx")])--- > insertLookupWithKey f 5 "xxx" empty == (Nothing, singleton 5 "xxx")------ This is how to define @insertLookup@ using @insertLookupWithKey@:------ > let insertLookup kx x t = insertLookupWithKey (\_ a _ -> a) kx x t--- > insertLookup 5 "x" (fromList [(5,"a"), (3,"b")]) == (Just "a", fromList [(3, "b"), (5, "x")])--- > insertLookup 7 "x" (fromList [(5,"a"), (3,"b")]) == (Nothing, fromList [(3, "b"), (5, "a"), (7, "x")])---- See Note: Type of local 'go' function-insertLookupWithKey :: Ord k => (k -> a -> a -> a) -> k -> a -> Map k a- -> (Maybe a, Map k a)-insertLookupWithKey = go- where- go :: Ord k => (k -> a -> a -> a) -> k -> a -> Map k a -> (Maybe a, Map k a)- STRICT_2_OF_4(go)- go _ kx x Tip = (Nothing, singleton kx x)- go f kx x (Bin sy ky y l r) =- case compare kx ky of- LT -> let (found, l') = go f kx x l- in (found, balanceL ky y l' r)- GT -> let (found, r') = go f kx x r- in (found, balanceR ky y l r')- EQ -> (Just y, Bin sy kx (f kx x y) l r)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE insertLookupWithKey #-}-#else-{-# INLINE insertLookupWithKey #-}-#endif--{--------------------------------------------------------------------- Deletion---------------------------------------------------------------------}--- | /O(log n)/. Delete a key and its value from the map. When the key is not--- a member of the map, the original map is returned.------ > delete 5 (fromList [(5,"a"), (3,"b")]) == singleton 3 "b"--- > delete 7 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "a")]--- > delete 5 empty == empty---- See Note: Type of local 'go' function-delete :: Ord k => k -> Map k a -> Map k a-delete = go- where- go :: Ord k => k -> Map k a -> Map k a- STRICT_1_OF_2(go)- go _ Tip = Tip- go k (Bin _ kx x l r) =- case compare k kx of- LT -> balanceR kx x (go k l) r- GT -> balanceL kx x l (go k r)- EQ -> glue l r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE delete #-}-#else-{-# INLINE delete #-}-#endif---- | /O(log n)/. Update a value at a specific key with the result of the provided function.--- When the key is not--- a member of the map, the original map is returned.------ > adjust ("new " ++) 5 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "new a")]--- > adjust ("new " ++) 7 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "a")]--- > adjust ("new " ++) 7 empty == empty--adjust :: Ord k => (a -> a) -> k -> Map k a -> Map k a-adjust f = adjustWithKey (\_ x -> f x)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE adjust #-}-#else-{-# INLINE adjust #-}-#endif---- | /O(log n)/. Adjust a value at a specific key. When the key is not--- a member of the map, the original map is returned.------ > let f key x = (show key) ++ ":new " ++ x--- > adjustWithKey f 5 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "5:new a")]--- > adjustWithKey f 7 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "a")]--- > adjustWithKey f 7 empty == empty--adjustWithKey :: Ord k => (k -> a -> a) -> k -> Map k a -> Map k a-adjustWithKey f = updateWithKey (\k' x' -> Just (f k' x'))-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE adjustWithKey #-}-#else-{-# INLINE adjustWithKey #-}-#endif---- | /O(log n)/. The expression (@'update' f k map@) updates the value @x@--- at @k@ (if it is in the map). If (@f x@) is 'Nothing', the element is--- deleted. If it is (@'Just' y@), the key @k@ is bound to the new value @y@.------ > let f x = if x == "a" then Just "new a" else Nothing--- > update f 5 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "new a")]--- > update f 7 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "a")]--- > update f 3 (fromList [(5,"a"), (3,"b")]) == singleton 5 "a"--update :: Ord k => (a -> Maybe a) -> k -> Map k a -> Map k a-update f = updateWithKey (\_ x -> f x)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE update #-}-#else-{-# INLINE update #-}-#endif---- | /O(log n)/. The expression (@'updateWithKey' f k map@) updates the--- value @x@ at @k@ (if it is in the map). If (@f k x@) is 'Nothing',--- the element is deleted. If it is (@'Just' y@), the key @k@ is bound--- to the new value @y@.------ > let f k x = if x == "a" then Just ((show k) ++ ":new a") else Nothing--- > updateWithKey f 5 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "5:new a")]--- > updateWithKey f 7 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "a")]--- > updateWithKey f 3 (fromList [(5,"a"), (3,"b")]) == singleton 5 "a"---- See Note: Type of local 'go' function-updateWithKey :: Ord k => (k -> a -> Maybe a) -> k -> Map k a -> Map k a-updateWithKey = go- where- go :: Ord k => (k -> a -> Maybe a) -> k -> Map k a -> Map k a- STRICT_2_OF_3(go)- go _ _ Tip = Tip- go f k(Bin sx kx x l r) =- case compare k kx of- LT -> balanceR kx x (go f k l) r- GT -> balanceL kx x l (go f k r)- EQ -> case f kx x of- Just x' -> Bin sx kx x' l r- Nothing -> glue l r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE updateWithKey #-}-#else-{-# INLINE updateWithKey #-}-#endif---- | /O(log n)/. Lookup and update. See also 'updateWithKey'.--- The function returns changed value, if it is updated.--- Returns the original key value if the map entry is deleted.------ > let f k x = if x == "a" then Just ((show k) ++ ":new a") else Nothing--- > updateLookupWithKey f 5 (fromList [(5,"a"), (3,"b")]) == (Just "5:new a", fromList [(3, "b"), (5, "5:new a")])--- > updateLookupWithKey f 7 (fromList [(5,"a"), (3,"b")]) == (Nothing, fromList [(3, "b"), (5, "a")])--- > updateLookupWithKey f 3 (fromList [(5,"a"), (3,"b")]) == (Just "b", singleton 5 "a")---- See Note: Type of local 'go' function-updateLookupWithKey :: Ord k => (k -> a -> Maybe a) -> k -> Map k a -> (Maybe a,Map k a)-updateLookupWithKey = go- where- go :: Ord k => (k -> a -> Maybe a) -> k -> Map k a -> (Maybe a,Map k a)- STRICT_2_OF_3(go)- go _ _ Tip = (Nothing,Tip)- go f k (Bin sx kx x l r) =- case compare k kx of- LT -> let (found,l') = go f k l in (found,balanceR kx x l' r)- GT -> let (found,r') = go f k r in (found,balanceL kx x l r')- EQ -> case f kx x of- Just x' -> (Just x',Bin sx kx x' l r)- Nothing -> (Just x,glue l r)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE updateLookupWithKey #-}-#else-{-# INLINE updateLookupWithKey #-}-#endif---- | /O(log n)/. The expression (@'alter' f k map@) alters the value @x@ at @k@, or absence thereof.--- 'alter' can be used to insert, delete, or update a value in a 'Map'.--- In short : @'lookup' k ('alter' f k m) = f ('lookup' k m)@.------ > let f _ = Nothing--- > alter f 7 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "a")]--- > alter f 5 (fromList [(5,"a"), (3,"b")]) == singleton 3 "b"--- >--- > let f _ = Just "c"--- > alter f 7 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "a"), (7, "c")]--- > alter f 5 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "c")]---- See Note: Type of local 'go' function-alter :: Ord k => (Maybe a -> Maybe a) -> k -> Map k a -> Map k a-alter = go- where- go :: Ord k => (Maybe a -> Maybe a) -> k -> Map k a -> Map k a- STRICT_2_OF_3(go)- go f k Tip = case f Nothing of- Nothing -> Tip- Just x -> singleton k x-- go f k (Bin sx kx x l r) = case compare k kx of- LT -> balance kx x (go f k l) r- GT -> balance kx x l (go f k r)- EQ -> case f (Just x) of- Just x' -> Bin sx kx x' l r- Nothing -> glue l r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE alter #-}-#else-{-# INLINE alter #-}-#endif--{--------------------------------------------------------------------- Indexing---------------------------------------------------------------------}--- | /O(log n)/. Return the /index/ of a key, which is its zero-based index in--- the sequence sorted by keys. The index is a number from /0/ up to, but not--- including, the 'size' of the map. Calls 'error' when the key is not--- a 'member' of the map.------ > findIndex 2 (fromList [(5,"a"), (3,"b")]) Error: element is not in the map--- > findIndex 3 (fromList [(5,"a"), (3,"b")]) == 0--- > findIndex 5 (fromList [(5,"a"), (3,"b")]) == 1--- > findIndex 6 (fromList [(5,"a"), (3,"b")]) Error: element is not in the map---- See Note: Type of local 'go' function-findIndex :: Ord k => k -> Map k a -> Int-findIndex = go 0- where- go :: Ord k => Int -> k -> Map k a -> Int- STRICT_1_OF_3(go)- STRICT_2_OF_3(go)- go _ _ Tip = error "Map.findIndex: element is not in the map"- go idx k (Bin _ kx _ l r) = case compare k kx of- LT -> go idx k l- GT -> go (idx + size l + 1) k r- EQ -> idx + size l-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE findIndex #-}-#endif---- | /O(log n)/. Lookup the /index/ of a key, which is its zero-based index in--- the sequence sorted by keys. The index is a number from /0/ up to, but not--- including, the 'size' of the map.------ > isJust (lookupIndex 2 (fromList [(5,"a"), (3,"b")])) == False--- > fromJust (lookupIndex 3 (fromList [(5,"a"), (3,"b")])) == 0--- > fromJust (lookupIndex 5 (fromList [(5,"a"), (3,"b")])) == 1--- > isJust (lookupIndex 6 (fromList [(5,"a"), (3,"b")])) == False---- See Note: Type of local 'go' function-lookupIndex :: Ord k => k -> Map k a -> Maybe Int-lookupIndex = go 0- where- go :: Ord k => Int -> k -> Map k a -> Maybe Int- STRICT_1_OF_3(go)- STRICT_2_OF_3(go)- go _ _ Tip = Nothing- go idx k (Bin _ kx _ l r) = case compare k kx of- LT -> go idx k l- GT -> go (idx + size l + 1) k r- EQ -> Just $! idx + size l-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE lookupIndex #-}-#endif---- | /O(log n)/. Retrieve an element by its /index/, i.e. by its zero-based--- index in the sequence sorted by keys. If the /index/ is out of range (less--- than zero, greater or equal to 'size' of the map), 'error' is called.------ > elemAt 0 (fromList [(5,"a"), (3,"b")]) == (3,"b")--- > elemAt 1 (fromList [(5,"a"), (3,"b")]) == (5, "a")--- > elemAt 2 (fromList [(5,"a"), (3,"b")]) Error: index out of range--elemAt :: Int -> Map k a -> (k,a)-STRICT_1_OF_2(elemAt)-elemAt _ Tip = error "Map.elemAt: index out of range"-elemAt i (Bin _ kx x l r)- = case compare i sizeL of- LT -> elemAt i l- GT -> elemAt (i-sizeL-1) r- EQ -> (kx,x)- where- sizeL = size l---- | /O(log n)/. Update the element at /index/, i.e. by its zero-based index in--- the sequence sorted by keys. If the /index/ is out of range (less than zero,--- greater or equal to 'size' of the map), 'error' is called.------ > updateAt (\ _ _ -> Just "x") 0 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "x"), (5, "a")]--- > updateAt (\ _ _ -> Just "x") 1 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "x")]--- > updateAt (\ _ _ -> Just "x") 2 (fromList [(5,"a"), (3,"b")]) Error: index out of range--- > updateAt (\ _ _ -> Just "x") (-1) (fromList [(5,"a"), (3,"b")]) Error: index out of range--- > updateAt (\_ _ -> Nothing) 0 (fromList [(5,"a"), (3,"b")]) == singleton 5 "a"--- > updateAt (\_ _ -> Nothing) 1 (fromList [(5,"a"), (3,"b")]) == singleton 3 "b"--- > updateAt (\_ _ -> Nothing) 2 (fromList [(5,"a"), (3,"b")]) Error: index out of range--- > updateAt (\_ _ -> Nothing) (-1) (fromList [(5,"a"), (3,"b")]) Error: index out of range--updateAt :: (k -> a -> Maybe a) -> Int -> Map k a -> Map k a-updateAt f i t = i `seq`- case t of- Tip -> error "Map.updateAt: index out of range"- Bin sx kx x l r -> case compare i sizeL of- LT -> balanceR kx x (updateAt f i l) r- GT -> balanceL kx x l (updateAt f (i-sizeL-1) r)- EQ -> case f kx x of- Just x' -> Bin sx kx x' l r- Nothing -> glue l r- where- sizeL = size l---- | /O(log n)/. Delete the element at /index/, i.e. by its zero-based index in--- the sequence sorted by keys. If the /index/ is out of range (less than zero,--- greater or equal to 'size' of the map), 'error' is called.------ > deleteAt 0 (fromList [(5,"a"), (3,"b")]) == singleton 5 "a"--- > deleteAt 1 (fromList [(5,"a"), (3,"b")]) == singleton 3 "b"--- > deleteAt 2 (fromList [(5,"a"), (3,"b")]) Error: index out of range--- > deleteAt (-1) (fromList [(5,"a"), (3,"b")]) Error: index out of range--deleteAt :: Int -> Map k a -> Map k a-deleteAt i t = i `seq`- case t of- Tip -> error "Map.deleteAt: index out of range"- Bin _ kx x l r -> case compare i sizeL of- LT -> balanceR kx x (deleteAt i l) r- GT -> balanceL kx x l (deleteAt (i-sizeL-1) r)- EQ -> glue l r- where- sizeL = size l---{--------------------------------------------------------------------- Minimal, Maximal---------------------------------------------------------------------}--- | /O(log n)/. The minimal key of the map. Calls 'error' if the map is empty.------ > findMin (fromList [(5,"a"), (3,"b")]) == (3,"b")--- > findMin empty Error: empty map has no minimal element--findMin :: Map k a -> (k,a)-findMin (Bin _ kx x Tip _) = (kx,x)-findMin (Bin _ _ _ l _) = findMin l-findMin Tip = error "Map.findMin: empty map has no minimal element"---- | /O(log n)/. The maximal key of the map. Calls 'error' if the map is empty.------ > findMax (fromList [(5,"a"), (3,"b")]) == (5,"a")--- > findMax empty Error: empty map has no maximal element--findMax :: Map k a -> (k,a)-findMax (Bin _ kx x _ Tip) = (kx,x)-findMax (Bin _ _ _ _ r) = findMax r-findMax Tip = error "Map.findMax: empty map has no maximal element"---- | /O(log n)/. Delete the minimal key. Returns an empty map if the map is empty.------ > deleteMin (fromList [(5,"a"), (3,"b"), (7,"c")]) == fromList [(5,"a"), (7,"c")]--- > deleteMin empty == empty--deleteMin :: Map k a -> Map k a-deleteMin (Bin _ _ _ Tip r) = r-deleteMin (Bin _ kx x l r) = balanceR kx x (deleteMin l) r-deleteMin Tip = Tip---- | /O(log n)/. Delete the maximal key. Returns an empty map if the map is empty.------ > deleteMax (fromList [(5,"a"), (3,"b"), (7,"c")]) == fromList [(3,"b"), (5,"a")]--- > deleteMax empty == empty--deleteMax :: Map k a -> Map k a-deleteMax (Bin _ _ _ l Tip) = l-deleteMax (Bin _ kx x l r) = balanceL kx x l (deleteMax r)-deleteMax Tip = Tip---- | /O(log n)/. Update the value at the minimal key.------ > updateMin (\ a -> Just ("X" ++ a)) (fromList [(5,"a"), (3,"b")]) == fromList [(3, "Xb"), (5, "a")]--- > updateMin (\ _ -> Nothing) (fromList [(5,"a"), (3,"b")]) == singleton 5 "a"--updateMin :: (a -> Maybe a) -> Map k a -> Map k a-updateMin f m- = updateMinWithKey (\_ x -> f x) m---- | /O(log n)/. Update the value at the maximal key.------ > updateMax (\ a -> Just ("X" ++ a)) (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "Xa")]--- > updateMax (\ _ -> Nothing) (fromList [(5,"a"), (3,"b")]) == singleton 3 "b"--updateMax :: (a -> Maybe a) -> Map k a -> Map k a-updateMax f m- = updateMaxWithKey (\_ x -> f x) m----- | /O(log n)/. Update the value at the minimal key.------ > updateMinWithKey (\ k a -> Just ((show k) ++ ":" ++ a)) (fromList [(5,"a"), (3,"b")]) == fromList [(3,"3:b"), (5,"a")]--- > updateMinWithKey (\ _ _ -> Nothing) (fromList [(5,"a"), (3,"b")]) == singleton 5 "a"--updateMinWithKey :: (k -> a -> Maybe a) -> Map k a -> Map k a-updateMinWithKey _ Tip = Tip-updateMinWithKey f (Bin sx kx x Tip r) = case f kx x of- Nothing -> r- Just x' -> Bin sx kx x' Tip r-updateMinWithKey f (Bin _ kx x l r) = balanceR kx x (updateMinWithKey f l) r---- | /O(log n)/. Update the value at the maximal key.------ > updateMaxWithKey (\ k a -> Just ((show k) ++ ":" ++ a)) (fromList [(5,"a"), (3,"b")]) == fromList [(3,"b"), (5,"5:a")]--- > updateMaxWithKey (\ _ _ -> Nothing) (fromList [(5,"a"), (3,"b")]) == singleton 3 "b"--updateMaxWithKey :: (k -> a -> Maybe a) -> Map k a -> Map k a-updateMaxWithKey _ Tip = Tip-updateMaxWithKey f (Bin sx kx x l Tip) = case f kx x of- Nothing -> l- Just x' -> Bin sx kx x' l Tip-updateMaxWithKey f (Bin _ kx x l r) = balanceL kx x l (updateMaxWithKey f r)---- | /O(log n)/. Retrieves the minimal (key,value) pair of the map, and--- the map stripped of that element, or 'Nothing' if passed an empty map.------ > minViewWithKey (fromList [(5,"a"), (3,"b")]) == Just ((3,"b"), singleton 5 "a")--- > minViewWithKey empty == Nothing--minViewWithKey :: Map k a -> Maybe ((k,a), Map k a)-minViewWithKey Tip = Nothing-minViewWithKey x = Just (deleteFindMin x)---- | /O(log n)/. Retrieves the maximal (key,value) pair of the map, and--- the map stripped of that element, or 'Nothing' if passed an empty map.------ > maxViewWithKey (fromList [(5,"a"), (3,"b")]) == Just ((5,"a"), singleton 3 "b")--- > maxViewWithKey empty == Nothing--maxViewWithKey :: Map k a -> Maybe ((k,a), Map k a)-maxViewWithKey Tip = Nothing-maxViewWithKey x = Just (deleteFindMax x)---- | /O(log n)/. Retrieves the value associated with minimal key of the--- map, and the map stripped of that element, or 'Nothing' if passed an--- empty map.------ > minView (fromList [(5,"a"), (3,"b")]) == Just ("b", singleton 5 "a")--- > minView empty == Nothing--minView :: Map k a -> Maybe (a, Map k a)-minView Tip = Nothing-minView x = Just (first snd $ deleteFindMin x)---- | /O(log n)/. Retrieves the value associated with maximal key of the--- map, and the map stripped of that element, or 'Nothing' if passed an------ > maxView (fromList [(5,"a"), (3,"b")]) == Just ("a", singleton 3 "b")--- > maxView empty == Nothing--maxView :: Map k a -> Maybe (a, Map k a)-maxView Tip = Nothing-maxView x = Just (first snd $ deleteFindMax x)---- Update the 1st component of a tuple (special case of Control.Arrow.first)-first :: (a -> b) -> (a,c) -> (b,c)-first f (x,y) = (f x, y)--{--------------------------------------------------------------------- Union.---------------------------------------------------------------------}--- | The union of a list of maps:--- (@'unions' == 'Prelude.foldl' 'union' 'empty'@).------ > unions [(fromList [(5, "a"), (3, "b")]), (fromList [(5, "A"), (7, "C")]), (fromList [(5, "A3"), (3, "B3")])]--- > == fromList [(3, "b"), (5, "a"), (7, "C")]--- > unions [(fromList [(5, "A3"), (3, "B3")]), (fromList [(5, "A"), (7, "C")]), (fromList [(5, "a"), (3, "b")])]--- > == fromList [(3, "B3"), (5, "A3"), (7, "C")]--unions :: Ord k => [Map k a] -> Map k a-unions ts- = foldlStrict union empty ts-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE unions #-}-#endif---- | The union of a list of maps, with a combining operation:--- (@'unionsWith' f == 'Prelude.foldl' ('unionWith' f) 'empty'@).------ > unionsWith (++) [(fromList [(5, "a"), (3, "b")]), (fromList [(5, "A"), (7, "C")]), (fromList [(5, "A3"), (3, "B3")])]--- > == fromList [(3, "bB3"), (5, "aAA3"), (7, "C")]--unionsWith :: Ord k => (a->a->a) -> [Map k a] -> Map k a-unionsWith f ts- = foldlStrict (unionWith f) empty ts-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE unionsWith #-}-#endif---- | /O(n+m)/.--- The expression (@'union' t1 t2@) takes the left-biased union of @t1@ and @t2@.--- It prefers @t1@ when duplicate keys are encountered,--- i.e. (@'union' == 'unionWith' 'const'@).--- The implementation uses the efficient /hedge-union/ algorithm.------ > union (fromList [(5, "a"), (3, "b")]) (fromList [(5, "A"), (7, "C")]) == fromList [(3, "b"), (5, "a"), (7, "C")]--union :: Ord k => Map k a -> Map k a -> Map k a-union Tip t2 = t2-union t1 Tip = t1-union t1 t2 = hedgeUnion NothingS NothingS t1 t2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE union #-}-#endif---- left-biased hedge union-hedgeUnion :: Ord a => MaybeS a -> MaybeS a -> Map a b -> Map a b -> Map a b-hedgeUnion _ _ t1 Tip = t1-hedgeUnion blo bhi Tip (Bin _ kx x l r) = join kx x (filterGt blo l) (filterLt bhi r)-hedgeUnion _ _ t1 (Bin _ kx x Tip Tip) = insertR kx x t1 -- According to benchmarks, this special case increases- -- performance up to 30%. It does not help in difference or intersection.-hedgeUnion blo bhi (Bin _ kx x l r) t2 = join kx x (hedgeUnion blo bmi l (trim blo bmi t2))- (hedgeUnion bmi bhi r (trim bmi bhi t2))- where bmi = JustS kx-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE hedgeUnion #-}-#endif--{--------------------------------------------------------------------- Union with a combining function---------------------------------------------------------------------}--- | /O(n+m)/. Union with a combining function. The implementation uses the efficient /hedge-union/ algorithm.------ > unionWith (++) (fromList [(5, "a"), (3, "b")]) (fromList [(5, "A"), (7, "C")]) == fromList [(3, "b"), (5, "aA"), (7, "C")]--unionWith :: Ord k => (a -> a -> a) -> Map k a -> Map k a -> Map k a-unionWith f m1 m2- = unionWithKey (\_ x y -> f x y) m1 m2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE unionWith #-}-#endif---- | /O(n+m)/.--- Union with a combining function. The implementation uses the efficient /hedge-union/ algorithm.------ > let f key left_value right_value = (show key) ++ ":" ++ left_value ++ "|" ++ right_value--- > unionWithKey f (fromList [(5, "a"), (3, "b")]) (fromList [(5, "A"), (7, "C")]) == fromList [(3, "b"), (5, "5:a|A"), (7, "C")]--unionWithKey :: Ord k => (k -> a -> a -> a) -> Map k a -> Map k a -> Map k a-unionWithKey f t1 t2 = mergeWithKey (\k x1 x2 -> Just $ f k x1 x2) id id t1 t2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE unionWithKey #-}-#endif--{--------------------------------------------------------------------- Difference---------------------------------------------------------------------}--- | /O(n+m)/. Difference of two maps.--- Return elements of the first map not existing in the second map.--- The implementation uses an efficient /hedge/ algorithm comparable with /hedge-union/.------ > difference (fromList [(5, "a"), (3, "b")]) (fromList [(5, "A"), (7, "C")]) == singleton 3 "b"--difference :: Ord k => Map k a -> Map k b -> Map k a-difference Tip _ = Tip-difference t1 Tip = t1-difference t1 t2 = hedgeDiff NothingS NothingS t1 t2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE difference #-}-#endif--hedgeDiff :: Ord a => MaybeS a -> MaybeS a -> Map a b -> Map a c -> Map a b-hedgeDiff _ _ Tip _ = Tip-hedgeDiff blo bhi (Bin _ kx x l r) Tip = join kx x (filterGt blo l) (filterLt bhi r)-hedgeDiff blo bhi t (Bin _ kx _ l r) = merge (hedgeDiff blo bmi (trim blo bmi t) l)- (hedgeDiff bmi bhi (trim bmi bhi t) r)- where bmi = JustS kx-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE hedgeDiff #-}-#endif---- | /O(n+m)/. Difference with a combining function.--- When two equal keys are--- encountered, the combining function is applied to the values of these keys.--- If it returns 'Nothing', the element is discarded (proper set difference). If--- it returns (@'Just' y@), the element is updated with a new value @y@.--- The implementation uses an efficient /hedge/ algorithm comparable with /hedge-union/.------ > let f al ar = if al == "b" then Just (al ++ ":" ++ ar) else Nothing--- > differenceWith f (fromList [(5, "a"), (3, "b")]) (fromList [(5, "A"), (3, "B"), (7, "C")])--- > == singleton 3 "b:B"--differenceWith :: Ord k => (a -> b -> Maybe a) -> Map k a -> Map k b -> Map k a-differenceWith f m1 m2- = differenceWithKey (\_ x y -> f x y) m1 m2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE differenceWith #-}-#endif---- | /O(n+m)/. Difference with a combining function. When two equal keys are--- encountered, the combining function is applied to the key and both values.--- If it returns 'Nothing', the element is discarded (proper set difference). If--- it returns (@'Just' y@), the element is updated with a new value @y@.--- The implementation uses an efficient /hedge/ algorithm comparable with /hedge-union/.------ > let f k al ar = if al == "b" then Just ((show k) ++ ":" ++ al ++ "|" ++ ar) else Nothing--- > differenceWithKey f (fromList [(5, "a"), (3, "b")]) (fromList [(5, "A"), (3, "B"), (10, "C")])--- > == singleton 3 "3:b|B"--differenceWithKey :: Ord k => (k -> a -> b -> Maybe a) -> Map k a -> Map k b -> Map k a-differenceWithKey f t1 t2 = mergeWithKey f id (const Tip) t1 t2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE differenceWithKey #-}-#endif---{--------------------------------------------------------------------- Intersection---------------------------------------------------------------------}--- | /O(n+m)/. Intersection of two maps.--- Return data in the first map for the keys existing in both maps.--- (@'intersection' m1 m2 == 'intersectionWith' 'const' m1 m2@).--- The implementation uses an efficient /hedge/ algorithm comparable with--- /hedge-union/.------ > intersection (fromList [(5, "a"), (3, "b")]) (fromList [(5, "A"), (7, "C")]) == singleton 5 "a"--intersection :: Ord k => Map k a -> Map k b -> Map k a-intersection Tip _ = Tip-intersection _ Tip = Tip-intersection t1 t2 = hedgeInt NothingS NothingS t1 t2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE intersection #-}-#endif--hedgeInt :: Ord k => MaybeS k -> MaybeS k -> Map k a -> Map k b -> Map k a-hedgeInt _ _ _ Tip = Tip-hedgeInt _ _ Tip _ = Tip-hedgeInt blo bhi (Bin _ kx x l r) t2 = let l' = hedgeInt blo bmi l (trim blo bmi t2)- r' = hedgeInt bmi bhi r (trim bmi bhi t2)- in if kx `member` t2 then join kx x l' r' else merge l' r'- where bmi = JustS kx-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE hedgeInt #-}-#endif---- | /O(n+m)/. Intersection with a combining function. The implementation uses--- an efficient /hedge/ algorithm comparable with /hedge-union/.------ > intersectionWith (++) (fromList [(5, "a"), (3, "b")]) (fromList [(5, "A"), (7, "C")]) == singleton 5 "aA"--intersectionWith :: Ord k => (a -> b -> c) -> Map k a -> Map k b -> Map k c-intersectionWith f m1 m2- = intersectionWithKey (\_ x y -> f x y) m1 m2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE intersectionWith #-}-#endif---- | /O(n+m)/. Intersection with a combining function. The implementation uses--- an efficient /hedge/ algorithm comparable with /hedge-union/.------ > let f k al ar = (show k) ++ ":" ++ al ++ "|" ++ ar--- > intersectionWithKey f (fromList [(5, "a"), (3, "b")]) (fromList [(5, "A"), (7, "C")]) == singleton 5 "5:a|A"---intersectionWithKey :: Ord k => (k -> a -> b -> c) -> Map k a -> Map k b -> Map k c-intersectionWithKey f t1 t2 = mergeWithKey (\k x1 x2 -> Just $ f k x1 x2) (const Tip) (const Tip) t1 t2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE intersectionWithKey #-}-#endif---{--------------------------------------------------------------------- MergeWithKey---------------------------------------------------------------------}---- | /O(n+m)/. A high-performance universal combining function. This function--- is used to define 'unionWith', 'unionWithKey', 'differenceWith',--- 'differenceWithKey', 'intersectionWith', 'intersectionWithKey' and can be--- used to define other custom combine functions.------ Please make sure you know what is going on when using 'mergeWithKey',--- otherwise you can be surprised by unexpected code growth or even--- corruption of the data structure.------ When 'mergeWithKey' is given three arguments, it is inlined to the call--- site. You should therefore use 'mergeWithKey' only to define your custom--- combining functions. For example, you could define 'unionWithKey',--- 'differenceWithKey' and 'intersectionWithKey' as------ > myUnionWithKey f m1 m2 = mergeWithKey (\k x1 x2 -> Just (f k x1 x2)) id id m1 m2--- > myDifferenceWithKey f m1 m2 = mergeWithKey f id (const empty) m1 m2--- > myIntersectionWithKey f m1 m2 = mergeWithKey (\k x1 x2 -> Just (f k x1 x2)) (const empty) (const empty) m1 m2------ When calling @'mergeWithKey' combine only1 only2@, a function combining two--- 'IntMap's is created, such that------ * if a key is present in both maps, it is passed with both corresponding--- values to the @combine@ function. Depending on the result, the key is either--- present in the result with specified value, or is left out;------ * a nonempty subtree present only in the first map is passed to @only1@ and--- the output is added to the result;------ * a nonempty subtree present only in the second map is passed to @only2@ and--- the output is added to the result.------ The @only1@ and @only2@ methods /must return a map with a subset (possibly empty) of the keys of the given map/.--- The values can be modified arbitrarily. Most common variants of @only1@ and--- @only2@ are 'id' and @'const' 'empty'@, but for example @'map' f@ or--- @'filterWithKey' f@ could be used for any @f@.--mergeWithKey :: Ord k => (k -> a -> b -> Maybe c) -> (Map k a -> Map k c) -> (Map k b -> Map k c)- -> Map k a -> Map k b -> Map k c-mergeWithKey f g1 g2 = go- where- go Tip t2 = g2 t2- go t1 Tip = g1 t1- go t1 t2 = hedgeMerge NothingS NothingS t1 t2-- hedgeMerge _ _ t1 Tip = g1 t1- hedgeMerge blo bhi Tip (Bin _ kx x l r) = g2 $ join kx x (filterGt blo l) (filterLt bhi r)- hedgeMerge blo bhi (Bin _ kx x l r) t2 = let l' = hedgeMerge blo bmi l (trim blo bmi t2)- (found, trim_t2) = trimLookupLo kx bhi t2- r' = hedgeMerge bmi bhi r trim_t2- in case found of- Nothing -> case g1 (singleton kx x) of- Tip -> merge l' r'- (Bin _ _ x' Tip Tip) -> join kx x' l' r'- _ -> error "mergeWithKey: Given function only1 does not fulfil required conditions (see documentation)"- Just x2 -> case f kx x x2 of- Nothing -> merge l' r'- Just x' -> join kx x' l' r'- where bmi = JustS kx-{-# INLINE mergeWithKey #-}--{--------------------------------------------------------------------- Submap---------------------------------------------------------------------}--- | /O(n+m)/.--- This function is defined as (@'isSubmapOf' = 'isSubmapOfBy' (==)@).----isSubmapOf :: (Ord k,Eq a) => Map k a -> Map k a -> Bool-isSubmapOf m1 m2 = isSubmapOfBy (==) m1 m2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE isSubmapOf #-}-#endif--{- | /O(n+m)/.- The expression (@'isSubmapOfBy' f t1 t2@) returns 'True' if- all keys in @t1@ are in tree @t2@, and when @f@ returns 'True' when- applied to their respective values. For example, the following- expressions are all 'True':-- > isSubmapOfBy (==) (fromList [('a',1)]) (fromList [('a',1),('b',2)])- > isSubmapOfBy (<=) (fromList [('a',1)]) (fromList [('a',1),('b',2)])- > isSubmapOfBy (==) (fromList [('a',1),('b',2)]) (fromList [('a',1),('b',2)])-- But the following are all 'False':-- > isSubmapOfBy (==) (fromList [('a',2)]) (fromList [('a',1),('b',2)])- > isSubmapOfBy (<) (fromList [('a',1)]) (fromList [('a',1),('b',2)])- > isSubmapOfBy (==) (fromList [('a',1),('b',2)]) (fromList [('a',1)])----}-isSubmapOfBy :: Ord k => (a->b->Bool) -> Map k a -> Map k b -> Bool-isSubmapOfBy f t1 t2- = (size t1 <= size t2) && (submap' f t1 t2)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE isSubmapOfBy #-}-#endif--submap' :: Ord a => (b -> c -> Bool) -> Map a b -> Map a c -> Bool-submap' _ Tip _ = True-submap' _ _ Tip = False-submap' f (Bin _ kx x l r) t- = case found of- Nothing -> False- Just y -> f x y && submap' f l lt && submap' f r gt- where- (lt,found,gt) = splitLookup kx t-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE submap' #-}-#endif---- | /O(n+m)/. Is this a proper submap? (ie. a submap but not equal).--- Defined as (@'isProperSubmapOf' = 'isProperSubmapOfBy' (==)@).-isProperSubmapOf :: (Ord k,Eq a) => Map k a -> Map k a -> Bool-isProperSubmapOf m1 m2- = isProperSubmapOfBy (==) m1 m2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE isProperSubmapOf #-}-#endif--{- | /O(n+m)/. Is this a proper submap? (ie. a submap but not equal).- The expression (@'isProperSubmapOfBy' f m1 m2@) returns 'True' when- @m1@ and @m2@ are not equal,- all keys in @m1@ are in @m2@, and when @f@ returns 'True' when- applied to their respective values. For example, the following- expressions are all 'True':-- > isProperSubmapOfBy (==) (fromList [(1,1)]) (fromList [(1,1),(2,2)])- > isProperSubmapOfBy (<=) (fromList [(1,1)]) (fromList [(1,1),(2,2)])-- But the following are all 'False':-- > isProperSubmapOfBy (==) (fromList [(1,1),(2,2)]) (fromList [(1,1),(2,2)])- > isProperSubmapOfBy (==) (fromList [(1,1),(2,2)]) (fromList [(1,1)])- > isProperSubmapOfBy (<) (fromList [(1,1)]) (fromList [(1,1),(2,2)])----}-isProperSubmapOfBy :: Ord k => (a -> b -> Bool) -> Map k a -> Map k b -> Bool-isProperSubmapOfBy f t1 t2- = (size t1 < size t2) && (submap' f t1 t2)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE isProperSubmapOfBy #-}-#endif--{--------------------------------------------------------------------- Filter and partition---------------------------------------------------------------------}--- | /O(n)/. Filter all values that satisfy the predicate.------ > filter (> "a") (fromList [(5,"a"), (3,"b")]) == singleton 3 "b"--- > filter (> "x") (fromList [(5,"a"), (3,"b")]) == empty--- > filter (< "a") (fromList [(5,"a"), (3,"b")]) == empty--filter :: (a -> Bool) -> Map k a -> Map k a-filter p m- = filterWithKey (\_ x -> p x) m---- | /O(n)/. Filter all keys\/values that satisfy the predicate.------ > filterWithKey (\k _ -> k > 4) (fromList [(5,"a"), (3,"b")]) == singleton 5 "a"--filterWithKey :: (k -> a -> Bool) -> Map k a -> Map k a-filterWithKey _ Tip = Tip-filterWithKey p (Bin _ kx x l r)- | p kx x = join kx x (filterWithKey p l) (filterWithKey p r)- | otherwise = merge (filterWithKey p l) (filterWithKey p r)---- | /O(n)/. Partition the map according to a predicate. The first--- map contains all elements that satisfy the predicate, the second all--- elements that fail the predicate. See also 'split'.------ > partition (> "a") (fromList [(5,"a"), (3,"b")]) == (singleton 3 "b", singleton 5 "a")--- > partition (< "x") (fromList [(5,"a"), (3,"b")]) == (fromList [(3, "b"), (5, "a")], empty)--- > partition (> "x") (fromList [(5,"a"), (3,"b")]) == (empty, fromList [(3, "b"), (5, "a")])--partition :: (a -> Bool) -> Map k a -> (Map k a,Map k a)-partition p m- = partitionWithKey (\_ x -> p x) m---- | /O(n)/. Partition the map according to a predicate. The first--- map contains all elements that satisfy the predicate, the second all--- elements that fail the predicate. See also 'split'.------ > partitionWithKey (\ k _ -> k > 3) (fromList [(5,"a"), (3,"b")]) == (singleton 5 "a", singleton 3 "b")--- > partitionWithKey (\ k _ -> k < 7) (fromList [(5,"a"), (3,"b")]) == (fromList [(3, "b"), (5, "a")], empty)--- > partitionWithKey (\ k _ -> k > 7) (fromList [(5,"a"), (3,"b")]) == (empty, fromList [(3, "b"), (5, "a")])--partitionWithKey :: (k -> a -> Bool) -> Map k a -> (Map k a,Map k a)-partitionWithKey p0 t0 = toPair $ go p0 t0- where- go _ Tip = (Tip :*: Tip)- go p (Bin _ kx x l r)- | p kx x = join kx x l1 r1 :*: merge l2 r2- | otherwise = merge l1 r1 :*: join kx x l2 r2- where- (l1 :*: l2) = go p l- (r1 :*: r2) = go p r---- | /O(n)/. Map values and collect the 'Just' results.------ > let f x = if x == "a" then Just "new a" else Nothing--- > mapMaybe f (fromList [(5,"a"), (3,"b")]) == singleton 5 "new a"--mapMaybe :: (a -> Maybe b) -> Map k a -> Map k b-mapMaybe f = mapMaybeWithKey (\_ x -> f x)---- | /O(n)/. Map keys\/values and collect the 'Just' results.------ > let f k _ = if k < 5 then Just ("key : " ++ (show k)) else Nothing--- > mapMaybeWithKey f (fromList [(5,"a"), (3,"b")]) == singleton 3 "key : 3"--mapMaybeWithKey :: (k -> a -> Maybe b) -> Map k a -> Map k b-mapMaybeWithKey _ Tip = Tip-mapMaybeWithKey f (Bin _ kx x l r) = case f kx x of- Just y -> join kx y (mapMaybeWithKey f l) (mapMaybeWithKey f r)- Nothing -> merge (mapMaybeWithKey f l) (mapMaybeWithKey f r)---- | /O(n)/. Map values and separate the 'Left' and 'Right' results.------ > let f a = if a < "c" then Left a else Right a--- > mapEither f (fromList [(5,"a"), (3,"b"), (1,"x"), (7,"z")])--- > == (fromList [(3,"b"), (5,"a")], fromList [(1,"x"), (7,"z")])--- >--- > mapEither (\ a -> Right a) (fromList [(5,"a"), (3,"b"), (1,"x"), (7,"z")])--- > == (empty, fromList [(5,"a"), (3,"b"), (1,"x"), (7,"z")])--mapEither :: (a -> Either b c) -> Map k a -> (Map k b, Map k c)-mapEither f m- = mapEitherWithKey (\_ x -> f x) m---- | /O(n)/. Map keys\/values and separate the 'Left' and 'Right' results.------ > let f k a = if k < 5 then Left (k * 2) else Right (a ++ a)--- > mapEitherWithKey f (fromList [(5,"a"), (3,"b"), (1,"x"), (7,"z")])--- > == (fromList [(1,2), (3,6)], fromList [(5,"aa"), (7,"zz")])--- >--- > mapEitherWithKey (\_ a -> Right a) (fromList [(5,"a"), (3,"b"), (1,"x"), (7,"z")])--- > == (empty, fromList [(1,"x"), (3,"b"), (5,"a"), (7,"z")])--mapEitherWithKey :: (k -> a -> Either b c) -> Map k a -> (Map k b, Map k c)-mapEitherWithKey f0 t0 = toPair $ go f0 t0- where- go _ Tip = (Tip :*: Tip)- go f (Bin _ kx x l r) = case f kx x of- Left y -> join kx y l1 r1 :*: merge l2 r2- Right z -> merge l1 r1 :*: join kx z l2 r2- where- (l1 :*: l2) = go f l- (r1 :*: r2) = go f r--{--------------------------------------------------------------------- Mapping---------------------------------------------------------------------}--- | /O(n)/. Map a function over all values in the map.------ > map (++ "x") (fromList [(5,"a"), (3,"b")]) == fromList [(3, "bx"), (5, "ax")]--map :: (a -> b) -> Map k a -> Map k b-map _ Tip = Tip-map f (Bin sx kx x l r) = Bin sx kx (f x) (map f l) (map f r)---- | /O(n)/. Map a function over all values in the map.------ > let f key x = (show key) ++ ":" ++ x--- > mapWithKey f (fromList [(5,"a"), (3,"b")]) == fromList [(3, "3:b"), (5, "5:a")]--mapWithKey :: (k -> a -> b) -> Map k a -> Map k b-mapWithKey _ Tip = Tip-mapWithKey f (Bin sx kx x l r) = Bin sx kx (f kx x) (mapWithKey f l) (mapWithKey f r)---- | /O(n)/.--- @'traverseWithKey' f s == 'fromList' <$> 'traverse' (\(k, v) -> (,) k <$> f k v) ('toList' m)@--- That is, behaves exactly like a regular 'traverse' except that the traversing--- function also has access to the key associated with a value.------ > traverseWithKey (\k v -> if odd k then Just (succ v) else Nothing) (fromList [(1, 'a'), (5, 'e')]) == Just (fromList [(1, 'b'), (5, 'f')])--- > traverseWithKey (\k v -> if odd k then Just (succ v) else Nothing) (fromList [(2, 'c')]) == Nothing-{-# INLINE traverseWithKey #-}-traverseWithKey :: Applicative t => (k -> a -> t b) -> Map k a -> t (Map k b)-traverseWithKey f = go- where- go Tip = pure Tip- go (Bin s k v l r)- = flip (Bin s k) <$> go l <*> f k v <*> go r---- | /O(n)/. The function 'mapAccum' threads an accumulating--- argument through the map in ascending order of keys.------ > let f a b = (a ++ b, b ++ "X")--- > mapAccum f "Everything: " (fromList [(5,"a"), (3,"b")]) == ("Everything: ba", fromList [(3, "bX"), (5, "aX")])--mapAccum :: (a -> b -> (a,c)) -> a -> Map k b -> (a,Map k c)-mapAccum f a m- = mapAccumWithKey (\a' _ x' -> f a' x') a m---- | /O(n)/. The function 'mapAccumWithKey' threads an accumulating--- argument through the map in ascending order of keys.------ > let f a k b = (a ++ " " ++ (show k) ++ "-" ++ b, b ++ "X")--- > mapAccumWithKey f "Everything:" (fromList [(5,"a"), (3,"b")]) == ("Everything: 3-b 5-a", fromList [(3, "bX"), (5, "aX")])--mapAccumWithKey :: (a -> k -> b -> (a,c)) -> a -> Map k b -> (a,Map k c)-mapAccumWithKey f a t- = mapAccumL f a t---- | /O(n)/. The function 'mapAccumL' threads an accumulating--- argument through the map in ascending order of keys.-mapAccumL :: (a -> k -> b -> (a,c)) -> a -> Map k b -> (a,Map k c)-mapAccumL _ a Tip = (a,Tip)-mapAccumL f a (Bin sx kx x l r) =- let (a1,l') = mapAccumL f a l- (a2,x') = f a1 kx x- (a3,r') = mapAccumL f a2 r- in (a3,Bin sx kx x' l' r')---- | /O(n)/. The function 'mapAccumR' threads an accumulating--- argument through the map in descending order of keys.-mapAccumRWithKey :: (a -> k -> b -> (a,c)) -> a -> Map k b -> (a,Map k c)-mapAccumRWithKey _ a Tip = (a,Tip)-mapAccumRWithKey f a (Bin sx kx x l r) =- let (a1,r') = mapAccumRWithKey f a r- (a2,x') = f a1 kx x- (a3,l') = mapAccumRWithKey f a2 l- in (a3,Bin sx kx x' l' r')---- | /O(n*log n)/.--- @'mapKeys' f s@ is the map obtained by applying @f@ to each key of @s@.------ The size of the result may be smaller if @f@ maps two or more distinct--- keys to the same new key. In this case the value at the greatest of the--- original keys is retained.------ > mapKeys (+ 1) (fromList [(5,"a"), (3,"b")]) == fromList [(4, "b"), (6, "a")]--- > mapKeys (\ _ -> 1) (fromList [(1,"b"), (2,"a"), (3,"d"), (4,"c")]) == singleton 1 "c"--- > mapKeys (\ _ -> 3) (fromList [(1,"b"), (2,"a"), (3,"d"), (4,"c")]) == singleton 3 "c"--mapKeys :: Ord k2 => (k1->k2) -> Map k1 a -> Map k2 a-mapKeys f = fromList . foldrWithKey (\k x xs -> (f k, x) : xs) []-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE mapKeys #-}-#endif---- | /O(n*log n)/.--- @'mapKeysWith' c f s@ is the map obtained by applying @f@ to each key of @s@.------ The size of the result may be smaller if @f@ maps two or more distinct--- keys to the same new key. In this case the associated values will be--- combined using @c@.------ > mapKeysWith (++) (\ _ -> 1) (fromList [(1,"b"), (2,"a"), (3,"d"), (4,"c")]) == singleton 1 "cdab"--- > mapKeysWith (++) (\ _ -> 3) (fromList [(1,"b"), (2,"a"), (3,"d"), (4,"c")]) == singleton 3 "cdab"--mapKeysWith :: Ord k2 => (a -> a -> a) -> (k1->k2) -> Map k1 a -> Map k2 a-mapKeysWith c f = fromListWith c . foldrWithKey (\k x xs -> (f k, x) : xs) []-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE mapKeysWith #-}-#endif----- | /O(n)/.--- @'mapKeysMonotonic' f s == 'mapKeys' f s@, but works only when @f@--- is strictly monotonic.--- That is, for any values @x@ and @y@, if @x@ < @y@ then @f x@ < @f y@.--- /The precondition is not checked./--- Semi-formally, we have:------ > and [x < y ==> f x < f y | x <- ls, y <- ls]--- > ==> mapKeysMonotonic f s == mapKeys f s--- > where ls = keys s------ This means that @f@ maps distinct original keys to distinct resulting keys.--- This function has better performance than 'mapKeys'.------ > mapKeysMonotonic (\ k -> k * 2) (fromList [(5,"a"), (3,"b")]) == fromList [(6, "b"), (10, "a")]--- > valid (mapKeysMonotonic (\ k -> k * 2) (fromList [(5,"a"), (3,"b")])) == True--- > valid (mapKeysMonotonic (\ _ -> 1) (fromList [(5,"a"), (3,"b")])) == False--mapKeysMonotonic :: (k1->k2) -> Map k1 a -> Map k2 a-mapKeysMonotonic _ Tip = Tip-mapKeysMonotonic f (Bin sz k x l r) =- Bin sz (f k) x (mapKeysMonotonic f l) (mapKeysMonotonic f r)--{--------------------------------------------------------------------- Folds---------------------------------------------------------------------}---- | /O(n)/. Fold the values in the map using the given right-associative--- binary operator, such that @'foldr' f z == 'Prelude.foldr' f z . 'elems'@.------ For example,------ > elems map = foldr (:) [] map------ > let f a len = len + (length a)--- > foldr f 0 (fromList [(5,"a"), (3,"bbb")]) == 4-foldr :: (a -> b -> b) -> b -> Map k a -> b-foldr f z = go z- where- go z' Tip = z'- go z' (Bin _ _ x l r) = go (f x (go z' r)) l-{-# INLINE foldr #-}---- | /O(n)/. A strict version of 'foldr'. Each application of the operator is--- evaluated before using the result in the next application. This--- function is strict in the starting value.-foldr' :: (a -> b -> b) -> b -> Map k a -> b-foldr' f z = go z- where- STRICT_1_OF_2(go)- go z' Tip = z'- go z' (Bin _ _ x l r) = go (f x (go z' r)) l-{-# INLINE foldr' #-}---- | /O(n)/. Fold the values in the map using the given left-associative--- binary operator, such that @'foldl' f z == 'Prelude.foldl' f z . 'elems'@.------ For example,------ > elems = reverse . foldl (flip (:)) []------ > let f len a = len + (length a)--- > foldl f 0 (fromList [(5,"a"), (3,"bbb")]) == 4-foldl :: (a -> b -> a) -> a -> Map k b -> a-foldl f z = go z- where- go z' Tip = z'- go z' (Bin _ _ x l r) = go (f (go z' l) x) r-{-# INLINE foldl #-}---- | /O(n)/. A strict version of 'foldl'. Each application of the operator is--- evaluated before using the result in the next application. This--- function is strict in the starting value.-foldl' :: (a -> b -> a) -> a -> Map k b -> a-foldl' f z = go z- where- STRICT_1_OF_2(go)- go z' Tip = z'- go z' (Bin _ _ x l r) = go (f (go z' l) x) r-{-# INLINE foldl' #-}---- | /O(n)/. Fold the keys and values in the map using the given right-associative--- binary operator, such that--- @'foldrWithKey' f z == 'Prelude.foldr' ('uncurry' f) z . 'toAscList'@.------ For example,------ > keys map = foldrWithKey (\k x ks -> k:ks) [] map------ > let f k a result = result ++ "(" ++ (show k) ++ ":" ++ a ++ ")"--- > foldrWithKey f "Map: " (fromList [(5,"a"), (3,"b")]) == "Map: (5:a)(3:b)"-foldrWithKey :: (k -> a -> b -> b) -> b -> Map k a -> b-foldrWithKey f z = go z- where- go z' Tip = z'- go z' (Bin _ kx x l r) = go (f kx x (go z' r)) l-{-# INLINE foldrWithKey #-}---- | /O(n)/. A strict version of 'foldrWithKey'. Each application of the operator is--- evaluated before using the result in the next application. This--- function is strict in the starting value.-foldrWithKey' :: (k -> a -> b -> b) -> b -> Map k a -> b-foldrWithKey' f z = go z- where- STRICT_1_OF_2(go)- go z' Tip = z'- go z' (Bin _ kx x l r) = go (f kx x (go z' r)) l-{-# INLINE foldrWithKey' #-}---- | /O(n)/. Fold the keys and values in the map using the given left-associative--- binary operator, such that--- @'foldlWithKey' f z == 'Prelude.foldl' (\\z' (kx, x) -> f z' kx x) z . 'toAscList'@.------ For example,------ > keys = reverse . foldlWithKey (\ks k x -> k:ks) []------ > let f result k a = result ++ "(" ++ (show k) ++ ":" ++ a ++ ")"--- > foldlWithKey f "Map: " (fromList [(5,"a"), (3,"b")]) == "Map: (3:b)(5:a)"-foldlWithKey :: (a -> k -> b -> a) -> a -> Map k b -> a-foldlWithKey f z = go z- where- go z' Tip = z'- go z' (Bin _ kx x l r) = go (f (go z' l) kx x) r-{-# INLINE foldlWithKey #-}---- | /O(n)/. A strict version of 'foldlWithKey'. Each application of the operator is--- evaluated before using the result in the next application. This--- function is strict in the starting value.-foldlWithKey' :: (a -> k -> b -> a) -> a -> Map k b -> a-foldlWithKey' f z = go z- where- STRICT_1_OF_2(go)- go z' Tip = z'- go z' (Bin _ kx x l r) = go (f (go z' l) kx x) r-{-# INLINE foldlWithKey' #-}--{--------------------------------------------------------------------- List variations---------------------------------------------------------------------}--- | /O(n)/.--- Return all elements of the map in the ascending order of their keys.--- Subject to list fusion.------ > elems (fromList [(5,"a"), (3,"b")]) == ["b","a"]--- > elems empty == []--elems :: Map k a -> [a]-elems = foldr (:) []---- | /O(n)/. Return all keys of the map in ascending order. Subject to list--- fusion.------ > keys (fromList [(5,"a"), (3,"b")]) == [3,5]--- > keys empty == []--keys :: Map k a -> [k]-keys = foldrWithKey (\k _ ks -> k : ks) []---- | /O(n)/. An alias for 'toAscList'. Return all key\/value pairs in the map--- in ascending key order. Subject to list fusion.------ > assocs (fromList [(5,"a"), (3,"b")]) == [(3,"b"), (5,"a")]--- > assocs empty == []--assocs :: Map k a -> [(k,a)]-assocs m- = toAscList m---- | /O(n)/. The set of all keys of the map.------ > keysSet (fromList [(5,"a"), (3,"b")]) == Data.Set.fromList [3,5]--- > keysSet empty == Data.Set.empty--keysSet :: Map k a -> Set.Set k-keysSet Tip = Set.Tip-keysSet (Bin sz kx _ l r) = Set.Bin sz kx (keysSet l) (keysSet r)---- | /O(n)/. Build a map from a set of keys and a function which for each key--- computes its value.------ > fromSet (\k -> replicate k 'a') (Data.Set.fromList [3, 5]) == fromList [(5,"aaaaa"), (3,"aaa")]--- > fromSet undefined Data.Set.empty == empty--fromSet :: (k -> a) -> Set.Set k -> Map k a-fromSet _ Set.Tip = Tip-fromSet f (Set.Bin sz x l r) = Bin sz x (f x) (fromSet f l) (fromSet f r)--{--------------------------------------------------------------------- Lists- use [foldlStrict] to reduce demand on the control-stack---------------------------------------------------------------------}--- | /O(n*log n)/. Build a map from a list of key\/value pairs. See also 'fromAscList'.--- If the list contains more than one value for the same key, the last value--- for the key is retained.------ If the keys of the list are ordered, linear-time implementation is used,--- with the performance equal to 'fromDistinctAscList'.------ > fromList [] == empty--- > fromList [(5,"a"), (3,"b"), (5, "c")] == fromList [(5,"c"), (3,"b")]--- > fromList [(5,"c"), (3,"b"), (5, "a")] == fromList [(5,"a"), (3,"b")]---- For some reason, when 'singleton' is used in fromList or in--- create, it is not inlined, so we inline it manually.-fromList :: Ord k => [(k,a)] -> Map k a-fromList [] = Tip-fromList [(kx, x)] = Bin 1 kx x Tip Tip-fromList ((kx0, x0) : xs0) | not_ordered kx0 xs0 = fromList' (Bin 1 kx0 x0 Tip Tip) xs0- | otherwise = go (1::Int) (Bin 1 kx0 x0 Tip Tip) xs0- where- not_ordered _ [] = False- not_ordered kx ((ky,_) : _) = kx >= ky- {-# INLINE not_ordered #-}-- fromList' t0 xs = foldlStrict ins t0 xs- where ins t (k,x) = insert k x t-- STRICT_1_OF_3(go)- go _ t [] = t- go _ t [(kx, x)] = insertMax kx x t- go s l xs@((kx, x) : xss) | not_ordered kx xss = fromList' l xs- | otherwise = case create s xss of- (r, ys, []) -> go (s `shiftL` 1) (join kx x l r) ys- (r, _, ys) -> fromList' (join kx x l r) ys-- -- The create is returning a triple (tree, xs, ys). Both xs and ys- -- represent not yet processed elements and only one of them can be nonempty.- -- If ys is nonempty, the keys in ys are not ordered with respect to tree- -- and must be inserted using fromList'. Otherwise the keys have been- -- ordered so far.- STRICT_1_OF_2(create)- create _ [] = (Tip, [], [])- create s xs@(xp : xss)- | s == 1 = case xp of (kx, x) | not_ordered kx xss -> (Bin 1 kx x Tip Tip, [], xss)- | otherwise -> (Bin 1 kx x Tip Tip, xss, [])- | otherwise = case create (s `shiftR` 1) xs of- res@(_, [], _) -> res- (l, [(ky, y)], zs) -> (insertMax ky y l, [], zs)- (l, ys@((ky, y):yss), _) | not_ordered ky yss -> (l, [], ys)- | otherwise -> case create (s `shiftR` 1) yss of- (r, zs, ws) -> (join ky y l r, zs, ws)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE fromList #-}-#endif---- | /O(n*log n)/. Build a map from a list of key\/value pairs with a combining function. See also 'fromAscListWith'.------ > fromListWith (++) [(5,"a"), (5,"b"), (3,"b"), (3,"a"), (5,"a")] == fromList [(3, "ab"), (5, "aba")]--- > fromListWith (++) [] == empty--fromListWith :: Ord k => (a -> a -> a) -> [(k,a)] -> Map k a-fromListWith f xs- = fromListWithKey (\_ x y -> f x y) xs-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE fromListWith #-}-#endif---- | /O(n*log n)/. Build a map from a list of key\/value pairs with a combining function. See also 'fromAscListWithKey'.------ > let f k a1 a2 = (show k) ++ a1 ++ a2--- > fromListWithKey f [(5,"a"), (5,"b"), (3,"b"), (3,"a"), (5,"a")] == fromList [(3, "3ab"), (5, "5a5ba")]--- > fromListWithKey f [] == empty--fromListWithKey :: Ord k => (k -> a -> a -> a) -> [(k,a)] -> Map k a-fromListWithKey f xs- = foldlStrict ins empty xs- where- ins t (k,x) = insertWithKey f k x t-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE fromListWithKey #-}-#endif---- | /O(n)/. Convert the map to a list of key\/value pairs. Subject to list fusion.------ > toList (fromList [(5,"a"), (3,"b")]) == [(3,"b"), (5,"a")]--- > toList empty == []--toList :: Map k a -> [(k,a)]-toList = toAscList---- | /O(n)/. Convert the map to a list of key\/value pairs where the keys are--- in ascending order. Subject to list fusion.------ > toAscList (fromList [(5,"a"), (3,"b")]) == [(3,"b"), (5,"a")]--toAscList :: Map k a -> [(k,a)]-toAscList = foldrWithKey (\k x xs -> (k,x):xs) []---- | /O(n)/. Convert the map to a list of key\/value pairs where the keys--- are in descending order. Subject to list fusion.------ > toDescList (fromList [(5,"a"), (3,"b")]) == [(5,"a"), (3,"b")]--toDescList :: Map k a -> [(k,a)]-toDescList = foldlWithKey (\xs k x -> (k,x):xs) []---- List fusion for the list generating functions.-#if __GLASGOW_HASKELL__--- The foldrFB and foldlFB are fold{r,l}WithKey equivalents, used for list fusion.--- They are important to convert unfused methods back, see mapFB in prelude.-foldrFB :: (k -> a -> b -> b) -> b -> Map k a -> b-foldrFB = foldrWithKey-{-# INLINE[0] foldrFB #-}-foldlFB :: (a -> k -> b -> a) -> a -> Map k b -> a-foldlFB = foldlWithKey-{-# INLINE[0] foldlFB #-}---- Inline assocs and toList, so that we need to fuse only toAscList.-{-# INLINE assocs #-}-{-# INLINE toList #-}---- The fusion is enabled up to phase 2 included. If it does not succeed,--- convert in phase 1 the expanded elems,keys,to{Asc,Desc}List calls back to--- elems,keys,to{Asc,Desc}List. In phase 0, we inline fold{lr}FB (which were--- used in a list fusion, otherwise it would go away in phase 1), and let compiler--- do whatever it wants with elems,keys,to{Asc,Desc}List -- it was forbidden to--- inline it before phase 0, otherwise the fusion rules would not fire at all.-{-# NOINLINE[0] elems #-}-{-# NOINLINE[0] keys #-}-{-# NOINLINE[0] toAscList #-}-{-# NOINLINE[0] toDescList #-}-{-# RULES "Map.elems" [~1] forall m . elems m = build (\c n -> foldrFB (\_ x xs -> c x xs) n m) #-}-{-# RULES "Map.elemsBack" [1] foldrFB (\_ x xs -> x : xs) [] = elems #-}-{-# RULES "Map.keys" [~1] forall m . keys m = build (\c n -> foldrFB (\k _ xs -> c k xs) n m) #-}-{-# RULES "Map.keysBack" [1] foldrFB (\k _ xs -> k : xs) [] = keys #-}-{-# RULES "Map.toAscList" [~1] forall m . toAscList m = build (\c n -> foldrFB (\k x xs -> c (k,x) xs) n m) #-}-{-# RULES "Map.toAscListBack" [1] foldrFB (\k x xs -> (k, x) : xs) [] = toAscList #-}-{-# RULES "Map.toDescList" [~1] forall m . toDescList m = build (\c n -> foldlFB (\xs k x -> c (k,x) xs) n m) #-}-{-# RULES "Map.toDescListBack" [1] foldlFB (\xs k x -> (k, x) : xs) [] = toDescList #-}-#endif--{--------------------------------------------------------------------- Building trees from ascending/descending lists can be done in linear time.-- Note that if [xs] is ascending that:- fromAscList xs == fromList xs- fromAscListWith f xs == fromListWith f xs---------------------------------------------------------------------}--- | /O(n)/. Build a map from an ascending list in linear time.--- /The precondition (input list is ascending) is not checked./------ > fromAscList [(3,"b"), (5,"a")] == fromList [(3, "b"), (5, "a")]--- > fromAscList [(3,"b"), (5,"a"), (5,"b")] == fromList [(3, "b"), (5, "b")]--- > valid (fromAscList [(3,"b"), (5,"a"), (5,"b")]) == True--- > valid (fromAscList [(5,"a"), (3,"b"), (5,"b")]) == False--fromAscList :: Eq k => [(k,a)] -> Map k a-fromAscList xs- = fromAscListWithKey (\_ x _ -> x) xs-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE fromAscList #-}-#endif---- | /O(n)/. Build a map from an ascending list in linear time with a combining function for equal keys.--- /The precondition (input list is ascending) is not checked./------ > fromAscListWith (++) [(3,"b"), (5,"a"), (5,"b")] == fromList [(3, "b"), (5, "ba")]--- > valid (fromAscListWith (++) [(3,"b"), (5,"a"), (5,"b")]) == True--- > valid (fromAscListWith (++) [(5,"a"), (3,"b"), (5,"b")]) == False--fromAscListWith :: Eq k => (a -> a -> a) -> [(k,a)] -> Map k a-fromAscListWith f xs- = fromAscListWithKey (\_ x y -> f x y) xs-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE fromAscListWith #-}-#endif---- | /O(n)/. Build a map from an ascending list in linear time with a--- combining function for equal keys.--- /The precondition (input list is ascending) is not checked./------ > let f k a1 a2 = (show k) ++ ":" ++ a1 ++ a2--- > fromAscListWithKey f [(3,"b"), (5,"a"), (5,"b"), (5,"b")] == fromList [(3, "b"), (5, "5:b5:ba")]--- > valid (fromAscListWithKey f [(3,"b"), (5,"a"), (5,"b"), (5,"b")]) == True--- > valid (fromAscListWithKey f [(5,"a"), (3,"b"), (5,"b"), (5,"b")]) == False--fromAscListWithKey :: Eq k => (k -> a -> a -> a) -> [(k,a)] -> Map k a-fromAscListWithKey f xs- = fromDistinctAscList (combineEq f xs)- where- -- [combineEq f xs] combines equal elements with function [f] in an ordered list [xs]- combineEq _ xs'- = case xs' of- [] -> []- [x] -> [x]- (x:xx) -> combineEq' x xx-- combineEq' z [] = [z]- combineEq' z@(kz,zz) (x@(kx,xx):xs')- | kx==kz = let yy = f kx xx zz in combineEq' (kx,yy) xs'- | otherwise = z:combineEq' x xs'-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE fromAscListWithKey #-}-#endif----- | /O(n)/. Build a map from an ascending list of distinct elements in linear time.--- /The precondition is not checked./------ > fromDistinctAscList [(3,"b"), (5,"a")] == fromList [(3, "b"), (5, "a")]--- > valid (fromDistinctAscList [(3,"b"), (5,"a")]) == True--- > valid (fromDistinctAscList [(3,"b"), (5,"a"), (5,"b")]) == False---- For some reason, when 'singleton' is used in fromDistinctAscList or in--- create, it is not inlined, so we inline it manually.-fromDistinctAscList :: [(k,a)] -> Map k a-fromDistinctAscList [] = Tip-fromDistinctAscList ((kx0, x0) : xs0) = go (1::Int) (Bin 1 kx0 x0 Tip Tip) xs0- where- STRICT_1_OF_3(go)- go _ t [] = t- go s l ((kx, x) : xs) = case create s xs of- (r, ys) -> go (s `shiftL` 1) (join kx x l r) ys-- STRICT_1_OF_2(create)- create _ [] = (Tip, [])- create s xs@(x' : xs')- | s == 1 = case x' of (kx, x) -> (Bin 1 kx x Tip Tip, xs')- | otherwise = case create (s `shiftR` 1) xs of- res@(_, []) -> res- (l, (ky, y):ys) -> case create (s `shiftR` 1) ys of- (r, zs) -> (join ky y l r, zs)---{--------------------------------------------------------------------- Utility functions that return sub-ranges of the original- tree. Some functions take a `Maybe value` as an argument to- allow comparisons against infinite values. These are called `blow`- (Nothing is -\infty) and `bhigh` (here Nothing is +\infty).- We use MaybeS value, which is a Maybe strict in the Just case.-- [trim blow bhigh t] A tree that is either empty or where [x > blow]- and [x < bhigh] for the value [x] of the root.- [filterGt blow t] A tree where for all values [k]. [k > blow]- [filterLt bhigh t] A tree where for all values [k]. [k < bhigh]-- [split k t] Returns two trees [l] and [r] where all keys- in [l] are <[k] and all keys in [r] are >[k].- [splitLookup k t] Just like [split] but also returns whether [k]- was found in the tree.---------------------------------------------------------------------}--data MaybeS a = NothingS | JustS !a--{--------------------------------------------------------------------- [trim blo bhi t] trims away all subtrees that surely contain no- values between the range [blo] to [bhi]. The returned tree is either- empty or the key of the root is between @blo@ and @bhi@.---------------------------------------------------------------------}-trim :: Ord k => MaybeS k -> MaybeS k -> Map k a -> Map k a-trim NothingS NothingS t = t-trim (JustS lk) NothingS t = greater lk t where greater lo (Bin _ k _ _ r) | k <= lo = greater lo r- greater _ t' = t'-trim NothingS (JustS hk) t = lesser hk t where lesser hi (Bin _ k _ l _) | k >= hi = lesser hi l- lesser _ t' = t'-trim (JustS lk) (JustS hk) t = middle lk hk t where middle lo hi (Bin _ k _ _ r) | k <= lo = middle lo hi r- middle lo hi (Bin _ k _ l _) | k >= hi = middle lo hi l- middle _ _ t' = t'-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE trim #-}-#endif---- Helper function for 'mergeWithKey'. The @'trimLookupLo' lk hk t@ performs both--- @'trim' (JustS lk) hk t@ and @'lookup' lk t@.---- See Note: Type of local 'go' function-trimLookupLo :: Ord k => k -> MaybeS k -> Map k a -> (Maybe a, Map k a)-trimLookupLo lk0 mhk0 t0 = toPair $ go lk0 mhk0 t0- where- go lk NothingS t = greater lk t- where greater :: Ord k => k -> Map k a -> StrictPair (Maybe a) (Map k a)- greater lo t'@(Bin _ kx x l r) = case compare lo kx of- LT -> lookup lo l :*: t'- EQ -> (Just x :*: r)- GT -> greater lo r- greater _ Tip = (Nothing :*: Tip)- go lk (JustS hk) t = middle lk hk t- where middle :: Ord k => k -> k -> Map k a -> StrictPair (Maybe a) (Map k a)- middle lo hi t'@(Bin _ kx x l r) = case compare lo kx of- LT | kx < hi -> lookup lo l :*: t'- | otherwise -> middle lo hi l- EQ -> Just x :*: lesser hi r- GT -> middle lo hi r- middle _ _ Tip = (Nothing :*: Tip)-- lesser :: Ord k => k -> Map k a -> Map k a- lesser hi (Bin _ k _ l _) | k >= hi = lesser hi l- lesser _ t' = t'-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE trimLookupLo #-}-#endif---{--------------------------------------------------------------------- [filterGt b t] filter all keys >[b] from tree [t]- [filterLt b t] filter all keys <[b] from tree [t]---------------------------------------------------------------------}-filterGt :: Ord k => MaybeS k -> Map k v -> Map k v-filterGt NothingS t = t-filterGt (JustS b) t = filter' b t- where filter' _ Tip = Tip- filter' b' (Bin _ kx x l r) =- case compare b' kx of LT -> join kx x (filter' b' l) r- EQ -> r- GT -> filter' b' r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE filterGt #-}-#endif--filterLt :: Ord k => MaybeS k -> Map k v -> Map k v-filterLt NothingS t = t-filterLt (JustS b) t = filter' b t- where filter' _ Tip = Tip- filter' b' (Bin _ kx x l r) =- case compare kx b' of LT -> join kx x l (filter' b' r)- EQ -> l- GT -> filter' b' l-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE filterLt #-}-#endif--{--------------------------------------------------------------------- Split---------------------------------------------------------------------}--- | /O(log n)/. The expression (@'split' k map@) is a pair @(map1,map2)@ where--- the keys in @map1@ are smaller than @k@ and the keys in @map2@ larger than @k@.--- Any key equal to @k@ is found in neither @map1@ nor @map2@.------ > split 2 (fromList [(5,"a"), (3,"b")]) == (empty, fromList [(3,"b"), (5,"a")])--- > split 3 (fromList [(5,"a"), (3,"b")]) == (empty, singleton 5 "a")--- > split 4 (fromList [(5,"a"), (3,"b")]) == (singleton 3 "b", singleton 5 "a")--- > split 5 (fromList [(5,"a"), (3,"b")]) == (singleton 3 "b", empty)--- > split 6 (fromList [(5,"a"), (3,"b")]) == (fromList [(3,"b"), (5,"a")], empty)--split :: Ord k => k -> Map k a -> (Map k a,Map k a)-split k0 t0 = k0 `seq` toPair $ go k0 t0- where- go k t =- case t of- Tip -> (Tip :*: Tip)- Bin _ kx x l r -> case compare k kx of- LT -> let (lt :*: gt) = go k l in lt :*: join kx x gt r- GT -> let (lt :*: gt) = go k r in join kx x l lt :*: gt- EQ -> (l :*: r)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE split #-}-#endif---- | /O(log n)/. The expression (@'splitLookup' k map@) splits a map just--- like 'split' but also returns @'lookup' k map@.------ > splitLookup 2 (fromList [(5,"a"), (3,"b")]) == (empty, Nothing, fromList [(3,"b"), (5,"a")])--- > splitLookup 3 (fromList [(5,"a"), (3,"b")]) == (empty, Just "b", singleton 5 "a")--- > splitLookup 4 (fromList [(5,"a"), (3,"b")]) == (singleton 3 "b", Nothing, singleton 5 "a")--- > splitLookup 5 (fromList [(5,"a"), (3,"b")]) == (singleton 3 "b", Just "a", empty)--- > splitLookup 6 (fromList [(5,"a"), (3,"b")]) == (fromList [(3,"b"), (5,"a")], Nothing, empty)--splitLookup :: Ord k => k -> Map k a -> (Map k a,Maybe a,Map k a)-splitLookup k t = k `seq`- case t of- Tip -> (Tip,Nothing,Tip)- Bin _ kx x l r -> case compare k kx of- LT -> let (lt,z,gt) = splitLookup k l- gt' = join kx x gt r- in gt' `seq` (lt,z,gt')- GT -> let (lt,z,gt) = splitLookup k r- lt' = join kx x l lt- in lt' `seq` (lt',z,gt)- EQ -> (l,Just x,r)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE splitLookup #-}-#endif--{--------------------------------------------------------------------- Utility functions that maintain the balance properties of the tree.- All constructors assume that all values in [l] < [k] and all values- in [r] > [k], and that [l] and [r] are valid trees.-- In order of sophistication:- [Bin sz k x l r] The type constructor.- [bin k x l r] Maintains the correct size, assumes that both [l]- and [r] are balanced with respect to each other.- [balance k x l r] Restores the balance and size.- Assumes that the original tree was balanced and- that [l] or [r] has changed by at most one element.- [join k x l r] Restores balance and size.-- Furthermore, we can construct a new tree from two trees. Both operations- assume that all values in [l] < all values in [r] and that [l] and [r]- are valid:- [glue l r] Glues [l] and [r] together. Assumes that [l] and- [r] are already balanced with respect to each other.- [merge l r] Merges two trees and restores balance.-- Note: in contrast to Adam's paper, we use (<=) comparisons instead- of (<) comparisons in [join], [merge] and [balance].- Quickcheck (on [difference]) showed that this was necessary in order- to maintain the invariants. It is quite unsatisfactory that I haven't- been able to find out why this is actually the case! Fortunately, it- doesn't hurt to be a bit more conservative.---------------------------------------------------------------------}--{--------------------------------------------------------------------- Join---------------------------------------------------------------------}-join :: k -> a -> Map k a -> Map k a -> Map k a-join kx x Tip r = insertMin kx x r-join kx x l Tip = insertMax kx x l-join kx x l@(Bin sizeL ky y ly ry) r@(Bin sizeR kz z lz rz)- | delta*sizeL < sizeR = balanceL kz z (join kx x l lz) rz- | delta*sizeR < sizeL = balanceR ky y ly (join kx x ry r)- | otherwise = bin kx x l r----- insertMin and insertMax don't perform potentially expensive comparisons.-insertMax,insertMin :: k -> a -> Map k a -> Map k a-insertMax kx x t- = case t of- Tip -> singleton kx x- Bin _ ky y l r- -> balanceR ky y l (insertMax kx x r)--insertMin kx x t- = case t of- Tip -> singleton kx x- Bin _ ky y l r- -> balanceL ky y (insertMin kx x l) r--{--------------------------------------------------------------------- [merge l r]: merges two trees.---------------------------------------------------------------------}-merge :: Map k a -> Map k a -> Map k a-merge Tip r = r-merge l Tip = l-merge l@(Bin sizeL kx x lx rx) r@(Bin sizeR ky y ly ry)- | delta*sizeL < sizeR = balanceL ky y (merge l ly) ry- | delta*sizeR < sizeL = balanceR kx x lx (merge rx r)- | otherwise = glue l r--{--------------------------------------------------------------------- [glue l r]: glues two trees together.- Assumes that [l] and [r] are already balanced with respect to each other.---------------------------------------------------------------------}-glue :: Map k a -> Map k a -> Map k a-glue Tip r = r-glue l Tip = l-glue l r- | size l > size r = let ((km,m),l') = deleteFindMax l in balanceR km m l' r- | otherwise = let ((km,m),r') = deleteFindMin r in balanceL km m l r'----- | /O(log n)/. Delete and find the minimal element.------ > deleteFindMin (fromList [(5,"a"), (3,"b"), (10,"c")]) == ((3,"b"), fromList[(5,"a"), (10,"c")])--- > deleteFindMin Error: can not return the minimal element of an empty map--deleteFindMin :: Map k a -> ((k,a),Map k a)-deleteFindMin t- = case t of- Bin _ k x Tip r -> ((k,x),r)- Bin _ k x l r -> let (km,l') = deleteFindMin l in (km,balanceR k x l' r)- Tip -> (error "Map.deleteFindMin: can not return the minimal element of an empty map", Tip)---- | /O(log n)/. Delete and find the maximal element.------ > deleteFindMax (fromList [(5,"a"), (3,"b"), (10,"c")]) == ((10,"c"), fromList [(3,"b"), (5,"a")])--- > deleteFindMax empty Error: can not return the maximal element of an empty map--deleteFindMax :: Map k a -> ((k,a),Map k a)-deleteFindMax t- = case t of- Bin _ k x l Tip -> ((k,x),l)- Bin _ k x l r -> let (km,r') = deleteFindMax r in (km,balanceL k x l r')- Tip -> (error "Map.deleteFindMax: can not return the maximal element of an empty map", Tip)---{--------------------------------------------------------------------- [balance l x r] balances two trees with value x.- The sizes of the trees should balance after decreasing the- size of one of them. (a rotation).-- [delta] is the maximal relative difference between the sizes of- two trees, it corresponds with the [w] in Adams' paper.- [ratio] is the ratio between an outer and inner sibling of the- heavier subtree in an unbalanced setting. It determines- whether a double or single rotation should be performed- to restore balance. It is corresponds with the inverse- of $\alpha$ in Adam's article.-- Note that according to the Adam's paper:- - [delta] should be larger than 4.646 with a [ratio] of 2.- - [delta] should be larger than 3.745 with a [ratio] of 1.534.-- But the Adam's paper is erroneous:- - It can be proved that for delta=2 and delta>=5 there does- not exist any ratio that would work.- - Delta=4.5 and ratio=2 does not work.-- That leaves two reasonable variants, delta=3 and delta=4,- both with ratio=2.-- - A lower [delta] leads to a more 'perfectly' balanced tree.- - A higher [delta] performs less rebalancing.-- In the benchmarks, delta=3 is faster on insert operations,- and delta=4 has slightly better deletes. As the insert speedup- is larger, we currently use delta=3.----------------------------------------------------------------------}-delta,ratio :: Int-delta = 3-ratio = 2---- The balance function is equivalent to the following:------ balance :: k -> a -> Map k a -> Map k a -> Map k a--- balance k x l r--- | sizeL + sizeR <= 1 = Bin sizeX k x l r--- | sizeR > delta*sizeL = rotateL k x l r--- | sizeL > delta*sizeR = rotateR k x l r--- | otherwise = Bin sizeX k x l r--- where--- sizeL = size l--- sizeR = size r--- sizeX = sizeL + sizeR + 1------ rotateL :: a -> b -> Map a b -> Map a b -> Map a b--- rotateL k x l r@(Bin _ _ _ ly ry) | size ly < ratio*size ry = singleL k x l r--- | otherwise = doubleL k x l r------ rotateR :: a -> b -> Map a b -> Map a b -> Map a b--- rotateR k x l@(Bin _ _ _ ly ry) r | size ry < ratio*size ly = singleR k x l r--- | otherwise = doubleR k x l r------ singleL, singleR :: a -> b -> Map a b -> Map a b -> Map a b--- singleL k1 x1 t1 (Bin _ k2 x2 t2 t3) = bin k2 x2 (bin k1 x1 t1 t2) t3--- singleR k1 x1 (Bin _ k2 x2 t1 t2) t3 = bin k2 x2 t1 (bin k1 x1 t2 t3)------ doubleL, doubleR :: a -> b -> Map a b -> Map a b -> Map a b--- doubleL k1 x1 t1 (Bin _ k2 x2 (Bin _ k3 x3 t2 t3) t4) = bin k3 x3 (bin k1 x1 t1 t2) (bin k2 x2 t3 t4)--- doubleR k1 x1 (Bin _ k2 x2 t1 (Bin _ k3 x3 t2 t3)) t4 = bin k3 x3 (bin k2 x2 t1 t2) (bin k1 x1 t3 t4)------ It is only written in such a way that every node is pattern-matched only once.--balance :: k -> a -> Map k a -> Map k a -> Map k a-balance k x l r = case l of- Tip -> case r of- Tip -> Bin 1 k x Tip Tip- (Bin _ _ _ Tip Tip) -> Bin 2 k x Tip r- (Bin _ rk rx Tip rr@(Bin _ _ _ _ _)) -> Bin 3 rk rx (Bin 1 k x Tip Tip) rr- (Bin _ rk rx (Bin _ rlk rlx _ _) Tip) -> Bin 3 rlk rlx (Bin 1 k x Tip Tip) (Bin 1 rk rx Tip Tip)- (Bin rs rk rx rl@(Bin rls rlk rlx rll rlr) rr@(Bin rrs _ _ _ _))- | rls < ratio*rrs -> Bin (1+rs) rk rx (Bin (1+rls) k x Tip rl) rr- | otherwise -> Bin (1+rs) rlk rlx (Bin (1+size rll) k x Tip rll) (Bin (1+rrs+size rlr) rk rx rlr rr)-- (Bin ls lk lx ll lr) -> case r of- Tip -> case (ll, lr) of- (Tip, Tip) -> Bin 2 k x l Tip- (Tip, (Bin _ lrk lrx _ _)) -> Bin 3 lrk lrx (Bin 1 lk lx Tip Tip) (Bin 1 k x Tip Tip)- ((Bin _ _ _ _ _), Tip) -> Bin 3 lk lx ll (Bin 1 k x Tip Tip)- ((Bin lls _ _ _ _), (Bin lrs lrk lrx lrl lrr))- | lrs < ratio*lls -> Bin (1+ls) lk lx ll (Bin (1+lrs) k x lr Tip)- | otherwise -> Bin (1+ls) lrk lrx (Bin (1+lls+size lrl) lk lx ll lrl) (Bin (1+size lrr) k x lrr Tip)- (Bin rs rk rx rl rr)- | rs > delta*ls -> case (rl, rr) of- (Bin rls rlk rlx rll rlr, Bin rrs _ _ _ _)- | rls < ratio*rrs -> Bin (1+ls+rs) rk rx (Bin (1+ls+rls) k x l rl) rr- | otherwise -> Bin (1+ls+rs) rlk rlx (Bin (1+ls+size rll) k x l rll) (Bin (1+rrs+size rlr) rk rx rlr rr)- (_, _) -> error "Failure in Data.Map.balance"- | ls > delta*rs -> case (ll, lr) of- (Bin lls _ _ _ _, Bin lrs lrk lrx lrl lrr)- | lrs < ratio*lls -> Bin (1+ls+rs) lk lx ll (Bin (1+rs+lrs) k x lr r)- | otherwise -> Bin (1+ls+rs) lrk lrx (Bin (1+lls+size lrl) lk lx ll lrl) (Bin (1+rs+size lrr) k x lrr r)- (_, _) -> error "Failure in Data.Map.balance"- | otherwise -> Bin (1+ls+rs) k x l r-{-# NOINLINE balance #-}---- Functions balanceL and balanceR are specialised versions of balance.--- balanceL only checks whether the left subtree is too big,--- balanceR only checks whether the right subtree is too big.---- balanceL is called when left subtree might have been inserted to or when--- right subtree might have been deleted from.-balanceL :: k -> a -> Map k a -> Map k a -> Map k a-balanceL k x l r = case r of- Tip -> case l of- Tip -> Bin 1 k x Tip Tip- (Bin _ _ _ Tip Tip) -> Bin 2 k x l Tip- (Bin _ lk lx Tip (Bin _ lrk lrx _ _)) -> Bin 3 lrk lrx (Bin 1 lk lx Tip Tip) (Bin 1 k x Tip Tip)- (Bin _ lk lx ll@(Bin _ _ _ _ _) Tip) -> Bin 3 lk lx ll (Bin 1 k x Tip Tip)- (Bin ls lk lx ll@(Bin lls _ _ _ _) lr@(Bin lrs lrk lrx lrl lrr))- | lrs < ratio*lls -> Bin (1+ls) lk lx ll (Bin (1+lrs) k x lr Tip)- | otherwise -> Bin (1+ls) lrk lrx (Bin (1+lls+size lrl) lk lx ll lrl) (Bin (1+size lrr) k x lrr Tip)-- (Bin rs _ _ _ _) -> case l of- Tip -> Bin (1+rs) k x Tip r-- (Bin ls lk lx ll lr)- | ls > delta*rs -> case (ll, lr) of- (Bin lls _ _ _ _, Bin lrs lrk lrx lrl lrr)- | lrs < ratio*lls -> Bin (1+ls+rs) lk lx ll (Bin (1+rs+lrs) k x lr r)- | otherwise -> Bin (1+ls+rs) lrk lrx (Bin (1+lls+size lrl) lk lx ll lrl) (Bin (1+rs+size lrr) k x lrr r)- (_, _) -> error "Failure in Data.Map.balanceL"- | otherwise -> Bin (1+ls+rs) k x l r-{-# NOINLINE balanceL #-}---- balanceR is called when right subtree might have been inserted to or when--- left subtree might have been deleted from.-balanceR :: k -> a -> Map k a -> Map k a -> Map k a-balanceR k x l r = case l of- Tip -> case r of- Tip -> Bin 1 k x Tip Tip- (Bin _ _ _ Tip Tip) -> Bin 2 k x Tip r- (Bin _ rk rx Tip rr@(Bin _ _ _ _ _)) -> Bin 3 rk rx (Bin 1 k x Tip Tip) rr- (Bin _ rk rx (Bin _ rlk rlx _ _) Tip) -> Bin 3 rlk rlx (Bin 1 k x Tip Tip) (Bin 1 rk rx Tip Tip)- (Bin rs rk rx rl@(Bin rls rlk rlx rll rlr) rr@(Bin rrs _ _ _ _))- | rls < ratio*rrs -> Bin (1+rs) rk rx (Bin (1+rls) k x Tip rl) rr- | otherwise -> Bin (1+rs) rlk rlx (Bin (1+size rll) k x Tip rll) (Bin (1+rrs+size rlr) rk rx rlr rr)-- (Bin ls _ _ _ _) -> case r of- Tip -> Bin (1+ls) k x l Tip-- (Bin rs rk rx rl rr)- | rs > delta*ls -> case (rl, rr) of- (Bin rls rlk rlx rll rlr, Bin rrs _ _ _ _)- | rls < ratio*rrs -> Bin (1+ls+rs) rk rx (Bin (1+ls+rls) k x l rl) rr- | otherwise -> Bin (1+ls+rs) rlk rlx (Bin (1+ls+size rll) k x l rll) (Bin (1+rrs+size rlr) rk rx rlr rr)- (_, _) -> error "Failure in Data.Map.balanceR"- | otherwise -> Bin (1+ls+rs) k x l r-{-# NOINLINE balanceR #-}---{--------------------------------------------------------------------- The bin constructor maintains the size of the tree---------------------------------------------------------------------}-bin :: k -> a -> Map k a -> Map k a -> Map k a-bin k x l r- = Bin (size l + size r + 1) k x l r-{-# INLINE bin #-}---{--------------------------------------------------------------------- Eq converts the tree to a list. In a lazy setting, this- actually seems one of the faster methods to compare two trees- and it is certainly the simplest :-)---------------------------------------------------------------------}-instance (Eq k,Eq a) => Eq (Map k a) where- t1 == t2 = (size t1 == size t2) && (toAscList t1 == toAscList t2)--{--------------------------------------------------------------------- Ord---------------------------------------------------------------------}--instance (Ord k, Ord v) => Ord (Map k v) where- compare m1 m2 = compare (toAscList m1) (toAscList m2)--{--------------------------------------------------------------------- Functor---------------------------------------------------------------------}-instance Functor (Map k) where- fmap f m = map f m--instance Traversable (Map k) where- traverse f = traverseWithKey (\_ -> f)--instance Foldable.Foldable (Map k) where- fold Tip = mempty- fold (Bin _ _ v l r) = Foldable.fold l `mappend` v `mappend` Foldable.fold r- foldr = foldr- foldl = foldl- foldMap _ Tip = mempty- foldMap f (Bin _ _ v l r) = Foldable.foldMap f l `mappend` f v `mappend` Foldable.foldMap f r--instance (NFData k, NFData a) => NFData (Map k a) where- rnf Tip = ()- rnf (Bin _ kx x l r) = rnf kx `seq` rnf x `seq` rnf l `seq` rnf r--{--------------------------------------------------------------------- Read---------------------------------------------------------------------}-instance (Ord k, Read k, Read e) => Read (Map k e) where-#ifdef __GLASGOW_HASKELL__- readPrec = parens $ prec 10 $ do- Ident "fromList" <- lexP- xs <- readPrec- return (fromList xs)-- readListPrec = readListPrecDefault-#else- readsPrec p = readParen (p > 10) $ \ r -> do- ("fromList",s) <- lex r- (xs,t) <- reads s- return (fromList xs,t)-#endif--{--------------------------------------------------------------------- Show---------------------------------------------------------------------}-instance (Show k, Show a) => Show (Map k a) where- showsPrec d m = showParen (d > 10) $- showString "fromList " . shows (toList m)---- | /O(n)/. Show the tree that implements the map. The tree is shown--- in a compressed, hanging format. See 'showTreeWith'.-showTree :: (Show k,Show a) => Map k a -> String-showTree m- = showTreeWith showElem True False m- where- showElem k x = show k ++ ":=" ++ show x---{- | /O(n)/. The expression (@'showTreeWith' showelem hang wide map@) shows- the tree that implements the map. Elements are shown using the @showElem@ function. If @hang@ is- 'True', a /hanging/ tree is shown otherwise a rotated tree is shown. If- @wide@ is 'True', an extra wide version is shown.--> Map> let t = fromDistinctAscList [(x,()) | x <- [1..5]]-> Map> putStrLn $ showTreeWith (\k x -> show (k,x)) True False t-> (4,())-> +--(2,())-> | +--(1,())-> | +--(3,())-> +--(5,())->-> Map> putStrLn $ showTreeWith (\k x -> show (k,x)) True True t-> (4,())-> |-> +--(2,())-> | |-> | +--(1,())-> | |-> | +--(3,())-> |-> +--(5,())->-> Map> putStrLn $ showTreeWith (\k x -> show (k,x)) False True t-> +--(5,())-> |-> (4,())-> |-> | +--(3,())-> | |-> +--(2,())-> |-> +--(1,())---}-showTreeWith :: (k -> a -> String) -> Bool -> Bool -> Map k a -> String-showTreeWith showelem hang wide t- | hang = (showsTreeHang showelem wide [] t) ""- | otherwise = (showsTree showelem wide [] [] t) ""--showsTree :: (k -> a -> String) -> Bool -> [String] -> [String] -> Map k a -> ShowS-showsTree showelem wide lbars rbars t- = case t of- Tip -> showsBars lbars . showString "|\n"- Bin _ kx x Tip Tip- -> showsBars lbars . showString (showelem kx x) . showString "\n"- Bin _ kx x l r- -> showsTree showelem wide (withBar rbars) (withEmpty rbars) r .- showWide wide rbars .- showsBars lbars . showString (showelem kx x) . showString "\n" .- showWide wide lbars .- showsTree showelem wide (withEmpty lbars) (withBar lbars) l--showsTreeHang :: (k -> a -> String) -> Bool -> [String] -> Map k a -> ShowS-showsTreeHang showelem wide bars t- = case t of- Tip -> showsBars bars . showString "|\n"- Bin _ kx x Tip Tip- -> showsBars bars . showString (showelem kx x) . showString "\n"- Bin _ kx x l r- -> showsBars bars . showString (showelem kx x) . showString "\n" .- showWide wide bars .- showsTreeHang showelem wide (withBar bars) l .- showWide wide bars .- showsTreeHang showelem wide (withEmpty bars) r--showWide :: Bool -> [String] -> String -> String-showWide wide bars- | wide = showString (concat (reverse bars)) . showString "|\n"- | otherwise = id--showsBars :: [String] -> ShowS-showsBars bars- = case bars of- [] -> id- _ -> showString (concat (reverse (tail bars))) . showString node--node :: String-node = "+--"--withBar, withEmpty :: [String] -> [String]-withBar bars = "| ":bars-withEmpty bars = " ":bars--{--------------------------------------------------------------------- Typeable---------------------------------------------------------------------}--#include "Typeable.h"-INSTANCE_TYPEABLE2(Map,mapTc,"Map")--{--------------------------------------------------------------------- Assertions---------------------------------------------------------------------}--- | /O(n)/. Test if the internal map structure is valid.------ > valid (fromAscList [(3,"b"), (5,"a")]) == True--- > valid (fromAscList [(5,"a"), (3,"b")]) == False--valid :: Ord k => Map k a -> Bool-valid t- = balanced t && ordered t && validsize t--ordered :: Ord a => Map a b -> Bool-ordered t- = bounded (const True) (const True) t- where- bounded lo hi t'- = case t' of- Tip -> True- Bin _ kx _ l r -> (lo kx) && (hi kx) && bounded lo (<kx) l && bounded (>kx) hi r---- | Exported only for "Debug.QuickCheck"-balanced :: Map k a -> Bool-balanced t- = case t of- Tip -> True- Bin _ _ _ l r -> (size l + size r <= 1 || (size l <= delta*size r && size r <= delta*size l)) &&- balanced l && balanced r--validsize :: Map a b -> Bool-validsize t- = (realsize t == Just (size t))- where- realsize t'- = case t' of- Tip -> Just 0- Bin sz _ _ l r -> case (realsize l,realsize r) of- (Just n,Just m) | n+m+1 == sz -> Just sz- _ -> Nothing--{--------------------------------------------------------------------- Utilities---------------------------------------------------------------------}-foldlStrict :: (a -> b -> a) -> a -> [b] -> a-foldlStrict f = go- where- go z [] = z- go z (x:xs) = let z' = f z x in z' `seq` go z' xs-{-# INLINE foldlStrict #-}
− containers-0.5.2.1/Darcs/Data/Map/Strict.hs
@@ -1,1180 +0,0 @@-{-# LANGUAGE CPP #-}-#if !defined(TESTING) && __GLASGOW_HASKELL__ >= 703-{-# LANGUAGE Safe #-}-#endif--------------------------------------------------------------------------------- |--- Module : Data.Map.Strict--- Copyright : (c) Daan Leijen 2002--- (c) Andriy Palamarchuk 2008--- License : BSD-style--- Maintainer : libraries@haskell.org--- Stability : provisional--- Portability : portable------ An efficient implementation of ordered maps from keys to values--- (dictionaries).------ API of this module is strict in both the keys and the values.--- If you need value-lazy maps, use "Data.Map.Lazy" instead.--- The 'Map' type is shared between the lazy and strict modules,--- meaning that the same 'Map' value can be passed to functions in--- both modules (although that is rarely needed).------ These modules are intended to be imported qualified, to avoid name--- clashes with Prelude functions, e.g.------ > import qualified Data.Map.Strict as Map------ The implementation of 'Map' is based on /size balanced/ binary trees (or--- trees of /bounded balance/) as described by:------ * Stephen Adams, \"/Efficient sets: a balancing act/\",--- Journal of Functional Programming 3(4):553-562, October 1993,--- <http://www.swiss.ai.mit.edu/~adams/BB/>.------ * J. Nievergelt and E.M. Reingold,--- \"/Binary search trees of bounded balance/\",--- SIAM journal of computing 2(1), March 1973.------ Note that the implementation is /left-biased/ -- the elements of a--- first argument are always preferred to the second, for example in--- 'union' or 'insert'.------ Operation comments contain the operation time complexity in--- the Big-O notation (<http://en.wikipedia.org/wiki/Big_O_notation>).------ Be aware that the 'Functor', 'Traversable' and 'Data' instances--- are the same as for the "Data.Map.Lazy" module, so if they are used--- on strict maps, the resulting maps will be lazy.---------------------------------------------------------------------------------- See the notes at the beginning of Data.Map.Base.--module Darcs.Data.Map.Strict- (- -- * Strictness properties- -- $strictness-- -- * Map type-#if !defined(TESTING)- Map -- instance Eq,Show,Read-#else- Map(..) -- instance Eq,Show,Read-#endif-- -- * Operators- , (!), (\\)-- -- * Query- , null- , size- , member- , notMember- , lookup- , findWithDefault- , lookupLT- , lookupGT- , lookupLE- , lookupGE-- -- * Construction- , empty- , singleton-- -- ** Insertion- , insert- , insertWith- , insertWithKey- , insertLookupWithKey-- -- ** Delete\/Update- , delete- , adjust- , adjustWithKey- , update- , updateWithKey- , updateLookupWithKey- , alter-- -- * Combine-- -- ** Union- , union- , unionWith- , unionWithKey- , unions- , unionsWith-- -- ** Difference- , difference- , differenceWith- , differenceWithKey-- -- ** Intersection- , intersection- , intersectionWith- , intersectionWithKey-- -- ** Universal combining function- , mergeWithKey-- -- * Traversal- -- ** Map- , map- , mapWithKey- , traverseWithKey- , mapAccum- , mapAccumWithKey- , mapAccumRWithKey- , mapKeys- , mapKeysWith- , mapKeysMonotonic-- -- * Folds- , foldr- , foldl- , foldrWithKey- , foldlWithKey- -- ** Strict folds- , foldr'- , foldl'- , foldrWithKey'- , foldlWithKey'-- -- * Conversion- , elems- , keys- , assocs- , keysSet- , fromSet-- -- ** Lists- , toList- , fromList- , fromListWith- , fromListWithKey-- -- ** Ordered lists- , toAscList- , toDescList- , fromAscList- , fromAscListWith- , fromAscListWithKey- , fromDistinctAscList-- -- * Filter- , filter- , filterWithKey- , partition- , partitionWithKey-- , mapMaybe- , mapMaybeWithKey- , mapEither- , mapEitherWithKey-- , split- , splitLookup-- -- * Submap- , isSubmapOf, isSubmapOfBy- , isProperSubmapOf, isProperSubmapOfBy-- -- * Indexed- , lookupIndex- , findIndex- , elemAt- , updateAt- , deleteAt-- -- * Min\/Max- , findMin- , findMax- , deleteMin- , deleteMax- , deleteFindMin- , deleteFindMax- , updateMin- , updateMax- , updateMinWithKey- , updateMaxWithKey- , minView- , maxView- , minViewWithKey- , maxViewWithKey-- -- * Debugging- , showTree- , showTreeWith- , valid--#if defined(TESTING)- -- * Internals- , bin- , balanced- , join- , merge-#endif- ) where--import Prelude hiding (lookup,map,filter,foldr,foldl,null)--import Darcs.Data.Map.Base hiding- ( findWithDefault- , singleton- , insert- , insertWith- , insertWithKey- , insertLookupWithKey- , adjust- , adjustWithKey- , update- , updateWithKey- , updateLookupWithKey- , alter- , unionWith- , unionWithKey- , unionsWith- , differenceWith- , differenceWithKey- , intersectionWith- , intersectionWithKey- , mergeWithKey- , map- , mapWithKey- , mapAccum- , mapAccumWithKey- , mapAccumRWithKey- , mapKeysWith- , fromSet- , fromList- , fromListWith- , fromListWithKey- , fromAscList- , fromAscListWith- , fromAscListWithKey- , fromDistinctAscList- , mapMaybe- , mapMaybeWithKey- , mapEither- , mapEitherWithKey- , updateAt- , updateMin- , updateMax- , updateMinWithKey- , updateMaxWithKey- )-import qualified Darcs.Data.Set.Base as Set-import Darcs.Data.StrictPair-import Data.Bits (shiftL, shiftR)---- Use macros to define strictness of functions. STRICT_x_OF_y--- denotes an y-ary function strict in the x-th parameter. Similarly--- STRICT_x_y_OF_z denotes an z-ary function strict in the x-th and--- y-th parameter. We do not use BangPatterns, because they are not--- in any standard and we want the compilers to be compiled by as many--- compilers as possible.-#define STRICT_1_OF_2(fn) fn arg _ | arg `seq` False = undefined-#define STRICT_1_OF_3(fn) fn arg _ _ | arg `seq` False = undefined-#define STRICT_2_OF_3(fn) fn _ arg _ | arg `seq` False = undefined-#define STRICT_1_2_OF_3(fn) fn arg1 arg2 _ | arg1 `seq` arg2 `seq` False = undefined-#define STRICT_2_3_OF_4(fn) fn _ arg1 arg2 _ | arg1 `seq` arg2 `seq` False = undefined---- $strictness------ This module satisfies the following strictness properties:------ 1. Key and value arguments are evaluated to WHNF;------ 2. Keys and values are evaluated to WHNF before they are stored in--- the map.------ Here are some examples that illustrate the first property:------ > insertWith (\ new old -> old) k undefined m == undefined--- > delete undefined m == undefined------ Here are some examples that illustrate the second property:------ > map (\ v -> undefined) m == undefined -- m is not empty--- > mapKeys (\ k -> undefined) m == undefined -- m is not empty--{--------------------------------------------------------------------- Query---------------------------------------------------------------------}---- | /O(log n)/. The expression @('findWithDefault' def k map)@ returns--- the value at key @k@ or returns default value @def@--- when the key is not in the map.------ > findWithDefault 'x' 1 (fromList [(5,'a'), (3,'b')]) == 'x'--- > findWithDefault 'x' 5 (fromList [(5,'a'), (3,'b')]) == 'a'---- See Map.Base.Note: Local 'go' functions and capturing-findWithDefault :: Ord k => a -> k -> Map k a -> a-findWithDefault def k = def `seq` k `seq` go- where- go Tip = def- go (Bin _ kx x l r) = case compare k kx of- LT -> go l- GT -> go r- EQ -> x-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE findWithDefault #-}-#else-{-# INLINE findWithDefault #-}-#endif--{--------------------------------------------------------------------- Construction---------------------------------------------------------------------}---- | /O(1)/. A map with a single element.------ > singleton 1 'a' == fromList [(1, 'a')]--- > size (singleton 1 'a') == 1--singleton :: k -> a -> Map k a-singleton k x = x `seq` Bin 1 k x Tip Tip-{-# INLINE singleton #-}--{--------------------------------------------------------------------- Insertion---------------------------------------------------------------------}--- | /O(log n)/. Insert a new key and value in the map.--- If the key is already present in the map, the associated value is--- replaced with the supplied value. 'insert' is equivalent to--- @'insertWith' 'const'@.------ > insert 5 'x' (fromList [(5,'a'), (3,'b')]) == fromList [(3, 'b'), (5, 'x')]--- > insert 7 'x' (fromList [(5,'a'), (3,'b')]) == fromList [(3, 'b'), (5, 'a'), (7, 'x')]--- > insert 5 'x' empty == singleton 5 'x'---- See Map.Base.Note: Type of local 'go' function-insert :: Ord k => k -> a -> Map k a -> Map k a-insert = go- where- go :: Ord k => k -> a -> Map k a -> Map k a- STRICT_1_2_OF_3(go)- go kx x Tip = singleton kx x- go kx x (Bin sz ky y l r) =- case compare kx ky of- LT -> balanceL ky y (go kx x l) r- GT -> balanceR ky y l (go kx x r)- EQ -> Bin sz kx x l r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE insert #-}-#else-{-# INLINE insert #-}-#endif---- | /O(log n)/. Insert with a function, combining new value and old value.--- @'insertWith' f key value mp@--- will insert the pair (key, value) into @mp@ if key does--- not exist in the map. If the key does exist, the function will--- insert the pair @(key, f new_value old_value)@.------ > insertWith (++) 5 "xxx" (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "xxxa")]--- > insertWith (++) 7 "xxx" (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "a"), (7, "xxx")]--- > insertWith (++) 5 "xxx" empty == singleton 5 "xxx"--insertWith :: Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a-insertWith f = insertWithKey (\_ x' y' -> f x' y')-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE insertWith #-}-#else-{-# INLINE insertWith #-}-#endif---- | /O(log n)/. Insert with a function, combining key, new value and old value.--- @'insertWithKey' f key value mp@--- will insert the pair (key, value) into @mp@ if key does--- not exist in the map. If the key does exist, the function will--- insert the pair @(key,f key new_value old_value)@.--- Note that the key passed to f is the same key passed to 'insertWithKey'.------ > let f key new_value old_value = (show key) ++ ":" ++ new_value ++ "|" ++ old_value--- > insertWithKey f 5 "xxx" (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "5:xxx|a")]--- > insertWithKey f 7 "xxx" (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "a"), (7, "xxx")]--- > insertWithKey f 5 "xxx" empty == singleton 5 "xxx"---- See Map.Base.Note: Type of local 'go' function-insertWithKey :: Ord k => (k -> a -> a -> a) -> k -> a -> Map k a -> Map k a-insertWithKey = go- where- go :: Ord k => (k -> a -> a -> a) -> k -> a -> Map k a -> Map k a- STRICT_2_3_OF_4(go)- go _ kx x Tip = singleton kx x- go f kx x (Bin sy ky y l r) =- case compare kx ky of- LT -> balanceL ky y (go f kx x l) r- GT -> balanceR ky y l (go f kx x r)- EQ -> let x' = f kx x y- in x' `seq` Bin sy kx x' l r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE insertWithKey #-}-#else-{-# INLINE insertWithKey #-}-#endif---- | /O(log n)/. Combines insert operation with old value retrieval.--- The expression (@'insertLookupWithKey' f k x map@)--- is a pair where the first element is equal to (@'lookup' k map@)--- and the second element equal to (@'insertWithKey' f k x map@).------ > let f key new_value old_value = (show key) ++ ":" ++ new_value ++ "|" ++ old_value--- > insertLookupWithKey f 5 "xxx" (fromList [(5,"a"), (3,"b")]) == (Just "a", fromList [(3, "b"), (5, "5:xxx|a")])--- > insertLookupWithKey f 7 "xxx" (fromList [(5,"a"), (3,"b")]) == (Nothing, fromList [(3, "b"), (5, "a"), (7, "xxx")])--- > insertLookupWithKey f 5 "xxx" empty == (Nothing, singleton 5 "xxx")------ This is how to define @insertLookup@ using @insertLookupWithKey@:------ > let insertLookup kx x t = insertLookupWithKey (\_ a _ -> a) kx x t--- > insertLookup 5 "x" (fromList [(5,"a"), (3,"b")]) == (Just "a", fromList [(3, "b"), (5, "x")])--- > insertLookup 7 "x" (fromList [(5,"a"), (3,"b")]) == (Nothing, fromList [(3, "b"), (5, "a"), (7, "x")])---- See Map.Base.Note: Type of local 'go' function-insertLookupWithKey :: Ord k => (k -> a -> a -> a) -> k -> a -> Map k a- -> (Maybe a, Map k a)-insertLookupWithKey f0 kx0 x0 t0 = toPair $ go f0 kx0 x0 t0- where- go :: Ord k => (k -> a -> a -> a) -> k -> a -> Map k a -> StrictPair (Maybe a) (Map k a)- STRICT_2_3_OF_4(go)- go _ kx x Tip = Nothing :*: singleton kx x- go f kx x (Bin sy ky y l r) =- case compare kx ky of- LT -> let (found :*: l') = go f kx x l- in found :*: balanceL ky y l' r- GT -> let (found :*: r') = go f kx x r- in found :*: balanceR ky y l r'- EQ -> let x' = f kx x y- in x' `seq` (Just y :*: Bin sy kx x' l r)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE insertLookupWithKey #-}-#else-{-# INLINE insertLookupWithKey #-}-#endif--{--------------------------------------------------------------------- Deletion---------------------------------------------------------------------}---- | /O(log n)/. Update a value at a specific key with the result of the provided function.--- When the key is not--- a member of the map, the original map is returned.------ > adjust ("new " ++) 5 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "new a")]--- > adjust ("new " ++) 7 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "a")]--- > adjust ("new " ++) 7 empty == empty--adjust :: Ord k => (a -> a) -> k -> Map k a -> Map k a-adjust f = adjustWithKey (\_ x -> f x)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE adjust #-}-#else-{-# INLINE adjust #-}-#endif---- | /O(log n)/. Adjust a value at a specific key. When the key is not--- a member of the map, the original map is returned.------ > let f key x = (show key) ++ ":new " ++ x--- > adjustWithKey f 5 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "5:new a")]--- > adjustWithKey f 7 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "a")]--- > adjustWithKey f 7 empty == empty--adjustWithKey :: Ord k => (k -> a -> a) -> k -> Map k a -> Map k a-adjustWithKey f = updateWithKey (\k' x' -> Just (f k' x'))-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE adjustWithKey #-}-#else-{-# INLINE adjustWithKey #-}-#endif---- | /O(log n)/. The expression (@'update' f k map@) updates the value @x@--- at @k@ (if it is in the map). If (@f x@) is 'Nothing', the element is--- deleted. If it is (@'Just' y@), the key @k@ is bound to the new value @y@.------ > let f x = if x == "a" then Just "new a" else Nothing--- > update f 5 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "new a")]--- > update f 7 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "a")]--- > update f 3 (fromList [(5,"a"), (3,"b")]) == singleton 5 "a"--update :: Ord k => (a -> Maybe a) -> k -> Map k a -> Map k a-update f = updateWithKey (\_ x -> f x)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE update #-}-#else-{-# INLINE update #-}-#endif---- | /O(log n)/. The expression (@'updateWithKey' f k map@) updates the--- value @x@ at @k@ (if it is in the map). If (@f k x@) is 'Nothing',--- the element is deleted. If it is (@'Just' y@), the key @k@ is bound--- to the new value @y@.------ > let f k x = if x == "a" then Just ((show k) ++ ":new a") else Nothing--- > updateWithKey f 5 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "5:new a")]--- > updateWithKey f 7 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "a")]--- > updateWithKey f 3 (fromList [(5,"a"), (3,"b")]) == singleton 5 "a"---- See Map.Base.Note: Type of local 'go' function-updateWithKey :: Ord k => (k -> a -> Maybe a) -> k -> Map k a -> Map k a-updateWithKey = go- where- go :: Ord k => (k -> a -> Maybe a) -> k -> Map k a -> Map k a- STRICT_2_OF_3(go)- go _ _ Tip = Tip- go f k(Bin sx kx x l r) =- case compare k kx of- LT -> balanceR kx x (go f k l) r- GT -> balanceL kx x l (go f k r)- EQ -> case f kx x of- Just x' -> x' `seq` Bin sx kx x' l r- Nothing -> glue l r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE updateWithKey #-}-#else-{-# INLINE updateWithKey #-}-#endif---- | /O(log n)/. Lookup and update. See also 'updateWithKey'.--- The function returns changed value, if it is updated.--- Returns the original key value if the map entry is deleted.------ > let f k x = if x == "a" then Just ((show k) ++ ":new a") else Nothing--- > updateLookupWithKey f 5 (fromList [(5,"a"), (3,"b")]) == (Just "5:new a", fromList [(3, "b"), (5, "5:new a")])--- > updateLookupWithKey f 7 (fromList [(5,"a"), (3,"b")]) == (Nothing, fromList [(3, "b"), (5, "a")])--- > updateLookupWithKey f 3 (fromList [(5,"a"), (3,"b")]) == (Just "b", singleton 5 "a")---- See Map.Base.Note: Type of local 'go' function-updateLookupWithKey :: Ord k => (k -> a -> Maybe a) -> k -> Map k a -> (Maybe a,Map k a)-updateLookupWithKey f0 k0 t0 = toPair $ go f0 k0 t0- where- go :: Ord k => (k -> a -> Maybe a) -> k -> Map k a -> StrictPair (Maybe a) (Map k a)- STRICT_2_OF_3(go)- go _ _ Tip = (Nothing :*: Tip)- go f k (Bin sx kx x l r) =- case compare k kx of- LT -> let (found :*: l') = go f k l- in found :*: balanceR kx x l' r- GT -> let (found :*: r') = go f k r- in found :*: balanceL kx x l r'- EQ -> case f kx x of- Just x' -> x' `seq` (Just x' :*: Bin sx kx x' l r)- Nothing -> (Just x :*: glue l r)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE updateLookupWithKey #-}-#else-{-# INLINE updateLookupWithKey #-}-#endif---- | /O(log n)/. The expression (@'alter' f k map@) alters the value @x@ at @k@, or absence thereof.--- 'alter' can be used to insert, delete, or update a value in a 'Map'.--- In short : @'lookup' k ('alter' f k m) = f ('lookup' k m)@.------ > let f _ = Nothing--- > alter f 7 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "a")]--- > alter f 5 (fromList [(5,"a"), (3,"b")]) == singleton 3 "b"--- >--- > let f _ = Just "c"--- > alter f 7 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "a"), (7, "c")]--- > alter f 5 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "c")]---- See Map.Base.Note: Type of local 'go' function-alter :: Ord k => (Maybe a -> Maybe a) -> k -> Map k a -> Map k a-alter = go- where- go :: Ord k => (Maybe a -> Maybe a) -> k -> Map k a -> Map k a- STRICT_2_OF_3(go)- go f k Tip = case f Nothing of- Nothing -> Tip- Just x -> singleton k x-- go f k (Bin sx kx x l r) = case compare k kx of- LT -> balance kx x (go f k l) r- GT -> balance kx x l (go f k r)- EQ -> case f (Just x) of- Just x' -> x' `seq` Bin sx kx x' l r- Nothing -> glue l r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE alter #-}-#else-{-# INLINE alter #-}-#endif--{--------------------------------------------------------------------- Indexing---------------------------------------------------------------------}---- | /O(log n)/. Update the element at /index/. Calls 'error' when an--- invalid index is used.------ > updateAt (\ _ _ -> Just "x") 0 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "x"), (5, "a")]--- > updateAt (\ _ _ -> Just "x") 1 (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "x")]--- > updateAt (\ _ _ -> Just "x") 2 (fromList [(5,"a"), (3,"b")]) Error: index out of range--- > updateAt (\ _ _ -> Just "x") (-1) (fromList [(5,"a"), (3,"b")]) Error: index out of range--- > updateAt (\_ _ -> Nothing) 0 (fromList [(5,"a"), (3,"b")]) == singleton 5 "a"--- > updateAt (\_ _ -> Nothing) 1 (fromList [(5,"a"), (3,"b")]) == singleton 3 "b"--- > updateAt (\_ _ -> Nothing) 2 (fromList [(5,"a"), (3,"b")]) Error: index out of range--- > updateAt (\_ _ -> Nothing) (-1) (fromList [(5,"a"), (3,"b")]) Error: index out of range--updateAt :: (k -> a -> Maybe a) -> Int -> Map k a -> Map k a-updateAt f i t = i `seq`- case t of- Tip -> error "Map.updateAt: index out of range"- Bin sx kx x l r -> case compare i sizeL of- LT -> balanceR kx x (updateAt f i l) r- GT -> balanceL kx x l (updateAt f (i-sizeL-1) r)- EQ -> case f kx x of- Just x' -> x' `seq` Bin sx kx x' l r- Nothing -> glue l r- where- sizeL = size l--{--------------------------------------------------------------------- Minimal, Maximal---------------------------------------------------------------------}---- | /O(log n)/. Update the value at the minimal key.------ > updateMin (\ a -> Just ("X" ++ a)) (fromList [(5,"a"), (3,"b")]) == fromList [(3, "Xb"), (5, "a")]--- > updateMin (\ _ -> Nothing) (fromList [(5,"a"), (3,"b")]) == singleton 5 "a"--updateMin :: (a -> Maybe a) -> Map k a -> Map k a-updateMin f m- = updateMinWithKey (\_ x -> f x) m---- | /O(log n)/. Update the value at the maximal key.------ > updateMax (\ a -> Just ("X" ++ a)) (fromList [(5,"a"), (3,"b")]) == fromList [(3, "b"), (5, "Xa")]--- > updateMax (\ _ -> Nothing) (fromList [(5,"a"), (3,"b")]) == singleton 3 "b"--updateMax :: (a -> Maybe a) -> Map k a -> Map k a-updateMax f m- = updateMaxWithKey (\_ x -> f x) m----- | /O(log n)/. Update the value at the minimal key.------ > updateMinWithKey (\ k a -> Just ((show k) ++ ":" ++ a)) (fromList [(5,"a"), (3,"b")]) == fromList [(3,"3:b"), (5,"a")]--- > updateMinWithKey (\ _ _ -> Nothing) (fromList [(5,"a"), (3,"b")]) == singleton 5 "a"--updateMinWithKey :: (k -> a -> Maybe a) -> Map k a -> Map k a-updateMinWithKey _ Tip = Tip-updateMinWithKey f (Bin sx kx x Tip r) = case f kx x of- Nothing -> r- Just x' -> x' `seq` Bin sx kx x' Tip r-updateMinWithKey f (Bin _ kx x l r) = balanceR kx x (updateMinWithKey f l) r---- | /O(log n)/. Update the value at the maximal key.------ > updateMaxWithKey (\ k a -> Just ((show k) ++ ":" ++ a)) (fromList [(5,"a"), (3,"b")]) == fromList [(3,"b"), (5,"5:a")]--- > updateMaxWithKey (\ _ _ -> Nothing) (fromList [(5,"a"), (3,"b")]) == singleton 3 "b"--updateMaxWithKey :: (k -> a -> Maybe a) -> Map k a -> Map k a-updateMaxWithKey _ Tip = Tip-updateMaxWithKey f (Bin sx kx x l Tip) = case f kx x of- Nothing -> l- Just x' -> x' `seq` Bin sx kx x' l Tip-updateMaxWithKey f (Bin _ kx x l r) = balanceL kx x l (updateMaxWithKey f r)--{--------------------------------------------------------------------- Union.---------------------------------------------------------------------}---- | The union of a list of maps, with a combining operation:--- (@'unionsWith' f == 'Prelude.foldl' ('unionWith' f) 'empty'@).------ > unionsWith (++) [(fromList [(5, "a"), (3, "b")]), (fromList [(5, "A"), (7, "C")]), (fromList [(5, "A3"), (3, "B3")])]--- > == fromList [(3, "bB3"), (5, "aAA3"), (7, "C")]--unionsWith :: Ord k => (a->a->a) -> [Map k a] -> Map k a-unionsWith f ts- = foldlStrict (unionWith f) empty ts-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE unionsWith #-}-#endif--{--------------------------------------------------------------------- Union with a combining function---------------------------------------------------------------------}--- | /O(n+m)/. Union with a combining function. The implementation uses the efficient /hedge-union/ algorithm.------ > unionWith (++) (fromList [(5, "a"), (3, "b")]) (fromList [(5, "A"), (7, "C")]) == fromList [(3, "b"), (5, "aA"), (7, "C")]--unionWith :: Ord k => (a -> a -> a) -> Map k a -> Map k a -> Map k a-unionWith f m1 m2- = unionWithKey (\_ x y -> f x y) m1 m2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE unionWith #-}-#endif---- | /O(n+m)/.--- Union with a combining function. The implementation uses the efficient /hedge-union/ algorithm.------ > let f key left_value right_value = (show key) ++ ":" ++ left_value ++ "|" ++ right_value--- > unionWithKey f (fromList [(5, "a"), (3, "b")]) (fromList [(5, "A"), (7, "C")]) == fromList [(3, "b"), (5, "5:a|A"), (7, "C")]--unionWithKey :: Ord k => (k -> a -> a -> a) -> Map k a -> Map k a -> Map k a-unionWithKey f t1 t2 = mergeWithKey (\k x1 x2 -> Just $ f k x1 x2) id id t1 t2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE unionWithKey #-}-#endif--{--------------------------------------------------------------------- Difference---------------------------------------------------------------------}---- | /O(n+m)/. Difference with a combining function.--- When two equal keys are--- encountered, the combining function is applied to the values of these keys.--- If it returns 'Nothing', the element is discarded (proper set difference). If--- it returns (@'Just' y@), the element is updated with a new value @y@.--- The implementation uses an efficient /hedge/ algorithm comparable with /hedge-union/.------ > let f al ar = if al == "b" then Just (al ++ ":" ++ ar) else Nothing--- > differenceWith f (fromList [(5, "a"), (3, "b")]) (fromList [(5, "A"), (3, "B"), (7, "C")])--- > == singleton 3 "b:B"--differenceWith :: Ord k => (a -> b -> Maybe a) -> Map k a -> Map k b -> Map k a-differenceWith f m1 m2- = differenceWithKey (\_ x y -> f x y) m1 m2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE differenceWith #-}-#endif---- | /O(n+m)/. Difference with a combining function. When two equal keys are--- encountered, the combining function is applied to the key and both values.--- If it returns 'Nothing', the element is discarded (proper set difference). If--- it returns (@'Just' y@), the element is updated with a new value @y@.--- The implementation uses an efficient /hedge/ algorithm comparable with /hedge-union/.------ > let f k al ar = if al == "b" then Just ((show k) ++ ":" ++ al ++ "|" ++ ar) else Nothing--- > differenceWithKey f (fromList [(5, "a"), (3, "b")]) (fromList [(5, "A"), (3, "B"), (10, "C")])--- > == singleton 3 "3:b|B"--differenceWithKey :: Ord k => (k -> a -> b -> Maybe a) -> Map k a -> Map k b -> Map k a-differenceWithKey f t1 t2 = mergeWithKey f id (const Tip) t1 t2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE differenceWithKey #-}-#endif---{--------------------------------------------------------------------- Intersection---------------------------------------------------------------------}---- | /O(n+m)/. Intersection with a combining function. The implementation uses--- an efficient /hedge/ algorithm comparable with /hedge-union/.------ > intersectionWith (++) (fromList [(5, "a"), (3, "b")]) (fromList [(5, "A"), (7, "C")]) == singleton 5 "aA"--intersectionWith :: Ord k => (a -> b -> c) -> Map k a -> Map k b -> Map k c-intersectionWith f m1 m2- = intersectionWithKey (\_ x y -> f x y) m1 m2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE intersectionWith #-}-#endif---- | /O(n+m)/. Intersection with a combining function. The implementation uses--- an efficient /hedge/ algorithm comparable with /hedge-union/.------ > let f k al ar = (show k) ++ ":" ++ al ++ "|" ++ ar--- > intersectionWithKey f (fromList [(5, "a"), (3, "b")]) (fromList [(5, "A"), (7, "C")]) == singleton 5 "5:a|A"---intersectionWithKey :: Ord k => (k -> a -> b -> c) -> Map k a -> Map k b -> Map k c-intersectionWithKey f t1 t2 = mergeWithKey (\k x1 x2 -> Just $ f k x1 x2) (const Tip) (const Tip) t1 t2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE intersectionWithKey #-}-#endif---{--------------------------------------------------------------------- MergeWithKey---------------------------------------------------------------------}---- | /O(n+m)/. A high-performance universal combining function. This function--- is used to define 'unionWith', 'unionWithKey', 'differenceWith',--- 'differenceWithKey', 'intersectionWith', 'intersectionWithKey' and can be--- used to define other custom combine functions.------ Please make sure you know what is going on when using 'mergeWithKey',--- otherwise you can be surprised by unexpected code growth or even--- corruption of the data structure.------ When 'mergeWithKey' is given three arguments, it is inlined to the call--- site. You should therefore use 'mergeWithKey' only to define your custom--- combining functions. For example, you could define 'unionWithKey',--- 'differenceWithKey' and 'intersectionWithKey' as------ > myUnionWithKey f m1 m2 = mergeWithKey (\k x1 x2 -> Just (f k x1 x2)) id id m1 m2--- > myDifferenceWithKey f m1 m2 = mergeWithKey f id (const empty) m1 m2--- > myIntersectionWithKey f m1 m2 = mergeWithKey (\k x1 x2 -> Just (f k x1 x2)) (const empty) (const empty) m1 m2------ When calling @'mergeWithKey' combine only1 only2@, a function combining two--- 'IntMap's is created, such that------ * if a key is present in both maps, it is passed with both corresponding--- values to the @combine@ function. Depending on the result, the key is either--- present in the result with specified value, or is left out;------ * a nonempty subtree present only in the first map is passed to @only1@ and--- the output is added to the result;------ * a nonempty subtree present only in the second map is passed to @only2@ and--- the output is added to the result.------ The @only1@ and @only2@ methods /must return a map with a subset (possibly empty) of the keys of the given map/.--- The values can be modified arbitrarily. Most common variants of @only1@ and--- @only2@ are 'id' and @'const' 'empty'@, but for example @'map' f@ or--- @'filterWithKey' f@ could be used for any @f@.--mergeWithKey :: Ord k => (k -> a -> b -> Maybe c) -> (Map k a -> Map k c) -> (Map k b -> Map k c)- -> Map k a -> Map k b -> Map k c-mergeWithKey f g1 g2 = go- where- go Tip t2 = g2 t2- go t1 Tip = g1 t1- go t1 t2 = hedgeMerge NothingS NothingS t1 t2-- hedgeMerge _ _ t1 Tip = g1 t1- hedgeMerge blo bhi Tip (Bin _ kx x l r) = g2 $ join kx x (filterGt blo l) (filterLt bhi r)- hedgeMerge blo bhi (Bin _ kx x l r) t2 = let l' = hedgeMerge blo bmi l (trim blo bmi t2)- (found, trim_t2) = trimLookupLo kx bhi t2- r' = hedgeMerge bmi bhi r trim_t2- in case found of- Nothing -> case g1 (singleton kx x) of- Tip -> merge l' r'- (Bin _ _ x' Tip Tip) -> join kx x' l' r'- _ -> error "mergeWithKey: Given function only1 does not fulfil required conditions (see documentation)"- Just x2 -> case f kx x x2 of- Nothing -> merge l' r'- Just x' -> x' `seq` join kx x' l' r'- where bmi = JustS kx-{-# INLINE mergeWithKey #-}--{--------------------------------------------------------------------- Filter and partition---------------------------------------------------------------------}---- | /O(n)/. Map values and collect the 'Just' results.------ > let f x = if x == "a" then Just "new a" else Nothing--- > mapMaybe f (fromList [(5,"a"), (3,"b")]) == singleton 5 "new a"--mapMaybe :: (a -> Maybe b) -> Map k a -> Map k b-mapMaybe f = mapMaybeWithKey (\_ x -> f x)---- | /O(n)/. Map keys\/values and collect the 'Just' results.------ > let f k _ = if k < 5 then Just ("key : " ++ (show k)) else Nothing--- > mapMaybeWithKey f (fromList [(5,"a"), (3,"b")]) == singleton 3 "key : 3"--mapMaybeWithKey :: (k -> a -> Maybe b) -> Map k a -> Map k b-mapMaybeWithKey _ Tip = Tip-mapMaybeWithKey f (Bin _ kx x l r) = case f kx x of- Just y -> y `seq` join kx y (mapMaybeWithKey f l) (mapMaybeWithKey f r)- Nothing -> merge (mapMaybeWithKey f l) (mapMaybeWithKey f r)---- | /O(n)/. Map values and separate the 'Left' and 'Right' results.------ > let f a = if a < "c" then Left a else Right a--- > mapEither f (fromList [(5,"a"), (3,"b"), (1,"x"), (7,"z")])--- > == (fromList [(3,"b"), (5,"a")], fromList [(1,"x"), (7,"z")])--- >--- > mapEither (\ a -> Right a) (fromList [(5,"a"), (3,"b"), (1,"x"), (7,"z")])--- > == (empty, fromList [(5,"a"), (3,"b"), (1,"x"), (7,"z")])--mapEither :: (a -> Either b c) -> Map k a -> (Map k b, Map k c)-mapEither f m- = mapEitherWithKey (\_ x -> f x) m---- | /O(n)/. Map keys\/values and separate the 'Left' and 'Right' results.------ > let f k a = if k < 5 then Left (k * 2) else Right (a ++ a)--- > mapEitherWithKey f (fromList [(5,"a"), (3,"b"), (1,"x"), (7,"z")])--- > == (fromList [(1,2), (3,6)], fromList [(5,"aa"), (7,"zz")])--- >--- > mapEitherWithKey (\_ a -> Right a) (fromList [(5,"a"), (3,"b"), (1,"x"), (7,"z")])--- > == (empty, fromList [(1,"x"), (3,"b"), (5,"a"), (7,"z")])--mapEitherWithKey :: (k -> a -> Either b c) -> Map k a -> (Map k b, Map k c)-mapEitherWithKey f0 t0 = toPair $ go f0 t0- where- go _ Tip = (Tip :*: Tip)- go f (Bin _ kx x l r) = case f kx x of- Left y -> y `seq` (join kx y l1 r1 :*: merge l2 r2)- Right z -> z `seq` (merge l1 r1 :*: join kx z l2 r2)- where- (l1 :*: l2) = go f l- (r1 :*: r2) = go f r--{--------------------------------------------------------------------- Mapping---------------------------------------------------------------------}--- | /O(n)/. Map a function over all values in the map.------ > map (++ "x") (fromList [(5,"a"), (3,"b")]) == fromList [(3, "bx"), (5, "ax")]--map :: (a -> b) -> Map k a -> Map k b-map _ Tip = Tip-map f (Bin sx kx x l r) = let x' = f x in x' `seq` Bin sx kx x' (map f l) (map f r)---- | /O(n)/. Map a function over all values in the map.------ > let f key x = (show key) ++ ":" ++ x--- > mapWithKey f (fromList [(5,"a"), (3,"b")]) == fromList [(3, "3:b"), (5, "5:a")]--mapWithKey :: (k -> a -> b) -> Map k a -> Map k b-mapWithKey _ Tip = Tip-mapWithKey f (Bin sx kx x l r) = let x' = f kx x- in x' `seq` Bin sx kx x' (mapWithKey f l) (mapWithKey f r)---- | /O(n)/. The function 'mapAccum' threads an accumulating--- argument through the map in ascending order of keys.------ > let f a b = (a ++ b, b ++ "X")--- > mapAccum f "Everything: " (fromList [(5,"a"), (3,"b")]) == ("Everything: ba", fromList [(3, "bX"), (5, "aX")])--mapAccum :: (a -> b -> (a,c)) -> a -> Map k b -> (a,Map k c)-mapAccum f a m- = mapAccumWithKey (\a' _ x' -> f a' x') a m---- | /O(n)/. The function 'mapAccumWithKey' threads an accumulating--- argument through the map in ascending order of keys.------ > let f a k b = (a ++ " " ++ (show k) ++ "-" ++ b, b ++ "X")--- > mapAccumWithKey f "Everything:" (fromList [(5,"a"), (3,"b")]) == ("Everything: 3-b 5-a", fromList [(3, "bX"), (5, "aX")])--mapAccumWithKey :: (a -> k -> b -> (a,c)) -> a -> Map k b -> (a,Map k c)-mapAccumWithKey f a t- = mapAccumL f a t---- | /O(n)/. The function 'mapAccumL' threads an accumulating--- argument through the map in ascending order of keys.-mapAccumL :: (a -> k -> b -> (a,c)) -> a -> Map k b -> (a,Map k c)-mapAccumL _ a Tip = (a,Tip)-mapAccumL f a (Bin sx kx x l r) =- let (a1,l') = mapAccumL f a l- (a2,x') = f a1 kx x- (a3,r') = mapAccumL f a2 r- in x' `seq` (a3,Bin sx kx x' l' r')---- | /O(n)/. The function 'mapAccumR' threads an accumulating--- argument through the map in descending order of keys.-mapAccumRWithKey :: (a -> k -> b -> (a,c)) -> a -> Map k b -> (a,Map k c)-mapAccumRWithKey _ a Tip = (a,Tip)-mapAccumRWithKey f a (Bin sx kx x l r) =- let (a1,r') = mapAccumRWithKey f a r- (a2,x') = f a1 kx x- (a3,l') = mapAccumRWithKey f a2 l- in x' `seq` (a3,Bin sx kx x' l' r')---- | /O(n*log n)/.--- @'mapKeysWith' c f s@ is the map obtained by applying @f@ to each key of @s@.------ The size of the result may be smaller if @f@ maps two or more distinct--- keys to the same new key. In this case the associated values will be--- combined using @c@.------ > mapKeysWith (++) (\ _ -> 1) (fromList [(1,"b"), (2,"a"), (3,"d"), (4,"c")]) == singleton 1 "cdab"--- > mapKeysWith (++) (\ _ -> 3) (fromList [(1,"b"), (2,"a"), (3,"d"), (4,"c")]) == singleton 3 "cdab"--mapKeysWith :: Ord k2 => (a -> a -> a) -> (k1->k2) -> Map k1 a -> Map k2 a-mapKeysWith c f = fromListWith c . foldrWithKey (\k x xs -> (f k, x) : xs) []-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE mapKeysWith #-}-#endif--{--------------------------------------------------------------------- Conversions---------------------------------------------------------------------}---- | /O(n)/. Build a map from a set of keys and a function which for each key--- computes its value.------ > fromSet (\k -> replicate k 'a') (Data.Set.fromList [3, 5]) == fromList [(5,"aaaaa"), (3,"aaa")]--- > fromSet undefined Data.Set.empty == empty--fromSet :: (k -> a) -> Set.Set k -> Map k a-fromSet _ Set.Tip = Tip-fromSet f (Set.Bin sz x l r) = case f x of v -> v `seq` Bin sz x v (fromSet f l) (fromSet f r)--{--------------------------------------------------------------------- Lists- use [foldlStrict] to reduce demand on the control-stack---------------------------------------------------------------------}--- | /O(n*log n)/. Build a map from a list of key\/value pairs. See also 'fromAscList'.--- If the list contains more than one value for the same key, the last value--- for the key is retained.------ If the keys of the list are ordered, linear-time implementation is used,--- with the performance equal to 'fromDistinctAscList'.------ > fromList [] == empty--- > fromList [(5,"a"), (3,"b"), (5, "c")] == fromList [(5,"c"), (3,"b")]--- > fromList [(5,"c"), (3,"b"), (5, "a")] == fromList [(5,"a"), (3,"b")]---- For some reason, when 'singleton' is used in fromList or in--- create, it is not inlined, so we inline it manually.-fromList :: Ord k => [(k,a)] -> Map k a-fromList [] = Tip-fromList [(kx, x)] = x `seq` Bin 1 kx x Tip Tip-fromList ((kx0, x0) : xs0) | not_ordered kx0 xs0 = x0 `seq` fromList' (Bin 1 kx0 x0 Tip Tip) xs0- | otherwise = x0 `seq` go (1::Int) (Bin 1 kx0 x0 Tip Tip) xs0- where- not_ordered _ [] = False- not_ordered kx ((ky,_) : _) = kx >= ky- {-# INLINE not_ordered #-}-- fromList' t0 xs = foldlStrict ins t0 xs- where ins t (k,x) = insert k x t-- STRICT_1_OF_3(go)- go _ t [] = t- go _ t [(kx, x)] = x `seq` insertMax kx x t- go s l xs@((kx, x) : xss) | not_ordered kx xss = fromList' l xs- | otherwise = case create s xss of- (r, ys, []) -> x `seq` go (s `shiftL` 1) (join kx x l r) ys- (r, _, ys) -> x `seq` fromList' (join kx x l r) ys-- -- The create is returning a triple (tree, xs, ys). Both xs and ys- -- represent not yet processed elements and only one of them can be nonempty.- -- If ys is nonempty, the keys in ys are not ordered with respect to tree- -- and must be inserted using fromList'. Otherwise the keys have been- -- ordered so far.- STRICT_1_OF_2(create)- create _ [] = (Tip, [], [])- create s xs@(xp : xss)- | s == 1 = case xp of (kx, x) | not_ordered kx xss -> x `seq` (Bin 1 kx x Tip Tip, [], xss)- | otherwise -> x `seq` (Bin 1 kx x Tip Tip, xss, [])- | otherwise = case create (s `shiftR` 1) xs of- res@(_, [], _) -> res- (l, [(ky, y)], zs) -> y `seq` (insertMax ky y l, [], zs)- (l, ys@((ky, y):yss), _) | not_ordered ky yss -> (l, [], ys)- | otherwise -> case create (s `shiftR` 1) yss of- (r, zs, ws) -> y `seq` (join ky y l r, zs, ws)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE fromList #-}-#endif---- | /O(n*log n)/. Build a map from a list of key\/value pairs with a combining function. See also 'fromAscListWith'.------ > fromListWith (++) [(5,"a"), (5,"b"), (3,"b"), (3,"a"), (5,"a")] == fromList [(3, "ab"), (5, "aba")]--- > fromListWith (++) [] == empty--fromListWith :: Ord k => (a -> a -> a) -> [(k,a)] -> Map k a-fromListWith f xs- = fromListWithKey (\_ x y -> f x y) xs-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE fromListWith #-}-#endif---- | /O(n*log n)/. Build a map from a list of key\/value pairs with a combining function. See also 'fromAscListWithKey'.------ > let f k a1 a2 = (show k) ++ a1 ++ a2--- > fromListWithKey f [(5,"a"), (5,"b"), (3,"b"), (3,"a"), (5,"a")] == fromList [(3, "3ab"), (5, "5a5ba")]--- > fromListWithKey f [] == empty--fromListWithKey :: Ord k => (k -> a -> a -> a) -> [(k,a)] -> Map k a-fromListWithKey f xs- = foldlStrict ins empty xs- where- ins t (k,x) = insertWithKey f k x t-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE fromListWithKey #-}-#endif--{--------------------------------------------------------------------- Building trees from ascending/descending lists can be done in linear time.-- Note that if [xs] is ascending that:- fromAscList xs == fromList xs- fromAscListWith f xs == fromListWith f xs---------------------------------------------------------------------}--- | /O(n)/. Build a map from an ascending list in linear time.--- /The precondition (input list is ascending) is not checked./------ > fromAscList [(3,"b"), (5,"a")] == fromList [(3, "b"), (5, "a")]--- > fromAscList [(3,"b"), (5,"a"), (5,"b")] == fromList [(3, "b"), (5, "b")]--- > valid (fromAscList [(3,"b"), (5,"a"), (5,"b")]) == True--- > valid (fromAscList [(5,"a"), (3,"b"), (5,"b")]) == False--fromAscList :: Eq k => [(k,a)] -> Map k a-fromAscList xs- = fromAscListWithKey (\_ x _ -> x) xs-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE fromAscList #-}-#endif---- | /O(n)/. Build a map from an ascending list in linear time with a combining function for equal keys.--- /The precondition (input list is ascending) is not checked./------ > fromAscListWith (++) [(3,"b"), (5,"a"), (5,"b")] == fromList [(3, "b"), (5, "ba")]--- > valid (fromAscListWith (++) [(3,"b"), (5,"a"), (5,"b")]) == True--- > valid (fromAscListWith (++) [(5,"a"), (3,"b"), (5,"b")]) == False--fromAscListWith :: Eq k => (a -> a -> a) -> [(k,a)] -> Map k a-fromAscListWith f xs- = fromAscListWithKey (\_ x y -> f x y) xs-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE fromAscListWith #-}-#endif---- | /O(n)/. Build a map from an ascending list in linear time with a--- combining function for equal keys.--- /The precondition (input list is ascending) is not checked./------ > let f k a1 a2 = (show k) ++ ":" ++ a1 ++ a2--- > fromAscListWithKey f [(3,"b"), (5,"a"), (5,"b"), (5,"b")] == fromList [(3, "b"), (5, "5:b5:ba")]--- > valid (fromAscListWithKey f [(3,"b"), (5,"a"), (5,"b"), (5,"b")]) == True--- > valid (fromAscListWithKey f [(5,"a"), (3,"b"), (5,"b"), (5,"b")]) == False--fromAscListWithKey :: Eq k => (k -> a -> a -> a) -> [(k,a)] -> Map k a-fromAscListWithKey f xs- = fromDistinctAscList (combineEq f xs)- where- -- [combineEq f xs] combines equal elements with function [f] in an ordered list [xs]- combineEq _ xs'- = case xs' of- [] -> []- [x] -> [x]- (x:xx) -> combineEq' x xx-- combineEq' z [] = [z]- combineEq' z@(kz,zz) (x@(kx,xx):xs')- | kx==kz = let yy = f kx xx zz in yy `seq` combineEq' (kx,yy) xs'- | otherwise = z:combineEq' x xs'-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE fromAscListWithKey #-}-#endif---- | /O(n)/. Build a map from an ascending list of distinct elements in linear time.--- /The precondition is not checked./------ > fromDistinctAscList [(3,"b"), (5,"a")] == fromList [(3, "b"), (5, "a")]--- > valid (fromDistinctAscList [(3,"b"), (5,"a")]) == True--- > valid (fromDistinctAscList [(3,"b"), (5,"a"), (5,"b")]) == False---- For some reason, when 'singleton' is used in fromDistinctAscList or in--- create, it is not inlined, so we inline it manually.-fromDistinctAscList :: [(k,a)] -> Map k a-fromDistinctAscList [] = Tip-fromDistinctAscList ((kx0, x0) : xs0) = x0 `seq` go (1::Int) (Bin 1 kx0 x0 Tip Tip) xs0- where- STRICT_1_OF_3(go)- go _ t [] = t- go s l ((kx, x) : xs) = case create s xs of- (r, ys) -> x `seq` go (s `shiftL` 1) (join kx x l r) ys-- STRICT_1_OF_2(create)- create _ [] = (Tip, [])- create s xs@(x' : xs')- | s == 1 = case x' of (kx, x) -> x `seq` (Bin 1 kx x Tip Tip, xs')- | otherwise = case create (s `shiftR` 1) xs of- res@(_, []) -> res- (l, (ky, y):ys) -> case create (s `shiftR` 1) ys of- (r, zs) -> y `seq` (join ky y l r, zs)
− containers-0.5.2.1/Darcs/Data/Set/Base.hs
@@ -1,1526 +0,0 @@-{-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__-{-# LANGUAGE DeriveDataTypeable, StandaloneDeriving #-}-#endif-#if !defined(TESTING) && __GLASGOW_HASKELL__ >= 703-{-# LANGUAGE Trustworthy #-}-#endif--------------------------------------------------------------------------------- |--- Module : Data.Set.Base--- Copyright : (c) Daan Leijen 2002--- License : BSD-style--- Maintainer : libraries@haskell.org--- Stability : provisional--- Portability : portable------ An efficient implementation of sets.------ These modules are intended to be imported qualified, to avoid name--- clashes with Prelude functions, e.g.------ > import Data.Set (Set)--- > import qualified Data.Set as Set------ The implementation of 'Set' is based on /size balanced/ binary trees (or--- trees of /bounded balance/) as described by:------ * Stephen Adams, \"/Efficient sets: a balancing act/\",--- Journal of Functional Programming 3(4):553-562, October 1993,--- <http://www.swiss.ai.mit.edu/~adams/BB/>.------ * J. Nievergelt and E.M. Reingold,--- \"/Binary search trees of bounded balance/\",--- SIAM journal of computing 2(1), March 1973.------ Note that the implementation is /left-biased/ -- the elements of a--- first argument are always preferred to the second, for example in--- 'union' or 'insert'. Of course, left-biasing can only be observed--- when equality is an equivalence relation instead of structural--- equality.---------------------------------------------------------------------------------- [Note: Using INLINABLE]--- ~~~~~~~~~~~~~~~~~~~~~~~--- It is crucial to the performance that the functions specialize on the Ord--- type when possible. GHC 7.0 and higher does this by itself when it sees th--- unfolding of a function -- that is why all public functions are marked--- INLINABLE (that exposes the unfolding).----- [Note: Using INLINE]--- ~~~~~~~~~~~~~~~~~~~~--- For other compilers and GHC pre 7.0, we mark some of the functions INLINE.--- We mark the functions that just navigate down the tree (lookup, insert,--- delete and similar). That navigation code gets inlined and thus specialized--- when possible. There is a price to pay -- code growth. The code INLINED is--- therefore only the tree navigation, all the real work (rebalancing) is not--- INLINED by using a NOINLINE.------ All methods marked INLINE have to be nonrecursive -- a 'go' function doing--- the real work is provided.----- [Note: Type of local 'go' function]--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--- If the local 'go' function uses an Ord class, it sometimes heap-allocates--- the Ord dictionary when the 'go' function does not have explicit type.--- In that case we give 'go' explicit type. But this slightly decrease--- performance, as the resulting 'go' function can float out to top level.----- [Note: Local 'go' functions and capturing]--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--- As opposed to IntSet, when 'go' function captures an argument, increased--- heap-allocation can occur: sometimes in a polymorphic function, the 'go'--- floats out of its enclosing function and then it heap-allocates the--- dictionary and the argument. Maybe it floats out too late and strictness--- analyzer cannot see that these could be passed on stack.------ For example, change 'member' so that its local 'go' function is not passing--- argument x and then look at the resulting code for hedgeInt.----- [Note: Order of constructors]--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--- The order of constructors of Set matters when considering performance.--- Currently in GHC 7.0, when type has 2 constructors, a forward conditional--- jump is made when successfully matching second constructor. Successful match--- of first constructor results in the forward jump not taken.--- On GHC 7.0, reordering constructors from Tip | Bin to Bin | Tip--- improves the benchmark by up to 10% on x86.--module Darcs.Data.Set.Base (- -- * Set type- Set(..) -- instance Eq,Ord,Show,Read,Data,Typeable-- -- * Operators- , (\\)-- -- * Query- , null- , size- , member- , notMember- , lookupLT- , lookupGT- , lookupLE- , lookupGE- , isSubsetOf- , isProperSubsetOf-- -- * Construction- , empty- , singleton- , insert- , delete-- -- * Combine- , union- , unions- , difference- , intersection-- -- * Filter- , filter- , partition- , split- , splitMember-- -- * Indexed- , lookupIndex- , findIndex- , elemAt- , deleteAt-- -- * Map- , map- , mapMonotonic-- -- * Folds- , foldr- , foldl- -- ** Strict folds- , foldr'- , foldl'- -- ** Legacy folds- , fold-- -- * Min\/Max- , findMin- , findMax- , deleteMin- , deleteMax- , deleteFindMin- , deleteFindMax- , maxView- , minView-- -- * Conversion-- -- ** List- , elems- , toList- , fromList-- -- ** Ordered list- , toAscList- , toDescList- , fromAscList- , fromDistinctAscList-- -- * Debugging- , showTree- , showTreeWith- , valid-- -- Internals (for testing)- , bin- , balanced- , join- , merge- ) where--import Prelude hiding (filter,foldl,foldr,null,map)-import qualified Data.List as List-import Data.Bits (shiftL, shiftR)-import Data.Monoid (Monoid(..))-import qualified Data.Foldable as Foldable-import Data.Typeable-import Control.DeepSeq (NFData(rnf))--import Darcs.Data.StrictPair--#if __GLASGOW_HASKELL__-import GHC.Exts ( build )-import Text.Read-import Data.Data-#endif---- Use macros to define strictness of functions.--- STRICT_x_OF_y denotes an y-ary function strict in the x-th parameter.--- We do not use BangPatterns, because they are not in any standard and we--- want the compilers to be compiled by as many compilers as possible.-#define STRICT_1_OF_2(fn) fn arg _ | arg `seq` False = undefined-#define STRICT_1_OF_3(fn) fn arg _ _ | arg `seq` False = undefined-#define STRICT_2_OF_3(fn) fn _ arg _ | arg `seq` False = undefined--{--------------------------------------------------------------------- Operators---------------------------------------------------------------------}-infixl 9 \\ ------ | /O(n+m)/. See 'difference'.-(\\) :: Ord a => Set a -> Set a -> Set a-m1 \\ m2 = difference m1 m2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE (\\) #-}-#endif--{--------------------------------------------------------------------- Sets are size balanced trees---------------------------------------------------------------------}--- | A set of values @a@.---- See Note: Order of constructors-data Set a = Bin {-# UNPACK #-} !Size !a !(Set a) !(Set a)- | Tip--type Size = Int--instance Ord a => Monoid (Set a) where- mempty = empty- mappend = union- mconcat = unions--instance Foldable.Foldable Set where- fold Tip = mempty- fold (Bin _ k l r) = Foldable.fold l `mappend` k `mappend` Foldable.fold r- foldr = foldr- foldl = foldl- foldMap _ Tip = mempty- foldMap f (Bin _ k l r) = Foldable.foldMap f l `mappend` f k `mappend` Foldable.foldMap f r--#if __GLASGOW_HASKELL__--{--------------------------------------------------------------------- A Data instance---------------------------------------------------------------------}---- This instance preserves data abstraction at the cost of inefficiency.--- We provide limited reflection services for the sake of data abstraction.--instance (Data a, Ord a) => Data (Set a) where- gfoldl f z set = z fromList `f` (toList set)- toConstr _ = fromListConstr- gunfold k z c = case constrIndex c of- 1 -> k (z fromList)- _ -> error "gunfold"- dataTypeOf _ = setDataType- dataCast1 f = gcast1 f--fromListConstr :: Constr-fromListConstr = mkConstr setDataType "fromList" [] Prefix--setDataType :: DataType-setDataType = mkDataType "Data.Set.Base.Set" [fromListConstr]--#endif--{--------------------------------------------------------------------- Query---------------------------------------------------------------------}--- | /O(1)/. Is this the empty set?-null :: Set a -> Bool-null Tip = True-null (Bin {}) = False-{-# INLINE null #-}---- | /O(1)/. The number of elements in the set.-size :: Set a -> Int-size Tip = 0-size (Bin sz _ _ _) = sz-{-# INLINE size #-}---- | /O(log n)/. Is the element in the set?-member :: Ord a => a -> Set a -> Bool-member = go- where- STRICT_1_OF_2(go)- go _ Tip = False- go x (Bin _ y l r) = case compare x y of- LT -> go x l- GT -> go x r- EQ -> True-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE member #-}-#else-{-# INLINE member #-}-#endif---- | /O(log n)/. Is the element not in the set?-notMember :: Ord a => a -> Set a -> Bool-notMember a t = not $ member a t-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE notMember #-}-#else-{-# INLINE notMember #-}-#endif---- | /O(log n)/. Find largest element smaller than the given one.------ > lookupLT 3 (fromList [3, 5]) == Nothing--- > lookupLT 5 (fromList [3, 5]) == Just 3-lookupLT :: Ord a => a -> Set a -> Maybe a-lookupLT = goNothing- where- STRICT_1_OF_2(goNothing)- goNothing _ Tip = Nothing- goNothing x (Bin _ y l r) | x <= y = goNothing x l- | otherwise = goJust x y r-- STRICT_1_OF_3(goJust)- goJust _ best Tip = Just best- goJust x best (Bin _ y l r) | x <= y = goJust x best l- | otherwise = goJust x y r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE lookupLT #-}-#else-{-# INLINE lookupLT #-}-#endif---- | /O(log n)/. Find smallest element greater than the given one.------ > lookupGT 4 (fromList [3, 5]) == Just 5--- > lookupGT 5 (fromList [3, 5]) == Nothing-lookupGT :: Ord a => a -> Set a -> Maybe a-lookupGT = goNothing- where- STRICT_1_OF_2(goNothing)- goNothing _ Tip = Nothing- goNothing x (Bin _ y l r) | x < y = goJust x y l- | otherwise = goNothing x r-- STRICT_1_OF_3(goJust)- goJust _ best Tip = Just best- goJust x best (Bin _ y l r) | x < y = goJust x y l- | otherwise = goJust x best r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE lookupGT #-}-#else-{-# INLINE lookupGT #-}-#endif---- | /O(log n)/. Find largest element smaller or equal to the given one.------ > lookupLE 2 (fromList [3, 5]) == Nothing--- > lookupLE 4 (fromList [3, 5]) == Just 3--- > lookupLE 5 (fromList [3, 5]) == Just 5-lookupLE :: Ord a => a -> Set a -> Maybe a-lookupLE = goNothing- where- STRICT_1_OF_2(goNothing)- goNothing _ Tip = Nothing- goNothing x (Bin _ y l r) = case compare x y of LT -> goNothing x l- EQ -> Just y- GT -> goJust x y r-- STRICT_1_OF_3(goJust)- goJust _ best Tip = Just best- goJust x best (Bin _ y l r) = case compare x y of LT -> goJust x best l- EQ -> Just y- GT -> goJust x y r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE lookupLE #-}-#else-{-# INLINE lookupLE #-}-#endif---- | /O(log n)/. Find smallest element greater or equal to the given one.------ > lookupGE 3 (fromList [3, 5]) == Just 3--- > lookupGE 4 (fromList [3, 5]) == Just 5--- > lookupGE 6 (fromList [3, 5]) == Nothing-lookupGE :: Ord a => a -> Set a -> Maybe a-lookupGE = goNothing- where- STRICT_1_OF_2(goNothing)- goNothing _ Tip = Nothing- goNothing x (Bin _ y l r) = case compare x y of LT -> goJust x y l- EQ -> Just y- GT -> goNothing x r-- STRICT_1_OF_3(goJust)- goJust _ best Tip = Just best- goJust x best (Bin _ y l r) = case compare x y of LT -> goJust x y l- EQ -> Just y- GT -> goJust x best r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE lookupGE #-}-#else-{-# INLINE lookupGE #-}-#endif--{--------------------------------------------------------------------- Construction---------------------------------------------------------------------}--- | /O(1)/. The empty set.-empty :: Set a-empty = Tip-{-# INLINE empty #-}---- | /O(1)/. Create a singleton set.-singleton :: a -> Set a-singleton x = Bin 1 x Tip Tip-{-# INLINE singleton #-}--{--------------------------------------------------------------------- Insertion, Deletion---------------------------------------------------------------------}--- | /O(log n)/. Insert an element in a set.--- If the set already contains an element equal to the given value,--- it is replaced with the new value.---- See Note: Type of local 'go' function-insert :: Ord a => a -> Set a -> Set a-insert = go- where- go :: Ord a => a -> Set a -> Set a- STRICT_1_OF_2(go)- go x Tip = singleton x- go x (Bin sz y l r) = case compare x y of- LT -> balanceL y (go x l) r- GT -> balanceR y l (go x r)- EQ -> Bin sz x l r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE insert #-}-#else-{-# INLINE insert #-}-#endif---- Insert an element to the set only if it is not in the set.--- Used by `union`.---- See Note: Type of local 'go' function-insertR :: Ord a => a -> Set a -> Set a-insertR = go- where- go :: Ord a => a -> Set a -> Set a- STRICT_1_OF_2(go)- go x Tip = singleton x- go x t@(Bin _ y l r) = case compare x y of- LT -> balanceL y (go x l) r- GT -> balanceR y l (go x r)- EQ -> t-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE insertR #-}-#else-{-# INLINE insertR #-}-#endif---- | /O(log n)/. Delete an element from a set.---- See Note: Type of local 'go' function-delete :: Ord a => a -> Set a -> Set a-delete = go- where- go :: Ord a => a -> Set a -> Set a- STRICT_1_OF_2(go)- go _ Tip = Tip- go x (Bin _ y l r) = case compare x y of- LT -> balanceR y (go x l) r- GT -> balanceL y l (go x r)- EQ -> glue l r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE delete #-}-#else-{-# INLINE delete #-}-#endif--{--------------------------------------------------------------------- Subset---------------------------------------------------------------------}--- | /O(n+m)/. Is this a proper subset? (ie. a subset but not equal).-isProperSubsetOf :: Ord a => Set a -> Set a -> Bool-isProperSubsetOf s1 s2- = (size s1 < size s2) && (isSubsetOf s1 s2)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE isProperSubsetOf #-}-#endif----- | /O(n+m)/. Is this a subset?--- @(s1 `isSubsetOf` s2)@ tells whether @s1@ is a subset of @s2@.-isSubsetOf :: Ord a => Set a -> Set a -> Bool-isSubsetOf t1 t2- = (size t1 <= size t2) && (isSubsetOfX t1 t2)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE isSubsetOf #-}-#endif--isSubsetOfX :: Ord a => Set a -> Set a -> Bool-isSubsetOfX Tip _ = True-isSubsetOfX _ Tip = False-isSubsetOfX (Bin _ x l r) t- = found && isSubsetOfX l lt && isSubsetOfX r gt- where- (lt,found,gt) = splitMember x t-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE isSubsetOfX #-}-#endif---{--------------------------------------------------------------------- Minimal, Maximal---------------------------------------------------------------------}--- | /O(log n)/. The minimal element of a set.-findMin :: Set a -> a-findMin (Bin _ x Tip _) = x-findMin (Bin _ _ l _) = findMin l-findMin Tip = error "Set.findMin: empty set has no minimal element"---- | /O(log n)/. The maximal element of a set.-findMax :: Set a -> a-findMax (Bin _ x _ Tip) = x-findMax (Bin _ _ _ r) = findMax r-findMax Tip = error "Set.findMax: empty set has no maximal element"---- | /O(log n)/. Delete the minimal element. Returns an empty set if the set is empty.-deleteMin :: Set a -> Set a-deleteMin (Bin _ _ Tip r) = r-deleteMin (Bin _ x l r) = balanceR x (deleteMin l) r-deleteMin Tip = Tip---- | /O(log n)/. Delete the maximal element. Returns an empty set if the set is empty.-deleteMax :: Set a -> Set a-deleteMax (Bin _ _ l Tip) = l-deleteMax (Bin _ x l r) = balanceL x l (deleteMax r)-deleteMax Tip = Tip--{--------------------------------------------------------------------- Union.---------------------------------------------------------------------}--- | The union of a list of sets: (@'unions' == 'foldl' 'union' 'empty'@).-unions :: Ord a => [Set a] -> Set a-unions = foldlStrict union empty-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE unions #-}-#endif---- | /O(n+m)/. The union of two sets, preferring the first set when--- equal elements are encountered.--- The implementation uses the efficient /hedge-union/ algorithm.-union :: Ord a => Set a -> Set a -> Set a-union Tip t2 = t2-union t1 Tip = t1-union t1 t2 = hedgeUnion NothingS NothingS t1 t2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE union #-}-#endif--hedgeUnion :: Ord a => MaybeS a -> MaybeS a -> Set a -> Set a -> Set a-hedgeUnion _ _ t1 Tip = t1-hedgeUnion blo bhi Tip (Bin _ x l r) = join x (filterGt blo l) (filterLt bhi r)-hedgeUnion _ _ t1 (Bin _ x Tip Tip) = insertR x t1 -- According to benchmarks, this special case increases- -- performance up to 30%. It does not help in difference or intersection.-hedgeUnion blo bhi (Bin _ x l r) t2 = join x (hedgeUnion blo bmi l (trim blo bmi t2))- (hedgeUnion bmi bhi r (trim bmi bhi t2))- where bmi = JustS x-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE hedgeUnion #-}-#endif--{--------------------------------------------------------------------- Difference---------------------------------------------------------------------}--- | /O(n+m)/. Difference of two sets.--- The implementation uses an efficient /hedge/ algorithm comparable with /hedge-union/.-difference :: Ord a => Set a -> Set a -> Set a-difference Tip _ = Tip-difference t1 Tip = t1-difference t1 t2 = hedgeDiff NothingS NothingS t1 t2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE difference #-}-#endif--hedgeDiff :: Ord a => MaybeS a -> MaybeS a -> Set a -> Set a -> Set a-hedgeDiff _ _ Tip _ = Tip-hedgeDiff blo bhi (Bin _ x l r) Tip = join x (filterGt blo l) (filterLt bhi r)-hedgeDiff blo bhi t (Bin _ x l r) = merge (hedgeDiff blo bmi (trim blo bmi t) l)- (hedgeDiff bmi bhi (trim bmi bhi t) r)- where bmi = JustS x-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE hedgeDiff #-}-#endif--{--------------------------------------------------------------------- Intersection---------------------------------------------------------------------}--- | /O(n+m)/. The intersection of two sets. The implementation uses an--- efficient /hedge/ algorithm comparable with /hedge-union/. Elements of the--- result come from the first set, so for example------ > import qualified Data.Set as S--- > data AB = A | B deriving Show--- > instance Ord AB where compare _ _ = EQ--- > instance Eq AB where _ == _ = True--- > main = print (S.singleton A `S.intersection` S.singleton B,--- > S.singleton B `S.intersection` S.singleton A)------ prints @(fromList [A],fromList [B])@.-intersection :: Ord a => Set a -> Set a -> Set a-intersection Tip _ = Tip-intersection _ Tip = Tip-intersection t1 t2 = hedgeInt NothingS NothingS t1 t2-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE intersection #-}-#endif--hedgeInt :: Ord a => MaybeS a -> MaybeS a -> Set a -> Set a -> Set a-hedgeInt _ _ _ Tip = Tip-hedgeInt _ _ Tip _ = Tip-hedgeInt blo bhi (Bin _ x l r) t2 = let l' = hedgeInt blo bmi l (trim blo bmi t2)- r' = hedgeInt bmi bhi r (trim bmi bhi t2)- in if x `member` t2 then join x l' r' else merge l' r'- where bmi = JustS x-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE hedgeInt #-}-#endif--{--------------------------------------------------------------------- Filter and partition---------------------------------------------------------------------}--- | /O(n)/. Filter all elements that satisfy the predicate.-filter :: (a -> Bool) -> Set a -> Set a-filter _ Tip = Tip-filter p (Bin _ x l r)- | p x = join x (filter p l) (filter p r)- | otherwise = merge (filter p l) (filter p r)---- | /O(n)/. Partition the set into two sets, one with all elements that satisfy--- the predicate and one with all elements that don't satisfy the predicate.--- See also 'split'.-partition :: (a -> Bool) -> Set a -> (Set a,Set a)-partition p0 t0 = toPair $ go p0 t0- where- go _ Tip = (Tip :*: Tip)- go p (Bin _ x l r) = case (go p l, go p r) of- ((l1 :*: l2), (r1 :*: r2))- | p x -> join x l1 r1 :*: merge l2 r2- | otherwise -> merge l1 r1 :*: join x l2 r2--{----------------------------------------------------------------------- Map-----------------------------------------------------------------------}---- | /O(n*log n)/.--- @'map' f s@ is the set obtained by applying @f@ to each element of @s@.------ It's worth noting that the size of the result may be smaller if,--- for some @(x,y)@, @x \/= y && f x == f y@--map :: Ord b => (a->b) -> Set a -> Set b-map f = fromList . List.map f . toList-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE map #-}-#endif---- | /O(n)/. The------ @'mapMonotonic' f s == 'map' f s@, but works only when @f@ is monotonic.--- /The precondition is not checked./--- Semi-formally, we have:------ > and [x < y ==> f x < f y | x <- ls, y <- ls]--- > ==> mapMonotonic f s == map f s--- > where ls = toList s--mapMonotonic :: (a->b) -> Set a -> Set b-mapMonotonic _ Tip = Tip-mapMonotonic f (Bin sz x l r) = Bin sz (f x) (mapMonotonic f l) (mapMonotonic f r)--{--------------------------------------------------------------------- Fold---------------------------------------------------------------------}--- | /O(n)/. Fold the elements in the set using the given right-associative--- binary operator. This function is an equivalent of 'foldr' and is present--- for compatibility only.------ /Please note that fold will be deprecated in the future and removed./-fold :: (a -> b -> b) -> b -> Set a -> b-fold = foldr-{-# INLINE fold #-}---- | /O(n)/. Fold the elements in the set using the given right-associative--- binary operator, such that @'foldr' f z == 'Prelude.foldr' f z . 'toAscList'@.------ For example,------ > toAscList set = foldr (:) [] set-foldr :: (a -> b -> b) -> b -> Set a -> b-foldr f z = go z- where- go z' Tip = z'- go z' (Bin _ x l r) = go (f x (go z' r)) l-{-# INLINE foldr #-}---- | /O(n)/. A strict version of 'foldr'. Each application of the operator is--- evaluated before using the result in the next application. This--- function is strict in the starting value.-foldr' :: (a -> b -> b) -> b -> Set a -> b-foldr' f z = go z- where- STRICT_1_OF_2(go)- go z' Tip = z'- go z' (Bin _ x l r) = go (f x (go z' r)) l-{-# INLINE foldr' #-}---- | /O(n)/. Fold the elements in the set using the given left-associative--- binary operator, such that @'foldl' f z == 'Prelude.foldl' f z . 'toAscList'@.------ For example,------ > toDescList set = foldl (flip (:)) [] set-foldl :: (a -> b -> a) -> a -> Set b -> a-foldl f z = go z- where- go z' Tip = z'- go z' (Bin _ x l r) = go (f (go z' l) x) r-{-# INLINE foldl #-}---- | /O(n)/. A strict version of 'foldl'. Each application of the operator is--- evaluated before using the result in the next application. This--- function is strict in the starting value.-foldl' :: (a -> b -> a) -> a -> Set b -> a-foldl' f z = go z- where- STRICT_1_OF_2(go)- go z' Tip = z'- go z' (Bin _ x l r) = go (f (go z' l) x) r-{-# INLINE foldl' #-}--{--------------------------------------------------------------------- List variations---------------------------------------------------------------------}--- | /O(n)/. An alias of 'toAscList'. The elements of a set in ascending order.--- Subject to list fusion.-elems :: Set a -> [a]-elems = toAscList--{--------------------------------------------------------------------- Lists---------------------------------------------------------------------}--- | /O(n)/. Convert the set to a list of elements. Subject to list fusion.-toList :: Set a -> [a]-toList = toAscList---- | /O(n)/. Convert the set to an ascending list of elements. Subject to list fusion.-toAscList :: Set a -> [a]-toAscList = foldr (:) []---- | /O(n)/. Convert the set to a descending list of elements. Subject to list--- fusion.-toDescList :: Set a -> [a]-toDescList = foldl (flip (:)) []---- List fusion for the list generating functions.-#if __GLASGOW_HASKELL__--- The foldrFB and foldlFB are foldr and foldl equivalents, used for list fusion.--- They are important to convert unfused to{Asc,Desc}List back, see mapFB in prelude.-foldrFB :: (a -> b -> b) -> b -> Set a -> b-foldrFB = foldr-{-# INLINE[0] foldrFB #-}-foldlFB :: (a -> b -> a) -> a -> Set b -> a-foldlFB = foldl-{-# INLINE[0] foldlFB #-}---- Inline elems and toList, so that we need to fuse only toAscList.-{-# INLINE elems #-}-{-# INLINE toList #-}---- The fusion is enabled up to phase 2 included. If it does not succeed,--- convert in phase 1 the expanded to{Asc,Desc}List calls back to--- to{Asc,Desc}List. In phase 0, we inline fold{lr}FB (which were used in--- a list fusion, otherwise it would go away in phase 1), and let compiler do--- whatever it wants with to{Asc,Desc}List -- it was forbidden to inline it--- before phase 0, otherwise the fusion rules would not fire at all.-{-# NOINLINE[0] toAscList #-}-{-# NOINLINE[0] toDescList #-}-{-# RULES "Set.toAscList" [~1] forall s . toAscList s = build (\c n -> foldrFB c n s) #-}-{-# RULES "Set.toAscListBack" [1] foldrFB (:) [] = toAscList #-}-{-# RULES "Set.toDescList" [~1] forall s . toDescList s = build (\c n -> foldlFB (\xs x -> c x xs) n s) #-}-{-# RULES "Set.toDescListBack" [1] foldlFB (\xs x -> x : xs) [] = toDescList #-}-#endif---- | /O(n*log n)/. Create a set from a list of elements.------ If the elemens are ordered, linear-time implementation is used,--- with the performance equal to 'fromDistinctAscList'.---- For some reason, when 'singleton' is used in fromList or in--- create, it is not inlined, so we inline it manually.-fromList :: Ord a => [a] -> Set a-fromList [] = Tip-fromList [x] = Bin 1 x Tip Tip-fromList (x0 : xs0) | not_ordered x0 xs0 = fromList' (Bin 1 x0 Tip Tip) xs0- | otherwise = go (1::Int) (Bin 1 x0 Tip Tip) xs0- where- not_ordered _ [] = False- not_ordered x (y : _) = x >= y- {-# INLINE not_ordered #-}-- fromList' t0 xs = foldlStrict ins t0 xs- where ins t x = insert x t-- STRICT_1_OF_3(go)- go _ t [] = t- go _ t [x] = insertMax x t- go s l xs@(x : xss) | not_ordered x xss = fromList' l xs- | otherwise = case create s xss of- (r, ys, []) -> go (s `shiftL` 1) (join x l r) ys- (r, _, ys) -> fromList' (join x l r) ys-- -- The create is returning a triple (tree, xs, ys). Both xs and ys- -- represent not yet processed elements and only one of them can be nonempty.- -- If ys is nonempty, the keys in ys are not ordered with respect to tree- -- and must be inserted using fromList'. Otherwise the keys have been- -- ordered so far.- STRICT_1_OF_2(create)- create _ [] = (Tip, [], [])- create s xs@(x : xss)- | s == 1 = if not_ordered x xss then (Bin 1 x Tip Tip, [], xss)- else (Bin 1 x Tip Tip, xss, [])- | otherwise = case create (s `shiftR` 1) xs of- res@(_, [], _) -> res- (l, [y], zs) -> (insertMax y l, [], zs)- (l, ys@(y:yss), _) | not_ordered y yss -> (l, [], ys)- | otherwise -> case create (s `shiftR` 1) yss of- (r, zs, ws) -> (join y l r, zs, ws)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE fromList #-}-#endif--{--------------------------------------------------------------------- Building trees from ascending/descending lists can be done in linear time.-- Note that if [xs] is ascending that:- fromAscList xs == fromList xs---------------------------------------------------------------------}--- | /O(n)/. Build a set from an ascending list in linear time.--- /The precondition (input list is ascending) is not checked./-fromAscList :: Eq a => [a] -> Set a-fromAscList xs- = fromDistinctAscList (combineEq xs)- where- -- [combineEq xs] combines equal elements with [const] in an ordered list [xs]- combineEq xs'- = case xs' of- [] -> []- [x] -> [x]- (x:xx) -> combineEq' x xx-- combineEq' z [] = [z]- combineEq' z (x:xs')- | z==x = combineEq' z xs'- | otherwise = z:combineEq' x xs'-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE fromAscList #-}-#endif----- | /O(n)/. Build a set from an ascending list of distinct elements in linear time.--- /The precondition (input list is strictly ascending) is not checked./---- For some reason, when 'singleton' is used in fromDistinctAscList or in--- create, it is not inlined, so we inline it manually.-fromDistinctAscList :: [a] -> Set a-fromDistinctAscList [] = Tip-fromDistinctAscList (x0 : xs0) = go (1::Int) (Bin 1 x0 Tip Tip) xs0- where- STRICT_1_OF_3(go)- go _ t [] = t- go s l (x : xs) = case create s xs of- (r, ys) -> go (s `shiftL` 1) (join x l r) ys-- STRICT_1_OF_2(create)- create _ [] = (Tip, [])- create s xs@(x : xs')- | s == 1 = (Bin 1 x Tip Tip, xs')- | otherwise = case create (s `shiftR` 1) xs of- res@(_, []) -> res- (l, y:ys) -> case create (s `shiftR` 1) ys of- (r, zs) -> (join y l r, zs)--{--------------------------------------------------------------------- Eq converts the set to a list. In a lazy setting, this- actually seems one of the faster methods to compare two trees- and it is certainly the simplest :-)---------------------------------------------------------------------}-instance Eq a => Eq (Set a) where- t1 == t2 = (size t1 == size t2) && (toAscList t1 == toAscList t2)--{--------------------------------------------------------------------- Ord---------------------------------------------------------------------}--instance Ord a => Ord (Set a) where- compare s1 s2 = compare (toAscList s1) (toAscList s2)--{--------------------------------------------------------------------- Show---------------------------------------------------------------------}-instance Show a => Show (Set a) where- showsPrec p xs = showParen (p > 10) $- showString "fromList " . shows (toList xs)--{--------------------------------------------------------------------- Read---------------------------------------------------------------------}-instance (Read a, Ord a) => Read (Set a) where-#ifdef __GLASGOW_HASKELL__- readPrec = parens $ prec 10 $ do- Ident "fromList" <- lexP- xs <- readPrec- return (fromList xs)-- readListPrec = readListPrecDefault-#else- readsPrec p = readParen (p > 10) $ \ r -> do- ("fromList",s) <- lex r- (xs,t) <- reads s- return (fromList xs,t)-#endif--{--------------------------------------------------------------------- Typeable/Data---------------------------------------------------------------------}--#include "Typeable.h"-INSTANCE_TYPEABLE1(Set,setTc,"Set")--{--------------------------------------------------------------------- NFData---------------------------------------------------------------------}--instance NFData a => NFData (Set a) where- rnf Tip = ()- rnf (Bin _ y l r) = rnf y `seq` rnf l `seq` rnf r--{--------------------------------------------------------------------- Utility functions that return sub-ranges of the original- tree. Some functions take a `Maybe value` as an argument to- allow comparisons against infinite values. These are called `blow`- (Nothing is -\infty) and `bhigh` (here Nothing is +\infty).- We use MaybeS value, which is a Maybe strict in the Just case.-- [trim blow bhigh t] A tree that is either empty or where [x > blow]- and [x < bhigh] for the value [x] of the root.- [filterGt blow t] A tree where for all values [k]. [k > blow]- [filterLt bhigh t] A tree where for all values [k]. [k < bhigh]-- [split k t] Returns two trees [l] and [r] where all values- in [l] are <[k] and all keys in [r] are >[k].- [splitMember k t] Just like [split] but also returns whether [k]- was found in the tree.---------------------------------------------------------------------}--data MaybeS a = NothingS | JustS !a--{--------------------------------------------------------------------- [trim blo bhi t] trims away all subtrees that surely contain no- values between the range [blo] to [bhi]. The returned tree is either- empty or the key of the root is between @blo@ and @bhi@.---------------------------------------------------------------------}-trim :: Ord a => MaybeS a -> MaybeS a -> Set a -> Set a-trim NothingS NothingS t = t-trim (JustS lx) NothingS t = greater lx t where greater lo (Bin _ x _ r) | x <= lo = greater lo r- greater _ t' = t'-trim NothingS (JustS hx) t = lesser hx t where lesser hi (Bin _ x l _) | x >= hi = lesser hi l- lesser _ t' = t'-trim (JustS lx) (JustS hx) t = middle lx hx t where middle lo hi (Bin _ x _ r) | x <= lo = middle lo hi r- middle lo hi (Bin _ x l _) | x >= hi = middle lo hi l- middle _ _ t' = t'-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE trim #-}-#endif--{--------------------------------------------------------------------- [filterGt b t] filter all values >[b] from tree [t]- [filterLt b t] filter all values <[b] from tree [t]---------------------------------------------------------------------}-filterGt :: Ord a => MaybeS a -> Set a -> Set a-filterGt NothingS t = t-filterGt (JustS b) t = filter' b t- where filter' _ Tip = Tip- filter' b' (Bin _ x l r) =- case compare b' x of LT -> join x (filter' b' l) r- EQ -> r- GT -> filter' b' r-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE filterGt #-}-#endif--filterLt :: Ord a => MaybeS a -> Set a -> Set a-filterLt NothingS t = t-filterLt (JustS b) t = filter' b t- where filter' _ Tip = Tip- filter' b' (Bin _ x l r) =- case compare x b' of LT -> join x l (filter' b' r)- EQ -> l- GT -> filter' b' l-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE filterLt #-}-#endif--{--------------------------------------------------------------------- Split---------------------------------------------------------------------}--- | /O(log n)/. The expression (@'split' x set@) is a pair @(set1,set2)@--- where @set1@ comprises the elements of @set@ less than @x@ and @set2@--- comprises the elements of @set@ greater than @x@.-split :: Ord a => a -> Set a -> (Set a,Set a)-split x0 t0 = toPair $ go x0 t0- where- go _ Tip = (Tip :*: Tip)- go x (Bin _ y l r)- = case compare x y of- LT -> let (lt :*: gt) = go x l in (lt :*: join y gt r)- GT -> let (lt :*: gt) = go x r in (join y l lt :*: gt)- EQ -> (l :*: r)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE split #-}-#endif---- | /O(log n)/. Performs a 'split' but also returns whether the pivot--- element was found in the original set.-splitMember :: Ord a => a -> Set a -> (Set a,Bool,Set a)-splitMember _ Tip = (Tip, False, Tip)-splitMember x (Bin _ y l r)- = case compare x y of- LT -> let (lt, found, gt) = splitMember x l- gt' = join y gt r- in gt' `seq` (lt, found, gt')- GT -> let (lt, found, gt) = splitMember x r- lt' = join y l lt- in lt' `seq` (lt', found, gt)- EQ -> (l, True, r)-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE splitMember #-}-#endif--{--------------------------------------------------------------------- Indexing---------------------------------------------------------------------}---- | /O(log n)/. Return the /index/ of an element, which is its zero-based--- index in the sorted sequence of elements. The index is a number from /0/ up--- to, but not including, the 'size' of the set. Calls 'error' when the element--- is not a 'member' of the set.------ > findIndex 2 (fromList [5,3]) Error: element is not in the set--- > findIndex 3 (fromList [5,3]) == 0--- > findIndex 5 (fromList [5,3]) == 1--- > findIndex 6 (fromList [5,3]) Error: element is not in the set---- See Note: Type of local 'go' function-findIndex :: Ord a => a -> Set a -> Int-findIndex = go 0- where- go :: Ord a => Int -> a -> Set a -> Int- STRICT_1_OF_3(go)- STRICT_2_OF_3(go)- go _ _ Tip = error "Set.findIndex: element is not in the set"- go idx x (Bin _ kx l r) = case compare x kx of- LT -> go idx x l- GT -> go (idx + size l + 1) x r- EQ -> idx + size l-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE findIndex #-}-#endif---- | /O(log n)/. Lookup the /index/ of an element, which is its zero-based index in--- the sorted sequence of elements. The index is a number from /0/ up to, but not--- including, the 'size' of the set.------ > isJust (lookupIndex 2 (fromList [5,3])) == False--- > fromJust (lookupIndex 3 (fromList [5,3])) == 0--- > fromJust (lookupIndex 5 (fromList [5,3])) == 1--- > isJust (lookupIndex 6 (fromList [5,3])) == False---- See Note: Type of local 'go' function-lookupIndex :: Ord a => a -> Set a -> Maybe Int-lookupIndex = go 0- where- go :: Ord a => Int -> a -> Set a -> Maybe Int- STRICT_1_OF_3(go)- STRICT_2_OF_3(go)- go _ _ Tip = Nothing- go idx x (Bin _ kx l r) = case compare x kx of- LT -> go idx x l- GT -> go (idx + size l + 1) x r- EQ -> Just $! idx + size l-#if __GLASGOW_HASKELL__ >= 700-{-# INLINABLE lookupIndex #-}-#endif---- | /O(log n)/. Retrieve an element by its /index/, i.e. by its zero-based--- index in the sorted sequence of elements. If the /index/ is out of range (less--- than zero, greater or equal to 'size' of the set), 'error' is called.------ > elemAt 0 (fromList [5,3]) == 3--- > elemAt 1 (fromList [5,3]) == 5--- > elemAt 2 (fromList [5,3]) Error: index out of range--elemAt :: Int -> Set a -> a-STRICT_1_OF_2(elemAt)-elemAt _ Tip = error "Set.elemAt: index out of range"-elemAt i (Bin _ x l r)- = case compare i sizeL of- LT -> elemAt i l- GT -> elemAt (i-sizeL-1) r- EQ -> x- where- sizeL = size l---- | /O(log n)/. Delete the element at /index/, i.e. by its zero-based index in--- the sorted sequence of elements. If the /index/ is out of range (less than zero,--- greater or equal to 'size' of the set), 'error' is called.------ > deleteAt 0 (fromList [5,3]) == singleton 5--- > deleteAt 1 (fromList [5,3]) == singleton 3--- > deleteAt 2 (fromList [5,3]) Error: index out of range--- > deleteAt (-1) (fromList [5,3]) Error: index out of range--deleteAt :: Int -> Set a -> Set a-deleteAt i t = i `seq`- case t of- Tip -> error "Set.deleteAt: index out of range"- Bin _ x l r -> case compare i sizeL of- LT -> balanceR x (deleteAt i l) r- GT -> balanceL x l (deleteAt (i-sizeL-1) r)- EQ -> glue l r- where- sizeL = size l---{--------------------------------------------------------------------- Utility functions that maintain the balance properties of the tree.- All constructors assume that all values in [l] < [x] and all values- in [r] > [x], and that [l] and [r] are valid trees.-- In order of sophistication:- [Bin sz x l r] The type constructor.- [bin x l r] Maintains the correct size, assumes that both [l]- and [r] are balanced with respect to each other.- [balance x l r] Restores the balance and size.- Assumes that the original tree was balanced and- that [l] or [r] has changed by at most one element.- [join x l r] Restores balance and size.-- Furthermore, we can construct a new tree from two trees. Both operations- assume that all values in [l] < all values in [r] and that [l] and [r]- are valid:- [glue l r] Glues [l] and [r] together. Assumes that [l] and- [r] are already balanced with respect to each other.- [merge l r] Merges two trees and restores balance.-- Note: in contrast to Adam's paper, we use (<=) comparisons instead- of (<) comparisons in [join], [merge] and [balance].- Quickcheck (on [difference]) showed that this was necessary in order- to maintain the invariants. It is quite unsatisfactory that I haven't- been able to find out why this is actually the case! Fortunately, it- doesn't hurt to be a bit more conservative.---------------------------------------------------------------------}--{--------------------------------------------------------------------- Join---------------------------------------------------------------------}-join :: a -> Set a -> Set a -> Set a-join x Tip r = insertMin x r-join x l Tip = insertMax x l-join x l@(Bin sizeL y ly ry) r@(Bin sizeR z lz rz)- | delta*sizeL < sizeR = balanceL z (join x l lz) rz- | delta*sizeR < sizeL = balanceR y ly (join x ry r)- | otherwise = bin x l r----- insertMin and insertMax don't perform potentially expensive comparisons.-insertMax,insertMin :: a -> Set a -> Set a-insertMax x t- = case t of- Tip -> singleton x- Bin _ y l r- -> balanceR y l (insertMax x r)--insertMin x t- = case t of- Tip -> singleton x- Bin _ y l r- -> balanceL y (insertMin x l) r--{--------------------------------------------------------------------- [merge l r]: merges two trees.---------------------------------------------------------------------}-merge :: Set a -> Set a -> Set a-merge Tip r = r-merge l Tip = l-merge l@(Bin sizeL x lx rx) r@(Bin sizeR y ly ry)- | delta*sizeL < sizeR = balanceL y (merge l ly) ry- | delta*sizeR < sizeL = balanceR x lx (merge rx r)- | otherwise = glue l r--{--------------------------------------------------------------------- [glue l r]: glues two trees together.- Assumes that [l] and [r] are already balanced with respect to each other.---------------------------------------------------------------------}-glue :: Set a -> Set a -> Set a-glue Tip r = r-glue l Tip = l-glue l r- | size l > size r = let (m,l') = deleteFindMax l in balanceR m l' r- | otherwise = let (m,r') = deleteFindMin r in balanceL m l r'---- | /O(log n)/. Delete and find the minimal element.------ > deleteFindMin set = (findMin set, deleteMin set)--deleteFindMin :: Set a -> (a,Set a)-deleteFindMin t- = case t of- Bin _ x Tip r -> (x,r)- Bin _ x l r -> let (xm,l') = deleteFindMin l in (xm,balanceR x l' r)- Tip -> (error "Set.deleteFindMin: can not return the minimal element of an empty set", Tip)---- | /O(log n)/. Delete and find the maximal element.------ > deleteFindMax set = (findMax set, deleteMax set)-deleteFindMax :: Set a -> (a,Set a)-deleteFindMax t- = case t of- Bin _ x l Tip -> (x,l)- Bin _ x l r -> let (xm,r') = deleteFindMax r in (xm,balanceL x l r')- Tip -> (error "Set.deleteFindMax: can not return the maximal element of an empty set", Tip)---- | /O(log n)/. Retrieves the minimal key of the set, and the set--- stripped of that element, or 'Nothing' if passed an empty set.-minView :: Set a -> Maybe (a, Set a)-minView Tip = Nothing-minView x = Just (deleteFindMin x)---- | /O(log n)/. Retrieves the maximal key of the set, and the set--- stripped of that element, or 'Nothing' if passed an empty set.-maxView :: Set a -> Maybe (a, Set a)-maxView Tip = Nothing-maxView x = Just (deleteFindMax x)--{--------------------------------------------------------------------- [balance x l r] balances two trees with value x.- The sizes of the trees should balance after decreasing the- size of one of them. (a rotation).-- [delta] is the maximal relative difference between the sizes of- two trees, it corresponds with the [w] in Adams' paper.- [ratio] is the ratio between an outer and inner sibling of the- heavier subtree in an unbalanced setting. It determines- whether a double or single rotation should be performed- to restore balance. It is correspondes with the inverse- of $\alpha$ in Adam's article.-- Note that according to the Adam's paper:- - [delta] should be larger than 4.646 with a [ratio] of 2.- - [delta] should be larger than 3.745 with a [ratio] of 1.534.-- But the Adam's paper is errorneous:- - it can be proved that for delta=2 and delta>=5 there does- not exist any ratio that would work- - delta=4.5 and ratio=2 does not work-- That leaves two reasonable variants, delta=3 and delta=4,- both with ratio=2.-- - A lower [delta] leads to a more 'perfectly' balanced tree.- - A higher [delta] performs less rebalancing.-- In the benchmarks, delta=3 is faster on insert operations,- and delta=4 has slightly better deletes. As the insert speedup- is larger, we currently use delta=3.----------------------------------------------------------------------}-delta,ratio :: Int-delta = 3-ratio = 2---- The balance function is equivalent to the following:------ balance :: a -> Set a -> Set a -> Set a--- balance x l r--- | sizeL + sizeR <= 1 = Bin sizeX x l r--- | sizeR > delta*sizeL = rotateL x l r--- | sizeL > delta*sizeR = rotateR x l r--- | otherwise = Bin sizeX x l r--- where--- sizeL = size l--- sizeR = size r--- sizeX = sizeL + sizeR + 1------ rotateL :: a -> Set a -> Set a -> Set a--- rotateL x l r@(Bin _ _ ly ry) | size ly < ratio*size ry = singleL x l r--- | otherwise = doubleL x l r--- rotateR :: a -> Set a -> Set a -> Set a--- rotateR x l@(Bin _ _ ly ry) r | size ry < ratio*size ly = singleR x l r--- | otherwise = doubleR x l r------ singleL, singleR :: a -> Set a -> Set a -> Set a--- singleL x1 t1 (Bin _ x2 t2 t3) = bin x2 (bin x1 t1 t2) t3--- singleR x1 (Bin _ x2 t1 t2) t3 = bin x2 t1 (bin x1 t2 t3)------ doubleL, doubleR :: a -> Set a -> Set a -> Set a--- doubleL x1 t1 (Bin _ x2 (Bin _ x3 t2 t3) t4) = bin x3 (bin x1 t1 t2) (bin x2 t3 t4)--- doubleR x1 (Bin _ x2 t1 (Bin _ x3 t2 t3)) t4 = bin x3 (bin x2 t1 t2) (bin x1 t3 t4)------ It is only written in such a way that every node is pattern-matched only once.------ Only balanceL and balanceR are needed at the moment, so balance is not here anymore.--- In case it is needed, it can be found in Data.Map.---- Functions balanceL and balanceR are specialised versions of balance.--- balanceL only checks whether the left subtree is too big,--- balanceR only checks whether the right subtree is too big.---- balanceL is called when left subtree might have been inserted to or when--- right subtree might have been deleted from.-balanceL :: a -> Set a -> Set a -> Set a-balanceL x l r = case r of- Tip -> case l of- Tip -> Bin 1 x Tip Tip- (Bin _ _ Tip Tip) -> Bin 2 x l Tip- (Bin _ lx Tip (Bin _ lrx _ _)) -> Bin 3 lrx (Bin 1 lx Tip Tip) (Bin 1 x Tip Tip)- (Bin _ lx ll@(Bin _ _ _ _) Tip) -> Bin 3 lx ll (Bin 1 x Tip Tip)- (Bin ls lx ll@(Bin lls _ _ _) lr@(Bin lrs lrx lrl lrr))- | lrs < ratio*lls -> Bin (1+ls) lx ll (Bin (1+lrs) x lr Tip)- | otherwise -> Bin (1+ls) lrx (Bin (1+lls+size lrl) lx ll lrl) (Bin (1+size lrr) x lrr Tip)-- (Bin rs _ _ _) -> case l of- Tip -> Bin (1+rs) x Tip r-- (Bin ls lx ll lr)- | ls > delta*rs -> case (ll, lr) of- (Bin lls _ _ _, Bin lrs lrx lrl lrr)- | lrs < ratio*lls -> Bin (1+ls+rs) lx ll (Bin (1+rs+lrs) x lr r)- | otherwise -> Bin (1+ls+rs) lrx (Bin (1+lls+size lrl) lx ll lrl) (Bin (1+rs+size lrr) x lrr r)- (_, _) -> error "Failure in Data.Map.balanceL"- | otherwise -> Bin (1+ls+rs) x l r-{-# NOINLINE balanceL #-}---- balanceR is called when right subtree might have been inserted to or when--- left subtree might have been deleted from.-balanceR :: a -> Set a -> Set a -> Set a-balanceR x l r = case l of- Tip -> case r of- Tip -> Bin 1 x Tip Tip- (Bin _ _ Tip Tip) -> Bin 2 x Tip r- (Bin _ rx Tip rr@(Bin _ _ _ _)) -> Bin 3 rx (Bin 1 x Tip Tip) rr- (Bin _ rx (Bin _ rlx _ _) Tip) -> Bin 3 rlx (Bin 1 x Tip Tip) (Bin 1 rx Tip Tip)- (Bin rs rx rl@(Bin rls rlx rll rlr) rr@(Bin rrs _ _ _))- | rls < ratio*rrs -> Bin (1+rs) rx (Bin (1+rls) x Tip rl) rr- | otherwise -> Bin (1+rs) rlx (Bin (1+size rll) x Tip rll) (Bin (1+rrs+size rlr) rx rlr rr)-- (Bin ls _ _ _) -> case r of- Tip -> Bin (1+ls) x l Tip-- (Bin rs rx rl rr)- | rs > delta*ls -> case (rl, rr) of- (Bin rls rlx rll rlr, Bin rrs _ _ _)- | rls < ratio*rrs -> Bin (1+ls+rs) rx (Bin (1+ls+rls) x l rl) rr- | otherwise -> Bin (1+ls+rs) rlx (Bin (1+ls+size rll) x l rll) (Bin (1+rrs+size rlr) rx rlr rr)- (_, _) -> error "Failure in Data.Map.balanceR"- | otherwise -> Bin (1+ls+rs) x l r-{-# NOINLINE balanceR #-}--{--------------------------------------------------------------------- The bin constructor maintains the size of the tree---------------------------------------------------------------------}-bin :: a -> Set a -> Set a -> Set a-bin x l r- = Bin (size l + size r + 1) x l r-{-# INLINE bin #-}---{--------------------------------------------------------------------- Utilities---------------------------------------------------------------------}-foldlStrict :: (a -> b -> a) -> a -> [b] -> a-foldlStrict f = go- where- go z [] = z- go z (x:xs) = let z' = f z x in z' `seq` go z' xs-{-# INLINE foldlStrict #-}--{--------------------------------------------------------------------- Debugging---------------------------------------------------------------------}--- | /O(n)/. Show the tree that implements the set. The tree is shown--- in a compressed, hanging format.-showTree :: Show a => Set a -> String-showTree s- = showTreeWith True False s---{- | /O(n)/. The expression (@showTreeWith hang wide map@) shows- the tree that implements the set. If @hang@ is- @True@, a /hanging/ tree is shown otherwise a rotated tree is shown. If- @wide@ is 'True', an extra wide version is shown.--> Set> putStrLn $ showTreeWith True False $ fromDistinctAscList [1..5]-> 4-> +--2-> | +--1-> | +--3-> +--5->-> Set> putStrLn $ showTreeWith True True $ fromDistinctAscList [1..5]-> 4-> |-> +--2-> | |-> | +--1-> | |-> | +--3-> |-> +--5->-> Set> putStrLn $ showTreeWith False True $ fromDistinctAscList [1..5]-> +--5-> |-> 4-> |-> | +--3-> | |-> +--2-> |-> +--1---}-showTreeWith :: Show a => Bool -> Bool -> Set a -> String-showTreeWith hang wide t- | hang = (showsTreeHang wide [] t) ""- | otherwise = (showsTree wide [] [] t) ""--showsTree :: Show a => Bool -> [String] -> [String] -> Set a -> ShowS-showsTree wide lbars rbars t- = case t of- Tip -> showsBars lbars . showString "|\n"- Bin _ x Tip Tip- -> showsBars lbars . shows x . showString "\n"- Bin _ x l r- -> showsTree wide (withBar rbars) (withEmpty rbars) r .- showWide wide rbars .- showsBars lbars . shows x . showString "\n" .- showWide wide lbars .- showsTree wide (withEmpty lbars) (withBar lbars) l--showsTreeHang :: Show a => Bool -> [String] -> Set a -> ShowS-showsTreeHang wide bars t- = case t of- Tip -> showsBars bars . showString "|\n"- Bin _ x Tip Tip- -> showsBars bars . shows x . showString "\n"- Bin _ x l r- -> showsBars bars . shows x . showString "\n" .- showWide wide bars .- showsTreeHang wide (withBar bars) l .- showWide wide bars .- showsTreeHang wide (withEmpty bars) r--showWide :: Bool -> [String] -> String -> String-showWide wide bars- | wide = showString (concat (reverse bars)) . showString "|\n"- | otherwise = id--showsBars :: [String] -> ShowS-showsBars bars- = case bars of- [] -> id- _ -> showString (concat (reverse (tail bars))) . showString node--node :: String-node = "+--"--withBar, withEmpty :: [String] -> [String]-withBar bars = "| ":bars-withEmpty bars = " ":bars--{--------------------------------------------------------------------- Assertions---------------------------------------------------------------------}--- | /O(n)/. Test if the internal set structure is valid.-valid :: Ord a => Set a -> Bool-valid t- = balanced t && ordered t && validsize t--ordered :: Ord a => Set a -> Bool-ordered t- = bounded (const True) (const True) t- where- bounded lo hi t'- = case t' of- Tip -> True- Bin _ x l r -> (lo x) && (hi x) && bounded lo (<x) l && bounded (>x) hi r--balanced :: Set a -> Bool-balanced t- = case t of- Tip -> True- Bin _ _ l r -> (size l + size r <= 1 || (size l <= delta*size r && size r <= delta*size l)) &&- balanced l && balanced r--validsize :: Set a -> Bool-validsize t- = (realsize t == Just (size t))- where- realsize t'- = case t' of- Tip -> Just 0- Bin sz _ l r -> case (realsize l,realsize r) of- (Just n,Just m) | n+m+1 == sz -> Just sz- _ -> Nothing
− containers-0.5.2.1/Darcs/Data/StrictPair.hs
@@ -1,13 +0,0 @@-{-# LANGUAGE CPP #-}-#if !defined(TESTING) && __GLASGOW_HASKELL__ >= 703-{-# LANGUAGE Trustworthy #-}-#endif-module Darcs.Data.StrictPair (StrictPair(..), toPair) where---- | Same as regular Haskell pairs, but (x :*: _|_) = (_|_ :*: y) =--- _|_-data StrictPair a b = !a :*: !b--toPair :: StrictPair a b -> (a, b)-toPair (x :*: y) = (x, y)-{-# INLINE toPair #-}
contrib/darcs-errors.hlint view
@@ -1,28 +1,3 @@--- Only report errors, since we use this as part of the testsuite. It needs to--- be easy to see what tripped up the testcase.--ignore "Eta reduce" = ""-ignore "Use camelCase" = ""-ignore "Use const" = ""-ignore "Use on" = ""-ignore "Use foldr" = ""-ignore "Use String" = ""-ignore "Use string literal" = ""-ignore "Use guards" = ""-ignore "Use :" = ""-ignore "Redundant brackets" = ""-ignore "Redundant do" = ""-ignore "Redundant return" = ""-ignore "Redundant $" = ""-ignore "Redundant lambda" = ""--ignore "Use fewer imports" = ""-ignore "Use better pragmas" = ""-ignore "Use let" = ""-ignore "Operator rotate" = ""-ignore "Use foldl" = ""-ignore "Unused LANGUAGE pragma" = ""- -- The problem with Prelude readFile is that it's based on hGetContents, which -- is lazy by definition. This also means that unless you force consumption of -- the produced list, it will keep an fd open for the file, possibly
+ contrib/runHLint.sh view
@@ -0,0 +1,12 @@+#!/bin/bash++hlint --hint=contrib/darcs-errors.hlint --cpp-simple ./src+hlint --cpp-simple ./src++# --cpp-simple is to avoid issues with MIN_VERSION+# see https://github.com/ndmitchell/hlint/issues/53+# using it creates other problems since #if are ignored..++# When the above bug is solved we can use:+# hlint --hint=contrib/darcs-errors.hlint --cpp-include=./src ./src+# hlint --cpp-include=./src ./src
darcs.cabal view
@@ -1,5 +1,5 @@ Name: darcs-version: 2.10.3+version: 2.12.0 License: GPL License-file: COPYING Author: David Roundy <droundy@darcs.net>, <darcs-devel@darcs.net>@@ -33,7 +33,7 @@ Homepage: http://darcs.net/ Build-Type: Custom-Cabal-Version: >= 1.10+Cabal-Version: >= 1.16 extra-source-files: -- C headers@@ -44,6 +44,7 @@ contrib/_darcs.zsh, contrib/darcs_completion, contrib/cygwin-wrapper.bash, contrib/update_roundup.pl, contrib/upload.cgi, contrib/darcs-errors.hlint,+ contrib/runHLint.sh, README.md, CHANGELOG @@ -61,12 +62,9 @@ tests/network/*.sh tests/lib tests/data/example_binary.png+ harness/hstestdata.zip tests/README.test_maintainers.txt - -- hashed-storage- hashed-storage/LICENSE- hashed-storage/testdata.zip- GNUmakefile source-repository head@@ -100,9 +98,6 @@ description: Build darcs executable default: True -flag hpc- default: False- flag rts default: False @@ -116,13 +111,6 @@ manual: True description: Build with warnings-as-errors --- To allow building with containers < 0.5, we keep a local copy of this--- module.-flag use-local-data-map-strict- default: False- description: Support containers < 0.5, by using a local copy of the- Data.Map.Strict module from containers 0.5.- -- Note that the Setup script checks whether -liconv is necessary. This flag -- lets us override that decision. When it is True, we use -liconv. When it -- is False, we run tests to decide.@@ -130,9 +118,6 @@ Description: Explicitly link against the libiconv library. Default: False -flag hashed-storage-diff- default: False- -- with time>=1.5 (needed with GHC 7.10) we get defaultTimeLocale from time:Data.Time -- with time<1.5 we get defaultTimeLocale from old-locale:System.Locale flag use-time-1point5@@ -165,7 +150,6 @@ Darcs.Patch.Commute Darcs.Patch.CommuteFn Darcs.Patch.Conflict- Darcs.Patch.ConflictMarking Darcs.Patch.Debug Darcs.Patch.Depends Darcs.Patch.Dummy@@ -179,10 +163,10 @@ Darcs.Patch.Invert Darcs.Patch.Match Darcs.Patch.Matchable- Darcs.Patch.MaybeInternal Darcs.Patch.Merge Darcs.Patch.MonadProgress Darcs.Patch.Named+ Darcs.Patch.Named.Wrapped Darcs.Patch.OldDate Darcs.Patch.PatchInfoAnd Darcs.Patch.Patchy@@ -198,27 +182,28 @@ Darcs.Patch.Prim.V1.Details Darcs.Patch.Prim.V1.Read Darcs.Patch.Prim.V1.Show- Darcs.Patch.Prim.V3- Darcs.Patch.Prim.V3.ObjectMap- Darcs.Patch.Prim.V3.Apply- Darcs.Patch.Prim.V3.Coalesce- Darcs.Patch.Prim.V3.Commute- Darcs.Patch.Prim.V3.Core- Darcs.Patch.Prim.V3.Details- Darcs.Patch.Prim.V3.Read- Darcs.Patch.Prim.V3.Show+ Darcs.Patch.Prim.FileUUID+ Darcs.Patch.Prim.FileUUID.ObjectMap+ Darcs.Patch.Prim.FileUUID.Apply+ Darcs.Patch.Prim.FileUUID.Coalesce+ Darcs.Patch.Prim.FileUUID.Commute+ Darcs.Patch.Prim.FileUUID.Core+ Darcs.Patch.Prim.FileUUID.Details+ Darcs.Patch.Prim.FileUUID.Read+ Darcs.Patch.Prim.FileUUID.Show Darcs.Patch.Progress Darcs.Patch.Read Darcs.Patch.Rebase+ Darcs.Patch.Rebase.Container Darcs.Patch.Rebase.Fixup+ Darcs.Patch.Rebase.Item Darcs.Patch.Rebase.Name- Darcs.Patch.Rebase.NameHack- Darcs.Patch.Rebase.Recontext Darcs.Patch.Rebase.Viewing Darcs.Patch.ReadMonads Darcs.Patch.RegChars Darcs.Patch.Repair Darcs.Patch.RepoPatch+ Darcs.Patch.RepoType Darcs.Patch.Set Darcs.Patch.Show Darcs.Patch.Split@@ -237,42 +222,39 @@ Darcs.Patch.V1.Viewing Darcs.Patch.V2 Darcs.Patch.V2.Non- Darcs.Patch.V2.Real+ Darcs.Patch.V2.RepoPatch Darcs.Patch.Witnesses.Eq Darcs.Patch.Witnesses.Ordered Darcs.Patch.Witnesses.Sealed Darcs.Patch.Witnesses.Show Darcs.Patch.Witnesses.Unsafe Darcs.Patch.Witnesses.WZipper+ Darcs.Prelude Darcs.Repository Darcs.Repository.ApplyPatches Darcs.Repository.Cache+ Darcs.Repository.Clone Darcs.Repository.PatchIndex- Darcs.Repository.Compat Darcs.Repository.Diff- Darcs.Repository.External Darcs.Repository.Flags Darcs.Repository.Format Darcs.Repository.HashedIO Darcs.Repository.HashedRepo Darcs.Repository.Internal Darcs.Repository.Job- Darcs.Repository.Lock- Darcs.Repository.LowLevel Darcs.Repository.Merge Darcs.Repository.InternalTypes Darcs.Repository.Match Darcs.Repository.Motd Darcs.Repository.Old+ Darcs.Repository.Packs+ Darcs.Repository.Pending Darcs.Repository.Prefs Darcs.Repository.Rebase Darcs.Repository.Repair- Darcs.Repository.Read Darcs.Repository.Resolution- Darcs.Repository.Ssh Darcs.Repository.State Darcs.Repository.Test- Darcs.Repository.Util Darcs.UI.ApplyPatches Darcs.UI.Commands Darcs.UI.Commands.Add@@ -306,6 +288,7 @@ Darcs.UI.Commands.ShowAuthors Darcs.UI.Commands.ShowBug Darcs.UI.Commands.ShowContents+ Darcs.UI.Commands.ShowDependencies Darcs.UI.Commands.ShowFiles Darcs.UI.Commands.ShowIndex Darcs.UI.Commands.ShowPatchIndex@@ -331,8 +314,8 @@ Darcs.UI.Options.Markdown Darcs.UI.Options.Matching Darcs.UI.Options.Util+ Darcs.UI.PatchHeader Darcs.UI.PrintPatch- Darcs.UI.RemoteApply Darcs.UI.RunCommand Darcs.UI.SelectChanges Darcs.UI.TheCommands@@ -342,6 +325,7 @@ Darcs.Util.Bug Darcs.Util.ByteString Darcs.Util.CommandLine+ Darcs.Util.Compat Darcs.Util.Crypt.SHA1 Darcs.Util.Crypt.SHA256 Darcs.Util.DateMatcher@@ -356,9 +340,13 @@ Darcs.Util.Environment Darcs.Util.Exception Darcs.Util.Exec+ Darcs.Util.External Darcs.Util.File Darcs.Util.Global+ Darcs.Util.Hash+ Darcs.Util.Index Darcs.Util.IsoDate+ Darcs.Util.Lock Darcs.Util.Path Darcs.Util.Printer Darcs.Util.Printer.Color@@ -369,8 +357,13 @@ Darcs.Util.SignalHandler Darcs.Util.Ssh Darcs.Util.Text+ Darcs.Util.Tree+ Darcs.Util.Tree.Hashed+ Darcs.Util.Tree.Monad+ Darcs.Util.Tree.Plain Darcs.Util.URL Darcs.Util.Workaround+ Bundled.Posix other-modules: Version Darcs.Util.Download.Curl@@ -382,6 +375,7 @@ src/umask.c src/system_encoding.c + -- see http://bugs.darcs.net/issue1037 cc-options: -D_REENTRANT if os(windows)@@ -397,61 +391,48 @@ System.Posix.IO cpp-options: -DWIN32 c-sources: src/win32/send_email.c- build-depends: unix-compat >= 0.1.2 && < 0.5,- Win32 >= 2.2 && < 2.4+ build-depends: Win32 >= 2.3 && < 2.4 else other-modules: Darcs.Util.Encoding.IConv c-sources: src/h_iconv.c-- if flag(use-local-data-map-strict)- build-depends: containers >= 0.4 && < 0.5,- deepseq >= 1.3 && < 1.4- hs-source-dirs: containers-0.5.2.1- other-modules:- Darcs.Data.Map.Base- Darcs.Data.Map.Strict- Darcs.Data.Set.Base- Darcs.Data.StrictPair- cpp-options: -DUSE_LOCAL_DATA_MAP_STRICT- else- build-depends: containers >= 0.5 && < 0.6-- if os(solaris)- cc-options: -DHAVE_SIGINFO_H-- build-depends: base >= 4.5 && < 4.9+ build-depends: unix >= 2.6.0.1 && < 2.8 - build-depends: binary >= 0.5 && < 0.9,+ build-depends: base >= 4.6 && < 4.10,+ binary >= 0.5 && < 0.9,+ containers >= 0.5 && < 0.6, regex-compat-tdfa >= 0.95.1 && < 0.96, regex-applicative >= 0.2 && < 0.4,- mtl >= 2.1 && < 2.3,+ mtl >= 2.1.2 && < 2.3, transformers >= 0.3 && < 0.4.0.0 || > 0.4.0.0 && < 0.6, -- for the Control.Monad.Error -> Control.Monad.Except -- transition transformers-compat >= 0.4 && < 0.6, parsec >= 3.1 && < 3.2,- html == 1.0.*,- filepath >= 1.2.0.0 && < 1.5.0.0,+ fgl >= 5.5.0.1 && < 5.6,+ graphviz >= 2999.17.0.1 && < 2999.19,+ html >= 1.0.1.2 && < 1.1,+ filepath >= 1.3.0.1 && < 1.5.0.0, haskeline >= 0.6.3 && < 0.8, cryptohash >= 0.4 && < 0.12, base16-bytestring >= 0.1 && < 0.2,- utf8-string >= 0.3.6 && < 1.1,- vector >= 0.7 && < 0.12,+ utf8-string >= 0.3.6 && < 1.1,+ vector >= 0.10.0.1 && < 0.12, tar >= 0.4 && < 0.6, data-ordlist == 0.4.*, attoparsec >= 0.11 && < 0.14,- zip-archive >= 0.2.3 && < 0.3-- if !os(windows)- build-depends: unix >= 2.5 && < 2.8-- build-depends: bytestring >= 0.9.0 && < 0.11,- old-time >= 1.1 && < 1.2,- directory >= 1.1.0.2 && < 1.3.0.0,- process >= 1.1.0.1 && < 1.5.0.0,- array >= 0.4 && < 0.6,- random >= 1.0 && < 1.2,- hashable >= 1.0 && < 1.3+ zip-archive >= 0.2.3 && < 0.4,+ async >= 2.0.1.4 && < 2.2,+ sandi >= 0.2 && < 0.4,+ unix-compat >= 0.1.2 && < 0.5,+ bytestring >= 0.10.0.2 && < 0.11,+ old-time >= 1.1 && < 1.2,+ directory >= 1.2.0.1 && < 1.3.0.0,+ process >= 1.1.0.2 && < 1.5.0.0,+ array >= 0.4.0.1 && < 0.6,+ random >= 1.0.1.1 && < 1.2,+ hashable >= 1.1.2.5 && < 1.3,+ mmap >= 0.5 && < 0.6,+ zlib >= 0.5.4.1 && < 0.7.0.0 -- release notes of GHC 7.10.2 recommends to use text >= 1.2.1.3: -- https://mail.haskell.org/pipermail/haskell/2015-July/024641.html@@ -463,8 +444,8 @@ if flag(use-time-1point5) build-depends: time >= 1.5 && < 1.7 else- build-depends: time >= 1.4 && < 1.5,- old-locale >= 1.0 && < 1.1+ build-depends: time >= 1.4.0.1 && < 1.5,+ old-locale >= 1.0.0.5 && < 1.1 if flag(optimize) ghc-options: -O2@@ -475,16 +456,9 @@ ghc-options: -Werror -- Note: "if true" works around a cabal bug with order of flag composition+ -- fixed in Cabal 1.18 https://github.com/haskell/cabal/commit/cf0cf077ab6836584fc3bf51d867e63824811d4d if true- ghc-options: -Wall -funbox-strict-fields -fwarn-tabs-- ghc-prof-options: -prof -auto-all-- if flag(hpc)- ghc-prof-options: -fhpc -hpcdir dist/hpc/libdarcs-- if flag(rts)- ghc-options: -rtsopts+ ghc-options: -Wall -funbox-strict-fields -fwarn-tabs -fno-warn-dodgy-imports if flag(curl) cpp-options: -DHAVE_CURL@@ -500,18 +474,14 @@ if flag(network-uri) build-depends: network-uri == 2.6.*, network == 2.6.* else- build-depends: network >= 2.3 && < 2.6- build-depends: HTTP >= 4000.2.3 && < 4000.4+ build-depends: network >= 2.4.1.2 && < 2.6+ build-depends: HTTP >= 4000.2.8 && < 4000.4 cpp-options: -DHAVE_HTTP x-have-http: if (!flag(curl) && !flag(http)) buildable: False - build-depends: mmap >= 0.5 && < 0.6-- build-depends: zlib >= 0.5.3.0 && < 0.7.0.0- -- The terminfo package cannot be built on Windows. if flag(terminfo) && !os(windows) build-depends: terminfo >= 0.3 && < 0.5@@ -528,52 +498,12 @@ FlexibleInstances ScopedTypeVariables KindSignatures+ DataKinds+ ConstraintKinds RankNTypes TypeFamilies NoMonoLocalBinds - if impl(ghc>=7.6)- -- in ghc < 7.6 we need to import Prelude hiding (catch)- -- in ghc >= 7.6 catch isn't in Prelude- -- once the minimum version of ghc is >= 7.6 we can remove the hiding- -- clauses and this flag- ghc-options: -fno-warn-dodgy-imports--- -- hashed-storage inclusion- hs-source-dirs: hashed-storage-- exposed-modules:- Storage.Hashed- Storage.Hashed.AnchoredPath- Storage.Hashed.Index- Storage.Hashed.Monad- Storage.Hashed.Tree- Storage.Hashed.Hash- Storage.Hashed.Packed-- Storage.Hashed.Plain- Storage.Hashed.Darcs-- if flag(hashed-storage-diff)- exposed-modules:- Storage.Hashed.Diff- build-depends: lcs-- other-modules:- Bundled.Posix- Storage.Hashed.Utils-- build-depends: sandi >= 0.2 && < 0.4,- unix-compat >= 0.1.2 && < 0.5,- cryptohash >= 0.4 && < 0.12-- if os(windows)- cpp-options: -DWIN32- build-depends: Win32 >= 2.2 && < 2.4-- -- end of hashed-storage inclusion- -- ---------------------------------------------------------------------- -- darcs itself -- ----------------------------------------------------------------------@@ -599,28 +529,22 @@ -- Note: "if true" works around a cabal bug with order of flag composition if true- ghc-options: -Wall -funbox-strict-fields -fwarn-tabs+ ghc-options: -Wall -funbox-strict-fields -fwarn-tabs -fno-warn-dodgy-imports - ghc-prof-options: -prof -auto-all if flag(threaded) ghc-options: -threaded if flag(static) ghc-options: -static -optl-static -optl-pthread - if flag(hpc)- ghc-prof-options: -fhpc -hpcdir dist/hpc/darcs- if flag(rts) ghc-options: -rtsopts + -- see http://bugs.darcs.net/issue1037 cc-options: -D_REENTRANT - build-depends: base >= 4.5 && < 4.9- build-depends: darcs,- filepath >= 1.2.0.0 && < 1.5.0.0,- regex-compat-tdfa >= 0.95.1 && < 0.96+ base >= 4.6 && < 4.10 -- if true to work around cabal bug with flag ordering if true@@ -648,30 +572,28 @@ main-is: test.hs hs-source-dirs: harness - build-depends: base >= 4.5 && < 4.9- if os(windows) cpp-options: -DWIN32+ build-depends: Win32 >= 2.2 && < 2.4 build-depends: darcs,- array >= 0.4 && < 0.6,- bytestring >= 0.9.0 && < 0.11,+ base >= 4.6 && < 4.10,+ array >= 0.4.0.1 && < 0.6,+ bytestring >= 0.10.0.2 && < 0.11, cmdargs >= 0.10 && < 0.11, containers >= 0.1 && < 0.6,- filepath >= 1.2.0.0 && < 1.5.0.0,- html == 1.0.*,+ filepath >= 1.3.0.1 && < 1.5.0.0, mtl >= 2.1 && < 2.3,- parsec >= 3.1 && < 3.2,- regex-compat-tdfa >= 0.95.1 && < 0.96, shelly >= 1.6.2 && < 1.7,- split >= 0.1.4.1 && < 0.3,- directory >= 1.1.0.2 && < 1.3.0.0,+ split >= 0.2.2 && < 0.3,+ directory >= 1.2.0.1 && < 1.3.0.0, FindBin >= 0.0 && < 0.1,- QuickCheck >= 2.3 && < 2.9,- HUnit >= 1.0 && < 1.4,+ QuickCheck >= 2.6 && < 2.9,+ HUnit >= 1.2.5.2 && < 1.4, test-framework >= 0.4.0 && < 0.9, test-framework-hunit >= 0.2.2 && < 0.4,- test-framework-quickcheck2 >= 0.3 && < 0.4+ test-framework-quickcheck2 >= 0.3 && < 0.4,+ zip-archive >= 0.2.3 && < 0.4 -- release notes of GHC 7.10.2 recommends to use text >= 1.2.1.3: -- https://mail.haskell.org/pipermail/haskell/2015-July/024641.html@@ -693,22 +615,24 @@ Darcs.Test.Patch.Properties.Generic Darcs.Test.Patch.Properties.GenericUnwitnessed Darcs.Test.Patch.Properties.Check- Darcs.Test.Patch.Properties.Real+ Darcs.Test.Patch.Properties.RepoPatchV2 Darcs.Test.Patch.Arbitrary.Generic- Darcs.Test.Patch.Arbitrary.Real Darcs.Test.Patch.Arbitrary.PrimV1- Darcs.Test.Patch.Arbitrary.PrimV3- Darcs.Test.Patch.Arbitrary.PatchV1+ Darcs.Test.Patch.Arbitrary.PrimFileUUID+ Darcs.Test.Patch.Arbitrary.RepoPatchV1+ Darcs.Test.Patch.Arbitrary.RepoPatchV2 Darcs.Test.Patch.Rebase Darcs.Test.Patch.RepoModel+ Darcs.Test.Patch.Selection Darcs.Test.Patch.Utils Darcs.Test.Patch.V1Model- Darcs.Test.Patch.V3Model+ Darcs.Test.Patch.FileUUIDModel Darcs.Test.Patch.WithState Darcs.Test.Patch Darcs.Test.Misc Darcs.Test.Util.TestResult Darcs.Test.Util.QuickCheck+ Storage.Hashed.Test if flag(optimize) ghc-options: -O2@@ -722,16 +646,13 @@ if true ghc-options: -Wall -funbox-strict-fields -fwarn-tabs - ghc-prof-options: -prof -auto-all if flag(threaded) ghc-options: -threaded - if flag(hpc)- ghc-prof-options: -fhpc -hpcdir dist/hpc/darcs-test- if flag(rts) ghc-options: -rtsopts + -- see http://bugs.darcs.net/issue1037 cc-options: -D_REENTRANT -- if true to work around cabal bug with flag ordering@@ -745,65 +666,10 @@ FlexibleInstances ScopedTypeVariables KindSignatures+ DataKinds+ ConstraintKinds RankNTypes TypeFamilies NoMonoLocalBinds --- hashed-storage inclusion-test-suite hashed-storage-test- buildable: True- type: exitcode-stdio-1.0- default-language: Haskell2010 - hs-source-dirs: hashed-storage-- ghc-options: -Wall -O2 -fwarn-tabs- ghc-prof-options: -prof -auto-all -O2-- if flag(hpc)- ghc-prof-options: -fhpc-- if flag(warn-as-error)- ghc-options: -Werror-- if impl(ghc>=7.6)- -- in ghc < 7.6 we need to import Prelude hiding (catch)- -- in ghc >= 7.6 catch isn't in Prelude- -- once the minimum version of ghc is >= 7.6 we can remove the hiding- -- clauses and this flag- ghc-options: -fno-warn-dodgy-imports-- main-is: test.hs- other-modules: Bundled.Posix- Storage.Hashed.Test-- if os(windows)- cpp-options: -DWIN32- build-depends: Win32 >= 2.2 && < 2.4-- -- hashed-storage inclusion: duplicated these from the library -- -- probably needed because of the higher cabal-version in darcs.cabal- -- not using version constraints here to save on maintaining duplicates,- -- since these packages are all constrained elsewhere in the cabal file.- build-depends: base,- containers,- mtl,- directory,- filepath,- bytestring,- sandi,- cryptohash,- binary,- zlib,- mmap,- unix-compat-- build-depends: test-framework,- test-framework-hunit,- test-framework-quickcheck2,- QuickCheck,- HUnit,- process,- zip-archive---- end of hashed-storage inclusion
harness/Darcs/Test/Patch.hs view
@@ -39,11 +39,11 @@ import Darcs.Patch.Witnesses.Show import Darcs.Patch.Prim( PrimPatch, coalesce, FromPrim, PrimOf, PrimPatchBase ) import qualified Darcs.Patch.Prim.V1 as V1 ( Prim )-import qualified Darcs.Patch.Prim.V3 as V3 ( Prim )+import qualified Darcs.Patch.Prim.FileUUID as FileUUID ( Prim ) import Darcs.Patch.RepoPatch ( RepoPatch ) import Darcs.Patch.Type ( PatchType(..) )-import Darcs.Patch.V1 as V1 ( Patch )-import Darcs.Patch.V2.Real ( isConsistent, isForward, RealPatch )+import Darcs.Patch.V1 as V1 ( RepoPatchV1 )+import Darcs.Patch.V2.RepoPatch ( isConsistent, isForward, RepoPatchV2 ) import Darcs.Patch.Patchy ( Commute(..), Patchy ) import Darcs.Patch.Merge( Merge ) import Darcs.Patch.Show ( ShowPatchBasic )@@ -51,14 +51,15 @@ import Darcs.Test.Patch.Arbitrary.Generic import qualified Darcs.Test.Patch.Arbitrary.PrimV1 as P1-import Darcs.Test.Patch.Arbitrary.PrimV3()-import Darcs.Test.Patch.Arbitrary.Real-import Darcs.Test.Patch.Arbitrary.PatchV1 ()+import Darcs.Test.Patch.Arbitrary.PrimFileUUID()+import Darcs.Test.Patch.Arbitrary.RepoPatchV1 ()+import Darcs.Test.Patch.Arbitrary.RepoPatchV2 import Darcs.Test.Patch.Arbitrary.PrimV1 () import Darcs.Test.Patch.RepoModel import Darcs.Test.Patch.WithState( WithState, WithStartState ) import qualified Darcs.Test.Patch.Info+import qualified Darcs.Test.Patch.Selection import qualified Darcs.Test.Patch.Examples.Set1 as Ex import qualified Darcs.Test.Patch.Examples.Set2Unwitnessed as ExU@@ -67,7 +68,7 @@ import qualified Darcs.Test.Patch.Properties.V1Set1 as Prop1 import qualified Darcs.Test.Patch.Properties.V1Set2 as Prop2 import qualified Darcs.Test.Patch.Properties.Generic as PropG-import qualified Darcs.Test.Patch.Properties.Real as PropR+import qualified Darcs.Test.Patch.Properties.RepoPatchV2 as PropR import qualified Darcs.Test.Patch.Properties.GenericUnwitnessed as PropU import qualified Darcs.Test.Patch.Rebase as Rebase@@ -124,29 +125,29 @@ , testCases "FL prim inverses commute" (PropU.commuteInverses WSub.commute) $ ExU.commutablesFL , testCases "fails" (PropU.commuteFails WSub.commute) ([] :: [(V1.Prim WSub.:> V1.Prim) wX wY]) , testCases "read and show work on Prim" PropU.show_read ExU.primPatches- , testCases "read and show work on RealPatch" PropU.show_read ExU.realPatches- , testCases "example flattenings work" PropU.consistentTreeFlattenings ExU.realPatchLoopExamples- , testCases "real merge input consistent" (PropU.mergeArgumentsConsistent isConsistent) ExU.realMergeables- , testCases "real merge input is forward" (PropU.mergeArgumentsConsistent isForward) ExU.realMergeables- , testCases "real merge output is forward" (PropU.mergeConsistent isForward) ExU.realMergeables- , testCases "real merge output consistent" (PropU.mergeConsistent isConsistent) ExU.realMergeables- , testCases "real merge either way" PropU.mergeEitherWay ExU.realMergeables- , testCases "real merge and commute" PropU.mergeCommute ExU.realMergeables+ , testCases "read and show work on RepoPatchV2" PropU.show_read ExU.repov2Patches+ , testCases "example flattenings work" PropU.consistentTreeFlattenings ExU.repov2PatchLoopExamples+ , testCases "V2 merge input consistent" (PropU.mergeArgumentsConsistent isConsistent) ExU.repov2Mergeables+ , testCases "V2 merge input is forward" (PropU.mergeArgumentsConsistent isForward) ExU.repov2Mergeables+ , testCases "V2 merge output is forward" (PropU.mergeConsistent isForward) ExU.repov2Mergeables+ , testCases "V2 merge output consistent" (PropU.mergeConsistent isConsistent) ExU.repov2Mergeables+ , testCases "V2 merge either way" PropU.mergeEitherWay ExU.repov2Mergeables+ , testCases "V2 merge and commute" PropU.mergeCommute ExU.repov2Mergeables - , testCases "real recommute" (PropU.recommute WSub.commute) ExU.realCommutables- , testCases "real inverses commute" (PropU.commuteInverses WSub.commute) ExU.realCommutables- , testCases "real permutivity" (PropU.permutivity WSub.commute) ExU.realNonduplicateTriples- , testCases "real partial permutivity" (PropU.partialPermutivity WSub.commute) ExU.realNonduplicateTriples+ , testCases "V2 recommute" (PropU.recommute WSub.commute) ExU.repov2Commutables+ , testCases "V2 inverses commute" (PropU.commuteInverses WSub.commute) ExU.repov2Commutables+ , testCases "V2 permutivity" (PropU.permutivity WSub.commute) ExU.repov2NonduplicateTriples+ , testCases "V2 partial permutivity" (PropU.partialPermutivity WSub.commute) ExU.repov2NonduplicateTriples ] -instance PrimPatch prim => Check (RealPatch prim) where+instance PrimPatch prim => Check (RepoPatchV2 prim) where checkPatch p = return $ isNothing $ isConsistent p -instance Check V3.Prim where+instance Check FileUUID.Prim where checkPatch _ = return True -- XXX -commuteReals :: PrimPatch prim => (RealPatch prim :> RealPatch prim) wX wY -> Maybe ((RealPatch prim :> RealPatch prim) wX wY)-commuteReals = commute+commuteRepoPatchV2s :: PrimPatch prim => (RepoPatchV2 prim :> RepoPatchV2 prim) wX wY -> Maybe ((RepoPatchV2 prim :> RepoPatchV2 prim) wX wY)+commuteRepoPatchV2s = commute qc_prim :: forall prim wX wY wA model. (PrimPatch prim, ArbitraryPrim prim, Show2 prim@@ -183,8 +184,8 @@ [ pair_properties (undefined :: prim wX wY) "arbitrary" arbitraryThing' , pair_properties (undefined :: FL prim wX wY) "arbitrary FL" arbitraryThing' , coalesce_properties (undefined :: prim wX wY) "arbitrary" arbitraryThing'- , nonreal_commute_properties (undefined :: prim wX wY) "arbitrary" arbitraryThing'- , nonreal_commute_properties (undefined :: FL prim wX wY) "arbitrary FL" arbitraryThing'+ , nonrpv2_commute_properties (undefined :: prim wX wY) "arbitrary" arbitraryThing'+ , nonrpv2_commute_properties (undefined :: FL prim wX wY) "arbitrary FL" arbitraryThing' , patch_properties (undefined :: prim wX wA) "arbitrary" arbitraryThing' , patch_properties (undefined :: FL prim wX wA) "arbitrary FL" arbitraryThing' , patch_repo_properties (undefined :: prim wX wA) "arbitrary" arbitraryThing'@@ -197,41 +198,41 @@ qc_V2P1 :: [Test] qc_V2P1 = [ testProperty "tree flattenings are consistent... "- PropR.propConsistentTreeFlattenings- , testProperty "with quickcheck that real patches are consistent... "+ (PropR.propConsistentTreeFlattenings :: Sealed (WithStartState (ModelOf V1.Prim) (Tree V1.Prim)) -> Bool)+ , testProperty "with quickcheck that RepoPatchV2 patches are consistent... " (unseal $ P1.patchFromTree $ fromMaybe . isConsistent) -- permutivity ---------------------------------------------------------------------------- , testConditional "permutivity" (unseal $ P1.commuteTripleFromTree notDuplicatestriple)- (unseal $ P1.commuteTripleFromTree $ PropG.permutivity commuteReals)+ (unseal $ P1.commuteTripleFromTree $ PropG.permutivity commuteRepoPatchV2s) , testConditional "partial permutivity" (unseal $ P1.commuteTripleFromTree notDuplicatestriple)- (unseal $ P1.commuteTripleFromTree $ PropG.partialPermutivity commuteReals)+ (unseal $ P1.commuteTripleFromTree $ PropG.partialPermutivity commuteRepoPatchV2s) , testConditional "nontrivial permutivity" (unseal $ P1.commuteTripleFromTree (\t -> nontrivialTriple t && notDuplicatestriple t))- (unseal $ P1.commuteTripleFromTree $ (PropG.permutivity commuteReals))+ (unseal $ P1.commuteTripleFromTree $ (PropG.permutivity commuteRepoPatchV2s)) ] qc_V2 :: forall prim wXx wYy . (PrimPatch prim, Show1 (ModelOf prim), RepoModel (ModelOf prim),- Check (RealPatch prim), ArbitraryPrim prim, Show2 prim,+ Check (RepoPatchV2 prim), ArbitraryPrim prim, Show2 prim, RepoState (ModelOf prim) ~ ApplyState prim) => prim wXx wYy -> [Test] qc_V2 _ =- [ testProperty "readPatch and showPatch work on RealPatch... "- (unseal $ patchFromTree $ (PropG.show_read :: RealPatch prim wX wY -> TestResult))- , testProperty "readPatch and showPatch work on FL RealPatch... "- (unseal2 $ (PropG.show_read :: FL (RealPatch prim) wX wY -> TestResult))+ [ testProperty "readPatch and showPatch work on RepoPatchV2... "+ (unseal $ patchFromTree $ (PropG.show_read :: RepoPatchV2 prim wX wY -> TestResult))+ , testProperty "readPatch and showPatch work on FL RepoPatchV2... "+ (unseal2 $ (PropG.show_read :: FL (RepoPatchV2 prim) wX wY -> TestResult)) , testProperty "we can do merges using QuickCheck" (isNothing . (PropG.propIsMergeable :: Sealed (WithStartState (ModelOf prim) (Tree prim))- -> Maybe (Tree (RealPatch prim) wX)))+ -> Maybe (Tree (RepoPatchV2 prim) wX))) ] ++ concat- [ merge_properties (undefined :: RealPatch prim wX wY) "tree" mergePairFromTree- , merge_properties (undefined :: RealPatch prim wX wY) "twfp" mergePairFromTWFP- , pair_properties (undefined :: RealPatch prim wX wY) "tree" commutePairFromTree- , pair_properties (undefined :: RealPatch prim wX wY) "twfp" commutePairFromTWFP- , patch_properties (undefined :: RealPatch prim wX wY) "tree" patchFromTree+ [ merge_properties (undefined :: RepoPatchV2 prim wX wY) "tree" mergePairFromTree+ , merge_properties (undefined :: RepoPatchV2 prim wX wY) "twfp" mergePairFromTWFP+ , pair_properties (undefined :: RepoPatchV2 prim wX wY) "tree" commutePairFromTree+ , pair_properties (undefined :: RepoPatchV2 prim wX wY) "twfp" commutePairFromTWFP+ , patch_properties (undefined :: RepoPatchV2 prim wX wY) "tree" patchFromTree ] properties :: forall thing gen. (Show1 gen, Arbitrary (Sealed gen)) =>@@ -272,12 +273,12 @@ properties gen "commute" genname (if runCoalesceTests p then [ ("coalesce commutes with commute", const True, PropG.coalesceCommute coalesce) ] else []) --- The following properties do not hold for "Real" patches (conflictors and+-- The following properties do not hold for "RepoPatchV2" patches (conflictors and -- duplicates, specifically) .-nonreal_commute_properties :: forall p gen x y+nonrpv2_commute_properties :: forall p gen x y . (Show1 gen, Arbitrary (Sealed gen), Patchy p, ShowPatchBasic p, MyEq p) => p x y -> PropList (p :> p) gen-nonreal_commute_properties _ genname gen =+nonrpv2_commute_properties _ genname gen = properties gen "commute" genname [ ("patch & inverse commute", const True , PropG.patchAndInverseCommute commute) , ("patch & inverse commute", nontrivialCommute, PropG.patchAndInverseCommute commute)@@ -356,13 +357,13 @@ ] -- the following properties are disabled, because they routinely lead to -- exponential cases, making the tests run for ever and ever; nevertheless, -- we would expect them to hold- {- ++ merge_properties (undefined :: V1.Patch Prim wX wY) "tree" mergePairFromTree- ++ merge_properties (undefined :: V1.Patch Prim wX wY) "twfp" mergePairFromTWFP- ++ commute_properties (undefined :: V1.Patch Prim wX wY) "tree" commutePairFromTree- ++ commute_properties (undefined :: V1.Patch Prim wX wY) "twfp" commutePairFromTWFP -}+ {- ++ merge_properties (undefined :: V1.RepoPatchV1 Prim wX wY) "tree" mergePairFromTree+ ++ merge_properties (undefined :: V1.RepoPatchV1 Prim wX wY) "twfp" mergePairFromTWFP+ ++ commute_properties (undefined :: V1.RepoPatchV1 Prim wX wY) "tree" commutePairFromTree+ ++ commute_properties (undefined :: V1.RepoPatchV1 Prim wX wY) "twfp" commutePairFromTWFP -} -- tests (either QuickCheck or Unit) that should be run on any type of patch-general_patchTests :: (RepoPatch p, ArbitraryPrim (PrimOf p), Show2 (PrimOf p)) => PatchType p -> [Test]+general_patchTests :: (RepoPatch p, ArbitraryPrim (PrimOf p), Show2 (PrimOf p)) => PatchType rt p -> [Test] general_patchTests pt = [ testGroup "Rebase patches" $ Rebase.testSuite pt ]@@ -371,13 +372,14 @@ testSuite :: [Test] testSuite = [ testGroup "Darcs.Patch.Prim.V1" $ qc_prim (undefined :: V1.Prim wX wY) , testGroup "Darcs.Patch.V1 (using Prim.V1)" $- unit_V1P1 ++ qc_V1P1 ++ general_patchTests (PatchType :: PatchType (V1.Patch V1.Prim))+ unit_V1P1 ++ qc_V1P1 ++ general_patchTests (PatchType :: PatchType rt (V1.RepoPatchV1 V1.Prim)) , testGroup "Darcs.Patch.V2 (using Prim.V1)" $ unit_V2P1 ++ qc_V2 (undefined :: V1.Prim wX wY) ++ qc_V2P1 ++- general_patchTests (PatchType :: PatchType (RealPatch V1.Prim))- -- , testGroup "Darcs.Patch.Prim.V3" $ qc_prim (undefined :: V3.Prim wX wY)- , testGroup "Darcs.Patch.V2 (using Prim.V3)" $- qc_V2 (undefined :: V3.Prim wX wY) ++- general_patchTests (PatchType :: PatchType (RealPatch V3.Prim))+ general_patchTests (PatchType :: PatchType rt (RepoPatchV2 V1.Prim))+ -- , testGroup "Darcs.Patch.Prim.FileUUID" $ qc_prim (undefined :: FileUUID.Prim wX wY)+ , testGroup "Darcs.Patch.V2 (using Prim.FileUUID)" $+ qc_V2 (undefined :: FileUUID.Prim wX wY) +++ general_patchTests (PatchType :: PatchType rt (RepoPatchV2 FileUUID.Prim)) , Darcs.Test.Patch.Info.testSuite+ , Darcs.Test.Patch.Selection.testSuite ]
harness/Darcs/Test/Patch/Arbitrary/Generic.hs view
@@ -27,7 +27,7 @@ import Darcs.Patch.Patchy ( Invert(..), Commute(..) ) import Darcs.Patch.Prim ( PrimOf, PrimPatch, PrimPatchBase, FromPrim(..), PrimConstruct( anIdentity ) ) import Darcs.Patch.Prim.V1 ()-import Darcs.Patch.V2 ( RealPatch ) -- XXX this is more or less a hack+import Darcs.Patch.V2 ( RepoPatchV2 ) -- XXX this is more or less a hack --import Darcs.ColorPrinter ( errorDoc ) --import Darcs.ColorPrinter ( traceDoc ) import Darcs.Patch.Witnesses.Show@@ -119,7 +119,7 @@ instance MightBeEmptyHunk (FL p) class MightHaveDuplicate p where- -- |"duplicates" in V2 patches (RealPatch) have lots of bugs+ -- |"duplicates" in V2 patches (RepoPatchV2) have lots of bugs -- that break various commute/merge properties. hasDuplicate :: p wX wY -> Bool hasDuplicate _ = False@@ -261,6 +261,6 @@ else do n <- choose (0, num - 1) return $ Sealed $ WithStartState rm $ TWFP n t where -- just used to get the length. In principle this should be independent of the patch type.- flattenOneRP :: Tree prim wX -> Sealed (FL (RealPatch prim) wX)+ flattenOneRP :: Tree prim wX -> Sealed (FL (RepoPatchV2 prim) wX) flattenOneRP = flattenOne
− harness/Darcs/Test/Patch/Arbitrary/PatchV1.hs
@@ -1,290 +0,0 @@--- Copyright (C) 2002-2003,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.--{-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE CPP, FlexibleInstances, TypeSynonymInstances #-}---module Darcs.Test.Patch.Arbitrary.PatchV1 () where--import Prelude hiding ( pi )-import System.IO.Unsafe ( unsafePerformIO )-import Test.QuickCheck-import Control.Applicative-import Control.Monad ( liftM, liftM2, liftM3, liftM4, replicateM )--import Darcs.Patch.Info ( PatchInfo, patchinfo )-import qualified Data.ByteString as B ( ByteString )-import qualified Data.ByteString.Char8 as BC ( pack )--import Darcs.Patch ( addfile, adddir, move,- hunk, tokreplace, binary,- changepref, invert, merge )-import Darcs.Patch.V1 ()-import qualified Darcs.Patch.V1.Core as V1 ( Patch(..) )-import Darcs.Patch.Prim.V1 ()-import Darcs.Patch.Prim.V1.Core ( Prim(..) )-import Darcs.Patch.Witnesses.Ordered-import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed), unseal, mapSeal, Sealed2(..) )-import Darcs.Patch.Witnesses.Unsafe---- This definitely feels a bit weird to be importing Properties here, and--- probably means we want to move this elsewhere, but Darcs.Test.Patch.Check is--- already taken with something apparently only semi-related-import Darcs.Test.Patch.Properties.Check( checkAPatch )-import Darcs.Test.Patch.Arbitrary.Generic ( MightHaveDuplicate )--#include "impossible.h"--type Patch = V1.Patch Prim--class ArbitraryP p where- arbitraryP :: Gen (Sealed (p wX))--{--TODO: there is a lot of overlap in testing between between this module-and Darcs.Test.Patch.QuickCheck--This module tests Prim and V1 patches, and Darcs.Test.Patch.QuickCheck-tests Prim and V2 patches--This module's generator covers a wider set of patch types, but is less-likely to generate conflicts than Darcs.Test.Patch.QuickCheck.--Until this is cleaned up, we take some care that the Arbitrary instances-do not overlap and are only used for tests from the respective-modules.--(There are also tests in other modules that probably depend on the-Arbitrary instances in this module.)--}--instance Arbitrary (Sealed (Prim wX)) where- arbitrary = arbitraryP--instance Arbitrary (Sealed (FL Patch wX)) where- arbitrary = arbitraryP---- instance Arbitrary (Sealed2 (Prim :> Prim)) where- -- arbitrary = unseal Sealed2 <$> arbitraryP--instance Arbitrary (Sealed2 (FL Patch)) where- arbitrary = unseal Sealed2 <$> arbitraryP--instance Arbitrary (Sealed2 (FL Patch :\/: FL Patch)) where- arbitrary = unseal Sealed2 <$> arbitraryP--instance Arbitrary (Sealed2 (FL Patch :> FL Patch)) where- arbitrary = unseal Sealed2 <$> arbitraryP--instance Arbitrary (Sealed2 (FL Patch :> FL Patch :> FL Patch)) where- arbitrary = unseal Sealed2 <$> arbitraryP---instance (ArbitraryP p1, ArbitraryP p2) => ArbitraryP (p1 :> p2) where- arbitraryP = do Sealed p1 <- arbitraryP- Sealed p2 <- arbitraryP- return (Sealed (p1 :> p2))--instance (ArbitraryP p1, ArbitraryP p2) => ArbitraryP (p1 :\/: p2) where- arbitraryP = do Sealed p1 <- arbitraryP- Sealed p2 <- arbitraryP- return (Sealed (unsafeCoercePEnd p1 :\/: p2))--instance ArbitraryP (FL Patch) where- arbitraryP = sized arbpatch--instance ArbitraryP Prim where- arbitraryP = onepatchgen--instance MightHaveDuplicate (V1.Patch prim)--hunkgen :: Gen (Sealed (Prim wX))-hunkgen = do- i <- frequency [(1,choose (0,5)),(1,choose (0,35)),- (2,return 0),(3,return 1),(2,return 2),(1,return 3)]- j <- frequency [(1,choose (0,5)),(1,choose (0,35)),- (2,return 0),(3,return 1),(2,return 2),(1,return 3)]- if i == 0 && j == 0 then hunkgen- else Sealed <$>- liftM4 hunk filepathgen linenumgen- (replicateM i filelinegen)- (replicateM j filelinegen)--tokreplacegen :: Gen (Sealed (Prim wX))-tokreplacegen = do- f <- filepathgen- o <- tokengen- n <- tokengen- if o == n- then return $ Sealed $ tokreplace f "A-Za-z" "old" "new"- else return $ Sealed $ tokreplace f "A-Za-z_" o n--twofilegen :: (forall wY . FilePath -> FilePath -> Prim wX wY) -> Gen (Sealed (Prim wX))-twofilegen p = do- n1 <- filepathgen- n2 <- filepathgen- if n1 /= n2 && checkAPatch (p n1 n2)- then return $ Sealed $ p n1 n2- else twofilegen p--chprefgen :: Gen (Sealed (Prim wX))-chprefgen = do- f <- oneof [return "color", return "movie"]- o <- tokengen- n <- tokengen- if o == n then return $ Sealed $ changepref f "old" "new"- else return $ Sealed $ changepref f o n--simplepatchgen :: Gen (Sealed (Prim wX))-simplepatchgen = frequency [(1,liftM (Sealed . addfile) filepathgen),- (1,liftM (Sealed . adddir) filepathgen),- (1,liftM3 (\x y z -> Sealed (binary x y z)) filepathgen arbitrary arbitrary),- (1,twofilegen move),- (1,tokreplacegen),- (1,chprefgen),- (7,hunkgen)- ]--onepatchgen :: Gen (Sealed (Prim wX))-onepatchgen = oneof [simplepatchgen, mapSeal (invert . unsafeCoerceP) `fmap` simplepatchgen]--norecursgen :: Int -> Gen (Sealed (FL Patch wX))-norecursgen 0 = mapSeal (\p -> V1.PP p :>: NilFL) `fmap` onepatchgen-norecursgen n = oneof [mapSeal (\p -> V1.PP p :>: NilFL) `fmap` onepatchgen,flatcompgen n]--arbpatch :: Int -> Gen (Sealed (FL Patch wX))-arbpatch 0 = mapSeal (\p -> V1.PP p :>: NilFL) `fmap` onepatchgen-arbpatch n = frequency [(3,mapSeal (\p -> V1.PP p :>: NilFL) `fmap` onepatchgen),- (2,flatcompgen n),- (0,rawMergeGen n),- (0,mergegen n),- (1,mapSeal (\p -> V1.PP p :>: NilFL) `fmap` onepatchgen)- ]---- | Generate an arbitrary list of at least one element-unempty :: Arbitrary a => Gen [a]-unempty = do- a <- arbitrary- as <- arbitrary- return (a:as)--rawMergeGen :: Int -> Gen (Sealed (FL Patch wX))-rawMergeGen n = do Sealed p1 <- arbpatch len- Sealed p2 <- arbpatch len- if checkAPatch (invert p1:>:p2:>:NilFL) &&- checkAPatch (invert p2:>:p1:>:NilFL)- then case merge (p2 :\/: p1) of- _ :/\: p2' -> return (Sealed (unsafeCoercePStart p2'))- else rawMergeGen n- where len = if n < 15 then n`div`3 else 3--mergegen :: Int -> Gen (Sealed (FL Patch wX))-mergegen n = do- Sealed p1 <- norecursgen len- Sealed p2 <- norecursgen len- if checkAPatch (invert p1:>:p2:>:NilFL) &&- checkAPatch (invert p2:>:p1:>:NilFL)- then case merge (p2:\/:p1) of- _ :/\: p2' ->- if checkAPatch (p1+>+p2')- then return $ Sealed $ p1+>+p2'- else impossible- else mergegen n- where len = if n < 15 then n`div`3 else 3--arbpi :: Gen PatchInfo-arbpi = do n <- unempty- a <- unempty- l <- unempty- d <- unempty- return $ unsafePerformIO $ patchinfo n d a l--instance Arbitrary PatchInfo where- arbitrary = arbpi--instance Arbitrary B.ByteString where- arbitrary = liftM BC.pack arbitrary--flatlistgen :: Int -> Gen (Sealed (FL Patch wX))-flatlistgen 0 = return $ Sealed NilFL-flatlistgen n = do Sealed x <- onepatchgen- Sealed xs <- flatlistgen (n-1)- return (Sealed (V1.PP x :>: xs))--flatcompgen :: Int -> Gen (Sealed (FL Patch wX))-flatcompgen n = do- Sealed ps <- flatlistgen n- let myp = regularizePatches $ ps- if checkAPatch myp- then return $ Sealed myp- else flatcompgen n---- resize to size 25, that means we'll get line numbers no greater--- than 1025 using QuickCheck 2.1-linenumgen :: Gen Int-linenumgen = frequency [(1,return 1), (1,return 2), (1,return 3),- (3,liftM (\n->1+abs n) (resize 25 arbitrary)) ]--tokengen :: Gen String-tokengen = oneof [return "hello", return "world", return "this",- return "is", return "a", return "silly",- return "token", return "test"]--toklinegen :: Gen String-toklinegen = liftM unwords $ replicateM 3 tokengen--filelinegen :: Gen B.ByteString-filelinegen = liftM BC.pack $- frequency [(1,map fromSafeChar `fmap` arbitrary),(5,toklinegen),- (1,return ""), (1,return "{"), (1,return "}") ]--filepathgen :: Gen String-filepathgen = liftM fixpath badfpgen--fixpath :: String -> String-fixpath "" = "test"-fixpath p = fpth p--fpth :: String -> String-fpth ('/':'/':cs) = fpth ('/':cs)-fpth (c:cs) = c : fpth cs-fpth [] = []--newtype SafeChar = SS Char-instance Arbitrary SafeChar where- arbitrary = oneof $ map (return . SS) (['a'..'z']++['A'..'Z']++['1'..'9']++"0")--fromSafeChar :: SafeChar -> Char-fromSafeChar (SS s) = s--badfpgen :: Gen String-badfpgen = frequency [(1,return "test"), (1,return "hello"), (1,return "world"),- (1,map fromSafeChar `fmap` arbitrary),- (1,liftM2 (\a b-> a++"/"++b) filepathgen filepathgen) ]--regularizePatches :: FL Patch wX wY -> FL Patch wX wY-regularizePatches patches = rpint (unsafeCoerceP NilFL) patches- where -- this reverses the list, which seems odd and causes- -- the witness unsafety- rpint :: FL Patch wX wY -> FL Patch wA wB -> FL Patch wX wY- rpint ok_ps NilFL = ok_ps- rpint ok_ps (p:>:ps) =- if checkAPatch (unsafeCoerceP p:>:ok_ps)- then rpint (unsafeCoerceP p:>:ok_ps) ps- else rpint ok_ps ps-
+ harness/Darcs/Test/Patch/Arbitrary/PrimFileUUID.hs view
@@ -0,0 +1,259 @@+-- TODO: Remove these warning disabling flags...+{-# OPTIONS_GHC -w #-}+{-# LANGUAGE CPP, MultiParamTypeClasses, OverloadedStrings #-}+module Darcs.Test.Patch.Arbitrary.PrimFileUUID where++import qualified Darcs.Test.Patch.Arbitrary.Generic as T+ ( commuteTripleFromTree, commutePairFromTree, commutePairFromTWFP+ , mergePairFromTree, mergePairFromTWFP+ , patchFromTree )+import Darcs.Test.Patch.Arbitrary.Generic+import Darcs.Test.Patch.RepoModel++import Control.Monad ( liftM )+import Test.QuickCheck+import Darcs.Test.Patch.WithState+import Darcs.Patch.Witnesses.Sealed+import Darcs.Patch.Witnesses.Eq+import Darcs.Patch.Witnesses.Unsafe+import Darcs.Patch.Witnesses.Ordered+import Darcs.Patch.Prim.FileUUID ()+import Darcs.Patch.Prim.FileUUID.Core ( Prim(..), Location, Hunk(..), UUID(..) )+import Darcs.Patch.RepoPatch ( RepoPatch )++import Darcs.Test.Patch.FileUUIDModel+import Darcs.Test.Util.QuickCheck ( alpha, notIn, maybeOf )++import Darcs.UI.Commands.Replace ( defaultToks )+import Darcs.Patch.Prim++import Control.Applicative ( (<$>) )+import qualified Data.ByteString.Char8 as BC+import qualified Data.ByteString as BS+import Data.Maybe ( isJust )+import qualified Data.Map as M+import Darcs.Util.Hash( Hash(..) )++#include "impossible.h"++patchFromTree :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . p wY wZ -> t) -> WithStartState FileUUIDModel (Tree Prim) wX -> t+patchFromTree = T.patchFromTree++mergePairFromTree :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . (p :\/: p) wY wZ -> t) -> WithStartState FileUUIDModel (Tree Prim) wX -> t+mergePairFromTree = T.mergePairFromTree++mergePairFromTWFP :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . (p :\/: p) wY wZ -> t) -> WithStartState FileUUIDModel (TreeWithFlattenPos Prim) wX -> t+mergePairFromTWFP = T.mergePairFromTWFP++commutePairFromTWFP :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . (p :> p) wY wZ -> t) -> WithStartState FileUUIDModel (TreeWithFlattenPos Prim) wX -> t+commutePairFromTWFP = T.commutePairFromTWFP++commutePairFromTree :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . (p :> p) wY wZ -> t) -> WithStartState FileUUIDModel (Tree Prim) wX -> t+commutePairFromTree = T.commutePairFromTree++commuteTripleFromTree :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . (p :> p :> p) wY wZ -> t) -> WithStartState FileUUIDModel (Tree Prim) wX -> t+commuteTripleFromTree = T.commuteTripleFromTree++type instance ModelOf Prim = FileUUIDModel+instance ArbitraryPrim Prim where+ runCoalesceTests _ = False+ hasPrimConstruct _ = False++hunkIdentity (Hunk _ old new) | old == new = unsafeCoerceP IsEq+hunkIdentity _ = NotEq++instance NullPatch Prim where+ nullPatch (BinaryHunk _ x) = hunkIdentity x+ nullPatch (TextHunk _ x) = hunkIdentity x+ nullPatch _ = NotEq++instance Arbitrary (Sealed2 (FL (WithState FileUUIDModel Prim))) where+ arbitrary = do repo <- ourSmallRepo+ liftM (unseal (seal2 . wesPatch)) $ arbitraryState repo++-- instance Show1 (TreeWithFlattenPos Prim) where+-- showDict1 = ShowDictClass++-- WithState and propFail are handy for debugging arbitrary code+propFail :: Int -> Tree Prim wX -> Bool+propFail n xs = sizeTree xs < n++----------------------------------------------------------------------+-- * QuickCheck generators++aHunk :: forall wX wY . BS.ByteString -> Gen (Hunk wX wY)+aHunk content+ = sized $ \n ->+ do pos <- choose (0, BS.length content)+ let prefixLen = pos+ restLen = BS.length content - prefixLen+ oldLen <- frequency+ [ (75, choose (0, min restLen n))+ , (25, choose (0, min 10 restLen))+ ]+ let nonempty x = if oldLen /= 0 then x else 0+ newLen <- frequency+ [ ( 54, choose (1,min 1 n) )+ , ( nonempty 42, choose (1,min 1 oldLen) )+ , ( nonempty 2, return oldLen )+ , ( nonempty 2, return 0 )+ ]+ new <- BS.concat <$> vectorOf newLen aLine+ let old = BS.take oldLen $ BS.drop prefixLen $ content+ return $ Hunk pos old new++aTextHunk :: forall wX wY . (UUID, Object Fail) -> Gen (Prim wX wY)+aTextHunk (uuid, (Blob text _)) =+ do hunk <- aHunk (unFail text)+ return $ TextHunk uuid hunk++aManifest :: forall wX wY . UUID -> Location -> Object Fail -> Gen (Prim wX wY)+aManifest uuid loc (Directory dir) =+ do newFilename <- aFilename `notIn` (M.keys dir)+ return $ Manifest uuid loc++aDemanifest :: forall wX wY . UUID -> Location -> Gen (Prim wX wY)+aDemanifest uuid loc = return $ Demanifest uuid loc++-- | Generates any type of 'Prim' patch, except binary and setpref patches.+aPrim :: forall wX wY . FileUUIDModel wX -> Gen (WithEndState FileUUIDModel (Prim wX) wY)+aPrim repo+ = do mbFile <- maybeOf repoFiles+ mbDir <- maybeOf repoDirs+ mbExisting <- maybeOf $ repoObjects repo+ mbManifested <- maybeOf manifested+ fresh <- anUUID+ filename <- aFilename+ dir <- elements (rootDir:repoDirs)+ mbOtherDir <- maybeOf repoDirs+ let whenfile x = if isJust mbFile then x else 0+ whendir x = if isJust mbDir then x else 0+ whenexisting x = if isJust mbExisting then x else 0+ whenmanifested x = if isJust mbManifested then x else 0+ patch <- frequency+ [ ( whenfile 12, aTextHunk $ fromJust mbFile )+ , ( 2, aTextHunk (fresh, Blob (return "") NoHash) ) -- create an empty thing+ , ( whenexisting (whendir 2), -- manifest an existing object+ aManifest (fst $ fromJust mbExisting)+ (fst $ fromJust mbDir, filename)+ (snd $ fromJust mbDir))+ , ( whenmanifested 2, uncurry aDemanifest $ fromJust mbManifested )+ -- TODO: demanifest+ ]+ let repo' = unFail $ repoApply repo patch+ return $ WithEndState patch repo'+ where+ manifested = [ (id, (dirid, name)) | (dirid, Directory dir) <- repoDirs+ , (name, id) <- M.toList dir ]+ repoFiles = [ (id, Blob x y) | (id, Blob x y) <- repoObjects repo ]+ repoDirs = [ (id, Directory x) | (id, Directory x) <- repoObjects repo ]+ rootDir = (UUID "ROOT", root repo)++----------------------------------------------------------------------+-- *** Pairs of primitive patches++-- Try to generate commutable pairs of hunks+hunkPair :: forall wX wY . (UUID, Object Fail) -> Gen ((Prim :> Prim) wX wY)+hunkPair (uuid, (Blob file _)) =+ do h1@(Hunk l1 old1 new1) <- aHunk (unFail file)+ (delta, content') <- selectChunk h1 (unFail file)+ Hunk l2' old2 new2 <- aHunk content'+ let l2 = l2'+delta+ return (TextHunk uuid (Hunk l1 old1 new1) :> TextHunk uuid (Hunk l2 old2 new2))+ where+ selectChunk (Hunk l old new) content = elements [prefix, suffix]+ where start = l - 1+ prefix = (0, BS.take start content)+ suffix = (start + BS.length new, BS.drop (start + BS.length old) content)+ selectChunk _ _ = impossible++aPrimPair :: forall wX wY . FileUUIDModel wX -> Gen (WithEndState FileUUIDModel ((Prim :> Prim) wX) wY)+aPrimPair repo+ = do mbFile <- maybeOf repoFiles+ frequency+ [ ( if isJust mbFile then 1 else 0+ , do p1 :> p2 <- hunkPair $ fromJust mbFile+ let repo' = unFail $ repoApply repo p1+ repo'' = unFail $ repoApply repo' p2+ return $ WithEndState (p1 :> p2) repo''+ )+ , ( 1+ , do Sealed wesP <- arbitraryState repo+ return $ unsafeCoerceP1 wesP+ )+ ]+ where+ repoFiles = [ (id, Blob x y) | (id, Blob x y) <- repoObjects repo ]++----------------------------------------------------------------------+-- Arbitrary instances++ourSmallRepo :: Gen (FileUUIDModel wX)+ourSmallRepo = aSmallRepo++instance ArbitraryState FileUUIDModel Prim where+ arbitraryState s = seal <$> aPrim s+++instance Arbitrary (Sealed2 Prim) where+ arbitrary = makeS2Gen ourSmallRepo++instance Arbitrary (Sealed (Prim x)) where+ arbitrary = makeSGen ourSmallRepo++instance Arbitrary (Sealed2 (Prim :> Prim)) where+ arbitrary = do repo <- ourSmallRepo+ WithEndState pp _ <- aPrimPair repo+ return $ seal2 pp++instance Arbitrary (Sealed ((Prim :> Prim) wA)) where+ arbitrary = do repo <- ourSmallRepo+ WithEndState pp _ <- aPrimPair repo+ return $ seal pp++instance Arbitrary (Sealed2 (Prim :> Prim :> Prim)) where+ arbitrary = makeS2Gen ourSmallRepo++instance Arbitrary (Sealed ((Prim :> Prim :> Prim) a)) where+ arbitrary = makeSGen ourSmallRepo++instance Arbitrary (Sealed2 (FL Prim)) where+ arbitrary = makeS2Gen ourSmallRepo++instance Arbitrary (Sealed ((FL Prim) wA)) where+ arbitrary = makeSGen ourSmallRepo++instance Arbitrary (Sealed2 (FL Prim :> FL Prim)) where+ arbitrary = makeS2Gen ourSmallRepo++instance Arbitrary (Sealed ((FL Prim :> FL Prim) wA)) where+ arbitrary = makeSGen ourSmallRepo++instance Arbitrary (Sealed2 (WithState FileUUIDModel Prim)) where+ arbitrary = makeWS2Gen ourSmallRepo++instance Arbitrary (Sealed (WithState FileUUIDModel Prim wA)) where+ arbitrary = makeWSGen ourSmallRepo++instance Arbitrary (Sealed (WithState FileUUIDModel (FL Prim) wA)) where+ arbitrary = makeWSGen ourSmallRepo++instance Arbitrary (Sealed2 (WithState FileUUIDModel (Prim :> Prim))) where+ arbitrary = do repo <- ourSmallRepo+ WithEndState pp repo' <- aPrimPair repo+ return $ seal2 $ WithState repo pp repo'++instance Arbitrary (Sealed (WithState FileUUIDModel (Prim :> Prim) a)) where+ arbitrary = do repo <- ourSmallRepo+ WithEndState pp repo' <- aPrimPair repo+ return $ seal $ WithState repo pp repo'+++instance Arbitrary (Sealed2 (WithState FileUUIDModel (FL Prim))) where+ arbitrary = makeWS2Gen ourSmallRepo++instance Arbitrary (Sealed2 (WithState FileUUIDModel (FL Prim :> FL Prim))) where+ arbitrary = makeWS2Gen ourSmallRepo++instance Arbitrary (Sealed (WithState FileUUIDModel (FL Prim :> FL Prim) a)) where+ arbitrary = makeWSGen ourSmallRepo
harness/Darcs/Test/Patch/Arbitrary/PrimV1.hs view
@@ -6,6 +6,10 @@ ( commuteTripleFromTree, commutePairFromTree, commutePairFromTWFP , mergePairFromTree, mergePairFromTWFP , patchFromTree )++import Prelude ()+import Darcs.Prelude+ import Darcs.Test.Patch.Arbitrary.Generic import Darcs.Test.Patch.RepoModel @@ -22,12 +26,12 @@ import Darcs.Patch.FileHunk( IsHunk( isHunk ), FileHunk(..) ) import Darcs.Test.Patch.V1Model+import Darcs.Util.Path import Darcs.Test.Util.QuickCheck ( alpha, notIn, maybeOf ) import Darcs.UI.Commands.Replace ( defaultToks ) import Darcs.Patch.Prim -import Control.Applicative ( (<$>) ) import qualified Data.ByteString.Char8 as BC import Data.Maybe ( isJust )
− harness/Darcs/Test/Patch/Arbitrary/PrimV3.hs
@@ -1,259 +0,0 @@--- TODO: Remove these warning disabling flags...-{-# OPTIONS_GHC -w #-}-{-# LANGUAGE CPP, MultiParamTypeClasses, OverloadedStrings #-}-module Darcs.Test.Patch.Arbitrary.PrimV3 where--import qualified Darcs.Test.Patch.Arbitrary.Generic as T- ( commuteTripleFromTree, commutePairFromTree, commutePairFromTWFP- , mergePairFromTree, mergePairFromTWFP- , patchFromTree )-import Darcs.Test.Patch.Arbitrary.Generic-import Darcs.Test.Patch.RepoModel--import Control.Monad ( liftM )-import Test.QuickCheck-import Darcs.Test.Patch.WithState-import Darcs.Patch.Witnesses.Sealed-import Darcs.Patch.Witnesses.Eq-import Darcs.Patch.Witnesses.Unsafe-import Darcs.Patch.Witnesses.Ordered-import Darcs.Patch.Prim.V3 ()-import Darcs.Patch.Prim.V3.Core ( Prim(..), Location, Hunk(..), UUID(..) )-import Darcs.Patch.RepoPatch ( RepoPatch )--import Darcs.Test.Patch.V3Model-import Darcs.Test.Util.QuickCheck ( alpha, notIn, maybeOf )--import Darcs.UI.Commands.Replace ( defaultToks )-import Darcs.Patch.Prim--import Control.Applicative ( (<$>) )-import qualified Data.ByteString.Char8 as BC-import qualified Data.ByteString as BS-import Data.Maybe ( isJust )-import qualified Data.Map as M-import Storage.Hashed.Hash( Hash(..) )--#include "impossible.h"--patchFromTree :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . p wY wZ -> t) -> WithStartState V3Model (Tree Prim) wX -> t-patchFromTree = T.patchFromTree--mergePairFromTree :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . (p :\/: p) wY wZ -> t) -> WithStartState V3Model (Tree Prim) wX -> t-mergePairFromTree = T.mergePairFromTree--mergePairFromTWFP :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . (p :\/: p) wY wZ -> t) -> WithStartState V3Model (TreeWithFlattenPos Prim) wX -> t-mergePairFromTWFP = T.mergePairFromTWFP--commutePairFromTWFP :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . (p :> p) wY wZ -> t) -> WithStartState V3Model (TreeWithFlattenPos Prim) wX -> t-commutePairFromTWFP = T.commutePairFromTWFP--commutePairFromTree :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . (p :> p) wY wZ -> t) -> WithStartState V3Model (Tree Prim) wX -> t-commutePairFromTree = T.commutePairFromTree--commuteTripleFromTree :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . (p :> p :> p) wY wZ -> t) -> WithStartState V3Model (Tree Prim) wX -> t-commuteTripleFromTree = T.commuteTripleFromTree--type instance ModelOf Prim = V3Model-instance ArbitraryPrim Prim where- runCoalesceTests _ = False- hasPrimConstruct _ = False--hunkIdentity (Hunk _ old new) | old == new = unsafeCoerceP IsEq-hunkIdentity _ = NotEq--instance NullPatch Prim where- nullPatch (BinaryHunk _ x) = hunkIdentity x- nullPatch (TextHunk _ x) = hunkIdentity x- nullPatch _ = NotEq--instance Arbitrary (Sealed2 (FL (WithState V3Model Prim))) where- arbitrary = do repo <- ourSmallRepo- liftM (unseal (seal2 . wesPatch)) $ arbitraryState repo---- instance Show1 (TreeWithFlattenPos Prim) where--- showDict1 = ShowDictClass---- WithState and propFail are handy for debugging arbitrary code-propFail :: Int -> Tree Prim wX -> Bool-propFail n xs = sizeTree xs < n--------------------------------------------------------------------------- * QuickCheck generators--aHunk :: forall wX wY . BS.ByteString -> Gen (Hunk wX wY)-aHunk content- = sized $ \n ->- do pos <- choose (0, BS.length content)- let prefixLen = pos- restLen = BS.length content - prefixLen- oldLen <- frequency- [ (75, choose (0, min restLen n))- , (25, choose (0, min 10 restLen))- ]- let nonempty x = if oldLen /= 0 then x else 0- newLen <- frequency- [ ( 54, choose (1,min 1 n) )- , ( nonempty 42, choose (1,min 1 oldLen) )- , ( nonempty 2, return oldLen )- , ( nonempty 2, return 0 )- ]- new <- BS.concat <$> vectorOf newLen aLine- let old = BS.take oldLen $ BS.drop prefixLen $ content- return $ Hunk pos old new--aTextHunk :: forall wX wY . (UUID, Object Fail) -> Gen (Prim wX wY)-aTextHunk (uuid, (Blob text _)) =- do hunk <- aHunk (unFail text)- return $ TextHunk uuid hunk--aManifest :: forall wX wY . UUID -> Location -> Object Fail -> Gen (Prim wX wY)-aManifest uuid loc (Directory dir) =- do newFilename <- aFilename `notIn` (M.keys dir)- return $ Manifest uuid loc--aDemanifest :: forall wX wY . UUID -> Location -> Gen (Prim wX wY)-aDemanifest uuid loc = return $ Demanifest uuid loc---- | Generates any type of 'Prim' patch, except binary and setpref patches.-aPrim :: forall wX wY . V3Model wX -> Gen (WithEndState V3Model (Prim wX) wY)-aPrim repo- = do mbFile <- maybeOf repoFiles- mbDir <- maybeOf repoDirs- mbExisting <- maybeOf $ repoObjects repo- mbManifested <- maybeOf manifested- fresh <- anUUID- filename <- aFilename- dir <- elements (rootDir:repoDirs)- mbOtherDir <- maybeOf repoDirs- let whenfile x = if isJust mbFile then x else 0- whendir x = if isJust mbDir then x else 0- whenexisting x = if isJust mbExisting then x else 0- whenmanifested x = if isJust mbManifested then x else 0- patch <- frequency- [ ( whenfile 12, aTextHunk $ fromJust mbFile )- , ( 2, aTextHunk (fresh, Blob (return "") NoHash) ) -- create an empty thing- , ( whenexisting (whendir 2), -- manifest an existing object- aManifest (fst $ fromJust mbExisting)- (fst $ fromJust mbDir, filename)- (snd $ fromJust mbDir))- , ( whenmanifested 2, uncurry aDemanifest $ fromJust mbManifested )- -- TODO: demanifest- ]- let repo' = unFail $ repoApply repo patch- return $ WithEndState patch repo'- where- manifested = [ (id, (dirid, name)) | (dirid, Directory dir) <- repoDirs- , (name, id) <- M.toList dir ]- repoFiles = [ (id, Blob x y) | (id, Blob x y) <- repoObjects repo ]- repoDirs = [ (id, Directory x) | (id, Directory x) <- repoObjects repo ]- rootDir = (UUID "ROOT", root repo)--------------------------------------------------------------------------- *** Pairs of primitive patches---- Try to generate commutable pairs of hunks-hunkPair :: forall wX wY . (UUID, Object Fail) -> Gen ((Prim :> Prim) wX wY)-hunkPair (uuid, (Blob file _)) =- do h1@(Hunk l1 old1 new1) <- aHunk (unFail file)- (delta, content') <- selectChunk h1 (unFail file)- Hunk l2' old2 new2 <- aHunk content'- let l2 = l2'+delta- return (TextHunk uuid (Hunk l1 old1 new1) :> TextHunk uuid (Hunk l2 old2 new2))- where- selectChunk (Hunk l old new) content = elements [prefix, suffix]- where start = l - 1- prefix = (0, BS.take start content)- suffix = (start + BS.length new, BS.drop (start + BS.length old) content)- selectChunk _ _ = impossible--aPrimPair :: forall wX wY . V3Model wX -> Gen (WithEndState V3Model ((Prim :> Prim) wX) wY)-aPrimPair repo- = do mbFile <- maybeOf repoFiles- frequency- [ ( if isJust mbFile then 1 else 0- , do p1 :> p2 <- hunkPair $ fromJust mbFile- let repo' = unFail $ repoApply repo p1- repo'' = unFail $ repoApply repo' p2- return $ WithEndState (p1 :> p2) repo''- )- , ( 1- , do Sealed wesP <- arbitraryState repo- return $ unsafeCoerceP1 wesP- )- ]- where- repoFiles = [ (id, Blob x y) | (id, Blob x y) <- repoObjects repo ]--------------------------------------------------------------------------- Arbitrary instances--ourSmallRepo :: Gen (V3Model wX)-ourSmallRepo = aSmallRepo--instance ArbitraryState V3Model Prim where- arbitraryState s = seal <$> aPrim s---instance Arbitrary (Sealed2 Prim) where- arbitrary = makeS2Gen ourSmallRepo--instance Arbitrary (Sealed (Prim x)) where- arbitrary = makeSGen ourSmallRepo--instance Arbitrary (Sealed2 (Prim :> Prim)) where- arbitrary = do repo <- ourSmallRepo- WithEndState pp _ <- aPrimPair repo- return $ seal2 pp--instance Arbitrary (Sealed ((Prim :> Prim) wA)) where- arbitrary = do repo <- ourSmallRepo- WithEndState pp _ <- aPrimPair repo- return $ seal pp--instance Arbitrary (Sealed2 (Prim :> Prim :> Prim)) where- arbitrary = makeS2Gen ourSmallRepo--instance Arbitrary (Sealed ((Prim :> Prim :> Prim) a)) where- arbitrary = makeSGen ourSmallRepo--instance Arbitrary (Sealed2 (FL Prim)) where- arbitrary = makeS2Gen ourSmallRepo--instance Arbitrary (Sealed ((FL Prim) wA)) where- arbitrary = makeSGen ourSmallRepo--instance Arbitrary (Sealed2 (FL Prim :> FL Prim)) where- arbitrary = makeS2Gen ourSmallRepo--instance Arbitrary (Sealed ((FL Prim :> FL Prim) wA)) where- arbitrary = makeSGen ourSmallRepo--instance Arbitrary (Sealed2 (WithState V3Model Prim)) where- arbitrary = makeWS2Gen ourSmallRepo--instance Arbitrary (Sealed (WithState V3Model Prim wA)) where- arbitrary = makeWSGen ourSmallRepo--instance Arbitrary (Sealed (WithState V3Model (FL Prim) wA)) where- arbitrary = makeWSGen ourSmallRepo--instance Arbitrary (Sealed2 (WithState V3Model (Prim :> Prim))) where- arbitrary = do repo <- ourSmallRepo- WithEndState pp repo' <- aPrimPair repo- return $ seal2 $ WithState repo pp repo'--instance Arbitrary (Sealed (WithState V3Model (Prim :> Prim) a)) where- arbitrary = do repo <- ourSmallRepo- WithEndState pp repo' <- aPrimPair repo- return $ seal $ WithState repo pp repo'---instance Arbitrary (Sealed2 (WithState V3Model (FL Prim))) where- arbitrary = makeWS2Gen ourSmallRepo--instance Arbitrary (Sealed2 (WithState V3Model (FL Prim :> FL Prim))) where- arbitrary = makeWS2Gen ourSmallRepo--instance Arbitrary (Sealed (WithState V3Model (FL Prim :> FL Prim) a)) where- arbitrary = makeWSGen ourSmallRepo
− harness/Darcs/Test/Patch/Arbitrary/Real.hs
@@ -1,69 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE UndecidableInstances #-}-module Darcs.Test.Patch.Arbitrary.Real where-import Darcs.Test.Patch.Arbitrary.Generic-import Darcs.Test.Patch.Arbitrary.PrimV1 ()-import Darcs.Test.Patch.RepoModel--import Darcs.Patch.Witnesses.Ordered-import Darcs.Patch.Merge ( Merge(..) )-import Darcs.Patch.Patchy ( Patchy, Commute(..) )-import Darcs.Patch.Prim ( PrimPatch, anIdentity )-import Darcs.Patch.V2 ( RealPatch )-import Darcs.Patch.V2.Real ( isDuplicate )--import Test.QuickCheck-import Darcs.Test.Patch.WithState-import Darcs.Patch.Witnesses.Sealed-import Darcs.Patch.Witnesses.Eq-import Darcs.Patch.Prim ( FromPrim(..) )---nontrivialReals :: PrimPatch prim => (RealPatch prim :> RealPatch prim) wX wY -> Bool-nontrivialReals = nontrivialCommute--nontrivialCommute :: (Patchy p, MyEq p) => (p :> p) wX wY -> Bool-nontrivialCommute (x :> y) = case commute (x :> y) of- Just (y' :> x') -> not (y' `unsafeCompare` y) ||- not (x' `unsafeCompare` x)- Nothing -> False--nontrivialMergeReals :: PrimPatch prim => (RealPatch prim :\/: RealPatch prim) wX wY -> Bool-nontrivialMergeReals = nontrivialMerge--nontrivialMerge :: (Patchy p, MyEq p, Merge p) => (p :\/: p) wX wY -> Bool-nontrivialMerge (x :\/: y) = case merge (x :\/: y) of- y' :/\: x' -> not (y' `unsafeCompare` y) ||- not (x' `unsafeCompare` x)--instance MightHaveDuplicate (RealPatch prim) where- hasDuplicate = isDuplicate--instance (RepoModel (ModelOf prim), ArbitraryPrim prim)- => Arbitrary (Sealed2 (FL (RealPatch prim))) where- arbitrary = do Sealed (WithStartState _ tree) <- arbitrary :: Gen (Sealed (WithStartState (ModelOf prim) (Tree prim)))- return $ unseal seal2 (flattenOne tree)--instance (RepoModel (ModelOf prim), ArbitraryPrim prim)- => Arbitrary (Sealed2 (RealPatch prim)) where- arbitrary = do Sealed (WithStartState _ tree) <- arbitrary :: Gen (Sealed (WithStartState (ModelOf prim) (Tree prim)))- case mapFL seal2 `unseal` flattenOne tree of- [] -> return $ seal2 $ fromPrim anIdentity- ps -> elements ps--notDuplicatestriple :: (RealPatch prim :> RealPatch prim :> RealPatch prim) wX wY -> Bool-notDuplicatestriple (a :> b :> c) = not (isDuplicate a || isDuplicate b || isDuplicate c)--nontrivialTriple :: PrimPatch prim => (RealPatch prim :> RealPatch prim :> RealPatch prim) wX wY -> Bool-nontrivialTriple (a :> b :> c) =- case commute (a :> b) of- Nothing -> False- Just (b' :> a') ->- case commute (a' :> c) of- Nothing -> False- Just (c'' :> a'') ->- case commute (b :> c) of- Nothing -> False- Just (c' :> b'') -> (not (a `unsafeCompare` a') || not (b `unsafeCompare` b')) &&- (not (c' `unsafeCompare` c) || not (b'' `unsafeCompare` b)) &&- (not (c'' `unsafeCompare` c) || not (a'' `unsafeCompare` a'))
+ harness/Darcs/Test/Patch/Arbitrary/RepoPatchV1.hs view
@@ -0,0 +1,291 @@+-- Copyright (C) 2002-2003,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.++{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE CPP, FlexibleInstances, TypeSynonymInstances #-}+++module Darcs.Test.Patch.Arbitrary.RepoPatchV1 () where++import Prelude ()+import Darcs.Prelude++import System.IO.Unsafe ( unsafePerformIO )+import Test.QuickCheck+import Control.Monad ( liftM, liftM2, liftM3, liftM4, replicateM )++import Darcs.Patch.Info ( PatchInfo, patchinfo )+import qualified Data.ByteString as B ( ByteString )+import qualified Data.ByteString.Char8 as BC ( pack )++import Darcs.Patch ( addfile, adddir, move,+ hunk, tokreplace, binary,+ changepref, invert, merge )+import Darcs.Patch.V1 ()+import qualified Darcs.Patch.V1.Core as V1 ( RepoPatchV1(..) )+import Darcs.Patch.Prim.V1 ()+import Darcs.Patch.Prim.V1.Core ( Prim(..) )+import Darcs.Patch.Witnesses.Ordered+import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed), unseal, mapSeal, Sealed2(..) )+import Darcs.Patch.Witnesses.Unsafe++-- This definitely feels a bit weird to be importing Properties here, and+-- probably means we want to move this elsewhere, but Darcs.Test.Patch.Check is+-- already taken with something apparently only semi-related+import Darcs.Test.Patch.Properties.Check( checkAPatch )+import Darcs.Test.Patch.Arbitrary.Generic ( MightHaveDuplicate )++#include "impossible.h"++type Patch = V1.RepoPatchV1 Prim++class ArbitraryP p where+ arbitraryP :: Gen (Sealed (p wX))++{-+TODO: there is a lot of overlap in testing between between this module+and Darcs.Test.Patch.QuickCheck++This module tests Prim and V1 patches, and Darcs.Test.Patch.QuickCheck+tests Prim and V2 patches++This module's generator covers a wider set of patch types, but is less+likely to generate conflicts than Darcs.Test.Patch.QuickCheck.++Until this is cleaned up, we take some care that the Arbitrary instances+do not overlap and are only used for tests from the respective+modules.++(There are also tests in other modules that probably depend on the+Arbitrary instances in this module.)+-}++instance Arbitrary (Sealed (Prim wX)) where+ arbitrary = arbitraryP++instance Arbitrary (Sealed (FL Patch wX)) where+ arbitrary = arbitraryP++-- instance Arbitrary (Sealed2 (Prim :> Prim)) where+ -- arbitrary = unseal Sealed2 <$> arbitraryP++instance Arbitrary (Sealed2 (FL Patch)) where+ arbitrary = unseal Sealed2 <$> arbitraryP++instance Arbitrary (Sealed2 (FL Patch :\/: FL Patch)) where+ arbitrary = unseal Sealed2 <$> arbitraryP++instance Arbitrary (Sealed2 (FL Patch :> FL Patch)) where+ arbitrary = unseal Sealed2 <$> arbitraryP++instance Arbitrary (Sealed2 (FL Patch :> FL Patch :> FL Patch)) where+ arbitrary = unseal Sealed2 <$> arbitraryP+++instance (ArbitraryP p1, ArbitraryP p2) => ArbitraryP (p1 :> p2) where+ arbitraryP = do Sealed p1 <- arbitraryP+ Sealed p2 <- arbitraryP+ return (Sealed (p1 :> p2))++instance (ArbitraryP p1, ArbitraryP p2) => ArbitraryP (p1 :\/: p2) where+ arbitraryP = do Sealed p1 <- arbitraryP+ Sealed p2 <- arbitraryP+ return (Sealed (unsafeCoercePEnd p1 :\/: p2))++instance ArbitraryP (FL Patch) where+ arbitraryP = sized arbpatch++instance ArbitraryP Prim where+ arbitraryP = onepatchgen++instance MightHaveDuplicate (V1.RepoPatchV1 prim)++hunkgen :: Gen (Sealed (Prim wX))+hunkgen = do+ i <- frequency [(1,choose (0,5)),(1,choose (0,35)),+ (2,return 0),(3,return 1),(2,return 2),(1,return 3)]+ j <- frequency [(1,choose (0,5)),(1,choose (0,35)),+ (2,return 0),(3,return 1),(2,return 2),(1,return 3)]+ if i == 0 && j == 0 then hunkgen+ else Sealed <$>+ liftM4 hunk filepathgen linenumgen+ (replicateM i filelinegen)+ (replicateM j filelinegen)++tokreplacegen :: Gen (Sealed (Prim wX))+tokreplacegen = do+ f <- filepathgen+ o <- tokengen+ n <- tokengen+ if o == n+ then return $ Sealed $ tokreplace f "A-Za-z" "old" "new"+ else return $ Sealed $ tokreplace f "A-Za-z_" o n++twofilegen :: (forall wY . FilePath -> FilePath -> Prim wX wY) -> Gen (Sealed (Prim wX))+twofilegen p = do+ n1 <- filepathgen+ n2 <- filepathgen+ if n1 /= n2 && checkAPatch (p n1 n2)+ then return $ Sealed $ p n1 n2+ else twofilegen p++chprefgen :: Gen (Sealed (Prim wX))+chprefgen = do+ f <- oneof [return "color", return "movie"]+ o <- tokengen+ n <- tokengen+ if o == n then return $ Sealed $ changepref f "old" "new"+ else return $ Sealed $ changepref f o n++simplepatchgen :: Gen (Sealed (Prim wX))+simplepatchgen = frequency [(1,liftM (Sealed . addfile) filepathgen),+ (1,liftM (Sealed . adddir) filepathgen),+ (1,liftM3 (\x y z -> Sealed (binary x y z)) filepathgen arbitrary arbitrary),+ (1,twofilegen move),+ (1,tokreplacegen),+ (1,chprefgen),+ (7,hunkgen)+ ]++onepatchgen :: Gen (Sealed (Prim wX))+onepatchgen = oneof [simplepatchgen, mapSeal (invert . unsafeCoerceP) `fmap` simplepatchgen]++norecursgen :: Int -> Gen (Sealed (FL Patch wX))+norecursgen 0 = mapSeal (\p -> V1.PP p :>: NilFL) `fmap` onepatchgen+norecursgen n = oneof [mapSeal (\p -> V1.PP p :>: NilFL) `fmap` onepatchgen,flatcompgen n]++arbpatch :: Int -> Gen (Sealed (FL Patch wX))+arbpatch 0 = mapSeal (\p -> V1.PP p :>: NilFL) `fmap` onepatchgen+arbpatch n = frequency [(3,mapSeal (\p -> V1.PP p :>: NilFL) `fmap` onepatchgen),+ (2,flatcompgen n),+ (0,rawMergeGen n),+ (0,mergegen n),+ (1,mapSeal (\p -> V1.PP p :>: NilFL) `fmap` onepatchgen)+ ]++-- | Generate an arbitrary list of at least one element+unempty :: Arbitrary a => Gen [a]+unempty = do+ a <- arbitrary+ as <- arbitrary+ return (a:as)++rawMergeGen :: Int -> Gen (Sealed (FL Patch wX))+rawMergeGen n = do Sealed p1 <- arbpatch len+ Sealed p2 <- arbpatch len+ if checkAPatch (invert p1:>:p2:>:NilFL) &&+ checkAPatch (invert p2:>:p1:>:NilFL)+ then case merge (p2 :\/: p1) of+ _ :/\: p2' -> return (Sealed (unsafeCoercePStart p2'))+ else rawMergeGen n+ where len = if n < 15 then n`div`3 else 3++mergegen :: Int -> Gen (Sealed (FL Patch wX))+mergegen n = do+ Sealed p1 <- norecursgen len+ Sealed p2 <- norecursgen len+ if checkAPatch (invert p1:>:p2:>:NilFL) &&+ checkAPatch (invert p2:>:p1:>:NilFL)+ then case merge (p2:\/:p1) of+ _ :/\: p2' ->+ if checkAPatch (p1+>+p2')+ then return $ Sealed $ p1+>+p2'+ else impossible+ else mergegen n+ where len = if n < 15 then n`div`3 else 3++arbpi :: Gen PatchInfo+arbpi = do n <- unempty+ a <- unempty+ l <- unempty+ d <- unempty+ return $ unsafePerformIO $ patchinfo n d a l++instance Arbitrary PatchInfo where+ arbitrary = arbpi++instance Arbitrary B.ByteString where+ arbitrary = liftM BC.pack arbitrary++flatlistgen :: Int -> Gen (Sealed (FL Patch wX))+flatlistgen 0 = return $ Sealed NilFL+flatlistgen n = do Sealed x <- onepatchgen+ Sealed xs <- flatlistgen (n-1)+ return (Sealed (V1.PP x :>: xs))++flatcompgen :: Int -> Gen (Sealed (FL Patch wX))+flatcompgen n = do+ Sealed ps <- flatlistgen n+ let myp = regularizePatches $ ps+ if checkAPatch myp+ then return $ Sealed myp+ else flatcompgen n++-- resize to size 25, that means we'll get line numbers no greater+-- than 1025 using QuickCheck 2.1+linenumgen :: Gen Int+linenumgen = frequency [(1,return 1), (1,return 2), (1,return 3),+ (3,liftM (\n->1+abs n) (resize 25 arbitrary)) ]++tokengen :: Gen String+tokengen = oneof [return "hello", return "world", return "this",+ return "is", return "a", return "silly",+ return "token", return "test"]++toklinegen :: Gen String+toklinegen = liftM unwords $ replicateM 3 tokengen++filelinegen :: Gen B.ByteString+filelinegen = liftM BC.pack $+ frequency [(1,map fromSafeChar `fmap` arbitrary),(5,toklinegen),+ (1,return ""), (1,return "{"), (1,return "}") ]++filepathgen :: Gen String+filepathgen = liftM fixpath badfpgen++fixpath :: String -> String+fixpath "" = "test"+fixpath p = fpth p++fpth :: String -> String+fpth ('/':'/':cs) = fpth ('/':cs)+fpth (c:cs) = c : fpth cs+fpth [] = []++newtype SafeChar = SS Char+instance Arbitrary SafeChar where+ arbitrary = oneof $ map (return . SS) (['a'..'z']++['A'..'Z']++['1'..'9']++"0")++fromSafeChar :: SafeChar -> Char+fromSafeChar (SS s) = s++badfpgen :: Gen String+badfpgen = frequency [(1,return "test"), (1,return "hello"), (1,return "world"),+ (1,map fromSafeChar `fmap` arbitrary),+ (1,liftM2 (\a b-> a++"/"++b) filepathgen filepathgen) ]++regularizePatches :: FL Patch wX wY -> FL Patch wX wY+regularizePatches patches = rpint (unsafeCoerceP NilFL) patches+ where -- this reverses the list, which seems odd and causes+ -- the witness unsafety+ rpint :: FL Patch wX wY -> FL Patch wA wB -> FL Patch wX wY+ rpint ok_ps NilFL = ok_ps+ rpint ok_ps (p:>:ps) =+ if checkAPatch (unsafeCoerceP p:>:ok_ps)+ then rpint (unsafeCoerceP p:>:ok_ps) ps+ else rpint ok_ps ps+
+ harness/Darcs/Test/Patch/Arbitrary/RepoPatchV2.hs view
@@ -0,0 +1,69 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE UndecidableInstances #-}+module Darcs.Test.Patch.Arbitrary.RepoPatchV2 where+import Darcs.Test.Patch.Arbitrary.Generic+import Darcs.Test.Patch.Arbitrary.PrimV1 ()+import Darcs.Test.Patch.RepoModel++import Darcs.Patch.Witnesses.Ordered+import Darcs.Patch.Merge ( Merge(..) )+import Darcs.Patch.Patchy ( Patchy, Commute(..) )+import Darcs.Patch.Prim ( PrimPatch, anIdentity )+import Darcs.Patch.V2 ( RepoPatchV2 )+import Darcs.Patch.V2.RepoPatch ( isDuplicate )++import Test.QuickCheck+import Darcs.Test.Patch.WithState+import Darcs.Patch.Witnesses.Sealed+import Darcs.Patch.Witnesses.Eq+import Darcs.Patch.Prim ( FromPrim(..) )+++nontrivialRepoPatchV2s :: PrimPatch prim => (RepoPatchV2 prim :> RepoPatchV2 prim) wX wY -> Bool+nontrivialRepoPatchV2s = nontrivialCommute++nontrivialCommute :: (Patchy p, MyEq p) => (p :> p) wX wY -> Bool+nontrivialCommute (x :> y) = case commute (x :> y) of+ Just (y' :> x') -> not (y' `unsafeCompare` y) ||+ not (x' `unsafeCompare` x)+ Nothing -> False++nontrivialMergerepoPatchV2s :: PrimPatch prim => (RepoPatchV2 prim :\/: RepoPatchV2 prim) wX wY -> Bool+nontrivialMergerepoPatchV2s = nontrivialMerge++nontrivialMerge :: (Patchy p, MyEq p, Merge p) => (p :\/: p) wX wY -> Bool+nontrivialMerge (x :\/: y) = case merge (x :\/: y) of+ y' :/\: x' -> not (y' `unsafeCompare` y) ||+ not (x' `unsafeCompare` x)++instance MightHaveDuplicate (RepoPatchV2 prim) where+ hasDuplicate = isDuplicate++instance (RepoModel (ModelOf prim), ArbitraryPrim prim)+ => Arbitrary (Sealed2 (FL (RepoPatchV2 prim))) where+ arbitrary = do Sealed (WithStartState _ tree) <- arbitrary :: Gen (Sealed (WithStartState (ModelOf prim) (Tree prim)))+ return $ unseal seal2 (flattenOne tree)++instance (RepoModel (ModelOf prim), ArbitraryPrim prim)+ => Arbitrary (Sealed2 (RepoPatchV2 prim)) where+ arbitrary = do Sealed (WithStartState _ tree) <- arbitrary :: Gen (Sealed (WithStartState (ModelOf prim) (Tree prim)))+ case mapFL seal2 `unseal` flattenOne tree of+ [] -> return $ seal2 $ fromPrim anIdentity+ ps -> elements ps++notDuplicatestriple :: (RepoPatchV2 prim :> RepoPatchV2 prim :> RepoPatchV2 prim) wX wY -> Bool+notDuplicatestriple (a :> b :> c) = not (isDuplicate a || isDuplicate b || isDuplicate c)++nontrivialTriple :: PrimPatch prim => (RepoPatchV2 prim :> RepoPatchV2 prim :> RepoPatchV2 prim) wX wY -> Bool+nontrivialTriple (a :> b :> c) =+ case commute (a :> b) of+ Nothing -> False+ Just (b' :> a') ->+ case commute (a' :> c) of+ Nothing -> False+ Just (c'' :> a'') ->+ case commute (b :> c) of+ Nothing -> False+ Just (c' :> b'') -> (not (a `unsafeCompare` a') || not (b `unsafeCompare` b')) &&+ (not (c' `unsafeCompare` c) || not (b'' `unsafeCompare` b)) &&+ (not (c'' `unsafeCompare` c) || not (a'' `unsafeCompare` a'))
harness/Darcs/Test/Patch/Examples/Set1.hs view
@@ -35,7 +35,7 @@ , adddir, addfile, hunk, binary, rmdir, rmfile, tokreplace ) import Darcs.Patch.Prim ( PrimOf, FromPrim ) import Darcs.Patch.Prim.V1 ( Prim )-import qualified Darcs.Patch.V1 as V1 ( Patch )+import qualified Darcs.Patch.V1 as V1 ( RepoPatchV1 ) import Darcs.Test.Patch.Properties.Check( checkAPatch ) import Darcs.Patch.Witnesses.Ordered import Darcs.Patch.Witnesses.Sealed ( unsafeUnseal )@@ -43,7 +43,7 @@ #include "impossible.h" -type Patch = V1.Patch Prim+type Patch = V1.RepoPatchV1 Prim -- 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@@ -132,68 +132,68 @@ -- ---------------------------------------------------------------------- -- | Here we provide a set of known interesting commutes.-knownCommutes :: [((FL Patch:<FL Patch) wX wY,(FL Patch:<FL Patch) wX wY)]+knownCommutes :: [((FL Patch :> FL Patch) wX wY,(FL Patch :> FL Patch) wX wY)] knownCommutes = [- (testhunk 1 [] ["A"]:<- testhunk 2 [] ["B"],- testhunk 3 [] ["B"]:<- testhunk 1 [] ["A"]),- (fromPrim (tokreplace "test" "A-Za-z_" "old" "new"):<- testhunk 2+ (testhunk 2 [] ["B"]:>+ testhunk 1 [] ["A"],+ testhunk 1 [] ["A"]:>+ testhunk 3 [] ["B"]),+ (testhunk 2 ["hello world all that is old is good old_"]- ["I don't like old things"],+ ["I don't like old things"]:>+ fromPrim (tokreplace "test" "A-Za-z_" "old" "new"),+ fromPrim (tokreplace "test" "A-Za-z_" "old" "new"):> 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")),+ ["I don't like new things"]),+ (testhunk 2 ["C"] ["D"]:>+ testhunk 1 ["A"] ["B"],+ testhunk 1 ["A"] ["B"]:>+ testhunk 2 ["C"] ["D"]),+ ((quickmerge (fromPrim (addfile "hello"):\/:fromPrim (addfile "hello"))):>+ fromPrim (rmfile "NwNSO"),+ fromPrim (rmfile "NwNSO"):>+ (quickmerge (fromPrim (addfile "hello"):\/:fromPrim (addfile "hello")))), - (quickmerge (testhunk 3 ["o"] ["n"]:\/:- testhunk 3 ["o"] ["v"]):<- testhunk 1 [] ["a"],- testhunk 1 [] ["a"]:<+ (testhunk 1 [] ["a"]:>+ quickmerge (testhunk 3 ["o"] ["n"]:\/:+ testhunk 3 ["o"] ["v"]), quickmerge (testhunk 2 ["o"] ["n"]:\/:- testhunk 2 ["o"] ["v"])),+ testhunk 2 ["o"] ["v"]):>+ testhunk 1 [] ["a"]), - (testhunk 1 ["A"] []:<- testhunk 3 ["B"] [],- testhunk 2 ["B"] []:<- testhunk 1 ["A"] []),+ (testhunk 3 ["B"] []:>+ testhunk 1 ["A"] [],+ testhunk 1 ["A"] []:>+ testhunk 2 ["B"] []), - (testhunk 1 ["A"] ["B"]:<- testhunk 2 ["B"] ["C"],- testhunk 2 ["B"] ["C"]:<- testhunk 1 ["A"] ["B"]),+ (testhunk 2 ["B"] ["C"]:>+ testhunk 1 ["A"] ["B"],+ testhunk 1 ["A"] ["B"]:>+ testhunk 2 ["B"] ["C"]), - (testhunk 1 ["A"] ["B"]:<- testhunk 3 ["B"] ["C"],- testhunk 3 ["B"] ["C"]:<- testhunk 1 ["A"] ["B"]),+ (testhunk 3 ["B"] ["C"]:>+ testhunk 1 ["A"] ["B"],+ testhunk 1 ["A"] ["B"]:>+ testhunk 3 ["B"] ["C"]), - (testhunk 1 ["A"] ["B","C"]:<- testhunk 2 ["B"] ["C","D"],- testhunk 3 ["B"] ["C","D"]:<- testhunk 1 ["A"] ["B","C"])]+ (testhunk 2 ["B"] ["C","D"]:>+ testhunk 1 ["A"] ["B","C"],+ testhunk 1 ["A"] ["B","C"]:>+ testhunk 3 ["B"] ["C","D"])] where testhunk l o n = fromPrim $ hunk "test" l (map BC.pack o) (map BC.pack n) -knownCantCommutes :: [(FL Patch:< FL Patch) wX wY]+knownCantCommutes :: [(FL Patch :> FL Patch) wX wY] knownCantCommutes = [- (testhunk 2 ["o"] ["n"]:<+ (testhunk 1 [] ["A"]:>+ testhunk 2 ["o"] ["n"]),+ (testhunk 1 ["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"))]+ (testhunk 1 [] ["a"]:>+ quickmerge (testhunk 2 ["o"] ["n"]:\/:+ testhunk 2 ["o"] ["v"])),+ (fromPrim (addfile "test"):>+ fromPrim (hunk "test" 1 ([BC.pack "a"]) ([BC.pack "b"])))] where testhunk l o n = fromPrim $ hunk "test" l (map BC.pack o) (map BC.pack n) -- ----------------------------------------------------------------------
harness/Darcs/Test/Patch/Examples/Set2Unwitnessed.hs view
@@ -22,8 +22,8 @@ module Darcs.Test.Patch.Examples.Set2Unwitnessed ( primPermutables, primPatches , commutables, commutablesFL- , realCommutables , realMergeables, realTriples- , realNonduplicateTriples, realPatches, realPatchLoopExamples+ , repov2Commutables , repov2Mergeables, repov2Triples+ , repov2NonduplicateTriples, repov2Patches, repov2PatchLoopExamples ) where import Data.Maybe ( catMaybes )@@ -33,12 +33,12 @@ import Darcs.Patch.Patchy ( Invert(..) ) import Darcs.Patch.Prim ( PrimPatch ) import Darcs.Patch.Prim.V1 ( Prim )-import Darcs.Patch.V2 ( RealPatch )-import Darcs.Patch.V2.Real ( prim2real )+import Darcs.Patch.V2 ( RepoPatchV2 )+import Darcs.Patch.V2.RepoPatch ( prim2repopatchV2 ) -- import Darcs.Test.Patch.Test () -- for instance Eq Patch -- import Darcs.Test.Patch.Examples.Set2Unwitnessed import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )-import qualified Darcs.Test.Patch.Arbitrary.Real as W ( notDuplicatestriple )+import qualified Darcs.Test.Patch.Arbitrary.RepoPatchV2 as W ( notDuplicatestriple ) --import Darcs.Util.Printer ( greenText ) --import Darcs.Util.Printer.Color ( traceDoc ) --import Darcs.Util.Printer.Color ( errorDoc )@@ -48,10 +48,10 @@ import qualified Darcs.Patch.Witnesses.Ordered as W ( (:>), (:\/:) ) import qualified Data.ByteString as B ( ByteString ) import Darcs.Test.Patch.V1Model ( V1Model, Content- , makeRepo, makeName, makeFile)+ , makeRepo, makeFile) import Darcs.Test.Patch.WithState ( WithStartState(..) ) import Darcs.Patch.Prim.V1.Core ( Prim(FP), FilePatchType(Hunk) )-import Darcs.Util.Path ( FileName, fp2fn )+import Darcs.Util.Path ( FileName, fp2fn, makeName ) import Darcs.Patch.Prim ( PrimPatchBase(..), FromPrim ) import Darcs.Patch.Merge ( Merge ) import Darcs.Test.Patch.Arbitrary.Generic@@ -237,8 +237,8 @@ packStringLetters :: String -> [B.ByteString] packStringLetters = map (BC.pack . (:[])) -w_realPatchLoopExamples :: [Sealed (WithStartState V1Model (Tree Prim))]-w_realPatchLoopExamples =+w_repov2PatchLoopExamples :: [Sealed (WithStartState V1Model (Tree Prim))]+w_repov2PatchLoopExamples = [Sealed (WithStartState (makeSimpleRepo fx_name []) $ canonizeTree (ParTree@@ -304,19 +304,19 @@ fx = fp2fn "./F" -mergeExamples :: [Sealed2 (RealPatch Prim :\/: RealPatch Prim)]+mergeExamples :: [Sealed2 (RepoPatchV2 Prim :\/: RepoPatchV2 Prim)] mergeExamples = map (mapSeal2 fromW) w_mergeExamples -realPatchLoopExamples :: [Sealed (WithStartState V1Model (Tree Prim))]-realPatchLoopExamples = w_realPatchLoopExamples+repov2PatchLoopExamples :: [Sealed (WithStartState V1Model (Tree Prim))]+repov2PatchLoopExamples = w_repov2PatchLoopExamples -commuteExamples :: [Sealed2 (RealPatch Prim :> RealPatch Prim)]+commuteExamples :: [Sealed2 (RepoPatchV2 Prim :> RepoPatchV2 Prim)] commuteExamples = map (mapSeal2 fromW) w_commuteExamples -tripleExamples :: [Sealed2 (RealPatch Prim :> RealPatch Prim :> RealPatch Prim)]+tripleExamples :: [Sealed2 (RepoPatchV2 Prim :> RepoPatchV2 Prim :> RepoPatchV2 Prim)] tripleExamples = map (mapSeal2 fromW) w_tripleExamples -notDuplicatestriple :: (RealPatch Prim :> RealPatch Prim :> RealPatch Prim) wX wY -> Bool+notDuplicatestriple :: (RepoPatchV2 Prim :> RepoPatchV2 Prim :> RepoPatchV2 Prim) wX wY -> Bool notDuplicatestriple = W.notDuplicatestriple . toW quickhunk :: PrimPatch prim => Int -> String -> String -> prim wX wY@@ -355,45 +355,45 @@ primPatches = concatMap mergeable2patches mergeables where mergeable2patches (x:\/:y) = [x,y] -realPatches :: [RealPatch Prim wX wY]-realPatches = concatMap commutable2patches realCommutables+repov2Patches :: [RepoPatchV2 Prim wX wY]+repov2Patches = concatMap commutable2patches repov2Commutables where commutable2patches (x:>y) = [x,y] -realTriples :: [(RealPatch Prim :> RealPatch Prim :> RealPatch Prim) wX wY]-realTriples = [ob' :> oa2 :> a2'',- oa' :> oa2 :> a2'']+repov2Triples :: [(RepoPatchV2 Prim :> RepoPatchV2 Prim :> RepoPatchV2 Prim) wX wY]+repov2Triples = [ob' :> oa2 :> a2'',+ oa' :> oa2 :> a2''] ++ map unsafeUnseal2 tripleExamples- ++ map unsafeUnseal2 (concatMap getTriples realFLs)- where oa = prim2real $ quickhunk 1 "o" "aa"+ ++ map unsafeUnseal2 (concatMap getTriples repov2FLs)+ where oa = prim2repopatchV2 $ quickhunk 1 "o" "aa" oa2 = oa- a2 = prim2real $ quickhunk 2 "a34" "2xx"- ob = prim2real $ quickhunk 1 "o" "bb"+ a2 = prim2repopatchV2 $ quickhunk 2 "a34" "2xx"+ ob = prim2repopatchV2 $ quickhunk 1 "o" "bb" ob' :/\: oa' = merge (oa :\/: ob) a2' :/\: _ = merge (ob' :\/: a2) a2'' :/\: _ = merge (oa2 :\/: a2') -realNonduplicateTriples :: [(RealPatch Prim :> RealPatch Prim :> RealPatch Prim) wX wY]-realNonduplicateTriples = filter (notDuplicatestriple) realTriples+repov2NonduplicateTriples :: [(RepoPatchV2 Prim :> RepoPatchV2 Prim :> RepoPatchV2 Prim) wX wY]+repov2NonduplicateTriples = filter (notDuplicatestriple) repov2Triples -realFLs :: [FL (RealPatch Prim) wX wY]-realFLs = [oa :>: invert oa :>: oa :>: invert oa :>: ps +>+ oa :>: invert oa :>: NilFL]- where oa = prim2real $ quickhunk 1 "o" "a"+repov2FLs :: [FL (RepoPatchV2 Prim) wX wY]+repov2FLs = [oa :>: invert oa :>: oa :>: invert oa :>: ps +>+ oa :>: invert oa :>: NilFL]+ where oa = prim2repopatchV2 $ quickhunk 1 "o" "a" ps :/\: _ = merge (oa :>: invert oa :>: NilFL :\/: oa :>: invert oa :>: NilFL) -realCommutables :: [(RealPatch Prim :> RealPatch Prim) wX wY]-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"+repov2Commutables :: [(RepoPatchV2 Prim :> RepoPatchV2 Prim) wX wY]+repov2Commutables = map unsafeUnseal2 commuteExamples+++ map mergeable2commutable repov2Mergeables+++ [invert oa :> ob'] ++ map unsafeUnseal2 (concatMap getPairs repov2FLs)+ where oa = prim2repopatchV2 $ quickhunk 1 "o" "a"+ ob = prim2repopatchV2 $ quickhunk 1 "o" "b" _ :/\: ob' = mergeFL (ob :\/: oa :>: invert oa :>: NilFL) -realMergeables :: [(RealPatch Prim :\/: RealPatch Prim) wX wY]-realMergeables = map (\ (x :\/: y) -> prim2real x :\/: prim2real y) mergeables- ++ realIglooMergeables- ++ realQuickcheckMergeables+repov2Mergeables :: [(RepoPatchV2 Prim :\/: RepoPatchV2 Prim) wX wY]+repov2Mergeables = map (\ (x :\/: y) -> prim2repopatchV2 x :\/: prim2repopatchV2 y) mergeables+ ++ repov2IglooMergeables+ ++ repov2QuickcheckMergeables ++ map unsafeUnseal2 mergeExamples- ++ catMaybes (map pair2m (concatMap getPairs realFLs))+ ++ catMaybes (map pair2m (concatMap getPairs repov2FLs)) ++ [(oa :\/: od), (oa :\/: a2'), (ob' :\/: od''),@@ -410,16 +410,16 @@ (ob'' :\/: og''), (ob'' :\/: oc''), (oc' :\/: od'')]- where oa = prim2real $ quickhunk 1 "o" "aa"- a2 = prim2real $ quickhunk 2 "a34" "2xx"- og = prim2real $ quickhunk 3 "4" "g"- ob = prim2real $ quickhunk 1 "o" "bb"- b2 = prim2real $ quickhunk 2 "b" "2"- oc = prim2real $ quickhunk 1 "o" "cc"- od = prim2real $ quickhunk 7 "x" "d"- oe = prim2real $ quickhunk 7 "x" "e"- pf = prim2real $ quickhunk 7 "x" "f"- od'' = prim2real $ quickhunk 8 "x" "d"+ where oa = prim2repopatchV2 $ quickhunk 1 "o" "aa"+ a2 = prim2repopatchV2 $ quickhunk 2 "a34" "2xx"+ og = prim2repopatchV2 $ quickhunk 3 "4" "g"+ ob = prim2repopatchV2 $ quickhunk 1 "o" "bb"+ b2 = prim2repopatchV2 $ quickhunk 2 "b" "2"+ oc = prim2repopatchV2 $ quickhunk 1 "o" "cc"+ od = prim2repopatchV2 $ quickhunk 7 "x" "d"+ oe = prim2repopatchV2 $ quickhunk 7 "x" "e"+ pf = prim2repopatchV2 $ quickhunk 7 "x" "f"+ od'' = prim2repopatchV2 $ quickhunk 8 "x" "d" ob' :>: b2' :>: NilFL :/\: _ = mergeFL (oa :\/: ob :>: b2 :>: NilFL) a2' :/\: _ = merge (ob' :\/: a2) ob'' :/\: _ = merge (a2 :\/: ob')@@ -431,13 +431,13 @@ oc''' :/\: _ = merge (ob' :\/: oc') oe' :/\: _ = merge (od :\/: oe) of' :/\: _ = merge (od :\/: pf)- pair2m :: Sealed2 (RealPatch Prim :> RealPatch Prim)- -> Maybe ((RealPatch Prim :\/: RealPatch Prim) wX wY)+ pair2m :: Sealed2 (RepoPatchV2 Prim :> RepoPatchV2 Prim)+ -> Maybe ((RepoPatchV2 Prim :\/: RepoPatchV2 Prim) wX wY) pair2m (Sealed2 (xx :> y)) = do y' :> _ <- commute (xx :> y) return $ unsafeCoerceP (xx :\/: y') -realIglooMergeables :: [(RealPatch Prim :\/: RealPatch Prim) wX wY]-realIglooMergeables = [(a :\/: b),+repov2IglooMergeables :: [(RepoPatchV2 Prim :\/: RepoPatchV2 Prim) wX wY]+repov2IglooMergeables = [(a :\/: b), (b :\/: c), (a :\/: c), (x :\/: a),@@ -447,18 +447,18 @@ (z' :\/: y'), (x' :\/: z'), (a :\/: a)]- where a = prim2real $ quickhunk 1 "1" "A"- b = prim2real $ quickhunk 2 "2" "B"- c = prim2real $ quickhunk 3 "3" "C"- x = prim2real $ quickhunk 1 "1BC" "xbc"- y = prim2real $ quickhunk 1 "A2C" "ayc"- z = prim2real $ quickhunk 1 "AB3" "abz"+ where a = prim2repopatchV2 $ quickhunk 1 "1" "A"+ b = prim2repopatchV2 $ quickhunk 2 "2" "B"+ c = prim2repopatchV2 $ quickhunk 3 "3" "C"+ x = prim2repopatchV2 $ quickhunk 1 "1BC" "xbc"+ y = prim2repopatchV2 $ quickhunk 1 "A2C" "ayc"+ z = prim2repopatchV2 $ quickhunk 1 "AB3" "abz" x' :/\: _ = merge (a :\/: x) y' :/\: _ = merge (b :\/: y) z' :/\: _ = merge (c :\/: z) -realQuickcheckMergeables :: [(RealPatch Prim :\/: RealPatch Prim) wX wY]-realQuickcheckMergeables = [-- invert k1 :\/: n1+repov2QuickcheckMergeables :: [(RepoPatchV2 Prim :\/: RepoPatchV2 Prim) wX wY]+repov2QuickcheckMergeables = [-- invert k1 :\/: n1 --, invert k2 :\/: n2 hb :\/: k , b' :\/: b'@@ -467,25 +467,25 @@ , k' :\/: k' , k3 :\/: k3 ] ++ catMaybes (map pair2m pairs)- where hb = prim2real $ quickhunk 0 "" "hb"- k = prim2real $ quickhunk 0 "" "k"- n = prim2real $ quickhunk 0 "" "n"- b = prim2real $ quickhunk 1 "b" ""- d = prim2real $ quickhunk 2 "" "d"+ where hb = prim2repopatchV2 $ quickhunk 0 "" "hb"+ k = prim2repopatchV2 $ quickhunk 0 "" "k"+ n = prim2repopatchV2 $ quickhunk 0 "" "n"+ b = prim2repopatchV2 $ quickhunk 1 "b" ""+ d = prim2repopatchV2 $ quickhunk 2 "" "d" d':/\:_ = merge (b :\/: d) --k1 :>: n1 :>: NilFL :/\: _ = mergeFL (hb :\/: k :>: n :>: NilFL) --k2 :>: n2 :>: NilFL :/\: _ = -- merge (hb :>: b :>: NilFL :\/: k :>: n :>: NilFL) k' :>: n' :>: NilFL :/\: _ :>: b' :>: _ = merge (hb :>: b :>: d' :>: NilFL :\/: k :>: n :>: NilFL) pairs = getPairs (hb :>: b :>: d' :>: k' :>: n' :>: NilFL)- pair2m :: Sealed2 (RealPatch Prim :> RealPatch Prim)- -> Maybe ((RealPatch Prim :\/: RealPatch Prim) wX wY)+ pair2m :: Sealed2 (RepoPatchV2 Prim :> RepoPatchV2 Prim)+ -> Maybe ((RepoPatchV2 Prim :\/: RepoPatchV2 Prim) wX wY) pair2m (Sealed2 (xx :> y)) = do y' :> _ <- commute (xx :> y) return $ unsafeCoerceP (xx :\/: y') - i = prim2real $ quickhunk 0 "" "i"- x = prim2real $ quickhunk 0 "" "x"- xi = prim2real $ quickhunk 0 "xi" ""+ i = prim2repopatchV2 $ quickhunk 0 "" "i"+ x = prim2repopatchV2 $ quickhunk 0 "" "x"+ xi = prim2repopatchV2 $ quickhunk 0 "xi" "" d3 :/\: _ = merge (xi :\/: d) _ :/\: k3 = mergeFL (k :\/: i :>: x :>: xi :>: d3 :>: NilFL)
+ harness/Darcs/Test/Patch/FileUUIDModel.hs view
@@ -0,0 +1,214 @@+{-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-unused-binds -fno-warn-unused-imports -fno-warn-orphans #-}+{-# LANGUAGE CPP, OverloadedStrings, MultiParamTypeClasses, StandaloneDeriving #-}+++-- | Repository model+module Darcs.Test.Patch.FileUUIDModel+ ( FileUUIDModel+ , Object(..)+ , repoApply+ , emptyFile+ , emptyDir+ , nullRepo+ , isEmpty+ , root, repoObjects+ , aFilename, aDirname+ , aLine, aContent+ , aFile, aDir+ , aRepo+ , anUUID+ ) where+++import Darcs.Test.Util.QuickCheck ( alpha, uniques, bSized )+import Darcs.Test.Patch.RepoModel++import Darcs.Patch.Apply( Apply(..), applyToState )+import Darcs.Patch.ApplyMonad( ApplyMonad(..) )+import Darcs.Patch.Prim.FileUUID.Core( UUID(..), Hunk(..), Prim(..), Object(..) )+import Darcs.Patch.Prim.FileUUID.Apply( ObjectMap(..) )+import Darcs.Patch.Witnesses.Sealed ( Sealed, seal )+import Darcs.Patch.Witnesses.Show++import Darcs.Util.Path+import Darcs.Util.Tree( Tree, TreeItem )+import qualified Darcs.Util.Tree as T+import Darcs.Util.Hash( Hash(..) )+import Darcs.Util.Tree.Hashed ( darcsUpdateHashes )++import Control.Applicative ( (<$>) )+import Control.Arrow ( second )+import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as BC+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Lazy.Char8 as BLC+import Data.List ( intercalate, sort )+import qualified Data.Map as M+import Test.QuickCheck+ ( Arbitrary(..)+ , Gen, choose, vectorOf, frequency, oneof )++#include "impossible.h"+++----------------------------------------------------------------------+-- * Model definition++newtype FileUUIDModel wX = FileUUIDModel { repoMap :: ObjectMap Fail }++----------------------------------------+-- Instances++instance Show (Object Fail) where+ show (Directory l) = show l+ show (Blob c _) = show c++deriving instance Eq (Object Fail)++instance Show (FileUUIDModel x) where+ show = showModel++instance Show1 FileUUIDModel where+ showDict1 = ShowDictClass++----------------------------------------------------------------------+-- * Constructors++objectMap :: (Monad m) => M.Map UUID (Object m) -> ObjectMap m+objectMap map = ObjectMap { getObject = get, putObject = put, listObjects = list }+ where list = return $ M.keys map+ put k o = return $ objectMap (M.insert k o map)+ get k = return $ M.lookup k map++emptyRepo :: FileUUIDModel wX+emptyRepo = FileUUIDModel (objectMap M.empty)++emptyFile :: (Monad m) => Object m+emptyFile = Blob (return BS.empty) NoHash++emptyDir :: Object m+emptyDir = Directory M.empty++----------------------------------------------------------------------+-- * Queries++nullRepo :: FileUUIDModel wX -> Bool+nullRepo = null . repoObjects++-- | @isEmpty file@ <=> file content is empty+-- @isEmpty dir@ <=> dir has no child+isEmpty :: Object Fail -> Bool+isEmpty (Directory d) = M.null d+isEmpty (Blob f _) = BS.null $ unFail f++-- | The root directory of a repository.+root :: FileUUIDModel wX -> Object Fail+root (FileUUIDModel repo) = fromJust $ unFail $ getObject repo (UUID "ROOT")++repoObjects :: FileUUIDModel wX -> [(UUID, Object Fail)]+repoObjects (FileUUIDModel repo) = [ (id, obj id) |+ id <- unFail $ listObjects repo, not $ isEmpty $ obj id ]+ where obj id = fromJust $ unFail $ getObject repo id++----------------------------------------------------------------------+-- * Comparing repositories++----------------------------------------------------------------------+-- * QuickCheck generators++-- Testing code assumes that aFilename and aDirname generators +-- will always be able to generate a unique name given a list of+-- existing names. This should be OK as long as the number of possible+-- file/dirnames is much bigger than the number of files/dirs per repository.++-- 'Arbitrary' 'FileUUIDModel' instance is based on the 'aSmallRepo' generator.+++-- | Files are distinguish by ending their names with ".txt".+aFilename :: Gen BS.ByteString+aFilename = do len <- choose (1,maxLength)+ name <- vectorOf len alpha+ return $ BC.pack $ name ++ ".txt"+ where+ maxLength = 3++aDirname :: Gen BS.ByteString+aDirname = do len <- choose (1,maxLength)+ BC.pack <$> vectorOf len alpha+ where+ maxLength = 3++aWord :: Gen BS.ByteString+aWord = do c <- alpha+ return $ BC.pack[c]++aLine :: Gen BS.ByteString+aLine = do wordsNo <- choose (1,2)+ ws <- vectorOf wordsNo aWord+ return $ BC.unwords ws++aContent :: Gen BS.ByteString+aContent = bSized 0 0.5 80 $ \k ->+ do n <- choose (0,k)+ BC.intercalate "\n" <$> vectorOf n aLine++aFile :: (Monad m) => Gen (Object m)+aFile = aContent >>= \c -> return $ Blob (return c) NoHash++aDir :: (Monad m) => [UUID] -> [UUID] -> Gen [(UUID, Object m)]+aDir [] _ = return []+aDir (dirid:dirids) fileids =+ do dirsplit <- choose (1, length dirids)+ filesplit <- choose (1, length fileids)+ let ids = take filesplit fileids+ rem = drop filesplit fileids+ files <- vectorOf filesplit aFile+ names <- vectorOf filesplit aFilename+ dirnames <- vectorOf dirsplit aDirname+ dirs <- subdirs (take dirsplit dirids)+ (drop dirsplit dirids)+ (drop filesplit fileids)+ return $ (dirid, Directory $ M.fromList $ names `zip` ids ++ dirnames `zip` dirids)+ : (fileids `zip` files) ++ dirs+ where subdirs [] _ _ = return []+ subdirs tomake dirs files = do+ dirsplit <- choose (1, length dirs)+ filesplit <- choose (1, length files)+ dir <- aDir (head tomake : take dirsplit dirs) (take filesplit files)+ rem <- subdirs (tail tomake) (drop dirsplit dirs) (drop filesplit files)+ return $ dir ++ rem+++anUUID :: Gen UUID+anUUID = UUID . BC.pack <$> vectorOf 32 (oneof $ map return "0123456789")++-- | @aRepo filesNo dirsNo@ produces repositories with *at most* +-- @filesNo@ files and @dirsNo@ directories. +-- The structure of the repository is aleatory.+aRepo :: Int -- ^ Maximum number of files+ -> Int -- ^ Maximum number of directories+ -> Gen (FileUUIDModel wX)+aRepo maxFiles maxDirs+ = do let minFiles = if maxDirs == 0 && maxFiles > 0 then 1 else 0+ filesNo <- choose (minFiles,maxFiles)+ let minDirs = if filesNo == 0 && maxDirs > 0 then 1 else 0+ dirsNo <- choose (minDirs,maxDirs)+ dirids <- (UUID "ROOT":) <$> uniques dirsNo anUUID+ fileids <- uniques filesNo anUUID+ objectmap <- aDir dirids fileids+ return $ FileUUIDModel $ objectMap $ M.fromList objectmap++-- | Generate small repositories.+-- Small repositories help generating (potentially) conflicting patches.+instance RepoModel FileUUIDModel where+ type RepoState FileUUIDModel = ObjectMap+ aSmallRepo = do filesNo <- frequency [(3, return 1), (1, return 2)]+ dirsNo <- frequency [(3, return 1), (1, return 0)]+ aRepo filesNo dirsNo+ repoApply (FileUUIDModel state) patch = FileUUIDModel <$> applyToState patch state+ showModel model = "FileUUIDModel{\n" ++ unlines (map entry $ repoObjects model) ++ "}"+ where entry (id, obj) = show id ++ " -> " ++ show obj+ eqModel r1 r2 = repoObjects r1 == repoObjects r2++instance Arbitrary (Sealed FileUUIDModel) where+ arbitrary = seal <$> aSmallRepo
harness/Darcs/Test/Patch/Properties/Check.hs view
@@ -20,7 +20,7 @@ effect ) import Darcs.Patch.Invert ( Invert ) import Darcs.Patch.V1 ()-import qualified Darcs.Patch.V1.Core as V1 ( Patch(..) )+import qualified Darcs.Patch.V1.Core as V1 ( RepoPatchV1(..) ) import Darcs.Patch.V1.Core ( isMerger ) import Darcs.Patch.Prim.V1 () import Darcs.Patch.Prim.V1.Core ( Prim(..), DirPatchType(..), FilePatchType(..) )@@ -39,7 +39,7 @@ checkAPatch p = doCheck $ do _ <- checkPatch p checkPatch $ invert p -instance Check (V1.Patch Prim) where+instance Check (V1.RepoPatchV1 Prim) where checkPatch p | isMerger p = do checkPatch $ effect p checkPatch (V1.Merger _ _ _ _) = impossible
harness/Darcs/Test/Patch/Properties/GenericUnwitnessed.hs view
@@ -10,7 +10,7 @@ import Darcs.Test.Patch.RepoModel( RepoModel, RepoState ) import Darcs.Test.Patch.WithState( WithStartState ) -import qualified Darcs.Test.Patch.Properties.Real as W ( propConsistentTreeFlattenings )+import qualified Darcs.Test.Patch.Properties.RepoPatchV2 as W ( propConsistentTreeFlattenings ) import Darcs.Test.Patch.WSub import Darcs.Test.Util.TestResult @@ -24,7 +24,7 @@ import Darcs.Patch.Merge ( Merge ) import Darcs.Patch ( Patchy ) import Darcs.Util.Printer ( Doc, redText, ($$) )-import qualified Storage.Hashed.Tree as HST ( Tree )+import qualified Darcs.Util.Tree as T ( Tree ) permutivity :: (Patchy wp, ShowPatchBasic wp, MyEq wp, WSub wp p)@@ -78,7 +78,7 @@ -> (Prim :> Prim :> Prim) wA wB -> TestResult coalesceCommute f = W.coalesceCommute (fmap toW . f . fromW) . toW -consistentTreeFlattenings :: (RepoState model ~ HST.Tree, RepoModel model)+consistentTreeFlattenings :: (RepoState model ~ T.Tree, RepoModel model) => Sealed (WithStartState model (Tree Prim)) -> TestResult consistentTreeFlattenings = (\x -> if W.propConsistentTreeFlattenings x then succeeded
− harness/Darcs/Test/Patch/Properties/Real.hs
@@ -1,35 +0,0 @@-{-# LANGUAGE CPP #-}-module Darcs.Test.Patch.Properties.Real- ( propConsistentTreeFlattenings ) where--import Darcs.Test.Patch.Arbitrary.Generic ( Tree, flattenTree, G2(..), mapTree )-import Darcs.Test.Patch.WithState-import Darcs.Test.Patch.RepoModel ( RepoModel, repoApply, showModel, eqModel, RepoState- , Fail, maybeFail )-import qualified Storage.Hashed.Tree as HST ( Tree )--import Darcs.Patch.Witnesses.Sealed( Sealed(..) )-import Darcs.Patch.V2.Real( prim2real )-import Darcs.Patch.Prim.V1 ( Prim )--#include "impossible.h"--assertEqualFst :: (RepoModel a, Show b, Show c) => (Fail (a x), b) -> (Fail (a x), c) -> Bool-assertEqualFst (x,bx) (y,by)- | Just x' <- maybeFail x, Just y' <- maybeFail y, x' `eqModel` y' = True- | Nothing <- maybeFail x, Nothing <- maybeFail y = True- | otherwise = error ("Not really equal:\n" ++ showx ++ "\nand\n" ++ showy- ++ "\ncoming from\n" ++ show bx ++ "\nand\n" ++ show by)- where showx | Just x' <- maybeFail x = showModel x'- | otherwise = "Nothing"- showy | Just y' <- maybeFail y = showModel y'- | otherwise = "Nothing"--propConsistentTreeFlattenings :: (RepoState model ~ HST.Tree, RepoModel model)- => Sealed (WithStartState model (Tree Prim)) -> Bool-propConsistentTreeFlattenings (Sealed (WithStartState start t))- = fromJust $- do Sealed (G2 flat) <- return $ flattenTree $ mapTree prim2real t- rms <- return $ map (start `repoApply`) flat- return $ and $ zipWith assertEqualFst (zip rms flat) (tail $ zip rms flat)-
+ harness/Darcs/Test/Patch/Properties/RepoPatchV2.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE CPP #-}+module Darcs.Test.Patch.Properties.RepoPatchV2+ ( propConsistentTreeFlattenings ) where++import Darcs.Test.Patch.Arbitrary.Generic ( Tree, flattenTree, G2(..), mapTree )+import Darcs.Test.Patch.WithState+import Darcs.Test.Patch.RepoModel ( RepoModel, repoApply, showModel, eqModel, RepoState+ , Fail, maybeFail )+import qualified Darcs.Util.Tree as T ( Tree )++import Darcs.Patch.Witnesses.Sealed( Sealed(..) )+import Darcs.Patch.V2.RepoPatch( prim2repopatchV2 )+import Darcs.Patch.Prim.V1 ( Prim )++#include "impossible.h"++assertEqualFst :: (RepoModel a, Show b, Show c) => (Fail (a x), b) -> (Fail (a x), c) -> Bool+assertEqualFst (x,bx) (y,by)+ | Just x' <- maybeFail x, Just y' <- maybeFail y, x' `eqModel` y' = True+ | Nothing <- maybeFail x, Nothing <- maybeFail y = True+ | otherwise = error ("Not really equal:\n" ++ showx ++ "\nand\n" ++ showy+ ++ "\ncoming from\n" ++ show bx ++ "\nand\n" ++ show by)+ where showx | Just x' <- maybeFail x = showModel x'+ | otherwise = "Nothing"+ showy | Just y' <- maybeFail y = showModel y'+ | otherwise = "Nothing"++propConsistentTreeFlattenings :: (RepoState model ~ T.Tree, RepoModel model)+ => Sealed (WithStartState model (Tree Prim)) -> Bool+propConsistentTreeFlattenings (Sealed (WithStartState start t))+ = fromJust $+ do Sealed (G2 flat) <- return $ flattenTree $ mapTree prim2repopatchV2 t+ rms <- return $ map (start `repoApply`) flat+ return $ and $ zipWith assertEqualFst (zip rms flat) (tail $ zip rms flat)+
harness/Darcs/Test/Patch/Properties/V1Set1.hs view
@@ -14,7 +14,7 @@ import Darcs.Patch.Merge ( Merge ) import Darcs.Patch.Read ( ReadPatch ) import Darcs.Patch.Show ( ShowPatchBasic )-import qualified Darcs.Patch.V1 as V1 ( Patch )+import qualified Darcs.Patch.V1 as V1 ( RepoPatchV1 ) import Darcs.Test.Patch.Properties.Check ( checkAPatch, Check ) import Darcs.Util.Printer ( renderPS, RenderMode(..) ) import Darcs.Patch.Witnesses.Eq@@ -27,15 +27,13 @@ import Darcs.Util.Printer ( text ) -type Patch = V1.Patch Prim+type Patch = V1.RepoPatchV1 Prim quickmerge :: (Patchy p, Merge p) => (p :\/: p ) wX wY -> p wY wZ quickmerge (p1:\/:p2) = case merge (p1:\/:p2) of _ :/\: p1' -> unsafeCoercePEnd p1' -instance Show2 p => Show ((p :< p) wX wY) where- show (x :< y) = show2 x ++ " :< " ++ show2 y instance MyEq p => Eq ((p :/\: p) wX wY) where (x :/\: y) == (x' :/\: y') = isIsEq (x =\/= x') && isIsEq (y =\/= y') @@ -74,8 +72,8 @@ _ :/\: p2' -> case merge (p1:\/:p2) of _ :/\: p1' ->- case commute (p1:>p2') of- Just (_:>p1'b) ->+ case commute (p1 :> p2') of+ Just (_ :> p1'b) -> if not $ p1'b `eqFLUnsafe` p1' then failed $ text $ "Merge swapping problem with...\np1 "++ show p1++"merged with\np2 "++@@ -102,28 +100,28 @@ where p1_ = mapFL_FL fromPrim $ concatFL $ mapFL_FL (canonize D.MyersDiff) $ sortCoalesceFL $ effect p1 p1_p = mapFL_FL fromPrim $ concatFL $ mapFL_FL (canonize D.PatienceDiff) $ sortCoalesceFL $ effect p1 -checkCommute :: ((FL Patch:< FL Patch) wX wY, (FL Patch:< FL Patch) wX wY) -> TestResult-checkCommute (p1:<p2,p2':<p1') =- case commute (p2:>p1) of- Just (p1a:>p2a) ->- if (p2a:< p1a) == (p2':< p1')+checkCommute :: ((FL Patch :> FL Patch) wX wY, (FL Patch :> FL Patch) wX wY) -> TestResult+checkCommute (p2 :> p1,p1' :> p2') =+ case commute (p2 :> p1) of+ Just (p1a :> p2a) ->+ if (p1a :> p2a) == (p1' :> p2') then succeeded else failed $ text $ "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 -> failed $ text $ "Commute failed!\n"++show p1++"\n"++show p2 <&&>- case commute (p1':>p2') of- Just (p2a:>p1a) ->- if (p1a:< p2a) == (p1:< p2)+ case commute (p1' :> p2') of+ Just (p2a :> p1a) ->+ if (p2a :> p1a) == (p2 :> p1) then succeeded else failed $ text $ "Commute gave wrong value!\n"++show p2a++"\n"++show p1a ++"should have been\n"++show p2'++"\n"++show p1' Nothing -> failed $ text $ "Commute failed!\n"++show p2'++"\n"++show p1' -checkCantCommute :: (FL Patch:< FL Patch) wX wY -> TestResult-checkCantCommute (p1:<p2) =- case commute (p2:>p1) of+checkCantCommute :: (FL Patch :> FL Patch) wX wY -> TestResult+checkCantCommute (p2 :> p1) =+ case commute (p2 :> p1) of Nothing -> succeeded _ -> failed $ text $ show p1 ++ "\n\n" ++ show p2 ++ "\nArgh, these guys shouldn't commute!\n"
harness/Darcs/Test/Patch/Properties/V1Set2.hs view
@@ -49,7 +49,7 @@ fromPrim, showPatch ) import Darcs.Patch.Commute ( Commute ) import Darcs.Patch.Invert ( Invert )-import qualified Darcs.Patch.V1 as V1 ( Patch )+import qualified Darcs.Patch.V1 as V1 ( RepoPatchV1 ) import Darcs.Patch.V1.Commute ( unravel, merger ) import Darcs.Patch.Prim.V1 () import Darcs.Patch.Prim.V1.Core ( Prim(..) )@@ -63,7 +63,7 @@ #include "impossible.h" -type Patch = V1.Patch Prim+type Patch = V1.RepoPatchV1 Prim -- | Groups a set of tests by giving them the same prefix in their description.@@ -241,14 +241,14 @@ where (names, cs) = unzip subcommutes prop_subcommute c (Sealed2 (p1:>p2)) = does c p1 p2 ==>- case runWrappedCommuteFunction c (p2:< p1) of- Succeeded (p1':<p2') ->- case runWrappedCommuteFunction c (invert p2:< p1') of- Succeeded (p1'':<ip2x') -> isIsEq (p1'' =/\= p1) &&- case runWrappedCommuteFunction c (invert p1:< invert p2) of- Succeeded (ip2':< ip1') ->- case runWrappedCommuteFunction c (p2':< invert p1) of- Succeeded (ip1o':< p2o) -> isJust ((do+ case runWrappedCommuteFunction c (p1 :> p2) of+ Succeeded (p2' :> p1') ->+ case runWrappedCommuteFunction c (p1' :> invert p2) of+ Succeeded (ip2x' :> p1'') -> isIsEq (p1'' =/\= p1) &&+ case runWrappedCommuteFunction c (invert p2 :> invert p1 ) of+ Succeeded (ip1' :> ip2') ->+ case runWrappedCommuteFunction c (invert p1 :> p2') of+ Succeeded (p2o :> ip1o') -> isJust ((do IsEq <- return $ invert ip1' =/\= p1' IsEq <- return $ invert ip2' =/\= p2' IsEq <- return $ ip1o' =/\= ip1'@@ -267,14 +267,14 @@ (names, cs) = unzip . filter ((/= "speedyCommute") . fst) $ subcommutes prop_subcommute c (Sealed2 (p1 :> p2)) = nontrivial c p1 p2 ==>- case runWrappedCommuteFunction c (p2:< p1) of- Succeeded (p1':<p2') ->- case runWrappedCommuteFunction c (invert p2:< p1') of- Succeeded (p1'':<ip2x') -> isIsEq (p1'' =/\= p1) &&- case runWrappedCommuteFunction c (invert p1:< invert p2) of- Succeeded (ip2':< ip1') ->- case runWrappedCommuteFunction c (p2':< invert p1) of- Succeeded (ip1o':< p2o) -> isJust ((do+ case runWrappedCommuteFunction c (p1 :> p2) of+ Succeeded (p2' :> p1') ->+ case runWrappedCommuteFunction c (p1' :> invert p2) of+ Succeeded (ip2x' :> p1'') -> isIsEq (p1'' =/\= p1) &&+ case runWrappedCommuteFunction c (invert p2 :> invert p1) of+ Succeeded (ip1' :> ip2') ->+ case runWrappedCommuteFunction c (invert p1 :> p2') of+ Succeeded (p2o :> ip1o') -> isJust ((do IsEq <- return $ invert ip1' =/\= p1' IsEq <- return $ invert ip2' =/\= p2' IsEq <- return $ ip1o' =/\= ip1'@@ -293,24 +293,24 @@ (names, cs) = unzip . filter ((/= "speedyCommute") . fst) $ subcommutes prop c (Sealed2 (p1 :> p2)) = doesFail c p1 p2 ==>- case runWrappedCommuteFunction c (invert p1 :< invert p2) of+ case runWrappedCommuteFunction c (invert p2 :> invert p1 ) of Failed -> True _ -> False doesFail :: WrappedCommuteFunction -> Prim wX wY -> Prim wY wZ -> Bool doesFail c p1 p2 =- fails (runWrappedCommuteFunction c (p2:<p1)) && checkAPatch (p1 :>: p2 :>: NilFL)+ fails (runWrappedCommuteFunction c (p1 :> p2)) && checkAPatch (p1 :>: p2 :>: NilFL) where fails Failed = True fails _ = False does :: WrappedCommuteFunction -> Prim wX wY -> Prim wY wZ -> Bool does c p1 p2 =- succeeds (runWrappedCommuteFunction c (p2:<p1)) && checkAPatch (p1 :>: p2 :>: NilFL)+ succeeds (runWrappedCommuteFunction c (p1 :> p2)) && checkAPatch (p1 :>: p2 :>: NilFL) where succeeds (Succeeded _) = True succeeds _ = False nontrivial :: WrappedCommuteFunction -> Prim wX wY -> Prim wY wZ -> Bool nontrivial c p1 p2 =- succeeds (runWrappedCommuteFunction c (p2:<p1)) && checkAPatch (p1 :>: p2 :>: NilFL)- where succeeds (Succeeded (p1' :< p2')) = not (p1' `unsafeCompare` p1 && p2' `unsafeCompare` p2)+ succeeds (runWrappedCommuteFunction c (p1 :> p2)) && checkAPatch (p1 :>: p2 :>: NilFL)+ where succeeds (Succeeded (p2' :> p1' )) = not (p1' `unsafeCompare` p1 && p2' `unsafeCompare` p2) succeeds _ = False
harness/Darcs/Test/Patch/Rebase.hs view
@@ -9,7 +9,7 @@ import Darcs.Patch import Darcs.Patch.Conflict-import Darcs.Patch.Rebase+import Darcs.Patch.Rebase.Fixup import Darcs.Patch.Rebase.Viewing import Darcs.Patch.Type import Darcs.Patch.Witnesses.Ordered@@ -17,7 +17,7 @@ import Darcs.Test.Patch.Arbitrary.Generic -testSuite :: forall p . (RepoPatch p, ArbitraryPrim (PrimOf p), Show2 (PrimOf p)) => PatchType p -> [Test]+testSuite :: forall rt p . (RepoPatch p, ArbitraryPrim (PrimOf p), Show2 (PrimOf p)) => PatchType rt p -> [Test] testSuite pt = if hasPrimConstruct (undefined :: PrimOf p WX WX) then@@ -29,7 +29,7 @@ data WX -duplicateConflictedEffect :: forall p . (RepoPatch p, Show2 (PrimOf p)) => PatchType p -> Test+duplicateConflictedEffect :: forall rt p . (RepoPatch p, Show2 (PrimOf p)) => PatchType rt p -> Test duplicateConflictedEffect _ = testCase "duplicate in rebase fixup has a conflicted effect" $ unless (all (/= Okay) cStatuses) $
+ harness/Darcs/Test/Patch/Selection.hs view
@@ -0,0 +1,60 @@+-- Copyright (C) 2016 G. Hoffmann++module Darcs.Test.Patch.Selection ( testSuite ) where++import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit ( testCase )++import Darcs.Patch.Witnesses.Ordered ( FL(..), (:>)(..) )+import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )++import Darcs.Patch.Prim.V1 ( Prim )+import Darcs.Patch.V2 ( RepoPatchV2 )+import Darcs.Patch.RepoType ( RepoType(..), RebaseType(..) )++import Darcs.UI.SelectChanges+ ( PatchSelectionOptions(..)+ , selectionContext+ , runSelection+ , WhichChanges(..)+ )+import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd(..) )+import Darcs.Patch.Info ( rawPatchInfo )+import Darcs.UI.Options.All+ ( Verbosity(..), Summary(..)+ , WithContext(..), SelectDeps(..), MatchFlag(..) )++-- A test module for interactive patch selection.++type Patch = RepoPatchV2 Prim++testSuite :: Test+testSuite = testGroup "Darcs.Patch.Selection" $+ [ dontReadContents whch | whch <- [Last, LastReversed, First, FirstReversed] ]++dontReadContents :: WhichChanges -> Test+dontReadContents whch =+ testCase ("Matching on patch metadata does not open patch contents: " ++ show whch)+ $ do+ let -- here is an FL of patches whose metadata can be read but whose contents+ -- should NEVER be read, otherwise something really bad would happen.+ launchNuclearMissilesPatches = unsafeCoerceP $ lnmPatches [ "P " ++ show i | i <- [1..5::Int] ]+ lnmPatches [] = NilFL+ lnmPatches (n:names) = buildPatch n :>: lnmPatches names+ buildPatch :: String -> PatchInfoAnd ('RepoType 'NoRebase) Patch wX wY+ buildPatch name = PIAP (rawPatchInfo "1999" name "harness" [] False) (error "Patch content read!")+ pso = PatchSelectionOptions+ { verbosity = Quiet+ , matchFlags = [OnePatch "."] -- match on every patch+ , interactive = False+ , selectDeps = AutoDeps+ , summary = NoSummary+ , withContext = NoContext+ }+ context = selectionContext whch "select" pso Nothing Nothing+ (unselected :> selected) <- runSelection launchNuclearMissilesPatches context+ -- Forcing selection to happen (at least to the point of knowing whether unselected+ -- and unselected are NilFL or not) should not evaluate the `undefined` inside of our+ -- patches, ie, we don't need to read too much.+ unselected `seq` selected `seq` return ()+
harness/Darcs/Test/Patch/V1Model.hs view
@@ -3,8 +3,7 @@ -- | Repository model module Darcs.Test.Patch.V1Model- ( module Storage.Hashed.AnchoredPath- , V1Model, repoTree+ ( V1Model, repoTree , RepoItem, File, Dir, Content , makeRepo, emptyRepo , makeFile, emptyFile@@ -25,6 +24,9 @@ ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Test.Util.QuickCheck ( alpha, uniques, bSized ) import Darcs.Test.Patch.RepoModel @@ -32,12 +34,11 @@ import Darcs.Patch.Witnesses.Sealed ( Sealed, seal ) import Darcs.Patch.Witnesses.Show -import Storage.Hashed.AnchoredPath-import Storage.Hashed.Tree( Tree, TreeItem )-import Storage.Hashed.Darcs ( darcsUpdateHashes )-import qualified Storage.Hashed.Tree as T+import Darcs.Util.Path+import Darcs.Util.Tree( Tree, TreeItem )+import Darcs.Util.Tree.Hashed ( darcsUpdateHashes )+import qualified Darcs.Util.Tree as T -import Control.Applicative ( (<$>) ) import Control.Arrow ( second ) import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as BC
− harness/Darcs/Test/Patch/V3Model.hs
@@ -1,215 +0,0 @@-{-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-unused-binds -fno-warn-unused-imports -fno-warn-orphans #-}-{-# LANGUAGE CPP, OverloadedStrings, MultiParamTypeClasses, StandaloneDeriving #-}----- | Repository model-module Darcs.Test.Patch.V3Model- ( module Storage.Hashed.AnchoredPath- , V3Model- , Object(..)- , repoApply- , emptyFile- , emptyDir- , nullRepo- , isEmpty- , root, repoObjects- , aFilename, aDirname- , aLine, aContent- , aFile, aDir- , aRepo- , anUUID- ) where---import Darcs.Test.Util.QuickCheck ( alpha, uniques, bSized )-import Darcs.Test.Patch.RepoModel--import Darcs.Patch.Apply( Apply(..), applyToState )-import Darcs.Patch.ApplyMonad( ApplyMonad(..) )-import Darcs.Patch.Prim.V3.Core( UUID(..), Hunk(..), Prim(..), Object(..) )-import Darcs.Patch.Prim.V3.Apply( ObjectMap(..) )-import Darcs.Patch.Witnesses.Sealed ( Sealed, seal )-import Darcs.Patch.Witnesses.Show--import Storage.Hashed.AnchoredPath-import Storage.Hashed.Tree( Tree, TreeItem )-import Storage.Hashed.Darcs ( darcsUpdateHashes )-import Storage.Hashed.Hash( Hash(..) )-import qualified Storage.Hashed.Tree as T--import Control.Applicative ( (<$>) )-import Control.Arrow ( second )-import qualified Data.ByteString as BS-import qualified Data.ByteString.Char8 as BC-import qualified Data.ByteString.Lazy as BL-import qualified Data.ByteString.Lazy.Char8 as BLC-import Data.List ( intercalate, sort )-import qualified Data.Map as M-import Test.QuickCheck- ( Arbitrary(..)- , Gen, choose, vectorOf, frequency, oneof )--#include "impossible.h"---------------------------------------------------------------------------- * Model definition--newtype V3Model wX = V3Model { repoMap :: ObjectMap Fail }--------------------------------------------- Instances--instance Show (Object Fail) where- show (Directory l) = show l- show (Blob c _) = show c--deriving instance Eq (Object Fail)--instance Show (V3Model x) where- show = showModel--instance Show1 V3Model where- showDict1 = ShowDictClass--------------------------------------------------------------------------- * Constructors--objectMap :: (Monad m) => M.Map UUID (Object m) -> ObjectMap m-objectMap map = ObjectMap { getObject = get, putObject = put, listObjects = list }- where list = return $ M.keys map- put k o = return $ objectMap (M.insert k o map)- get k = return $ M.lookup k map--emptyRepo :: V3Model wX-emptyRepo = V3Model (objectMap M.empty)--emptyFile :: (Monad m) => Object m-emptyFile = Blob (return BS.empty) NoHash--emptyDir :: Object m-emptyDir = Directory M.empty--------------------------------------------------------------------------- * Queries--nullRepo :: V3Model wX -> Bool-nullRepo = null . repoObjects---- | @isEmpty file@ <=> file content is empty--- @isEmpty dir@ <=> dir has no child-isEmpty :: Object Fail -> Bool-isEmpty (Directory d) = M.null d-isEmpty (Blob f _) = BS.null $ unFail f---- | The root directory of a repository.-root :: V3Model wX -> Object Fail-root (V3Model repo) = fromJust $ unFail $ getObject repo (UUID "ROOT")--repoObjects :: V3Model wX -> [(UUID, Object Fail)]-repoObjects (V3Model repo) = [ (id, obj id) |- id <- unFail $ listObjects repo, not $ isEmpty $ obj id ]- where obj id = fromJust $ unFail $ getObject repo id--------------------------------------------------------------------------- * Comparing repositories--------------------------------------------------------------------------- * QuickCheck generators---- Testing code assumes that aFilename and aDirname generators --- will always be able to generate a unique name given a list of--- existing names. This should be OK as long as the number of possible--- file/dirnames is much bigger than the number of files/dirs per repository.---- 'Arbitrary' 'V3Model' instance is based on the 'aSmallRepo' generator.----- | Files are distinguish by ending their names with ".txt".-aFilename :: Gen BS.ByteString-aFilename = do len <- choose (1,maxLength)- name <- vectorOf len alpha- return $ BC.pack $ name ++ ".txt"- where- maxLength = 3--aDirname :: Gen BS.ByteString-aDirname = do len <- choose (1,maxLength)- BC.pack <$> vectorOf len alpha- where- maxLength = 3--aWord :: Gen BS.ByteString-aWord = do c <- alpha- return $ BC.pack[c]--aLine :: Gen BS.ByteString-aLine = do wordsNo <- choose (1,2)- ws <- vectorOf wordsNo aWord- return $ BC.unwords ws--aContent :: Gen BS.ByteString-aContent = bSized 0 0.5 80 $ \k ->- do n <- choose (0,k)- BC.intercalate "\n" <$> vectorOf n aLine--aFile :: (Monad m) => Gen (Object m)-aFile = aContent >>= \c -> return $ Blob (return c) NoHash--aDir :: (Monad m) => [UUID] -> [UUID] -> Gen [(UUID, Object m)]-aDir [] _ = return []-aDir (dirid:dirids) fileids =- do dirsplit <- choose (1, length dirids)- filesplit <- choose (1, length fileids)- let ids = take filesplit fileids- rem = drop filesplit fileids- files <- vectorOf filesplit aFile- names <- vectorOf filesplit aFilename- dirnames <- vectorOf dirsplit aDirname- dirs <- subdirs (take dirsplit dirids)- (drop dirsplit dirids)- (drop filesplit fileids)- return $ (dirid, Directory $ M.fromList $ names `zip` ids ++ dirnames `zip` dirids)- : (fileids `zip` files) ++ dirs- where subdirs [] _ _ = return []- subdirs tomake dirs files = do- dirsplit <- choose (1, length dirs)- filesplit <- choose (1, length files)- dir <- aDir (head tomake : take dirsplit dirs) (take filesplit files)- rem <- subdirs (tail tomake) (drop dirsplit dirs) (drop filesplit files)- return $ dir ++ rem---anUUID :: Gen UUID-anUUID = UUID . BC.pack <$> vectorOf 32 (oneof $ map return "0123456789")---- | @aRepo filesNo dirsNo@ produces repositories with *at most* --- @filesNo@ files and @dirsNo@ directories. --- The structure of the repository is aleatory.-aRepo :: Int -- ^ Maximum number of files- -> Int -- ^ Maximum number of directories- -> Gen (V3Model wX)-aRepo maxFiles maxDirs- = do let minFiles = if maxDirs == 0 && maxFiles > 0 then 1 else 0- filesNo <- choose (minFiles,maxFiles)- let minDirs = if filesNo == 0 && maxDirs > 0 then 1 else 0- dirsNo <- choose (minDirs,maxDirs)- dirids <- (UUID "ROOT":) <$> uniques dirsNo anUUID- fileids <- uniques filesNo anUUID- objectmap <- aDir dirids fileids- return $ V3Model $ objectMap $ M.fromList objectmap---- | Generate small repositories.--- Small repositories help generating (potentially) conflicting patches.-instance RepoModel V3Model where- type RepoState V3Model = ObjectMap- aSmallRepo = do filesNo <- frequency [(3, return 1), (1, return 2)]- dirsNo <- frequency [(3, return 1), (1, return 0)]- aRepo filesNo dirsNo- repoApply (V3Model state) patch = V3Model <$> applyToState patch state- showModel model = "V3Model{\n" ++ unlines (map entry $ repoObjects model) ++ "}"- where entry (id, obj) = show id ++ " -> " ++ show obj- eqModel r1 r2 = repoObjects r1 == repoObjects r2--instance Arbitrary (Sealed V3Model) where- arbitrary = seal <$> aSmallRepo
harness/Darcs/Test/Patch/WSub.hs view
@@ -31,7 +31,7 @@ import Darcs.Patch.Merge ( Merge ) import Darcs.Patch.Prim.V1 ( Prim )-import Darcs.Patch.V2 ( RealPatch )+import Darcs.Patch.V2 ( RepoPatchV2 ) import Darcs.Patch.Patchy ( Commute, Invert(..) ) @@ -81,7 +81,7 @@ toW NilFL = unsafeCoerceP W.NilFL toW (x :>: xs) = unsafeCoercePEnd (toW x) W.:>: unsafeCoercePStart (toW xs) -instance WSub prim prim => WSub (RealPatch prim) (RealPatch prim) where+instance WSub prim prim => WSub (RepoPatchV2 prim) (RepoPatchV2 prim) where fromW = id toW = id @@ -126,10 +126,10 @@ commute :: (WSub wp p, Commute wp) => (p :> p) wX wY -> Maybe ((p :> p) wX wY) commute = fmap fromW . W.commute . toW -getPairs :: FL (RealPatch Prim) wX wY -> [Sealed2 (RealPatch Prim :> RealPatch Prim)]+getPairs :: FL (RepoPatchV2 Prim) wX wY -> [Sealed2 (RepoPatchV2 Prim :> RepoPatchV2 Prim)] getPairs = map (mapSeal2 fromW) . W.getPairs . toW -getTriples :: FL (RealPatch Prim) wX wY -> [Sealed2 (RealPatch Prim :> RealPatch Prim :> RealPatch Prim)]+getTriples :: FL (RepoPatchV2 Prim) wX wY -> [Sealed2 (RepoPatchV2 Prim :> RepoPatchV2 Prim :> RepoPatchV2 Prim)] getTriples = map (mapSeal2 fromW) . W.getTriples . toW coalesce :: (Prim :> Prim) wX wY -> Maybe (FL Prim wX wY)
harness/Darcs/Test/Util/QuickCheck.hs view
@@ -11,7 +11,9 @@ where -import Control.Applicative+import Prelude ()+import Darcs.Prelude+ import Test.QuickCheck.Gen
+ harness/Storage/Hashed/Test.hs view
@@ -0,0 +1,542 @@+{-# LANGUAGE ScopedTypeVariables, FlexibleInstances, CPP #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+module Storage.Hashed.Test( tests ) where++import Prelude hiding ( filter, readFile, writeFile, lookup, (<$>) )+import qualified Prelude+import qualified Data.ByteString.Lazy.Char8 as BL+import qualified Data.ByteString.Char8 as BS+import System.Directory( doesFileExist, removeFile, doesDirectoryExist+ , createDirectory, removeDirectoryRecursive+ , setCurrentDirectory )+import System.FilePath( (</>) )+import Control.Monad.Identity+import Control.Monad.Trans( lift )+import Control.Applicative( (<$>) )+import Control.Exception ( catch, IOException )+import Codec.Archive.Zip( extractFilesFromArchive, toArchive )++import Data.Maybe+import Data.Word+import Data.List( sort, intercalate, intersperse )++import Darcs.Util.Path hiding ( setCurrentDirectory )+import Darcs.Util.Tree hiding ( lookup )+import Darcs.Util.Index+import Darcs.Util.Tree.Hashed+import Darcs.Util.Hash+import Darcs.Util.Tree.Monad hiding ( tree, createDirectory )+import Darcs.Util.Tree.Plain++import System.Mem( performGC )++import qualified Data.Set as S+import qualified Data.Map as M++import qualified Bundled.Posix as Posix+ ( getFileStatus, getSymbolicLinkStatus, fileSize, fileExists )++import Test.HUnit hiding ( path )+import Test.Framework( testGroup )+import qualified Test.Framework as TF ( Test )+import Test.QuickCheck++import Test.Framework.Providers.HUnit+import Test.Framework.Providers.QuickCheck2++------------------------+-- Test Data+--++blobs :: [(AnchoredPath, BL.ByteString)]+blobs = [ (floatPath "foo_a", BL.pack "a\n")+ , (floatPath "foo_dir/foo_a", BL.pack "a\n")+ , (floatPath "foo_dir/foo_b", BL.pack "b\n")+ , (floatPath "foo_dir/foo_subdir/foo_a", BL.pack "a\n")+ , (floatPath "foo space/foo\nnewline", BL.pack "newline\n")+ , (floatPath "foo space/foo\\backslash", BL.pack "backslash\n")+ , (floatPath "foo space/foo_a", BL.pack "a\n") ]++files :: [AnchoredPath]+files = map fst blobs++dirs :: [AnchoredPath]+dirs = [ floatPath "foo_dir"+ , floatPath "foo_dir/foo_subdir"+ , floatPath "foo space" ]++emptyStub :: TreeItem IO+emptyStub = Stub (return emptyTree) NoHash++testTree :: Tree IO+testTree =+ makeTree [ (makeName "foo", emptyStub)+ , (makeName "subtree", SubTree sub)+ , (makeName "substub", Stub getsub NoHash) ]+ where sub = makeTree [ (makeName "stub", emptyStub)+ , (makeName "substub", Stub getsub2 NoHash)+ , (makeName "x", SubTree emptyTree) ]+ getsub = return sub+ getsub2 = return $ makeTree [ (makeName "file", File emptyBlob)+ , (makeName "file2",+ File $ Blob (return $ BL.pack "foo") NoHash) ]++equals_testdata :: Tree IO -> IO ()+equals_testdata t = sequence_ [+ do isJust (findFile t p) @? show p ++ " in tree"+ ours <- readBlob (fromJust $ findFile t p)+ ours @?= stored+ | (p, stored) <- blobs ] >>+ sequence_ [ isJust (Prelude.lookup p blobs) @? show p ++ " extra in tree"+ | (p, File _) <- list t ]++---------------------------+-- Test list+--++tests :: [TF.Test]+tests = [ testGroup "Bundled.Posix" posix+ , testGroup "Darcs.Util.Hash" hash+ , testGroup "Darcs.Util.Tree" tree+ , testGroup "Darcs.Util.Index" index+ , testGroup "Darcs.Util.Tree.Monad" monad+ , testGroup "Hashed Storage" hashed ]++--------------------------+-- Tests+--++hashed :: [TF.Test]+hashed = [ testCase "plain has all files" have_files+ , testCase "pristine has all files" have_pristine_files+ , testCase "pristine has no extras" pristine_no_extra+ , testCase "pristine file contents match" pristine_contents+ , testCase "plain file contents match" plain_contents+ , testCase "writePlainTree works" write_plain ]+ where+ check_file t f = assertBool+ ("path " ++ show f ++ " is missing in tree " ++ show t)+ (isJust $ find t f)+ check_files = forM_ files . check_file++ pristine_no_extra = extractRepoAndRun $+ do+ t <- readDarcsPristine "." >>= expand+ forM_ (list t) $ \(path,_) -> assertBool (show path ++ " is extraneous in tree")+ (path `elem` (dirs ++ files))+ have_files = extractRepoAndRun ( readPlainTree "." >>= expand >>= check_files )+ have_pristine_files = extractRepoAndRun ( readDarcsPristine "." >>= expand >>= check_files )++ pristine_contents = extractRepoAndRun $+ do+ t <- readDarcsPristine "." >>= expand+ equals_testdata t++ plain_contents = extractRepoAndRun $+ do+ t <- expand =<< filter nondarcs `fmap` readPlainTree "."+ equals_testdata t++ write_plain = extractRepoAndRun $+ do+ orig <- readDarcsPristine "." >>= expand+ writePlainTree orig "_darcs/plain"+ t <- expand =<< readPlainTree "_darcs/plain"+ equals_testdata t++index :: [TF.Test]+index = [ testCase "index versioning" check_index_versions+ , testCase "index listing" check_index+ , testCase "index content" check_index_content+ , testProperty "xlate32" prop_xlate32+ , testProperty "xlate64" prop_xlate64+ , testProperty "align bounded" prop_align_bounded+ , testProperty "align aligned" prop_align_aligned ]+ where pristine = readDarcsPristine "." >>= expand+ build_index =+ do x <- pristine+ exist <- doesFileExist "_darcs/index"+ performGC -- required in win32 to trigger file close+ when exist $ removeFile "_darcs/index"+ idx <- updateIndex =<< updateIndexFrom "_darcs/index" darcsTreeHash x+ return (x, idx)+ check_index = extractRepoAndRun $+ do (pris, idx) <- build_index+ (sort $ map fst $ list idx) @?= (sort $ map fst $ list pris)+ check_blob_pair p x y =+ do a <- readBlob x+ b <- readBlob y+ assertEqual ("content match on " ++ show p) a b+ check_index_content = extractRepoAndRun $+ do (_, idx) <- build_index+ plain <- readPlainTree "."+ x <- sequence $ zipCommonFiles check_blob_pair plain idx+ assertBool "files match" (length x > 0)+ check_index_versions = extractRepoAndRun $+ do performGC -- required in win32 to trigger file close+ Prelude.writeFile "_darcs/index" "nonsense index... do not crash!"+ valid <- indexFormatValid "_darcs/index"+ assertBool "index format invalid" $ not valid+ prop_xlate32 x = (xlate32 . xlate32) x == x where _types = x :: Word32+ prop_xlate64 x = (xlate64 . xlate64) x == x where _types = x :: Word64+ prop_align_bounded (bound, x) =+ bound > 0 && bound < 1024 && x >= 0 ==>+ align bound x >= x && align bound x < x + bound+ where _types = (bound, x) :: (Int, Int)+ prop_align_aligned (bound, x) =+ bound > 0 && bound < 1024 && x >= 0 ==>+ align bound x `rem` bound == 0+ where _types = (bound, x) :: (Int, Int)++tree :: [TF.Test]+tree = [ testCase "modifyTree" check_modify+ , testCase "complex modifyTree" check_modify_complex+ , testCase "modifyTree removal" check_modify_remove+ , testCase "expand" check_expand+ , testCase "expandPath" check_expand_path+ , testCase "expandPath of sub" check_expand_path_sub+ , testCase "diffTrees" check_diffTrees+ , testCase "diffTrees identical" check_diffTrees_ident+ , testProperty "expandPath" prop_expandPath+ , testProperty "shapeEq" prop_shape_eq+ , testProperty "expandedShapeEq" prop_expanded_shape_eq+ , testProperty "expand is identity" prop_expand_id+ , testProperty "filter True is identity" prop_filter_id+ , testProperty "filter False is empty" prop_filter_empty+ , testProperty "restrict both ways keeps shape" prop_restrict_shape_commutative+ , testProperty "restrict is a subtree of both" prop_restrict_subtree+ , testProperty "overlay keeps shape" prop_overlay_shape+ , testProperty "overlay is superset of over" prop_overlay_super ]+ where blob x = File $ Blob (return (BL.pack x)) (sha256 $ BL.pack x)+ name = Name . BS.pack+ check_modify =+ let t = makeTree [(name "foo", blob "bar")]+ modify = modifyTree t (floatPath "foo") (Just $ blob "bla")+ in do x <- readBlob $ fromJust $ findFile t (floatPath "foo")+ y <- readBlob $ fromJust $ findFile modify (floatPath "foo")+ assertEqual "old version" x (BL.pack "bar")+ assertEqual "new version" y (BL.pack "bla")+ assertBool "list has foo" $+ isJust (Prelude.lookup (floatPath "foo") $ list modify)+ length (list modify) @?= 1+ check_modify_complex =+ let t = makeTree [ (name "foo", blob "bar")+ , (name "bar", SubTree t1) ]+ t1 = makeTree [ (name "foo", blob "bar") ]+ modify = modifyTree t (floatPath "bar/foo") (Just $ blob "bla")+ in do foo <- readBlob $ fromJust $ findFile t (floatPath "foo")+ foo' <- readBlob $ fromJust $ findFile modify (floatPath "foo")+ bar_foo <- readBlob $ fromJust $+ findFile t (floatPath "bar/foo")+ bar_foo' <- readBlob $ fromJust $+ findFile modify (floatPath "bar/foo")+ assertEqual "old foo" foo (BL.pack "bar")+ assertEqual "old bar/foo" bar_foo (BL.pack "bar")+ assertEqual "new foo" foo' (BL.pack "bar")+ assertEqual "new bar/foo" bar_foo' (BL.pack "bla")+ assertBool "list has bar/foo" $+ isJust (Prelude.lookup (floatPath "bar/foo") $ list modify)+ assertBool "list has foo" $+ isJust (Prelude.lookup (floatPath "foo") $ list modify)+ length (list modify) @?= length (list t)+ check_modify_remove =+ let t1 = makeTree [(name "foo", blob "bar")]+ t2 :: Tree Identity = makeTree [ (name "foo", blob "bar")+ , (name "bar", SubTree t1) ]+ modify1 = modifyTree t1 (floatPath "foo") Nothing+ modify2 = modifyTree t2 (floatPath "bar") Nothing+ file = findFile modify1 (floatPath "foo")+ subtree = findTree modify2 (floatPath "bar")+ in do assertBool "file is gone" (isNothing file)+ assertBool "subtree is gone" (isNothing subtree)++ no_stubs t = null [ () | (_, Stub _ _) <- list t ]+ path = floatPath "substub/substub/file"+ badpath = floatPath "substub/substub/foo"+ check_expand = do+ x <- expand testTree+ assertBool "no stubs in testTree" $ not (no_stubs testTree)+ assertBool "stubs in expanded tree" $ no_stubs x+ assertBool "path reachable" $ path `elem` (map fst $ list x)+ assertBool "badpath not reachable" $+ badpath `notElem` (map fst $ list x)+ check_expand_path = do+ test_exp <- expand testTree+ t <- expandPath testTree path+ t' <- expandPath test_exp path+ t'' <- expandPath testTree $ floatPath "substub/x"+ assertBool "path not reachable in testTree" $ path `notElem` (map fst $ list testTree)+ assertBool "path reachable in t" $ path `elem` (map fst $ list t)+ assertBool "path reachable in t'" $ path `elem` (map fst $ list t')+ assertBool "path reachable in t (with findFile)" $+ isJust $ findFile t path+ assertBool "path reachable in t' (with findFile)" $+ isJust $ findFile t' path+ assertBool "path not reachable in t''" $ path `notElem` (map fst $ list t'')+ assertBool "badpath not reachable in t" $+ badpath `notElem` (map fst $ list t)+ assertBool "badpath not reachable in t'" $+ badpath `notElem` (map fst $ list t')++ check_expand_path_sub = do+ t <- expandPath testTree $ floatPath "substub"+ t' <- expandPath testTree $ floatPath "substub/stub"+ t'' <- expandPath testTree $ floatPath "subtree/stub"+ assertBool "leaf is not a Stub" $+ isNothing (findTree testTree $ floatPath "substub")+ assertBool "leaf is not a Stub" $ isJust (findTree t $ floatPath "substub")+ assertBool "leaf is not a Stub (2)" $ isJust (findTree t' $ floatPath "substub/stub")+ assertBool "leaf is not a Stub (3)" $ isJust (findTree t'' $ floatPath "subtree/stub")++ check_diffTrees = extractRepoAndRun $+ do Prelude.writeFile "foo_dir/foo_a" "b\n"+ working_plain <- filter nondarcs `fmap` readPlainTree "."+ working <- updateIndex =<<+ updateIndexFrom "_darcs/index" darcsTreeHash working_plain+ pristine <- readDarcsPristine "."+ (working', pristine') <- diffTrees working pristine+ let foo_work = findFile working' (floatPath "foo_dir/foo_a")+ foo_pris = findFile pristine' (floatPath "foo_dir/foo_a")+ working' `shapeEq` pristine'+ @? show working' ++ " `shapeEq` " ++ show pristine'+ assertBool "foo_dir/foo_a is in working'" $ isJust foo_work+ assertBool "foo_dir/foo_a is in pristine'" $ isJust foo_pris+ foo_work_c <- readBlob (fromJust foo_work)+ foo_pris_c <- readBlob (fromJust foo_pris)+ BL.unpack foo_work_c @?= "b\n"+ BL.unpack foo_pris_c @?= "a\n"+ assertEqual "working' tree is minimal" 2 (length $ list working')+ assertEqual "pristine' tree is minimal" 2 (length $ list pristine')++ check_diffTrees_ident = do+ pristine <- readDarcsPristine "."+ (t1, t2) <- diffTrees pristine pristine+ assertBool "t1 is empty" $ null (list t1)+ assertBool "t2 is empty" $ null (list t2)++ prop_shape_eq x = no_stubs x ==> x `shapeEq` x+ where _types = x :: Tree Identity+ prop_expanded_shape_eq x = runIdentity $ expandedShapeEq x x+ where _types = x :: Tree Identity+ prop_expand_id x = no_stubs x ==> runIdentity (expand x) `shapeEq` x+ where _types = x :: Tree Identity+ prop_filter_id x = runIdentity $ expandedShapeEq x $ filter (\_ _ -> True) x+ where _types = x :: Tree Identity+ prop_filter_empty x = runIdentity $ expandedShapeEq emptyTree $ filter (\_ _ -> False) x+ where _types = x :: Tree Identity+ prop_restrict_shape_commutative (t1, t2) =+ no_stubs t1 && no_stubs t2 && not (restrict t1 t2 `shapeEq` emptyTree) ==>+ restrict t1 t2 `shapeEq` restrict t2 t1+ where _types = (t1 :: Tree Identity, t2 :: Tree Identity)+ prop_restrict_subtree (t1, t2) =+ no_stubs t1 && not (restrict t1 t2 `shapeEq` emptyTree) ==>+ let restricted = S.fromList (map fst $ list $ restrict t1 t2)+ orig1 = S.fromList (map fst $ list t1)+ orig2 = S.fromList (map fst $ list t2)+ in and [restricted `S.isSubsetOf` orig1, restricted `S.isSubsetOf` orig2]+ where _types = (t1 :: Tree Identity, t2 :: Tree Identity)+ prop_overlay_shape (t1 :: Tree Identity, t2) =+ (Just LT == runIdentity (t2 `cmpExpandedShape` t1)) ==>+ runIdentity $ (t1 `overlay` t2) `expandedShapeEq` t1+ prop_overlay_super (t1 :: Tree Identity, t2) =+ (Just LT == runIdentity (t2 `cmpExpandedShape` t1)) && no_stubs t2 ==>+ Just EQ == (runIdentity $ restrict t2 (t1 `overlay` t2) `cmpTree` t2)+ prop_expandPath (TreeWithPath t p) =+ notStub $ find (runIdentity $ expandPath t p) p+ where notStub (Just (Stub _ _)) = False+ notStub Nothing = error "Did not exist."+ notStub _ = True++hash :: [TF.Test]+hash = [ testProperty "decodeBase16 . encodeBase16 == id" prop_base16 ]+ where prop_base16 x = (decodeBase16 . encodeBase16) x == x++monad :: [TF.Test]+monad = [ testCase "path expansion" check_virtual+ , testCase "rename" check_rename ]+ where check_virtual = virtualTreeMonad run testTree >> return ()+ where run = do file <- readFile (floatPath "substub/substub/file")+ file2 <- readFile (floatPath "substub/substub/file2")+ lift $ BL.unpack file @?= ""+ lift $ BL.unpack file2 @?= "foo"+ check_rename = do (_, t) <- virtualTreeMonad run testTree+ t' <- darcsAddMissingHashes =<< expand t+ forM_ [ (p, i) | (p, i) <- list t' ] $ \(p,i) ->+ assertBool ("have hash: " ++ show p) $ itemHash i /= NoHash+ where run = do rename (floatPath "substub/substub/file") (floatPath "substub/file2")++posix :: [TF.Test]+posix = [ testCase "getFileStatus" $ check_stat Posix.getFileStatus+ , testCase "getSymbolicLinkStatus" $ check_stat Posix.getSymbolicLinkStatus ]+ where check_stat fun = extractRepoAndRun $ do+ x <- Posix.fileSize `fmap` fun "foo_a"+ Prelude.writeFile "test_empty" ""+ y <- Posix.fileSize `fmap` fun "test_empty"+ exist_nonexistent <- Posix.fileExists `fmap` fun "test_does_not_exist"+ exist_existent <- Posix.fileExists `fmap` fun "test_empty"+ assertEqual "file size" x 2+ assertEqual "file size" y 0+ assertBool "existence check" $ not exist_nonexistent+ assertBool "existence check" exist_existent++----------------------------------+-- Arbitrary instances+--++#if !MIN_VERSION_QuickCheck(2,8,2)+-- these instances were added to QuickCheck itself in version 2.8.2+instance (Arbitrary a, Ord a) => Arbitrary (S.Set a)+ where arbitrary = S.fromList `fmap` arbitrary++instance (Arbitrary k, Arbitrary v, Ord k) => Arbitrary (M.Map k v)+ where arbitrary = M.fromList `fmap` arbitrary+#endif++instance Arbitrary BL.ByteString where+ arbitrary = BL.pack `fmap` arbitrary++instance Arbitrary Hash where+ arbitrary = sized hash'+ where hash' 0 = return NoHash+ hash' _ = do+ tag <- oneof [return False, return True]+ case tag of+ False -> SHA256 . BS.pack <$> sequence [ arbitrary | _ <- [1..32] :: [Int] ]+ True -> SHA1 . BS.pack <$> sequence [ arbitrary | _ <- [1..20] :: [Int] ]++instance (Monad m) => Arbitrary (TreeItem m) where+ arbitrary = sized tree'+ where tree' 0 = oneof [ return (File emptyBlob), return (SubTree emptyTree) ]+ tree' n = oneof [ file n, subtree n ]+ file 0 = return (File emptyBlob)+ file _ = do content <- arbitrary+ return (File $ Blob (return content) NoHash)+ subtree n = do branches <- choose (1, n)+ let sub name = do t <- tree' ((n - 1) `div` branches)+ return (makeName $ show name, t)+ sublist <- mapM sub [0..branches]+ oneof [ tree' 0+ , return (SubTree $ makeTree sublist)+ , return $ (Stub $ return (makeTree sublist)) NoHash ]++instance (Monad m) => Arbitrary (Tree m) where+ arbitrary = do item <- arbitrary+ case item of+ File _ -> arbitrary+ Stub _ _ -> arbitrary+ SubTree t -> return t++data TreeWithPath = TreeWithPath (Tree Identity) AnchoredPath deriving (Show)++instance Arbitrary TreeWithPath where+ arbitrary = do t <- arbitrary+ p <- oneof $ return (AnchoredPath []) :+ (map (return . fst) $ list (runIdentity $ expand t))+ return $ TreeWithPath t p++---------------------------+-- Other instances+--++instance Show (Blob m) where+ show (Blob _ h) = "Blob " ++ show h++instance Show (TreeItem m) where+ show (File f) = "File (" ++ show f ++ ")"+ show (Stub _ h) = "Stub _ " ++ show h+ show (SubTree s) = "SubTree (" ++ show s ++ ")"++instance Show (Tree m) where+ show t = "Tree " ++ show (treeHash t) ++ " { " +++ (concat . intersperse ", " $ itemstrs) ++ " }"+ where itemstrs = map show $ listImmediate t++instance Show (Int -> Int) where+ show f = "[" ++ intercalate ", " (map val [1..20]) ++ " ...]"+ where val x = show x ++ " -> " ++ show (f x)++-----------------------+-- Test utilities+--++shapeEq :: Tree m -> Tree m -> Bool+shapeEq a b = Just EQ == cmpShape a b++expandedShapeEq :: (Monad m, Functor m) => Tree m -> Tree m -> m Bool+expandedShapeEq a b = (Just EQ ==) <$> cmpExpandedShape a b++cmpcat :: [Maybe Ordering] -> Maybe Ordering+cmpcat (x:y:rest) | x == y = cmpcat (x:rest)+ | x == Just EQ = cmpcat (y:rest)+ | y == Just EQ = cmpcat (x:rest)+ | otherwise = Nothing+cmpcat [x] = x+cmpcat [] = Just EQ -- empty things are equal++cmpTree :: (Monad m, Functor m) => Tree m -> Tree m -> m (Maybe Ordering)+cmpTree x y = do x' <- expand x+ y' <- expand y+ con <- contentsEq x' y'+ return $ cmpcat [cmpShape x' y', con]+ where contentsEq a b = cmpcat <$> sequence (zipTrees cmp a b)+ cmp _ (Just (File a)) (Just (File b)) = do a' <- readBlob a+ b' <- readBlob b+ return $ Just (compare a' b')+ cmp _ _ _ = return (Just EQ) -- neutral++cmpShape :: Tree m -> Tree m -> Maybe Ordering+cmpShape t r = cmpcat $ zipTrees cmp t r+ where cmp _ (Just a) (Just b) = a `item` b+ cmp _ Nothing (Just _) = Just LT+ cmp _ (Just _) Nothing = Just GT+ cmp _ Nothing Nothing = Just EQ+ item (File _) (File _) = Just EQ+ item (SubTree s) (SubTree p) = s `cmpShape` p+ item _ _ = Nothing++cmpExpandedShape :: (Monad m) => Tree m -> Tree m -> m (Maybe Ordering)+cmpExpandedShape a b = do x <- expand a+ y <- expand b+ return $ x `cmpShape` y++nondarcs :: AnchoredPath -> TreeItem m -> Bool+nondarcs (AnchoredPath (Name x:_)) _ | x == BS.pack "_darcs" = False+ | otherwise = True+nondarcs (AnchoredPath []) _ = True++readDarcsPristine :: FilePath -> IO (Tree IO)+readDarcsPristine dir = do+ let darcs = dir </> "_darcs"+ h_inventory = darcs </> "hashed_inventory"+ repo <- doesDirectoryExist darcs+ unless repo $ fail $ "Not a darcs repository: " ++ dir+ isHashed <- doesFileExist h_inventory+ if isHashed+ then do inv <- BS.readFile h_inventory+ let thelines = BS.split '\n' inv+ case thelines of+ [] -> return emptyTree+ (pris_line:_) -> do+ let thehash = decodeDarcsHash $ BS.drop 9 pris_line+ thesize = decodeDarcsSize $ BS.drop 9 pris_line+ when (thehash == NoHash) $ fail $ "Bad pristine root: " ++ show pris_line+ readDarcsHashed (darcs </> "pristine.hashed") (thesize, thehash)+ else do have_pristine <- doesDirectoryExist $ darcs </> "pristine"+ have_current <- doesDirectoryExist $ darcs </> "current"+ case (have_pristine, have_current) of+ (True, _) -> readPlainTree $ darcs </> "pristine"+ (False, True) -> readPlainTree $ darcs </> "current"+ (_, _) -> fail "No pristine tree is available!"++extractRepoAndRun :: IO a -> IO a+extractRepoAndRun action =+ do zipFile <- toArchive `fmap` BL.readFile "harness/hstestdata.zip"+ removeDirectoryRecursive "_test_playground" `catch` \(_ :: IOException) -> return ()+ createDirectory "_test_playground"+ setCurrentDirectory "_test_playground"+ extractFilesFromArchive [] zipFile+ result <- action+ setCurrentDirectory ".."+ removeDirectoryRecursive "_test_playground"+ return result+
+ harness/hstestdata.zip view
binary file changed (absent → 16669 bytes)
harness/test.hs view
@@ -12,6 +12,7 @@ import qualified Darcs.Test.Misc import qualified Darcs.Test.Patch import qualified Darcs.Test.Email+import qualified Storage.Hashed.Test import Control.Monad ( filterM ) import Control.Exception ( SomeException )@@ -37,6 +38,10 @@ doUnit :: IO [Test] doUnit = return unitTests +-- | TODO make runnable in parallel+doHashed :: IO [Test]+doHashed = return Storage.Hashed.Test.tests+ -- | This is the big list of tests that will be run using testrunner. unitTests :: [Test] unitTests =@@ -138,6 +143,7 @@ runtest t = withTmp $ \dir -> do cp "tests/lib" dir+ cp "tests/network/sshlib" dir cp ("tests" </> testfile t) (dir </> "test") srcdir <- pwd silently $ sub $ cd dir >> runtest' t (toTextIgnore srcdir)@@ -208,7 +214,8 @@ -- harness -- ---------------------------------------------------------------------- -data Config = Config { failing :: Bool+data Config = Config { hashed :: Bool+ , failing :: Bool , shell :: Bool , network :: Bool , unit :: Bool@@ -230,9 +237,10 @@ defaultConfig :: Annotate Ann defaultConfig = record Config{}- [ failing := False += help "Run the failing (shell) tests [no]"+ [ hashed := False += help "Run hashed-storage tests [no]"+ , failing := False += help "Run the failing (shell) tests [no]" , shell := True += help "Run the passing, non-network shell tests [yes]"- , network := False += help "Run the network shell tests [no]"+ , network := True += help "Run the network shell tests [yes]" , unit := True += help "Run the unit tests [yes]" , myers := False += help "Use myers diff [no]" , patience := True += help "Use patience diff [yes]" += name "p"@@ -302,7 +310,8 @@ stests <- shelly $ if shell conf then findShell darcsBin (testDir conf) False diffAlgorithm repoFormat else return [] utests <- if unit conf then doUnit else return [] ntests <- shelly $ if network conf then findNetwork darcsBin (testDir conf) diffAlgorithm repoFormat else return []- defaultMainWithArgs (ftests ++ stests ++ utests ++ ntests) args+ hstests <- if hashed conf then doHashed else return []+ defaultMainWithArgs (ftests ++ stests ++ utests ++ ntests ++ hstests) args where exeSuffix :: String #ifdef WIN32
− hashed-storage/Bundled/Posix.hsc
@@ -1,116 +0,0 @@-{-# LANGUAGE CPP, RankNTypes #-}--module Bundled.Posix( getFdStatus, getSymbolicLinkStatus, getFileStatus- , getFileStatusBS- , fileExists- , modificationTime, fileSize, FileStatus- , EpochTime, isDirectory, isRegularFile ) where--import qualified Data.ByteString.Char8 as BS-#if mingw32_HOST_OS-#else-import Data.ByteString.Unsafe( unsafeUseAsCString )-#endif-import Foreign.Marshal.Alloc ( allocaBytes )-import Foreign.C.Error ( throwErrno, getErrno, eNOENT )-import Foreign.C.Types ( CTime, CInt )-import Foreign.Ptr ( Ptr )--import System.Posix.Internals- ( CStat, c_fstat, sizeof_stat- , st_mode, st_size, st_mtime, s_isdir, s_isreg )-#if mingw32_HOST_OS-import System.Posix.Internals ( c_stat, CFilePath )-#endif--import System.Posix.Types ( Fd(..), CMode, EpochTime )--#if mingw32_HOST_OS-import Foreign.C.String( withCWString, CWString )-#else-import Foreign.C.String ( withCString, CString )-#endif--#if mingw32_HOST_OS-import Data.Int ( Int64 )--type FileOffset = Int64-lstat :: CFilePath -> Ptr CStat -> IO CInt-lstat = c_stat-#else-import System.Posix.Types ( FileOffset )-import System.Posix.Internals( lstat )-#endif--#if mingw32_HOST_OS-bsToPath :: forall a. BS.ByteString -> (CWString -> IO a) -> IO a-bsToPath s f = withCWString (BS.unpack s) f-strToPath :: forall a. String -> (CWString -> IO a) -> IO a-strToPath = withCWString-#else-bsToPath :: forall a. BS.ByteString -> (CString -> IO a) -> IO a-bsToPath = unsafeUseAsCString-strToPath :: forall a. String -> (CString -> IO a) -> IO a-strToPath = withCString-#endif--data FileStatus = FileStatus {- fst_exists :: !Bool,- fst_mode :: !CMode,- fst_mtime :: !CTime,- fst_size :: !FileOffset- }--getFdStatus :: Fd -> IO FileStatus-getFdStatus (Fd fd) = do- do_stat (c_fstat fd)--do_stat :: (Ptr CStat -> IO CInt) -> IO FileStatus-do_stat stat_func = do- allocaBytes sizeof_stat $! \p -> do- ret <- stat_func p- if (ret == -1) then do err <- getErrno- if (err == eNOENT)- then return $! (FileStatus False 0 0 0)- else throwErrno "do_stat"- else do mode <- st_mode p- mtime <- st_mtime p- size <- st_size p- return $! FileStatus True mode mtime size-{-# INLINE do_stat #-}--isDirectory :: FileStatus -> Bool-isDirectory = s_isdir . fst_mode--isRegularFile :: FileStatus -> Bool-isRegularFile = s_isreg . fst_mode--modificationTime :: FileStatus -> EpochTime-modificationTime = fst_mtime--fileSize :: FileStatus -> FileOffset-fileSize = fst_size--fileExists :: FileStatus -> Bool-fileExists = fst_exists--#include <sys/stat.h>---- lstat is broken on win32 with at least GHC 6.10.3-getSymbolicLinkStatus :: FilePath -> IO FileStatus-##if mingw32_HOST_OS-getSymbolicLinkStatus = getFileStatus-##else-getSymbolicLinkStatus fp =- do_stat (\p -> (fp `strToPath` (`lstat` p)))-##endif--getFileStatus :: FilePath -> IO FileStatus-getFileStatus fp =- do_stat (\p -> (fp `strToPath` (`lstat` p)))---- | Requires NULL-terminated bytestring -> unsafe! Use with care.-getFileStatusBS :: BS.ByteString -> IO FileStatus-getFileStatusBS fp =- do_stat (\p -> (fp `bsToPath` (`lstat` p)))-{-# INLINE getFileStatusBS #-}
− hashed-storage/LICENSE
@@ -1,26 +0,0 @@-Copyright Petr Rockai, Jose Neder 2009-2013--All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are met:-- * Redistributions of source code must retain the above copyright- notice, this list of conditions and the following disclaimer.-- * Redistributions in binary form must reproduce the above- copyright notice, this list of conditions and the following- disclaimer in the documentation and/or other materials provided- with the distribution.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
− hashed-storage/Storage/Hashed.hs
@@ -1,51 +0,0 @@-module Storage.Hashed- ( -- * Obtaining Trees.- --- -- | Please note that Trees obtained this way will contain Stub- -- items. These need to be executed (they are IO actions) in order to be- -- accessed. Use 'expand' to do this. However, many operations are- -- perfectly fine to be used on a stubbed Tree (and it is often more- -- efficient to do everything that can be done before expanding a Tree).- readPlainTree, readDarcsHashed-- -- * Blob access.- , readBlob-- -- * Writing trees.- , writePlainTree, writeDarcsHashed-- -- * Unsafe functions for the curious explorer.- --- -- | These are more useful for playing within ghci than for real, serious- -- programs. They generally trade safety for conciseness. Please use- -- responsibly. Don't kill innocent kittens.- , floatPath, printPath ) where--import Storage.Hashed.AnchoredPath-import qualified Data.ByteString.Char8 as BS-import qualified Data.ByteString.Lazy.Char8 as BL-import Storage.Hashed.Tree ( Tree, TreeItem(..), listImmediate, find, readBlob )---- For re-exports.-import Storage.Hashed.Darcs( readDarcsHashed, writeDarcsHashed )-import Storage.Hashed.Plain( readPlainTree, writePlainTree )----------------------------- For explorers------- | Take a relative FilePath within a Tree and print the contents of the--- object there. Useful for exploration, less so for serious programming.-printPath :: Tree IO -> FilePath -> IO ()-printPath t p = print' $ find t (floatPath p)- where print' Nothing = putStrLn $ "ERROR: No object at " ++ p- print' (Just (File b)) = do- putStrLn $ "== Contents of file " ++ p ++ ":"- BL.unpack `fmap` readBlob b >>= putStr- print' (Just (SubTree t')) = do- putStrLn $ "== Listing Tree " ++ p ++ " (immediates only):"- putStr $ unlines $ map BS.unpack $ listNames t'- print' (Just (Stub _ _)) =- putStrLn $ "== (not listing stub at " ++ p ++ ")"- listNames t' = [ n | (Name n, _) <- listImmediate t' ]-
− hashed-storage/Storage/Hashed/AnchoredPath.hs
@@ -1,110 +0,0 @@--- | This module implements relative paths within a Tree. All paths are--- anchored at a certain root (this is usually the Tree root). They are--- represented by a list of Names (these are just strict bytestrings).-module Storage.Hashed.AnchoredPath- ( Name(..), AnchoredPath(..), anchoredRoot, appendPath, anchorPath- , isPrefix, parent, parents, catPaths, flatten, makeName, appendToName- -- * Unsafe functions.- , floatBS, floatPath, replacePrefixPath ) where--import qualified Data.ByteString.Char8 as BS-import Data.List( isPrefixOf, inits )-import System.FilePath( (</>), splitDirectories, normalise, dropTrailingPathSeparator )------------------------------------ AnchoredPath utilities-----newtype Name = Name BS.ByteString deriving (Eq, Show, Ord)---- | This is a type of "sane" file paths. These are always canonic in the sense--- that there are no stray slashes, no ".." components and similar. They are--- usually used to refer to a location within a Tree, but a relative filesystem--- path works just as well. These are either constructed from individual name--- components (using "appendPath", "catPaths" and "makeName"), or converted--- from a FilePath ("floatPath" -- but take care when doing that) or .-newtype AnchoredPath = AnchoredPath [Name] deriving (Eq, Show, Ord)---- | Check whether a path is a prefix of another path.-isPrefix :: AnchoredPath -> AnchoredPath -> Bool-(AnchoredPath a) `isPrefix` (AnchoredPath b) = a `isPrefixOf` b---- | Append an element to the end of a path.-appendPath :: AnchoredPath -> Name -> AnchoredPath-appendPath (AnchoredPath p) n =- case n of- (Name s) | s == BS.empty -> AnchoredPath p- | s == BS.pack "." -> AnchoredPath p- | otherwise -> AnchoredPath $ p ++ [n]---- | Catenate two paths together. Not very safe, but sometimes useful--- (e.g. when you are representing paths relative to a different point than a--- Tree root).-catPaths :: AnchoredPath -> AnchoredPath -> AnchoredPath-catPaths (AnchoredPath p) (AnchoredPath n) = AnchoredPath $ p ++ n---- | Get parent (path) of a given path. foo/bar/baz -> foo/bar-parent :: AnchoredPath -> AnchoredPath-parent (AnchoredPath x) = AnchoredPath (init x)---- | List all parents of a given path. foo/bar/baz -> [foo, foo/bar]-parents :: AnchoredPath -> [AnchoredPath]-parents (AnchoredPath x) = map AnchoredPath . init . inits $ x---- | Take a "root" directory and an anchored path and produce a full--- 'FilePath'. Moreover, you can use @anchorPath \"\"@ to get a relative--- 'FilePath'.-anchorPath :: FilePath -> AnchoredPath -> FilePath-anchorPath dir p = dir </> BS.unpack (flatten p)-{-# INLINE anchorPath #-}---- | Unsafe. Only ever use on bytestrings that came from flatten on a--- pre-existing AnchoredPath.-floatBS :: BS.ByteString -> AnchoredPath-floatBS = AnchoredPath . map Name . takeWhile (not . BS.null) . BS.split '/'--flatten :: AnchoredPath -> BS.ByteString-flatten (AnchoredPath []) = BS.singleton '.'-flatten (AnchoredPath p) = BS.intercalate (BS.singleton '/')- [ n | (Name n) <- p ]--makeName :: String -> Name-makeName ".." = error ".. is not a valid AnchoredPath component name"-makeName n | '/' `elem` n = error "/ may not occur in a valid AnchoredPath component name"- | otherwise = Name $ BS.pack n---- | Take a relative FilePath and turn it into an AnchoredPath. The operation--- is (relatively) unsafe. Basically, by using floatPath, you are testifying--- that the argument is a path relative to some common root -- i.e. the root of--- the associated "Tree" object. Also, there are certain invariants about--- AnchoredPath that this function tries hard to preserve, but probably cannot--- guarantee (i.e. this is a best-effort thing). You should sanitize any--- FilePaths before you declare them "good" by converting into AnchoredPath--- (using this function).-floatPath :: FilePath -> AnchoredPath-floatPath = make . splitDirectories . normalise . dropTrailingPathSeparator- where make ["."] = AnchoredPath []- make x = AnchoredPath $ map (Name . BS.pack) x---anchoredRoot :: AnchoredPath-anchoredRoot = AnchoredPath []---- | Take a prefix path, the changed prefix path, and a path to change.--- Assumes the prefix path is a valid prefix. If prefix is wrong return--- AnchoredPath [].-replacePrefixPath :: AnchoredPath -> AnchoredPath -> AnchoredPath -> AnchoredPath-replacePrefixPath (AnchoredPath []) b c = catPaths b c-replacePrefixPath (AnchoredPath (r:p)) b (AnchoredPath (r':p'))- | r == r' = replacePrefixPath (AnchoredPath p) b (AnchoredPath p')- | otherwise = AnchoredPath []-replacePrefixPath _ _ _ = AnchoredPath []---- | Append a ByteString to the last Name of an AnchoredPath.-appendToName :: AnchoredPath -> String -> AnchoredPath-appendToName (AnchoredPath p) s = AnchoredPath (init p++[Name finalname])- where suffix = BS.pack s- finalname | suffix `elem` (BS.tails lastname) = lastname- | otherwise = BS.append lastname suffix- lastname = case last p of- Name name -> name
− hashed-storage/Storage/Hashed/Darcs.hs
@@ -1,310 +0,0 @@-{-# LANGUAGE ScopedTypeVariables, BangPatterns #-}---- | A few darcs-specific utility functions. These are used for reading and--- writing darcs and darcs-compatible hashed trees.-module Storage.Hashed.Darcs where--import Prelude hiding ( lookup, catch )-import System.FilePath ( (</>) )--import System.Directory( doesFileExist )-import Codec.Compression.GZip( decompress, compress )-import Control.Applicative( (<$>) )-import Control.Exception( catch, IOException )--import qualified Data.ByteString.Char8 as BS8-import qualified Data.ByteString.Lazy.Char8 as BL8-import qualified Data.ByteString.Lazy as BL-import qualified Data.ByteString as BS--import Data.List( sortBy )-import Data.Char( chr, ord, isSpace )-import Data.Maybe( fromJust, isJust )-import Control.Monad.State.Strict--import Storage.Hashed.Tree hiding ( lookup )-import Storage.Hashed.AnchoredPath-import Storage.Hashed.Utils-import Storage.Hashed.Hash-import Storage.Hashed.Packed-import Storage.Hashed.Monad-------------------------------------------------------------------------- Utilities for coping with the darcs directory format.------- | 'darcsDecodeWhite' interprets the Darcs-specific \"encoded\" filenames--- produced by 'darcsEncodeWhite'------ > darcsDecodeWhite "hello\32\there" == "hello there"--- > darcsDecodeWhite "hello\92\there" == "hello\there"--- > darcsDecodeWhite "hello\there" == error "malformed filename"-darcsDecodeWhite :: String -> FilePath-darcsDecodeWhite ('\\':cs) =- case break (=='\\') cs of- (theord, '\\':rest) ->- chr (read theord) : darcsDecodeWhite rest- _ -> error "malformed filename"-darcsDecodeWhite (c:cs) = c: darcsDecodeWhite cs-darcsDecodeWhite "" = ""---- | 'darcsEncodeWhite' 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.------ > darcsEncodeWhite "hello there" == "hello\32\there"--- > darcsEncodeWhite "hello\there" == "hello\92\there"-darcsEncodeWhite :: FilePath -> String-darcsEncodeWhite (c:cs) | isSpace c || c == '\\' =- '\\' : (show $ ord c) ++ "\\" ++ darcsEncodeWhite cs-darcsEncodeWhite (c:cs) = c : darcsEncodeWhite cs-darcsEncodeWhite [] = []--darcsEncodeWhiteBS :: BS8.ByteString -> BS8.ByteString-darcsEncodeWhiteBS = BS8.pack . darcsEncodeWhite . BS8.unpack--decodeDarcsHash :: BS8.ByteString -> Hash-decodeDarcsHash bs = case BS8.split '-' bs of- [s, h] | BS8.length s == 10 -> decodeBase16 h- _ -> decodeBase16 bs--decodeDarcsSize :: BS8.ByteString -> Maybe Int-decodeDarcsSize bs = case BS8.split '-' bs of- [s, _] | BS8.length s == 10 ->- case reads (BS8.unpack s) of- [(x, _)] -> Just x- _ -> Nothing- _ -> Nothing--darcsLocation :: FilePath -> (Maybe Int, Hash) -> FileSegment-darcsLocation dir (s,h) = case hash of- "" -> error "darcsLocation: invalid hash"- _ -> (dir </> prefix s ++ hash, Nothing)- where prefix Nothing = ""- prefix (Just s') = formatSize s' ++ "-"- formatSize s' = let n = show s' in replicate (10 - length n) '0' ++ n- hash = BS8.unpack (encodeBase16 h)--------------------------------------------------- Darcs directory format.-----darcsFormatDir :: Tree m -> Maybe BL8.ByteString-darcsFormatDir t = BL8.fromChunks <$> concat <$>- mapM string (sortBy cmp $ listImmediate t)- where cmp (Name a, _) (Name b, _) = compare a b- string (Name name, item) =- do header <- case item of- File _ -> Just $ BS8.pack "file:\n"- _ -> Just $ BS8.pack "directory:\n"- hash <- case itemHash item of- NoHash -> Nothing- x -> Just $ encodeBase16 x- return $ [ header- , darcsEncodeWhiteBS name- , BS8.singleton '\n'- , hash, BS8.singleton '\n' ]--darcsParseDir :: BL8.ByteString -> [(ItemType, Name, Maybe Int, Hash)]-darcsParseDir content = parse (BL8.split '\n' content)- where- parse (t:n:h':r) = (header t,- Name $ BS8.pack $ darcsDecodeWhite (BL8.unpack n),- decodeDarcsSize hash,- decodeDarcsHash hash) : parse r- where hash = BS8.concat $ BL8.toChunks h'- parse _ = []- header x- | x == BL8.pack "file:" = BlobType- | x == BL8.pack "directory:" = TreeType- | otherwise = error $ "Error parsing darcs hashed dir: " ++ BL8.unpack x--------------------------------------------- Utilities.------- | Compute a darcs-compatible hash value for a tree-like structure.-darcsTreeHash :: Tree m -> Hash-darcsTreeHash t = case darcsFormatDir t of- Nothing -> NoHash- Just x -> sha256 x---- The following two are mostly for experimental use in Packed.--darcsUpdateDirHashes :: Tree m -> Tree m-darcsUpdateDirHashes = updateSubtrees update- where update t = t { treeHash = darcsTreeHash t }--darcsUpdateHashes :: (Monad m, Functor m) => Tree m -> m (Tree m)-darcsUpdateHashes = updateTree update- where update (SubTree t) = return . SubTree $ t { treeHash = darcsTreeHash t }- update (File blob@(Blob con _)) =- do hash <- sha256 <$> readBlob blob- return $ File (Blob con hash)- update stub = return stub--darcsHash :: (Monad m, Functor m) => TreeItem m -> m Hash-darcsHash (SubTree t) = return $ darcsTreeHash t-darcsHash (File blob) = sha256 <$> readBlob blob-darcsHash _ = return NoHash--darcsAddMissingHashes :: (Monad m, Functor m) => Tree m -> m (Tree m)-darcsAddMissingHashes = addMissingHashes darcsHash------------------------------------------------ Reading darcs pristine data------- | Read and parse a darcs-style hashed directory listing from a given @dir@--- and with a given @hash@.-readDarcsHashedDir :: FilePath -> (Maybe Int, Hash) -> IO [(ItemType, Name, Maybe Int, Hash)]-readDarcsHashedDir dir h = do- exist <- doesFileExist $ fst (darcsLocation dir h)- unless exist $ fail $ "error opening " ++ fst (darcsLocation dir h)- compressed <- readSegment $ darcsLocation dir h- let content = decompress compressed- return $ if BL8.null compressed- then []- else darcsParseDir content---- | Read in a darcs-style hashed tree. This is mainly useful for reading--- \"pristine.hashed\". You need to provide the root hash you are interested in--- (found in _darcs/hashed_inventory).-readDarcsHashed' :: Bool -> FilePath -> (Maybe Int, Hash) -> IO (Tree IO)-readDarcsHashed' _ _ (_, NoHash) = fail "Cannot readDarcsHashed NoHash"-readDarcsHashed' sizefail dir root@(_, hash) = do- items' <- readDarcsHashedDir dir root- subs <- sequence [- do when (sizefail && isJust s) $- fail ("Unexpectedly encountered size-prefixed hash in " ++ dir)- case tp of- BlobType -> return (d, File $- Blob (readBlob' (s, h)) h)- TreeType ->- do let t = readDarcsHashed dir (s, h)- return (d, Stub t h)- | (tp, d, s, h) <- items' ]- return $ makeTreeWithHash subs hash- where readBlob' = fmap decompress . readSegment . darcsLocation dir--readDarcsHashed :: FilePath -> (Maybe Int, Hash) -> IO (Tree IO)-readDarcsHashed = readDarcsHashed' False--readDarcsHashedNosize :: FilePath -> Hash -> IO (Tree IO)-readDarcsHashedNosize dir hash = readDarcsHashed' True dir (Nothing, hash)--------------------------------------------------------- Writing darcs-style hashed trees.------- | Write a Tree into a darcs-style hashed directory.-writeDarcsHashed :: Tree IO -> FilePath -> IO Hash-writeDarcsHashed tree' dir =- do t <- darcsUpdateDirHashes <$> expand tree'- sequence_ [ dump =<< readBlob b | (_, File b) <- list t ]- let dirs = darcsFormatDir t : [ darcsFormatDir d | (_, SubTree d) <- list t ]- _ <- mapM dump $ map fromJust dirs- return $ darcsTreeHash t- where dump bits =- do let name = dir </> BS8.unpack (encodeBase16 $ sha256 bits)- exist <- doesFileExist name- unless exist $ BL.writeFile name (compress bits)---- | Create a hashed file from a 'FilePath' and content. In case the file exists--- it is kept untouched and is assumed to have the right content. XXX Corrupt--- files should be probably renamed out of the way automatically or something--- (probably when they are being read though).-fsCreateHashedFile :: FilePath -> BL8.ByteString -> TreeIO ()-fsCreateHashedFile fn content =- liftIO $ do- exist <- doesFileExist fn- unless exist $ BL.writeFile fn content---- | Run a 'TreeIO' @action@ in a hashed setting. The @initial@ tree is assumed--- to be fully available from the @directory@, and any changes will be written--- out to same. Please note that actual filesystem files are never removed.-hashedTreeIO :: TreeIO a -- ^ action- -> Tree IO -- ^ initial- -> FilePath -- ^ directory- -> IO (a, Tree IO)-hashedTreeIO action t dir =- do runTreeMonad action $ initialState t darcsHash updateItem- where updateItem _ (File b) = File <$> updateFile b- updateItem _ (SubTree s) = SubTree <$> updateSub s- updateItem _ x = return x-- updateFile b@(Blob _ !h) = do- content <- liftIO $ readBlob b- let fn = dir </> BS8.unpack (encodeBase16 h)- nblob = Blob (decompress <$> rblob) h- rblob = BL.fromChunks <$> return <$> BS.readFile fn- newcontent = compress content- fsCreateHashedFile fn newcontent- return nblob- updateSub s = do- let !hash = treeHash s- Just dirdata = darcsFormatDir s- fn = dir </> BS8.unpack (encodeBase16 hash)- fsCreateHashedFile fn (compress dirdata)- return s------------------------------------------------------------------- Reading and writing packed pristine. EXPERIMENTAL.--------- | Read a Tree in the darcs hashed format from an object storage. This is--- basically the same as readDarcsHashed from Storage.Hashed, but uses an--- object storage instead of traditional darcs filesystem layout. Requires the--- tree root hash as a starting point.-readPackedDarcsPristine :: OS -> Hash -> IO (Tree IO)-readPackedDarcsPristine os root =- do items' <- darcsParseDir <$> grab root- subs <- sequence [- case tp of- BlobType -> return (d, File $ file h)- TreeType -> let t = readPackedDarcsPristine os h- in return (d, Stub t h)- | (tp, d, _, h) <- items' ]- return $ makeTreeWithHash subs root- where file h = Blob (grab h) h- grab hash = do maybeseg <- lookup os hash- case maybeseg of- Nothing -> fail $ "hash " ++ BS8.unpack (encodeBase16 hash) ++ " not available"- Just seg -> readSegment seg---- | Write a Tree into an object storage, using the darcs-style directory--- formatting (and therefore darcs-style hashes). Gives back the object storage--- and the root hash of the stored Tree. NB. The function expects that the Tree--- comes equipped with darcs-style hashes already!-writePackedDarcsPristine :: Tree IO -> OS -> IO (OS, Hash)-writePackedDarcsPristine tree' os =- do t <- darcsUpdateDirHashes <$> expand tree'- files <- sequence [ readBlob b | (_, File b) <- list t ]- let dirs = darcsFormatDir t : [ darcsFormatDir d | (_, SubTree d) <- list t ]- os' <- hatch os $ files ++ (map fromJust dirs)- return (os', darcsTreeHash t)--storePackedDarcsPristine :: Tree IO -> OS -> IO (OS, Hash)-storePackedDarcsPristine tree' os =- do (os', root) <- writePackedDarcsPristine tree' os- return $ (os' { roots = root : roots os'- -- FIXME we probably don't want to override the references- -- thing completely here...- , references = darcsPristineRefs }, root)--darcsPristineRefs :: FileSegment -> IO [Hash]-darcsPristineRefs fs = do- con <- (darcsParseDir <$> readSegment fs) `catch` \(_ :: IOException) -> return []- return $! [ hash | (_, _, _, hash) <- con, valid hash ]- where valid NoHash = False- valid _ = True--darcsCheckExpand :: Tree IO- -> IO (Either [(FilePath, Hash, Maybe Hash)] (Tree IO))-darcsCheckExpand t = do- problemsOrTree <- checkExpand darcsHash t- case problemsOrTree of- Left problems -> return . Left $ map render problems- Right tree' -> return . Right $ tree'- where- render (path, h, h') = (anchorPath "." path, h, h')
− hashed-storage/Storage/Hashed/Diff.hs
@@ -1,136 +0,0 @@-module Storage.Hashed.Diff where--import Prelude hiding ( lookup, filter )-import qualified Data.ByteString.Lazy.Char8 as BL-import Storage.Hashed.Tree-import Storage.Hashed.AnchoredPath-import Data.List.LCS-import Data.List ( groupBy )--unidiff :: Tree IO -> Tree IO -> IO BL.ByteString-unidiff l r =- do (from, to) <- diffTrees l r- diffs <- sequence $ zipCommonFiles diff from to- return $ BL.concat diffs- where diff p a b = do x <- readBlob a- y <- readBlob b- return $ diff' p x y- diff' p x y =- case unifiedDiff x y of- x' | BL.null x' -> BL.empty- | otherwise ->- (BL.pack $ "--- " ++ anchorPath "old" p ++ "\n" ++- "+++ " ++ anchorPath "new" p ++ "\n")- `BL.append` x'--type Line = BL.ByteString-data WeaveLine = Common Line- | Remove Line- | Add Line- | Replace Line Line- | Skip Int deriving Show---- | A weave -- two files woven together, with common and differing regions--- marked up. Cf. 'WeaveLine'.-type Weave = [WeaveLine]---- | Sort of a sub-weave.-type Hunk = [WeaveLine]---- | Produce unified diff (in a string form, ie. formatted) from a pair of--- bytestrings.-unifiedDiff :: BL.ByteString -> BL.ByteString -> BL.ByteString-unifiedDiff a b = printUnified $ concat unifiedHunks- where unifiedHunks = reduceContext 3 $ map unifyHunk $ hunks $ weave a b---- | Weave two bytestrings. Intermediate data structure for the actual unidiff--- implementation. No skips are produced.-weave :: BL.ByteString -> BL.ByteString -> Weave-weave a' b' = weave' left common right- where left = init' (BL.split '\n' a') -- drop trailing newline- right = init' (BL.split '\n' b') -- drop trailing newline- init' [] = []- init' x = init x- common = lcs left right- weave' [] [] [] = []- weave' [] c [] = error $ "oops: Left & Right empty, Common: " ++ show c- weave' [] [] (b:bs) = Add b : weave' [] [] bs- weave' (a:as) [] [] = Remove a : weave' as [] []- weave' (a:as) [] (b:bs) = Replace a b : weave' as [] bs- weave' (a:as) (c:cs) (b:bs)- | a == c && b == c = Common a : weave' as cs bs- | a == c && b /= c = Add b : weave' (a:as) (c:cs) bs- | a /= c && b == c = Remove a : weave' as (c:cs) (b:bs)- | a /= c && b /= c = Replace a b : weave' as (c:cs) bs- | otherwise = error "oops!"- weave' a c b = error $ "oops: \nLeft: " ++ show a ++ "\nCommon: " ++ show c ++ "\nRight: " ++ show b---- | Break up a 'Weave' into 'Hunk's.-hunks :: Weave -> [Hunk]-hunks = groupBy grp- where grp (Common _) (Common _) = True- grp (Common _) _ = False- grp _ (Common _) = False- grp _ _ = True---- | Reformat a 'Hunk' into a format suitable for unified diff. Replaces are--- turned into add/remove pairs, all removals in a hunk go before all--- adds. 'Hunk's of 'Common' lines are left intact. Produces input suitable for--- 'reduceContext'.-unifyHunk :: Hunk -> Hunk-unifyHunk h = case h of- (Common _:_) -> h- _ -> reorder $ concatMap breakup h- where reorder h' = [ Remove a | Remove a <- h' ] ++ [ Add a | Add a <- h' ]- breakup (Replace f t) = [Remove f, Add t]- breakup x = [x]---- | Break up a 'Weave' into unified 'Hunk's, leaving @n@ lines of context around--- every hunk. Consecutive 'Common' lines not used as context are replaced with--- 'Skip's.-reduceContext :: Int -> [Hunk] -> [Hunk]-reduceContext n hs =- case hs of- [] -> []- [Common _:_] -> []- [x] -> [x]- [h,t] -> [reduce 0 n h, reduce n 0 t]- (h:rest) -> reduce 0 n h :- map (reduce n n) (init rest) ++- [reduce n 0 $ last rest]- where- reduce s e h@(Common _:_)- | length h <= s + e = h- | otherwise = take s h ++- [Skip $ length h - e - s ] ++- drop (length h - e) h- reduce _ _ h = h---- | Format a 'Weave' for printing.-deweave :: Weave -> BL.ByteString-deweave = BL.unlines . map disp- where disp (Common l) = BL.cons ' ' l- disp (Remove l) = BL.cons '-' l- disp (Add l) = BL.cons '+' l- disp (Replace _ t) = BL.cons '!' t- disp (Skip n) = BL.pack $ "-- skip " ++ show n ++ " lines --"---- | Print a \"hunked\" weave in form of an unified diff. 'Hunk' boundaries are--- marked up as 'Skip' lines. Cf. 'reduceContext'.-printUnified :: Weave -> BL.ByteString-printUnified hunked = printHunks 1 1 $ groupBy splits hunked- where splits (Skip _) _ = False- splits _ (Skip _) = False- splits _ _ = True- printHunks _ _ [] = BL.empty- printHunks l r ([Skip n]:rest) =- printHunks (n+l) (n+r) rest- printHunks l r (h:rest) =- (BL.pack $ "@@ -" ++ show l ++ "," ++- show (removals h) ++ " +" ++ show r ++- "," ++ show (adds h) ++ " @@\n")- `BL.append` deweave h `BL.append`- printHunks (l + removals h) (r + adds h) rest- commons h = length [ () | (Common _) <- h ]- adds h = commons h + length [ () | (Add _) <- h ]- removals h = commons h + length [ () | (Remove _) <- h ]
− hashed-storage/Storage/Hashed/Hash.hs
@@ -1,80 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-module Storage.Hashed.Hash( Hash(..), encodeBase64u, decodeBase64u- , encodeBase16, decodeBase16, sha256, rawHash- , match ) where--import qualified Crypto.Hash.SHA256 as SHA256 ( hash )-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as BL-import qualified Data.ByteString.Char8 as BS8--import qualified Codec.Binary.Base64Url as B64U-import qualified Codec.Binary.Base16 as B16--import Data.Maybe( isJust, fromJust )-import Data.Char( toLower, toUpper )--import Data.Data( Data )-import Data.Typeable( Typeable )--data Hash = SHA256 !BS.ByteString- | SHA1 !BS.ByteString- | NoHash- deriving (Show, Eq, Ord, Read, Typeable, Data)--base16 :: BS.ByteString -> BS.ByteString-debase16 :: BS.ByteString -> Maybe BS.ByteString-base64u :: BS.ByteString -> BS.ByteString-debase64u :: BS.ByteString -> Maybe BS.ByteString--base16 = BS8.map toLower . B16.b16_enc-base64u = B64U.encode-debase64u bs = case B64U.decode bs of- Right s -> Just s- Left _ -> Nothing-debase16 bs = case B16.b16_dec $ BS8.map toUpper bs of- Right (s, _) -> Just s- Left _ -> Nothing--encodeBase64u :: Hash -> BS.ByteString-encodeBase64u (SHA256 bs) = base64u bs-encodeBase64u (SHA1 bs) = base64u bs-encodeBase64u NoHash = BS.empty---- | Produce a base16 (ascii-hex) encoded string from a hash. This can be--- turned back into a Hash (see "decodeBase16". This is a loss-less process.-encodeBase16 :: Hash -> BS.ByteString-encodeBase16 (SHA256 bs) = base16 bs-encodeBase16 (SHA1 bs) = base16 bs-encodeBase16 NoHash = BS.empty---- | Take a base64/url-encoded string and decode it as a "Hash". If the string--- is malformed, yields NoHash.-decodeBase64u :: BS.ByteString -> Hash-decodeBase64u bs- | BS.length bs == 44 && isJust (debase64u bs) = SHA256 (fromJust $ debase64u bs)- | BS.length bs == 28 && isJust (debase64u bs) = SHA1 (fromJust $ debase64u bs)- | otherwise = NoHash---- | Take a base16-encoded string and decode it as a "Hash". If the string is--- malformed, yields NoHash.-decodeBase16 :: BS.ByteString -> Hash-decodeBase16 bs | BS.length bs == 64 && isJust (debase16 bs) = SHA256 (fromJust $ debase16 bs)- | BS.length bs == 40 && isJust (debase16 bs) = SHA1 (fromJust $ debase16 bs)- | otherwise = NoHash---- | Compute a sha256 of a (lazy) ByteString. However, although this works--- correctly for any bytestring, it is only efficient if the bytestring only--- has a sigle chunk.-sha256 :: BL.ByteString -> Hash-sha256 bits = SHA256 $ SHA256.hash $ BS.concat $ BL.toChunks bits--rawHash :: Hash -> BS.ByteString-rawHash NoHash = error "Cannot obtain raw hash from NoHash."-rawHash (SHA1 s) = s-rawHash (SHA256 s) = s--match :: Hash -> Hash -> Bool-NoHash `match` _ = False-_ `match` NoHash = False-x `match` y = x == y
− hashed-storage/Storage/Hashed/Index.hs
@@ -1,525 +0,0 @@-{-# LANGUAGE CPP, ScopedTypeVariables, MultiParamTypeClasses #-}---- | This module contains plain tree indexing code. The index itself is a--- CACHE: you should only ever use it as an optimisation and never as a primary--- storage. In practice, this means that when we change index format, the--- application is expected to throw the old index away and build a fresh--- index. Please note that tracking index validity is out of scope for this--- library: this is responsibility of your application. It is advisable that in--- your validity tracking code, you also check for format validity (see--- 'indexFormatValid') and scrap and re-create index when needed.------ The index is a binary file that overlays a hashed tree over the working--- copy. This means that every working file and directory has an entry in the--- index, that contains its path and hash and validity data. The validity data--- is a timestamp plus the file size. The file hashes are sha256's of the--- file's content. It also contains the fileid to track moved files.------ There are two entry types, a file entry and a directory entry. Both have a--- common binary format (see 'Item'). The on-disk format is best described by--- the section /Index format/ below.------ For each file, the index has a copy of the file's last modification--- timestamp taken at the instant when the hash has been computed. This means--- that when file size and timestamp of a file in working copy matches those in--- the index, we assume that the hash stored in the index for given file is--- valid. These hashes are then exposed in the resulting 'Tree' object, and can--- be leveraged by eg. 'diffTrees' to compare many files quickly.------ You may have noticed that we also keep hashes of directories. These are--- assumed to be valid whenever the complete subtree has been valid. At any--- point, as soon as a size or timestamp mismatch is found, the working file in--- question is opened, its hash (and timestamp and size) is recomputed and--- updated in-place in the index file (everything lives at a fixed offset and--- is fixed size, so this isn't an issue). This is also true of directories:--- when a file in a directory changes hash, this triggers recomputation of all--- of its parent directory hashes; moreover this is done efficiently -- each--- directory is updated at most once during an update run.------ /Index format/------ The Index is organised into \"lines\" where each line describes a single--- indexed item. Cf. 'Item'.------ The first word on the index \"line\" is the length of the file path (which is--- the only variable-length part of the line). Then comes the path itself, then--- fixed-length hash (sha256) of the file in question, then three words, one for--- size, one for "aux", which is used differently for directories and for files, and--- one for the fileid (inode or fhandle) of the file.------ With directories, this aux holds the offset of the next sibling line in the--- index, so we can efficiently skip reading the whole subtree starting at a--- given directory (by just seeking aux bytes forward). The lines are--- pre-ordered with respect to directory structure -- the directory comes first--- and after it come all its items. Cf. 'readIndex''.------ For files, the aux field holds a timestamp.--module Storage.Hashed.Index( readIndex, updateIndexFrom, indexFormatValid- , updateIndex, listFileIDs, Index, filter- , getFileID )- where--import Prelude hiding ( lookup, readFile, writeFile, filter, catch )-import Storage.Hashed.Utils-import Storage.Hashed.Tree-import Storage.Hashed.AnchoredPath-import Data.Int( Int64, Int32 )--import Bundled.Posix( getFileStatusBS, modificationTime,- getFileStatus, fileSize, fileExists, isDirectory )-import System.IO.MMap( mmapFileForeignPtr, mmapFileByteString, Mode(..) )-import System.IO( )-import System.Directory( doesFileExist, getCurrentDirectory, doesDirectoryExist )-#if mingw32_HOST_OS-import System.Directory( renameFile )-import System.FilePath( (<.>) )-#else-import System.Directory( removeFile )-#endif-import System.FilePath( (</>) )-import System.Posix.Types ( FileID )--import Control.Monad( when )-import Control.Exception( catch, SomeException )-import Control.Applicative( (<$>) )--import qualified Data.ByteString as BS-import qualified Data.ByteString.Char8 as BSC-import Data.ByteString.Unsafe( unsafeHead, unsafeDrop )-import Data.ByteString.Internal( toForeignPtr, fromForeignPtr, memcpy- , nullForeignPtr, c2w )--import Data.IORef( )-import Data.Maybe( fromJust, isJust, fromMaybe )-import Data.Bits( Bits )--import Foreign.Storable-import Foreign.ForeignPtr-import Foreign.Ptr--import Storage.Hashed.Hash( sha256, rawHash )-#ifdef WIN32-import System.Win32.File ( createFile, getFileInformationByHandle, BY_HANDLE_FILE_INFORMATION(..),- fILE_SHARE_NONE, fILE_FLAG_BACKUP_SEMANTICS,- gENERIC_NONE, oPEN_EXISTING, closeHandle )-#else-import System.PosixCompat ( fileID, getSymbolicLinkStatus )-#endif------------------------------- Indexed trees------- | Description of a a single indexed item. The structure itself does not--- contain any data, just pointers to the underlying mmap (bytestring is a--- pointer + offset + length).------ The structure is recursive-ish (as opposed to flat-ish structure, which is--- used by git...) It turns out that it's hard to efficiently read a flat index--- with our internal data structures -- we need to turn the flat index into a--- recursive Tree object, which is rather expensive... As a bonus, we can also--- efficiently implement subtree queries this way (cf. 'readIndex').-data Item = Item { iBase :: !(Ptr ())- , iHashAndDescriptor :: !BS.ByteString- } deriving Show--size_magic :: Int-size_magic = 4 -- the magic word, first 4 bytes of the index--size_dsclen, size_hash, size_size, size_aux, size_fileid :: Int-size_size = 8 -- file/directory size (Int64)-size_aux = 8 -- aux (Int64)-size_fileid = 8 -- fileid (inode or fhandle FileID)-size_dsclen = 4 -- this many bytes store the length of the path-size_hash = 32 -- hash representation--off_size, off_aux, off_hash, off_dsc, off_dsclen, off_fileid :: Int-off_size = 0-off_aux = off_size + size_size-off_fileid = off_aux + size_aux-off_dsclen = off_fileid + size_fileid-off_hash = off_dsclen + size_dsclen-off_dsc = off_hash + size_hash--itemAllocSize :: AnchoredPath -> Int-itemAllocSize apath =- align 4 $ size_hash + size_size + size_aux + size_fileid + size_dsclen + 2 + BS.length (flatten apath)--itemSize, itemNext :: Item -> Int-itemSize i = size_size + size_aux + size_fileid + size_dsclen + (BS.length $ iHashAndDescriptor i)-itemNext i = align 4 (itemSize i + 1)--iPath, iHash, iDescriptor :: Item -> BS.ByteString-iDescriptor = unsafeDrop size_hash . iHashAndDescriptor-iPath = unsafeDrop 1 . iDescriptor-iHash = BS.take size_hash . iHashAndDescriptor--iSize, iAux :: Item -> Ptr Int64-iSize i = plusPtr (iBase i) off_size-iAux i = plusPtr (iBase i) off_aux--iFileID :: Item -> Ptr FileID-iFileID i = plusPtr (iBase i) off_fileid--itemIsDir :: Item -> Bool-itemIsDir i = unsafeHead (iDescriptor i) == c2w 'D'---- xlatePeek32 = fmap xlate32 . peek-xlatePeek64 :: (Storable a, Num a, Bits a) => Ptr a -> IO a-xlatePeek64 = fmap xlate64 . peek---- xlatePoke32 ptr v = poke ptr (xlate32 v)-xlatePoke64 :: (Storable a, Num a, Bits a) => Ptr a -> a -> IO ()-xlatePoke64 ptr v = poke ptr (xlate64 v)---- | Lay out the basic index item structure in memory. The memory location is--- given by a ForeignPointer () and an offset. The path and type given are--- written out, and a corresponding Item is given back. The remaining bits of--- the item can be filled out using 'update'.-createItem :: ItemType -> AnchoredPath -> ForeignPtr () -> Int -> IO Item-createItem typ apath fp off =- do let dsc = BS.concat [ BSC.singleton $ if typ == TreeType then 'D' else 'F'- , flatten apath- , BS.singleton 0 ]- (dsc_fp, dsc_start, dsc_len) = toForeignPtr dsc- withForeignPtr fp $ \p ->- withForeignPtr dsc_fp $ \dsc_p ->- do fileid <- fromMaybe 0 <$> getFileID apath- pokeByteOff p (off + off_fileid) (xlate64 $ fromIntegral fileid :: Int64)- pokeByteOff p (off + off_dsclen) (xlate32 $ fromIntegral dsc_len :: Int32)- memcpy (plusPtr p $ off + off_dsc)- (plusPtr dsc_p dsc_start)- (fromIntegral dsc_len)- peekItem fp off---- | Read the on-disk representation into internal data structure.------ See the module-level section /Index format/ for details on how the index--- is structured.-peekItem :: ForeignPtr () -> Int -> IO Item-peekItem fp off =- withForeignPtr fp $ \p -> do- nl' :: Int32 <- xlate32 `fmap` peekByteOff p (off + off_dsclen)- when (nl' <= 2) $ fail "Descriptor too short in peekItem!"- let nl = fromIntegral nl'- dsc = fromForeignPtr (castForeignPtr fp) (off + off_hash) (size_hash + nl - 1)- return $! Item { iBase = plusPtr p off- , iHashAndDescriptor = dsc }---- | Update an existing item with new hash and optionally mtime (give Nothing--- when updating directory entries).-updateItem :: Item -> Int64 -> Hash -> IO ()-updateItem item _ NoHash =- fail $ "Index.update NoHash: " ++ BSC.unpack (iPath item)-updateItem item size hash =- do xlatePoke64 (iSize item) size- unsafePokeBS (iHash item) (rawHash hash)--updateFileID :: Item -> FileID -> IO ()-updateFileID item fileid = xlatePoke64 (iFileID item) $ fromIntegral fileid-updateAux :: Item -> Int64 -> IO ()-updateAux item aux = xlatePoke64 (iAux item) $ aux-updateTime :: forall a.(Enum a) => Item -> a -> IO ()-updateTime item mtime = updateAux item (fromIntegral $ fromEnum mtime)--iHash' :: Item -> Hash-iHash' i = SHA256 (iHash i)---- | Gives a ForeignPtr to mmapped index, which can be used for reading and--- updates. The req_size parameter, if non-0, expresses the requested size of--- the index file. mmapIndex will grow the index if it is smaller than this.-mmapIndex :: forall a. FilePath -> Int -> IO (ForeignPtr a, Int)-mmapIndex indexpath req_size = do- exist <- doesFileExist indexpath- act_size <- fromIntegral `fmap` if exist then fileSize `fmap` getFileStatus indexpath- else return 0- let size = case req_size > 0 of- True -> req_size- False | act_size >= size_magic -> act_size - size_magic- | otherwise -> 0- case size of- 0 -> return (castForeignPtr nullForeignPtr, size)- _ -> do (x, _, _) <- mmapFileForeignPtr indexpath- ReadWriteEx (Just (0, size + size_magic))- return (x, size)--data IndexM m = Index { mmap :: (ForeignPtr ())- , basedir :: FilePath- , hashtree :: Tree m -> Hash- , predicate :: AnchoredPath -> TreeItem m -> Bool }- | EmptyIndex--type Index = IndexM IO--data State = State { dirlength :: !Int- , path :: !AnchoredPath- , start :: !Int }--data Result = Result { -- | marks if the item has changed since the last update to the index- changed :: !Bool- -- | next is the position of the next item, in bytes.- , next :: !Int- -- | treeitem is Nothing in case of the item doesn't exist in the tree- -- or is filtered by a FilterTree. Or a TreeItem otherwise.- , treeitem :: !(Maybe (TreeItem IO))- -- | resitem is the item extracted.- , resitem :: !Item }--data ResultF = ResultF { -- | nextF is the position of the next item, in bytes.- nextF :: !Int- -- | resitemF is the item extracted.- , resitemF :: !Item- -- | _fileIDs contains the fileids of the files and folders inside,- -- in a folder item and its own fileid for file item).- , _fileIDs :: [((AnchoredPath, ItemType), FileID)] }--readItem :: Index -> State -> IO Result-readItem index state = do- item <- peekItem (mmap index) (start state)- res' <- if itemIsDir item- then readDir index state item- else readFile index state item- return res'--readDir :: Index -> State -> Item -> IO Result-readDir index state item =- do following <- fromIntegral <$> xlatePeek64 (iAux item)- st <- getFileStatusBS (iPath item)- let exists = fileExists st && isDirectory st- fileid <- fromIntegral <$> (xlatePeek64 $ iFileID item)- fileid' <- fromMaybe fileid <$> (getFileID' $ BSC.unpack $ iPath item)- when (fileid == 0) $ updateFileID item fileid'- let name it dirlen = Name $ (BS.drop (dirlen + 1) $ iDescriptor it) -- FIXME MAGIC- namelength = (BS.length $ iDescriptor item) - (dirlength state)- myname = name item (dirlength state)- substate = state { start = start state + itemNext item- , path = path state `appendPath` myname- , dirlength = if myname == Name (BSC.singleton '.')- then dirlength state- else dirlength state + namelength }-- want = exists && (predicate index) (path substate) (Stub undefined NoHash)- oldhash = iHash' item-- subs off | off < following = do- result <- readItem index $ substate { start = off }- rest <- subs $ next result- return $! (name (resitem result) $ dirlength substate, result) : rest- subs coff | coff == following = return []- | otherwise = fail $ "Offset mismatch at " ++ show coff ++- " (ends at " ++ show following ++ ")"-- inferiors <- if want then subs $ start substate- else return []-- let we_changed = or [ changed x | (_, x) <- inferiors ] || nullleaf- nullleaf = null inferiors && oldhash == nullsha- nullsha = SHA256 (BS.replicate 32 0)- tree' = makeTree [ (n, fromJust $ treeitem s) | (n, s) <- inferiors, isJust $ treeitem s ]- treehash = if we_changed then hashtree index tree' else oldhash- tree = tree' { treeHash = treehash }-- when (exists && we_changed) $ updateItem item 0 treehash- return $ Result { changed = not exists || we_changed- , next = following- , treeitem = if want then Just $ SubTree tree- else Nothing- , resitem = item }--readFile :: Index -> State -> Item -> IO Result-readFile index state item =- do st <- getFileStatusBS (iPath item)- mtime <- fromIntegral <$> (xlatePeek64 $ iAux item)- size <- xlatePeek64 $ iSize item- fileid <- fromIntegral <$> (xlatePeek64 $ iFileID item)- fileid' <- fromMaybe fileid <$> (getFileID' $ BSC.unpack $ iPath item)- let mtime' = modificationTime st- size' = fromIntegral $ fileSize st- readblob = readSegment (basedir index </> BSC.unpack (iPath item), Nothing)- exists = fileExists st && not (isDirectory st)- we_changed = mtime /= mtime' || size /= size'- hash = iHash' item- when (exists && we_changed) $- do hash' <- sha256 `fmap` readblob- updateItem item size' hash'- updateTime item mtime'- when (fileid == 0) $ updateFileID item fileid'- return $ Result { changed = not exists || we_changed- , next = start state + itemNext item- , treeitem = if exists then Just $ File $ Blob readblob hash else Nothing- , resitem = item }--updateIndex :: Index -> IO (Tree IO)-updateIndex EmptyIndex = return emptyTree-updateIndex index =- do let initial = State { start = size_magic- , dirlength = 0- , path = AnchoredPath [] }- res <- readItem index initial- case treeitem res of- Just (SubTree tree) -> return $ filter (predicate index) tree- _ -> fail "Unexpected failure in updateIndex!"---- | Return a list containing all the file/folder names in an index, with--- their respective ItemType and FileID.-listFileIDs :: Index -> IO ([((AnchoredPath, ItemType), FileID)])-listFileIDs EmptyIndex = return []-listFileIDs index =- do let initial = State { start = size_magic- , dirlength = 0- , path = AnchoredPath [] }- res <- readItemFileIDs index initial- return $ _fileIDs res--readItemFileIDs :: Index -> State -> IO ResultF-readItemFileIDs index state = do- item <- peekItem (mmap index) (start state)- res' <- if itemIsDir item- then readDirFileIDs index state item- else readFileFileID index state item- return res'--readDirFileIDs :: Index -> State -> Item -> IO ResultF-readDirFileIDs index state item =- do fileid <- fromIntegral <$> (xlatePeek64 $ iFileID item)- following <- fromIntegral <$> xlatePeek64 (iAux item)- let name it dirlen = Name $ (BS.drop (dirlen + 1) $ iDescriptor it) -- FIXME MAGIC- namelength = (BS.length $ iDescriptor item) - (dirlength state)- myname = name item (dirlength state)- substate = state { start = start state + itemNext item- , path = path state `appendPath` myname- , dirlength = if myname == Name (BSC.singleton '.')- then dirlength state- else dirlength state + namelength }- subs off | off < following = do- result <- readItemFileIDs index $ substate { start = off }- rest <- subs $ nextF result- return $! (name (resitemF result) $ dirlength substate, result) : rest- subs coff | coff == following = return []- | otherwise = fail $ "Offset mismatch at " ++ show coff ++- " (ends at " ++ show following ++ ")"- inferiors <- subs $ start substate- return $ ResultF { nextF = following- , resitemF = item- , _fileIDs = (((path substate, TreeType), fileid):concatMap (_fileIDs . snd) inferiors) }--readFileFileID :: Index -> State -> Item -> IO ResultF-readFileFileID _ state item =- do fileid' <- fromIntegral <$> (xlatePeek64 $ iFileID item)- let name it dirlen = Name $ (BS.drop (dirlen + 1) $ iDescriptor it)- myname = name item (dirlength state)- return $ ResultF { nextF = start state + itemNext item- , resitemF = item- , _fileIDs = [((path state `appendPath` myname, BlobType), fileid')] }----- | Read an index and build up a 'Tree' object from it, referring to current--- working directory. The initial Index object returned by readIndex is not--- directly useful. However, you can use 'Tree.filter' on it. Either way, to--- obtain the actual Tree object, call update.------ The usual use pattern is this:------ > do (idx, update) <- readIndex--- > tree <- update =<< filter predicate idx------ The resulting tree will be fully expanded.-readIndex :: FilePath -> (Tree IO -> Hash) -> IO Index-readIndex indexpath ht = do- (mmap_ptr, mmap_size) <- mmapIndex indexpath 0- base <- getCurrentDirectory- return $ if mmap_size == 0 then EmptyIndex- else Index { mmap = mmap_ptr- , basedir = base- , hashtree = ht- , predicate = \_ _ -> True }--formatIndex :: ForeignPtr () -> Tree IO -> Tree IO -> IO ()-formatIndex mmap_ptr old reference =- do _ <- create (SubTree reference) (AnchoredPath []) size_magic- unsafePokeBS magic (BSC.pack "HSI5")- where magic = fromForeignPtr (castForeignPtr mmap_ptr) 0 4- create (File _) path' off =- do i <- createItem BlobType path' mmap_ptr off- let flatpath = BSC.unpack $ flatten path'- case find old path' of- Nothing -> return ()- -- TODO calling getFileStatus here is both slightly- -- inefficient and slightly race-prone- Just ti -> do st <- getFileStatus flatpath- let hash = itemHash ti- mtime = modificationTime st- size = fileSize st- updateItem i (fromIntegral size) hash- updateTime i mtime- return $ off + itemNext i- create (SubTree s) path' off =- do i <- createItem TreeType path' mmap_ptr off- case find old path' of- Nothing -> return ()- Just ti | itemHash ti == NoHash -> return ()- | otherwise -> updateItem i 0 $ itemHash ti- let subs [] = return $ off + itemNext i- subs ((name,x):xs) = do- let path'' = path' `appendPath` name- noff <- subs xs- create x path'' noff- lastOff <- subs (listImmediate s)- xlatePoke64 (iAux i) (fromIntegral lastOff)- return lastOff- create (Stub _ _) path' _ =- fail $ "Cannot create index from stubbed Tree at " ++ show path'---- | Will add and remove files in index to make it match the 'Tree' object--- given (it is an error for the 'Tree' to contain a file or directory that--- does not exist in a plain form in current working directory).-updateIndexFrom :: FilePath -> (Tree IO -> Hash) -> Tree IO -> IO Index-updateIndexFrom indexpath hashtree' ref =- do old_idx <- updateIndex =<< readIndex indexpath hashtree'- reference <- expand ref- let len_root = itemAllocSize anchoredRoot- len = len_root + sum [ itemAllocSize p | (p, _) <- list reference ]- exist <- doesFileExist indexpath-#if mingw32_HOST_OS- when exist $ renameFile indexpath (indexpath <.> "old")-#else- when exist $ removeFile indexpath -- to avoid clobbering oldidx-#endif- (mmap_ptr, _) <- mmapIndex indexpath len- formatIndex mmap_ptr old_idx reference- readIndex indexpath hashtree'---- | Check that a given file is an index file with a format we can handle. You--- should remove and re-create the index whenever this is not true.-indexFormatValid :: FilePath -> IO Bool-indexFormatValid path' =- do magic <- mmapFileByteString path' (Just (0, size_magic))- return $ case BSC.unpack magic of- "HSI5" -> True- _ -> False- `catch` \(_::SomeException) -> return False--instance FilterTree IndexM IO where- filter _ EmptyIndex = EmptyIndex- filter p index = index { predicate = \a b -> predicate index a b && p a b }----- | For a given file or folder path, get the corresponding fileID from the--- filesystem.-getFileID :: AnchoredPath -> IO (Maybe FileID)-getFileID = getFileID' . anchorPath ""--getFileID' :: FilePath -> IO (Maybe FileID)-getFileID' fp = do file_exists <- doesFileExist fp- dir_exists <- doesDirectoryExist fp- if file_exists || dir_exists-#ifdef WIN32- then do h <- createFile fp gENERIC_NONE fILE_SHARE_NONE Nothing oPEN_EXISTING fILE_FLAG_BACKUP_SEMANTICS Nothing- fhnumber <- (Just . fromIntegral . bhfiFileIndex) <$> getFileInformationByHandle h- closeHandle h- return fhnumber-#else- then (Just . fileID) <$> getSymbolicLinkStatus fp-#endif- else return Nothing
− hashed-storage/Storage/Hashed/Monad.hs
@@ -1,277 +0,0 @@-{-# LANGUAGE ScopedTypeVariables, BangPatterns, TypeSynonymInstances, UndecidableInstances, FlexibleInstances #-}---- | An experimental monadic interface to Tree mutation. The main idea is to--- simulate IO-ish manipulation of real filesystem (that's the state part of--- the monad), and to keep memory usage down by reasonably often dumping the--- intermediate data to disk and forgetting it. The monad interface itself is--- generic, and a number of actual implementations can be used. This module--- provides just 'virtualTreeIO' that never writes any changes, but may trigger--- filesystem reads as appropriate.-module Storage.Hashed.Monad- ( virtualTreeIO, virtualTreeMonad- , readFile, writeFile, createDirectory, rename, copy, unlink- , fileExists, directoryExists, exists, withDirectory- , currentDirectory- , tree, TreeState, TreeMonad, TreeIO, runTreeMonad- , initialState, replaceItem- , findM, findFileM, findTreeM- , TreeRO, TreeRW- ) where--import Prelude hiding ( readFile, writeFile )--import Storage.Hashed.AnchoredPath-import Storage.Hashed.Tree--import Control.Applicative( (<$>) )--import Data.List( sortBy )-import Data.Int( Int64 )-import Data.Maybe( isNothing, isJust )--import qualified Data.ByteString.Lazy.Char8 as BL-import Control.Monad.RWS.Strict-import qualified Data.Map as M--type Changed = M.Map AnchoredPath (Int64, Int64) -- size, age---- | Internal state of the 'TreeIO' monad. Keeps track of the current Tree--- content, unsync'd changes and a current working directory (of the monad).-data TreeState m = TreeState { tree :: !(Tree m)- , changed :: !Changed- , changesize :: !Int64- , maxage :: !Int64- , updateHash :: TreeItem m -> m Hash- , update :: AnchoredPath -> TreeItem m -> TreeMonad m (TreeItem m) }---- | A 'TreeIO' monad. A sort of like IO but it keeps a 'TreeState' around as well,--- which is a sort of virtual filesystem. Depending on how you obtained your--- 'TreeIO', the actions in your virtual filesystem get somehow reflected in the--- actual real filesystem. For 'virtualTreeIO', nothing happens in real--- filesystem, however with 'plainTreeIO', the plain tree will be updated every--- now and then, and with 'hashedTreeIO' a darcs-style hashed tree will get--- updated.-type TreeMonad m = RWST AnchoredPath () (TreeState m) m-type TreeIO = TreeMonad IO--class (Functor m, Monad m) => TreeRO m where- currentDirectory :: m AnchoredPath- withDirectory :: AnchoredPath -> m a -> m a- expandTo :: AnchoredPath -> m AnchoredPath- -- | Grab content of a file in the current Tree at the given path.- readFile :: AnchoredPath -> m BL.ByteString- -- | Check for existence of a node (file or directory, doesn't matter).- exists :: AnchoredPath -> m Bool- -- | Check for existence of a directory.- directoryExists ::AnchoredPath -> m Bool- -- | Check for existence of a file.- fileExists :: AnchoredPath -> m Bool--class TreeRO m => TreeRW m where- -- | Change content of a file at a given path. The change will be- -- eventually flushed to disk, but might be buffered for some time.- writeFile :: AnchoredPath -> BL.ByteString -> m ()- createDirectory :: AnchoredPath -> m ()- unlink :: AnchoredPath -> m ()- rename :: AnchoredPath -> AnchoredPath -> m ()- copy :: AnchoredPath -> AnchoredPath -> m ()--initialState :: Tree m -> (TreeItem m -> m Hash)- -> (AnchoredPath -> TreeItem m -> TreeMonad m (TreeItem m)) -> TreeState m-initialState t uh u = TreeState { tree = t- , changed = M.empty- , changesize = 0- , updateHash = uh- , maxage = 0- , update = u }--flush :: (Functor m, Monad m) => TreeMonad m ()-flush = do changed' <- map fst <$> M.toList <$> gets changed- dirs' <- gets tree >>= \t -> return [ path | (path, SubTree _) <- list t ]- modify $ \st -> st { changed = M.empty, changesize = 0 }- forM_ (changed' ++ dirs' ++ [AnchoredPath []]) flushItem--runTreeMonad' :: (Functor m, Monad m) => TreeMonad m a -> TreeState m -> m (a, Tree m)-runTreeMonad' action initial = do- (out, final, _) <- runRWST action (AnchoredPath []) initial- return (out, tree final)--runTreeMonad :: (Functor m, Monad m) => TreeMonad m a -> TreeState m -> m (a, Tree m)-runTreeMonad action initial = do- let action' = do x <- action- flush- return x- runTreeMonad' action' initial---- | Run a TreeIO action without storing any changes. This is useful for--- running monadic tree mutations for obtaining the resulting Tree (as opposed--- to their effect of writing a modified tree to disk). The actions can do both--- read and write -- reads are passed through to the actual filesystem, but the--- writes are held in memory in a form of modified Tree.-virtualTreeMonad :: (Functor m, Monad m) => TreeMonad m a -> Tree m -> m (a, Tree m)-virtualTreeMonad action t = runTreeMonad' action $- initialState t (\_ -> return NoHash) (\_ x -> return x)--virtualTreeIO :: TreeIO a -> Tree IO -> IO (a, Tree IO)-virtualTreeIO = virtualTreeMonad---- | Modifies an item in the current Tree. This action keeps an account of the--- modified data, in changed and changesize, for subsequent flush--- operations. Any modifications (as in "modifyTree") are allowed.-modifyItem :: (Functor m, Monad m)- => AnchoredPath -> Maybe (TreeItem m) -> TreeMonad m ()-modifyItem path item = do- path' <- (`catPaths` path) `fmap` currentDirectory- age <- gets maxage- changed' <- gets changed- let getsize (Just (File b)) = lift (BL.length `fmap` readBlob b)- getsize _ = return 0- size <- getsize item- let change = case M.lookup path' changed' of- Nothing -> size- Just (oldsize, _) -> size - oldsize-- modify $ \st -> st { tree = modifyTree (tree st) path' item- , changed = M.insert path' (size, age) (changed st)- , maxage = age + 1- , changesize = (changesize st + change) }--renameChanged :: (Functor m, Monad m)- => AnchoredPath -> AnchoredPath -> TreeMonad m ()-renameChanged from to = modify $ \st -> st { changed = rename' $ changed st }- where rename' = M.fromList . map renameone . M.toList- renameone (x, d) | from `isPrefix` x = (to `catPaths` relative from x, d)- | otherwise = (x, d)- relative (AnchoredPath from') (AnchoredPath x) = AnchoredPath $ drop (length from') x---- | Replace an item with a new version without modifying the content of the--- tree. This does not do any change tracking. Ought to be only used from a--- 'sync' implementation for a particular storage format. The presumed use-case--- is that an existing in-memory Blob is replaced with a one referring to an--- on-disk file.-replaceItem :: (Functor m, Monad m)- => AnchoredPath -> Maybe (TreeItem m) -> TreeMonad m ()-replaceItem path item = do- path' <- (`catPaths` path) `fmap` currentDirectory- modify $ \st -> st { tree = modifyTree (tree st) path' item }--flushItem :: forall m. (Monad m, Functor m) => AnchoredPath -> TreeMonad m ()-flushItem path =- do current <- gets tree- case find current path of- Nothing -> return () -- vanished, do nothing- Just x -> do y <- fixHash x- new <- gets update >>= ($ y) . ($ path)- replaceItem path (Just new)- where fixHash :: TreeItem m -> TreeMonad m (TreeItem m)- fixHash f@(File (Blob con NoHash)) = do- hash <- gets updateHash >>= \x -> lift $ x f- return $ File $ Blob con hash- fixHash (SubTree s) | treeHash s == NoHash =- gets updateHash >>= \f -> SubTree <$> lift (addMissingHashes f s)- fixHash x = return x----- | If buffers are becoming large, sync, otherwise do nothing.-flushSome :: (Monad m, Functor m) => TreeMonad m ()-flushSome = do x <- gets changesize- when (x > megs 100) $ do- remaining <- go =<< sortBy age <$> M.toList <$> gets changed- modify $ \s -> s { changed = M.fromList remaining }- where go [] = return []- go ((path, (size, _)):chs) = do- x <- (\s -> s - size) <$> gets changesize- flushItem path- modify $ \s -> s { changesize = x }- if (x > megs 50) then go chs- else return $ chs- megs = (* (1024 * 1024))- age (_, (_, a)) (_, (_, b)) = compare a b--instance (Functor m, Monad m) => TreeRO (TreeMonad m) where- expandTo p =- do t <- gets tree- p' <- (`catPaths` p) `fmap` ask- t' <- lift $ expandPath t p'- modify $ \st -> st { tree = t' }- return p'-- fileExists p =- do p' <- expandTo p- (isJust . (flip findFile p')) `fmap` gets tree-- directoryExists p =- do p' <- expandTo p- (isJust . (flip findTree p')) `fmap` gets tree-- exists p =- do p' <- expandTo p- (isJust . (flip find p')) `fmap` gets tree-- readFile p =- do p' <- expandTo p- t <- gets tree- let f = findFile t p'- case f of- Nothing -> fail $ "No such file " ++ show p'- Just x -> lift (readBlob x)-- currentDirectory = ask- withDirectory dir act = do- dir' <- expandTo dir- local (\_ -> dir') act--instance (Functor m, Monad m) => TreeRW (TreeMonad m) where- writeFile p con =- do _ <- expandTo p- modifyItem p (Just blob)- flushSome- where blob = File $ Blob (return con) hash- hash = NoHash -- we would like to say "sha256 con" here, but due- -- to strictness of Hash in Blob, this would often- -- lead to unnecessary computation which would then- -- be discarded anyway; we rely on the sync- -- implementation to fix up any NoHash occurrences-- createDirectory p =- do _ <- expandTo p- modifyItem p $ Just $ SubTree emptyTree-- unlink p =- do _ <- expandTo p- modifyItem p Nothing-- rename from to =- do from' <- expandTo from- to' <- expandTo to- tr <- gets tree- let item = find tr from'- found_to = find tr to'- unless (isNothing found_to) $- fail $ "Error renaming: destination " ++ show to ++ " exists."- unless (isNothing item) $ do- modifyItem from Nothing- modifyItem to item- renameChanged from to-- copy from to =- do from' <- expandTo from- _ <- expandTo to- tr <- gets tree- let item = find tr from'- unless (isNothing item) $ modifyItem to item--findM' :: forall m a. (Monad m, Functor m)- => (Tree m -> AnchoredPath -> a) -> Tree m -> AnchoredPath -> m a-findM' what t path = fst <$> virtualTreeMonad (look path) t- where look :: AnchoredPath -> TreeMonad m a- look = expandTo >=> \p' -> flip what p' <$> gets tree--findM :: (Monad m, Functor m) => Tree m -> AnchoredPath -> m (Maybe (TreeItem m))-findM = findM' find--findTreeM :: (Monad m, Functor m) => Tree m -> AnchoredPath -> m (Maybe (Tree m))-findTreeM = findM' findTree--findFileM :: (Monad m, Functor m) => Tree m -> AnchoredPath -> m (Maybe (Blob m))-findFileM = findM' findFile
− hashed-storage/Storage/Hashed/Packed.hs
@@ -1,227 +0,0 @@-{-# LANGUAGE ParallelListComp #-}--- | This module implements an "object storage". This is a directory on disk--- containing a content-addressed storage. This is useful for storing all kinds--- of things, particularly filesystem trees, or darcs pristine caches and patch--- objects. However, this is an abstract, flat storage: no tree semantics are--- provided. You just need to provide a reference-collecting functionality,--- computing a list of references for any given object. The system provides--- transparent garbage collection and packing.-module Storage.Hashed.Packed- ( Format(..), Block, OS- -- * Basic operations.- , hatch, compact, repack, lookup- -- * Creating and loading.- , create, load- -- * Low-level.- , format, blockLookup, live, hatchery, mature, roots, references, rootdir- ) where--import Prelude hiding ( lookup, read )-import Storage.Hashed.AnchoredPath( )-import Storage.Hashed.Tree ( )-import Storage.Hashed.Utils-import Storage.Hashed.Hash--import Control.Monad( forM, forM_, unless )-import Control.Applicative( (<$>) )-import System.FilePath( (</>), (<.>) )-import System.Directory( createDirectoryIfMissing, removeFile- , getDirectoryContents )--import Bundled.Posix( fileExists, isDirectory, getFileStatus )--import qualified Data.ByteString.Lazy.Char8 as BL-import qualified Data.ByteString.Char8 as BS-import Data.Maybe( listToMaybe, catMaybes, isNothing )-import Data.Binary( encode, decode )--import qualified Data.Set as S-import qualified Data.Map as M-import Data.List( sort )-import Data.Int( Int64 )---- | On-disk format for object storage: we implement a completely loose format--- (one file per object), a compact format stored in a single append-only file--- and an immutable \"pack\" format.-data Format = Loose | Compact | Pack deriving (Show, Eq)--loose_dirs :: [[Char]]-loose_dirs = let chars = ['0'..'9'] ++ ['a'..'f']- in [ [a,b] | a <- chars, b <- chars ]--loosePath :: OS -> Hash -> FilePath-loosePath _ NoHash = error "No path for NoHash!"-loosePath os hash =- let hash' = BS.unpack (encodeBase16 hash)- in rootdir os </> "hatchery" </> take 2 hash' </> drop 2 hash'--looseLookup :: OS -> Hash -> IO (Maybe FileSegment)-looseLookup _ NoHash = return Nothing-looseLookup os hash = do- let path = loosePath os hash- exist <- fileExists <$> getFileStatus path- return $ if exist then Just (path, Nothing)- else Nothing---- | Object storage block. When used as a hatchery, the loose or compact format--- are preferable, while for mature space, the pack format is more useful.-data Block = Block { blockLookup :: Hash -> IO (Maybe FileSegment)- , size :: Int64- , format :: Format }---- | Object storage. Contains a single \"hatchery\" and possibly a number of--- mature space blocks, usually in form of packs. It also keeps a list of root--- pointers and has a way to extract pointers from objects (externally--- supplied). These last two things are used to implement a simple GC.-data OS = OS { hatchery :: Block- , mature :: [Block]- , roots :: [Hash]- , references :: FileSegment -> IO [Hash]- , rootdir :: FilePath }---- | Reduce number of packs in the object storage. This may both recombine--- packs to eliminate dead objects and join some packs to form bigger packs.-repack :: OS -> IO OS-repack _ = error "repack undefined"---- | Add new objects to the object storage (i.e. put them into hatchery). It is--- safe to call this even on objects that are already present in the storage:--- such objects will be skipped.-hatch :: OS -> [BL.ByteString] -> IO OS-hatch os blobs =- do processed <- mapM sieve blobs- write [ (h, b) | (True, h, b) <- processed ]- where write bits =- case format (hatchery os) of- Loose ->- do _ <- forM bits $ \(hash, blob) -> do- BL.writeFile (loosePath os hash) blob- return os- Compact -> error "hatch/compact undefined"- _ -> fail "Hatchery must be either Loose or Compact."- sieve blob = do let hash = sha256 blob- absent <- isNothing <$> lookup os hash- return (absent, hash, blob)---- | Move things from hatchery into a (new) pack.-compact :: OS -> IO OS-compact os = do objects <- live os [hatchery os]- block <- createPack os (M.toList objects)- cleanup- return $ os { mature = block:mature os }- where cleanup =- case format (hatchery os) of- Loose -> forM_ loose_dirs $ nuke . ((rootdir os </> "hatchery") </>)- Compact -> removeFile (rootdir os </> "hatchery") >> return ()- _ -> fail "Hatchery must be either Loose or Compact."- nuke dir = mapM (removeFile . (dir </>)) =<<- (Prelude.filter (`notElem` [".", ".."]) `fmap`- getDirectoryContents dir)--blocksLookup :: [Block] -> Hash -> IO (Maybe (Hash, FileSegment))-blocksLookup blocks hash =- do segment <- cat `fmap` mapM (flip blockLookup hash) blocks- return $ case segment of- Nothing -> Nothing- Just seg -> Just (hash, seg)- where cat = listToMaybe . catMaybes--lookup :: OS -> Hash -> IO (Maybe FileSegment)-lookup os hash =- do res <- blocksLookup (hatchery os : mature os) hash- return $ case res of- Nothing -> Nothing- Just (_, seg) -> Just seg---- | Create an empty object storage in given directory, with a hatchery of--- given format. The directory is created if needed, but is assumed to be--- empty.-create :: FilePath -> Format -> IO OS-create path fmt = do createDirectoryIfMissing True path- _ <- initHatchery- load path- where initHatchery | fmt == Loose =- do mkdir hatchpath- forM loose_dirs $ mkdir . (hatchpath </>)- | fmt == Compact =- error "create/mkHatchery Compact undefined"- | otherwise =- error "create/mkHatchery Pack undefined"- mkdir = createDirectoryIfMissing False- hatchpath = path </> "hatchery"--load :: FilePath -> IO OS-load path =- do hatch_stat <- getFileStatus $ path </> "hatchery"- let is_os = fileExists hatch_stat- is_dir = isDirectory hatch_stat- unless is_os $ fail $ path ++ " is not an object storage!"- let _hatchery = Block { blockLookup = look os- , format = if is_dir then Loose else Compact- , size = undefined }- os = OS { hatchery = _hatchery- , rootdir = path- , mature = packs- , roots = _roots- , references = undefined }- look | format _hatchery == Loose = looseLookup- | otherwise = undefined- packs = [] -- FIXME read packs- _roots = [] -- FIXME read root pointers- return os--readPack :: FilePath -> IO Block-readPack file = do bits <- readSegment (file, Nothing)- let count = decode (BL.take 8 $ bits)- _lookup NoHash _ _ = return Nothing- _lookup hash first final = do- let middle = first + ((final - first) `div` 2)- rawhash = rawHash hash- res <- case ( compare rawhash (hashof first)- , compare rawhash (hashof middle)- , compare rawhash (hashof final) ) of- (LT, _, _) -> return Nothing- ( _, _, GT) -> return Nothing- (EQ, _, _) -> return $ Just (segof first)- ( _, _, EQ) -> return $ Just (segof final)- (GT, EQ, LT) -> return $ Just (segof middle)- (GT, GT, LT) | middle /= final -> _lookup hash middle final- (GT, LT, LT) | first /= middle -> _lookup hash first middle- ( _, _, _) -> return Nothing- return res- headerof i = BL.take 51 $ BL.drop (8 + i * 51) bits- hashof i = BS.concat $ BL.toChunks $ BL.take 32 $ headerof i- segof i = (file, Just (count * 51 + 8 + from, sz))- where from = decode (BL.take 8 $ BL.drop 33 $ headerof i)- sz = decode (BL.take 8 $ BL.drop 42 $ headerof i)- return $ Block { size = BL.length bits- , format = Pack- , blockLookup = \h -> _lookup h 0 (count - 1) }--createPack :: OS -> [(Hash, FileSegment)] -> IO Block-createPack os bits =- do contents <- mapM readSegment (map snd bits)- let offsets = scanl (+) 0 $ map BL.length contents- headerbits = [ BL.concat [ BL.fromChunks [rawhash]- , BL.pack "@"- , encode offset- , BL.pack "!"- , encode $ BL.length string- , BL.pack "\n" ]- | (SHA256 rawhash, _) <- bits- | string <- contents- | offset <- offsets ]- header = BL.concat $ (encode $ length bits) : sort headerbits- blob = BL.concat $ header:contents- hash = sha256 blob- path = rootdir os </> BS.unpack (encodeBase16 hash) <.> "bin"- BL.writeFile path blob- readPack path---- | Build a map of live objects (i.e. those reachable from the given roots) in--- a given list of Blocks.-live :: OS -> [Block] -> IO (M.Map Hash FileSegment)-live os blocks =- reachable (references os)- (blocksLookup blocks)- (S.fromList $ roots os)
− hashed-storage/Storage/Hashed/Plain.hs
@@ -1,78 +0,0 @@--- | The plain format implementation resides in this module. The plain format--- does not use any hashing and basically just wraps a normal filesystem tree--- in the hashed-storage API.------ NB. The 'read' function on Blobs coming from a plain tree is susceptible to--- file content changes. Since we use mmap in 'read', this will break--- referential transparency and produce unexpected results. Please always make--- sure that all parallel access to the underlying filesystem tree never--- mutates files. Unlink + recreate is fine though (in other words, the--- 'writePlainTree' and 'plainTreeIO' implemented in this module are safe in--- this respect).-module Storage.Hashed.Plain( readPlainTree, writePlainTree,- plainTreeIO -- (obsolete? if so remove implementation!)- ) where--import Data.Maybe( catMaybes )-import qualified Data.ByteString.Char8 as BS8-import qualified Data.ByteString.Lazy as BL-import System.FilePath( (</>) )-import System.Directory( getDirectoryContents- , createDirectoryIfMissing )-import Bundled.Posix( getFileStatus, isDirectory, isRegularFile, FileStatus )--import Storage.Hashed.AnchoredPath-import Storage.Hashed.Utils-import Storage.Hashed.Hash( Hash( NoHash) )-import Storage.Hashed.Tree( Tree(), TreeItem(..)- , Blob(..), makeTree- , list, readBlob, expand )-import Storage.Hashed.Monad( TreeIO, runTreeMonad, initialState )-import Control.Monad.State( liftIO )--readPlainDir :: FilePath -> IO [(FilePath, FileStatus)]-readPlainDir dir =- withCurrentDirectory dir $ do- items <- getDirectoryContents "."- sequence [ do st <- getFileStatus s- return (s, st)- | s <- items, s `notElem` [ ".", ".." ] ]--readPlainTree :: FilePath -> IO (Tree IO)-readPlainTree dir = do- items <- readPlainDir dir- let subs = catMaybes [- let name = Name (BS8.pack name')- in case status of- _ | isDirectory status -> Just (name, Stub (readPlainTree (dir </> name')) NoHash)- _ | isRegularFile status -> Just (name, File $ Blob (readBlob' name) NoHash)- _ -> Nothing- | (name', status) <- items ]- return $ makeTree subs- where readBlob' (Name name) = readSegment (dir </> BS8.unpack name, Nothing)---- | Write out /full/ tree to a plain directory structure. If you instead want--- to make incremental updates, refer to "Storage.Hashed.Monad".-writePlainTree :: Tree IO -> FilePath -> IO ()-writePlainTree t dir = do- createDirectoryIfMissing True dir- expand t >>= mapM_ write . list- where write (p, File b) = write' p b- write (p, SubTree _) =- createDirectoryIfMissing True (anchorPath dir p)- write _ = return ()- write' p b = readBlob b >>= BL.writeFile (anchorPath dir p)---- | Run a 'TreeIO' action in a plain tree setting. Writes out changes to the--- plain tree every now and then (after the action is finished, the last tree--- state is always flushed to disk). XXX Modify the tree with filesystem--- reading and put it back into st (ie. replace the in-memory Blobs with normal--- ones, so the memory can be GCd).-plainTreeIO :: TreeIO a -> Tree IO -> FilePath -> IO (a, Tree IO)-plainTreeIO action t _ = runTreeMonad action $ initialState t (\_ -> return NoHash) updatePlain- where updatePlain path (File b) =- do liftIO $ createDirectoryIfMissing True (anchorPath "" $ parent path)- liftIO $ readBlob b >>= BL.writeFile (anchorPath "" path)- return $ File $ Blob (BL.readFile $ anchorPath "" path) NoHash- updatePlain _ x = return x-
− hashed-storage/Storage/Hashed/Test.hs
@@ -1,621 +0,0 @@-{-# LANGUAGE ScopedTypeVariables, FlexibleInstances, CPP #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}-module Storage.Hashed.Test( tests ) where--import Prelude hiding ( filter, readFile, writeFile, lookup )-import qualified Prelude-import qualified Data.ByteString.Lazy.Char8 as BL-import qualified Data.ByteString.Char8 as BS-import Control.Exception( finally )-import System.Directory( doesFileExist, removeFile, doesDirectoryExist )-import System.FilePath( (</>) )-import Control.Monad.Identity-import Control.Monad.Trans( lift )-import Control.Applicative( (<$>) )--import Data.Maybe-import Data.Word-import Data.List( sort, intercalate, nub, intersperse )--import Storage.Hashed-import Storage.Hashed.AnchoredPath-import Storage.Hashed.Tree hiding ( lookup )-import Storage.Hashed.Index-import Storage.Hashed.Utils-import Storage.Hashed.Darcs-import Storage.Hashed.Packed hiding ( lookup )-import Storage.Hashed.Hash-import Storage.Hashed.Monad hiding ( tree )--import System.Mem( performGC )--import qualified Data.Set as S-import qualified Data.Map as M--import qualified Bundled.Posix as Posix- ( getFileStatus, getSymbolicLinkStatus, fileSize, fileExists )--import Test.HUnit hiding ( path )-import Test.Framework( testGroup )-import qualified Test.Framework as TF ( Test )-import Test.QuickCheck--import Test.Framework.Providers.HUnit-import Test.Framework.Providers.QuickCheck2----------------------------- Test Data-----blobs :: [(AnchoredPath, BL.ByteString)]-blobs = [ (floatPath "foo_a", BL.pack "a\n")- , (floatPath "foo_dir/foo_a", BL.pack "a\n")- , (floatPath "foo_dir/foo_b", BL.pack "b\n")- , (floatPath "foo_dir/foo_subdir/foo_a", BL.pack "a\n")- , (floatPath "foo space/foo\nnewline", BL.pack "newline\n")- , (floatPath "foo space/foo\\backslash", BL.pack "backslash\n")- , (floatPath "foo space/foo_a", BL.pack "a\n") ]--files :: [AnchoredPath]-files = map fst blobs--dirs :: [AnchoredPath]-dirs = [ floatPath "foo_dir"- , floatPath "foo_dir/foo_subdir"- , floatPath "foo space" ]--emptyStub :: TreeItem IO-emptyStub = Stub (return emptyTree) NoHash--testTree :: Tree IO-testTree =- makeTree [ (makeName "foo", emptyStub)- , (makeName "subtree", SubTree sub)- , (makeName "substub", Stub getsub NoHash) ]- where sub = makeTree [ (makeName "stub", emptyStub)- , (makeName "substub", Stub getsub2 NoHash)- , (makeName "x", SubTree emptyTree) ]- getsub = return sub- getsub2 = return $ makeTree [ (makeName "file", File emptyBlob)- , (makeName "file2",- File $ Blob (return $ BL.pack "foo") NoHash) ]--equals_testdata :: Tree IO -> IO ()-equals_testdata t = sequence_ [- do isJust (findFile t p) @? show p ++ " in tree"- ours <- readBlob (fromJust $ findFile t p)- ours @?= stored- | (p, stored) <- blobs ] >>- sequence_ [ isJust (Prelude.lookup p blobs) @? show p ++ " extra in tree"- | (p, File _) <- list t ]-------------------------------- Test list-----tests :: [TF.Test]-tests = [ testGroup "Bundled.Posix" posix- , testGroup "Storage.Hashed.Utils" utils- , testGroup "Storage.Hashed.Hash" hash- , testGroup "Storage.Hashed.Tree" tree- , testGroup "Storage.Hashed.Index" index- , testGroup "Storage.Hashed.Packed" packed- , testGroup "Storage.Hashed.Monad" monad- , testGroup "Storage.Hashed" hashed ]------------------------------- Tests-----hashed :: [TF.Test]-hashed = [ testCase "plain has all files" have_files- , testCase "pristine has all files" have_pristine_files- , testCase "pristine has no extras" pristine_no_extra- , testCase "pristine file contents match" pristine_contents- , testCase "plain file contents match" plain_contents- , testCase "writePlainTree works" write_plain ]- where- check_file t f = assertBool- ("path " ++ show f ++ " is missing in tree " ++ show t)- (isJust $ find t f)- check_files = forM_ files . check_file-- pristine_no_extra = do- t <- readDarcsPristine "." >>= expand- forM_ (list t) $ \(path,_) -> assertBool (show path ++ " is extraneous in tree")- (path `elem` (dirs ++ files))- have_files = readPlainTree "." >>= expand >>= check_files- have_pristine_files =- readDarcsPristine "." >>= expand >>= check_files-- pristine_contents = do- t <- readDarcsPristine "." >>= expand- equals_testdata t-- plain_contents = do- t <- expand =<< filter nondarcs `fmap` readPlainTree "."- equals_testdata t-- write_plain = do- orig <- readDarcsPristine "." >>= expand- writePlainTree orig "_darcs/plain"- t <- expand =<< readPlainTree "_darcs/plain"- equals_testdata t--index :: [TF.Test]-index = [ testCase "index versioning" check_index_versions- , testCase "index listing" check_index- , testCase "index content" check_index_content ]- where pristine = readDarcsPristine "." >>= expand- build_index =- do x <- pristine- exist <- doesFileExist "_darcs/index"- performGC -- required in win32 to trigger file close- when exist $ removeFile "_darcs/index"- idx <- updateIndex =<< updateIndexFrom "_darcs/index" darcsTreeHash x- return (x, idx)- check_index =- do (pris, idx) <- build_index- (sort $ map fst $ list idx) @?= (sort $ map fst $ list pris)- check_blob_pair p x y =- do a <- readBlob x- b <- readBlob y- assertEqual ("content match on " ++ show p) a b- check_index_content =- do (_, idx) <- build_index- plain <- readPlainTree "."- x <- sequence $ zipCommonFiles check_blob_pair plain idx- assertBool "files match" (length x > 0)- check_index_versions =- do performGC -- required in win32 to trigger file close- Prelude.writeFile "_darcs/index" "nonsense index... do not crash!"- valid <- indexFormatValid "_darcs/index"- assertBool "index format invalid" $ not valid--tree :: [TF.Test]-tree = [ testCase "modifyTree" check_modify- , testCase "complex modifyTree" check_modify_complex- , testCase "modifyTree removal" check_modify_remove- , testCase "expand" check_expand- , testCase "expandPath" check_expand_path- , testCase "expandPath of sub" check_expand_path_sub- , testCase "diffTrees" check_diffTrees- , testCase "diffTrees identical" check_diffTrees_ident- , testProperty "expandPath" prop_expandPath- , testProperty "shapeEq" prop_shape_eq- , testProperty "expandedShapeEq" prop_expanded_shape_eq- , testProperty "expand is identity" prop_expand_id- , testProperty "filter True is identity" prop_filter_id- , testProperty "filter False is empty" prop_filter_empty- , testProperty "restrict both ways keeps shape" prop_restrict_shape_commutative- , testProperty "restrict is a subtree of both" prop_restrict_subtree- , testProperty "overlay keeps shape" prop_overlay_shape- , testProperty "overlay is superset of over" prop_overlay_super ]- where blob x = File $ Blob (return (BL.pack x)) (sha256 $ BL.pack x)- name = Name . BS.pack- check_modify =- let t = makeTree [(name "foo", blob "bar")]- modify = modifyTree t (floatPath "foo") (Just $ blob "bla")- in do x <- readBlob $ fromJust $ findFile t (floatPath "foo")- y <- readBlob $ fromJust $ findFile modify (floatPath "foo")- assertEqual "old version" x (BL.pack "bar")- assertEqual "new version" y (BL.pack "bla")- assertBool "list has foo" $- isJust (Prelude.lookup (floatPath "foo") $ list modify)- length (list modify) @?= 1- check_modify_complex =- let t = makeTree [ (name "foo", blob "bar")- , (name "bar", SubTree t1) ]- t1 = makeTree [ (name "foo", blob "bar") ]- modify = modifyTree t (floatPath "bar/foo") (Just $ blob "bla")- in do foo <- readBlob $ fromJust $ findFile t (floatPath "foo")- foo' <- readBlob $ fromJust $ findFile modify (floatPath "foo")- bar_foo <- readBlob $ fromJust $- findFile t (floatPath "bar/foo")- bar_foo' <- readBlob $ fromJust $- findFile modify (floatPath "bar/foo")- assertEqual "old foo" foo (BL.pack "bar")- assertEqual "old bar/foo" bar_foo (BL.pack "bar")- assertEqual "new foo" foo' (BL.pack "bar")- assertEqual "new bar/foo" bar_foo' (BL.pack "bla")- assertBool "list has bar/foo" $- isJust (Prelude.lookup (floatPath "bar/foo") $ list modify)- assertBool "list has foo" $- isJust (Prelude.lookup (floatPath "foo") $ list modify)- length (list modify) @?= length (list t)- check_modify_remove =- let t1 = makeTree [(name "foo", blob "bar")]- t2 :: Tree Identity = makeTree [ (name "foo", blob "bar")- , (name "bar", SubTree t1) ]- modify1 = modifyTree t1 (floatPath "foo") Nothing- modify2 = modifyTree t2 (floatPath "bar") Nothing- file = findFile modify1 (floatPath "foo")- subtree = findTree modify2 (floatPath "bar")- in do assertBool "file is gone" (isNothing file)- assertBool "subtree is gone" (isNothing subtree)-- no_stubs t = null [ () | (_, Stub _ _) <- list t ]- path = floatPath "substub/substub/file"- badpath = floatPath "substub/substub/foo"- check_expand = do- x <- expand testTree- assertBool "no stubs in testTree" $ not (no_stubs testTree)- assertBool "stubs in expanded tree" $ no_stubs x- assertBool "path reachable" $ path `elem` (map fst $ list x)- assertBool "badpath not reachable" $- badpath `notElem` (map fst $ list x)- check_expand_path = do- test_exp <- expand testTree- t <- expandPath testTree path- t' <- expandPath test_exp path- t'' <- expandPath testTree $ floatPath "substub/x"- assertBool "path not reachable in testTree" $ path `notElem` (map fst $ list testTree)- assertBool "path reachable in t" $ path `elem` (map fst $ list t)- assertBool "path reachable in t'" $ path `elem` (map fst $ list t')- assertBool "path reachable in t (with findFile)" $- isJust $ findFile t path- assertBool "path reachable in t' (with findFile)" $- isJust $ findFile t' path- assertBool "path not reachable in t''" $ path `notElem` (map fst $ list t'')- assertBool "badpath not reachable in t" $- badpath `notElem` (map fst $ list t)- assertBool "badpath not reachable in t'" $- badpath `notElem` (map fst $ list t')-- check_expand_path_sub = do- t <- expandPath testTree $ floatPath "substub"- t' <- expandPath testTree $ floatPath "substub/stub"- t'' <- expandPath testTree $ floatPath "subtree/stub"- assertBool "leaf is not a Stub" $- isNothing (findTree testTree $ floatPath "substub")- assertBool "leaf is not a Stub" $ isJust (findTree t $ floatPath "substub")- assertBool "leaf is not a Stub (2)" $ isJust (findTree t' $ floatPath "substub/stub")- assertBool "leaf is not a Stub (3)" $ isJust (findTree t'' $ floatPath "subtree/stub")-- check_diffTrees =- flip finally (Prelude.writeFile "foo_dir/foo_a" "a\n") $- do Prelude.writeFile "foo_dir/foo_a" "b\n"- working_plain <- filter nondarcs `fmap` readPlainTree "."- working <- updateIndex =<<- updateIndexFrom "_darcs/index" darcsTreeHash working_plain- pristine <- readDarcsPristine "."- (working', pristine') <- diffTrees working pristine- let foo_work = findFile working' (floatPath "foo_dir/foo_a")- foo_pris = findFile pristine' (floatPath "foo_dir/foo_a")- working' `shapeEq` pristine'- @? show working' ++ " `shapeEq` " ++ show pristine'- assertBool "foo_dir/foo_a is in working'" $ isJust foo_work- assertBool "foo_dir/foo_a is in pristine'" $ isJust foo_pris- foo_work_c <- readBlob (fromJust foo_work)- foo_pris_c <- readBlob (fromJust foo_pris)- BL.unpack foo_work_c @?= "b\n"- BL.unpack foo_pris_c @?= "a\n"- assertEqual "working' tree is minimal" 2 (length $ list working')- assertEqual "pristine' tree is minimal" 2 (length $ list pristine')-- check_diffTrees_ident = do- pristine <- readDarcsPristine "."- (t1, t2) <- diffTrees pristine pristine- assertBool "t1 is empty" $ null (list t1)- assertBool "t2 is empty" $ null (list t2)-- prop_shape_eq x = no_stubs x ==> x `shapeEq` x- where _types = x :: Tree Identity- prop_expanded_shape_eq x = runIdentity $ expandedShapeEq x x- where _types = x :: Tree Identity- prop_expand_id x = no_stubs x ==> runIdentity (expand x) `shapeEq` x- where _types = x :: Tree Identity- prop_filter_id x = runIdentity $ expandedShapeEq x $ filter (\_ _ -> True) x- where _types = x :: Tree Identity- prop_filter_empty x = runIdentity $ expandedShapeEq emptyTree $ filter (\_ _ -> False) x- where _types = x :: Tree Identity- prop_restrict_shape_commutative (t1, t2) =- no_stubs t1 && no_stubs t2 && not (restrict t1 t2 `shapeEq` emptyTree) ==>- restrict t1 t2 `shapeEq` restrict t2 t1- where _types = (t1 :: Tree Identity, t2 :: Tree Identity)- prop_restrict_subtree (t1, t2) =- no_stubs t1 && not (restrict t1 t2 `shapeEq` emptyTree) ==>- let restricted = S.fromList (map fst $ list $ restrict t1 t2)- orig1 = S.fromList (map fst $ list t1)- orig2 = S.fromList (map fst $ list t2)- in and [restricted `S.isSubsetOf` orig1, restricted `S.isSubsetOf` orig2]- where _types = (t1 :: Tree Identity, t2 :: Tree Identity)- prop_overlay_shape (t1 :: Tree Identity, t2) =- (Just LT == runIdentity (t2 `cmpExpandedShape` t1)) ==>- runIdentity $ (t1 `overlay` t2) `expandedShapeEq` t1- prop_overlay_super (t1 :: Tree Identity, t2) =- (Just LT == runIdentity (t2 `cmpExpandedShape` t1)) && no_stubs t2 ==>- Just EQ == (runIdentity $ restrict t2 (t1 `overlay` t2) `cmpTree` t2)- prop_expandPath (TreeWithPath t p) =- notStub $ find (runIdentity $ expandPath t p) p- where notStub (Just (Stub _ _)) = False- notStub Nothing = error "Did not exist."- notStub _ = True--packed :: [TF.Test]-packed = [ testCase "loose pristine tree" check_loose- , testCase "load" check_load- , testCase "live" check_live- , testCase "compact" check_compact ]- where root_hash = treeHash <$> get_pristine- get_pristine = darcsUpdateDirHashes <$> (expand =<< readDarcsPristine ".")- check_loose = do x <- readDarcsPristine "."- os <- create "_darcs/loose" Loose- (os', root) <- writePackedDarcsPristine x os- y <- expand =<< readPackedDarcsPristine os' root- equals_testdata y- check_load = do os <- load "_darcs/loose"- format (hatchery os) @?= Loose- root <- root_hash- y <- expand =<< readPackedDarcsPristine os root- equals_testdata y- check_live = do os <- load "_darcs/loose"- x <- get_pristine- root <- root_hash- alive <- live (os { roots = [ root ]- , references = darcsPristineRefs }) [hatchery os]- sequence_ [ assertBool (show hashValue ++ " is alive") $- hashValue `S.member` M.keysSet alive- | hashValue <- map (itemHash . snd) $ list x ]- length (M.toList alive) @?= 1 + length (nub $ map snd blobs) + length dirs- check_compact = do os <- load "_darcs/loose"- x <- darcsUpdateDirHashes `fmap` (expand =<< readDarcsPristine ".")- (os', root) <- storePackedDarcsPristine x os- hatch_root_old <- blockLookup (hatchery os') root- assertBool "bits in the old hatchery" $ isJust hatch_root_old-- os'' <- compact os'- length (mature os'') @?= 1- hatch_root <- blockLookup (hatchery os'') root- mature_root <- blockLookup (head $ mature os'') root- assertBool "bits no longer in hatchery" $ isNothing hatch_root- assertBool "bits now in the mature space" $ isJust mature_root- mature_root_con <- readSegment (fromJust mature_root)- Just mature_root_con @?= darcsFormatDir x-- y <- expand =<< readPackedDarcsPristine os'' root- equals_testdata y--utils :: [TF.Test]-utils = [ testProperty "xlate32" prop_xlate32- , testProperty "xlate64" prop_xlate64- , testProperty "align bounded" prop_align_bounded- , testProperty "align aligned" prop_align_aligned- , testProperty "reachable is a subset" prop_reach_subset- , testProperty "roots are reachable" prop_reach_roots- , testProperty "nonexistent roots are not reachable" prop_reach_nonroots- , testCase "an example for reachable" check_reachable- , testCase "fixFrom" check_fixFrom- , testCase "mmap empty file" check_mmapEmpty ]- where prop_xlate32 x = (xlate32 . xlate32) x == x where _types = x :: Word32- prop_xlate64 x = (xlate64 . xlate64) x == x where _types = x :: Word64- prop_align_bounded (bound, x) =- bound > 0 && bound < 1024 && x >= 0 ==>- align bound x >= x && align bound x < x + bound- where _types = (bound, x) :: (Int, Int)- prop_align_aligned (bound, x) =- bound > 0 && bound < 1024 && x >= 0 ==>- align bound x `rem` bound == 0- where _types = (bound, x) :: (Int, Int)-- check_fixFrom = let f 0 = 0- f n = f (n - 1) in fixFrom f 5 @?= (0 :: Integer)-- check_mmapEmpty = flip finally (removeFile "test_empty") $ do- Prelude.writeFile "test_empty" ""- x <- readSegment ("test_empty", Nothing)- x @?= BL.empty-- reachable' ref look rootsSet = runIdentity $ reachable ref look rootsSet-- check_reachable = let refs 0 = [1, 2]- refs 1 = [2]- refs 2 = [0, 4]- refs 3 = [4, 6, 7]- refs 4 = [0, 1]- refs _ = error "internal error in check_reachable"- set = S.fromList [1, 2]- mp = M.fromList [ (n, refs n) | n <- [0..10] :: [Int] ]- reach = reachable' return (lookup mp) set- in do M.keysSet reach @?= S.fromList [0, 1, 2, 4]-- prop_reach_subset (set :: S.Set Int, mp :: M.Map Int [Int]) =- M.keysSet (reachable' return (lookup mp) set)- `S.isSubsetOf` M.keysSet mp- prop_reach_roots (set :: S.Set Int, mp :: M.Map Int [Int]) =- set `S.isSubsetOf` M.keysSet mp- ==> set `S.isSubsetOf`- M.keysSet (reachable' return (lookup mp) set)-- prop_reach_nonroots (set :: S.Set Int, mp :: M.Map Int [Int]) =- set `S.intersection` M.keysSet mp- == M.keysSet (reachable' (return . const [])- (lookup mp) set)-- lookup :: (Ord a) => M.Map a [a] -> a -> Identity (Maybe (a, [a]))- lookup m k = return $ case M.lookupIndex k m of- Nothing -> Nothing- Just i -> Just $ M.elemAt i m--hash :: [TF.Test]-hash = [ testProperty "decodeBase16 . encodeBase16 == id" prop_base16- , testProperty "decodeBase64u . encodeBase64u == id" prop_base64u ]- where prop_base16 x = (decodeBase16 . encodeBase16) x == x- prop_base64u x = (decodeBase64u . encodeBase64u) x == x--monad :: [TF.Test]-monad = [ testCase "path expansion" check_virtual- , testCase "rename" check_rename ]- where check_virtual = virtualTreeMonad run testTree >> return ()- where run = do file <- readFile (floatPath "substub/substub/file")- file2 <- readFile (floatPath "substub/substub/file2")- lift $ BL.unpack file @?= ""- lift $ BL.unpack file2 @?= "foo"- check_rename = do (_, t) <- virtualTreeMonad run testTree- t' <- darcsAddMissingHashes =<< expand t- forM_ [ (p, i) | (p, i) <- list t' ] $ \(p,i) ->- assertBool ("have hash: " ++ show p) $ itemHash i /= NoHash- where run = do rename (floatPath "substub/substub/file") (floatPath "substub/file2")--posix :: [TF.Test]-posix = [ testCase "getFileStatus" $ check_stat Posix.getFileStatus- , testCase "getSymbolicLinkStatus" $ check_stat Posix.getSymbolicLinkStatus ]- where check_stat fun = flip finally (removeFile "test_empty") $ do- x <- Posix.fileSize `fmap` fun "foo_a"- Prelude.writeFile "test_empty" ""- y <- Posix.fileSize `fmap` fun "test_empty"- exist_nonexistent <- Posix.fileExists `fmap` fun "test_does_not_exist"- exist_existent <- Posix.fileExists `fmap` fun "test_empty"- assertEqual "file size" x 2- assertEqual "file size" y 0- assertBool "existence check" $ not exist_nonexistent- assertBool "existence check" exist_existent--------------------------------------- Arbitrary instances-----#if !MIN_VERSION_QuickCheck(2,8,2)--- these instances were added to QuickCheck itself in version 2.8.2-instance (Arbitrary a, Ord a) => Arbitrary (S.Set a)- where arbitrary = S.fromList `fmap` arbitrary--instance (Arbitrary k, Arbitrary v, Ord k) => Arbitrary (M.Map k v)- where arbitrary = M.fromList `fmap` arbitrary-#endif--instance Arbitrary BL.ByteString where- arbitrary = BL.pack `fmap` arbitrary--instance Arbitrary Hash where- arbitrary = sized hash'- where hash' 0 = return NoHash- hash' _ = do- tag <- oneof [return False, return True]- case tag of- False -> SHA256 . BS.pack <$> sequence [ arbitrary | _ <- [1..32] :: [Int] ]- True -> SHA1 . BS.pack <$> sequence [ arbitrary | _ <- [1..20] :: [Int] ]--instance (Monad m) => Arbitrary (TreeItem m) where- arbitrary = sized tree'- where tree' 0 = oneof [ return (File emptyBlob), return (SubTree emptyTree) ]- tree' n = oneof [ file n, subtree n ]- file 0 = return (File emptyBlob)- file _ = do content <- arbitrary- return (File $ Blob (return content) NoHash)- subtree n = do branches <- choose (1, n)- let sub name = do t <- tree' ((n - 1) `div` branches)- return (makeName $ show name, t)- sublist <- mapM sub [0..branches]- oneof [ tree' 0- , return (SubTree $ makeTree sublist)- , return $ (Stub $ return (makeTree sublist)) NoHash ]--instance (Monad m) => Arbitrary (Tree m) where- arbitrary = do item <- arbitrary- case item of- File _ -> arbitrary- Stub _ _ -> arbitrary- SubTree t -> return t--data TreeWithPath = TreeWithPath (Tree Identity) AnchoredPath deriving (Show)--instance Arbitrary TreeWithPath where- arbitrary = do t <- arbitrary- p <- oneof $ return (AnchoredPath []) :- (map (return . fst) $ list (runIdentity $ expand t))- return $ TreeWithPath t p-------------------------------- Other instances-----instance Show (Blob m) where- show (Blob _ h) = "Blob " ++ show h--instance Show (TreeItem m) where- show (File f) = "File (" ++ show f ++ ")"- show (Stub _ h) = "Stub _ " ++ show h- show (SubTree s) = "SubTree (" ++ show s ++ ")"--instance Show (Tree m) where- show t = "Tree " ++ show (treeHash t) ++ " { " ++- (concat . intersperse ", " $ itemstrs) ++ " }"- where itemstrs = map show $ listImmediate t--instance Show (Int -> Int) where- show f = "[" ++ intercalate ", " (map val [1..20]) ++ " ...]"- where val x = show x ++ " -> " ++ show (f x)---------------------------- Test utilities-----shapeEq :: Tree m -> Tree m -> Bool-shapeEq a b = Just EQ == cmpShape a b--expandedShapeEq :: (Monad m, Functor m) => Tree m -> Tree m -> m Bool-expandedShapeEq a b = (Just EQ ==) <$> cmpExpandedShape a b--cmpcat :: [Maybe Ordering] -> Maybe Ordering-cmpcat (x:y:rest) | x == y = cmpcat (x:rest)- | x == Just EQ = cmpcat (y:rest)- | y == Just EQ = cmpcat (x:rest)- | otherwise = Nothing-cmpcat [x] = x-cmpcat [] = Just EQ -- empty things are equal--cmpTree :: (Monad m, Functor m) => Tree m -> Tree m -> m (Maybe Ordering)-cmpTree x y = do x' <- expand x- y' <- expand y- con <- contentsEq x' y'- return $ cmpcat [cmpShape x' y', con]- where contentsEq a b = cmpcat <$> sequence (zipTrees cmp a b)- cmp _ (Just (File a)) (Just (File b)) = do a' <- readBlob a- b' <- readBlob b- return $ Just (compare a' b')- cmp _ _ _ = return (Just EQ) -- neutral--cmpShape :: Tree m -> Tree m -> Maybe Ordering-cmpShape t r = cmpcat $ zipTrees cmp t r- where cmp _ (Just a) (Just b) = a `item` b- cmp _ Nothing (Just _) = Just LT- cmp _ (Just _) Nothing = Just GT- cmp _ Nothing Nothing = Just EQ- item (File _) (File _) = Just EQ- item (SubTree s) (SubTree p) = s `cmpShape` p- item _ _ = Nothing--cmpExpandedShape :: (Monad m) => Tree m -> Tree m -> m (Maybe Ordering)-cmpExpandedShape a b = do x <- expand a- y <- expand b- return $ x `cmpShape` y--nondarcs :: AnchoredPath -> TreeItem m -> Bool-nondarcs (AnchoredPath (Name x:_)) _ | x == BS.pack "_darcs" = False- | otherwise = True-nondarcs (AnchoredPath []) _ = True--readDarcsPristine :: FilePath -> IO (Tree IO)-readDarcsPristine dir = do- let darcs = dir </> "_darcs"- h_inventory = darcs </> "hashed_inventory"- repo <- doesDirectoryExist darcs- unless repo $ fail $ "Not a darcs repository: " ++ dir- isHashed <- doesFileExist h_inventory- if isHashed- then do inv <- BS.readFile h_inventory- let thelines = BS.split '\n' inv- case thelines of- [] -> return emptyTree- (pris_line:_) -> do- let thehash = decodeDarcsHash $ BS.drop 9 pris_line- thesize = decodeDarcsSize $ BS.drop 9 pris_line- when (thehash == NoHash) $ fail $ "Bad pristine root: " ++ show pris_line- readDarcsHashed (darcs </> "pristine.hashed") (thesize, thehash)- else do have_pristine <- doesDirectoryExist $ darcs </> "pristine"- have_current <- doesDirectoryExist $ darcs </> "current"- case (have_pristine, have_current) of- (True, _) -> readPlainTree $ darcs </> "pristine"- (False, True) -> readPlainTree $ darcs </> "current"- (_, _) -> fail "No pristine tree is available!"
− hashed-storage/Storage/Hashed/Tree.hs
@@ -1,464 +0,0 @@-{-# LANGUAGE ScopedTypeVariables, MultiParamTypeClasses, FlexibleInstances, BangPatterns #-}---- | The abstract representation of a Tree and useful abstract utilities to--- handle those.-module Storage.Hashed.Tree- ( Tree, Blob(..), TreeItem(..), ItemType(..), Hash(..)- , makeTree, makeTreeWithHash, emptyTree, emptyBlob, makeBlob, makeBlobBS-- -- * Unfolding stubbed (lazy) Trees.- --- -- | By default, Tree obtained by a read function is stubbed: it will- -- contain Stub items that need to be executed in order to access the- -- respective subtrees. 'expand' will produce an unstubbed Tree.- , expandUpdate, expand, expandPath, checkExpand-- -- * Tree access and lookup.- , items, list, listImmediate, treeHash- , lookup, find, findFile, findTree, itemHash, itemType- , zipCommonFiles, zipFiles, zipTrees, diffTrees-- -- * Files (Blobs).- , readBlob-- -- * Filtering trees.- , FilterTree(..), restrict-- -- * Manipulating trees.- , modifyTree, updateTree, partiallyUpdateTree, updateSubtrees, overlay- , addMissingHashes ) where--import Control.Exception( catch, IOException )-import Prelude hiding( lookup, filter, all, catch )-import Storage.Hashed.AnchoredPath-import Storage.Hashed.Hash--import qualified Data.ByteString.Lazy.Char8 as BL-import qualified Data.ByteString.Char8 as BS-import qualified Data.Map as M--import Data.Maybe( catMaybes, isNothing )-import Data.Either( lefts, rights )-import Data.List( union, sort )-import Control.Monad( filterM )-import Control.Applicative( (<$>) )------------------------------------- Tree, Blob and friends-----data Blob m = Blob !(m BL.ByteString) !Hash-data TreeItem m = File !(Blob m)- | SubTree !(Tree m)- | Stub !(m (Tree m)) !Hash--data ItemType = TreeType | BlobType deriving (Show, Eq, Ord)---- | Abstraction of a filesystem tree.--- Please note that the Tree returned by the respective read operations will--- have TreeStub items in it. To obtain a Tree without such stubs, call--- expand on it, eg.:------ > tree <- readDarcsPristine "." >>= expand------ When a Tree is expanded, it becomes \"final\". All stubs are forced and the--- Tree can be traversed purely. Access to actual file contents stays in IO--- though.------ A Tree may have a Hash associated with it. A pair of Tree's is identical--- whenever their hashes are (the reverse need not hold, since not all Trees--- come equipped with a hash).-data Tree m = Tree { items :: (M.Map Name (TreeItem m))- -- | Get hash of a Tree. This is guaranteed to uniquely- -- identify the Tree (including any blob content), as far as- -- cryptographic hashes are concerned. Sha256 is recommended.- , treeHash :: !Hash }--listImmediate :: Tree m -> [(Name, TreeItem m)]-listImmediate = M.toList . items---- | Get a hash of a TreeItem. May be Nothing.-itemHash :: TreeItem m -> Hash-itemHash (File (Blob _ h)) = h-itemHash (SubTree t) = treeHash t-itemHash (Stub _ h) = h--itemType :: TreeItem m -> ItemType-itemType (File _) = BlobType-itemType (SubTree _) = TreeType-itemType (Stub _ _) = TreeType--emptyTree :: (Monad m) => Tree m-emptyTree = Tree { items = M.empty- , treeHash = NoHash }--emptyBlob :: (Monad m) => Blob m-emptyBlob = Blob (return BL.empty) NoHash--makeBlob :: (Monad m) => BL.ByteString -> Blob m-makeBlob str = Blob (return str) (sha256 str)--makeBlobBS :: (Monad m) => BS.ByteString -> Blob m-makeBlobBS s' = let s = BL.fromChunks [s'] in Blob (return s) (sha256 s)--makeTree :: (Monad m) => [(Name,TreeItem m)] -> Tree m-makeTree l = Tree { items = M.fromList l- , treeHash = NoHash }--makeTreeWithHash :: (Monad m) => [(Name,TreeItem m)] -> Hash -> Tree m-makeTreeWithHash l h = Tree { items = M.fromList l- , treeHash = h }---------------------------------------- Tree access and lookup------- | Look up a 'Tree' item (an immediate subtree or blob).-lookup :: Tree m -> Name -> Maybe (TreeItem m)-lookup t n = M.lookup n (items t)--find' :: TreeItem m -> AnchoredPath -> Maybe (TreeItem m)-find' t (AnchoredPath []) = Just t-find' (SubTree t) (AnchoredPath (d : rest)) =- case lookup t d of- Just sub -> find' sub (AnchoredPath rest)- Nothing -> Nothing-find' _ _ = Nothing---- | Find a 'TreeItem' by its path. Gives 'Nothing' if the path is invalid.-find :: Tree m -> AnchoredPath -> Maybe (TreeItem m)-find = find' . SubTree---- | Find a 'Blob' by its path. Gives 'Nothing' if the path is invalid, or does--- not point to a Blob.-findFile :: Tree m -> AnchoredPath -> Maybe (Blob m)-findFile t p = case find t p of- Just (File x) -> Just x- _ -> Nothing---- | Find a 'Tree' by its path. Gives 'Nothing' if the path is invalid, or does--- not point to a Tree.-findTree :: Tree m -> AnchoredPath -> Maybe (Tree m)-findTree t p = case find t p of- Just (SubTree x) -> Just x- _ -> Nothing---- | List all contents of a 'Tree'.-list :: Tree m -> [(AnchoredPath, TreeItem m)]-list t_ = paths t_ (AnchoredPath [])- where paths t p = [ (appendPath p n, i)- | (n,i) <- listImmediate t ] ++- concat [ paths subt (appendPath p subn)- | (subn, SubTree subt) <- listImmediate t ]--expandUpdate :: (Monad m) => (AnchoredPath -> Tree m -> m (Tree m)) -> Tree m -> m (Tree m)-expandUpdate update t_ = go (AnchoredPath []) t_- where go path t = do- let subtree (name, sub) = do tree <- go (path `appendPath` name) =<< unstub sub- return (name, SubTree tree)- expanded <- mapM subtree [ x | x@(_, item) <- listImmediate t, isSub item ]- let orig_map = M.filter (not . isSub) (items t)- expanded_map = M.fromList expanded- tree = t { items = M.union orig_map expanded_map }- update path tree---- | Expand a stubbed Tree into a one with no stubs in it. You might want to--- filter the tree before expanding to save IO. This is the basic--- implementation, which may be overriden by some Tree instances (this is--- especially true of the Index case).-expand :: (Monad m) => Tree m -> m (Tree m)-expand = expandUpdate $ \_ -> return---- | Unfold a path in a (stubbed) Tree, such that the leaf node of the path is--- reachable without crossing any stubs. Moreover, the leaf ought not be a Stub--- in the resulting Tree. A non-existent path is expanded as far as it can be.-expandPath :: (Monad m) => Tree m -> AnchoredPath -> m (Tree m)-expandPath t_ path_ = expand' t_ path_- where expand' t (AnchoredPath []) = return t- expand' t (AnchoredPath (n:rest)) =- case lookup t n of- (Just item) | isSub item -> amend t n rest =<< unstub item- _ -> return t -- fail $ "Descent error in expandPath: " ++ show path_- amend t name rest sub = do- sub' <- expand' sub (AnchoredPath rest)- let tree = t { items = M.insert name (SubTree sub') (items t) }- return tree---- | Check the disk version of a Tree: expands it, and checks each--- hash. Returns either the expanded tree or a list of AnchoredPaths--- where there are problems. The first argument is the hashing function--- used to create the tree.-checkExpand :: (TreeItem IO -> IO Hash) -> Tree IO- -> IO (Either [(AnchoredPath, Hash, Maybe Hash)] (Tree IO))-checkExpand hashFunc t = go (AnchoredPath []) t- where- go path t_ = do- let- subtree (name, sub) =- do let here = path `appendPath` name- sub' <- (Just <$> unstub sub) `catch` \(_ :: IOException) -> return Nothing- case sub' of- Nothing -> return $ Left [(here, treeHash t_, Nothing)]- Just sub'' -> do- treeOrTrouble <- go (path `appendPath` name) sub''- return $ case treeOrTrouble of- Left problems -> Left problems- Right tree -> Right (name, SubTree tree)- badBlob (_, f@(File (Blob _ h))) =- fmap (/= h) (hashFunc f `catch` (\(_ :: IOException) -> return NoHash))- badBlob _ = return False- render (name, f@(File (Blob _ h))) = do- h' <- (Just <$> hashFunc f) `catch` \(_ :: IOException) -> return Nothing- return (path `appendPath` name, h, h')- render (name, _) = return (path `appendPath` name, NoHash, Nothing)- subs <- mapM subtree [ x | x@(_, item) <- listImmediate t_, isSub item ]- badBlobs <- filterM badBlob (listImmediate t) >>= mapM render- let problems = badBlobs ++ (concat $ lefts subs)- if null problems- then do- let orig_map = M.filter (not . isSub) (items t)- expanded_map = M.fromList $ rights subs- tree = t_ {items = orig_map `M.union` expanded_map}- h' <- hashFunc (SubTree t_)- if h' `match` treeHash t_- then return $ Right tree- else return $ Left [(path, treeHash t_, Just h')]- else return $ Left problems--class (Monad m) => FilterTree a m where- -- | Given @pred tree@, produce a 'Tree' that only has items for which- -- @pred@ returns @True@.- -- The tree might contain stubs. When expanded, these will be subject to- -- filtering as well.- filter :: (AnchoredPath -> TreeItem m -> Bool) -> a m -> a m--instance (Monad m) => FilterTree Tree m where- filter predicate t_ = filter' t_ (AnchoredPath [])- where filter' t path = t { items = M.mapMaybeWithKey (wibble path) $ items t }- wibble path name item =- let npath = path `appendPath` name in- if predicate npath item- then Just $ filterSub npath item- else Nothing- filterSub npath (SubTree t) = SubTree $ filter' t npath- filterSub npath (Stub stub h) =- Stub (do x <- stub- return $ filter' x npath) h- filterSub _ x = x---- | Given two Trees, a @guide@ and a @tree@, produces a new Tree that is a--- identical to @tree@, but only has those items that are present in both--- @tree@ and @guide@. The @guide@ Tree may not contain any stubs.-restrict :: (FilterTree t m, Monad n) => Tree n -> t m -> t m-restrict guide tree = filter accept tree- where accept path item =- case (find guide path, item) of- (Just (SubTree _), SubTree _) -> True- (Just (SubTree _), Stub _ _) -> True- (Just (File _), File _) -> True- (Just (Stub _ _), _) ->- error "*sulk* Go away, you, you precondition violator!"- (_, _) -> False---- | Read a Blob into a Lazy ByteString. Might be backed by an mmap, use with--- care.-readBlob :: Blob m -> m BL.ByteString-readBlob (Blob r _) = r---- | For every pair of corresponding blobs from the two supplied trees,--- evaluate the supplied function and accumulate the results in a list. Hint:--- to get IO actions through, just use sequence on the resulting list.--- NB. This won't expand any stubs.-zipCommonFiles :: (AnchoredPath -> Blob m -> Blob m -> a) -> Tree m -> Tree m -> [a]-zipCommonFiles f a b = catMaybes [ flip (f p) x `fmap` findFile a p- | (p, File x) <- list b ]---- | For each file in each of the two supplied trees, evaluate the supplied--- function (supplying the corresponding file from the other tree, or Nothing)--- and accumulate the results in a list. Hint: to get IO actions through, just--- use sequence on the resulting list. NB. This won't expand any stubs.-zipFiles :: (AnchoredPath -> Maybe (Blob m) -> Maybe (Blob m) -> a)- -> Tree m -> Tree m -> [a]-zipFiles f a b = [ f p (findFile a p) (findFile b p)- | p <- paths a `sortedUnion` paths b ]- where paths t = sort [ p | (p, File _) <- list t ]--zipTrees :: (AnchoredPath -> Maybe (TreeItem m) -> Maybe (TreeItem m) -> a)- -> Tree m -> Tree m -> [a]-zipTrees f a b = [ f p (find a p) (find b p)- | p <- reverse (paths a `sortedUnion` paths b) ]- where paths t = sort [ p | (p, _) <- list t ]---- | Helper function for taking the union of AnchoredPath lists that--- are already sorted. This function does not check the precondition--- so use it carefully.-sortedUnion :: [AnchoredPath] -> [AnchoredPath] -> [AnchoredPath]-sortedUnion [] ys = ys-sortedUnion xs [] = xs-sortedUnion a@(x:xs) b@(y:ys) = case compare x y of- LT -> x : sortedUnion xs b- EQ -> x : sortedUnion xs ys- GT -> y : sortedUnion a ys---- | Cautiously extracts differing subtrees from a pair of Trees. It will never--- do any unneccessary expanding. Tree hashes are used to cut the comparison as--- high up the Tree branches as possible. The result is a pair of trees that do--- not share any identical subtrees. They are derived from the first and second--- parameters respectively and they are always fully expanded. It might be--- advantageous to feed the result into 'zipFiles' or 'zipTrees'.-diffTrees :: forall m. (Functor m, Monad m) => Tree m -> Tree m -> m (Tree m, Tree m)-diffTrees left right =- if treeHash left `match` treeHash right- then return (emptyTree, emptyTree)- else diff left right- where isFile (File _) = True- isFile _ = False- notFile = not . isFile- isEmpty = null . listImmediate- subtree :: TreeItem m -> m (Tree m)- subtree (Stub x _) = x- subtree (SubTree x) = return x- subtree (File _) = error "diffTrees tried to descend a File as a subtree"- maybeUnfold (Stub x _) = SubTree `fmap` (x >>= expand)- maybeUnfold (SubTree x) = SubTree `fmap` expand x- maybeUnfold i = return i- immediateN t = [ n | (n, _) <- listImmediate t ]- diff left' right' = do- is <- sequence [- case (lookup left' n, lookup right' n) of- (Just l, Nothing) -> do- l' <- maybeUnfold l- return (n, Just l', Nothing)- (Nothing, Just r) -> do- r' <- maybeUnfold r- return (n, Nothing, Just r')- (Just l, Just r)- | itemHash l `match` itemHash r ->- return (n, Nothing, Nothing)- | notFile l && notFile r ->- do x <- subtree l- y <- subtree r- (x', y') <- diffTrees x y- if isEmpty x' && isEmpty y'- then return (n, Nothing, Nothing)- else return (n, Just $ SubTree x', Just $ SubTree y')- | isFile l && isFile r ->- return (n, Just l, Just r)- | otherwise ->- do l' <- maybeUnfold l- r' <- maybeUnfold r- return (n, Just l', Just r')- _ -> error "n lookups failed"- | n <- immediateN left' `union` immediateN right' ]- let is_l = [ (n, l) | (n, Just l, _) <- is ]- is_r = [ (n, r) | (n, _, Just r) <- is ]- return (makeTree is_l, makeTree is_r)---- | Modify a Tree (by replacing, or removing or adding items).-modifyTree :: (Monad m) => Tree m -> AnchoredPath -> Maybe (TreeItem m) -> Tree m-modifyTree t_ p_ i_ = snd $ go t_ p_ i_- where fix t unmod items' = (unmod, t { items = (countmap items':: Int) `seq` items'- , treeHash = if unmod then treeHash t else NoHash })-- go t (AnchoredPath []) (Just (SubTree sub)) = (treeHash t `match` treeHash sub, sub)-- go t (AnchoredPath [n]) (Just item) = fix t unmod items'- where !items' = M.insert n item (items t)- !unmod = itemHash item `match` case lookup t n of- Nothing -> NoHash- Just i -> itemHash i-- go t (AnchoredPath [n]) Nothing = fix t unmod items'- where !items' = M.delete n (items t)- !unmod = isNothing $ lookup t n-- go t path@(AnchoredPath (n:r)) item = fix t unmod items'- where subtree s = go s (AnchoredPath r) item- !items' = M.insert n sub (items t)- !sub = snd sub'- !unmod = fst sub'- !sub' = case lookup t n of- Just (SubTree s) -> let (mod', sub'') = subtree s in (mod', SubTree sub'')- Just (Stub s _) -> (False, Stub (do x <- s- return $! snd $! subtree x) NoHash)- Nothing -> (False, SubTree $! snd $! subtree emptyTree)- _ -> error $ "Modify tree at " ++ show path-- go _ (AnchoredPath []) (Just (Stub _ _)) =- error $ "BUG: Error descending in modifyTree, path = " ++ show p_- go _ (AnchoredPath []) (Just (File _)) =- error $ "BUG: Error descending in modifyTree, path = " ++ show p_- go _ (AnchoredPath []) Nothing =- error $ "BUG: Error descending in modifyTree, path = " ++ show p_--countmap :: forall a k. M.Map k a -> Int-countmap = M.fold (\_ i -> i + 1) 0--updateSubtrees :: (Tree m -> Tree m) -> Tree m -> Tree m-updateSubtrees fun t =- fun $ t { items = M.mapWithKey (curry $ snd . update) $ items t- , treeHash = NoHash }- where update (k, SubTree s) = (k, SubTree $ updateSubtrees fun s)- update (k, File f) = (k, File f)- update (_, Stub _ _) = error "Stubs not supported in updateTreePostorder"---- | Does /not/ expand the tree.-updateTree :: (Functor m, Monad m) => (TreeItem m -> m (TreeItem m)) -> Tree m -> m (Tree m)-updateTree fun t = partiallyUpdateTree fun (\_ _ -> True) t---- | Does /not/ expand the tree.-partiallyUpdateTree :: (Functor m, Monad m) => (TreeItem m -> m (TreeItem m))- -> (AnchoredPath -> TreeItem m -> Bool) -> Tree m -> m (Tree m)-partiallyUpdateTree fun predi t' = go (AnchoredPath []) t'- where go path t = do- items' <- M.fromList <$> mapM (maybeupdate path) (listImmediate t)- SubTree t'' <- fun . SubTree $ t { items = items'- , treeHash = NoHash }- return t''- maybeupdate path (k, item) = case predi (path `appendPath` k) item of- True -> update (path `appendPath` k) (k, item)- False -> return (k, item)- update path (k, SubTree tree) = (\new -> (k, SubTree new)) <$> go path tree- update _ (k, item) = (\new -> (k, new)) <$> fun item---- | Lay one tree over another. The resulting Tree will look like the base (1st--- parameter) Tree, although any items also present in the overlay Tree will be--- taken from the overlay. It is not allowed to overlay a different kind of an--- object, nor it is allowed for the overlay to add new objects to base. This--- means that the overlay Tree should be a subset of the base Tree (although--- any extraneous items will be ignored by the implementation).-overlay :: (Functor m, Monad m) => Tree m -> Tree m -> Tree m-overlay base over = Tree { items = M.fromList immediate- , treeHash = NoHash }- where immediate = [ (n, get n) | (n, _) <- listImmediate base ]- get n = case (M.lookup n $ items base, M.lookup n $ items over) of- (Just (File _), Just f@(File _)) -> f- (Just (SubTree b), Just (SubTree o)) -> SubTree $ overlay b o- (Just (Stub b _), Just (SubTree o)) -> Stub (flip overlay o `fmap` b) NoHash- (Just (SubTree b), Just (Stub o _)) -> Stub (overlay b `fmap` o) NoHash- (Just (Stub b _), Just (Stub o _)) -> Stub (do o' <- o- b' <- b- return $ overlay b' o') NoHash- (Just x, _) -> x- (_, _) -> error $ "Unexpected case in overlay at get " ++ show n ++ "."--addMissingHashes :: (Monad m, Functor m) => (TreeItem m -> m Hash) -> Tree m -> m (Tree m)-addMissingHashes make = updateTree update -- use partiallyUpdateTree here- where update (SubTree t) = make (SubTree t) >>= \x -> return $ SubTree (t { treeHash = x })- update (File blob@(Blob con NoHash)) =- do hash <- make $ File blob- return $ File (Blob con hash)- update (Stub s NoHash) = update . SubTree =<< s- update x = return x-------- Private utilities shared among multiple functions. ----------unstub :: (Monad m) => TreeItem m -> m (Tree m)-unstub (Stub s _) = s-unstub (SubTree s) = return s-unstub _ = return emptyTree--isSub :: TreeItem m -> Bool-isSub (File _) = False-isSub _ = True-
− hashed-storage/Storage/Hashed/Utils.hs
@@ -1,148 +0,0 @@-{-# LANGUAGE CPP, ScopedTypeVariables #-}---- | Mostly internal utilities for use by the rest of the library. Subject to--- removal without further notice.-module Storage.Hashed.Utils where--import Prelude hiding ( lookup, catch )-import System.Mem( performGC )-import Bundled.Posix( getFileStatus, fileSize )-import System.Directory( getCurrentDirectory, setCurrentDirectory )-import System.FilePath( (</>), isAbsolute )-import Data.Int( Int64 )-import Data.Maybe( catMaybes )-import Control.Exception( catch, bracket, SomeException(..) )-import Control.Monad( when )-import Control.Monad.Identity( runIdentity )-import Control.Applicative( (<$>) )--import Foreign.ForeignPtr( withForeignPtr )-import Foreign.Ptr( plusPtr )-import Data.ByteString.Internal( toForeignPtr, memcpy )-import System.IO (withFile, IOMode(ReadMode), hSeek, SeekMode(AbsoluteSeek))-import Data.Bits( Bits )-#ifdef BIGENDIAN-import Data.Bits( (.&.), (.|.), shift, shiftL, rotateR )-#endif--import qualified Data.ByteString.Lazy.Char8 as BL-import qualified Data.ByteString.Char8 as BS8-import qualified Data.ByteString as BS--import qualified Data.Set as S-import qualified Data.Map as M---- | Pointer to a filesystem, possibly with start/end offsets. Supposed to be--- fed to (uncurry mmapFileByteString) or similar.-type FileSegment = (FilePath, Maybe (Int64, Int))---- | Read in a FileSegment into a Lazy ByteString. Implemented using mmap.-readSegment :: FileSegment -> IO BL.ByteString-readSegment (f,range) = do- bs <- tryToRead- `catch` (\(_::SomeException) -> do- size <- fileSize `fmap` getFileStatus f- if size == 0- then return BS8.empty- else performGC >> tryToRead)- return $ BL.fromChunks [bs]- where- tryToRead = do - case range of- Nothing -> BS.readFile f- Just (off, size) -> withFile f ReadMode $ \h -> do- hSeek h AbsoluteSeek $ fromIntegral off- BS.hGet h size -{-# INLINE readSegment #-}---- | Run an IO action with @path@ as a working directory. Does neccessary--- bracketing.-withCurrentDirectory :: FilePath -> IO a -> IO a-withCurrentDirectory name =- bracket- (do cwd <- getCurrentDirectory- when (name /= "") (setCurrentDirectory name)- return cwd)- (\oldwd -> setCurrentDirectory oldwd- `catch` \(_::SomeException) -> return ())- . const--makeAbsolute :: FilePath -> IO FilePath-makeAbsolute p = do- cwd <- getCurrentDirectory- return $! if isAbsolute p then p else cwd </> p---- Wow, unsafe.-unsafePokeBS :: BS8.ByteString -> BS8.ByteString -> IO ()-unsafePokeBS to from =- do let (fp_to, off_to, len_to) = toForeignPtr to- (fp_from, off_from, len_from) = toForeignPtr from- when (len_to /= len_from) $ fail $ "Length mismatch in unsafePokeBS: from = "- ++ show len_from ++ " /= to = " ++ show len_to- withForeignPtr fp_from $ \p_from ->- withForeignPtr fp_to $ \p_to ->- memcpy (plusPtr p_to off_to)- (plusPtr p_from off_from)- (fromIntegral len_to)--align :: Integral a => a -> a -> a-align boundary i = case i `rem` boundary of- 0 -> i- x -> i + boundary - x-{-# INLINE align #-}--xlate32 :: (Num a, Bits a) => a -> a-xlate64 :: (Num a, Bits a) => a -> a--#ifdef LITTLEENDIAN-xlate32 = id-xlate64 = id-#endif--#ifdef BIGENDIAN-bytemask :: (Num a, Bits a) => a-bytemask = 255--xlate32 a = ((a .&. (bytemask `shift` 0)) `shiftL` 24) .|.- ((a .&. (bytemask `shift` 8)) `shiftL` 8) .|.- ((a .&. (bytemask `shift` 16)) `rotateR` 8) .|.- ((a .&. (bytemask `shift` 24)) `rotateR` 24)--xlate64 a = ((a .&. (bytemask `shift` 0)) `shiftL` 56) .|.- ((a .&. (bytemask `shift` 8)) `shiftL` 40) .|.- ((a .&. (bytemask `shift` 16)) `shiftL` 24) .|.- ((a .&. (bytemask `shift` 24)) `shiftL` 8) .|.- ((a .&. (bytemask `shift` 32)) `rotateR` 8) .|.- ((a .&. (bytemask `shift` 40)) `rotateR` 24) .|.- ((a .&. (bytemask `shift` 48)) `rotateR` 40) .|.- ((a .&. (bytemask `shift` 56)) `rotateR` 56)-#endif---- | Find a monadic fixed point of @f@ that is the least above @i@. (Will--- happily diverge if there is none.)-mfixFrom :: (Eq a, Functor m, Monad m) => (a -> m a) -> a -> m a-mfixFrom f i = do x <- f i- if x == i then return i- else mfixFrom f x---- | Find a fixed point of @f@ that is the least above @i@. (Will happily--- diverge if there is none.)-fixFrom :: (Eq a) => (a -> a) -> a -> a-fixFrom f i = runIdentity $ mfixFrom (return . f) i---- | For a @refs@ function, a @map@ (@key@ -> @value@) and a @rootSet@, find a--- submap of @map@ such that all items in @map@ are reachable, through @refs@--- from @rootSet@.-reachable :: forall monad key value. (Functor monad, Monad monad, Ord key, Eq value) =>- (value -> monad [key])- -> (key -> monad (Maybe (key, value)))- -> S.Set key -> monad (M.Map key value)-reachable refs lookup rootSet =- do lookupSet rootSet >>= mfixFrom expand- where lookupSet :: S.Set key -> monad (M.Map key value)- expand :: M.Map key value -> monad (M.Map key value)-- lookupSet s = do list <- mapM lookup (S.toAscList s)- return $ M.fromAscList (catMaybes list)- expand from = do refd <- concat <$> mapM refs (M.elems from)- M.union from <$> lookupSet (S.fromList refd)
− hashed-storage/test.hs
@@ -1,17 +0,0 @@-{-# LANGUAGE ScopedTypeVariables #-}-import Storage.Hashed.Test( tests )-import Prelude hiding( catch )-import Test.Framework( defaultMain )-import System.Directory( createDirectory, removeDirectoryRecursive- , setCurrentDirectory )-import Codec.Archive.Zip( extractFilesFromArchive, toArchive )-import qualified Data.ByteString.Lazy as BL-import Control.Exception( catch, IOException )--main :: IO ()-main = do zipFile <- toArchive `fmap` BL.readFile "hashed-storage/testdata.zip"- removeDirectoryRecursive "_test_playground" `catch` \(_ :: IOException) -> return ()- createDirectory "_test_playground"- setCurrentDirectory "_test_playground"- extractFilesFromArchive [] zipFile- defaultMain tests
− hashed-storage/testdata.zip
binary file changed (16669 → absent bytes)
release/distributed-context view
@@ -1,1 +1,1 @@-Just "\nContext:\n\n[TAG 2.10.3\nGuillaume Hoffmann <guillaumh@gmail.com>**20160129115730\n Ignore-this: 7eac9bf53015a285c43e6ea2b1e11a1d\n] \n"+Just "\nContext:\n\n[TAG 2.12.0\nGuillaume Hoffmann <guillaumh@gmail.com>**20160429142058\n Ignore-this: 5c8cbe0424942686a2168f9e6fd8e35d\n] \n"
+ src/Bundled/Posix.hsc view
@@ -0,0 +1,116 @@+{-# LANGUAGE CPP, RankNTypes #-}++module Bundled.Posix( getFdStatus, getSymbolicLinkStatus, getFileStatus+ , getFileStatusBS+ , fileExists+ , modificationTime, fileSize, FileStatus+ , EpochTime, isDirectory, isRegularFile ) where++import qualified Data.ByteString.Char8 as BS+#if mingw32_HOST_OS+#else+import Data.ByteString.Unsafe( unsafeUseAsCString )+#endif+import Foreign.Marshal.Alloc ( allocaBytes )+import Foreign.C.Error ( throwErrno, getErrno, eNOENT )+import Foreign.C.Types ( CTime, CInt )+import Foreign.Ptr ( Ptr )++import System.Posix.Internals+ ( CStat, c_fstat, sizeof_stat+ , st_mode, st_size, st_mtime, s_isdir, s_isreg )+#if mingw32_HOST_OS+import System.Posix.Internals ( c_stat, CFilePath )+#endif++import System.Posix.Types ( Fd(..), CMode, EpochTime )++#if mingw32_HOST_OS+import Foreign.C.String( withCWString, CWString )+#else+import Foreign.C.String ( withCString, CString )+#endif++#if mingw32_HOST_OS+import Data.Int ( Int64 )++type FileOffset = Int64+lstat :: CFilePath -> Ptr CStat -> IO CInt+lstat = c_stat+#else+import System.Posix.Types ( FileOffset )+import System.Posix.Internals( lstat )+#endif++#if mingw32_HOST_OS+bsToPath :: forall a. BS.ByteString -> (CWString -> IO a) -> IO a+bsToPath s f = withCWString (BS.unpack s) f+strToPath :: forall a. String -> (CWString -> IO a) -> IO a+strToPath = withCWString+#else+bsToPath :: forall a. BS.ByteString -> (CString -> IO a) -> IO a+bsToPath = unsafeUseAsCString+strToPath :: forall a. String -> (CString -> IO a) -> IO a+strToPath = withCString+#endif++data FileStatus = FileStatus {+ fst_exists :: !Bool,+ fst_mode :: !CMode,+ fst_mtime :: !CTime,+ fst_size :: !FileOffset+ }++getFdStatus :: Fd -> IO FileStatus+getFdStatus (Fd fd) = do+ do_stat (c_fstat fd)++do_stat :: (Ptr CStat -> IO CInt) -> IO FileStatus+do_stat stat_func = do+ allocaBytes sizeof_stat $! \p -> do+ ret <- stat_func p+ if (ret == -1) then do err <- getErrno+ if (err == eNOENT)+ then return $! (FileStatus False 0 0 0)+ else throwErrno "do_stat"+ else do mode <- st_mode p+ mtime <- st_mtime p+ size <- st_size p+ return $! FileStatus True mode mtime size+{-# INLINE do_stat #-}++isDirectory :: FileStatus -> Bool+isDirectory = s_isdir . fst_mode++isRegularFile :: FileStatus -> Bool+isRegularFile = s_isreg . fst_mode++modificationTime :: FileStatus -> EpochTime+modificationTime = fst_mtime++fileSize :: FileStatus -> FileOffset+fileSize = fst_size++fileExists :: FileStatus -> Bool+fileExists = fst_exists++#include <sys/stat.h>++-- lstat is broken on win32 with at least GHC 6.10.3+getSymbolicLinkStatus :: FilePath -> IO FileStatus+##if mingw32_HOST_OS+getSymbolicLinkStatus = getFileStatus+##else+getSymbolicLinkStatus fp =+ do_stat (\p -> (fp `strToPath` (`lstat` p)))+##endif++getFileStatus :: FilePath -> IO FileStatus+getFileStatus fp =+ do_stat (\p -> (fp `strToPath` (`lstat` p)))++-- | Requires NULL-terminated bytestring -> unsafe! Use with care.+getFileStatusBS :: BS.ByteString -> IO FileStatus+getFileStatusBS fp =+ do_stat (\p -> (fp `bsToPath` (`lstat` p)))+{-# INLINE getFileStatusBS #-}
src/Darcs/Patch.hs view
@@ -16,13 +16,14 @@ -- Boston, MA 02110-1301, USA. {-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE CPP, UndecidableInstances #-} -- XXX Undecidable only in GHC < 7 - module Darcs.Patch ( RepoPatch+ , RepoType+ , IsRepoType , PrimOf , Named+ , WrappedNamed , Patchy , fromPrim , fromPrims@@ -62,6 +63,7 @@ , invert , invertFL , invertRL+ , commuteFL , commuteFLorComplain , commuteRL , readPatch@@ -74,6 +76,7 @@ , applyToFilePaths , apply , applyToTree+ , maybeApplyToTree , effectOnFilePaths , patch2patchinfo , summary@@ -89,8 +92,8 @@ import Darcs.Patch.Apply ( applyToFilePaths, effectOnFilePaths, applyToTree,- ApplyState )-import Darcs.Patch.Commute ( commuteFLorComplain, commuteRL )+ maybeApplyToTree, ApplyState )+import Darcs.Patch.Commute ( commuteFL, commuteFLorComplain, commuteRL ) import Darcs.Patch.Conflict ( listConflictedFiles, resolveConflicts ) import Darcs.Patch.Effect ( Effect(effect) ) import Darcs.Patch.Invert ( invertRL, invertFL )@@ -100,6 +103,7 @@ getdeps, infopatch, patch2patchinfo, patchname, patchcontents )+import Darcs.Patch.Named.Wrapped ( WrappedNamed ) import Darcs.Patch.Patchy ( Patchy, showPatch, showNicely, showContextPatch, invert,@@ -118,14 +122,15 @@ tryToShrink, PrimPatch, PrimPatchBase(..) ) import Darcs.Patch.Read ( readPatch, readPatchPartial )-import Darcs.Patch.Rebase.NameHack ( NameHack ) import Darcs.Patch.Repair ( isInconsistent ) import Darcs.Patch.RepoPatch ( RepoPatch )+import Darcs.Patch.RepoType ( RepoType, IsRepoType ) import Darcs.Patch.Summary ( xmlSummary, plainSummary, plainSummaryPrims ) import Darcs.Patch.TokenReplace ( forceTokReplace ) import Darcs.Patch.V1.Commute ( merge ) -import Storage.Hashed.Tree( Tree )+import Darcs.Util.Tree( Tree ) -instance (Patchy p, NameHack p, ApplyState p ~ Tree) => Patchy (Named p)+instance (Patchy p, ApplyState p ~ Tree) => Patchy (Named p)+instance (Patchy p, ApplyState p ~ Tree) => Patchy (WrappedNamed rt p)
src/Darcs/Patch/Annotate.hs view
@@ -1,4 +1,3 @@-{-# OPTIONS_GHC -fno-warn-missing-methods #-} {-# LANGUAGE CPP, OverloadedStrings, TypeSynonymInstances, MultiParamTypeClasses #-} -- Copyright (C) 2010 Petr Rockai@@ -40,7 +39,8 @@ , AnnotateResult ) where -import Prelude hiding ( pi )+import Prelude ()+import Darcs.Prelude import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as BC@@ -52,15 +52,14 @@ import Data.Maybe( isJust, mapMaybe ) import Control.Monad.State ( modify, when, gets, State, execState )-import Control.Applicative( (<$>) ) -import Darcs.Patch.ApplyMonad( ApplyMonad(..) )+import Darcs.Patch.ApplyMonad( ApplyMonad(..), ApplyMonadTree(..) ) import Darcs.Patch.Apply ( Apply, apply, ApplyState ) import Darcs.Patch.Info ( PatchInfo(..), showPatchInfoUI, piAuthor, makePatchname ) import Darcs.Patch.PatchInfoAnd( info, PatchInfoAnd ) import Darcs.Patch.Witnesses.Ordered -import Storage.Hashed.Tree( Tree )+import Darcs.Util.Tree( Tree ) import Darcs.Util.Path ( FileName, movedirfilename, fn2ps, ps2fn ) import Darcs.Util.Printer( renderString, RenderMode(..) ) import Darcs.Util.ByteString ( linesPS, unlinesPS )@@ -87,14 +86,14 @@ type AnnotatedM = State Annotated --- XXX: No explicit method nor default method for 'editFile', 'editDirectory'-instance ApplyMonad AnnotatedM Tree where+instance ApplyMonad Tree AnnotatedM where type ApplyMonadBase AnnotatedM = AnnotatedM nestedApply _ _ = undefinedFun "nestedApply" liftApply _ _ = undefinedFun "liftApply" getApplyState = undefinedFun "getApplyState"- putApplyState _ = undefinedFun "putApplyState"++instance ApplyMonadTree AnnotatedM where mReadFilePS = undefinedFun "mReadFilePS" mDoesFileExist _ = return True@@ -177,7 +176,7 @@ annotate' :: (Apply p, ApplyState p ~ Tree)- => FL (PatchInfoAnd p) wX wY+ => FL (PatchInfoAnd rt p) wX wY -> Annotated -> Annotated annotate' NilFL ann = ann@@ -188,7 +187,7 @@ annotate :: (Apply p, ApplyState p ~ Tree) => D.DiffAlgorithm- -> FL (PatchInfoAnd p) wX wY+ -> FL (PatchInfoAnd rt p) wX wY -> FileName -> B.ByteString -> AnnotateResult@@ -206,7 +205,7 @@ annotateDirectory :: (Apply p, ApplyState p ~ Tree) => D.DiffAlgorithm- -> FL (PatchInfoAnd p) wX wY+ -> FL (PatchInfoAnd rt p) wX wY -> FileName -> [FileName] -> AnnotateResult
src/Darcs/Patch/Apply.hs view
@@ -33,19 +33,20 @@ , applyToFilePaths , applyToTree , applyToState+ , maybeApplyToTree , applyToFileMods , effectOnFilePaths ) where -import Prelude hiding ( catch, pi )+import Prelude ()+import Darcs.Prelude import Data.Set ( Set ) -import Control.Applicative ( (<$>) )+import Control.Exception ( catch, IOException ) import Control.Arrow ( (***) ) -import Storage.Hashed.Tree( Tree )-import Storage.Hashed.Monad( virtualTreeMonad )+import Darcs.Util.Tree( Tree ) import Darcs.Patch.ApplyMonad ( ApplyMonad(..), withFileNames, ApplyMonadTrans(..) ) import Darcs.Util.Path( FileName, fn2fp, fp2fn )@@ -55,7 +56,7 @@ class Apply p where type ApplyState p :: (* -> *) -> *- apply :: ApplyMonad m (ApplyState p) => p wX wY -> m ()+ apply :: ApplyMonad (ApplyState p) m => p wX wY -> m () instance Apply p => Apply (FL p) where type ApplyState (FL p) = ApplyState p@@ -93,15 +94,21 @@ => p wX wY -> Tree m -> m (Tree m)-applyToTree patch t = snd <$> virtualTreeMonad (apply patch) t-+applyToTree = applyToState -applyToState :: forall p m wX wY. (Apply p, ApplyMonadTrans m (ApplyState p))+applyToState :: forall p m wX wY. (Apply p, ApplyMonadTrans (ApplyState p) m) => p wX wY -> (ApplyState p) m -> m ((ApplyState p) m) applyToState patch t = snd <$> runApplyMonad (apply patch) t +-- | Attempts to apply a given replace patch to a Tree. If the apply fails (if+-- the file the patch applies to already contains the target token), we return+-- Nothing, otherwise we return the updated Tree.+maybeApplyToTree :: (Apply p, ApplyState p ~ Tree) => p wX wY -> Tree IO+ -> IO (Maybe (Tree IO))+maybeApplyToTree patch tree =+ (Just `fmap` applyToTree patch tree) `catch` (\(_ :: IOException) -> return Nothing) -------------------------------------------------------------------------------- -- | Apply a patch to set of 'FileName's, yielding the new set of 'FileName's and 'PatchMod's
src/Darcs/Patch/ApplyMonad.hs view
@@ -1,5 +1,9 @@ {-# OPTIONS_GHC -fno-warn-missing-methods -fno-warn-orphans #-}-{-# LANGUAGE TypeSynonymInstances, MultiParamTypeClasses #-}+{-# LANGUAGE TypeSynonymInstances, MultiParamTypeClasses+ , ConstraintKinds, UndecidableInstances, CPP #-}+#if __GLASGOW_HASKELL__ >= 800+{-# LANGUAGE UndecidableSuperClasses #-}+#endif -- Copyright (C) 2010, 2011 Petr Rockai -- -- Permission is hereby granted, free of charge, to any person@@ -21,14 +25,21 @@ -- ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -- SOFTWARE.-module Darcs.Patch.ApplyMonad( ApplyMonad(..), ApplyMonadTrans(..), withFileNames, withFiles, ToTree(..) ) where+module Darcs.Patch.ApplyMonad+ ( ApplyMonad(..), ApplyMonadTrans(..), ApplyMonadState(..)+ , withFileNames, withFiles, ToTree(..)+ , ApplyMonadTree(..)+ ) where +import Prelude ()+import Darcs.Prelude+ import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import qualified Data.Map as M-import qualified Storage.Hashed.Monad as HSM+import qualified Darcs.Util.Tree.Monad as TM+import Darcs.Util.Tree ( Tree ) import Data.Maybe ( fromMaybe )-import Storage.Hashed.Tree ( Tree ) import Darcs.Util.ByteString( linesPS, unlinesPS ) import Darcs.Util.Path ( FileName, movedirfilename, fn2fp, isParentOrEqOf, floatPath, AnchoredPath )@@ -36,8 +47,7 @@ import Control.Monad.Identity( Identity ) import Darcs.Patch.MonadProgress --- TODO should UUID/Object live somewhere more central?-import Darcs.Patch.Prim.V3.ObjectMap ( UUID, ObjectMap, DirContent )+import GHC.Exts ( Constraint ) fn2ap :: FileName -> AnchoredPath fn2ap = floatPath . fn2fp@@ -48,19 +58,46 @@ instance ToTree Tree where toTree = id -class (Functor m, Monad m, ApplyMonad (ApplyMonadOver m state) state)- => ApplyMonadTrans m (state :: (* -> *) -> *) where- type ApplyMonadOver m state :: * -> *- runApplyMonad :: (ApplyMonadOver m state) x -> state m -> m (x, state m)+class (Functor m, Monad m, ApplyMonad state (ApplyMonadOver state m))+ => ApplyMonadTrans (state :: (* -> *) -> *) m where+ type ApplyMonadOver state m :: * -> *+ runApplyMonad :: (ApplyMonadOver state m) x -> state m -> m (x, state m) -instance (Functor m, Monad m) => ApplyMonadTrans m Tree where- type ApplyMonadOver m Tree = HSM.TreeMonad m- runApplyMonad = HSM.virtualTreeMonad+instance (Functor m, Monad m) => ApplyMonadTrans Tree m where+ type ApplyMonadOver Tree m = TM.TreeMonad m+ runApplyMonad = TM.virtualTreeMonad -class (Functor m, Monad m, Functor (ApplyMonadBase m), Monad (ApplyMonadBase m), ToTree state)+class ApplyMonadState (state :: (* -> *) -> *) where+ type ApplyMonadStateOperations state :: (* -> *) -> Constraint++class (Functor m, Monad m) => ApplyMonadTree m where+ -- a semantic, Tree-based interface for patch application+ mDoesDirectoryExist :: FileName -> m Bool+ mDoesFileExist :: FileName -> m Bool+ mReadFilePS :: FileName -> m B.ByteString+ mReadFilePSs :: FileName -> m [B.ByteString]+ mReadFilePSs f = linesPS `fmap` mReadFilePS f+ mCreateDirectory :: FileName -> m ()+ mRemoveDirectory :: FileName -> m ()+ mCreateFile :: FileName -> m ()+ mCreateFile f = mModifyFilePS f $ \_ -> return B.empty+ mRemoveFile :: FileName -> m ()+ mRename :: FileName -> FileName -> m ()+ mModifyFilePS :: FileName -> (B.ByteString -> m B.ByteString) -> m ()+ mModifyFilePSs :: FileName -> ([B.ByteString] -> m [B.ByteString]) -> m ()+ mModifyFilePSs f j = mModifyFilePS f (fmap unlinesPS . j . linesPS)+ mChangePref :: String -> String -> String -> m ()+ mChangePref _ _ _ = return ()++instance ApplyMonadState Tree where+ type ApplyMonadStateOperations Tree = ApplyMonadTree++class ( Functor m, Monad m, Functor (ApplyMonadBase m), Monad (ApplyMonadBase m)+ , ApplyMonadStateOperations state m, ToTree state+ ) -- ApplyMonadOver (ApplyMonadBase m) ~ m is *not* required in general, -- since ApplyMonadBase is not injective- => ApplyMonad m (state :: (* -> *) -> *) where+ => ApplyMonad (state :: (* -> *) -> *) m where type ApplyMonadBase m :: * -> * nestedApply :: m x -> state (ApplyMonadBase m) -> m (x, state (ApplyMonadBase m))@@ -68,50 +105,27 @@ -> m (x, state (ApplyMonadBase m)) getApplyState :: m (state (ApplyMonadBase m))- putApplyState :: state m -> m () - -- a semantic, ObjectMap-based interface for patch application- editFile :: (state ~ ObjectMap) => UUID -> (B.ByteString -> B.ByteString) -> m ()- editDirectory :: (state ~ ObjectMap) => UUID -> (DirContent -> DirContent) -> m ()-- -- a semantic, Tree-based interface for patch application- mDoesDirectoryExist :: (state ~ Tree) => FileName -> m Bool- mDoesFileExist :: (state ~ Tree) => FileName -> m Bool- mReadFilePS :: (state ~ Tree) => FileName -> m B.ByteString- mReadFilePSs :: (state ~ Tree) => FileName -> m [B.ByteString]- mReadFilePSs f = linesPS `fmap` mReadFilePS f- mCreateDirectory :: (state ~ Tree) => FileName -> m ()- mRemoveDirectory :: (state ~ Tree) => FileName -> m ()- mCreateFile :: (state ~ Tree) => FileName -> m ()- mCreateFile f = mModifyFilePS f $ \_ -> return B.empty- mRemoveFile :: (state ~ Tree) => FileName -> m ()- mRename :: (state ~ Tree) => FileName -> FileName -> m ()- mModifyFilePS :: (state ~ Tree) => FileName -> (B.ByteString -> m B.ByteString) -> m ()- mModifyFilePSs :: (state ~ Tree) => FileName -> ([B.ByteString] -> m [B.ByteString]) -> m ()- mModifyFilePSs f j = mModifyFilePS f (fmap unlinesPS . j . linesPS)- mChangePref :: (state ~ Tree) => String -> String -> String -> m ()- mChangePref _ _ _ = return ()--instance (Functor m, Monad m) => ApplyMonad (HSM.TreeMonad m) Tree where- type ApplyMonadBase (HSM.TreeMonad m) = m- getApplyState = gets HSM.tree+instance (Functor m, Monad m) => ApplyMonad Tree (TM.TreeMonad m) where+ type ApplyMonadBase (TM.TreeMonad m) = m+ getApplyState = gets TM.tree nestedApply a start = lift $ runApplyMonad a start- liftApply a start = do x <- gets HSM.tree+ liftApply a start = do x <- gets TM.tree lift $ runApplyMonad (lift $ a x) start - -- putApplyState needs some support from HSM+instance (Functor m, Monad m) => ApplyMonadTree (TM.TreeMonad m) where - mDoesDirectoryExist d = HSM.directoryExists (fn2ap d)- mDoesFileExist d = HSM.fileExists (fn2ap d)- mReadFilePS p = B.concat `fmap` BL.toChunks `fmap` HSM.readFile (fn2ap p)- mModifyFilePS p j = do have <- HSM.fileExists (fn2ap p)- x <- if have then B.concat `fmap` BL.toChunks `fmap` HSM.readFile (fn2ap p)+ mDoesDirectoryExist d = TM.directoryExists (fn2ap d)+ mDoesFileExist d = TM.fileExists (fn2ap d)+ mReadFilePS p = B.concat `fmap` BL.toChunks `fmap` TM.readFile (fn2ap p)+ mModifyFilePS p j = do have <- TM.fileExists (fn2ap p)+ x <- if have then B.concat `fmap` BL.toChunks `fmap` TM.readFile (fn2ap p) else return B.empty- HSM.writeFile (fn2ap p) . BL.fromChunks . (:[]) =<< j x- mCreateDirectory p = HSM.createDirectory (fn2ap p)- mRename from to = HSM.rename (fn2ap from) (fn2ap to)- mRemoveDirectory = HSM.unlink . fn2ap- mRemoveFile = HSM.unlink . fn2ap+ TM.writeFile (fn2ap p) . BL.fromChunks . (:[]) =<< j x+ mCreateDirectory p = TM.createDirectory (fn2ap p)+ mRename from to = TM.rename (fn2ap from) (fn2ap to)+ mRemoveDirectory = TM.unlink . fn2ap+ mRemoveFile = TM.unlink . fn2ap -- Latest name, current original name. type OrigFileNameOf = (FileName, FileName)@@ -138,8 +152,11 @@ withFileNames mbofnos fps x = execState x ([], fps, ofnos) where ofnos = fromMaybe (map (\y -> (y, y)) fps) mbofnos -instance ApplyMonad FilePathMonad Tree where+instance ApplyMonad Tree FilePathMonad where type ApplyMonadBase FilePathMonad = Identity+++instance ApplyMonadTree FilePathMonad where -- We can't check it actually is a directory here mDoesDirectoryExist d = gets $ \(_, fs, _) -> d `elem` fs @@ -158,8 +175,10 @@ type RestrictedApply = State (M.Map FileName B.ByteString) -instance ApplyMonad RestrictedApply Tree where+instance ApplyMonad Tree RestrictedApply where type ApplyMonadBase RestrictedApply = Identity++instance ApplyMonadTree RestrictedApply where mDoesDirectoryExist _ = return True mCreateDirectory _ = return () mRemoveFile f = modify $ M.delete f
src/Darcs/Patch/ApplyPatches.hs view
@@ -12,8 +12,8 @@ import Darcs.Patch.Witnesses.Ordered ( FL(..), mapFL ) import Darcs.Util.Printer ( text, ($$) ) -applyPatches :: (MonadProgress m, ApplyMonad m (ApplyState p), Patchy p)- => FL (PatchInfoAnd p) wX wY -> m ()+applyPatches :: (MonadProgress m, ApplyMonad (ApplyState p) m, Patchy p)+ => FL (PatchInfoAnd rt p) wX wY -> m () applyPatches ps = runProgressActions "Applying patch" (mapFL doApply ps) where doApply hp = ProgressAction { paAction = apply (hopefully hp)
src/Darcs/Patch/Bracketed.hs view
@@ -3,6 +3,8 @@ , BracketedFL, mapBracketedFLFL, unBracketedFL ) where +import Prelude ()+import Darcs.Prelude import Darcs.Patch.Format ( PatchListFormat ) import Darcs.Patch.Witnesses.Ordered ( FL(..), mapFL_FL, concatFL )
src/Darcs/Patch/Bracketed/Instances.hs view
@@ -2,12 +2,34 @@ module Darcs.Patch.Bracketed.Instances () where import Darcs.Patch.Bracketed ( Bracketed(..) )+import Darcs.Patch.Effect ( Effect(..) )+import Darcs.Patch.Prim ( FromPrim(..), PrimPatchBase(..) ) import Darcs.Patch.Show ( ShowPatchBasic(..) ) import Darcs.Patch.Witnesses.Ordered ( FL(NilFL), mapFL ) import Darcs.Util.Printer ( vcat, blueText, ($$) ) +-- The PrimPatchBase, Effect and FromPrim instances are only+-- needed (by Darcs.Patch.Bundle) because the ReadPatch instance for+-- WrappedNamed unconditionally has them as requirements even though+-- they are only needed for the 'IsRebase case which isn't itself used+-- by Darcs.Patch.Bundle.+-- TODO see if this can be simplified+instance PrimPatchBase p => PrimPatchBase (Bracketed p) where+ type PrimOf (Bracketed p) = PrimOf p++instance Effect p => Effect (Bracketed p) where+ effect (Singleton p) = effect p+ effect (Braced ps) = effect ps+ effect (Parens ps) = effect ps++ effectRL (Singleton p) = effectRL p+ effectRL (Braced ps) = effectRL ps+ effectRL (Parens ps) = effectRL ps++instance FromPrim p => FromPrim (Bracketed p) where+ fromPrim p = Singleton (fromPrim p) instance ShowPatchBasic p => ShowPatchBasic (Bracketed p) where showPatch (Singleton p) = showPatch p
src/Darcs/Patch/Bundle.hs view
@@ -30,15 +30,18 @@ , parseBundle ) where +import Prelude ()+import Darcs.Prelude+ import Data.Char ( isAlpha, toLower, isDigit, isSpace ) import qualified Data.ByteString as B ( ByteString, length, null, drop, isPrefixOf ) import qualified Data.ByteString.Char8 as BC ( unpack, break, pack ) -import Storage.Hashed.Tree( Tree )-import Storage.Hashed.Monad( virtualTreeIO )+import Darcs.Util.Tree( Tree )+import Darcs.Util.Tree.Monad( virtualTreeIO ) -import Darcs.Patch ( RepoPatch, Named, showPatch, showContextPatch,+import Darcs.Patch ( RepoPatch, showPatch, showContextPatch, readPatchPartial ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Bracketed ( Bracketed, unBracketedFL )@@ -48,9 +51,13 @@ import Darcs.Patch.Format ( PatchListFormat ) import Darcs.Patch.Info ( PatchInfo, readPatchInfo, showPatchInfo, showPatchInfoUI, isTag )+import Darcs.Patch.Named.Wrapped ( WrappedNamed ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, piap, fmapFLPIAP, info,- patchInfoAndPatch, unavailable, hopefully )+ patchInfoAndPatch, unavailable, hopefully,+ generaliseRepoTypePIAP+ ) import Darcs.Patch.ReadMonads ( parseStrictly )+import Darcs.Patch.RepoType ( RepoType(..), RebaseType(..) ) import Darcs.Patch.Set ( PatchSet(..), Tagged(..), SealedPatchSet, Origin ) import Darcs.Patch.Show ( ShowPatchBasic ) import Darcs.Patch.Witnesses.Ordered@@ -67,16 +74,16 @@ -- |hashBundle creates a SHA1 string of a given a FL of named patches. This -- allows us to ensure that the patches in a received patchBundle have not been -- modified in transit.-hashBundle :: (PatchListFormat p, ShowPatchBasic p) => FL (Named p) wX wY+hashBundle :: (PatchListFormat p, ShowPatchBasic p) => FL (WrappedNamed rt p) wX wY -> String hashBundle to_be_sent = show $ sha1PS $ renderPS Standard $ vcat (mapFL showPatch to_be_sent) <> newline makeBundleN :: (ApplyState p ~ Tree, RepoPatch p) => Maybe (Tree IO)- -> PatchSet p wStart wX -> FL (Named p) wX wY -> 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 =+ -> PatchSet rt p wStart wX -> FL (WrappedNamed rt p) wX wY -> IO Doc+makeBundleN the_s (PatchSet (_ :<: Tagged t _ _) ps) to_be_sent =+ makeBundle2 the_s ((NilRL :<: t) +<+ ps) to_be_sent to_be_sent+makeBundleN the_s (PatchSet NilRL ps) to_be_sent = makeBundle2 the_s ps to_be_sent to_be_sent -- | In makeBundle2, it is presumed that the two patch sequences are@@ -85,8 +92,8 @@ -- generated, which is not the end of the world, but isn't very useful -- either. makeBundle2 :: (ApplyState p ~ Tree, RepoPatch p) => Maybe (Tree IO)- -> RL (PatchInfoAnd p) wStart wX -> FL (Named p) wX wY- -> FL (Named p) wX wY -> IO Doc+ -> RL (PatchInfoAnd rt p) wStart wX -> FL (WrappedNamed rt p) wX wY+ -> FL (WrappedNamed rt p) wX wY -> IO Doc 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@@ -106,9 +113,9 @@ $$ text "" common = mapRL info common' -parseBundle :: forall p. RepoPatch p => B.ByteString+parseBundle :: forall rt p. RepoPatch p => B.ByteString -> Either String- (Sealed ((PatchSet p :> FL (PatchInfoAnd p)) Origin))+ (Sealed ((PatchSet rt p :> FL (PatchInfoAnd rt p)) Origin)) parseBundle input | B.null input = Left "Bad patch bundle!" parseBundle input = case sillyLex input of ("New patches:", rest) -> case getPatches rest of@@ -147,28 +154,34 @@ sealCtxAndPs ctx ps = Right $ sealContextWithPatches ctx ps sealContextWithPatches :: RepoPatch p => [PatchInfo]- -> FL (PatchInfoAnd (Bracketed p)) wX wY+ -> FL (PatchInfoAnd ('RepoType 'NoRebase) (Bracketed p)) wX wY -> Sealed- ((PatchSet p :> FL (PatchInfoAnd p)) Origin)+ ((PatchSet rt p :> FL (PatchInfoAnd rt p)) Origin) sealContextWithPatches context bracketedPatches =- let patches = mapFL_FL (fmapFLPIAP unBracketedFL) bracketedPatches in+ let -- witness to fmapFLPIAP that the bundle won't contain stash/rebase patches+ -- TODO use EmptyCase with GHC 7.8++ notRebasing _+ = error "internal error: unreachable case (Darcs.Patch.Bundle.parseBundle.notRebasing)"+ patches = mapFL_FL (generaliseRepoTypePIAP . fmapFLPIAP unBracketedFL notRebasing)+ bracketedPatches+ in case reverse context of (x : ry) | isTag x -> let ps = unavailablePatches (reverse ry) t = Tagged (piUnavailable x) Nothing NilRL in- Sealed $ PatchSet ps (t :<: NilRL) :> patches- _ -> let ps = PatchSet (unavailablePatches context) NilRL in+ Sealed $ PatchSet (NilRL :<: t) ps :> patches+ _ -> let ps = PatchSet NilRL (unavailablePatches context) in Sealed $ ps :> patches -- 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. -- :{ -scanBundle :: forall p . RepoPatch p => B.ByteString- -> Either String (SealedPatchSet p Origin)+scanBundle :: forall rt p . RepoPatch p => B.ByteString+ -> Either String (SealedPatchSet rt p Origin) scanBundle bundle = do- Sealed (PatchSet recent tagged :> ps) <- parseBundle bundle- return . Sealed $ PatchSet (reverseFL ps +<+ recent) tagged+ Sealed (PatchSet tagged recent :> ps) <- parseBundle bundle+ return . Sealed $ PatchSet tagged (recent +<+ reverseFL ps) -- |filterGpgDashes unescapes a clearsigned patch, which will have had any -- lines starting with dashes escaped with a leading "- ".@@ -188,12 +201,12 @@ -- |unavailablePatches converts a list of PatchInfos into a RL of PatchInfoAnd -- Unavailable patches. This is used to represent the Context of a patchBundle.-unavailablePatches :: RepoPatch p => [PatchInfo] -> RL (PatchInfoAnd p) wX wY-unavailablePatches = foldr ((:<:) . piUnavailable) (unsafeCoerceP NilRL)+unavailablePatches :: RepoPatch p => [PatchInfo] -> RL (PatchInfoAnd rt p) wX wY+unavailablePatches = foldr (flip (:<:) . piUnavailable) (unsafeCoerceP NilRL) -- |piUnavailable returns an Unavailable within a PatchInfoAnd given a -- PatchInfo.-piUnavailable :: RepoPatch p => PatchInfo -> PatchInfoAnd p wX wY+piUnavailable :: RepoPatch p => PatchInfo -> PatchInfoAnd rt p wX wY piUnavailable i = patchInfoAndPatch i . unavailable $ "Patch not stored in patch bundle:\n" ++ renderString Encode (showPatchInfoUI i) @@ -214,7 +227,7 @@ -- returning the FL of as many patches-with-info as were successfully parsed, -- along with any unconsumed input. getPatches :: RepoPatch p => B.ByteString- -> (Sealed (FL (PatchInfoAnd (Bracketed p)) wX), B.ByteString)+ -> (Sealed (FL (PatchInfoAnd ('RepoType 'NoRebase) (Bracketed p)) wX), B.ByteString) getPatches ps = case parseStrictly readPatchInfo ps of Nothing -> (Sealed NilFL, ps) Just (pinfo, _) -> case readPatchPartial ps of@@ -229,19 +242,19 @@ where (a, b) = BC.break (== '\n') (dropSpace ps) -contextPatches :: RepoPatch p => PatchSet p Origin wX- -> (PatchSet p :> RL (PatchInfoAnd p)) Origin wX+contextPatches :: RepoPatch p => PatchSet rt p Origin wX+ -> (PatchSet rt p :> RL (PatchInfoAnd rt p)) Origin wX contextPatches set = case slightlyOptimizePatchset set of- PatchSet ps (Tagged t _ ps' :<: ts) ->- PatchSet ps' ts :> (ps +<+ (t :<: NilRL))- PatchSet ps NilRL -> PatchSet NilRL NilRL :> ps+ PatchSet (ts :<: Tagged t _ ps') ps ->+ PatchSet ts ps' :> ((NilRL :<: t) +<+ ps)+ PatchSet NilRL ps -> PatchSet NilRL NilRL :> ps -- |'scanContextFile' scans the context in the file of the given name.-scanContextFile :: RepoPatch p => FilePath -> IO (PatchSet p Origin wX)+scanContextFile :: RepoPatch p => FilePath -> IO (PatchSet rt p Origin wX) scanContextFile filename = scanContext `fmap` mmapFilePS filename where -- are the type witnesses sensible?- scanContext :: RepoPatch p => B.ByteString -> PatchSet p Origin wX+ scanContext :: RepoPatch p => B.ByteString -> PatchSet rt p Origin wX scanContext input | B.null input = error "Bad context!" | otherwise = case sillyLex input of@@ -249,8 +262,8 @@ (cont@(_ : _), _) | isTag (last cont) -> let ps = unavailablePatches $ init cont t = Tagged (piUnavailable $ last cont) Nothing NilRL in- PatchSet ps (t :<: NilRL)- (cont, _) -> PatchSet (unavailablePatches cont) NilRL+ PatchSet (NilRL :<: t) ps+ (cont, _) -> PatchSet NilRL (unavailablePatches cont) ("-----BEGIN PGP SIGNED MESSAGE-----",rest) -> scanContext $ filterGpgDashes rest (_, rest) -> scanContext rest@@ -258,20 +271,20 @@ -- | Minimize the context of a bundle to be sent, taking into account -- the patches selected to be sent minContext :: (RepoPatch p)- => PatchSet p wStart wB- -> FL (PatchInfoAnd p) wB wC- -> Sealed ((PatchSet p :> FL (PatchInfoAnd p)) wStart)-minContext (PatchSet topCommon behindTag) to_be_sent =+ => PatchSet rt p wStart wB+ -> FL (PatchInfoAnd rt p) wB wC+ -> Sealed ((PatchSet rt p :> FL (PatchInfoAnd rt p)) wStart)+minContext (PatchSet behindTag topCommon) to_be_sent = case go topCommon NilFL to_be_sent of- Sealed (c :> to_be_sent') -> seal (PatchSet c behindTag :> to_be_sent') + Sealed (c :> to_be_sent') -> seal (PatchSet behindTag c :> to_be_sent') where go :: (RepoPatch p)- => RL (PatchInfoAnd p) wA wB -- context we attempt to minimize- -> FL (PatchInfoAnd p) wB wC -- patches we cannot remove from context- -> FL (PatchInfoAnd p) wC wD -- patches to be included in the bundle- -> Sealed (( RL (PatchInfoAnd p) :> FL (PatchInfoAnd p) ) wA )+ => RL (PatchInfoAnd rt p) wA wB -- context we attempt to minimize+ -> FL (PatchInfoAnd rt p) wB wC -- patches we cannot remove from context+ -> FL (PatchInfoAnd rt p) wC wD -- patches to be included in the bundle+ -> Sealed (( RL (PatchInfoAnd rt p) :> FL (PatchInfoAnd rt p) ) wA ) go NilRL necessary to_be_sent' = seal (reverseFL necessary :> to_be_sent')- go (candidate :<: rest) necessary to_be_sent' =+ go (rest :<: candidate) necessary to_be_sent' = let fl1 = (candidate :>: NilFL) in case commute (fl1 :> necessary) of Nothing -> go rest (candidate :>: necessary) to_be_sent'
src/Darcs/Patch/Choices.hs view
@@ -54,11 +54,14 @@ forceMatchingFirst, forceMatchingLast, selectAllMiddles, makeUncertain, makeEverythingLater, makeEverythingSooner,- LabelledPatch, Label, label, lpPatch,+ LabelledPatch, Label, label, lpPatch, getLabelInt, Slot(..), substitute ) where +import Prelude ()+import Darcs.Prelude+ import Control.Monad.Identity ( Identity ) import Control.Monad.State ( StateT(..) ) @@ -114,6 +117,9 @@ label :: LabelledPatch p wX wY -> Label label (LP tg _) = tg +getLabelInt :: Label -> Integer+getLabelInt (Label _ i) = i+ lpPatch :: LabelledPatch p wX wY -> p wX wY lpPatch (LP _ p) = p @@ -256,11 +262,11 @@ +>+ PC lp True :>: ls}) psLast firsts middles bubble (PC lp True :>: ls) =- psLast firsts middles (lp :<: bubble) ls+ psLast firsts middles (bubble :<: lp) ls psLast firsts middles bubble (PC lp False :>: ls) = case commuteRL (bubble :> lp) of- Just (lp' :> bubble') -> psLast firsts (lp' :<: middles) bubble' ls- Nothing -> psLast firsts middles (lp :<: bubble) ls+ Just (lp' :> bubble') -> psLast firsts (middles :<: lp') bubble' ls+ Nothing -> psLast firsts middles (bubble :<: lp) ls psLast _ _ _ NilFL = impossible settleM middles = mapFL_FL (\lp -> PC lp False) $ reverseRL middles settleB bubble = mapFL_FL (\lp -> PC lp True) $ reverseRL bubble@@ -287,7 +293,7 @@ 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 (a :>: l2) = fmfLasts f (l1 :<: a) l2 fmfLasts f l1 NilFL = PCs { pcsFirsts = f , pcsLasts = reverseRL l1 } pred_pc :: forall wX wY . PatchChoice p wX wY -> Bool@@ -316,9 +322,9 @@ PatchChoices p wX wY samf f1 f2 l1 (pc@(PC lp False) :>: l2) = case commuteRL (l1 :> pc) of- Nothing -> samf f1 f2 (PC lp True :<: l1) l2- Just ((PC lp' _) :> l1') -> samf f1 (lp' :<: f2) l1' l2- samf f1 f2 l1 (PC lp True :>: l2) = samf f1 f2 (PC lp True :<: l1) l2+ Nothing -> samf f1 f2 (l1 :<: PC lp True) l2+ Just ((PC lp' _) :> l1') -> samf f1 (f2 :<: lp') l1' l2+ samf f1 f2 l1 (PC lp True :>: l2) = samf f1 f2 (l1 :<: PC lp True) l2 samf f1 f2 l1 NilFL = PCs (f1 +>+ reverseRL f2) (reverseRL l1) forceMatchingLast :: Patchy p => (forall wX wY . LabelledPatch p wX wY -> Bool)@@ -341,7 +347,7 @@ l' = mapFL_FL (\lp -> PC lp 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 (a :>: f2) l = fmlFirst pred b (f1 :<: a) f2 l fmlFirst pred b f1 NilFL l = PCs { pcsFirsts = reverseRL f1 , pcsLasts = mapFL_FL ch l} where ch (PC lp c) = (PC lp (if pred lp then b else c) )@@ -376,11 +382,11 @@ RL (LabelledPatch p) wM2 wM3 -> FL (PatchChoice p) wM3 wY -> (FL (LabelledPatch p) :> FL (PatchChoice p)) wM1 wY- mes middle bubble (PC lp True :>: ls) = mes middle (lp :<: bubble) ls+ mes middle bubble (PC lp True :>: ls) = mes middle (bubble :<: lp) ls mes middle bubble (PC lp False :>: ls) = case commuteRL (bubble :> lp) of- Nothing -> mes middle (lp :<: bubble) ls- Just (lp' :> bubble') -> mes (lp' :<: middle) bubble' ls+ Nothing -> mes middle (bubble :<: lp) ls+ Just (lp' :> bubble') -> mes (middle :<: lp') bubble' ls mes middle bubble NilFL = (reverseRL middle) :> mapFL_FL (\lp -> PC lp False) (reverseRL bubble) -- | 'substitute' @(a :||: bs)@ @pcs@ replaces @a@ with @bs@ in @pcs@ preserving the choice
src/Darcs/Patch/Commute.hs view
@@ -4,16 +4,17 @@ , commuteFLorComplain , commuteRL , commuteRLFL- , toFwdCommute- , toRevCommute , selfCommuter ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.CommuteFn ( CommuteFn ) import Darcs.Patch.Witnesses.Ordered ( FL(..), RL(..), reverseFL, reverseRL,- (:>)(..), (:<)(..) )+ (:>)(..) ) import Darcs.Patch.Witnesses.Sealed ( Sealed2, seal2 ) -- | Commute represents things that can be (possibly) commuted.@@ -44,10 +45,10 @@ -- |'commuteRL' commutes a RL past a single element. commuteRL :: Commute p => (RL p :> p) wX wY -> Maybe ((p :> RL p) wX wY)-commuteRL (z :<: zs :> w) = do+commuteRL (zs :<: z :> w) = do w' :> z' <- commute (z :> w) w'' :> zs' <- commuteRL (zs :> w')- return (w'' :> z' :<: zs')+ return (w'' :> zs' :<: z') commuteRL (NilRL :> w) = Just (w :> NilRL) -- |'commuteFL' commutes a single element past a FL.@@ -67,23 +68,6 @@ Right (ps' :> q'') -> Right (p' :>: ps' :> q'') Left l -> Left l Nothing -> Left $ seal2 p---- | Swaps the ordered pair type so that commute can be called directly.-toFwdCommute :: (Commute p, Commute q, Monad m) =>- ((p :< q) wX wY -> m ((q :< p) wX wY)) -> (q :> p) wX wY- -> m ((p :> q) wX wY)-toFwdCommute c (x :> y) = do- x' :< y' <- c (y :< x)- return (y' :> x')---- | Swaps the ordered pair type from the order expected by commute to the--- reverse order.-toRevCommute :: (Commute p, Commute q, Monad m) =>- ((p :> q) wX wY -> m ((q :> p) wX wY)) -> (q :< p) wX wY- -> m ((p :< q) wX wY)-toRevCommute c (x :< y) = do- x' :> y' <- c (y :> x)- return (y' :< x') -- |Build a commuter between a patch and itself using the operation from the type class. selfCommuter :: Commute p => CommuteFn p p
src/Darcs/Patch/CommuteFn.hs view
@@ -8,6 +8,9 @@ totalCommuterIdFL, totalCommuterFLId, totalCommuterFLFL ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Witnesses.Ordered ( (:>)(..) , (:\/:)(..)@@ -32,10 +35,10 @@ commuterIdRL :: CommuteFn p1 p2 -> CommuteFn p1 (RL p2) commuterIdRL _ (x :> NilRL) = return (NilRL :> x)-commuterIdRL commuter (x :> (y :<: ys))+commuterIdRL commuter (x :> (ys :<: y)) = do ys' :> x' <- commuterIdRL commuter (x :> ys) y' :> x'' <- commuter (x' :> y)- return ((y' :<: ys') :> x'')+ return ((ys' :<: y') :> x'') commuterIdFL :: CommuteFn p1 p2 -> CommuteFn p1 (FL p2) commuterIdFL _ (x :> NilFL) = return (NilFL :> x)@@ -67,10 +70,10 @@ commuterRLId :: CommuteFn p1 p2 -> CommuteFn (RL p1) p2 commuterRLId _ (NilRL :> y) = return (y :> NilRL)-commuterRLId commuter ((x :<: xs) :> y)+commuterRLId commuter ((xs :<: x) :> y) = do y' :> x' <- commuter (x :> y) y'' :> xs' <- commuterRLId commuter (xs :> y')- return (y'' :> (x' :<: xs'))+ return (y'' :> (xs' :<: x')) totalCommuterFLId :: TotalCommuteFn p1 p2 -> TotalCommuteFn (FL p1) p2 totalCommuterFLId _ (NilFL :> y) = y :> NilFL
src/Darcs/Patch/Conflict.hs view
@@ -1,32 +1,46 @@+-- Copyright (C) 2002-2003 David Roundy, 2010 Ganesh Sittampalam+{-# LANGUAGE CPP, ViewPatterns #-} module Darcs.Patch.Conflict- ( Conflict(..), CommuteNoConflicts(..)- , IsConflictedPrim(..), ConflictState(..) )- where+ ( Conflict(..), CommuteNoConflicts(..), listConflictedFiles+ , IsConflictedPrim(..), ConflictState(..)+ , mangleUnravelled+ ) where +import Prelude ()+import Darcs.Prelude++import qualified Data.ByteString.Char8 as BC (pack, last)+import qualified Data.ByteString as B (null, ByteString)+import Data.Maybe ( isJust )+import Data.List ( sort, intercalate )+import Data.List.Ordered ( nubSort )+ import Darcs.Patch.Effect ( Effect(..) )+import Darcs.Patch.FileHunk ( FileHunk(..), IsHunk, isHunk ) import Darcs.Patch.Inspect ( PatchInspect(..) )+import Darcs.Patch.Invert ( Invert(..) ) import Darcs.Patch.Permutations ()+import Darcs.Patch.Prim ( PrimPatch, is_filepatch, primIsHunk, primFromHunk ) import Darcs.Patch.Prim.Class ( PrimOf ) import Darcs.Patch.Witnesses.Ordered ( FL(..), RL(..), (:>)(..) , mapFL, reverseFL, mapRL, reverseRL )-import Darcs.Patch.Witnesses.Sealed ( Sealed, unseal )+import Darcs.Patch.Witnesses.Sealed ( Sealed(..), unseal, mapSeal ) import Darcs.Patch.Witnesses.Show ( Show2, showsPrec2 )+import Darcs.Util.Path ( FileName, fn2fp, fp2fn ) import Darcs.Util.Show ( appPrec )-import Data.List.Ordered ( nubSort ) +#include "impossible.h" +listConflictedFiles :: Conflict p => p wX wY -> [FilePath]+listConflictedFiles p =+ nubSort $ concatMap (unseal listTouchedFiles) $ concat $ resolveConflicts p+ class (Effect p, PatchInspect (PrimOf p)) => Conflict p where- listConflictedFiles :: p wX wY -> [FilePath]- listConflictedFiles p =- nubSort $ concatMap (unseal listTouchedFiles) $ concat $ resolveConflicts p resolveConflicts :: p wX wY -> [[Sealed (FL (PrimOf p) wY)]] conflictedEffect :: p wX wY -> [IsConflictedPrim (PrimOf p)]- conflictedEffect x = case listConflictedFiles x of- [] -> mapFL (IsC Okay) $ effect x- _ -> mapFL (IsC Conflicted) $ effect x class CommuteNoConflicts p where -- | If 'commuteNoConflicts' @x :> y@ succeeds, we know that that @x@ commutes@@ -36,7 +50,6 @@ commuteNoConflicts :: (p :> p) wX wY -> Maybe ((p :> p) wX wY) instance (CommuteNoConflicts p, Conflict p) => Conflict (FL p) where- listConflictedFiles = nubSort . concat . mapFL listConflictedFiles resolveConflicts NilFL = [] resolveConflicts x = resolveConflicts $ reverseFL x conflictedEffect = concat . mapFL conflictedEffect@@ -48,15 +61,14 @@ return $ ys' :> reverseRL rxs' instance (CommuteNoConflicts p, Conflict p) => Conflict (RL p) where- listConflictedFiles = nubSort . concat . mapRL listConflictedFiles resolveConflicts x = rcs x NilFL where rcs :: RL p wX wY -> FL p wY wW -> [[Sealed (FL (PrimOf p) wW)]] rcs NilRL _ = []- rcs (p:<:ps) passedby | (_:_) <- resolveConflicts p =+ rcs (ps:<:p) passedby | (_:_) <- resolveConflicts p = 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)+ rcs (ps:<:p) passedby = seq passedby $ rcs ps (p:>:passedby) conflictedEffect = concat . reverse . mapRL conflictedEffect instance CommuteNoConflicts p => CommuteNoConflicts (RL p) where@@ -83,9 +95,9 @@ commuteNoConflictsRL :: CommuteNoConflicts p => (RL p :> p) wX wY -> Maybe ((p :> RL p) wX wY) commuteNoConflictsRL (NilRL :> p) = Just (p :> NilRL)-commuteNoConflictsRL (p :<: ps :> q) = do q' :> p' <- commuteNoConflicts (p :> q)+commuteNoConflictsRL (ps :<: p :> q) = do q' :> p' <- commuteNoConflicts (p :> q) q'' :> ps' <- commuteNoConflictsRL (ps :> q')- return (q'' :> p' :<: ps')+ return (q'' :> ps' :<: p') commuteNoConflictsRLFL :: CommuteNoConflicts p => (RL p :> FL p) wX wY -> Maybe ((FL p :> RL p) wX wY) commuteNoConflictsRLFL (NilRL :> ys) = Just (ys :> NilRL)@@ -93,4 +105,80 @@ commuteNoConflictsRLFL (xs :> y :>: ys) = do y' :> xs' <- commuteNoConflictsRL (xs :> y) ys' :> xs'' <- commuteNoConflictsRLFL (xs' :> ys) return (y' :>: ys' :> xs'')+++applyHunks :: IsHunk prim => [Maybe B.ByteString] -> FL prim wX wY -> [Maybe B.ByteString]+applyHunks ms ((isHunk -> Just (FileHunk _ l o n)):>:ps) = applyHunks (rls l ms) ps+ where rls k _ | k <=0 = bug $ "bad hunk: start position <=0 (" ++ show k ++ ")"+ rls 1 mls = map Just n ++ drop (length o) mls+ rls i (ml:mls) = ml : rls (i-1) mls+ rls _ [] = bug "rls in applyHunks"+applyHunks ms NilFL = ms+applyHunks _ (_:>:_) = impossible++getAFilename :: PrimPatch prim => [Sealed (FL prim wX)] -> FileName+getAFilename (Sealed ((is_filepatch -> Just f):>:_):_) = f+getAFilename _ = fp2fn ""++getOld :: PrimPatch prim => [Maybe B.ByteString] -> [Sealed (FL prim wX)] -> [Maybe B.ByteString]+getOld = foldl getHunksOld++getHunksOld :: PrimPatch prim => [Maybe B.ByteString] -> Sealed (FL prim wX)+ -> [Maybe B.ByteString]+getHunksOld mls (Sealed ps) =+ applyHunks (applyHunks mls ps) (invert ps)++getHunksNew :: IsHunk prim => [Maybe B.ByteString] -> Sealed (FL prim wX)+ -> [Maybe B.ByteString]+getHunksNew mls (Sealed ps) = applyHunks mls ps++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++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?"+++mangleUnravelled :: PrimPatch prim => [Sealed (FL prim wX)] -> Sealed (FL prim wX)+mangleUnravelled pss = if onlyHunks pss+ then (:>: NilFL) `mapSeal` mangleUnravelledHunks pss+ else head pss++onlyHunks :: forall prim wX . PrimPatch prim => [Sealed (FL prim wX)] -> Bool+onlyHunks [] = False+onlyHunks pss = fn2fp f /= "" && all oh pss+ where f = getAFilename pss+ oh :: Sealed (FL prim wY) -> Bool+ oh (Sealed (p:>:ps)) = primIsHunk p &&+ [fn2fp f] == listTouchedFiles p &&+ oh (Sealed ps)+ oh (Sealed NilFL) = True++mangleUnravelledHunks :: PrimPatch prim => [Sealed (FL prim wX)] -> Sealed (prim wX)+--mangleUnravelledHunks [[h1],[h2]] = Deal with simple cases handily?+mangleUnravelledHunks pss =+ if null nchs then bug "mangleUnravelledHunks"+ else Sealed (primFromHunk (FileHunk filename l old new))+ 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] ++ old ++ [initial] ++ intercalate [middle] nchs ++ [bottom]+ top = BC.pack $ "v v v v v v v" ++ eol_c+ initial= BC.pack $ "=============" ++ eol_c+ middle = BC.pack $ "*************" ++ eol_c+ bottom = BC.pack $ "^ ^ ^ ^ ^ ^ ^" ++ eol_c+ eol_c = if any (\ps -> not (B.null ps) && BC.last ps == '\r') old+ then "\r"+ else ""
− src/Darcs/Patch/ConflictMarking.hs
@@ -1,97 +0,0 @@--- Copyright (C) 2002-2003 David Roundy, 2010 Ganesh Sittampalam-{-# LANGUAGE CPP, ViewPatterns #-}-module Darcs.Patch.ConflictMarking- ( mangleUnravelled- ) where--import qualified Data.ByteString.Char8 as BC (pack, last)-import qualified Data.ByteString as B (null, ByteString)-import Data.List ( sort, intercalate )-import Data.Maybe ( isJust )--import Darcs.Patch.FileHunk ( FileHunk(..), IsHunk, isHunk )-import Darcs.Util.Path ( FileName, fn2fp, fp2fn )-import Darcs.Patch.Inspect ( PatchInspect(..) )-import Darcs.Patch.Invert ( Invert(..) )-import Darcs.Patch.Permutations ()-import Darcs.Patch.Prim ( PrimPatch, is_filepatch, primIsHunk, primFromHunk )-import Darcs.Patch.Witnesses.Ordered ( FL(..) )-import Darcs.Patch.Witnesses.Sealed ( Sealed(..), mapSeal )--#include "impossible.h"--applyHunks :: IsHunk prim => [Maybe B.ByteString] -> FL prim wX wY -> [Maybe B.ByteString]-applyHunks ms ((isHunk -> Just (FileHunk _ l o n)):>:ps) = applyHunks (rls l ms) ps- where rls k _ | k <=0 = bug $ "bad hunk: start position <=0 (" ++ show k ++ ")"- rls 1 mls = map Just n ++ drop (length o) mls- rls i (ml:mls) = ml : rls (i-1) mls- rls _ [] = bug "rls in applyHunks"-applyHunks ms NilFL = ms-applyHunks _ (_:>:_) = impossible--getAFilename :: PrimPatch prim => [Sealed (FL prim wX)] -> FileName-getAFilename (Sealed ((is_filepatch -> Just f):>:_):_) = f-getAFilename _ = fp2fn ""--getOld :: PrimPatch prim => [Maybe B.ByteString] -> [Sealed (FL prim wX)] -> [Maybe B.ByteString]-getOld = foldl getHunksOld--getHunksOld :: PrimPatch prim => [Maybe B.ByteString] -> Sealed (FL prim wX)- -> [Maybe B.ByteString]-getHunksOld mls (Sealed ps) =- applyHunks (applyHunks mls ps) (invert ps)--getHunksNew :: IsHunk prim => [Maybe B.ByteString] -> Sealed (FL prim wX)- -> [Maybe B.ByteString]-getHunksNew mls (Sealed ps) = applyHunks mls ps--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--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?"---mangleUnravelled :: PrimPatch prim => [Sealed (FL prim wX)] -> Sealed (FL prim wX)-mangleUnravelled pss = if onlyHunks pss- then (:>: NilFL) `mapSeal` mangleUnravelledHunks pss- else head pss--onlyHunks :: forall prim wX . PrimPatch prim => [Sealed (FL prim wX)] -> Bool-onlyHunks [] = False-onlyHunks pss = fn2fp f /= "" && all oh pss- where f = getAFilename pss- oh :: Sealed (FL prim wY) -> Bool- oh (Sealed (p:>:ps)) = primIsHunk p &&- [fn2fp f] == listTouchedFiles p &&- oh (Sealed ps)- oh (Sealed NilFL) = True--mangleUnravelledHunks :: PrimPatch prim => [Sealed (FL prim wX)] -> Sealed (prim wX)---mangleUnravelledHunks [[h1],[h2]] = Deal with simple cases handily?-mangleUnravelledHunks pss =- if null nchs then bug "mangleUnravelledHunks"- else Sealed (primFromHunk (FileHunk filename l old new))- 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] ++ old ++ [initial] ++ intercalate [middle] nchs ++ [bottom]- top = BC.pack $ "v v v v v v v" ++ eol_c- initial= BC.pack $ "=============" ++ eol_c- middle = BC.pack $ "*************" ++ eol_c- bottom = BC.pack $ "^ ^ ^ ^ ^ ^ ^" ++ eol_c- eol_c = if any (\ps -> not (B.null ps) && BC.last ps == '\r') old- then "\r"- else ""-
src/Darcs/Patch/Depends.hs view
@@ -30,25 +30,36 @@ , splitOnTag , newsetUnion , newsetIntersection- , commuteToEnd , findUncommon , merge2FL+ , getDeps+ , SPatchAndDeps ) where +import Prelude ()+import Darcs.Prelude+ #include "impossible.h" import Prelude hiding ( pi ) import Data.List ( delete, intersect, (\\) ) import Data.Maybe ( fromMaybe )+import qualified Data.ByteString.Char8 as BC ( unpack )+import Control.Arrow ( (&&&) ) -import Darcs.Patch ( Patchy, getdeps, commute, commuteFLorComplain,- commuteRL )-import Darcs.Patch.Info ( PatchInfo, isTag, showPatchInfoUI )+import Darcs.Patch ( RepoPatch )+import Darcs.Patch.Apply( ApplyState )+import Darcs.Patch.Patchy ( Patchy )+import Darcs.Patch.Named ( Named (..), patch2patchinfo )+import Darcs.Patch.Named.Wrapped ( getdeps )+import Darcs.Patch.Choices ( Label, patchChoices, forceFirst+ , PatchChoices, lpPatch, getChoices+ , LabelledPatch, label )+import Darcs.Patch.Commute ( commute, commuteFL, commuteRL )+import Darcs.Patch.Info ( PatchInfo, isTag, showPatchInfoUI, _piName ) import Darcs.Patch.Merge ( Merge, mergeFL )-import Darcs.Patch.Permutations ( partitionFL, partitionRL,- removeSubsequenceRL )+import Darcs.Patch.Permutations ( partitionFL, partitionRL ) import Darcs.Patch.PatchInfoAnd( PatchInfoAnd, hopefully, hopefullyM, info )-import Darcs.Patch.Rebase.NameHack ( NameHack ) import Darcs.Patch.Set ( PatchSet(..), Tagged(..), SealedPatchSet, newset2RL, appendPSFL ) import Darcs.Patch.Progress ( progressRL )@@ -57,11 +68,12 @@ import Darcs.Patch.Witnesses.Ordered ( (:\/:)(..), (:/\:)(..), (:>)(..), Fork(..), (+>+), mapFL, RL(..), FL(..), isShorterThanRL,- (+<+), reverseFL, reverseRL, mapRL )+ (+<+), reverseFL, reverseRL, mapRL, lengthFL, splitAtFL ) import Darcs.Patch.Witnesses.Sealed- ( Sealed(..), FlippedSeal(..), flipSeal, seal )+ ( Sealed(..), FlippedSeal(..), flipSeal, seal, Sealed2(..), seal2 ) import Darcs.Util.Printer ( renderString, vcat, RenderMode(..) )+import Darcs.Util.Tree ( Tree ) {- - This module uses the following definitions:@@ -82,6 +94,54 @@ - since they explicitly depend on all uncovered patches. -} +-- | S(ealed) Patch and his dependencies.+type SPatchAndDeps p = ( Sealed2 (LabelledPatch (Named p))+ , Sealed2 (FL (LabelledPatch (Named p)))+ )++-- | Searchs dependencies in @repoFL@ of the patches in @getDepsFL@.+getDeps :: (RepoPatch p, ApplyState p ~ Tree) =>+ FL (Named p) wA wR -> FL (PatchInfoAnd rt p) wX wY -> [SPatchAndDeps p]+getDeps repoFL getDepsFL =+ let repoChoices = patchChoices repoFL+ getDepsFL' = mapFL (BC.unpack . _piName . info) getDepsFL+ labelledDeps = getLabelledDeps getDepsFL' repoChoices+ in+ map (deps repoChoices) labelledDeps+ where+ -- Search dependencies for the patch with label @l@ in @repoChoices@.+ deps :: (Patchy (Named p)) => PatchChoices (Named p) wX wY ->+ (String,Label) -> SPatchAndDeps p+ deps repoChoices (_,l) =+ case getChoices $ forceFirst l repoChoices of+ (ds :> _) -> let i = lengthFL ds+ in case splitAtFL (i-1) ds of+ -- Separate last patch in list+ ds' :> (r :>: NilFL) -> (seal2 r, seal2 ds')+ _ -> impossible -- Because deps at least+ -- has r, which is the patch+ -- that we are looking at+ -- dependencies.+ getLabelledDeps :: Patchy (Named p) => [String] ->+ PatchChoices (Named p) x y -> [(String, Label)]+ getLabelledDeps patchnames repoChoices =+ case getChoices repoChoices of+ a :> (b :> c) -> filterDepsFL patchnames a +++ filterDepsFL patchnames b +++ filterDepsFL patchnames c+ filterDepsFL :: [String] -> FL (LabelledPatch (Named p)) wX wY ->+ [(String, Label)]+ filterDepsFL _ NilFL = []+ filterDepsFL patchnames (lp :>: lps) =+ if fst dep `elem` patchnames+ then dep : filterDepsFL patchnames lps+ else filterDepsFL patchnames lps+ where+ lpTostring :: LabelledPatch (Named p) wA wB -> String+ lpTostring = BC.unpack . _piName . patch2patchinfo . lpPatch+ dep :: (String, Label)+ dep = lpTostring &&& label $ lp+ {-| taggedIntersection takes two 'PatchSet's and splits them into a /common/ intersection portion and two sets of patches. The intersection, however,@@ -95,21 +155,21 @@ taggedIntersection does its best to reduce the number of inventories that are accessed from its rightmost argument. -}-taggedIntersection :: forall p wStart wX wY . (Patchy p, NameHack p) =>- PatchSet p wStart wX -> PatchSet p wStart wY ->- Fork (RL (Tagged p))- (RL (PatchInfoAnd p))- (RL (PatchInfoAnd p)) wStart wX wY-taggedIntersection (PatchSet ps1 NilRL) s2 = Fork NilRL ps1 (newset2RL s2)-taggedIntersection s1 (PatchSet ps2 NilRL) = Fork NilRL (newset2RL s1) ps2-taggedIntersection s1 (PatchSet ps2 (Tagged t _ _ :<: _))- | Just (PatchSet ps1 ts1) <- maybeSplitSetOnTag (info t) s1 =+taggedIntersection :: forall rt p wStart wX wY . Patchy p =>+ PatchSet rt p wStart wX -> PatchSet rt p wStart wY ->+ Fork (RL (Tagged rt p))+ (RL (PatchInfoAnd rt p))+ (RL (PatchInfoAnd rt p)) wStart wX wY+taggedIntersection (PatchSet NilRL ps1) s2 = Fork NilRL ps1 (newset2RL s2)+taggedIntersection s1 (PatchSet NilRL ps2) = Fork NilRL (newset2RL s1) ps2+taggedIntersection s1 (PatchSet (_ :<: Tagged t _ _) ps2)+ | Just (PatchSet ts1 ps1) <- maybeSplitSetOnTag (info t) s1 = Fork ts1 ps1 (unsafeCoercePStart ps2)-taggedIntersection s1 s2@(PatchSet ps2 (Tagged t _ p :<: ts2)) =+taggedIntersection s1 s2@(PatchSet (ts2 :<: Tagged t _ p) ps2) = case hopefullyM t of- Just _ -> taggedIntersection s1 (PatchSet (ps2 +<+ t :<: p) ts2)+ Just _ -> taggedIntersection s1 (PatchSet ts2 (p :<: t +<+ ps2)) Nothing -> case splitOnTag (info t) s1 of- Just (PatchSet NilRL com :> us) ->+ Just (PatchSet com NilRL :> us) -> Fork com us (unsafeCoercePStart ps2) Just _ -> impossible Nothing -> Fork NilRL (newset2RL s1) (newset2RL s2)@@ -119,20 +179,20 @@ -- found, the PatchSet is split up, on that tag, such that all later patches -- are in the "since last tag" patch list. If the tag is not found, 'Nothing' -- is returned.-maybeSplitSetOnTag :: PatchInfo -> PatchSet p wStart wX- -> Maybe (PatchSet p wStart wX)-maybeSplitSetOnTag t0 origSet@(PatchSet ps (Tagged t _ pst :<: ts))+maybeSplitSetOnTag :: PatchInfo -> PatchSet rt p wStart wX+ -> Maybe (PatchSet rt p wStart wX)+maybeSplitSetOnTag t0 origSet@(PatchSet (ts :<: Tagged t _ pst) ps) | t0 == info t = Just origSet | otherwise = do- PatchSet ps' ts' <- maybeSplitSetOnTag t0 (PatchSet (t :<: pst) ts)- Just $ PatchSet (ps +<+ ps') ts'+ PatchSet ts' ps' <- maybeSplitSetOnTag t0 (PatchSet ts (pst :<: t))+ Just $ PatchSet ts' (ps' +<+ ps) maybeSplitSetOnTag _ _ = Nothing -getPatchesBeyondTag :: (Patchy p, NameHack p) => PatchInfo -> PatchSet p wStart wX- -> FlippedSeal (RL (PatchInfoAnd p)) wX-getPatchesBeyondTag t (PatchSet ps (Tagged hp _ _ :<: _)) | info hp == t =+getPatchesBeyondTag :: Patchy p => PatchInfo -> PatchSet rt p wStart wX+ -> FlippedSeal (RL (PatchInfoAnd rt p)) wX+getPatchesBeyondTag t (PatchSet (_ :<: Tagged hp _ _) ps) | info hp == t = flipSeal ps-getPatchesBeyondTag t patchset@(PatchSet (hp :<: ps) ts) =+getPatchesBeyondTag t patchset@(PatchSet ts (ps :<: hp)) = if info hp == t then if getUncovered patchset == [info hp] -- special case to avoid looking at redundant patches@@ -140,63 +200,63 @@ else case splitOnTag t patchset of Just (_ :> e) -> flipSeal e _ -> impossible- else case getPatchesBeyondTag t (PatchSet ps ts) of- FlippedSeal xxs -> FlippedSeal (hp :<: xxs)+ else case getPatchesBeyondTag t (PatchSet ts ps) of+ FlippedSeal xxs -> FlippedSeal (xxs :<: hp) getPatchesBeyondTag t (PatchSet NilRL NilRL) = bug $ "tag\n" ++ renderString Encode (showPatchInfoUI t) ++ "\nis not in the patchset in getPatchesBeyondTag."-getPatchesBeyondTag t0 (PatchSet NilRL (Tagged t _ ps :<: ts)) =- getPatchesBeyondTag t0 (PatchSet (t :<: ps) ts)+getPatchesBeyondTag t0 (PatchSet (ts :<: Tagged t _ ps) NilRL) =+ getPatchesBeyondTag t0 (PatchSet ts (ps :<: t)) -- |splitOnTag takes a tag's 'PatchInfo', and a 'PatchSet', and attempts to -- find the tag in the PatchSet, returning a pair: the clean PatchSet "up to" -- the tag, and a RL of patches after the tag; If the tag is not in the -- PatchSet, we return Nothing.-splitOnTag :: (Patchy p, NameHack p) => PatchInfo -> PatchSet p wStart wX- -> Maybe ((PatchSet p :> RL (PatchInfoAnd p)) wStart wX)+splitOnTag :: Patchy p => PatchInfo -> PatchSet rt p wStart wX+ -> Maybe ((PatchSet rt p :> RL (PatchInfoAnd rt p)) wStart wX) -- If the tag we are looking for is the first Tagged tag of the patchset, just -- separate out the patchset's patches.-splitOnTag t (PatchSet ps ts@(Tagged hp _ _ :<: _)) | info hp == t =- Just $ PatchSet NilRL ts :> ps+splitOnTag t (PatchSet ts@(_ :<: Tagged hp _ _) ps) | info hp == t =+ Just $ PatchSet ts NilRL :> ps -- If the tag is the most recent patch in the set, we check if the patch is the -- only non-depended-on patch in the set (i.e. it is a clean tag); creating a -- new Tagged out of the patches and tag, and adding it to the patchset, if -- this is the case. Otherwise, we try to make the tag clean.-splitOnTag t patchset@(PatchSet hps@(hp :<: ps) ts) | info hp == t =+splitOnTag t patchset@(PatchSet ts hps@(ps :<: hp)) | info hp == t = if getUncovered patchset == [t]- then Just $ PatchSet NilRL (Tagged hp Nothing ps :<: ts) :> NilRL+ then Just $ PatchSet (ts :<: Tagged hp Nothing ps) NilRL :> NilRL else case partitionRL ((`notElem` (t : ds)) . info) hps of -- Partition hps by those that are the tag and its explicit deps.- tagAndDeps@(hp' :<: ds') :> nonDeps ->+ tagAndDeps@(ds' :<: hp') :> nonDeps -> -- If @ds@ doesn't contain the tag of the first Tagged, that -- tag will also be returned by the call to getUncovered - so -- we need to unwrap the next Tagged in order to expose it to -- being partitioned out in the recursive call to splitOnTag.- if getUncovered (PatchSet tagAndDeps ts) == [t]+ if getUncovered (PatchSet ts tagAndDeps) == [t] then let tagged = Tagged hp' Nothing ds' in- return $ PatchSet NilRL (tagged :<: ts) :> nonDeps+ return $ PatchSet (ts :<: tagged) NilRL :> nonDeps else do- unfolded <- unwrapOneTagged $ PatchSet tagAndDeps ts+ unfolded <- unwrapOneTagged $ PatchSet ts tagAndDeps xx :> yy <- splitOnTag t unfolded- return $ xx :> (nonDeps +<+ yy)+ return $ xx :> (yy +<+ nonDeps) _ -> impossible where ds = getdeps (hopefully hp) -- We drop the leading patch, to try and find a non-Tagged tag.-splitOnTag t (PatchSet (p :<: ps) ts) = do- ns :> x <- splitOnTag t (PatchSet ps ts)- return $ ns :> (p :<: x)+splitOnTag t (PatchSet ts (ps :<: p)) = do+ ns :> x <- splitOnTag t (PatchSet ts ps)+ return $ ns :> (x :<: p) -- If there are no patches left, we "unfold" the next Tagged, and try again.-splitOnTag t0 patchset@(PatchSet NilRL (Tagged _ _ _s :<: _)) =+splitOnTag t0 patchset@(PatchSet (_ :<: Tagged _ _ _s) NilRL) = unwrapOneTagged patchset >>= splitOnTag t0 -- If we've checked all the patches, but haven't found the tag, return Nothing. splitOnTag _ (PatchSet NilRL NilRL) = Nothing -- |'unwrapOneTagged' unfolds a single Tagged object in a PatchSet, adding the -- tag and patches to the PatchSet's patch list.-unwrapOneTagged :: (Monad m) => PatchSet p wX wY -> m (PatchSet p wX wY)-unwrapOneTagged (PatchSet ps (Tagged t _ tps :<: ts)) =- return $ PatchSet (ps +<+ t :<: tps) ts+unwrapOneTagged :: (Monad m) => PatchSet rt p wX wY -> m (PatchSet rt p wX wY)+unwrapOneTagged (PatchSet (ts :<: Tagged t _ tps) ps) =+ return $ PatchSet ts (tps :<: t +<+ ps) unwrapOneTagged _ = fail "called unwrapOneTagged with no Tagged's in the set" -- | @getUncovered ps@ returns the 'PatchInfo' for all the patches in@@ -207,11 +267,11 @@ -- Keep in mind that in a typical repository with a lot of tags, only a small -- fraction of tags would be returned as they would be at least indirectly -- depended on by the topmost ones.-getUncovered :: PatchSet p wStart wX -> [PatchInfo]+getUncovered :: PatchSet rt p wStart wX -> [PatchInfo] getUncovered patchset = case patchset of- (PatchSet ps NilRL) -> findUncovered (mapRL infoAndExplicitDeps ps)- (PatchSet ps (Tagged t _ _ :<: _)) ->- findUncovered (mapRL infoAndExplicitDeps (ps +<+ t :<: NilRL))+ (PatchSet NilRL ps) -> findUncovered (mapRL infoAndExplicitDeps ps)+ (PatchSet (_ :<: Tagged t _ _) ps) ->+ findUncovered (mapRL infoAndExplicitDeps (NilRL :<: t +<+ ps)) where findUncovered :: [(PatchInfo, Maybe [PatchInfo])] -> [PatchInfo] findUncovered [] = []@@ -234,7 +294,7 @@ -- |infoAndExplicitDeps returns the patch info and (for tags only) the list -- of explicit dependencies of a patch.- infoAndExplicitDeps :: PatchInfoAnd p wX wY+ infoAndExplicitDeps :: PatchInfoAnd rt p wX wY -> (PatchInfo, Maybe [PatchInfo]) infoAndExplicitDeps p | isTag (info p) = (info p, getdeps `fmap` hopefullyM p)@@ -244,59 +304,46 @@ -- (see 'optimizePatchset') and only optimises at most one tag in -- there, going for the most recent tag which has no non-depended -- patch after it. Older tags won't be 'clean', which means the--- PatchSet will not be in 'unclean :< clean' state.-slightlyOptimizePatchset :: PatchSet p wStart wX -> PatchSet p wStart wX+-- PatchSet will not be in 'clean :> unclean' state.+slightlyOptimizePatchset :: PatchSet rt p wStart wX -> PatchSet rt p wStart wX slightlyOptimizePatchset (PatchSet ps0 ts0) = sops $ PatchSet (prog ps0) ts0 where prog = progressRL "Optimizing inventory"- sops :: PatchSet p wStart wY -> PatchSet p wStart wY- sops patchset@(PatchSet NilRL _) = patchset- sops patchset@(PatchSet (hp :<: ps) ts)+ sops :: PatchSet rt p wStart wY -> PatchSet rt p wStart wY+ sops patchset@(PatchSet _ NilRL) = patchset+ sops patchset@(PatchSet ts (ps :<: hp)) | isTag (info hp) = if getUncovered patchset == [info hp] -- exactly one tag and it depends on everything not already -- archived- then PatchSet NilRL (Tagged hp Nothing ps :<: ts)+ then PatchSet (ts :<: Tagged hp Nothing ps) NilRL -- other tags or other top-level patches too (so move past hp)- else let ps' = sops $ PatchSet (prog ps) ts in+ else let ps' = sops $ PatchSet ts (prog ps) in appendPSFL ps' (hp :>: NilFL)- | otherwise = appendPSFL (sops $ PatchSet ps ts) (hp :>: NilFL)+ | otherwise = appendPSFL (sops $ PatchSet ts ps) (hp :>: NilFL) -commuteToEnd :: forall p wStart wX wY- . (Patchy p, NameHack p)- => RL (PatchInfoAnd p) wX wY- -> PatchSet p wStart wY- -> (PatchSet p :> RL (PatchInfoAnd p)) wStart wX-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+removeFromPatchSet :: Patchy p => FL (PatchInfoAnd rt p) wX wY+ -> PatchSet rt p wStart wY -> Maybe (PatchSet rt p wStart wX)+removeFromPatchSet bad (PatchSet ts ps) | all (`elem` mapRL info ps) (mapFL info bad) = do+ ps' <- fastRemoveSubsequenceRL (reverseFL bad) ps+ return (PatchSet ts ps')+removeFromPatchSet _ (PatchSet NilRL _) = Nothing+removeFromPatchSet bad (PatchSet (ts :<: Tagged t _ tps) ps) =+ removeFromPatchSet bad (PatchSet ts (tps :<: t +<+ ps)) -removeFromPatchSet :: (Patchy p, NameHack p) => FL (PatchInfoAnd p) wX wY- -> PatchSet p wStart wY -> Maybe (PatchSet p wStart wX)-removeFromPatchSet bad0 = rfns (reverseFL bad0)- where- rfns :: (Patchy p, NameHack p)- => RL (PatchInfoAnd p) wX wY -> PatchSet p wStart wY- -> Maybe (PatchSet p wStart wX)- 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)+fastRemoveSubsequenceRL :: Patchy p+ => RL (PatchInfoAnd rt p) wY wZ+ -> RL (PatchInfoAnd rt p) wX wZ+ -> Maybe (RL (PatchInfoAnd rt p) wX wY)+fastRemoveSubsequenceRL NilRL ys = Just ys+fastRemoveSubsequenceRL (xs:<:x) ys = fastRemoveRL x ys >>= fastRemoveSubsequenceRL xs -findCommonAndUncommon :: forall p wStart wX wY . (Patchy p, NameHack p)- => PatchSet p wStart wX -> PatchSet p wStart wY- -> Fork (PatchSet p)- (FL (PatchInfoAnd p))- (FL (PatchInfoAnd p)) wStart wX wY+findCommonAndUncommon :: forall rt p wStart wX wY . Patchy p+ => PatchSet rt p wStart wX -> PatchSet rt p wStart wY+ -> Fork (PatchSet rt p)+ (FL (PatchInfoAnd rt p))+ (FL (PatchInfoAnd rt p)) wStart wX wY findCommonAndUncommon us them = case taggedIntersection us them of Fork common us' them' -> case partitionFL (infoIn them') $ reverseRL us' of@@ -311,15 +358,15 @@ ++ renderString Encode (vcat $ mapRL (showPatchInfoUI . info) $ reverseFL bad) _ :> NilFL :> only_theirs ->- Fork (PatchSet (reverseFL common2) common)+ Fork (PatchSet common (reverseFL common2)) only_ours (unsafeCoercePStart only_theirs) where infoIn inWhat = (`elem` mapRL info inWhat) . info -findCommonWithThem :: (Patchy p, NameHack p)- => PatchSet p wStart wX- -> PatchSet p wStart wY- -> (PatchSet p :> FL (PatchInfoAnd p)) wStart wX+findCommonWithThem :: Patchy p+ => PatchSet rt p wStart wX+ -> PatchSet rt p wStart wY+ -> (PatchSet rt p :> FL (PatchInfoAnd rt p)) wStart wX findCommonWithThem us them = case taggedIntersection us them of Fork common us' them' -> case partitionFL ((`elem` mapRL info them') . info) $ reverseRL us' of@@ -328,19 +375,19 @@ ++ renderString Encode (vcat $ mapRL (showPatchInfoUI . info) $ reverseFL bad) common2 :> _nilfl :> only_ours ->- PatchSet (reverseFL common2) common :> unsafeCoerceP only_ours+ PatchSet common (reverseFL common2) :> unsafeCoerceP only_ours -findUncommon :: (Patchy p, NameHack p)- => PatchSet p wStart wX -> PatchSet p wStart wY- -> (FL (PatchInfoAnd p) :\/: FL (PatchInfoAnd p)) wX wY+findUncommon :: Patchy p+ => PatchSet rt p wStart wX -> PatchSet rt p wStart wY+ -> (FL (PatchInfoAnd rt p) :\/: FL (PatchInfoAnd rt p)) wX wY findUncommon us them = case findCommonWithThem us them of _common :> us' -> case findCommonWithThem them us of _ :> them' -> unsafeCoercePStart us' :\/: them' -countUsThem :: (Patchy p, NameHack p)- => PatchSet p wStart wX- -> PatchSet p wStart wY+countUsThem :: Patchy p+ => PatchSet rt p wStart wX+ -> PatchSet rt p wStart wY -> (Int, Int) countUsThem us them = case taggedIntersection us them of@@ -348,18 +395,18 @@ tt = mapRL info them' in (length $ uu \\ tt, length $ tt \\ uu) -mergeThem :: (Patchy p, Merge p, NameHack p)- => PatchSet p wStart wX -> PatchSet p wStart wY- -> Sealed (FL (PatchInfoAnd p) wX)+mergeThem :: (Patchy p, Merge p)+ => PatchSet rt p wStart wX -> PatchSet rt p wStart wY+ -> Sealed (FL (PatchInfoAnd rt p) wX) mergeThem us them = case taggedIntersection us them of Fork _ us' them' -> case merge2FL (reverseRL us') (reverseRL them') of them'' :/\: _ -> Sealed them'' -newsetIntersection :: (Patchy p, NameHack p)- => [SealedPatchSet p wStart]- -> SealedPatchSet p wStart+newsetIntersection :: Patchy p+ => [SealedPatchSet rt p wStart]+ -> SealedPatchSet rt p wStart newsetIntersection [] = seal $ PatchSet NilRL NilRL newsetIntersection [x] = x newsetIntersection (Sealed y : ys) =@@ -368,38 +415,35 @@ Fork 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+ commonps :> _ -> seal $ PatchSet common commonps -newsetUnion :: (Patchy p, Merge p, NameHack p)- => [SealedPatchSet p wStart]- -> SealedPatchSet p wStart+newsetUnion :: (Patchy p, Merge p)+ => [SealedPatchSet rt p wStart]+ -> SealedPatchSet rt p wStart newsetUnion [] = seal $ PatchSet NilRL NilRL newsetUnion [x] = x-newsetUnion (Sealed y@(PatchSet psy tsy) : Sealed y2 : ys) =+newsetUnion (Sealed y@(PatchSet tsy psy) : Sealed y2 : ys) = case mergeThem y y2 of Sealed p2 ->- newsetUnion $ seal (PatchSet (reverseFL p2 +<+ psy) tsy) : ys+ newsetUnion $ seal (PatchSet tsy (psy +<+ reverseFL p2)) : 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+-- While this function is similar to 'mergeFL', there are some 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?)------ * 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 :: (Patchy p, Merge p, NameHack p)- => FL (PatchInfoAnd p) wX wY- -> FL (PatchInfoAnd p) wX wZ- -> (FL (PatchInfoAnd p) :/\: FL (PatchInfoAnd p)) wY wZ+-- (Question from Eric Kow: in what sense? Why not fix 'mergeFL'?)+-- (bf: I guess what was meant here is that 'merge2FL' works in the+-- the way it does because it considers patch meta data whereas+-- 'mergeFL' cannot since it must work for primitive patches, too.+merge2FL :: (Patchy p, Merge p)+ => FL (PatchInfoAnd rt p) wX wY+ -> FL (PatchInfoAnd rt p) wX wZ+ -> (FL (PatchInfoAnd rt p) :/\: FL (PatchInfoAnd rt p)) wY wZ merge2FL xs NilFL = NilFL :/\: xs merge2FL NilFL ys = ys :/\: NilFL merge2FL xs (y :>: ys) | Just xs' <- fastRemoveFL y xs = merge2FL xs' ys@@ -410,14 +454,14 @@ ys'' :/\: xs' -> ys'' :/\: (x' :>: xs') -areUnrelatedRepos :: (Patchy p, NameHack p)- => PatchSet p wStart wX- -> PatchSet p wStart wY -> Bool+areUnrelatedRepos :: Patchy p+ => PatchSet rt p wStart wX+ -> PatchSet rt p wStart wY -> Bool areUnrelatedRepos us them = case taggedIntersection us them of Fork c u t -> checkit c u t where- checkit (Tagged{} :<: _) _ _ = False+ checkit (_ :<: Tagged{}) _ _ = False checkit _ u t | t `isShorterThanRL` 5 = False | u `isShorterThanRL` 5 = False | otherwise = null $ intersect (mapRL info u) (mapRL info t)@@ -427,9 +471,21 @@ -- 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 :: (Patchy p, NameHack p)- => PatchInfoAnd p wX wY- -> FL (PatchInfoAnd p) wX wZ -> Maybe (FL (PatchInfoAnd p) wY wZ)+--+-- A note on the witness types: the patch to be removed is typed as if it had+-- to be the first in the list, since it has the same pre-context as the list.+-- The types fit together (internally, in this module) because we commute the+-- patch to the front before removing it and commutation inside a sequence does+-- not change the sequence's contexts.+--+-- However, the use sites outside this module are something different. We+-- usually need coercions to get the patch(es) to be removed in shape. This is+-- not very nice but probably unavoidable given the approximative nature of+-- context witnesses.+fastRemoveFL :: Patchy p+ => PatchInfoAnd rt p wX wY -- this type assumes element is at the front+ -> FL (PatchInfoAnd rt p) wX wZ+ -> Maybe (FL (PatchInfoAnd rt p) wY wZ) fastRemoveFL _ NilFL = Nothing fastRemoveFL a (b :>: bs) | IsEq <- a =\/= b = Just bs | info a `notElem` mapFL info bs = Nothing@@ -440,38 +496,37 @@ Just (b' :>: bs') where i = info a- pullout :: (Patchy p, NameHack p)- => RL (PatchInfoAnd p) wA0 wA- -> FL (PatchInfoAnd p) wA wB- -> Maybe ((PatchInfoAnd p :> FL (PatchInfoAnd p)) wA0 wB)+ pullout :: Patchy p+ => RL (PatchInfoAnd rt p) wA wB+ -> FL (PatchInfoAnd rt p) wB wC+ -> Maybe ((PatchInfoAnd rt p :> FL (PatchInfoAnd rt p)) wA wC) 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+ | otherwise = pullout (acc :<: x) xs -fastRemoveRL :: (Patchy p, NameHack p)- => PatchInfoAnd p wY wZ- -> RL (PatchInfoAnd p) wX wZ -> Maybe (RL (PatchInfoAnd p) wX wY)+-- | Same as 'fastRemoveFL' only for 'RL'.+fastRemoveRL :: Patchy p+ => PatchInfoAnd rt p wY wZ -- this type assumes element is at the back+ -> RL (PatchInfoAnd rt p) wX wZ+ -> Maybe (RL (PatchInfoAnd rt p) wX wY) fastRemoveRL _ NilRL = Nothing-fastRemoveRL a (b :<: bs) | IsEq <- a =/\= b = Just bs+fastRemoveRL a (bs :<: b) | IsEq <- b =/\= a = Just bs | info a `notElem` mapRL info bs = Nothing-fastRemoveRL a (b :<: bs) = do- bs' :> a' <- pullout NilFL bs+fastRemoveRL a (bs :<: b) = do+ bs' :> a' <- pullout bs NilFL b' :> a'' <- commute (a' :> b) IsEq <- return (a'' =/\= a)- Just (b' :<: bs')+ Just (bs' :<: b') where i = info a- pullout :: (Patchy p, NameHack p)- => FL (PatchInfoAnd p) wB wC- -> RL (PatchInfoAnd p) wA wB- -> Maybe ((RL (PatchInfoAnd p) :> PatchInfoAnd p) wA wC)- 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+ pullout :: Patchy p+ => RL (PatchInfoAnd rt p) wA wB+ -> FL (PatchInfoAnd rt p) wB wC+ -> Maybe ((RL (PatchInfoAnd rt p) :> PatchInfoAnd rt p) wA wC)+ pullout NilRL _ = Nothing+ pullout (xs :<: x) acc+ | info x == i = do acc' :> x' <- commuteFL (x :> acc)+ Just (xs +<+ reverseFL acc' :> x')+ | otherwise = pullout xs (x :>: acc)
src/Darcs/Patch/Dummy.hs view
@@ -8,7 +8,6 @@ import Darcs.Patch.FileHunk ( IsHunk ) import Darcs.Patch.Format ( PatchListFormat ) import Darcs.Patch.Matchable ( Matchable )-import Darcs.Patch.MaybeInternal ( MaybeInternal ) import Darcs.Patch.Patchy ( Patchy, ShowPatch, Invert, Commute, Apply(..), PatchInspect , ReadPatch )@@ -17,13 +16,12 @@ ( PrimConstruct, PrimCanonize, PrimClassify , PrimDetails, PrimShow, PrimRead, PrimApply ) import Darcs.Patch.Merge ( Merge)-import Darcs.Patch.Rebase.NameHack ( NameHack )-import Darcs.Patch.Rebase.Recontext ( RecontextRebase ) import Darcs.Patch.Repair ( Check, RepairToFL ) import Darcs.Patch.RepoPatch ( RepoPatch ) import Darcs.Patch.Show ( ShowPatchBasic ) import Darcs.Patch.Witnesses.Eq ( MyEq )-import Storage.Hashed.Tree( Tree )+import Darcs.Patch.Witnesses.Show ( Show2 )+import Darcs.Util.Tree( Tree ) data DummyPrim wX wY@@ -50,6 +48,7 @@ instance PrimRead DummyPrim instance PrimApply DummyPrim instance PrimPatch DummyPrim+instance Show2 DummyPrim instance PatchDebug DummyPrim @@ -61,6 +60,7 @@ instance ReadPatch DummyPatch instance ShowPatchBasic DummyPatch instance ShowPatch DummyPatch+instance Show2 DummyPatch instance Commute DummyPatch instance Apply DummyPatch where type ApplyState DummyPatch = Tree@@ -76,9 +76,6 @@ instance RepairToFL DummyPatch instance PrimPatchBase DummyPatch where type PrimOf DummyPatch = DummyPrim-instance MaybeInternal DummyPatch-instance NameHack DummyPatch-instance RecontextRebase DummyPatch instance RepoPatch DummyPatch instance PatchDebug DummyPatch
src/Darcs/Patch/Effect.hs view
@@ -1,5 +1,9 @@+{-# OPTIONS_GHC -fno-warn-unrecognised-pragmas #-} module Darcs.Patch.Effect ( Effect(..) ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Prim.Class ( PrimOf ) import Darcs.Patch.Witnesses.Ordered@@ -17,6 +21,7 @@ effect = reverseRL . effectRL effectRL :: p wX wY -> RL (PrimOf p) wX wY effectRL = reverseFL . effect+ {-# MINIMAL effect | effectRL #-} instance Effect p => Effect (FL p) where effect p = concatFL $ mapFL_FL effect p
src/Darcs/Patch/FileHunk.hs view
@@ -3,6 +3,9 @@ ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Util.Path ( FileName ) import Darcs.Patch.Format ( FileNameFormat ) import Darcs.Patch.Show ( formatFileName )
src/Darcs/Patch/Index/Monad.hs view
@@ -17,22 +17,23 @@ -- Boston, MA 02110-1301, USA. -{-# OPTIONS_GHC -fno-warn-missing-methods #-} {-# LANGUAGE CPP,GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} module Darcs.Patch.Index.Monad ( FileModMonad, withPatchMods ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Index.Types ( PatchMod(..) )-import Darcs.Patch.ApplyMonad ( ApplyMonad(..) )-import Control.Applicative ( Applicative )+import Darcs.Patch.ApplyMonad ( ApplyMonad(..), ApplyMonadTree(..) ) import Control.Monad.State import Control.Arrow import Darcs.Util.Path ( FileName, fn2fp, movedirfilename ) import qualified Data.Set as S import Data.Set ( Set ) import Data.List ( isPrefixOf )-import Storage.Hashed.Tree (Tree)+import Darcs.Util.Tree (Tree) #include "impossible.h" newtype FileModMonad a = FMM (State (Set FileName, [PatchMod FileName]) a)@@ -43,8 +44,13 @@ -- These instances are defined to be used only with -- apply.-instance ApplyMonad FileModMonad Tree where+instance ApplyMonad Tree FileModMonad where type ApplyMonadBase FileModMonad = FileModMonad+ nestedApply _ _ = bug "nestedApply FileModMonad"+ liftApply _ _ = bug "liftApply FileModMonad"+ getApplyState = bug "getApplyState FileModMonad"++instance ApplyMonadTree FileModMonad where mDoesDirectoryExist d = do fps <- gets fst return $ S.member d fps@@ -52,10 +58,6 @@ fps <- gets fst return $ S.member f fps mReadFilePS _ = bug "mReadFilePS FileModMonad"- nestedApply _ _ = bug "nestedApply FileModMonad"- liftApply _ _ = bug "liftApply FileModMonad"- getApplyState = bug "getApplyState FileModMonad"- putApplyState _ = bug "putApplyState FileModMonad" mCreateFile = createFile mCreateDirectory = createDir mRemoveFile = remove@@ -107,7 +109,7 @@ error $ unwords [ "error: patch index entry for" , if isFile then "file" else "directory" , fn2fp fn- , "created >1 times"+ , "created >1 times. Run `darcs repair` and try again." ] remove :: FileName -> FileModMonad ()
src/Darcs/Patch/Index/Types.hs view
@@ -18,6 +18,9 @@ module Darcs.Patch.Index.Types where +import Prelude ()+import Darcs.Prelude+ import Darcs.Util.Crypt.SHA1( SHA1(..) ) import Darcs.Util.Path ( fp2fn, fn2fp, FileName ) import Darcs.Patch.Info ( makePatchname, PatchInfo )
src/Darcs/Patch/Info.hs view
@@ -25,6 +25,10 @@ piName, piRename, piAuthor, piTag, piLog, showPatchInfo, isTag, readPatchInfos, escapeXML ) where++import Prelude ()+import Darcs.Prelude+ import System.Random ( randomRIO ) import Numeric ( showHex ) import Control.Monad ( when, unless, void )
src/Darcs/Patch/Inspect.hs view
@@ -3,6 +3,9 @@ ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Witnesses.Ordered ( FL, RL, reverseRL, mapFL ) import qualified Data.ByteString.Char8 as BC
src/Darcs/Patch/Invert.hs view
@@ -3,6 +3,9 @@ ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Witnesses.Ordered ( FL(..), RL(..), reverseFL, reverseRL ) @@ -11,11 +14,11 @@ invertFL :: Invert p => FL p wX wY -> RL p wY wX invertFL NilFL = NilRL-invertFL (x:>:xs) = invert x :<: invertFL xs+invertFL (x:>:xs) = invertFL xs :<: invert x invertRL :: Invert p => RL p wX wY -> FL p wY wX invertRL NilRL = NilFL-invertRL (x:<:xs) = invert x :>: invertRL xs+invertRL (xs:<:x) = invert x :>: invertRL xs instance Invert p => Invert (FL p) where invert = reverseRL . invertFL
src/Darcs/Patch/Match.hs view
@@ -67,6 +67,9 @@ , MatchFlag(..) ) where +import Prelude ()+import Darcs.Prelude+ import Text.ParserCombinators.Parsec ( parse , CharParser@@ -100,25 +103,27 @@ import Darcs.Util.Path ( AbsolutePath ) import Darcs.Patch ( Patchy+ , IsRepoType , hunkMatches , listTouchedFiles- , patchcontents- , Named , invert , invertRL- , patch2patchinfo , apply ) import Darcs.Patch.Info ( justName, justAuthor, justLog, makePatchname, piDate )+import Darcs.Patch.Named.Wrapped+ ( WrappedNamed+ , patch2patchinfo+ ) import qualified Data.ByteString.Char8 as BC import Darcs.Patch.Dummy ( DummyPatch ) import Darcs.Patch.Matchable ( Matchable )-import Darcs.Patch.MaybeInternal ( MaybeInternal, patchInternalChecker, isInternal, flIsInternal ) import Darcs.Patch.MonadProgress ( MonadProgress )+import Darcs.Patch.Named.Wrapped ( runInternalChecker, namedIsInternal, namedInternalChecker ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, info, conscientiously, hopefully ) import Darcs.Patch.Set ( PatchSet(..), Tagged(..), SealedPatchSet, newset2RL, Origin ) import Darcs.Patch.Type ( PatchType(..) )@@ -127,7 +132,7 @@ import Darcs.Patch.Depends ( getPatchesBeyondTag, splitOnTag ) import Darcs.Patch.Witnesses.Eq ( isIsEq )-import Darcs.Patch.Witnesses.Ordered ( RL(..), consRLSealed, FL(..), (:>)(..) )+import Darcs.Patch.Witnesses.Ordered ( RL(..), snocRLSealed, FL(..), (:>)(..) ) import Darcs.Patch.Witnesses.Sealed ( FlippedSeal(..), Sealed2(..), seal, flipSeal, seal2, unsealFlipped, unseal2, unseal )@@ -136,18 +141,18 @@ import Darcs.Util.DateMatcher ( parseDateMatcher ) -import Storage.Hashed.Tree ( Tree )+import Darcs.Util.Tree ( Tree ) #include "impossible.h" -- | A type for predicates over patches which do not care about -- contexts-type MatchFun p = Sealed2 (PatchInfoAnd p) -> Bool+type MatchFun rt p = Sealed2 (PatchInfoAnd rt p) -> Bool -- | A @Matcher@ is made of a 'MatchFun' which we will use to match -- patches and a @String@ representing it.-data Matcher p = MATCH String (MatchFun p)+data Matcher rt p = MATCH String (MatchFun rt p) -instance Show (Matcher p) where+instance Show (Matcher rt p) where show (MATCH s _) = '"':s ++ "\"" @@ -172,37 +177,37 @@ deriving ( Show ) -makeMatcher :: String -> (Sealed2 (PatchInfoAnd p) -> Bool) -> Matcher p+makeMatcher :: String -> (Sealed2 (PatchInfoAnd rt p) -> Bool) -> Matcher rt p makeMatcher = MATCH -- | @applyMatcher@ applies a matcher to a patch.-applyMatcher :: Matcher p -> PatchInfoAnd p wX wY -> Bool+applyMatcher :: Matcher rt p -> PatchInfoAnd rt p wX wY -> Bool applyMatcher (MATCH _ m) = m . seal2 -parseMatch :: Matchable p => String -> Either String (MatchFun p)+parseMatch :: Matchable p => String -> Either String (MatchFun rt p) parseMatch pattern = case parse matchParser "match" pattern of Left err -> Left $ "Invalid --match pattern '"++ pattern ++ "'.\n"++ unlines (map (" "++) $ lines $ show err) -- indent Right m -> Right m -matchPattern :: Matchable p => String -> Matcher p+matchPattern :: Matchable p => String -> Matcher rt p matchPattern pattern = case parseMatch pattern of Left err -> error err Right m -> makeMatcher pattern m -addInternalMatcher :: Matchable p => Maybe (Matcher p) -> Maybe (Matcher p)+addInternalMatcher :: (IsRepoType rt, Matchable p) => Maybe (Matcher rt p) -> Maybe (Matcher rt p) addInternalMatcher om =- case patchInternalChecker of+ case namedInternalChecker of Nothing -> om Just f ->- let matchFun = unseal2 (not . isIsEq . isInternal f . patchcontents . hopefully)+ let matchFun = unseal2 (not . isIsEq . runInternalChecker f . hopefully) in case om of Nothing -> Just (MATCH "internal patch" matchFun) Just (MATCH s oldFun) -> Just (MATCH s (\p -> matchFun p && oldFun p)) -matchParser :: Matchable p => CharParser st (MatchFun p)+matchParser :: Matchable p => CharParser st (MatchFun rt p) matchParser = submatcher <?> helpfulErrorMsg where submatcher = do@@ -217,18 +222,18 @@ ++ "\nfor more help, see `darcs help patterns`." -- This type signature is just to bind an ambiguous type var.- ps :: [(String, String, [String], String -> MatchFun DummyPatch)]+ ps :: [(String, String, [String], String -> MatchFun rt DummyPatch)] ps = primitiveMatchers -- matchAnyPatch is returned if submatch fails without consuming any -- input, i.e. if we pass --match '', we want to match anything.- matchAnyPatch :: Matchable p => MatchFun p+ matchAnyPatch :: Matchable p => MatchFun rt p matchAnyPatch = const True -submatch :: Matchable p => CharParser st (MatchFun p)+submatch :: Matchable p => CharParser st (MatchFun rt p) submatch = buildExpressionParser table match -table :: OperatorTable Char st (MatchFun p)+table :: OperatorTable Char st (MatchFun rt p) table = [ [prefix "not" negate_match, prefix "!" negate_match ] , [binary "||" or_match,@@ -248,13 +253,13 @@ trystring :: String -> CharParser st String trystring s = try $ string s -match :: Matchable p => CharParser st (MatchFun p)+match :: Matchable p => CharParser st (MatchFun rt p) match = between spaces spaces (parens submatch <|> choice matchers_) where matchers_ = map createMatchHelper primitiveMatchers -createMatchHelper :: (String, String, [String], String -> MatchFun p)- -> CharParser st (MatchFun p)+createMatchHelper :: (String, String, [String], String -> MatchFun rt p)+ -> CharParser st (MatchFun rt p) createMatchHelper (key,_,_,matcher) = do _ <- trystring key spaces@@ -278,7 +283,7 @@ "- --hash=HASH is a synonym for --matches='hash HASH'", "- --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'`",+ " `darcs log --from-patch='html.*documentation' --to-match='date 20040212'`", "", "The following primitive Boolean expressions are supported:" ,""]@@ -286,7 +291,7 @@ ++ ["", "Here are some examples:", ""] ++ examples where -- This type signature exists to appease GHC.- ps :: [(String, String, [String], String -> MatchFun DummyPatch)]+ ps :: [(String, String, [String], String -> MatchFun rt DummyPatch)] ps = primitiveMatchers keywords = [showKeyword k d | (k,d,_,_) <- ps] examples = [showExample k e | (k,_,es,_) <- ps, e <- es]@@ -296,13 +301,10 @@ -- or "exact STRING - match against exact patch name". "- " ++ keyword ++ " - " ++ description ++ "." showExample keyword example =- -- FIXME: this string is long, and its not a use case I've- -- ever seen in practice. Can we use something else,- -- like "darcs changes --matches"? --twb, 2008-12-28- " darcs annotate --summary --match "+ " darcs log --match " ++ "'" ++ keyword ++ " " ++ example ++ "'" -primitiveMatchers :: Matchable p => [(String, String, [String], String -> MatchFun p)]+primitiveMatchers :: Matchable p => [(String, String, [String], String -> MatchFun rt p)] -- ^ keyword (operator), help description, list -- of examples, matcher function primitiveMatchers =@@ -331,8 +333,8 @@ , ["src/foo.c", "src/", "\"src/*.(c|h)\""] , touchmatch ) ] -parens :: CharParser st (MatchFun p)- -> CharParser st (MatchFun p)+parens :: CharParser st (MatchFun rt p)+ -> CharParser st (MatchFun rt p) parens = between (string "(") (string ")") quoted :: CharParser st String@@ -345,7 +347,7 @@ <?> "string" namematch, exactmatch, authormatch, hunkmatch, hashmatch, datematch, touchmatch- :: Matchable p => String -> MatchFun p+ :: Matchable p => String -> MatchFun rt p namematch r (Sealed2 hp) = isJust $ matchRegex (mkRegex r) $ justName (info hp) @@ -353,12 +355,11 @@ authormatch a (Sealed2 hp) = isJust $ matchRegex (mkRegex a) $ justAuthor (info hp) -logmatch :: Matchable p => String -> MatchFun p+logmatch :: Matchable p => String -> MatchFun rt 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+hunkmatch r (Sealed2 hp) = let regexMatcher = isJust . matchRegex (mkRegex r) . BC.unpack+ in hunkMatches regexMatcher hp hashmatch h (Sealed2 hp) = let rh = show $ makePatchname (info hp) lh = map toLower h@@ -367,7 +368,7 @@ datematch d (Sealed2 hp) = let dm = unsafePerformIO $ parseDateMatcher d in dm $ piDate (info hp) -touchmatch r (Sealed2 hp) = let files = listTouchedFiles $ patchcontents $ hopefully hp+touchmatch r (Sealed2 hp) = let files = listTouchedFiles hp in any (isJust . matchRegex (mkRegex r)) files data InclusiveOrExclusive = Inclusive | Exclusive deriving Eq@@ -376,25 +377,30 @@ -- @flags@ which corresponds to a match that is "non-range". Thus, -- @--match@, @--patch@, @--hash@ and @--index@ make @haveNonrangeMatch@ -- true, but not @--from-patch@ or @--to-patch@.-haveNonrangeMatch :: forall p . Matchable p => PatchType p -> [MatchFlag] -> Bool+haveNonrangeMatch :: forall rt p . (IsRepoType rt, Matchable p) => PatchType rt p -> [MatchFlag] -> Bool haveNonrangeMatch _ fs = case hasIndexRange fs of Just (m,n) | m == n -> True; _ -> False- || isJust (nonrangeMatcher fs::Maybe (Matcher p))+ || isJust (nonrangeMatcher fs::Maybe (Matcher rt p)) -- | @havePatchsetMatch flags@ tells whether there is a "patchset -- match" in the flag list. A patchset match is @--match@ or -- @--patch@, or @--context@, but not @--from-patch@ nor (!) -- @--index@. -- Question: Is it supposed not to be a subset of @haveNonrangeMatch@?-havePatchsetMatch :: [MatchFlag] -> Bool-havePatchsetMatch fs = isJust (nonrangeMatcher fs::Maybe (Matcher DummyPatch)) || hasC fs+havePatchsetMatch+ :: forall rt p+ . (IsRepoType rt, Matchable p)+ => PatchType rt p -> [MatchFlag] -> Bool+havePatchsetMatch _ fs = isJust (nonrangeMatcher fs::Maybe (Matcher rt p)) || hasC fs where hasC [] = False hasC (Context _:_) = True hasC (_:xs) = hasC xs -getNonrangeMatchS :: (ApplyMonad m (ApplyState p), MonadProgress m, Matchable p, ApplyState p ~ Tree)+getNonrangeMatchS :: ( ApplyMonad (ApplyState p) m, MonadProgress m+ , IsRepoType rt, Matchable p, ApplyState p ~ Tree+ ) => [MatchFlag]- -> PatchSet p Origin wX+ -> PatchSet rt p Origin wX -> m () getNonrangeMatchS fs repo = case nonrangeMatcher fs of@@ -408,11 +414,11 @@ -- than against all patches since the creation of the repository. firstMatch :: [MatchFlag] -> Bool firstMatch fs = isJust (hasLastn fs)- || isJust (firstMatcher fs::Maybe (Matcher DummyPatch))+ || isJust (firstMatcher fs::Maybe (Matcher rt DummyPatch)) || isJust (hasIndexRange fs) -getFirstMatchS :: (ApplyMonad m (ApplyState p), MonadProgress m, Matchable p)- => [MatchFlag] -> PatchSet p Origin wX -> m ()+getFirstMatchS :: (ApplyMonad (ApplyState p) m, MonadProgress m, Matchable p, IsRepoType rt)+ => [MatchFlag] -> PatchSet rt p Origin wX -> m () getFirstMatchS fs repo = case hasLastn fs of Just n -> unpullLastN repo n@@ -430,10 +436,10 @@ -- is if we match against patches up to a point in the past on, rather -- than against all patches until now. secondMatch :: [MatchFlag] -> Bool-secondMatch fs = isJust (secondMatcher fs::Maybe (Matcher DummyPatch)) || isJust (hasIndexRange fs)+secondMatch fs = isJust (secondMatcher fs::Maybe (Matcher rt DummyPatch)) || isJust (hasIndexRange fs) -unpullLastN :: (ApplyMonad m (ApplyState p), MonadProgress m, Patchy p, MaybeInternal p)- => PatchSet p wX wY+unpullLastN :: (ApplyMonad (ApplyState p) m, MonadProgress m, Patchy p, IsRepoType rt)+ => PatchSet rt p wX wY -> Int -> m () unpullLastN repo n = applyInvRL `unsealFlipped` safetake n (newset2RL repo)@@ -442,7 +448,7 @@ checkMatchSyntax opts = case getMatchPattern opts of Nothing -> return ()- Just p -> either fail (const $ return ()) (parseMatch p::Either String (MatchFun DummyPatch))+ Just p -> either fail (const $ return ()) (parseMatch p::Either String (MatchFun rt DummyPatch)) getMatchPattern :: [MatchFlag] -> Maybe String getMatchPattern [] = Nothing@@ -450,16 +456,16 @@ getMatchPattern (SeveralPattern m:_) = Just m getMatchPattern (_:fs) = getMatchPattern fs -tagmatch :: Matchable p => String -> Matcher p+tagmatch :: Matchable p => String -> Matcher rt p tagmatch r = makeMatcher ("tag-name "++r) tm where tm (Sealed2 p) = let n = justName (info p) in "TAG " `isPrefixOf` n && isJust (matchRegex (mkRegex r) $ drop 4 n) -patchmatch :: Matchable p => String -> Matcher p+patchmatch :: Matchable p => String -> Matcher rt p patchmatch r = makeMatcher ("patch-name "++r) (namematch r) -hashmatch' :: Matchable p => String -> Matcher p+hashmatch' :: Matchable p => String -> Matcher rt p hashmatch' r = makeMatcher ("hash "++r) (hashmatch r) @@ -472,7 +478,8 @@ -- | @nonrangeMatcher@ is the criterion that is used to match against -- patches in the interval. It is 'Just m' when the @--patch@, @--match@, -- @--tag@ options are passed (or their plural variants).-nonrangeMatcher, nonrangeMatcherArgs :: Matchable p => [MatchFlag] -> Maybe (Matcher p)+nonrangeMatcher :: (IsRepoType rt, Matchable p) => [MatchFlag] -> Maybe (Matcher rt p)+nonrangeMatcherArgs :: Matchable p => [MatchFlag] -> Maybe (Matcher rt p) nonrangeMatcher fs = addInternalMatcher $ nonrangeMatcherArgs fs @@ -496,7 +503,7 @@ -- This left bound is also specified when we use the singular versions -- of @--patch@, @--match@ and @--tag@. Otherwise, @firstMatcher@ -- returns @Nothing@.-firstMatcher :: Matchable p => [MatchFlag] -> Maybe (Matcher p)+firstMatcher :: Matchable p => [MatchFlag] -> Maybe (Matcher rt p) firstMatcher [] = Nothing firstMatcher (OnePattern m:_) = strictJust $ matchPattern m firstMatcher (AfterPattern m:_) = strictJust $ matchPattern m@@ -512,7 +519,7 @@ firstMatcherIsTag (AfterTag _:_) = True firstMatcherIsTag (_:fs) = firstMatcherIsTag fs -secondMatcher :: Matchable p => [MatchFlag] -> Maybe (Matcher p)+secondMatcher :: Matchable p => [MatchFlag] -> Maybe (Matcher rt p) secondMatcher [] = Nothing secondMatcher (OnePattern m:_) = strictJust $ matchPattern m secondMatcher (UpToPattern m:_) = strictJust $ matchPattern m@@ -530,20 +537,20 @@ -- | @matchAPatchread fs p@ tells whether @p@ matches the matchers in -- the flags listed in @fs@.-matchAPatchread :: Matchable p => [MatchFlag] -> PatchInfoAnd p wX wY -> Bool+matchAPatchread :: (IsRepoType rt, Matchable p) => [MatchFlag] -> PatchInfoAnd rt p wX wY -> Bool matchAPatchread fs = case nonrangeMatcher fs of Nothing -> const True Just m -> applyMatcher m -- | @matchAPatch fs p@ tells whether @p@ matches the matchers in -- the flags @fs@-matchAPatch :: Matchable p => [MatchFlag] -> PatchInfoAnd p wX wY -> Bool+matchAPatch :: (IsRepoType rt, Matchable p) => [MatchFlag] -> PatchInfoAnd rt p wX wY -> Bool matchAPatch fs p = case nonrangeMatcher fs of Nothing -> True Just m -> applyMatcher m p -matchPatch :: Matchable p => [MatchFlag] -> PatchSet p wStart wX -> Sealed2 (Named p)+matchPatch :: (IsRepoType rt, Matchable p) => [MatchFlag] -> PatchSet rt p wStart wX -> Sealed2 (WrappedNamed rt p) matchPatch fs ps = case hasIndexRange fs of Just (a,a') | a == a' -> case unseal myhead $ dropn (a-1) ps of@@ -551,9 +558,9 @@ Nothing -> error "Patch out of range!" | otherwise -> bug ("Invalid index range match given to matchPatch: "++ show (PatchIndexRange a a'))- where myhead :: PatchSet p wStart wX -> Maybe (Sealed2 (PatchInfoAnd p))- myhead (PatchSet NilRL (Tagged t _ _ :<: _)) = Just $ seal2 t- myhead (PatchSet (x:<:_) _) = Just $ seal2 x+ where myhead :: PatchSet rt p wStart wX -> Maybe (Sealed2 (PatchInfoAnd rt p))+ myhead (PatchSet (_ :<: Tagged t _ _) NilRL) = Just $ seal2 t+ myhead (PatchSet _ (_:<:x)) = Just $ seal2 x myhead _ = Nothing Nothing -> case nonrangeMatcher fs of Nothing -> bug "Couldn't matchPatch"@@ -575,8 +582,8 @@ -- first matcher, ie the one that comes first dependencywise. Hence, -- patches in @matchFirstPatchset fs ps@ are the context for the ones -- we don't want.-matchFirstPatchset :: Matchable p => [MatchFlag] -> PatchSet p wStart wX- -> SealedPatchSet p wStart+matchFirstPatchset :: (IsRepoType rt, Matchable p) => [MatchFlag] -> PatchSet rt p wStart wX+ -> SealedPatchSet rt p wStart matchFirstPatchset fs patchset = case hasLastn fs of Just n -> dropn n patchset@@ -591,19 +598,19 @@ else matchAPatchset m patchset -- | @dropn n ps@ drops the @n@ last patches from @ps@.-dropn :: MaybeInternal p => Int -> PatchSet p wStart wX -> SealedPatchSet p wStart+dropn :: IsRepoType rt => Int -> PatchSet rt p wStart wX -> SealedPatchSet rt p wStart dropn n ps | n <= 0 = seal ps-dropn n (PatchSet NilRL (Tagged t _ ps :<: ts)) = dropn n $ PatchSet (t:<:ps) ts+dropn n (PatchSet (ts :<: Tagged t _ ps) NilRL) = dropn n $ PatchSet ts (ps:<:t) dropn _ (PatchSet NilRL NilRL) = seal $ PatchSet NilRL NilRL-dropn n (PatchSet (p:<:ps) ts)- | isIsEq (flIsInternal (patchcontents (hopefully p)))- = dropn n $ PatchSet ps ts-dropn n (PatchSet (_:<:ps) ts) = dropn (n-1) $ PatchSet ps ts+dropn n (PatchSet ts (ps:<:p))+ | isIsEq (namedIsInternal (hopefully p))+ = dropn n $ PatchSet ts ps+dropn n (PatchSet ts (ps:<:_)) = dropn (n-1) $ PatchSet ts ps -- | @matchSecondPatchset fs ps@ returns the part of @ps@ before its -- second matcher, ie the one that comes last dependencywise.-matchSecondPatchset :: Matchable p => [MatchFlag] -> PatchSet p wStart wX- -> SealedPatchSet p wStart+matchSecondPatchset :: (IsRepoType rt, Matchable p) => [MatchFlag] -> PatchSet rt p wStart wX+ -> SealedPatchSet rt p wStart matchSecondPatchset fs ps = case hasIndexRange fs of Just (a,_) -> dropn (a-1) ps@@ -618,7 +625,7 @@ -- the earliest patch in a sequence, as opposed to 'matchSecondPatchset' which picks up the -- first match starting from the latest patch splitSecondFL :: Matchable p- => (forall wA wB . q wA wB -> Sealed2 (PatchInfoAnd p))+ => (forall wA wB . q wA wB -> Sealed2 (PatchInfoAnd rt p)) -> [MatchFlag] -> FL q wX wY -> (FL q :> FL q) wX wY -- ^The first element is the patches before and including the first patch matching the second matcher,@@ -634,38 +641,38 @@ -- | @findAPatch m ps@ returns the last patch in @ps@ matching @m@, and -- calls 'error' if there is none.-findAPatch :: Matchable p => Matcher p -> PatchSet p wStart wX -> Sealed2 (Named p)+findAPatch :: Matchable p => Matcher rt p -> PatchSet rt p wStart wX -> Sealed2 (WrappedNamed rt 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)+findAPatch m (PatchSet (ts :<: Tagged t _ ps) NilRL) = findAPatch m (PatchSet ts (ps:<:t))+findAPatch m (PatchSet ts (ps:<:p)) | applyMatcher m p = seal2 $ hopefully p+ | otherwise = findAPatch m (PatchSet ts ps) -- | @matchAPatchset m ps@ returns a (the largest?) subset of @ps@ -- ending in patch which matches @m@. Calls 'error' if there is none.-matchAPatchset :: Matchable p => Matcher p -> PatchSet p wStart wX- -> SealedPatchSet p wStart+matchAPatchset :: Matchable p => Matcher rt p -> PatchSet rt p wStart wX+ -> SealedPatchSet rt p wStart 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)+matchAPatchset m (PatchSet (ts :<: Tagged t _ ps) NilRL) = matchAPatchset m (PatchSet ts (ps:<:t))+matchAPatchset m (PatchSet ts (ps:<:p)) | applyMatcher m p = seal (PatchSet ts (ps:<:p))+ | otherwise = matchAPatchset m (PatchSet ts ps) -- | @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+-- i.e. the last one you'd see if you ran darcs log -t @m@. Calls -- 'error' if there is no matching tag.-getMatchingTag :: Matchable p => Matcher p -> PatchSet p wStart wX -> SealedPatchSet p wStart+getMatchingTag :: Matchable p => Matcher rt p -> PatchSet rt p wStart wX -> SealedPatchSet rt p wStart 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)+getMatchingTag m (PatchSet (ts :<: Tagged t _ ps) NilRL) = getMatchingTag m (PatchSet ts (ps:<:t))+getMatchingTag m (PatchSet ts (ps:<:p)) | applyMatcher m p = -- found a non-clean tag, need to commute out the things that it doesn't depend on- case splitOnTag (info p) (PatchSet (p:<:ps) ts) of+ case splitOnTag (info p) (PatchSet ts (ps:<:p)) of Nothing -> bug "splitOnTag couldn't find tag we explicitly provided!" Just (patchSet :> _) -> seal patchSet- | otherwise = getMatchingTag m (PatchSet ps ts)+ | otherwise = getMatchingTag m (PatchSet ts ps) -splitMatchFL :: Matchable p => (forall wA wB . q wA wB -> Sealed2 (PatchInfoAnd p)) -> Matcher p -> FL q wX wY -> (FL q :> FL q) wX wY+splitMatchFL :: Matchable p => (forall wA wB . q wA wB -> Sealed2 (PatchInfoAnd rt p)) -> Matcher rt p -> FL q wX wY -> (FL q :> FL q) wX wY splitMatchFL _extract m NilFL = error $ "Couldn't find patch matching " ++ show m splitMatchFL extract m (p :>: ps) | unseal2 (applyMatcher m) . extract $ p = (p :>: NilFL) :> ps@@ -674,40 +681,40 @@ -- | @matchExists m ps@ tells whether there is a patch matching -- @m@ in @ps@-matchExists :: Matcher p -> PatchSet p wStart wX -> Bool+matchExists :: Matcher rt p -> PatchSet rt p wStart wX -> 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)+matchExists m (PatchSet (ts :<: Tagged t _ ps) NilRL) = matchExists m (PatchSet ts (ps:<:t))+matchExists m (PatchSet ts (ps:<:p)) | applyMatcher m p = True+ | otherwise = matchExists m (PatchSet ts ps) -applyInvToMatcher :: (Matchable p, ApplyMonad m (ApplyState p))- => InclusiveOrExclusive -> Matcher p -> PatchSet p Origin wX -> m ()+applyInvToMatcher :: (Matchable p, ApplyMonad (ApplyState p) m)+ => InclusiveOrExclusive -> Matcher rt p -> PatchSet rt p Origin wX -> 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)+applyInvToMatcher ioe m (PatchSet (ts :<: Tagged t _ ps) NilRL) = applyInvToMatcher ioe m+ (PatchSet ts (ps:<:t))+applyInvToMatcher ioe m (PatchSet xs (ps:<:p)) | applyMatcher m p = when (ioe == Inclusive) (applyInvp p)- | otherwise = applyInvp p >> applyInvToMatcher ioe m (PatchSet ps xs)+ | otherwise = applyInvp p >> applyInvToMatcher ioe m (PatchSet xs ps) -- | @applyNInv@ n ps applies the inverse of the last @n@ patches of @ps@.-applyNInv :: (Matchable p, ApplyMonad m (ApplyState p)) => Int -> PatchSet p Origin wX -> m ()+applyNInv :: (Matchable p, ApplyMonad (ApplyState p) m) => Int -> PatchSet rt p Origin wX -> m () applyNInv n _ | n <= 0 = return () applyNInv _ (PatchSet NilRL NilRL) = error "Index out of range."-applyNInv n (PatchSet NilRL (Tagged t _ ps :<: ts)) =- applyNInv n (PatchSet (t :<: ps) ts)-applyNInv n (PatchSet (p :<: ps) xs) =- applyInvp p >> applyNInv (n - 1) (PatchSet ps xs)+applyNInv n (PatchSet (ts :<: Tagged t _ ps) NilRL) =+ applyNInv n (PatchSet ts (ps :<: t))+applyNInv n (PatchSet xs (ps :<: p)) =+ applyInvp p >> applyNInv (n - 1) (PatchSet xs ps) -getMatcherS :: (ApplyMonad m (ApplyState p), Matchable p) =>- InclusiveOrExclusive -> Matcher p -> PatchSet p Origin wX -> m ()+getMatcherS :: (ApplyMonad (ApplyState p) m, Matchable p) =>+ InclusiveOrExclusive -> Matcher rt p -> PatchSet rt p Origin wX -> m () getMatcherS ioe m repo = if matchExists m repo then applyInvToMatcher ioe m repo else fail $ "Couldn't match pattern "++ show m -getTagS :: (ApplyMonad m (ApplyState p), MonadProgress m, Matchable p) =>- Matcher p -> PatchSet p Origin wX -> m ()+getTagS :: (ApplyMonad (ApplyState p) m, MonadProgress m, Matchable p) =>+ Matcher rt p -> PatchSet rt p Origin wX -> m () getTagS matcher repo = do let pinfo = patch2patchinfo `unseal2` findAPatch matcher repo case getPatchesBeyondTag pinfo repo of@@ -715,23 +722,22 @@ -- | @applyInvp@ tries to get the patch that's in a 'PatchInfoAnd -- patch', and to apply its inverse. If we fail to fetch the patch--- (presumably in a partial repositiory), then we share our sorrow--- with the user.-applyInvp :: (Patchy p, ApplyMonad m (ApplyState p)) => PatchInfoAnd p wX wY -> m ()+-- then we share our sorrow with the user.+applyInvp :: (Patchy p, ApplyMonad (ApplyState p) m) => PatchInfoAnd rt p wX wY -> m () applyInvp hp = apply (invert $ fromHopefully hp) where fromHopefully = conscientiously $ \e ->- text "Sorry, partial repository problem. Patch not available:"+ text "Sorry, patch not available:" $$ e $$ text "" $$ text "If you think what you're trying to do is ok then" $$ text "report this as a bug on the darcs-user list." -- | a version of 'take' for 'RL' lists that cater for contexts.-safetake :: MaybeInternal p => Int -> RL (PatchInfoAnd p) wX wY -> FlippedSeal (RL (PatchInfoAnd p)) wY+safetake :: IsRepoType rt => Int -> RL (PatchInfoAnd rt p) wX wY -> FlippedSeal (RL (PatchInfoAnd rt p)) wY safetake 0 _ = flipSeal NilRL safetake _ NilRL = error "There aren't that many patches..."-safetake i (a:<:as) | isIsEq (flIsInternal (patchcontents (hopefully a))) = a `consRLSealed` safetake i as-safetake i (a:<:as) = a `consRLSealed` safetake (i-1) as+safetake i (as:<:a) | isIsEq (namedIsInternal (hopefully a)) = safetake i as `snocRLSealed` a+safetake i (as:<:a) = safetake (i-1) as `snocRLSealed` a -applyInvRL :: (ApplyMonad m (ApplyState p), MonadProgress m, Patchy p) => RL (PatchInfoAnd p) wX wR -> m ()+applyInvRL :: (ApplyMonad (ApplyState p) m, MonadProgress m, Patchy p) => RL (PatchInfoAnd rt p) wX wR -> m () applyInvRL = applyPatches . invertRL -- this gives nicer feedback
src/Darcs/Patch/Matchable.hs view
@@ -4,10 +4,8 @@ module Darcs.Patch.Matchable ( Matchable ) where -import Darcs.Patch.MaybeInternal ( MaybeInternal ) import Darcs.Patch.Inspect ( PatchInspect ) import Darcs.Patch.Patchy ( Patchy )-import Darcs.Patch.Rebase.NameHack ( NameHack ) -class (Patchy p, PatchInspect p, MaybeInternal p, NameHack p)+class (Patchy p, PatchInspect p) => Matchable p
− src/Darcs/Patch/MaybeInternal.hs
@@ -1,33 +0,0 @@--- Copyright (C) 2009-2012 Ganesh Sittampalam------ BSD3-module Darcs.Patch.MaybeInternal- (- InternalChecker(..)- , MaybeInternal(..)- , flIsInternal- ) where--import Darcs.Patch.Witnesses.Eq ( EqCheck(..) )-import Darcs.Patch.Witnesses.Ordered ( FL )---- Note: the EqCheck result could be replaced by a Bool if clients were changed to commute the patch--- out if necessary.-newtype InternalChecker p = InternalChecker { isInternal :: forall wX wY . p wX wY -> EqCheck wX wY }---- |Provides a hook for flagging whether a patch is "internal" to the repo--- and therefore shouldn't be referred to externally, e.g. by inclusion in tags.--- Note that despite the name, every patch type has to implement it, but for--- normal (non-internal) types the default implementation is fine.--- Currently only used for rebase internal patches.-class MaybeInternal p where- -- | @maybe (const NotEq) (fmap isInternal patchInternalChecker) p@- -- returns 'IsEq' if @p@ is internal, and 'NotEq' otherwise.- -- The two-level structure is purely for efficiency: 'Nothing' and 'Just (InternalChecker (const NotEq))' are- -- semantically identical, but 'Nothing' allows clients to avoid traversing an entire list.- -- The patch type is passed as an 'FL' because that's how the internals of named patches are stored.- patchInternalChecker :: Maybe (InternalChecker (FL p))- patchInternalChecker = Nothing--flIsInternal :: MaybeInternal p => FL p wX wY -> EqCheck wX wY-flIsInternal = maybe (const NotEq) isInternal patchInternalChecker
src/Darcs/Patch/Merge.hs view
@@ -1,4 +1,3 @@- -- | -- Module : Darcs.Patch.Merge -- Maintainer : darcs-devel@darcs.net@@ -12,9 +11,6 @@ , mergeFL ) where --import Data.Maybe ( fromJust )- import Darcs.Patch.Commute ( Commute ) import Darcs.Patch.CommuteFn ( MergeFn ) import Darcs.Patch.Witnesses.Ordered@@ -31,31 +27,24 @@ merge :: (p :\/: p) wX wY -> (p :/\: p) wX wY - selfMerger :: Merge p => MergeFn p p selfMerger = merge - instance Merge p => Merge (FL p) where merge (NilFL :\/: x) = x :/\: NilFL merge (x :\/: NilFL) = NilFL :/\: x- merge ((x:>:xs) :\/: ys) = fromJust $ do- ys' :/\: x' <- return $ mergeFL (x :\/: ys)- xs' :/\: ys'' <- return $ merge (ys' :\/: xs)- return (ys'' :/\: (x' :>: xs'))-+ merge ((x:>:xs) :\/: ys) = case mergeFL (x :\/: ys) of+ ys' :/\: x' -> case merge (ys' :\/: xs) of+ xs' :/\: ys'' -> ys'' :/\: (x' :>: xs') instance Merge p => Merge (RL p) where merge (x :\/: y) = case merge (reverseRL x :\/: reverseRL y) of- (ry' :/\: rx') -> reverseFL ry' :/\: reverseFL rx'-+ (ry' :/\: rx') -> reverseFL ry' :/\: reverseFL rx' mergeFL :: Merge p => (p :\/: FL p) wX wY -> (FL p :/\: p) wX wY mergeFL (p :\/: NilFL) = NilFL :/\: p-mergeFL (p :\/: (x :>: xs)) = fromJust $ do- x' :/\: p' <- return $ merge (p :\/: x)- xs' :/\: p'' <- return $ mergeFL (p' :\/: xs)- return ((x' :>: xs') :/\: p'')-+mergeFL (p :\/: (x :>: xs)) = case merge (p :\/: x) of+ x' :/\: p' -> case mergeFL (p' :\/: xs) of+ xs' :/\: p'' -> (x' :>: xs') :/\: p''
src/Darcs/Patch/MonadProgress.hs view
@@ -27,10 +27,12 @@ , silentlyRunProgressActions ) where -import Prelude hiding ( catch )+import Prelude ()+import Darcs.Prelude+ import Darcs.Util.Printer ( Doc ) import Darcs.Util.Printer.Color () -- for instance Show Doc-import qualified Storage.Hashed.Monad as HSM+import qualified Darcs.Util.Tree.Monad as TM -- |a monadic action, annotated with a progress message that could be printed out -- while running the action, and a message that could be printed out on error.@@ -53,5 +55,5 @@ silentlyRunProgressActions :: Monad m => String -> [ProgressAction m ()] -> m () silentlyRunProgressActions _ = mapM_ paAction -instance (Functor m, Monad m) => MonadProgress (HSM.TreeMonad m) where+instance (Functor m, Monad m) => MonadProgress (TM.TreeMonad m) where runProgressActions = silentlyRunProgressActions
src/Darcs/Patch/Named.hs view
@@ -30,6 +30,9 @@ ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( pi ) import Darcs.Patch.CommuteFn ( CommuteFn, commuterIdFL, commuterFLId , MergeFn, mergerIdFL )@@ -46,7 +49,6 @@ import Darcs.Patch.Prim ( PrimOf, PrimPatchBase ) import Darcs.Patch.ReadMonads ( ParserM, option, lexChar, choice, skipWhile, anyChar )-import Darcs.Patch.Rebase.NameHack ( NameHack(..) ) import Darcs.Patch.Repair ( mapMaybeSnd, Repair(..), RepairToFL, Check(..) ) import Darcs.Patch.Show ( ShowPatchBasic(..), ShowPatch(..), showNamedPrefix ) import Darcs.Patch.Summary ( plainSummary )@@ -58,11 +60,8 @@ import Darcs.Patch.Witnesses.Sealed ( Sealed, mapSeal ) import Darcs.Patch.Witnesses.Show ( ShowDict(..), Show1(..), Show2(..) ) -import Darcs.Util.Diff ( DiffAlgorithm(MyersDiff) ) import Darcs.Util.Printer ( ($$), (<+>), (<>), prefix, text, vcat ) -import Data.Maybe ( fromMaybe )- -- | The @Named@ type adds a patch info about a patch, that is a name. data Named p wX wY where NamedP :: !PatchInfo@@ -158,13 +157,12 @@ invert (NamedP n d p) = NamedP (invertName n) (map invertName d) (invert p) -instance (Commute p, NameHack p) => Commute (Named p) where+instance Commute p => Commute (Named p) where commute (NamedP n1 d1 p1 :> NamedP n2 d2 p2) = if n2 `elem` d1 || n1 `elem` d2 then Nothing else do (p2' :> p1') <- commute (p1 :> p2)- let (informAdd, informDel) = fromMaybe (const id, const id) (nameHack MyersDiff)- return (NamedP n2 d2 (informAdd n1 p2') :> NamedP n1 d1 (informDel n2 p1'))+ return (NamedP n2 d2 p2' :> NamedP n1 d1 p1') commuterIdNamed :: CommuteFn p1 p2 -> CommuteFn p1 (Named p2) commuterIdNamed commuter (p1 :> NamedP n2 d2 p2) =@@ -176,7 +174,7 @@ do p2' :> p1' <- commuterFLId commuter (p1 :> p2) return (p2' :> NamedP n1 d1 p1') -instance (Merge p, NameHack p) => Merge (Named p) where+instance Merge p => Merge (Named p) where merge (NamedP n1 d1 p1 :\/: NamedP n2 d2 p2) = case merge (p1 :\/: p2) of (p2' :/\: p1') -> NamedP n2 d2 p2' :/\: NamedP n1 d1 p1'@@ -191,8 +189,8 @@ hunkMatches f (NamedP _ _ p) = hunkMatches f p instance (CommuteNoConflicts p, Conflict p) => Conflict (Named p) where- listConflictedFiles (NamedP _ _ p) = listConflictedFiles p resolveConflicts (NamedP _ _ p) = resolveConflicts p+ conflictedEffect (NamedP _ _ p) = conflictedEffect p instance Check p => Check (Named p) where isInconsistent (NamedP _ _ p) = isInconsistent p
+ src/Darcs/Patch/Named/Wrapped.hs view
@@ -0,0 +1,344 @@+{-# LANGUAGE StandaloneDeriving, TypeOperators #-}+module Darcs.Patch.Named.Wrapped+ ( WrappedNamed(..)+ , patch2patchinfo, activecontents+ , infopatch, namepatch, anonymous+ , getdeps, adddeps+ , mkRebase, toRebasing, fromRebasing+ , runInternalChecker, namedInternalChecker, namedIsInternal, removeInternalFL+ , fmapFL_WrappedNamed, (:~:)(..), (:~~:)(..)+ , generaliseRepoTypeWrapped+ ) where++import Prelude ()+import Darcs.Prelude++import Darcs.Patch.Apply ( Apply(..) )+import Darcs.Patch.Commute ( Commute(..) )+import Darcs.Patch.Conflict ( Conflict(..), CommuteNoConflicts(..) )+import Darcs.Patch.Effect ( Effect(..) )+import Darcs.Patch.FileHunk ( IsHunk(..) )+import Darcs.Patch.Format ( PatchListFormat(..), ListFormat, copyListFormat )+import Darcs.Patch.Info+ ( PatchInfo, showPatchInfo, showPatchInfoUI, patchinfo+ )+import Darcs.Patch.Invert ( Invert(..) )+import Darcs.Patch.Named ( Named(..), fmapFL_Named )+import qualified Darcs.Patch.Named as Base+ ( patch2patchinfo, patchcontents+ , infopatch, namepatch, anonymous+ , getdeps, adddeps+ )+import Darcs.Patch.Inspect ( PatchInspect(..) )+import Darcs.Patch.Merge ( Merge(..) )+import Darcs.Patch.Prim ( FromPrim, PrimOf )+import Darcs.Patch.Prim.Class ( PrimPatchBase )+import Darcs.Patch.Read ( ReadPatch(..) )+import qualified Darcs.Patch.Rebase.Container as Rebase+ ( Suspended(..)+ , addFixupsToSuspended, removeFixupsFromSuspended+ )+import Darcs.Patch.Repair ( mapMaybeSnd, Repair(..), RepairToFL(..), Check(..) )+import Darcs.Patch.RepoType+ ( RepoType(..), IsRepoType(..), SRepoType(..)+ , RebaseType(..), RebaseTypeOf, SRebaseType(..)+ )+import Darcs.Patch.Show ( ShowPatchBasic(..), ShowPatch(..) )++import Darcs.Patch.Witnesses.Eq ( EqCheck(..) )+import Darcs.Patch.Witnesses.Sealed ( mapSeal )+import Darcs.Patch.Witnesses.Show ( ShowDict(..), Show1(..), Show2(..) )+import Darcs.Patch.Witnesses.Ordered+ ( FL(..), mapFL_FL, mapFL, (:>)(..)+ , (:\/:)(..), (:/\:)(..)+ )++import Darcs.Util.IsoDate ( getIsoDateTime )+import Darcs.Util.Text ( formatParas )+import Darcs.Util.Printer ( ($$), (<>), vcat, prefix )++import Control.Applicative ( (<|>) )++-- |A layer inbetween the 'Named p' type and 'PatchInfoAnd p'+-- design for holding "internal" patches such as the rebase+-- container. Ideally these patches would be stored at the+-- repository level but this would require some significant+-- refactoring/cleaning up of that code.+data WrappedNamed (rt :: RepoType) p wX wY where+ NormalP :: !(Named p wX wY) -> WrappedNamed rt p wX wY+ RebaseP+ :: (PrimPatchBase p, FromPrim p, Effect p)+ => !PatchInfo -- TODO: this should always be the "internal implementation detail" rebase+ -- patch description, so could be replaced by just the Ignore-this and Date fields+ -> !(Rebase.Suspended p wX wX)+ -> WrappedNamed ('RepoType 'IsRebase) p wX wX+++deriving instance Show2 p => Show (WrappedNamed rt p wX wY)++instance Show2 p => Show1 (WrappedNamed rt p wX) where+ showDict1 = ShowDictClass++instance Show2 p => Show2 (WrappedNamed rt p) where+ showDict2 = ShowDictClass++-- TODO use Data.Type.Equality and PolyKinds from GHC 7.8/base 4.7+data (a :: * -> * -> *) :~: b where+ ReflPatch :: a :~: a++data (a :: RebaseType) :~~: b where+ ReflRebaseType :: a :~~: a++-- |lift a function over an 'FL' of patches to one over+-- a 'WrappedNamed rt'.+-- The function is only applied to "normal" patches,+-- and any rebase container patch is left alone.+fmapFL_WrappedNamed+ :: (FL p wA wB -> FL q wA wB)+ -> (RebaseTypeOf rt :~~: 'IsRebase -> p :~: q)+ -- ^If the patch might be a rebase container patch,+ -- then 'p' and 'q' must be the same type, as no+ -- transformation is applied. This function provides+ -- a witness to this requirement: if 'RebaseTypeOf rt'+ -- might be 'IsRebase', then it must be able to return+ -- a proof that 'p' and 'q' are equal. If 'RebaseTypeOf rt'+ -- must be 'NoRebase', then this function can never be called+ -- with a valid value.+ -> WrappedNamed rt p wA wB+ -> WrappedNamed rt q wA wB+fmapFL_WrappedNamed f _ (NormalP n) = NormalP (fmapFL_Named f n)+fmapFL_WrappedNamed _ whenRebase (RebaseP n s) =+ case whenRebase ReflRebaseType of+ ReflPatch -> RebaseP n s++patch2patchinfo :: WrappedNamed rt p wX wY -> PatchInfo+patch2patchinfo (NormalP p) = Base.patch2patchinfo p+patch2patchinfo (RebaseP name _) = name++namepatch :: String -> String -> String -> [String] -> FL p wX wY -> IO (WrappedNamed rt p wX wY)+namepatch date name author desc p = fmap NormalP (Base.namepatch date name author desc p)++anonymous :: FL p wX wY -> IO (WrappedNamed rt p wX wY)+anonymous p = fmap NormalP (Base.anonymous p)++infopatch :: PatchInfo -> FL p wX wY -> WrappedNamed rt p wX wY+infopatch i ps = NormalP (Base.infopatch i ps)++-- |Return a list of the underlying patches that are actually+-- 'active' in the repository, i.e. not suspended as part of a rebase+activecontents :: WrappedNamed rt p wX wY -> FL p wX wY+activecontents (NormalP p) = Base.patchcontents p+activecontents (RebaseP {}) = NilFL++adddeps :: WrappedNamed rt p wX wY -> [PatchInfo] -> WrappedNamed rt p wX wY+adddeps (NormalP n) pis = NormalP (Base.adddeps n pis)+adddeps (RebaseP {}) _ = error "Internal error: can't add dependencies to a rebase internal patch"++getdeps :: WrappedNamed rt p wX wY -> [PatchInfo]+getdeps (NormalP n) = Base.getdeps n+getdeps (RebaseP {}) = []++mkRebase :: (PrimPatchBase p, FromPrim p, Effect p)+ => Rebase.Suspended p wX wX+ -> IO (WrappedNamed ('RepoType 'IsRebase) p wX wX)+mkRebase s = do+ let name = "DO NOT TOUCH: Rebase patch"+ let desc = formatParas 72+ ["This patch is an internal implementation detail of rebase, used to store suspended patches, " +++ "and should not be visible in the user interface. Please report a bug if a darcs " +++ "command is showing you this patch."]+ date <- getIsoDateTime+ let author = "Invalid <invalid@invalid>"+ info <- patchinfo date name author desc+ return $ RebaseP info s++toRebasing :: Named p wX wY -> WrappedNamed ('RepoType 'IsRebase) p wX wY+toRebasing n = NormalP n++fromRebasing :: WrappedNamed ('RepoType 'IsRebase) p wX wY -> Named p wX wY+fromRebasing (NormalP n) = n+fromRebasing (RebaseP {}) = error "internal error: found rebasing internal patch"++generaliseRepoTypeWrapped+ :: WrappedNamed ('RepoType 'NoRebase) p wA wB+ -> WrappedNamed rt p wA wB+generaliseRepoTypeWrapped (NormalP p) = NormalP p++-- Note: the EqCheck result could be replaced by a Bool if clients were changed to commute the patch+-- out if necessary.+newtype InternalChecker p =+ InternalChecker { runInternalChecker :: forall wX wY . p wX wY -> EqCheck wX wY }++-- |Is the given 'WrappedNamed' patch an internal implementation detail+-- that shouldn't be visible in the UI or included in tags/matchers etc?+-- Two-level checker for efficiency: if the value of this is 'Nothing' for a given+-- patch type then there's no need to inspect patches of this type at all,+-- as none of them can be internal.+namedInternalChecker :: forall rt p . IsRepoType rt => Maybe (InternalChecker (WrappedNamed rt p))+namedInternalChecker =+ case singletonRepoType :: SRepoType rt of+ SRepoType SNoRebase -> Nothing+ SRepoType SIsRebase ->+ let+ isInternal :: WrappedNamed rt p wX wY -> EqCheck wX wY+ isInternal (NormalP {}) = NotEq+ isInternal (RebaseP {}) = IsEq+ in Just (InternalChecker isInternal)++-- |Is the given 'WrappedNamed' patch an internal implementation detail+-- that shouldn't be visible in the UI or included in tags/matchers etc?+namedIsInternal :: IsRepoType rt => WrappedNamed rt p wX wY -> EqCheck wX wY+namedIsInternal = maybe (const NotEq) runInternalChecker namedInternalChecker++removeInternalFL :: IsRepoType rt => FL (WrappedNamed rt p) wX wY -> FL (Named p) wX wY+removeInternalFL NilFL = NilFL+removeInternalFL (NormalP n :>: ps) = n :>: removeInternalFL ps+removeInternalFL (RebaseP {} :>: ps) = removeInternalFL ps++instance PrimPatchBase p => PrimPatchBase (WrappedNamed rt p) where+ type PrimOf (WrappedNamed rt p) = PrimOf p++instance Invert p => Invert (WrappedNamed rt p) where+ invert (NormalP n) = NormalP (invert n)+ invert (RebaseP i s) = RebaseP i s -- TODO is this sensible?++instance PatchListFormat (WrappedNamed rt p)++instance IsHunk (WrappedNamed rt p) where+ isHunk _ = Nothing++instance (ShowPatchBasic p, PatchListFormat p)+ => ShowPatchBasic (WrappedNamed rt p) where++ showPatch (NormalP n) = showPatch n+ showPatch (RebaseP i s) = showPatchInfo i <> showPatch s++instance ( ShowPatch p, PatchListFormat p, Apply p+ , PrimPatchBase p, IsHunk p, Conflict p, CommuteNoConflicts p+ )+ => ShowPatch (WrappedNamed rt p) where++ showContextPatch (NormalP n) = showContextPatch n+ showContextPatch (RebaseP i s) = fmap (showPatchInfo i <>) $ showContextPatch s++ description (NormalP n) = description n+ description (RebaseP i _) = showPatchInfoUI i++ summary (NormalP n) = summary n+ summary (RebaseP i _) = showPatchInfoUI i++ summaryFL = vcat . mapFL summary++ showNicely (NormalP n) = showNicely n+ showNicely (RebaseP i s) = showPatchInfoUI i $$+ prefix " " (showNicely s)++instance PatchInspect p => PatchInspect (WrappedNamed rt p) where+ listTouchedFiles (NormalP n) = listTouchedFiles n+ listTouchedFiles (RebaseP _ s) = listTouchedFiles s++ hunkMatches f (NormalP n) = hunkMatches f n+ hunkMatches f (RebaseP _ s) = hunkMatches f s++instance RepairToFL p => Repair (WrappedNamed rt p) where+ applyAndTryToFix (NormalP n) = fmap (mapMaybeSnd NormalP) $ applyAndTryToFix n+ applyAndTryToFix (RebaseP i s) = fmap (mapMaybeSnd (RebaseP i)) $ applyAndTryToFix s++-- This is a local hack to maintain backwards compatibility with+-- the on-disk format for rebases. Previously the rebase container+-- was internally represented via a 'Rebasing' type that sat *inside*+-- a 'Named', and so the rebase container patch had the structure+-- 'NamedP i [] (Suspendended s :>: NilFL)'. This structure was reflected+-- in the way it was saved on disk.+-- The easiest to read this structure is to use an intermediate type+-- that reflects the old structure.+-- TODO: switch to a more natural on-disk structure that directly+-- saves/reads 'RebaseP'.+data ReadRebasing p wX wY where+ ReadNormal :: p wX wY -> ReadRebasing p wX wY+ ReadSuspended :: Rebase.Suspended p wX wX -> ReadRebasing p wX wX++instance ( ReadPatch p, PrimPatchBase p, FromPrim p, Effect p, PatchListFormat p+ , IsRepoType rt+ ) => ReadPatch (WrappedNamed rt p) where+ readPatch' =+ case singletonRepoType :: SRepoType rt of+ SRepoType SIsRebase ->+ let wrapNamed :: Named (ReadRebasing p) wX wY -> WrappedNamed rt p wX wY+ wrapNamed (NamedP i [] (ReadSuspended s :>: NilFL))+ = RebaseP i s+ wrapNamed (NamedP i deps ps) = NormalP (NamedP i deps (mapFL_FL unRead ps))++ unRead (ReadNormal p) = p+ unRead (ReadSuspended _) = error "unexpected suspended patch"++ in fmap (mapSeal wrapNamed) readPatch'++ _ -> fmap (mapSeal NormalP) readPatch'++instance PatchListFormat p => PatchListFormat (ReadRebasing p) where+ patchListFormat = copyListFormat (patchListFormat :: ListFormat p)++instance (ReadPatch p, PatchListFormat p, PrimPatchBase p) => ReadPatch (ReadRebasing p) where+ readPatch' =+ mapSeal toSuspended <$> readPatch'+ <|> mapSeal ReadNormal <$> readPatch'+ where -- needed to get a suitably polymorphic type+ toSuspended :: Rebase.Suspended p wX wY -> ReadRebasing p wX wY+ toSuspended (Rebase.Items ps) = ReadSuspended (Rebase.Items ps)++instance (CommuteNoConflicts p, Conflict p) => Conflict (WrappedNamed rt p) where+ resolveConflicts (NormalP n) = resolveConflicts n+ resolveConflicts (RebaseP _ s) = resolveConflicts s++ conflictedEffect (NormalP n) = conflictedEffect n+ conflictedEffect (RebaseP _ s) = conflictedEffect s++instance Check p => Check (WrappedNamed rt p) where+ isInconsistent (NormalP n) = isInconsistent n+ isInconsistent (RebaseP _ s) = isInconsistent s++instance Apply p => Apply (WrappedNamed rt p) where+ type ApplyState (WrappedNamed rt p) = ApplyState p+ apply (NormalP n) = apply n+ apply (RebaseP _ s) = apply s++instance Effect p => Effect (WrappedNamed rt p) where+ effect (NormalP n) = effect n+ effect (RebaseP _ s) = effect s++ effectRL (NormalP n) = effectRL n+ effectRL (RebaseP _ s) = effectRL s++instance Commute p => Commute (WrappedNamed rt p) where+ commute (NormalP n1 :> NormalP n2) = do+ n2' :> n1' <- commute (n1 :> n2)+ return (NormalP n2' :> NormalP n1')++ commute (RebaseP i1 s1 :> RebaseP i2 s2) =+ -- Two rebases in sequence must have the same starting context,+ -- so they should trivially commute.+ -- This case shouldn't actually happen since each repo only has+ -- a single Suspended patch.+ return (RebaseP i2 s2 :> RebaseP i1 s1)++ commute (NormalP n1 :> RebaseP i2 s2) =+ return (RebaseP i2 (Rebase.addFixupsToSuspended n1 s2) :> NormalP n1)++ commute (RebaseP i1 s1 :> NormalP n2) =+ return (NormalP n2 :> RebaseP i1 (Rebase.removeFixupsFromSuspended n2 s1))++instance Merge p => Merge (WrappedNamed rt p) where+ merge (NormalP n1 :\/: NormalP n2) =+ case merge (n1 :\/: n2) of+ n2' :/\: n1' -> NormalP n2' :/\: NormalP n1'++ -- shouldn't happen as each repo only has a single Suspended patch+ merge (RebaseP i1 items1 :\/: RebaseP i2 items2) =+ RebaseP i2 items2 :/\: RebaseP i1 items1++ merge (NormalP n1 :\/: RebaseP i2 s2) =+ RebaseP i2 (Rebase.removeFixupsFromSuspended n1 s2) :/\: NormalP n1++ merge (RebaseP i1 s1 :\/: NormalP n2) =+ NormalP n2 :/\: RebaseP i1 (Rebase.removeFixupsFromSuspended n2 s1)
src/Darcs/Patch/OldDate.hs view
@@ -22,6 +22,9 @@ module Darcs.Patch.OldDate ( readUTCDate, showIsoDateTime ) where +import Prelude ( (^) )+import Darcs.Prelude+ import Text.ParserCombinators.Parsec import System.Time import Data.Char ( toUpper, isDigit )
src/Darcs/Patch/PatchInfoAnd.hs view
@@ -21,11 +21,14 @@ module Darcs.Patch.PatchInfoAnd ( Hopefully(..), SimpleHopefully(..), PatchInfoAnd(..), WPatchInfo, unWPatchInfo, compareWPatchInfo, piap, n2pia, patchInfoAndPatch,- fmapPIAP, fmapFLPIAP,+ fmapFLPIAP, generaliseRepoTypePIAP, conscientiously, hopefully, info, winfo, hopefullyM, createHashed, extractHash, actually, unavailable, patchDesc ) where +import Prelude ()+import Darcs.Prelude+ import System.IO.Unsafe ( unsafeInterleaveIO ) import Darcs.Util.SignalHandler ( catchNonSignal )@@ -33,19 +36,21 @@ ( Doc, renderString, errorDoc, text, ($$), vcat , RenderMode(..) ) import Darcs.Patch.Info ( PatchInfo, showPatchInfoUI, justName )-import Darcs.Patch ( Named, patch2patchinfo ) import Darcs.Patch.Conflict ( Conflict, CommuteNoConflicts ) import Darcs.Patch.Debug ( PatchDebug(..) ) import Darcs.Patch.Effect ( Effect(..) ) import Darcs.Patch.FileHunk ( IsHunk(..) ) import Darcs.Patch.Format ( PatchListFormat ) import Darcs.Patch.Merge ( Merge(..) )-import Darcs.Patch.Named ( fmapNamed, fmapFL_Named )-import Darcs.Patch.Prim ( PrimPatchBase(..) )+import Darcs.Patch.Named.Wrapped+ ( WrappedNamed, patch2patchinfo, fmapFL_WrappedNamed, (:~:), (:~~:)+ , generaliseRepoTypeWrapped+ )+import Darcs.Patch.Prim ( PrimPatchBase(..), FromPrim ) import Darcs.Patch.Patchy ( Patchy, ReadPatch(..), Apply(..), Invert(..), ShowPatch(..), Commute(..), PatchInspect(..) )-import Darcs.Patch.Rebase.NameHack ( NameHack ) import Darcs.Patch.Repair ( Repair(..), RepairToFL )+import Darcs.Patch.RepoType ( RepoType(..), IsRepoType, RebaseTypeOf, RebaseType(..) ) import Darcs.Patch.Show ( ShowPatchBasic(..) ) import Darcs.Patch.Witnesses.Eq ( MyEq(..), EqCheck(..) ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )@@ -53,9 +58,7 @@ import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed), seal, mapSeal ) import Darcs.Patch.Witnesses.Show ( Show1(..), Show2(..), ShowDict(ShowDictClass) ) import Darcs.Util.Exception ( prettyException )-import Storage.Hashed.Tree( Tree )--import Control.Applicative ( (<$>) )+import Darcs.Util.Tree( Tree ) -- | @'Hopefully' p C@ @(x y)@ is @'Either' String (p C@ @(x y))@ in a -- form adapted to darcs patches. The @C@ @(x y)@ represents the type@@ -76,17 +79,17 @@ -- | @'PatchInfoAnd' p wA wB@ represents a hope we have to get a -- patch through its info. We're not sure we have the patch, but we -- know its info.-data PatchInfoAnd p wA wB = PIAP !PatchInfo (Hopefully (Named p) wA wB)+data PatchInfoAnd rt p wA wB = PIAP !PatchInfo (Hopefully (WrappedNamed rt p) wA wB) deriving Show -instance Show2 p => Show1 (PatchInfoAnd p wX) where+instance Show2 p => Show1 (PatchInfoAnd rt p wX) where showDict1 = ShowDictClass -instance Show2 p => Show2 (PatchInfoAnd p) where+instance Show2 p => Show2 (PatchInfoAnd rt p) where showDict2 = ShowDictClass -instance PrimPatchBase p => PrimPatchBase (PatchInfoAnd p) where- type PrimOf (PatchInfoAnd p) = PrimOf p+instance PrimPatchBase p => PrimPatchBase (PatchInfoAnd rt p) where+ type PrimOf (PatchInfoAnd rt p) = PrimOf p -- | @'WPatchInfo' wA wB@ represents the info of a patch, marked with -- the patch's witnesses.@@ -109,44 +112,51 @@ where ff (Actually a) = Actually (f a) ff (Unavailable e) = Unavailable e -info :: PatchInfoAnd p wA wB -> PatchInfo+info :: PatchInfoAnd rt p wA wB -> PatchInfo info (PIAP i _) = i -patchDesc :: forall p wX wY . PatchInfoAnd p wX wY -> String+patchDesc :: forall rt p wX wY . PatchInfoAnd rt p wX wY -> String patchDesc p = justName $ info p -winfo :: PatchInfoAnd p wA wB -> WPatchInfo wA wB+winfo :: PatchInfoAnd rt p wA wB -> WPatchInfo wA wB winfo (PIAP i _) = WPatchInfo i -- | @'piap' i p@ creates a PatchInfoAnd containing p with info i.-piap :: PatchInfo -> Named p wA wB -> PatchInfoAnd p wA wB+piap :: PatchInfo -> WrappedNamed rt p wA wB -> PatchInfoAnd rt p wA wB piap i p = PIAP i (Hopefully $ Actually p) -- | @n2pia@ creates a PatchInfoAnd representing a @Named@ patch.-n2pia :: Named p wX wY -> PatchInfoAnd p wX wY+n2pia :: WrappedNamed rt p wX wY -> PatchInfoAnd rt p wX wY n2pia x = patch2patchinfo x `piap` x -patchInfoAndPatch :: PatchInfo -> Hopefully (Named p) wA wB -> PatchInfoAnd p wA wB+patchInfoAndPatch :: PatchInfo -> Hopefully (WrappedNamed rt p) wA wB -> PatchInfoAnd rt p wA wB patchInfoAndPatch = PIAP -fmapPIAP :: (forall wA wB . p wA wB -> q wA wB) -> PatchInfoAnd p wX wY -> PatchInfoAnd q wX wY-fmapPIAP f (PIAP i hp) = PIAP i (fmapH (fmapNamed f) hp)+fmapFLPIAP+ :: (FL p wX wY -> FL q wX wY)+ -> (RebaseTypeOf rt :~~: 'IsRebase -> p :~: q)+ -> PatchInfoAnd rt p wX wY+ -> PatchInfoAnd rt q wX wY+fmapFLPIAP f whenRebase (PIAP i hp)+ = PIAP i (fmapH (fmapFL_WrappedNamed f whenRebase) hp) -fmapFLPIAP :: (FL p wX wY -> FL q wX wY) -> PatchInfoAnd p wX wY -> PatchInfoAnd q wX wY-fmapFLPIAP f (PIAP i hp) = PIAP i (fmapH (fmapFL_Named f) hp)+generaliseRepoTypePIAP+ :: PatchInfoAnd ('RepoType 'NoRebase) p wA wB+ -> PatchInfoAnd rt p wA wB+generaliseRepoTypePIAP (PIAP i hp) = PIAP i (fmapH generaliseRepoTypeWrapped hp) -- | @'hopefully' hp@ tries to get a patch from a 'PatchInfoAnd' -- value. If it fails, it outputs an error \"failed to read patch: -- \<description of the patch>\". We get the description of the patch -- from the info part of 'hp'-hopefully :: PatchInfoAnd p wA wB -> Named p wA wB+hopefully :: PatchInfoAnd rt p wA wB -> WrappedNamed rt p wA wB hopefully = conscientiously $ \e -> text "failed to read patch:" $$ e -- | @'conscientiously' er hp@ tries to extract a patch from a 'PatchInfoAnd'. -- If it fails, it applies the error handling function @er@ to a description -- of the patch info component of @hp@. conscientiously :: (Doc -> Doc)- -> PatchInfoAnd p wA wB -> Named p wA wB+ -> PatchInfoAnd rt p wA wB -> WrappedNamed rt p wA wB conscientiously er (PIAP pinf hp) = case hopefully2either hp of Right p -> p@@ -154,7 +164,7 @@ -- | @hopefullyM@ is a version of @hopefully@ which calls @fail@ in a -- monad instead of erroring.-hopefullyM :: Monad m => PatchInfoAnd p wA wB -> m (Named p wA wB)+hopefullyM :: Monad m => PatchInfoAnd rt p wA wB -> m (WrappedNamed rt p wA wB) hopefullyM (PIAP pinf hp) = case hopefully2either hp of Right p -> return p Left e -> fail $ renderString Encode@@ -177,7 +187,7 @@ return (Sealed (Actually x)) handler e = return $ seal $ Unavailable $ prettyException e -extractHash :: PatchInfoAnd p wA wB -> Either (Named p wA wB) String+extractHash :: PatchInfoAnd rt p wA wB -> Either (WrappedNamed rt p wA wB) String extractHash (PIAP _ (Hashed s _)) = Right s extractHash hp = Left $ conscientiously (\e -> text "unable to read patch:" $$ e) hp @@ -187,21 +197,21 @@ -- Equality on PatchInfoAnd is solely determined by the PatchInfo -- It is a global invariant of darcs that once a patch is recorded, -- it should always have the same representation in the same context.-instance MyEq (PatchInfoAnd p) where+instance MyEq (PatchInfoAnd rt p) where unsafeCompare (PIAP i _) (PIAP i2 _) = i == i2 -instance Invert p => Invert (PatchInfoAnd p) where+instance Invert p => Invert (PatchInfoAnd rt p) where invert (PIAP i p) = PIAP i (invert `fmapH` p) -instance PatchListFormat (PatchInfoAnd p)+instance PatchListFormat (PatchInfoAnd rt p) -instance (PatchListFormat p, ShowPatchBasic p) => ShowPatchBasic (PatchInfoAnd p) where+instance (PatchListFormat p, ShowPatchBasic p) => ShowPatchBasic (PatchInfoAnd rt p) where showPatch (PIAP n p) = case hopefully2either p of Right x -> showPatch x Left _ -> showPatchInfoUI n instance (Apply p, Conflict p, CommuteNoConflicts p, IsHunk p, PatchListFormat p, PrimPatchBase p,- ShowPatch p, ApplyState p ~ Tree) => ShowPatch (PatchInfoAnd p) where+ ShowPatch p, ApplyState p ~ Tree) => ShowPatch (PatchInfoAnd rt p) where showContextPatch (PIAP n p) = case hopefully2either p of Right x -> showContextPatch x Left _ -> return $ showPatchInfoUI n@@ -214,39 +224,42 @@ Right x -> showNicely x Left _ -> showPatchInfoUI n -instance (Commute p, NameHack p) => Commute (PatchInfoAnd p) where+instance Commute p => Commute (PatchInfoAnd rt p) where commute (x :> y) = do y' :> x' <- commute (hopefully x :> hopefully y) return $ (info y `piap` y') :> (info x `piap` x') -instance (Merge p, NameHack p) => Merge (PatchInfoAnd p) where+instance Merge p => Merge (PatchInfoAnd rt p) where merge (x :\/: y) = case merge (hopefully x :\/: hopefully y) of y' :/\: x' -> (info y `piap` y') :/\: (info x `piap` x') -instance PatchInspect p => PatchInspect (PatchInfoAnd p) where+instance PatchInspect p => PatchInspect (PatchInfoAnd rt p) where listTouchedFiles = listTouchedFiles . hopefully- hunkMatches _ _ = error "hunkmatches not implemented for PatchInfoAnd"+ hunkMatches f = hunkMatches f . hopefully -instance Apply p => Apply (PatchInfoAnd p) where- type ApplyState (PatchInfoAnd p) = ApplyState p+instance Apply p => Apply (PatchInfoAnd rt p) where+ type ApplyState (PatchInfoAnd rt p) = ApplyState p apply p = apply $ hopefully p -instance RepairToFL p => Repair (PatchInfoAnd p) where+instance RepairToFL p => Repair (PatchInfoAnd rt p) where applyAndTryToFix p = do mp' <- applyAndTryToFix $ hopefully p case mp' of Nothing -> return Nothing Just (e,p') -> return $ Just (e, n2pia p') -instance (ReadPatch p, PatchListFormat p) => ReadPatch (PatchInfoAnd p) where+instance ( ReadPatch p, PatchListFormat p, PrimPatchBase p, Effect p, FromPrim p+ , IsRepoType rt+ ) => ReadPatch (PatchInfoAnd rt p) where+ readPatch' = mapSeal n2pia <$> readPatch' -instance Effect p => Effect (PatchInfoAnd p) where+instance Effect p => Effect (PatchInfoAnd rt p) where effect = effect . hopefully effectRL = effectRL . hopefully -instance IsHunk (PatchInfoAnd p) where+instance IsHunk (PatchInfoAnd rt p) where isHunk _ = Nothing -instance PatchDebug p => PatchDebug (PatchInfoAnd p)+instance PatchDebug p => PatchDebug (PatchInfoAnd rt p) -instance (Patchy p, NameHack p, ApplyState p ~ Tree) => Patchy (PatchInfoAnd p)+instance (Patchy p, ApplyState p ~ Tree) => Patchy (PatchInfoAnd rt p)
src/Darcs/Patch/Permutations.hs view
@@ -31,6 +31,9 @@ inverseCommuter ) where +import Prelude ()+import Darcs.Prelude+ import Data.Maybe ( mapMaybe ) import Darcs.Patch.Commute ( Commute, commute, commuteFLorComplain, commuteRL ) import Darcs.Patch.CommuteFn ( CommuteFn )@@ -68,10 +71,10 @@ 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 -> partitionFL' keepleft (middle :<: p') 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+ (tomiddle :> p' :> right') -> partitionFL' keepleft (middle +<+ tomiddle :<: p') right' ps+ | otherwise = partitionFL' keepleft middle (right :<: p) ps -- |split an 'RL' into "left" and "right" lists according to a predicate, using commutation as necessary.@@ -93,11 +96,11 @@ partitionRL' _ NilRL qs = reverseFL qs :> NilRL -partitionRL' keepright (p :<: ps) qs+partitionRL' keepright (ps :<: p) qs | keepright p, Right (qs' :> p') <- commuteFLorComplain (p :> qs) = case partitionRL' keepright ps qs' of- a :> b -> a :> p' :<: b+ a :> b -> a :> b :<: p' | otherwise = partitionRL' keepright ps (p :>: qs) commuteWhatWeCanFL :: Commute p => (p :> FL p) wX wY -> (FL p :> p :> FL p) wX wY@@ -121,13 +124,13 @@ genCommuteWhatWeCanRL :: Commute p => (forall wA wB . ((p :> q) wA wB -> Maybe ((q :> p) wA wB))) -> (RL p :> q) wX wY -> (RL p :> q :> RL p) wX wY-genCommuteWhatWeCanRL com (x :<: xs :> p) =+genCommuteWhatWeCanRL com (xs :<: x :> p) = case com (x :> p) of Nothing -> case commuteWhatWeCanRL (xs :> x) of xs1 :> x' :> xs2 -> case genCommuteWhatWeCanRL com (xs2 :> p) of- xs1' :> p' :> xs2' -> xs1' +<+ x' :<: xs1 :> p' :> xs2'+ xs1' :> p' :> xs2' -> xs1 :<: x' +<+ xs1' :> p' :> xs2' Just (p' :> x') -> case genCommuteWhatWeCanRL com (xs :> p') of- a :> p'' :> c -> a :> p'' :> x' :<: c+ xs1 :> p'' :> xs2 -> xs1 :> p'' :> xs2 :<: x' genCommuteWhatWeCanRL _ (NilRL :> y) = NilRL :> y :> NilRL @@ -154,7 +157,7 @@ removeRL :: (MyEq p, Commute p) => p wY wZ -> RL p wX wZ -> Maybe (RL p wX wY) removeRL x xs = r x $ headPermutationsRL xs where r :: (MyEq p, Commute p) => p wY wZ -> [RL p wX wZ] -> Maybe (RL p wX wY)- r z ((z':<:zs):zss) | IsEq <- z =/\= z' = Just zs+ r z ((zs:<:z'):zss) | IsEq <- z =/\= z' = Just zs | otherwise = r z zss r _ _ = Nothing @@ -178,7 +181,7 @@ | otherwise = rsRL a b where rsRL :: (MyEq p, Commute p) => RL p wAb wAbc -> RL p wA wAbc -> Maybe (RL p wA wAb) rsRL NilRL ys = Just ys- rsRL (x:<:xs) yys = removeRL x yys >>= removeSubsequenceRL xs+ rsRL (xs:<:x) yys = removeRL x yys >>= removeSubsequenceRL xs -- | This is a minor variant of 'headPermutationsFL' with each permutation -- is simply returned as a 'FL'@@ -213,10 +216,10 @@ -- patch sequence instead of to the beginning). headPermutationsRL :: Commute p => RL p wX wY -> [RL p wX wY] headPermutationsRL NilRL = []-headPermutationsRL (p:<:ps) =- (p:<:ps) : mapMaybe (swapfirstRL.(p:<:)) (headPermutationsRL ps)- where swapfirstRL (p1:<:p2:<:xs) = do p1':>p2' <- commute (p2:>p1)- Just $ p2':<:p1':<:xs+headPermutationsRL (ps:<:p) =+ (ps:<:p) : mapMaybe (swapfirstRL.(:<:p)) (headPermutationsRL ps)+ where swapfirstRL (xs:<:p2:<:p1) = do p1':>p2' <- commute (p2:>p1)+ Just $ xs:<:p1':<:p2' swapfirstRL _ = Nothing instance (MyEq p, Commute p) => MyEq (FL p) where@@ -233,7 +236,7 @@ a =/\= b | lengthRL a /= lengthRL b = NotEq | otherwise = cmpSameLength a b where cmpSameLength :: RL p wX wY -> RL p wW wY -> EqCheck wX wW- cmpSameLength (x:<:xs) xys | Just ys <- removeRL x xys = cmpSameLength xs ys+ cmpSameLength (xs:<:x) xys | Just ys <- removeRL x xys = cmpSameLength xs ys cmpSameLength NilRL NilRL = IsEq cmpSameLength _ _ = NotEq xs =\/= ys = reverseRL xs =\/= reverseRL ys
src/Darcs/Patch/Prim/Class.hs view
@@ -8,6 +8,9 @@ ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.ApplyMonad ( ApplyMonad ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.FileHunk ( FileHunk, IsHunk )@@ -23,6 +26,7 @@ import Darcs.Patch.Witnesses.Eq ( MyEq(..) ) import Darcs.Patch.Witnesses.Ordered ( FL(..), RL, (:>), mapFL, mapFL_FL, reverseFL )+import Darcs.Patch.Witnesses.Show ( Show2 ) import Darcs.Patch.Witnesses.Sealed ( Sealed ) import Darcs.Util.Printer ( Doc, vcat )@@ -34,6 +38,7 @@ class (Patchy prim, MyEq prim ,PatchListFormat prim, IsHunk prim, RepairToFL prim ,PatchInspect prim, ReadPatch prim, ShowPatch prim+ ,Show2 prim ,PrimConstruct prim, PrimCanonize prim ,PrimClassify prim, PrimDetails prim ,PrimShow prim, PrimRead prim, PrimApply prim@@ -146,4 +151,4 @@ readPrim :: ParserM m => FileNameFormat -> m (Sealed (prim wX)) class PrimApply prim where- applyPrimFL :: ApplyMonad m (ApplyState prim) => FL prim wX wY -> m ()+ applyPrimFL :: ApplyMonad (ApplyState prim) m => FL prim wX wY -> m ()
+ src/Darcs/Patch/Prim/FileUUID.hs view
@@ -0,0 +1,25 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+module Darcs.Patch.Prim.FileUUID ( Prim ) where++import Prelude ()+import Darcs.Prelude++import Darcs.Patch.Prim.FileUUID.Apply ()+import Darcs.Patch.Prim.FileUUID.Coalesce ()+import Darcs.Patch.Prim.FileUUID.Commute ()+import Darcs.Patch.Prim.FileUUID.Core ( Prim )+import Darcs.Patch.Prim.FileUUID.Details ()+import Darcs.Patch.Prim.FileUUID.Read ()+import Darcs.Patch.Prim.FileUUID.Show ()++import Darcs.Patch.Prim.Class ( PrimPatch, PrimPatchBase(..), FromPrim(..) )+import Darcs.Patch.Patchy ( Patchy )++instance PrimPatch Prim+instance Patchy Prim++instance PrimPatchBase Prim where+ type PrimOf Prim = Prim++instance FromPrim Prim where+ fromPrim = id
+ src/Darcs/Patch/Prim/FileUUID/Apply.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE CPP, MultiParamTypeClasses, OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-missing-methods #-}+module Darcs.Patch.Prim.FileUUID.Apply ( ObjectMap(..) ) where++import Prelude ()+import Darcs.Prelude++import Darcs.Patch.Apply ( Apply(..) )+import Darcs.Patch.ApplyMonad+ ( ApplyMonad(..), ApplyMonadTrans(..), ToTree(..), ApplyMonadState(..)+ )+import Darcs.Patch.Repair ( RepairToFL(..) )++import Darcs.Patch.Prim.Class ( PrimApply(..) )+import Darcs.Patch.Prim.FileUUID.Core ( Prim(..), hunkEdit )+import Darcs.Patch.Prim.FileUUID.ObjectMap++import Control.Monad.State( StateT, runStateT, gets, lift, put )+import qualified Data.Map as M++-- import Darcs.Patch.ApplyMonad ( ApplyMonad(..) )++import Darcs.Patch.Witnesses.Ordered ( FL(..) )+import Darcs.Util.Hash( Hash(..) )++import qualified Data.ByteString as B++#include "impossible.h"++instance Apply Prim where+ type ApplyState Prim = ObjectMap+ apply (Manifest i (dirid, name)) = editDirectory dirid (M.insert name i)+ apply (Demanifest _ (dirid, name)) = editDirectory dirid (M.delete name)+ apply (TextHunk i hunk) = editFile i (hunkEdit hunk)+ apply (BinaryHunk i hunk) = editFile i (hunkEdit hunk)+ apply Identity = return ()+ apply (Move{}) = bug "apply for move not implemented"++instance RepairToFL Prim where+ applyAndTryToFixFL p = apply p >> return Nothing++instance PrimApply Prim where+ applyPrimFL NilFL = return ()+ applyPrimFL (p:>:ps) = apply p >> applyPrimFL ps++instance ToTree ObjectMap -- TODO++editObject :: (Monad m) => UUID -> (Maybe (Object m) -> Object m) -> (StateT (ObjectMap m) m) ()+editObject i edit = do load <- gets getObject+ store <- gets putObject+ obj <- lift $ load i+ new <- lift $ store i $ edit obj+ put new+ return ()+++class ApplyMonadObjectMap m where+ -- a semantic, ObjectMap-based interface for patch application+ editFile :: UUID -> (B.ByteString -> B.ByteString) -> m ()+ editDirectory :: UUID -> (DirContent -> DirContent) -> m ()++instance ApplyMonadState ObjectMap where+ type ApplyMonadStateOperations ObjectMap = ApplyMonadObjectMap++instance (Functor m, Monad m) => ApplyMonad ObjectMap (StateT (ObjectMap m) m) where+ type ApplyMonadBase (StateT (ObjectMap m) m) = m++instance (Functor m, Monad m) => ApplyMonadObjectMap (StateT (ObjectMap m) m) where+ editFile i edit = editObject i edit'+ where edit' (Just (Blob x _)) = Blob (edit `fmap` x) NoHash+ edit' (Just (Directory x)) = Directory x -- error?+ edit' Nothing = Blob (return $ edit "") NoHash+ editDirectory i edit = editObject i edit'+ where edit' (Just (Directory x)) = Directory $ edit x+ edit' (Just (Blob x y)) = Blob x y -- error?+ edit' Nothing = Directory $ edit M.empty++instance (Functor m, Monad m) => ApplyMonadTrans ObjectMap m where+ type ApplyMonadOver ObjectMap m = StateT (ObjectMap m) m+ runApplyMonad = runStateT+
+ src/Darcs/Patch/Prim/FileUUID/Coalesce.hs view
@@ -0,0 +1,18 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+module Darcs.Patch.Prim.FileUUID.Coalesce () where++import Prelude ()+import Darcs.Prelude++import Darcs.Patch.Prim.Class ( PrimCanonize(..) )+import Darcs.Patch.Witnesses.Ordered( FL(..) )+import Darcs.Patch.Prim.FileUUID.Core( Prim )++-- TODO+instance PrimCanonize Prim where+ tryToShrink = error "tryToShrink"+ tryShrinkingInverse _ = error "tryShrinkingInverse"+ sortCoalesceFL = id+ canonize _ = (:>: NilFL)+ canonizeFL _ = id+ coalesce = const Nothing
+ src/Darcs/Patch/Prim/FileUUID/Commute.hs view
@@ -0,0 +1,73 @@+{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-overlapping-patterns #-}+{-# LANGUAGE CPP #-}+module Darcs.Patch.Prim.FileUUID.Commute+ ( CommuteMonad(..) )+ where++import Prelude ()+import Darcs.Prelude++import Data.List ( intersect )++import qualified Data.ByteString as BS (length)++import Darcs.Patch.Witnesses.Ordered ( (:>)(..) )+import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )+import Darcs.Patch.Prim.FileUUID.Core ( Prim(..), Hunk(..), touches )+import Darcs.Patch.Commute ( Commute(..) )+import Darcs.Patch.Permutations () -- for Invert instance of FL++#include "impossible.h"++class Monad m => CommuteMonad m where+ commuteFail :: m a+ -- TODO we eventually have to get rid of runCommute with this signature,+ -- since m might involve IO at some point, which we can't "run";+ -- alternatively, for IO it could always yield Nothing, having a separate+ -- IO-specific function to "run" commutes in IO++instance CommuteMonad Maybe where+ commuteFail = Nothing++instance Commute Prim where+ commute = commute'++class Commute' p where+ commute' :: (CommuteMonad m) => (p :> p) wX wY -> m ((p :> p) wX wY)++typematch :: Prim wX wY -> Prim wY wZ -> Bool+typematch _ _ = True -- TODO++instance Commute' Prim where+ commute' (a :> b) | null (touches a `intersect` touches b) = return (unsafeCoerceP b :> unsafeCoerceP a)+ | not (a `typematch` b) = commuteFail+ | otherwise = commuteOverlapping (a :> b)++-- Commute patches that have actual overlap in terms of touched objects, and their types allow+commuteOverlapping :: (CommuteMonad m) => (Prim :> Prim) wX wY -> m ((Prim :> Prim) wX wY)+commuteOverlapping (BinaryHunk a x :> BinaryHunk _ y) =+ do (y' :> x') <- commuteHunk (x :> y)+ return $ unsafeCoerceP (BinaryHunk a y' :> BinaryHunk a x')+commuteOverlapping (TextHunk a x :> TextHunk _ y) =+ do (y' :> x') <- commuteHunk (x :> y)+ return $ unsafeCoerceP (TextHunk a y' :> TextHunk a x')+commuteOverlapping _ = commuteFail++commuteHunk :: (CommuteMonad m) => (Hunk :> Hunk) wX wY -> m ((Hunk :> Hunk) wY wX)+commuteHunk (Hunk off1 old1 new1 :> Hunk off2 old2 new2)+ | off1 + lengthnew1 < off2 =+ return $ Hunk (off2 - lengthnew1 + lengthold1) old2 new2 :> Hunk off1 old1 new1+ | off2 + lengthold2 < off1 =+ return $ Hunk off2 old2 new2 :> Hunk (off1 + lengthnew2 - lengthold2) old1 new1+ | off1 + lengthnew1 == off2 &&+ lengthold2 /= 0 && lengthold1 /= 0 && lengthnew2 /= 0 && lengthnew1 /= 0 =+ return $ Hunk (off2 - lengthnew1 + lengthold1) old2 new2 :> Hunk off1 old1 new1+ | off2 + lengthold2 == off1 &&+ lengthold2 /= 0 && lengthold1 /= 0 && lengthnew2 /= 0 && lengthnew1 /= 0 =+ return $ Hunk off2 old2 new2 :> Hunk (off1 + lengthnew2 - lengthold2) old1 new1+ | otherwise = commuteFail+ where lengthnew1 = BS.length new1+ lengthnew2 = BS.length new2+ lengthold1 = BS.length old1+ lengthold2 = BS.length old2+commuteHunk _ = impossible
+ src/Darcs/Patch/Prim/FileUUID/Core.hs view
@@ -0,0 +1,151 @@+{-# LANGUAGE CPP, OverloadedStrings, StandaloneDeriving #-}++-- Copyright (C) 2011 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.+++module Darcs.Patch.Prim.FileUUID.Core+ ( Prim(..), Hunk(..), UUID(..), Location, Object(..), touches, hunkEdit )+ where++import Prelude ()+import Darcs.Prelude++import qualified Data.ByteString as BS++import Darcs.Patch.Witnesses.Eq ( MyEq(..) )+import Darcs.Patch.Witnesses.Show ( Show1(..), Show2(..), ShowDict(ShowDictClass) )+import Darcs.Patch.FileHunk( IsHunk(..) )+import Darcs.Patch.Invert ( Invert(..) )+import Darcs.Patch.Inspect ( PatchInspect(..) )+import Darcs.Patch.Prim.Class ( PrimConstruct(..), PrimClassify(..) )+import Darcs.Patch.Prim.FileUUID.ObjectMap++-- TODO: elaborate++data Hunk wX wY where+ Hunk :: !Int -> BS.ByteString -> BS.ByteString -> Hunk wX wY+ deriving Show++instance Show1 (Hunk wX) where+ showDict1 = ShowDictClass++instance Show2 Hunk where+ showDict2 = ShowDictClass++invertHunk :: Hunk wX wY -> Hunk wY wX+invertHunk (Hunk off old new) = Hunk off new old++hunkEdit :: Hunk wX wY -> BS.ByteString -> BS.ByteString+hunkEdit (Hunk off old new) bs = case splice bs (off) (off + BS.length old) of+ x | x == old -> BS.concat [ BS.take off bs, new, BS.drop (off + BS.length old) bs ]+ | otherwise -> error $ "error applying hunk: " ++ show off ++ " " ++ show old ++ " "+ ++ show new ++ " to " ++ show bs+ where splice bs' x y = BS.drop x $ BS.take y bs'++instance MyEq Hunk where+ unsafeCompare (Hunk i x y) (Hunk i' x' y') = i == i' && x == x' && y == y'++data Prim wX wY where+ BinaryHunk :: !UUID -> Hunk wX wY -> Prim wX wY+ TextHunk :: !UUID -> Hunk wX wY -> Prim wX wY++ -- TODO: String is not the right type here. However, what it represents is+ -- a single file *name* (not a path). No slashes allowed, no "." and ".."+ -- allowed either.+ Manifest :: !UUID -> Location -> Prim wX wY+ Demanifest :: !UUID -> Location -> Prim wX wY+ Move :: !UUID -> Location -> Location -> Prim wX wY+ Identity :: Prim wX wX++deriving instance Show (Prim wX wY)++instance Show1 (Prim wX) where+ showDict1 = ShowDictClass++instance Show2 Prim where+ showDict2 = ShowDictClass++touches :: Prim wX wY -> [UUID]+touches (BinaryHunk x _) = [x]+touches (TextHunk x _) = [x]+touches (Manifest _ (x, _)) = [x]+touches (Demanifest _ (x, _)) = [x]+touches (Move _ (x, _) (y, _)) = [x, y]+touches Identity = []++-- TODO: PrimClassify doesn't make sense for FileUUID prims+instance PrimClassify Prim where+ primIsAddfile _ = False+ primIsRmfile _ = False+ primIsAdddir _ = False+ primIsRmdir _ = False+ primIsHunk _ = False+ primIsMove _ = False+ primIsBinary _ = False+ primIsTokReplace _ = False+ primIsSetpref _ = False+ is_filepatch _ = Nothing++-- TODO: PrimConstruct makes no sense for FileUUID prims+instance PrimConstruct Prim where+ addfile _ = error "PrimConstruct addfile"+ rmfile _ = error "PrimConstruct rmfile"+ adddir _ = error "PrimConstruct adddir"+ rmdir _ = error "PrimConstruct rmdir"+ move _ _ = error "PrimConstruct move"+ changepref _ _ _ = error "PrimConstruct changepref"+ hunk _ _ _ _ = error "PrimConstruct hunk"+ tokreplace _ _ _ _ = error "PrimConstruct tokreplace"+ binary _ _ _ = error "PrimConstruct binary"+ primFromHunk _ = error "PrimConstruct primFromHunk"+ anIdentity = Identity++instance IsHunk Prim where+ isHunk _ = Nothing++instance Invert Prim where+ invert (BinaryHunk x h) = BinaryHunk x $ invertHunk h+ invert (TextHunk x h) = TextHunk x $ invertHunk h+ invert (Manifest x y) = Demanifest x y+ invert (Demanifest x y) = Manifest x y+ invert (Move x y z) = Move x z y+ invert Identity = Identity++instance PatchInspect Prim where+ -- We don't need this for FileUUID. Slashes are not allowed in Manifest and+ -- Demanifest patches and nothing else uses working-copy paths.+ listTouchedFiles _ = []++ -- TODO (used for --match 'hunk ...', presumably)+ hunkMatches _ _ = False++instance MyEq Prim where+ unsafeCompare (BinaryHunk a b) (BinaryHunk c d) = a == c && b `unsafeCompare` d+ unsafeCompare (TextHunk a b) (TextHunk c d) = a == c && b `unsafeCompare` d+ unsafeCompare (Manifest a b) (Manifest c d) = a == c && b == d+ unsafeCompare (Demanifest a b) (Demanifest c d) = a == c && b == d+ unsafeCompare Identity Identity = True+ unsafeCompare _ _ = False++instance Eq (Prim wX wY) where+ (==) = unsafeCompare
+ src/Darcs/Patch/Prim/FileUUID/Details.hs view
@@ -0,0 +1,12 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+module Darcs.Patch.Prim.FileUUID.Details+ ()+ where++import Darcs.Patch.Prim.Class ( PrimDetails(..) )+import Darcs.Patch.Prim.FileUUID.Core ( Prim(..) )+++-- TODO+instance PrimDetails Prim where+ summarizePrim _ = []
+ src/Darcs/Patch/Prim/FileUUID/ObjectMap.hs view
@@ -0,0 +1,43 @@+-- Copyright (C) 2011 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.++module Darcs.Patch.Prim.FileUUID.ObjectMap( UUID(..), Location, Object(..),+ ObjectMap(..), DirContent ) where++import Prelude ()+import Darcs.Prelude++import Darcs.Util.Hash( Hash )+import qualified Data.ByteString as BS (ByteString)+import qualified Data.Map as M+++newtype UUID = UUID BS.ByteString deriving (Eq, Ord, Show)+type Location = (UUID, BS.ByteString)+type DirContent = M.Map BS.ByteString UUID+data Object (m :: * -> *) = Directory DirContent+ | Blob (m BS.ByteString) !Hash++data ObjectMap (m :: * -> *) = ObjectMap { getObject :: UUID -> m (Maybe (Object m))+ , putObject :: UUID -> Object m -> m (ObjectMap m)+ , listObjects :: m [UUID]+ }
+ src/Darcs/Patch/Prim/FileUUID/Read.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE CPP, ViewPatterns, OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+module Darcs.Patch.Prim.FileUUID.Read () where++import Prelude ()+import Darcs.Prelude++import Darcs.Patch.Read ( ReadPatch(..) )+import Darcs.Patch.ReadMonads+import Darcs.Patch.Prim.Class( PrimRead(..) )+import Darcs.Patch.Prim.FileUUID.Core( Prim(..), Hunk(..) )+import Darcs.Patch.Prim.FileUUID.ObjectMap+import Darcs.Patch.Witnesses.Sealed( seal )++import Control.Monad ( liftM, liftM2 )+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC+import Data.Char ( chr )++#include "impossible.h"++instance PrimRead Prim where+ readPrim _ = do skipSpace+ choice $ map (liftM seal) [+ identity,+ hunk "hunk" TextHunk,+ hunk "binhunk" BinaryHunk,+ manifest "manifest" Manifest,+ manifest "demanifest" Demanifest ]++ where manifest kind ctor = liftM2 ctor (patch kind) location+ identity = lexString "identity" >> return Identity+ patch x = string x >> uuid+ uuid = UUID <$> myLex'+ filename = encoded+ encoded = decodeWhite <$> myLex'+ hunktext = skipSpace >> choice [ string "." >> encoded, string "!" >> return B.empty ]+ location = liftM2 (,) uuid filename+ hunk kind ctor = do uid <- patch kind+ offset <- int+ old <- hunktext+ new <- hunktext+ return $ ctor uid (Hunk offset old new)++instance ReadPatch Prim where+ readPatch' = readPrim undefined++-- XXX a bytestring version of decodeWhite from Darcs.FileName+decodeWhite :: B.ByteString -> B.ByteString+decodeWhite (BC.uncons -> Just ('\\', cs)) =+ case BC.break (=='\\') cs of+ (theord, BC.uncons -> Just ('\\', rest)) ->+ chr (read $ BC.unpack theord) `BC.cons` decodeWhite rest+ _ -> error "malformed filename"+decodeWhite (BC.uncons -> Just (c, cs)) = c `BC.cons` decodeWhite cs+decodeWhite (BC.uncons -> Nothing) = BC.empty+decodeWhite _ = impossible+
+ src/Darcs/Patch/Prim/FileUUID/Show.hs view
@@ -0,0 +1,70 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE CPP, OverloadedStrings #-}+module Darcs.Patch.Prim.FileUUID.Show+ ( showHunk )+ where++import Prelude ()+import Darcs.Prelude++import Prelude hiding ( pi )++import Data.Char ( isSpace, ord )+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC++import Darcs.Patch.Format ( PatchListFormat, FileNameFormat(..) )+import Darcs.Patch.Show ( ShowPatchBasic(..), ShowPatch(..) )+import Darcs.Patch.Summary ( plainSummaryPrim, plainSummaryPrims )+import Darcs.Patch.Prim.Class ( PrimShow(..) )+import Darcs.Patch.Prim.FileUUID.Core ( Prim(..), Hunk(..), UUID(..) )+import Darcs.Patch.Prim.FileUUID.Details ()+import Darcs.Util.Printer ( text, packedString, blueText, (<+>), (<>), Doc )++#include "impossible.h"++-- TODO this instance shouldn't really be necessary, as Prims aren't used generically+instance PatchListFormat Prim++instance ShowPatchBasic Prim where+ showPatch = showPrim OldFormat++instance ShowPatch Prim where+ showContextPatch p = return $ showPatch p+ summary = plainSummaryPrim+ summaryFL = plainSummaryPrims False []+ thing _ = "change"++instance PrimShow Prim where+ showPrim _ (TextHunk u h) = showHunk "hunk" u h+ showPrim _ (BinaryHunk u h) = showHunk "binhunk" u h+ showPrim _ (Manifest f (d,p)) = showManifest "manifest" d f p+ showPrim _ (Demanifest f (d,p)) = showManifest "demanifest" d f p+ showPrim _ Identity = blueText "identity"+ showPrim _ (Move{}) = bug "show for move not implemented"++showManifest :: String -> UUID -> UUID -> BC.ByteString -> Doc+showManifest txt dir file path = blueText txt <+>+ formatUUID file <+>+ formatUUID dir <+>+ packedString (encodeWhite path)++showHunk :: String -> UUID -> Hunk wX wY -> Doc+showHunk txt uid (Hunk off old new) = blueText txt <+>+ formatUUID uid <+>+ text (show off) <+>+ hunktext old <+>+ hunktext new+ where hunktext bit | B.null bit = text "!"+ | otherwise = text "." <> packedString (encodeWhite bit)++formatUUID :: UUID -> Doc+formatUUID (UUID x) = packedString x++-- XXX a bytestring version of encodeWhite from Darcs.FileName+encodeWhite :: B.ByteString -> B.ByteString+encodeWhite = BC.concatMap encode+ where encode c+ | isSpace c || c == '\\' = B.concat [ "\\", BC.pack $ show $ ord c, "\\" ]+ | otherwise = BC.singleton c+
src/Darcs/Patch/Prim/V1.hs view
@@ -1,6 +1,9 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module Darcs.Patch.Prim.V1 ( Prim ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Prim.V1.Apply () import Darcs.Patch.Prim.V1.Coalesce () import Darcs.Patch.Prim.V1.Commute ()
src/Darcs/Patch/Prim/V1/Apply.hs view
@@ -1,7 +1,10 @@-{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-incomplete-patterns #-}+{-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE CPP #-} module Darcs.Patch.Prim.V1.Apply () where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Apply ( Apply(..) ) import Darcs.Patch.Repair ( RepairToFL(..) ) @@ -15,8 +18,8 @@ import Darcs.Patch.Format ( FileNameFormat(..) ) import Darcs.Patch.TokenReplace ( tryTokInternal ) -import Darcs.Patch.ApplyMonad ( ApplyMonad(..) )-import Storage.Hashed.Tree( Tree )+import Darcs.Patch.ApplyMonad ( ApplyMonad(..), ApplyMonadTree(..) )+import Darcs.Util.Tree( Tree ) import Darcs.Patch.Witnesses.Ordered ( FL(..), mapFL_FL, spanFL, (:>)(..) ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoercePStart )@@ -95,7 +98,7 @@ applyPrimFL ps' where f_hunk (FP f' (Hunk{})) | f == f' = True f_hunk _ = False- hunkmod :: ApplyMonad m Tree => FL FilePatchType wX wY+ hunkmod :: ApplyMonad Tree m => FL FilePatchType wX wY -> B.ByteString -> m B.ByteString hunkmod NilFL ps = return ps hunkmod (Hunk line old new:>:hs) ps
src/Darcs/Patch/Prim/V1/Coalesce.hs view
@@ -4,6 +4,9 @@ () where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( pi ) import Control.Arrow ( second ) import Data.Maybe ( fromMaybe )@@ -22,7 +25,7 @@ import Darcs.Patch.Prim.V1.Show () import Darcs.Patch.Witnesses.Eq ( MyEq(..), EqCheck(..) ) import Darcs.Patch.Witnesses.Ordered- ( FL(..), RL(..), (:>)(..), (:<)(..)+ ( FL(..), RL(..), (:>)(..) , reverseRL, mapFL, mapFL_FL , concatFL, lengthFL, (+>+) ) import Darcs.Patch.Witnesses.Sealed@@ -32,7 +35,6 @@ import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP, unsafeCoercePEnd ) import Darcs.Patch.Invert ( Invert(..) ) import Darcs.Patch.Commute ( Commute(..) )--- import Darcs.Patch.Permutations () -- for Invert instance of FL import Darcs.Util.Diff ( getChanges ) import qualified Darcs.Util.Diff as D ( DiffAlgorithm )@@ -41,23 +43,23 @@ #include "impossible.h" --- | 'coalesceRev' @p2 :< p1@ tries to combine @p1@ and @p2@ into a single+-- | 'coalesceFwd' @p1 :> p2@ tries to combine @p1@ and @p2@ into a single -- patch without intermediary changes. For example, two hunk patches -- modifying adjacent lines can be coalesced into a bigger hunk patch. -- Or a patch which moves file A to file B can be coalesced with a -- patch that moves file B into file C, yielding a patch that moves -- file A to file C.-coalesceRev :: (Prim :< Prim) wX wY -> Maybe (FL Prim wX wY)-coalesceRev (FP f1 _ :< FP f2 _) | f1 /= f2 = Nothing-coalesceRev (p2 :< p1) | IsEq <- p2 =\/= invert p1 = Just NilFL-coalesceRev (FP f1 p1 :< FP _ p2) = fmap (:>: NilFL) $ coalesceFilePrim f1 (p1 :< p2) -- f1 = f2-coalesceRev (Move a b :< Move b' a') | a == a' = Just $ Move b' b :>: NilFL-coalesceRev (Move a b :< FP f AddFile) | f == a = Just $ FP b AddFile :>: NilFL-coalesceRev (Move a b :< DP f AddDir) | f == a = Just $ DP b AddDir :>: NilFL-coalesceRev (FP f RmFile :< Move a b) | b == f = Just $ FP a RmFile :>: NilFL-coalesceRev (DP f RmDir :< Move a b) | b == f = Just $ DP a RmDir :>: NilFL-coalesceRev (ChangePref p f1 t1 :< ChangePref p2 f2 t2) | p == p2 && t2 == f1 = Just $ ChangePref p f2 t1 :>: NilFL-coalesceRev _ = Nothing+coalesceFwd :: (Prim :> Prim) wX wY -> Maybe (FL Prim wX wY)+coalesceFwd (FP f1 _ :> FP f2 _) | f1 /= f2 = Nothing+coalesceFwd (p1 :> p2) | IsEq <- invert p1 =\/= p2 = Just NilFL+coalesceFwd (FP f1 p1 :> FP _ p2) = fmap (:>: NilFL) $ coalesceFilePrim f1 (p1 :> p2) -- f1 = f2+coalesceFwd (Move a b :> Move b' a') | b == b' = Just $ Move a a' :>: NilFL+coalesceFwd (FP f AddFile :> Move a b) | f == a = Just $ FP b AddFile :>: NilFL+coalesceFwd (DP f AddDir :> Move a b) | f == a = Just $ DP b AddDir :>: NilFL+coalesceFwd (Move a b :> FP f RmFile) | b == f = Just $ FP a RmFile :>: NilFL+coalesceFwd (Move a b :> DP f RmDir) | b == f = Just $ DP a RmDir :>: NilFL+coalesceFwd (ChangePref p1 f1 t1 :> ChangePref p2 f2 t2) | p1 == p2 && t1 == f2 = Just $ ChangePref p1 f1 t2 :>: NilFL+coalesceFwd _ = Nothing mapPrimFL :: (forall wX wY . FL Prim wX wY -> FL Prim wX wY) -> FL Prim wW wZ -> FL Prim wW wZ@@ -124,11 +126,11 @@ -> Maybe (FL Prim wW wZ) tryOne _ _ NilFL = Nothing tryOne sofar p (p1:>:ps) =- case coalesceRev (p1 :< p) of+ case coalesceFwd (p :> p1) of Just p' -> Just (reverseRL sofar +>+ p' +>+ ps) Nothing -> case commute (p :> p1) of Nothing -> Nothing- Just (p1' :> p') -> tryOne (p1':<:sofar) p' ps+ Just (p1' :> p') -> tryOne (sofar:<:p1') p' ps -- | The heart of "sortCoalesceFL" sortCoalesceFL2 :: FL Prim wX wY -> FL Prim wX wY@@ -158,7 +160,7 @@ -> Either (FL Prim wX wZ) (FL Prim wX wZ) pushCoalescePatch new NilFL = Left (new:>:NilFL) pushCoalescePatch new ps@(p:>:ps')- = case coalesceRev (p :< new) of+ = case coalesceFwd (new :> p) of Just (new' :>: NilFL) -> Right $ either id id $ pushCoalescePatch new' ps' Just NilFL -> Right ps' Just _ -> impossible -- coalesce either returns a singleton or empty@@ -171,17 +173,17 @@ Left r -> Left (p' :>: r) Nothing -> Left (new:>:ps) -coalesceFilePrim :: FileName -> (FilePatchType :< FilePatchType) wX wY+coalesceFilePrim :: FileName -> (FilePatchType :> FilePatchType) wX wY -> Maybe (Prim wX wY)-coalesceFilePrim f (Hunk line1 old1 new1 :< Hunk line2 old2 new2)- = coalesceHunk f line1 old1 new1 line2 old2 new2+coalesceFilePrim f (Hunk line1 old1 new1 :> Hunk line2 old2 new2)+ = coalesceHunk f line2 old2 new2 line1 old1 new1 -- Token replace patches operating right after (or before) AddFile (RmFile) -- is an identity patch, as far as coalescing is concerned.-coalesceFilePrim f (TokReplace{} :< AddFile) = Just $ FP f AddFile-coalesceFilePrim f (RmFile :< TokReplace{}) = Just $ FP f RmFile-coalesceFilePrim f (TokReplace t1 o1 n1 :< TokReplace t2 o2 n2)- | t1 == t2 && n2 == o1 = Just $ FP f $ TokReplace t1 o2 n1-coalesceFilePrim f (Binary m n :< Binary o m')+coalesceFilePrim f (AddFile :> TokReplace{}) = Just $ FP f AddFile+coalesceFilePrim f (TokReplace{} :> RmFile) = Just $ FP f RmFile+coalesceFilePrim f (TokReplace t1 o1 n1 :> TokReplace t2 o2 n2)+ | t1 == t2 && n1 == o2 = Just $ FP f $ TokReplace t1 o1 n2+coalesceFilePrim f (Binary o m' :> Binary m n) | m == m' = Just $ FP f $ Binary o n coalesceFilePrim _ _ = Nothing @@ -244,4 +246,4 @@ -- would be nice to understand why. canonizeFL da = concatFL . mapFL_FL (canonize da) . sortCoalesceFL . concatFL . mapFL_FL (canonize da)- coalesce (x :> y) = coalesceRev (y :< x)+ coalesce = coalesceFwd
src/Darcs/Patch/Prim/V1/Commute.hs view
@@ -6,20 +6,23 @@ ) where -import Prelude hiding ( pi )+import Prelude ()+import Darcs.Prelude++import Prelude hiding ( pi, Applicative(..) ) import Control.Monad ( MonadPlus, msum, mzero, mplus )-import Control.Applicative ( Applicative(..), Alternative(..) )+import Control.Applicative ( Alternative(..) ) import qualified Data.ByteString as B (ByteString, concat) import qualified Data.ByteString.Char8 as BC (pack) import Darcs.Util.Path ( FileName, fn2fp, movedirfilename )-import Darcs.Patch.Witnesses.Ordered ( (:<)(..) ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )+import Darcs.Patch.Witnesses.Ordered ( (:>)(..) ) import Darcs.Patch.Prim.V1.Core ( Prim(..), FilePatchType(..) ) import Darcs.Patch.Invert ( Invert(..) )-import Darcs.Patch.Commute ( Commute(..), toFwdCommute, toRevCommute )+import Darcs.Patch.Commute ( Commute(..) ) import Darcs.Patch.Permutations () -- for Invert instance of FL import Darcs.Patch.TokenReplace ( tryTokInternal ) @@ -75,12 +78,12 @@ toPerhaps Nothing = Failed cleverCommute :: CommuteFunction -> CommuteFunction-cleverCommute c (p1:<p2) =- case c (p1 :< p2) of+cleverCommute c (p1:>p2) =+ case c (p1 :> p2) of Succeeded x -> Succeeded x Failed -> Failed- Unknown -> case c (invert p2 :< invert p1) of- Succeeded (p1' :< p2') -> Succeeded (invert p2' :< invert p1')+ Unknown -> case c (invert p2 :> invert p1) of+ Succeeded (p1' :> p2') -> Succeeded (invert p2' :> invert p1') Failed -> Failed Unknown -> Unknown --cleverCommute c (p1,p2) = c (p1,p2) `mplus`@@ -91,17 +94,17 @@ speedyCommute :: CommuteFunction -- Deal with common cases quickly! -- Two file-patches modifying different files trivially commute.-speedyCommute (p2@(FP f' _) :< p1@(FP f _))- | f /= f' = Succeeded (unsafeCoerceP p1 :< unsafeCoerceP p2)+speedyCommute (p1@(FP f1 _) :> p2@(FP f2 _))+ | f1 /= f2 = Succeeded (unsafeCoerceP p2 :> unsafeCoerceP p1) speedyCommute _other = Unknown everythingElseCommute :: CommuteFunction everythingElseCommute = eec- where+ where eec :: CommuteFunction- eec (ChangePref p f t :<p1) = Succeeded (unsafeCoerceP p1 :< ChangePref p f t)- eec (p2 :<ChangePref p f t) = Succeeded (ChangePref p f t :< unsafeCoerceP p2)- eec xx = cleverCommute commuteFiledir xx+ eec (p1 :> ChangePref p f t) = Succeeded (ChangePref p f t :> unsafeCoerceP p1)+ eec (ChangePref p f t :> p2) = Succeeded (unsafeCoerceP p2 :> ChangePref p f t)+ eec xx = cleverCommute commuteFiledir xx {- Note that it must be true that@@ -115,8 +118,8 @@ -} instance Commute Prim where- commute x = toMaybe $ msum [toFwdCommute speedyCommute x,- toFwdCommute everythingElseCommute x+ commute x = toMaybe $ msum [speedyCommute x,+ everythingElseCommute x ] isSuperdir :: FileName -> FileName -> Bool@@ -125,40 +128,40 @@ length s2 >= length s1 + 1 && take (length s1 + 1) s2 == s1 ++ "/" commuteFiledir :: CommuteFunction-commuteFiledir (FP f1 p1 :< FP f2 p2) =- if f1 /= f2 then Succeeded ( FP f2 (unsafeCoerceP p2) :< FP f1 (unsafeCoerceP p1) )- else commuteFP f1 (p1 :< p2)-commuteFiledir (DP d1 p1 :< DP d2 p2) =+commuteFiledir (FP f1 p1 :> FP f2 p2) =+ if f1 /= f2 then Succeeded ( FP f2 (unsafeCoerceP p2) :> FP f1 (unsafeCoerceP p1) )+ else commuteFP f1 (p1 :> p2)+commuteFiledir (DP d1 p1 :> DP d2 p2) = if not (isInDirectory d1 d2 || isInDirectory d2 d1) && d1 /= d2- then Succeeded ( DP d2 (unsafeCoerceP p2) :< DP d1 (unsafeCoerceP p1) )+ then Succeeded ( DP d2 (unsafeCoerceP p2) :> DP d1 (unsafeCoerceP p1) ) else Failed-commuteFiledir (DP d dp :< FP f fp) =+commuteFiledir (FP f fp :> DP d dp) = if not $ isInDirectory d f- then Succeeded (FP f (unsafeCoerceP fp) :< DP d (unsafeCoerceP dp))+ then Succeeded (DP d (unsafeCoerceP dp) :> FP f (unsafeCoerceP fp)) else Failed -commuteFiledir (Move d d' :< FP f2 p2)- | f2 == d' = Failed- | (p2 == AddFile || p2 == RmFile) && d == f2 = Failed- | otherwise = Succeeded (FP (movedirfilename d d' f2) (unsafeCoerceP p2) :< Move d d')-commuteFiledir (Move d d' :< DP d2 p2)- | isSuperdir d2 d' || isSuperdir d2 d = Failed- | d == d2 = Failed -- The exact guard is p2 == AddDir && d == d2- -- but note d == d2 suffices because we know p2 != RmDir- -- (and hence p2 == AddDir) since patches must be sequential.- | d2 == d' = Failed- | otherwise = Succeeded (DP (movedirfilename d d' d2) (unsafeCoerceP p2) :< Move d d')-commuteFiledir (Move d d' :< Move f f')+commuteFiledir (FP f1 p1 :> Move d d')+ | f1 == d' = Failed+ | (p1 == AddFile || p1 == RmFile) && d == f1 = Failed+ | otherwise = Succeeded (Move d d' :> FP (movedirfilename d d' f1) (unsafeCoerceP p1))+commuteFiledir (DP d1 p1 :> Move d d')+ | isSuperdir d1 d' || isSuperdir d1 d = Failed+ | d == d1 = Failed -- The exact guard is p1 == AddDir && d == d1+ -- but note d == d1 suffices because we know p1 != RmDir+ -- (and hence p1 == AddDir) since patches must be sequential.+ | d1 == d' = Failed+ | otherwise = Succeeded (Move d d' :> DP (movedirfilename d d' d1) (unsafeCoerceP p1))+commuteFiledir (Move f f' :> Move d d') | f == d' || f' == d = Failed | f == d || f' == 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'))+ Succeeded (Move (movedirfilename f' f d) (movedirfilename f' f d') :>+ Move (movedirfilename d d' f) (movedirfilename d d' f')) commuteFiledir _ = Unknown -type CommuteFunction = forall wX wY . (Prim :< Prim) wX wY -> Perhaps ((Prim :< Prim) wX wY)+type CommuteFunction = forall wX wY . (Prim :> Prim) wX wY -> Perhaps ((Prim :> Prim) wX wY) newtype WrappedCommuteFunction = WrappedCommuteFunction { runWrappedCommuteFunction :: CommuteFunction } subcommutes :: [(String, WrappedCommuteFunction)]@@ -166,56 +169,56 @@ [("speedyCommute", WrappedCommuteFunction speedyCommute), ("commuteFiledir", WrappedCommuteFunction (cleverCommute commuteFiledir)), ("commuteFilepatches", WrappedCommuteFunction (cleverCommute commuteFilepatches)),- ("commutex", WrappedCommuteFunction (toPerhaps . toRevCommute commute))+ ("commutex", WrappedCommuteFunction (toPerhaps . commute)) ] commuteFilepatches :: CommuteFunction-commuteFilepatches (FP f1 p1 :< FP f2 p2) | f1 == f2 = commuteFP f1 (p1 :< p2)+commuteFilepatches (FP f1 p1 :> FP f2 p2) | f1 == f2 = commuteFP f1 (p1 :> p2) commuteFilepatches _ = Unknown -commuteFP :: FileName -> (FilePatchType :< FilePatchType) wX wY- -> Perhaps ((Prim :< Prim) wX wY)-commuteFP f (Hunk line1 [] [] :< p2) =- seq f $ Succeeded (FP f (unsafeCoerceP p2) :< FP f (Hunk line1 [] []))-commuteFP f (p2 :< Hunk line1 [] []) =- seq f $ Succeeded (FP f (Hunk line1 [] []) :< FP f (unsafeCoerceP p2))-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 tryTokReplace t o n old2 of+commuteFP :: FileName -> (FilePatchType :> FilePatchType) wX wY+ -> Perhaps ((Prim :> Prim) wX wY)+commuteFP f (p1 :> Hunk line1 [] []) =+ seq f $ Succeeded (FP f (Hunk line1 [] []) :> FP f (unsafeCoerceP p1))+commuteFP f (Hunk line1 [] [] :> p2) =+ seq f $ Succeeded (FP f (unsafeCoerceP p2) :> FP f (Hunk line1 [] []))+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 (Hunk line1 old1 new1 :> TokReplace t o n) = seq f $+ case tryTokReplace t o n old1 of Nothing -> Failed- Just old2' ->- case tryTokReplace t o n new2 of- Nothing -> Failed- Just new2' -> Succeeded (FP f (Hunk line2 old2' new2') :<- FP f (TokReplace t o n))-commuteFP f (TokReplace t o n :< TokReplace t2 o2 n2)- | seq f $ t /= t2 = Failed- | o == o2 = Failed- | n == o2 = Failed- | o == n2 = Failed- | n == n2 = Failed- | otherwise = Succeeded (FP f (TokReplace t2 o2 n2) :<- FP f (TokReplace t o n))+ Just old1' ->+ case tryTokReplace t o n new1 of+ Nothing -> Failed+ Just new1' -> Succeeded (FP f (TokReplace t o n) :>+ FP f (Hunk line1 old1' new1'))+commuteFP f (TokReplace t1 o1 n1 :> TokReplace t2 o2 n2)+ | seq f $ t1 /= t2 = Failed+ | o1 == o2 = Failed+ | n1 == o2 = Failed+ | o1 == n2 = Failed+ | n1 == n2 = Failed+ | otherwise = Succeeded (FP f (TokReplace t2 o2 n2) :>+ FP f (TokReplace t1 o1 n1)) commuteFP _ _ = Unknown -commuteHunk :: FileName -> (FilePatchType :< FilePatchType) wX wY- -> Maybe ((Prim :< Prim) wX wY)-commuteHunk f (Hunk line2 old2 new2 :< Hunk line1 old1 new1)+commuteHunk :: FileName -> (FilePatchType :> FilePatchType) wX wY+ -> Maybe ((Prim :> Prim) wX wY)+commuteHunk f (Hunk line1 old1 new1 :> Hunk line2 old2 new2) | seq f $ line1 + lengthnew1 < line2 =- Just (FP f (Hunk line1 old1 new1) :<- FP f (Hunk (line2 - lengthnew1 + lengthold1) old2 new2))+ Just (FP f (Hunk (line2 - lengthnew1 + lengthold1) old2 new2) :>+ FP f (Hunk line1 old1 new1)) | line2 + lengthold2 < line1 =- Just (FP f (Hunk (line1+ lengthnew2 - lengthold2) old1 new1) :<- FP f (Hunk line2 old2 new2))+ Just (FP f (Hunk line2 old2 new2) :>+ FP f (Hunk (line1+ lengthnew2 - lengthold2) old1 new1)) | line1 + lengthnew1 == line2 && lengthold2 /= 0 && lengthold1 /= 0 && lengthnew2 /= 0 && lengthnew1 /= 0 =- Just (FP f (Hunk line1 old1 new1) :<- FP f (Hunk (line2 - lengthnew1 + lengthold1) old2 new2))+ Just (FP f (Hunk (line2 - lengthnew1 + lengthold1) old2 new2) :>+ FP f (Hunk line1 old1 new1)) | line2 + lengthold2 == line1 && lengthold2 /= 0 && lengthold1 /= 0 && lengthnew2 /= 0 && lengthnew1 /= 0 =- Just (FP f (Hunk (line1 + lengthnew2 - lengthold2) old1 new1) :<- FP f (Hunk line2 old2 new2))+ Just (FP f (Hunk line2 old2 new2) :>+ FP f (Hunk (line1 + lengthnew2 - lengthold2) old1 new1)) | otherwise = seq f Nothing where lengthnew1 = length new1 lengthnew2 = length new2
src/Darcs/Patch/Prim/V1/Core.hs view
@@ -26,6 +26,9 @@ ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( pi ) import qualified Data.ByteString as B (ByteString)
src/Darcs/Patch/Prim/V1/Details.hs view
@@ -3,7 +3,9 @@ () where -import Prelude hiding ( pi )+import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Prim.Class ( PrimDetails(..) ) import Darcs.Patch.Prim.V1.Core ( Prim(..), FilePatchType(..), DirPatchType(..) )
src/Darcs/Patch/Prim/V1/Read.hs view
@@ -1,6 +1,9 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module Darcs.Patch.Prim.V1.Read () where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Prim.Class ( PrimRead(..), hunk, binary ) import Darcs.Patch.Prim.V1.Core ( Prim(..),
src/Darcs/Patch/Prim/V1/Show.hs view
@@ -4,13 +4,16 @@ ( showHunk ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( pi ) import Darcs.Util.ByteString ( fromPS2Hex ) import qualified Data.ByteString as B (ByteString, length, take, drop) import qualified Data.ByteString.Char8 as BC (head) -import Storage.Hashed.Tree( Tree )+import Darcs.Util.Tree( Tree ) import Darcs.Patch.FileHunk ( FileHunk(..), IsHunk(..), showFileHunk ) import Darcs.Util.Path ( FileName )@@ -42,7 +45,7 @@ showContextPatch (isHunk -> Just fh) = showContextHunk fh showContextPatch p = return $ showPatch p summary = plainSummaryPrim- summaryFL = plainSummaryPrims False+ summaryFL = plainSummaryPrims False [] thing _ = "change" instance Show (Prim wX wY) where
− src/Darcs/Patch/Prim/V3.hs
@@ -1,22 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-module Darcs.Patch.Prim.V3 ( Prim ) where--import Darcs.Patch.Prim.V3.Apply ()-import Darcs.Patch.Prim.V3.Coalesce ()-import Darcs.Patch.Prim.V3.Commute ()-import Darcs.Patch.Prim.V3.Core ( Prim )-import Darcs.Patch.Prim.V3.Details ()-import Darcs.Patch.Prim.V3.Read ()-import Darcs.Patch.Prim.V3.Show ()--import Darcs.Patch.Prim.Class ( PrimPatch, PrimPatchBase(..), FromPrim(..) )-import Darcs.Patch.Patchy ( Patchy )--instance PrimPatch Prim-instance Patchy Prim--instance PrimPatchBase Prim where- type PrimOf Prim = Prim--instance FromPrim Prim where- fromPrim = id
− src/Darcs/Patch/Prim/V3/Apply.hs
@@ -1,63 +0,0 @@-{-# LANGUAGE CPP, MultiParamTypeClasses, OverloadedStrings #-}-{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-missing-methods #-}-module Darcs.Patch.Prim.V3.Apply ( ObjectMap(..) ) where--import Darcs.Patch.Apply ( Apply(..) )-import Darcs.Patch.ApplyMonad ( ApplyMonad(..), ApplyMonadTrans(..), ToTree(..) )-import Darcs.Patch.Repair ( RepairToFL(..) )--import Darcs.Patch.Prim.Class ( PrimApply(..) )-import Darcs.Patch.Prim.V3.Core ( Prim(..), hunkEdit )-import Darcs.Patch.Prim.V3.ObjectMap--import Control.Monad.State( StateT, runStateT, gets, lift, put )-import qualified Data.Map as M---- import Darcs.Patch.ApplyMonad ( ApplyMonad(..) )--import Darcs.Patch.Witnesses.Ordered ( FL(..) )-import Storage.Hashed.Hash( Hash(..) )--#include "impossible.h"--instance Apply Prim where- type ApplyState Prim = ObjectMap- apply (Manifest i (dirid, name)) = editDirectory dirid (M.insert name i)- apply (Demanifest _ (dirid, name)) = editDirectory dirid (M.delete name)- apply (TextHunk i hunk) = editFile i (hunkEdit hunk)- apply (BinaryHunk i hunk) = editFile i (hunkEdit hunk)- apply Identity = return ()- apply (Move{}) = bug "apply for move not implemented"--instance RepairToFL Prim where- applyAndTryToFixFL p = apply p >> return Nothing--instance PrimApply Prim where- applyPrimFL NilFL = return ()- applyPrimFL (p:>:ps) = apply p >> applyPrimFL ps--instance ToTree ObjectMap -- TODO--editObject :: (Monad m) => UUID -> (Maybe (Object m) -> Object m) -> (StateT (ObjectMap m) m) ()-editObject i edit = do load <- gets getObject- store <- gets putObject- obj <- lift $ load i- new <- lift $ store i $ edit obj- put new- return ()--instance (Functor m, Monad m) => ApplyMonad (StateT (ObjectMap m) m) ObjectMap where- type ApplyMonadBase (StateT (ObjectMap m) m) = m- editFile i edit = editObject i edit'- where edit' (Just (Blob x _)) = Blob (edit `fmap` x) NoHash- edit' (Just (Directory x)) = Directory x -- error?- edit' Nothing = Blob (return $ edit "") NoHash- editDirectory i edit = editObject i edit'- where edit' (Just (Directory x)) = Directory $ edit x- edit' (Just (Blob x y)) = Blob x y -- error?- edit' Nothing = Directory $ edit M.empty--instance (Functor m, Monad m) => ApplyMonadTrans m ObjectMap where- type ApplyMonadOver m ObjectMap = StateT (ObjectMap m) m- runApplyMonad = runStateT-
− src/Darcs/Patch/Prim/V3/Coalesce.hs
@@ -1,15 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-module Darcs.Patch.Prim.V3.Coalesce () where--import Darcs.Patch.Prim.Class ( PrimCanonize(..) )-import Darcs.Patch.Witnesses.Ordered( FL(..) )-import Darcs.Patch.Prim.V3.Core( Prim )---- TODO-instance PrimCanonize Prim where- tryToShrink = error "tryToShrink"- tryShrinkingInverse _ = error "tryShrinkingInverse"- sortCoalesceFL = id- canonize _ = (:>: NilFL)- canonizeFL _ = id- coalesce = const Nothing
− src/Darcs/Patch/Prim/V3/Commute.hs
@@ -1,70 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-overlapping-patterns #-}-{-# LANGUAGE CPP #-}-module Darcs.Patch.Prim.V3.Commute- ( CommuteMonad(..) )- where--import Data.List ( intersect )--import qualified Data.ByteString as BS (length)--import Darcs.Patch.Witnesses.Ordered ( (:>)(..) )-import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )-import Darcs.Patch.Prim.V3.Core ( Prim(..), Hunk(..), touches )-import Darcs.Patch.Commute ( Commute(..) )-import Darcs.Patch.Permutations () -- for Invert instance of FL--#include "impossible.h"--class Monad m => CommuteMonad m where- commuteFail :: m a- -- TODO we eventually have to get rid of runCommute with this signature,- -- since m might involve IO at some point, which we can't "run";- -- alternatively, for IO it could always yield Nothing, having a separate- -- IO-specific function to "run" commutes in IO--instance CommuteMonad Maybe where- commuteFail = Nothing--instance Commute Prim where- commute = commute'--class Commute' p where- commute' :: (CommuteMonad m) => (p :> p) wX wY -> m ((p :> p) wX wY)--typematch :: Prim wX wY -> Prim wY wZ -> Bool-typematch _ _ = True -- TODO--instance Commute' Prim where- commute' (a :> b) | null (touches a `intersect` touches b) = return (unsafeCoerceP b :> unsafeCoerceP a)- | not (a `typematch` b) = commuteFail- | otherwise = commuteOverlapping (a :> b)---- Commute patches that have actual overlap in terms of touched objects, and their types allow-commuteOverlapping :: (CommuteMonad m) => (Prim :> Prim) wX wY -> m ((Prim :> Prim) wX wY)-commuteOverlapping (BinaryHunk a x :> BinaryHunk _ y) =- do (y' :> x') <- commuteHunk (x :> y)- return $ unsafeCoerceP (BinaryHunk a y' :> BinaryHunk a x')-commuteOverlapping (TextHunk a x :> TextHunk _ y) =- do (y' :> x') <- commuteHunk (x :> y)- return $ unsafeCoerceP (TextHunk a y' :> TextHunk a x')-commuteOverlapping _ = commuteFail--commuteHunk :: (CommuteMonad m) => (Hunk :> Hunk) wX wY -> m ((Hunk :> Hunk) wY wX)-commuteHunk (Hunk off1 old1 new1 :> Hunk off2 old2 new2)- | off1 + lengthnew1 < off2 =- return $ Hunk (off2 - lengthnew1 + lengthold1) old2 new2 :> Hunk off1 old1 new1- | off2 + lengthold2 < off1 =- return $ Hunk off2 old2 new2 :> Hunk (off1 + lengthnew2 - lengthold2) old1 new1- | off1 + lengthnew1 == off2 &&- lengthold2 /= 0 && lengthold1 /= 0 && lengthnew2 /= 0 && lengthnew1 /= 0 =- return $ Hunk (off2 - lengthnew1 + lengthold1) old2 new2 :> Hunk off1 old1 new1- | off2 + lengthold2 == off1 &&- lengthold2 /= 0 && lengthold1 /= 0 && lengthnew2 /= 0 && lengthnew1 /= 0 =- return $ Hunk off2 old2 new2 :> Hunk (off1 + lengthnew2 - lengthold2) old1 new1- | otherwise = commuteFail- where lengthnew1 = BS.length new1- lengthnew2 = BS.length new2- lengthold1 = BS.length old1- lengthold2 = BS.length old2-commuteHunk _ = impossible
− src/Darcs/Patch/Prim/V3/Core.hs
@@ -1,148 +0,0 @@-{-# LANGUAGE CPP, OverloadedStrings, StandaloneDeriving #-}---- Copyright (C) 2011 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.---module Darcs.Patch.Prim.V3.Core- ( Prim(..), Hunk(..), UUID(..), Location, Object(..), touches, hunkEdit )- where--import qualified Data.ByteString as BS--import Darcs.Patch.Witnesses.Eq ( MyEq(..) )-import Darcs.Patch.Witnesses.Show ( Show1(..), Show2(..), ShowDict(ShowDictClass) )-import Darcs.Patch.FileHunk( IsHunk(..) )-import Darcs.Patch.Invert ( Invert(..) )-import Darcs.Patch.Inspect ( PatchInspect(..) )-import Darcs.Patch.Prim.Class ( PrimConstruct(..), PrimClassify(..) )-import Darcs.Patch.Prim.V3.ObjectMap---- TODO: elaborate--data Hunk wX wY where- Hunk :: !Int -> BS.ByteString -> BS.ByteString -> Hunk wX wY- deriving Show--instance Show1 (Hunk wX) where- showDict1 = ShowDictClass--instance Show2 Hunk where- showDict2 = ShowDictClass--invertHunk :: Hunk wX wY -> Hunk wY wX-invertHunk (Hunk off old new) = Hunk off new old--hunkEdit :: Hunk wX wY -> BS.ByteString -> BS.ByteString-hunkEdit (Hunk off old new) bs = case splice bs (off) (off + BS.length old) of- x | x == old -> BS.concat [ BS.take off bs, new, BS.drop (off + BS.length old) bs ]- | otherwise -> error $ "error applying hunk: " ++ show off ++ " " ++ show old ++ " "- ++ show new ++ " to " ++ show bs- where splice bs' x y = BS.drop x $ BS.take y bs'--instance MyEq Hunk where- unsafeCompare (Hunk i x y) (Hunk i' x' y') = i == i' && x == x' && y == y'--data Prim wX wY where- BinaryHunk :: !UUID -> Hunk wX wY -> Prim wX wY- TextHunk :: !UUID -> Hunk wX wY -> Prim wX wY-- -- TODO: String is not the right type here. However, what it represents is- -- a single file *name* (not a path). No slashes allowed, no "." and ".."- -- allowed either.- Manifest :: !UUID -> Location -> Prim wX wY- Demanifest :: !UUID -> Location -> Prim wX wY- Move :: !UUID -> Location -> Location -> Prim wX wY- Identity :: Prim wX wX--deriving instance Show (Prim wX wY)--instance Show1 (Prim wX) where- showDict1 = ShowDictClass--instance Show2 Prim where- showDict2 = ShowDictClass--touches :: Prim wX wY -> [UUID]-touches (BinaryHunk x _) = [x]-touches (TextHunk x _) = [x]-touches (Manifest _ (x, _)) = [x]-touches (Demanifest _ (x, _)) = [x]-touches (Move _ (x, _) (y, _)) = [x, y]-touches Identity = []---- TODO: PrimClassify doesn't make sense for V3 prims-instance PrimClassify Prim where- primIsAddfile _ = False- primIsRmfile _ = False- primIsAdddir _ = False- primIsRmdir _ = False- primIsHunk _ = False- primIsMove _ = False- primIsBinary _ = False- primIsTokReplace _ = False- primIsSetpref _ = False- is_filepatch _ = Nothing---- TODO: PrimConstruct makes no sense for V3 prims-instance PrimConstruct Prim where- addfile _ = error "PrimConstruct addfile"- rmfile _ = error "PrimConstruct rmfile"- adddir _ = error "PrimConstruct adddir"- rmdir _ = error "PrimConstruct rmdir"- move _ _ = error "PrimConstruct move"- changepref _ _ _ = error "PrimConstruct changepref"- hunk _ _ _ _ = error "PrimConstruct hunk"- tokreplace _ _ _ _ = error "PrimConstruct tokreplace"- binary _ _ _ = error "PrimConstruct binary"- primFromHunk _ = error "PrimConstruct primFromHunk"- anIdentity = Identity--instance IsHunk Prim where- isHunk _ = Nothing--instance Invert Prim where- invert (BinaryHunk x h) = BinaryHunk x $ invertHunk h- invert (TextHunk x h) = TextHunk x $ invertHunk h- invert (Manifest x y) = Demanifest x y- invert (Demanifest x y) = Manifest x y- invert (Move x y z) = Move x z y- invert Identity = Identity--instance PatchInspect Prim where- -- We don't need this for V3. Slashes are not allowed in Manifest and- -- Demanifest patches and nothing else uses working-copy paths.- listTouchedFiles _ = []-- -- TODO (used for --match 'hunk ...', presumably)- hunkMatches _ _ = False--instance MyEq Prim where- unsafeCompare (BinaryHunk a b) (BinaryHunk c d) = a == c && b `unsafeCompare` d- unsafeCompare (TextHunk a b) (TextHunk c d) = a == c && b `unsafeCompare` d- unsafeCompare (Manifest a b) (Manifest c d) = a == c && b == d- unsafeCompare (Demanifest a b) (Demanifest c d) = a == c && b == d- unsafeCompare Identity Identity = True- unsafeCompare _ _ = False--instance Eq (Prim wX wY) where- (==) = unsafeCompare
− src/Darcs/Patch/Prim/V3/Details.hs
@@ -1,12 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-module Darcs.Patch.Prim.V3.Details- ()- where--import Darcs.Patch.Prim.Class ( PrimDetails(..) )-import Darcs.Patch.Prim.V3.Core ( Prim(..) )----- TODO-instance PrimDetails Prim where- summarizePrim _ = []
− src/Darcs/Patch/Prim/V3/ObjectMap.hs
@@ -1,40 +0,0 @@--- Copyright (C) 2011 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.--module Darcs.Patch.Prim.V3.ObjectMap( UUID(..), Location, Object(..),- ObjectMap(..), DirContent ) where--import Storage.Hashed.Hash( Hash )-import qualified Data.ByteString as BS (ByteString)-import qualified Data.Map as M---newtype UUID = UUID BS.ByteString deriving (Eq, Ord, Show)-type Location = (UUID, BS.ByteString)-type DirContent = M.Map BS.ByteString UUID-data Object (m :: * -> *) = Directory DirContent- | Blob (m BS.ByteString) !Hash--data ObjectMap (m :: * -> *) = ObjectMap { getObject :: UUID -> m (Maybe (Object m))- , putObject :: UUID -> Object m -> m (ObjectMap m)- , listObjects :: m [UUID]- }
− src/Darcs/Patch/Prim/V3/Read.hs
@@ -1,55 +0,0 @@-{-# LANGUAGE CPP, ViewPatterns, OverloadedStrings #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}-module Darcs.Patch.Prim.V3.Read () where-import Darcs.Patch.Read ( ReadPatch(..) )-import Darcs.Patch.ReadMonads-import Darcs.Patch.Prim.Class( PrimRead(..) )-import Darcs.Patch.Prim.V3.Core( Prim(..), Hunk(..) )-import Darcs.Patch.Prim.V3.ObjectMap-import Darcs.Patch.Witnesses.Sealed( seal )--import Control.Applicative ( (<$>) )-import Control.Monad ( liftM, liftM2 )-import qualified Data.ByteString as B-import qualified Data.ByteString.Char8 as BC-import Data.Char ( chr )--#include "impossible.h"--instance PrimRead Prim where- readPrim _ = do skipSpace- choice $ map (liftM seal) [- identity,- hunk "hunk" TextHunk,- hunk "binhunk" BinaryHunk,- manifest "manifest" Manifest,- manifest "demanifest" Demanifest ]-- where manifest kind ctor = liftM2 ctor (patch kind) location- identity = lexString "identity" >> return Identity- patch x = string x >> uuid- uuid = UUID <$> myLex'- filename = encoded- encoded = decodeWhite <$> myLex'- hunktext = skipSpace >> choice [ string "." >> encoded, string "!" >> return B.empty ]- location = liftM2 (,) uuid filename- hunk kind ctor = do uid <- patch kind- offset <- int- old <- hunktext- new <- hunktext- return $ ctor uid (Hunk offset old new)--instance ReadPatch Prim where- readPatch' = readPrim undefined---- XXX a bytestring version of decodeWhite from Darcs.FileName-decodeWhite :: B.ByteString -> B.ByteString-decodeWhite (BC.uncons -> Just ('\\', cs)) =- case BC.break (=='\\') cs of- (theord, BC.uncons -> Just ('\\', rest)) ->- chr (read $ BC.unpack theord) `BC.cons` decodeWhite rest- _ -> error "malformed filename"-decodeWhite (BC.uncons -> Just (c, cs)) = c `BC.cons` decodeWhite cs-decodeWhite (BC.uncons -> Nothing) = BC.empty-decodeWhite _ = impossible-
− src/Darcs/Patch/Prim/V3/Show.hs
@@ -1,67 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE CPP, OverloadedStrings #-}-module Darcs.Patch.Prim.V3.Show- ( showHunk )- where--import Prelude hiding ( pi )--import Data.Char ( isSpace, ord )-import qualified Data.ByteString as B-import qualified Data.ByteString.Char8 as BC--import Darcs.Patch.Format ( PatchListFormat, FileNameFormat(..) )-import Darcs.Patch.Show ( ShowPatchBasic(..), ShowPatch(..) )-import Darcs.Patch.Summary ( plainSummaryPrim, plainSummaryPrims )-import Darcs.Patch.Prim.Class ( PrimShow(..) )-import Darcs.Patch.Prim.V3.Core ( Prim(..), Hunk(..), UUID(..) )-import Darcs.Patch.Prim.V3.Details ()-import Darcs.Util.Printer ( text, packedString, blueText, (<+>), (<>), Doc )--#include "impossible.h"---- TODO this instance shouldn't really be necessary, as Prims aren't used generically-instance PatchListFormat Prim--instance ShowPatchBasic Prim where- showPatch = showPrim OldFormat--instance ShowPatch Prim where- showContextPatch p = return $ showPatch p- summary = plainSummaryPrim- summaryFL = plainSummaryPrims False- thing _ = "change"--instance PrimShow Prim where- showPrim _ (TextHunk u h) = showHunk "hunk" u h- showPrim _ (BinaryHunk u h) = showHunk "binhunk" u h- showPrim _ (Manifest f (d,p)) = showManifest "manifest" d f p- showPrim _ (Demanifest f (d,p)) = showManifest "demanifest" d f p- showPrim _ Identity = blueText "identity"- showPrim _ (Move{}) = bug "show for move not implemented"--showManifest :: String -> UUID -> UUID -> BC.ByteString -> Doc-showManifest txt dir file path = blueText txt <+>- formatUUID file <+>- formatUUID dir <+>- packedString (encodeWhite path)--showHunk :: String -> UUID -> Hunk wX wY -> Doc-showHunk txt uid (Hunk off old new) = blueText txt <+>- formatUUID uid <+>- text (show off) <+>- hunktext old <+>- hunktext new- where hunktext bit | B.null bit = text "!"- | otherwise = text "." <> packedString (encodeWhite bit)--formatUUID :: UUID -> Doc-formatUUID (UUID x) = packedString x---- XXX a bytestring version of encodeWhite from Darcs.FileName-encodeWhite :: B.ByteString -> B.ByteString-encodeWhite = BC.concatMap encode- where encode c- | isSpace c || c == '\\' = B.concat [ "\\", BC.pack $ show $ ord c, "\\" ]- | otherwise = BC.singleton c-
src/Darcs/Patch/Progress.hs view
@@ -6,6 +6,9 @@ , progressRLShowTags ) where +import Prelude ()+import Darcs.Prelude+ import System.IO.Unsafe ( unsafePerformIO ) import Darcs.Patch.Info ( justName, isTag )@@ -38,37 +41,37 @@ -- | Evaluate an 'RL' list and report progress. progressRL :: String -> RL a wX wY -> RL a wX wY progressRL _ NilRL = NilRL-progressRL k xxs@(x :<: xs) =+progressRL k xxs@(xs :<: x) = if xxsLen < minlist then xxs- else startProgress x k xxsLen :<: pl xs+ else pl xs :<: startProgress x k xxsLen where xxsLen = lengthRL xxs pl :: RL a wX wY -> RL a wX wY pl NilRL = NilRL- pl (y:<:NilRL) = unsafePerformIO $ do endTedious k- return (y:<:NilRL)- pl (y:<:ys) = progress k y :<: pl ys+ pl (NilRL:<:y) = unsafePerformIO $ do endTedious k+ return (NilRL:<:y)+ pl (ys:<:y) = pl ys :<: progress k y -- | Evaluate an 'RL' list and report progress. In addition to printing -- the number of patches we got, show the name of the last tag we got.-progressRLShowTags :: String -> RL (PatchInfoAnd p) wX wY- -> RL (PatchInfoAnd p) wX wY+progressRLShowTags :: String -> RL (PatchInfoAnd rt p) wX wY+ -> RL (PatchInfoAnd rt p) wX wY progressRLShowTags _ NilRL = NilRL-progressRLShowTags k xxs@(x :<: xs) =+progressRLShowTags k xxs@(xs :<: x) = if xxsLen < minlist then xxs- else startProgress x k xxsLen :<: pl xs+ else pl xs :<: startProgress x k xxsLen where xxsLen = lengthRL xxs - pl :: RL (PatchInfoAnd p) wX wY -> RL (PatchInfoAnd p) wX wY+ pl :: RL (PatchInfoAnd rt p) wX wY -> RL (PatchInfoAnd rt p) wX wY pl NilRL = NilRL- pl (y :<: NilRL) = unsafePerformIO $ do endTedious k- return (y :<: NilRL)- pl (y :<: ys) =+ pl (NilRL :<: y) = unsafePerformIO $ do endTedious k+ return (NilRL :<: y)+ pl (ys :<: y) = if isTag iy- then finishedOne k ("back to "++ justName iy) y :<: pl ys- else progressKeepLatest k y :<: pl ys+ then pl ys :<: finishedOne k ("back to "++ justName iy) y+ else pl ys :<: progressKeepLatest k y where iy = info y
src/Darcs/Patch/Read.hs view
@@ -23,7 +23,8 @@ readFileName ) where -import Prelude hiding ( pi )+import Prelude ()+import Darcs.Prelude import Darcs.Util.ByteString ( dropSpace ) import qualified Data.ByteString as B (ByteString, null)@@ -39,7 +40,7 @@ import Darcs.Patch.Witnesses.Ordered ( FL(..), RL, reverseFL ) import Darcs.Patch.Witnesses.Sealed ( Sealed(..), mapSeal ) -import Control.Applicative ( (<$>), (<|>) )+import Control.Applicative ( (<|>) ) import Control.Monad ( mzero ) import qualified Data.ByteString.Char8 as BC ( ByteString, unpack )
src/Darcs/Patch/ReadMonads.hs view
@@ -9,13 +9,16 @@ checkConsumes, linesStartingWith, linesStartingWithEndingWith) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Util.ByteString ( dropSpace, breakSpace, breakFirstPS, readIntPS, breakLastPS ) import qualified Data.ByteString as B (null, drop, length, tail, empty, ByteString) import qualified Data.ByteString.Char8 as BC ( uncons, dropWhile, break , splitAt, length, head )-import Control.Applicative ( Alternative(..), Applicative(..), (<$>) )+import Control.Applicative ( Alternative(..) ) import Data.Foldable ( asum ) import Control.Monad ( MonadPlus(..) )
src/Darcs/Patch/Rebase.hs view
@@ -1,84 +1,38 @@ -- Copyright (C) 2009 Ganesh Sittampalam -- -- BSD3-{-# LANGUAGE CPP, GADTs, PatternGuards, TypeOperators, NoMonomorphismRestriction, ViewPatterns, UndecidableInstances #-} module Darcs.Patch.Rebase- ( Rebasing(..), RebaseItem(..), RebaseName(..), RebaseFixup(..)- , simplifyPush, simplifyPushes- , mkSuspended- , takeHeadRebase, takeHeadRebaseFL, takeHeadRebaseRL- , takeAnyRebase, takeAnyRebaseAndTrailingPatches- , countToEdit+ ( takeHeadRebase+ , takeHeadRebaseFL+ , takeAnyRebase+ , takeAnyRebaseAndTrailingPatches ) where -import Darcs.Patch ( RepoPatch )-import Darcs.Patch.Commute ( selfCommuter )-import Darcs.Patch.CommuteFn ( CommuteFn )-import Darcs.Patch.Conflict ( Conflict(..), CommuteNoConflicts(..) )-import Darcs.Patch.Debug ( PatchDebug(..) )-import Darcs.Patch.Effect ( Effect(..) )-import Darcs.Patch.FileHunk ( IsHunk(..) )-import Darcs.Patch.Format ( PatchListFormat(..), ListFormat, copyListFormat )-import Darcs.Patch.Matchable ( Matchable )-import Darcs.Patch.MaybeInternal ( MaybeInternal(..), InternalChecker(..) )-import Darcs.Patch.Merge ( Merge(..) )-import Darcs.Patch.Named ( Named(..), patchcontents, namepatch- , commuterIdNamed- )+import Prelude ()+import Darcs.Prelude++import Darcs.Patch.Named.Wrapped ( WrappedNamed(RebaseP) ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, hopefully )-import Darcs.Patch.Patchy ( Invert(..), Commute(..), Patchy, Apply(..),- ShowPatch(..), ReadPatch(..),- PatchInspect(..)- )-import Darcs.Patch.Prim ( PrimPatchBase, PrimOf, FromPrim(..), FromPrim(..), canonizeFL )-import Darcs.Patch.Read ( bracketedFL )-import Darcs.Patch.Rebase.Fixup ( RebaseFixup(..) )-import Darcs.Patch.Rebase.Name- ( RebaseName(..)- , commutePrimName, commuteNamePrim- )-import Darcs.Patch.Rebase.NameHack ( NameHack(..) )-import Darcs.Patch.Rebase.Recontext ( RecontextRebase(..), RecontextRebase1(..), RecontextRebase2(..) )-import Darcs.Patch.Repair ( Check(..), RepairToFL(..) )+import Darcs.Patch.Rebase.Container ( Suspended(..) )+import Darcs.Patch.RepoType ( RepoType(..), RebaseType(..) ) import Darcs.Patch.Set ( PatchSet(..) )-import Darcs.Patch.Show ( ShowPatchBasic(..) )-import Darcs.Patch.ReadMonads ( ParserM, lexString, myLex' )-import Darcs.Patch.Witnesses.Eq import Darcs.Patch.Witnesses.Ordered import Darcs.Patch.Witnesses.Sealed-import Darcs.Patch.Witnesses.Show- ( Show1(..), Show2(..), showsPrec2- , ShowDict(ShowDictClass), appPrec- )-import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )-import qualified Darcs.Util.Diff as D ( DiffAlgorithm(MyersDiff) )-import Darcs.Util.IsoDate ( getIsoDateTime )-import Darcs.Util.Text ( formatParas )-import Darcs.Util.Printer ( vcat, text, blueText, ($$), (<+>) ) -import Prelude hiding ( pi )-import Control.Applicative ( (<$>), (<|>) )-import Control.Arrow ( (***), second )-import Control.Monad ( when )-import Data.Maybe ( catMaybes )-import qualified Data.ByteString as B ( ByteString )-import qualified Data.ByteString.Char8 as BC ( pack )--#include "impossible.h"- {- Notes Note [Rebase representation] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The entire rebase state is stored in a single Suspended patch.+The entire rebase state is stored in a single Suspended patch+(see Darcs.Patch.Rebase.Container). This is both unnatural and inefficient: - Unnatural because the rebase state is not really a patch and treating it as one requires various hacks: - - It has to be given a fake name: see mkSuspended+ - It has to be given a fake name: see mkRebase - Since 'Named p' actually contains 'FL p', we have to assume/assert that the FL either contains a sequence of Normals@@ -123,437 +77,56 @@ -} --- TODO: move some of the docs of types to individual constructors--- once http://trac.haskell.org/haddock/ticket/43 is fixed.---- |A patch that lives in a repository where a rebase is in--- progress. Such a repository will consist of @Normal@ patches--- along with exactly one @Suspended@ patch.------ Most rebase operations will require the @Suspended@ patch--- to be at the end of the repository.------ @Normal@ represents a normal patch within a respository where a--- rebase is in progress. @Normal p@ is given the same on-disk--- representation as @p@, so a repository can be switched into--- and out of rebasing mode simply by adding or removing a--- @Suspended@ patch and setting the appropriate format flag.------ The single @Suspended@ patch contains the entire rebase--- state, in the form of 'RebaseItem's.------ Note that the witnesses are such that the @Suspended@--- patch has no effect on the context of the rest of the--- repository; in a sense the patches within it are--- dangling off to one side from the main repository.------ See Note [Rebase representation] in the source for a discussion--- of the design choice to embed the rebase state in a single patch.-data Rebasing p wX wY where- Normal :: p wX wY -> Rebasing p wX wY- Suspended :: FL (RebaseItem p) wX wY -> Rebasing p wX wX--instance (Show2 p, Show2 (PrimOf p)) => Show (Rebasing p wX wY) where- showsPrec d (Normal p) =- showParen (d > appPrec) $ showString "Darcs.Patch.Rebase.Normal " . showsPrec2 (appPrec + 1) p- showsPrec d (Suspended p) =- showParen (d > appPrec) $ showString "Darcs.Patch.Rebase.Suspended " . showsPrec2 (appPrec + 1) p--instance (Show2 p, Show2 (PrimOf p)) => Show1 (Rebasing p wX) where- showDict1 = ShowDictClass--instance (Show2 p, Show2 (PrimOf p)) => Show2 (Rebasing p) where- showDict2 = ShowDictClass---- |A single item in the rebase state consists of either--- a patch that is being edited, or a fixup that adjusts--- the context so that a subsequent patch that is being edited--- \"makes sense\".------ @ToEdit@ holds a patch that is being edited. The name ('PatchInfo') of--- the patch will typically be the name the patch had before--- it was added to the rebase state; if it is moved back--- into the repository it must be given a fresh name to account--- for the fact that it will not necessarily have the same--- dependencies as the original patch. This is typically--- done by changing the @Ignore-This@ junk.------ @Fixup@ adjusts the context so that a subsequent @ToEdit@ patch--- is correct. Where possible, @Fixup@ changes are commuted--- as far as possible into the rebase state, so any remaining--- ones will typically cause a conflict when the @ToEdit@ patch--- is moved back into the repository.-data RebaseItem p wX wY where- ToEdit :: Named p wX wY -> RebaseItem p wX wY- Fixup :: RebaseFixup p wX wY -> RebaseItem p wX wY--instance (Show2 p, Show2 (PrimOf p)) => Show (RebaseItem p wX wY) where- showsPrec d (ToEdit p) =- showParen (d > appPrec) $ showString "ToEdit " . showsPrec2 (appPrec + 1) p- showsPrec d (Fixup p) =- showParen (d > appPrec) $ showString "Fixup " . showsPrec2 (appPrec + 1) p--instance (Show2 p, Show2 (PrimOf p)) => Show1 (RebaseItem p wX) where- showDict1 = ShowDictClass--instance (Show2 p, Show2 (PrimOf p)) => Show2 (RebaseItem p) where- showDict2 = ShowDictClass--countToEdit :: FL (RebaseItem p) wX wY -> Int-countToEdit NilFL = 0-countToEdit (ToEdit _ :>: ps) = 1 + countToEdit ps-countToEdit (_ :>: ps) = countToEdit ps--commuterRebasing :: (PrimPatchBase p, Commute p, Invert p, FromPrim p, Effect p)- => D.DiffAlgorithm -> CommuteFn p p- -> CommuteFn (Rebasing p) (Rebasing p)-commuterRebasing _ commuter (Normal p :> Normal q) = do- q' :> p' <- commuter (p :> q)- return (Normal q' :> Normal p')---- Two rebases in sequence must have the same starting context,--- so they should trivially commute.--- This case shouldn't actually happen since each repo only has--- a single Suspended patch.-commuterRebasing _ _ (p@(Suspended _) :> q@(Suspended _)) =- return (q :> p)--commuterRebasing da _ (Normal p :> Suspended qs) =- return (unseal Suspended (addFixup da p qs) :> Normal p)--commuterRebasing da _ (Suspended ps :> Normal q) =- return (Normal q :> unseal Suspended (addFixup da (invert q) ps))---instance (PrimPatchBase p, FromPrim p, Effect p, Invert p, Commute p) => Commute (Rebasing p) where- commute = commuterRebasing D.MyersDiff commute--instance (PrimPatchBase p, FromPrim p, Effect p, Commute p) => NameHack (Rebasing p) where- nameHack da = Just (pushIn . AddName, pushIn . DelName)- where- pushIn :: RebaseName p wX wX -> FL (Rebasing p) wX wY -> FL (Rebasing p) wX wY- pushIn n (Suspended ps :>: NilFL) = unseal (\qs -> Suspended qs :>: NilFL) (simplifyPush da (NameFixup n) ps)- pushIn _ ps = ps--instance (PrimPatchBase p, FromPrim p, Effect p, Invert p, Commute p, CommuteNoConflicts p) => CommuteNoConflicts (Rebasing p) where- commuteNoConflicts = commuterRebasing D.MyersDiff commuteNoConflicts--instance (PrimPatchBase p, FromPrim p, Effect p, Invert p, Merge p) => Merge (Rebasing p) where- merge (Normal p :\/: Normal q) = case merge (p :\/: q) of- q' :/\: p' -> Normal q' :/\: Normal p'- merge (p@(Suspended _) :\/: q@(Suspended _)) = q :/\: p- merge (Normal p :\/: Suspended qs) = unseal Suspended (addFixup D.MyersDiff (invert p) qs) :/\: Normal p- merge (Suspended ps :\/: Normal q) = Normal q :/\: unseal Suspended (addFixup D.MyersDiff (invert q) ps)--instance (PrimPatchBase p, PatchInspect p) => PatchInspect (Rebasing p) where- listTouchedFiles (Normal p) = listTouchedFiles p- listTouchedFiles (Suspended ps) = concat $ mapFL ltfItem ps- where ltfItem :: RebaseItem p wX wY -> [FilePath]- ltfItem (ToEdit q) = listTouchedFiles q- ltfItem (Fixup (PrimFixup q)) = listTouchedFiles q- ltfItem (Fixup (NameFixup _)) = []- hunkMatches f (Normal p) = hunkMatches f p- hunkMatches f (Suspended ps) = or $ mapFL hmItem ps- where hmItem :: RebaseItem p wA wB -> Bool- hmItem (ToEdit q) = hunkMatches f q- hmItem (Fixup (PrimFixup q)) = hunkMatches f q- hmItem (Fixup (NameFixup _)) = False--instance Invert p => Invert (Rebasing p) where- invert (Normal p) = Normal (invert p)- invert (Suspended ps) = Suspended ps -- TODO is this sensible?--instance Effect p => Effect (Rebasing p) where- effect (Normal p) = effect p- effect (Suspended _) = NilFL--instance (PrimPatchBase p, PatchListFormat p, Patchy p, FromPrim p, Conflict p, Effect p, CommuteNoConflicts p, IsHunk p)- => Patchy (Rebasing p)--instance PatchDebug p => PatchDebug (Rebasing p)--instance ( PrimPatchBase p, PatchListFormat p, Patchy p- , FromPrim p, Conflict p, Effect p- , PatchInspect p- , CommuteNoConflicts p, IsHunk p- )- => Matchable (Rebasing p)--instance (Conflict p, FromPrim p, Effect p, Invert p, Commute p) => Conflict (Rebasing p) where- resolveConflicts (Normal p) = resolveConflicts p- resolveConflicts (Suspended _) = []--instance Apply p => Apply (Rebasing p) where- type ApplyState (Rebasing p) = ApplyState p- apply (Normal p) = apply p- apply (Suspended _) = return ()--instance (PrimPatchBase p, PatchListFormat p, ShowPatchBasic p) => ShowPatchBasic (Rebasing p) where- showPatch (Normal p) = showPatch p- showPatch (Suspended ps) = blueText "rebase" <+> text "0.0" <+> blueText "{"- $$ vcat (mapFL showPatch ps)- $$ blueText "}"--instance (PrimPatchBase p, PatchListFormat p, Apply p, CommuteNoConflicts p, Conflict p, IsHunk p, ShowPatch p)- => ShowPatch (Rebasing p) where-- summary (Normal p) = summary p- summary (Suspended ps) = summaryFL ps- summaryFL ps = vcat (mapFL summary ps) -- TODO sort out summaries properly, considering expected conflicts--instance (PrimPatchBase p, PatchListFormat p, ShowPatchBasic p) => ShowPatchBasic (RebaseItem p) where- showPatch (ToEdit p) = blueText "rebase-toedit" <+> blueText "(" $$ showPatch p $$ blueText ")"- showPatch (Fixup (PrimFixup p)) = blueText "rebase-fixup" <+> blueText "(" $$ showPatch p $$ blueText ")"- showPatch (Fixup (NameFixup p)) = blueText "rebase-name" <+> blueText "(" $$ showPatch p $$ blueText ")"--instance (PrimPatchBase p, PatchListFormat p, Apply p, CommuteNoConflicts p, Conflict p, IsHunk p, ShowPatch p)- => ShowPatch (RebaseItem p) where-- summary (ToEdit p) = summary p- summary (Fixup (PrimFixup p)) = summary p- summary (Fixup (NameFixup n)) = summary n- summaryFL ps = vcat (mapFL summary ps) -- TODO sort out summaries properly, considering expected conflicts---instance (PrimPatchBase p, PatchListFormat p, ReadPatch p) => ReadPatch (RebaseItem p) where- readPatch' = mapSeal ToEdit <$> readWith (BC.pack "rebase-toedit") <|>- mapSeal (Fixup . PrimFixup) <$> readWith (BC.pack "rebase-fixup" ) <|>- mapSeal (Fixup . NameFixup) <$> readWith (BC.pack "rebase-name" )- where readWith :: forall m q wX . (ParserM m, ReadPatch q) => B.ByteString -> m (Sealed (q wX))- readWith str = do lexString str- lexString (BC.pack "(")- res <- readPatch'- lexString (BC.pack ")")- return res--instance PrimPatchBase p => PrimPatchBase (Rebasing p) where- type PrimOf (Rebasing p) = PrimOf p--instance (PrimPatchBase p, PatchListFormat p, ReadPatch p) => ReadPatch (Rebasing p) where- readPatch' =- do lexString (BC.pack "rebase")- version <- myLex'- when (version /= BC.pack "0.0") $ error $ "can't handle rebase version " ++ show version- (lexString (BC.pack "{}") >> return (seal (Suspended NilFL)))- <|>- (unseal (Sealed . Suspended) <$> bracketedFL readPatch' '{' '}')- <|> mapSeal Normal <$> readPatch'--instance IsHunk p => IsHunk (Rebasing p) where- isHunk (Normal p) = isHunk p- isHunk (Suspended _) = Nothing--instance FromPrim p => FromPrim (Rebasing p) where- fromPrim p = Normal (fromPrim p)--instance Check p => Check (Rebasing p) where- isInconsistent (Normal p) = isInconsistent p- isInconsistent (Suspended ps) =- case catMaybes (mapFL isInconsistent ps) of- [] -> Nothing- xs -> Just (vcat xs)--instance Check p => Check (RebaseItem p) where- isInconsistent (Fixup _) = Nothing- isInconsistent (ToEdit p) = isInconsistent p--instance RepairToFL p => RepairToFL (Rebasing p) where- applyAndTryToFixFL (Normal p) = fmap (second $ mapFL_FL Normal) <$> applyAndTryToFixFL p- -- TODO: ideally we would apply ps in a sandbox to check the individual patches- -- are consistent with each other.- applyAndTryToFixFL (Suspended ps) =- return . fmap (unlines *** ((:>: NilFL) . Suspended)) $ repairInternal ps---- Just repair the internals of the patch, without applying it to anything--- or checking against an external context.--- Included for the internal implementation of applyAndTryToFixFL for Rebasing,--- consider either generalising it for use everywhere, or removing once--- the implementation works in a sandbox and thus can use the "full" Repair on the--- contained patches.-class RepairInternalFL p where- repairInternalFL :: p wX wY -> Maybe ([String], FL p wX wY)--class RepairInternal p where- repairInternal :: p wX wY -> Maybe ([String], p wX wY)--instance RepairInternalFL p => RepairInternal (FL p) where- repairInternal NilFL = Nothing- repairInternal (x :>: ys) =- case (repairInternalFL x, repairInternal ys) of- (Nothing , Nothing) -> Nothing- (Just (e, rxs), Nothing) -> Just (e , rxs +>+ ys )- (Nothing , Just (e', rys)) -> Just (e' , x :>: rys)- (Just (e, rxs), Just (e', rys)) -> Just (e ++ e', rxs +>+ rys)--instance RepairInternalFL (RebaseItem p) where- repairInternalFL (ToEdit _) = Nothing- repairInternalFL (Fixup p) = fmap (second $ mapFL_FL Fixup) $ repairInternalFL p--instance RepairInternalFL (RebaseFixup p) where- repairInternalFL (PrimFixup _) = Nothing- repairInternalFL (NameFixup _) = Nothing--instance PatchListFormat p => PatchListFormat (Rebasing p) where- patchListFormat = copyListFormat (patchListFormat :: ListFormat p)--instance RepoPatch p => RepoPatch (Rebasing p)--instance (Commute p, PrimPatchBase p, FromPrim p, Effect p) => RecontextRebase (Rebasing p) where- recontextRebase = Just (RecontextRebase1 recontext)- where- recontext :: forall wY wZ . Named (Rebasing p) wY wZ -> (EqCheck wY wZ, RecontextRebase2 (Rebasing p) wY wZ)-- recontext (patchcontents -> (Suspended ps :>: NilFL))- = (IsEq, RecontextRebase2 (\fixups -> unseal mkSuspended(simplifyPushes D.MyersDiff (mapFL_FL translateFixup fixups) ps)))-- recontext _ = (NotEq, bug "trying to recontext rebase without rebase patch at head")-- translateFixup :: RebaseFixup (Rebasing p) wX wY -> RebaseFixup p wX wY- translateFixup (PrimFixup p) = PrimFixup p- translateFixup (NameFixup n) = NameFixup (translateName n)-- translateName :: RebaseName (Rebasing p) wX wY -> RebaseName p wX wY- translateName (AddName name) = AddName name- translateName (DelName name) = DelName name- translateName (Rename old new) = Rename old new--instance MaybeInternal (Rebasing p) where- patchInternalChecker = Just (InternalChecker rebaseIsInternal)- where rebaseIsInternal :: FL (Rebasing p) wX wY -> EqCheck wX wY- rebaseIsInternal (Suspended _ :>: NilFL) = IsEq- rebaseIsInternal _ = NotEq--addFixup :: (PrimPatchBase p, Commute p, FromPrim p, Effect p) => D.DiffAlgorithm -> p wX wY -> FL (RebaseItem p) wY wZ -> Sealed (FL (RebaseItem p) wX)-addFixup da p = simplifyPushes da (mapFL_FL PrimFixup (effect p))--canonizeNamePair :: (RebaseName p :> RebaseName p) wX wY -> FL (RebaseName p) wX wY-canonizeNamePair (AddName n :> Rename old new) | n == old = AddName new :>: NilFL-canonizeNamePair (Rename old new :> DelName n) | n == new = DelName old :>: NilFL-canonizeNamePair (Rename old1 new1 :> Rename old2 new2) | new1 == old2 = Rename old1 new2 :>: NilFL-canonizeNamePair (n1 :> n2) = n1 :>: n2 :>: NilFL---- |Given a list of rebase items, try to push a new fixup as far as possible into--- the list as possible, using both commutation and coalescing. If the fixup--- commutes past all the 'ToEdit' patches then it is dropped entirely.-simplifyPush :: (PrimPatchBase p, Commute p, FromPrim p, Effect p)- => D.DiffAlgorithm -> RebaseFixup p wX wY -> FL (RebaseItem p) wY wZ -> Sealed (FL (RebaseItem p) wX)--simplifyPush _ _f NilFL = Sealed NilFL--simplifyPush da (PrimFixup f1) (Fixup (PrimFixup f2) :>: ps)- | IsEq <- isInverse = Sealed ps- | otherwise- = case commute (f1 :> f2) of- Nothing -> Sealed (mapFL_FL (Fixup . PrimFixup) (canonizeFL da (f1 :>: f2 :>: NilFL)) +>+ ps)- Just (f2' :> f1') -> mapSeal (Fixup (PrimFixup f2') :>:) (simplifyPush da (PrimFixup f1') ps)- where isInverse = invert f1 =\/= f2--simplifyPush da (PrimFixup f) (Fixup (NameFixup n) :>: ps)- = case commutePrimName (f :> n) of- n' :> f' -> mapSeal (Fixup (NameFixup n') :>:) (simplifyPush da (PrimFixup f') ps)--simplifyPush da (PrimFixup f) (ToEdit e :>: ps)- = case commuterIdNamed selfCommuter (fromPrim f :> e) of- Nothing -> Sealed (Fixup (PrimFixup f) :>: ToEdit e :>: ps)- Just (e' :> f') -> mapSeal (ToEdit e' :>:) (simplifyPushes da (mapFL_FL PrimFixup (effect f')) ps)--simplifyPush da (NameFixup n1) (Fixup (NameFixup n2) :>: ps)- | IsEq <- isInverse = Sealed ps- | otherwise- = case commute (n1 :> n2) of- Nothing -> Sealed (mapFL_FL (Fixup . NameFixup) (canonizeNamePair (n1 :> n2)) +>+ ps)- Just (n2' :> n1') -> mapSeal (Fixup (NameFixup n2') :>:) (simplifyPush da (NameFixup n1') ps)- where isInverse = invert n1 =\/= n2--simplifyPush da (NameFixup n) (Fixup (PrimFixup f) :>: ps) =- case commuteNamePrim (n :> f) of- f' :> n' -> mapSeal (Fixup (PrimFixup f') :>:) (simplifyPush da (NameFixup n') ps)--simplifyPush da (NameFixup (AddName an)) (p@(ToEdit (NamedP pn deps _)) :>: ps)- | an == pn = impossible- | an `elem` deps = Sealed (Fixup (NameFixup (AddName an)) :>: p :>: ps)- | otherwise = mapSeal (unsafeCoerceP p :>:) (simplifyPush da (NameFixup (AddName an)) ps)-simplifyPush da (NameFixup (DelName dn)) (p@(ToEdit (NamedP pn deps _)) :>: ps)- -- this case can arise if a patch is suspended then a fresh copy is pulled from another repo- | dn == pn = Sealed (Fixup (NameFixup (DelName dn)) :>: p :>: ps)- | dn `elem` deps = impossible- | otherwise = mapSeal (unsafeCoerceP p :>:) (simplifyPush da (NameFixup (DelName dn)) ps)-simplifyPush da (NameFixup (Rename old new)) (p@(ToEdit (NamedP pn deps body)) :>: ps)- | old == pn = impossible- | new == pn = impossible- | old `elem` deps = impossible- | new `elem` deps =- let newdeps = map (\dep -> if new == dep then old else dep) deps- in mapSeal (ToEdit (NamedP pn newdeps (unsafeCoerceP body)) :>:) (simplifyPush da (NameFixup (Rename old new)) ps)- | otherwise = mapSeal (unsafeCoerceP p :>:) (simplifyPush da (NameFixup (Rename old new)) ps)---- |Like 'simplifyPush' but for a list of fixups.-simplifyPushes :: (PrimPatchBase p, Commute p, FromPrim p, Effect p)- => D.DiffAlgorithm -> FL (RebaseFixup p) wX wY -> FL (RebaseItem p) wY wZ -> Sealed (FL (RebaseItem p) wX)-simplifyPushes _ NilFL ps = Sealed ps-simplifyPushes da (f :>: fs) ps = unseal (simplifyPush da f) (simplifyPushes da fs ps)--mkSuspended :: FL (RebaseItem p) wX wY -> IO (Named (Rebasing p) wX wX)-mkSuspended ps = do- let name = "DO NOT TOUCH: Rebase patch"- let desc = formatParas 72- ["This patch is an internal implementation detail of rebase, used to store suspended patches, " ++- "and should not be visible in the user interface. Please report a bug if a darcs " ++- "command is showing you this patch."]- date <- getIsoDateTime- let author = "Invalid <invalid@invalid>"- namepatch date name author desc (Suspended ps :>: NilFL)- -- |given the repository contents, get the rebase container patch, and its contents -- The rebase patch can be anywhere in the repository and is returned without being -- commuted to the end.-takeAnyRebase :: PatchSet (Rebasing p) wA wB- -> (Sealed2 (PatchInfoAnd (Rebasing p)),- Sealed2 (FL (RebaseItem p)))-takeAnyRebase (PatchSet NilRL _) =+takeAnyRebase :: PatchSet ('RepoType 'IsRebase) p wA wB+ -> (Sealed2 (PatchInfoAnd ('RepoType 'IsRebase) p),+ Sealed2 (Suspended p))+takeAnyRebase (PatchSet _ NilRL) = -- it should never be behind a tag so we can stop now error "internal error: no suspended patch found"-takeAnyRebase (PatchSet (p :<: ps) pss)- | Suspended rs :>: NilFL <- patchcontents (hopefully p) = (Sealed2 p, Sealed2 rs)- | otherwise = takeAnyRebase (PatchSet ps pss)+takeAnyRebase (PatchSet pss (ps :<: p))+ | RebaseP _ rs <- hopefully p = (Sealed2 p, Sealed2 rs)+ | otherwise = takeAnyRebase (PatchSet pss ps) -- |given the repository contents, get the rebase container patch, its contents, and the -- rest of the repository contents. Commutes the patch to the end of the repository -- if necessary. The rebase patch must be at the head of the repository. takeAnyRebaseAndTrailingPatches- :: PatchSet (Rebasing p) wA wB- -> FlippedSeal (PatchInfoAnd (Rebasing p) :> RL (PatchInfoAnd (Rebasing p))) wB-takeAnyRebaseAndTrailingPatches (PatchSet NilRL _) =+ :: PatchSet ('RepoType 'IsRebase) p wA wB+ -> FlippedSeal (PatchInfoAnd ('RepoType 'IsRebase) p :> RL (PatchInfoAnd ('RepoType 'IsRebase) p)) wB+takeAnyRebaseAndTrailingPatches (PatchSet _ NilRL) = -- it should never be behind a tag so we can stop now error "internal error: no suspended patch found"-takeAnyRebaseAndTrailingPatches (PatchSet (p :<: ps) pss)- | Suspended _ :>: NilFL <- patchcontents (hopefully p) = FlippedSeal (p :> NilRL)- | otherwise = case takeAnyRebaseAndTrailingPatches (PatchSet ps pss) of- FlippedSeal (r :> ps') -> FlippedSeal (r :> (p :<: ps'))+takeAnyRebaseAndTrailingPatches (PatchSet pss (ps :<: p))+ | RebaseP _ _ <- hopefully p = FlippedSeal (p :> NilRL)+ | otherwise = case takeAnyRebaseAndTrailingPatches (PatchSet pss ps) of+ FlippedSeal (r :> ps') -> FlippedSeal (r :> (ps' :<: p)) -- |given the repository contents, get the rebase container patch, its contents, and the -- rest of the repository contents. The rebase patch must be at the head of the repository.-takeHeadRebase :: PatchSet (Rebasing p) wA wB- -> (PatchInfoAnd (Rebasing p) wB wB,- Sealed (FL (RebaseItem p) wB),- PatchSet (Rebasing p) wA wB)-takeHeadRebase (PatchSet NilRL _) = error "internal error: must have a rebase container patch at end of repository"-takeHeadRebase (PatchSet (p :<: ps) pss)- | Suspended rs :>: NilFL <- patchcontents (hopefully p) = (p, Sealed rs, PatchSet ps pss)+takeHeadRebase :: PatchSet ('RepoType 'IsRebase) p wA wB+ -> (PatchInfoAnd ('RepoType 'IsRebase) p wB wB,+ Suspended p wB wB,+ PatchSet ('RepoType 'IsRebase) p wA wB)+takeHeadRebase (PatchSet _ NilRL) = error "internal error: must have a rebase container patch at end of repository"+takeHeadRebase (PatchSet pss (ps :<: p))+ | RebaseP _ rs <- hopefully p = (p, rs, PatchSet pss ps) | otherwise = error "internal error: must have a rebase container patch at end of repository" -takeHeadRebaseRL :: RL (PatchInfoAnd (Rebasing p)) wA wB- -> (PatchInfoAnd (Rebasing p) wB wB,- Sealed (FL (RebaseItem p) wB),- RL (PatchInfoAnd (Rebasing p)) wA wB)+takeHeadRebaseRL :: RL (PatchInfoAnd ('RepoType 'IsRebase) p) wA wB+ -> (PatchInfoAnd ('RepoType 'IsRebase) p wB wB,+ Suspended p wB wB,+ RL (PatchInfoAnd ('RepoType 'IsRebase) p) wA wB) takeHeadRebaseRL NilRL = error "internal error: must have a suspended patch at end of repository"-takeHeadRebaseRL (p :<: ps)- | Suspended rs :>: NilFL <- patchcontents (hopefully p) = (p, Sealed rs, ps)+takeHeadRebaseRL (ps :<: p)+ | RebaseP _ rs <- hopefully p = (p, rs, ps) | otherwise = error "internal error: must have a suspended patch at end of repository" -takeHeadRebaseFL :: FL (PatchInfoAnd (Rebasing p)) wA wB- -> (PatchInfoAnd (Rebasing p) wB wB,- Sealed (FL (RebaseItem p) wB),- FL (PatchInfoAnd (Rebasing p)) wA wB)+takeHeadRebaseFL :: FL (PatchInfoAnd ('RepoType 'IsRebase) p) wA wB+ -> (PatchInfoAnd ('RepoType 'IsRebase) p wB wB,+ Suspended p wB wB,+ FL (PatchInfoAnd ('RepoType 'IsRebase) p) wA wB) takeHeadRebaseFL ps = let (a, b, c) = takeHeadRebaseRL (reverseFL ps) in (a, b, reverseRL c)
+ src/Darcs/Patch/Rebase/Container.hs view
@@ -0,0 +1,209 @@+{-# LANGUAGE UndecidableInstances, StandaloneDeriving #-}+module Darcs.Patch.Rebase.Container+ ( Suspended(..)+ , countToEdit, simplifyPush, simplifyPushes+ , addFixupsToSuspended, removeFixupsFromSuspended+ ) where++import Prelude ()+import Darcs.Prelude++import Darcs.Patch.Conflict ( Conflict(..), CommuteNoConflicts(..) )+import Darcs.Patch.Effect ( Effect(..) )+import Darcs.Patch.FileHunk ( IsHunk(..) )+import Darcs.Patch.Format ( PatchListFormat(..) )+import Darcs.Patch.Invert ( invert )+import Darcs.Patch.Named ( Named )+import Darcs.Patch.Patchy ( Commute(..), Apply(..),+ ShowPatch(..), ReadPatch(..),+ PatchInspect(..)+ )+import Darcs.Patch.Prim ( PrimPatchBase, PrimOf, FromPrim(..), FromPrim(..) )+import Darcs.Patch.Read ( bracketedFL )+import Darcs.Patch.Rebase.Fixup ( RebaseFixup(..), namedToFixups )+import Darcs.Patch.Rebase.Item ( RebaseItem(..) )+import qualified Darcs.Patch.Rebase.Item as Item ( countToEdit, simplifyPush, simplifyPushes )+import Darcs.Patch.Repair ( Check(..), Repair(..), RepairToFL(..) )+import Darcs.Patch.Show ( ShowPatchBasic(..) )+import Darcs.Patch.ReadMonads ( lexString, myLex' )+import Darcs.Patch.Witnesses.Ordered+import Darcs.Patch.Witnesses.Sealed+import Darcs.Patch.Witnesses.Show+ ( Show1(..), Show2(..)+ , ShowDict(ShowDictClass)+ )+import Darcs.Util.Printer ( vcat, text, blueText, ($$), (<+>) )+import qualified Darcs.Util.Diff as D ( DiffAlgorithm(MyersDiff) )++import Control.Applicative ( (<|>) )+import Control.Arrow ( (***), second )+import Control.Monad ( when )+import Data.Maybe ( catMaybes )+import qualified Data.ByteString.Char8 as BC ( pack )+++-- TODO: move some of the docs of types to individual constructors+-- once http://trac.haskell.org/haddock/ticket/43 is fixed.++-- |A patch that lives in a repository where a rebase is in+-- progress. Such a repository will consist of @Normal@ patches+-- along with exactly one @Suspended@ patch.+--+-- Most rebase operations will require the @Suspended@ patch+-- to be at the end of the repository.+--+-- @Normal@ represents a normal patch within a respository where a+-- rebase is in progress. @Normal p@ is given the same on-disk+-- representation as @p@, so a repository can be switched into+-- and out of rebasing mode simply by adding or removing a+-- @Suspended@ patch and setting the appropriate format flag.+--+-- The single @Suspended@ patch contains the entire rebase+-- state, in the form of 'RebaseItem's.+--+-- Note that the witnesses are such that the @Suspended@+-- patch has no effect on the context of the rest of the+-- repository; in a sense the patches within it are+-- dangling off to one side from the main repository.+--+-- See Note [Rebase representation] in the 'Darcs.Patch.Rebase' for+-- a discussion of the design choice to embed the rebase state in a+-- single patch.+data Suspended p wX wY where+ Items :: FL (RebaseItem p) wX wY -> Suspended p wX wX++deriving instance (Show2 p, Show2 (PrimOf p)) => Show (Suspended p wX wY)++instance (Show2 p, Show2 (PrimOf p)) => Show1 (Suspended p wX) where+ showDict1 = ShowDictClass++instance (Show2 p, Show2 (PrimOf p)) => Show2 (Suspended p) where+ showDict2 = ShowDictClass++instance (PrimPatchBase p, PatchInspect p) => PatchInspect (Suspended p) where+ listTouchedFiles (Items ps) = listTouchedFiles ps+ hunkMatches f (Items ps) = hunkMatches f ps++instance Effect (Suspended p) where+ effect (Items _) = NilFL++instance Conflict p => Conflict (Suspended p) where+ resolveConflicts _ = []+ conflictedEffect _ = []++instance Apply p => Apply (Suspended p) where+ type ApplyState (Suspended p) = ApplyState p+ apply _ = return ()++instance (PrimPatchBase p, PatchListFormat p, ShowPatchBasic p) => ShowPatchBasic (Suspended p) where+ showPatch (Items ps)+ = blueText "rebase" <+> text "0.0" <+> blueText "{"+ $$ vcat (mapFL showPatch ps)+ $$ blueText "}"++instance (PrimPatchBase p, PatchListFormat p, Apply p, CommuteNoConflicts p, Conflict p, IsHunk p, ShowPatch p) => ShowPatch (Suspended p) where++ showContextPatch s = return $ showPatch s++ description s = showPatch s++ summary (Items ps) = summaryFL ps++ summaryFL ps = vcat (mapFL summary ps)++instance PrimPatchBase p => PrimPatchBase (Suspended p) where+ type PrimOf (Suspended p) = PrimOf p++instance (PrimPatchBase p, PatchListFormat p, ReadPatch p) => ReadPatch (Suspended p) where+ readPatch' =+ do lexString (BC.pack "rebase")+ version <- myLex'+ when (version /= BC.pack "0.0") $ error $ "can't handle rebase version " ++ show version+ (lexString (BC.pack "{}") >> return (seal (Items NilFL)))+ <|>+ (unseal (Sealed . Items) <$> bracketedFL readPatch' '{' '}')++instance Check p => Check (Suspended p) where+ isInconsistent (Items ps) =+ case catMaybes (mapFL isInconsistent ps) of+ [] -> Nothing+ xs -> Just (vcat xs)++instance Apply p => Repair (Suspended p) where+ applyAndTryToFix (Items ps) =+ -- TODO: ideally we would apply ps in a sandbox to check the individual patches+ -- are consistent with each other.+ return . fmap (unlines *** Items) $ repairInternal ps++instance Apply p => RepairToFL (Suspended p) where+ applyAndTryToFixFL s = fmap (second $ (:>: NilFL)) <$> applyAndTryToFix s++-- Just repair the internals of the patch, without applying it to anything+-- or checking against an external context.+-- Included for the internal implementation of applyAndTryToFixFL for Rebasing,+-- consider either generalising it for use everywhere, or removing once+-- the implementation works in a sandbox and thus can use the "full" Repair on the+-- contained patches.+class RepairInternalFL p where+ repairInternalFL :: p wX wY -> Maybe ([String], FL p wX wY)++class RepairInternal p where+ repairInternal :: p wX wY -> Maybe ([String], p wX wY)++instance RepairInternalFL p => RepairInternal (FL p) where+ repairInternal NilFL = Nothing+ repairInternal (x :>: ys) =+ case (repairInternalFL x, repairInternal ys) of+ (Nothing , Nothing) -> Nothing+ (Just (e, rxs), Nothing) -> Just (e , rxs +>+ ys )+ (Nothing , Just (e', rys)) -> Just (e' , x :>: rys)+ (Just (e, rxs), Just (e', rys)) -> Just (e ++ e', rxs +>+ rys)++instance RepairInternalFL (RebaseItem p) where+ repairInternalFL (ToEdit _) = Nothing+ repairInternalFL (Fixup p) = fmap (second $ mapFL_FL Fixup) $ repairInternalFL p++instance RepairInternalFL (RebaseFixup p) where+ repairInternalFL (PrimFixup _) = Nothing+ repairInternalFL (NameFixup _) = Nothing++countToEdit :: Suspended p wX wY -> Int+countToEdit (Items ps) = Item.countToEdit ps++onSuspended+ :: (forall wZ . FL (RebaseItem p) wY wZ -> Sealed (FL (RebaseItem p) wX))+ -> Suspended p wY wY+ -> Suspended p wX wX+onSuspended f (Items ps) = unseal Items (f ps)++-- |add fixups for the name and effect of a patch to a 'Suspended'+addFixupsToSuspended+ :: (PrimPatchBase p, Commute p, FromPrim p, Effect p)+ => Named p wX wY+ -> Suspended p wY wY+ -> Suspended p wX wX+addFixupsToSuspended p = simplifyPushes D.MyersDiff (namedToFixups p)++-- |remove fixups (actually, add their inverse) for the name and effect of a patch to a 'Suspended'+removeFixupsFromSuspended+ :: (PrimPatchBase p, Commute p, FromPrim p, Effect p)+ => Named p wX wY+ -> Suspended p wX wX+ -> Suspended p wY wY+removeFixupsFromSuspended p = simplifyPushes D.MyersDiff (invert (namedToFixups p))++simplifyPush+ :: (PrimPatchBase p, Commute p, FromPrim p, Effect p)+ => D.DiffAlgorithm+ -> RebaseFixup p wX wY+ -> Suspended p wY wY+ -> Suspended p wX wX+simplifyPush da fixups = onSuspended (Item.simplifyPush da fixups)++simplifyPushes+ :: (PrimPatchBase p, Commute p, FromPrim p, Effect p)+ => D.DiffAlgorithm+ -> FL (RebaseFixup p) wX wY+ -> Suspended p wY wY+ -> Suspended p wX wX+simplifyPushes da fixups = onSuspended (Item.simplifyPushes da fixups)
src/Darcs/Patch/Rebase/Fixup.hs view
@@ -6,9 +6,12 @@ module Darcs.Patch.Rebase.Fixup ( RebaseFixup(..) , commuteNamedFixup, commuteFixupNamed, commuteNamedFixups- , flToNamesPrims+ , flToNamesPrims, namedToFixups ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Apply ( Apply(..) ) import Darcs.Patch.Commute ( Commute(..), selfCommuter ) import Darcs.Patch.CommuteFn ( totalCommuterIdFL )@@ -19,7 +22,7 @@ import Darcs.Patch.Named ( Named(..), commuterNamedId, commuterIdNamed ) import Darcs.Patch.Prim ( PrimPatchBase, PrimOf ) import Darcs.Patch.Rebase.Name- ( RebaseName+ ( RebaseName(..) , commuteNamedName, commuteNameNamed , commutePrimName, commuteNamePrim )@@ -36,6 +39,9 @@ data RebaseFixup p wX wY where PrimFixup :: PrimOf p wX wY -> RebaseFixup p wX wY NameFixup :: RebaseName p wX wY -> RebaseFixup p wX wY++namedToFixups :: Effect p => Named p wX wY -> FL (RebaseFixup p) wX wY+namedToFixups (NamedP p _ contents) = NameFixup (AddName p) :>: mapFL_FL PrimFixup (effect contents) instance Show2 (PrimOf p) => Show (RebaseFixup p wX wY) where showsPrec d (PrimFixup p) =
+ src/Darcs/Patch/Rebase/Item.hs view
@@ -0,0 +1,183 @@+{-# LANGUAGE CPP, UndecidableInstances #-}+module Darcs.Patch.Rebase.Item+ ( RebaseItem(..)+ , simplifyPush, simplifyPushes+ , countToEdit+ ) where++import Prelude ()+import Darcs.Prelude++import Darcs.Patch.Commute ( selfCommuter )+import Darcs.Patch.Conflict ( Conflict(..), CommuteNoConflicts(..) )+import Darcs.Patch.Effect ( Effect(..) )+import Darcs.Patch.FileHunk ( IsHunk(..) )+import Darcs.Patch.Format ( PatchListFormat(..) )+import Darcs.Patch.Named ( Named(..), commuterIdNamed )+import Darcs.Patch.Patchy ( Invert(..), Commute(..), Apply(..)+ , ShowPatch(..), ReadPatch(..)+ , PatchInspect(..)+ )+import Darcs.Patch.Prim ( PrimPatchBase, PrimOf, FromPrim(..), FromPrim(..), canonizeFL )+import Darcs.Patch.Rebase.Fixup ( RebaseFixup(..) )+import Darcs.Patch.Rebase.Name+ ( RebaseName(..)+ , commutePrimName, commuteNamePrim+ , canonizeNamePair+ )+import Darcs.Patch.Repair ( Check(..) )+import Darcs.Patch.Show ( ShowPatchBasic(..) )+import Darcs.Patch.ReadMonads ( ParserM, lexString )+import Darcs.Patch.Witnesses.Eq+import Darcs.Patch.Witnesses.Ordered+import Darcs.Patch.Witnesses.Sealed+import Darcs.Patch.Witnesses.Show+ ( Show1(..), Show2(..), showsPrec2+ , ShowDict(ShowDictClass), appPrec+ )+import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )+import qualified Darcs.Util.Diff as D ( DiffAlgorithm )+import Darcs.Util.Printer ( vcat, blueText, ($$), (<+>) )++import Control.Applicative ( (<|>) )+import qualified Data.ByteString as B ( ByteString )+import qualified Data.ByteString.Char8 as BC ( pack )++#include "impossible.h"++-- |A single item in the rebase state consists of either+-- a patch that is being edited, or a fixup that adjusts+-- the context so that a subsequent patch that is being edited+-- \"makes sense\".+--+-- @ToEdit@ holds a patch that is being edited. The name ('PatchInfo') of+-- the patch will typically be the name the patch had before+-- it was added to the rebase state; if it is moved back+-- into the repository it must be given a fresh name to account+-- for the fact that it will not necessarily have the same+-- dependencies as the original patch. This is typically+-- done by changing the @Ignore-This@ junk.+--+-- @Fixup@ adjusts the context so that a subsequent @ToEdit@ patch+-- is correct. Where possible, @Fixup@ changes are commuted+-- as far as possible into the rebase state, so any remaining+-- ones will typically cause a conflict when the @ToEdit@ patch+-- is moved back into the repository.+data RebaseItem p wX wY where+ ToEdit :: Named p wX wY -> RebaseItem p wX wY+ Fixup :: RebaseFixup p wX wY -> RebaseItem p wX wY++instance (Show2 p, Show2 (PrimOf p)) => Show (RebaseItem p wX wY) where+ showsPrec d (ToEdit p) =+ showParen (d > appPrec) $ showString "ToEdit " . showsPrec2 (appPrec + 1) p+ showsPrec d (Fixup p) =+ showParen (d > appPrec) $ showString "Fixup " . showsPrec2 (appPrec + 1) p++instance (Show2 p, Show2 (PrimOf p)) => Show1 (RebaseItem p wX) where+ showDict1 = ShowDictClass++instance (Show2 p, Show2 (PrimOf p)) => Show2 (RebaseItem p) where+ showDict2 = ShowDictClass++countToEdit :: FL (RebaseItem p) wX wY -> Int+countToEdit NilFL = 0+countToEdit (ToEdit _ :>: ps) = 1 + countToEdit ps+countToEdit (_ :>: ps) = countToEdit ps++-- |Given a list of rebase items, try to push a new fixup as far as possible into+-- the list as possible, using both commutation and coalescing. If the fixup+-- commutes past all the 'ToEdit' patches then it is dropped entirely.+simplifyPush :: (PrimPatchBase p, Commute p, FromPrim p, Effect p)+ => D.DiffAlgorithm -> RebaseFixup p wX wY -> FL (RebaseItem p) wY wZ -> Sealed (FL (RebaseItem p) wX)++simplifyPush _ _f NilFL = Sealed NilFL++simplifyPush da (PrimFixup f1) (Fixup (PrimFixup f2) :>: ps)+ | IsEq <- isInverse = Sealed ps+ | otherwise+ = case commute (f1 :> f2) of+ Nothing -> Sealed (mapFL_FL (Fixup . PrimFixup) (canonizeFL da (f1 :>: f2 :>: NilFL)) +>+ ps)+ Just (f2' :> f1') -> mapSeal (Fixup (PrimFixup f2') :>:) (simplifyPush da (PrimFixup f1') ps)+ where isInverse = invert f1 =\/= f2++simplifyPush da (PrimFixup f) (Fixup (NameFixup n) :>: ps)+ = case commutePrimName (f :> n) of+ n' :> f' -> mapSeal (Fixup (NameFixup n') :>:) (simplifyPush da (PrimFixup f') ps)++simplifyPush da (PrimFixup f) (ToEdit e :>: ps)+ = case commuterIdNamed selfCommuter (fromPrim f :> e) of+ Nothing -> Sealed (Fixup (PrimFixup f) :>: ToEdit e :>: ps)+ Just (e' :> f') -> mapSeal (ToEdit e' :>:) (simplifyPushes da (mapFL_FL PrimFixup (effect f')) ps)++simplifyPush da (NameFixup n1) (Fixup (NameFixup n2) :>: ps)+ | IsEq <- isInverse = Sealed ps+ | otherwise+ = case commute (n1 :> n2) of+ Nothing -> Sealed (mapFL_FL (Fixup . NameFixup) (canonizeNamePair (n1 :> n2)) +>+ ps)+ Just (n2' :> n1') -> mapSeal (Fixup (NameFixup n2') :>:) (simplifyPush da (NameFixup n1') ps)+ where isInverse = invert n1 =\/= n2++simplifyPush da (NameFixup n) (Fixup (PrimFixup f) :>: ps) =+ case commuteNamePrim (n :> f) of+ f' :> n' -> mapSeal (Fixup (PrimFixup f') :>:) (simplifyPush da (NameFixup n') ps)++simplifyPush da (NameFixup (AddName an)) (p@(ToEdit (NamedP pn deps _)) :>: ps)+ | an == pn = impossible+ | an `elem` deps = Sealed (Fixup (NameFixup (AddName an)) :>: p :>: ps)+ | otherwise = mapSeal (unsafeCoerceP p :>:) (simplifyPush da (NameFixup (AddName an)) ps)+simplifyPush da (NameFixup (DelName dn)) (p@(ToEdit (NamedP pn deps _)) :>: ps)+ -- this case can arise if a patch is suspended then a fresh copy is pulled from another repo+ | dn == pn = Sealed (Fixup (NameFixup (DelName dn)) :>: p :>: ps)+ | dn `elem` deps = impossible+ | otherwise = mapSeal (unsafeCoerceP p :>:) (simplifyPush da (NameFixup (DelName dn)) ps)+simplifyPush da (NameFixup (Rename old new)) (p@(ToEdit (NamedP pn deps body)) :>: ps)+ | old == pn = impossible+ | new == pn = impossible+ | old `elem` deps = impossible+ | new `elem` deps =+ let newdeps = map (\dep -> if new == dep then old else dep) deps+ in mapSeal (ToEdit (NamedP pn newdeps (unsafeCoerceP body)) :>:) (simplifyPush da (NameFixup (Rename old new)) ps)+ | otherwise = mapSeal (unsafeCoerceP p :>:) (simplifyPush da (NameFixup (Rename old new)) ps)++-- |Like 'simplifyPush' but for a list of fixups.+simplifyPushes :: (PrimPatchBase p, Commute p, FromPrim p, Effect p)+ => D.DiffAlgorithm -> FL (RebaseFixup p) wX wY -> FL (RebaseItem p) wY wZ -> Sealed (FL (RebaseItem p) wX)+simplifyPushes _ NilFL ps = Sealed ps+simplifyPushes da (f :>: fs) ps = unseal (simplifyPush da f) (simplifyPushes da fs ps)+++instance (PrimPatchBase p, PatchListFormat p, ShowPatchBasic p) => ShowPatchBasic (RebaseItem p) where+ showPatch (ToEdit p) = blueText "rebase-toedit" <+> blueText "(" $$ showPatch p $$ blueText ")"+ showPatch (Fixup (PrimFixup p)) = blueText "rebase-fixup" <+> blueText "(" $$ showPatch p $$ blueText ")"+ showPatch (Fixup (NameFixup p)) = blueText "rebase-name" <+> blueText "(" $$ showPatch p $$ blueText ")"++instance (PrimPatchBase p, PatchListFormat p, Apply p, CommuteNoConflicts p, Conflict p, IsHunk p, ShowPatch p)+ => ShowPatch (RebaseItem p) where++ summary (ToEdit p) = summary p+ summary (Fixup (PrimFixup p)) = summary p+ summary (Fixup (NameFixup n)) = summary n+ summaryFL ps = vcat (mapFL summary ps) -- TODO sort out summaries properly, considering expected conflicts+++instance (PrimPatchBase p, PatchListFormat p, ReadPatch p) => ReadPatch (RebaseItem p) where+ readPatch' = mapSeal ToEdit <$> readWith (BC.pack "rebase-toedit") <|>+ mapSeal (Fixup . PrimFixup) <$> readWith (BC.pack "rebase-fixup" ) <|>+ mapSeal (Fixup . NameFixup) <$> readWith (BC.pack "rebase-name" )+ where readWith :: forall m q wX . (ParserM m, ReadPatch q) => B.ByteString -> m (Sealed (q wX))+ readWith str = do lexString str+ lexString (BC.pack "(")+ res <- readPatch'+ lexString (BC.pack ")")+ return res++instance Check p => Check (RebaseItem p) where+ isInconsistent (Fixup _) = Nothing+ isInconsistent (ToEdit p) = isInconsistent p++instance (PrimPatchBase p, PatchInspect p) => PatchInspect (RebaseItem p) where+ listTouchedFiles (ToEdit p) = listTouchedFiles p+ listTouchedFiles (Fixup p) = listTouchedFiles p++ hunkMatches f (ToEdit p) = hunkMatches f p+ hunkMatches f (Fixup p) = hunkMatches f p
src/Darcs/Patch/Rebase/Name.hs view
@@ -7,8 +7,12 @@ ( RebaseName(..) , commuteNamePrim, commutePrimName , commuteNameNamed, commuteNamedName+ , canonizeNamePair ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.CommuteFn ( CommuteFn ) import Darcs.Patch.Effect ( Effect(..) ) import Darcs.Patch.Info ( PatchInfo, isInverted, showPatchInfo, readPatchInfo )@@ -203,4 +207,10 @@ | otherwise = let newdeps = map (\dep -> if old == dep then new else dep) deps in Just (Rename old new :> NamedP pn newdeps (unsafeCoerceP body))++canonizeNamePair :: (RebaseName p :> RebaseName p) wX wY -> FL (RebaseName p) wX wY+canonizeNamePair (AddName n :> Rename old new) | n == old = AddName new :>: NilFL+canonizeNamePair (Rename old new :> DelName n) | n == new = DelName old :>: NilFL+canonizeNamePair (Rename old1 new1 :> Rename old2 new2) | new1 == old2 = Rename old1 new2 :>: NilFL+canonizeNamePair (n1 :> n2) = n1 :>: n2 :>: NilFL
− src/Darcs/Patch/Rebase/NameHack.hs
@@ -1,23 +0,0 @@--- Copyright (C) 2009-2012 Ganesh Sittampalam------ BSD3-module Darcs.Patch.Rebase.NameHack- ( NameHack(..)- ) where--import Darcs.Patch.Info ( PatchInfo )-import Darcs.Patch.Witnesses.Ordered ( FL )-import qualified Darcs.Util.Diff as D ( DiffAlgorithm )---- |When commuting a @Normal@ patch past a @Suspended@ one, we need to adjust the--- internals of the @Suspended@ one to take account of the effect of the @Normal@ patch.--- This includes the name of the @Normal@ patch - but the layering is such that we--- are actually commuting patches of type @Named (Rebasing p)@ - i.e. @Rebasing p@--- doesn't actually contain the name. We therefore need to add a hook to the @Commute@--- instances for @Named@ which @Rebasing@ can then implement.------ There is a default so that other patch types only need to declare the instance.-class NameHack p where- nameHack :: D.DiffAlgorithm -> Maybe (PatchInfo -> FL p wX wY -> FL p wX wY, PatchInfo -> FL p wW wZ -> FL p wW wZ)- nameHack = \_ -> Nothing-
− src/Darcs/Patch/Rebase/Recontext.hs
@@ -1,45 +0,0 @@--- Copyright (C) 2012 Ganesh Sittampalam------ BSD3-module Darcs.Patch.Rebase.Recontext- (- RecontextRebase(..)- , RecontextRebase1(..)- , RecontextRebase2(..)- ) where--import Darcs.Patch.Named ( Named )-import Darcs.Patch.Rebase.Fixup ( RebaseFixup )-import Darcs.Patch.Witnesses.Eq ( EqCheck )-import Darcs.Patch.Witnesses.Ordered ( FL )---- |Check whether a given patch is a suspended rebase patch, and if so provide--- evidence that the start and end contexts are the same (from the point of view--- of the containing repo), and return a function that produces a new version--- with some fixups added.------ Nested in a type to avoid needing an impredicative argument to 'Maybe'.-newtype RecontextRebase1 p =- RecontextRebase1 {- recontextFunc1 :: forall wY wZ . Named p wY wZ -> (EqCheck wY wZ, RecontextRebase2 p wY wZ)- }---- |Return a suspended patch with the given fixups added.------ Nested in a type to avoid needing an impredicative argument to a tuple.-newtype RecontextRebase2 p wY wZ =- RecontextRebase2 {- recontextFunc2 :: forall wX . FL (RebaseFixup p) wX wY -> IO (Named p wX wX)- }---- |Some non-rebase code needs to manipulate the rebase state if one exists.--- This class provides the hook for them to do so without needing to explicitly--- detect that there is a rebase state: 'recontextRebase' abstracts out that--- information.------ The hook is used in amend-record - look there for an explanation of how.------ There is a default so that other patch types only need to declare the instance.-class RecontextRebase p where- recontextRebase :: Maybe (RecontextRebase1 p)- recontextRebase = Nothing
src/Darcs/Patch/Rebase/Viewing.hs view
@@ -11,6 +11,9 @@ , RebaseChange(..), toRebaseChanges ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Commute ( selfCommuter ) import Darcs.Patch.CommuteFn ( CommuteFn, commuterIdFL, commuterRLId, MergeFn , totalCommuterIdFL@@ -26,14 +29,19 @@ import Darcs.Patch.Info ( PatchInfo ) import Darcs.Patch.Invert ( invertFL, invertRL ) import Darcs.Patch.Matchable ( Matchable )-import Darcs.Patch.MaybeInternal ( MaybeInternal(..) ) import Darcs.Patch.Merge ( Merge(..), selfMerger ) import Darcs.Patch.Named ( Named(..), namepatch, infopatch , mergerIdNamed- , adddeps, getdeps+ , getdeps , patch2patchinfo, patchcontents )+import Darcs.Patch.Named.Wrapped+ ( WrappedNamed(..)+ )+import qualified Darcs.Patch.Named.Wrapped as Wrapped+ ( infopatch, adddeps+ ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, n2pia ) import Darcs.Patch.Patchy ( Invert(..), Commute(..), Patchy, Apply(..),@@ -43,16 +51,15 @@ import Darcs.Patch.Prim ( PrimPatch, PrimPatchBase, PrimOf, FromPrim(..), FromPrims(..) )-import Darcs.Patch.Rebase- ( Rebasing(..), RebaseItem(..)- )+import Darcs.Patch.Rebase.Container ( Suspended(..) ) import Darcs.Patch.Rebase.Fixup ( RebaseFixup(..) , commuteFixupNamed, commuteNamedFixups , flToNamesPrims )+import Darcs.Patch.Rebase.Item ( RebaseItem(..) ) import Darcs.Patch.Rebase.Name ( RebaseName(..) )-import Darcs.Patch.Rebase.NameHack ( NameHack(..) )+import Darcs.Patch.RepoType ( RepoType(..), RebaseType(..) ) import Darcs.Patch.Show ( ShowPatchBasic(..) ) import Darcs.Patch.Summary ( plainSummary ) import Darcs.Patch.Witnesses.Eq@@ -67,8 +74,6 @@ import Darcs.Util.Printer ( ($$), redText, empty, vcat ) import Darcs.Util.Show ( appPrec ) -import Prelude hiding ( pi )-import Control.Applicative ( (<$>) ) import Data.List ( nub, (\\) ) import Data.Maybe ( fromMaybe ) @@ -130,14 +135,14 @@ showString " " . showsPrec2 (appPrec + 1) changes -- |Get hold of the 'PatchInfoAnd' patch inside a 'RebaseSelect'.-rsToPia :: RebaseSelect p wX wY -> Sealed2 (PatchInfoAnd p)-rsToPia (RSFwd _ toEdit) = Sealed2 (n2pia toEdit)-rsToPia (RSRev _ toEdit) = Sealed2 (n2pia toEdit)+rsToPia :: RebaseSelect p wX wY -> Sealed2 (PatchInfoAnd ('RepoType 'NoRebase) p)+rsToPia (RSFwd _ toEdit) = Sealed2 (n2pia (NormalP toEdit))+rsToPia (RSRev _ toEdit) = Sealed2 (n2pia (NormalP toEdit)) instance PrimPatchBase p => PrimPatchBase (RebaseSelect p) where type PrimOf (RebaseSelect p) = PrimOf p -instance (PrimPatchBase p, PatchListFormat p, Conflict p, FromPrim p, Effect p, CommuteNoConflicts p, IsHunk p, Patchy p, ApplyState p ~ ApplyState (PrimOf p), NameHack p)+instance (PrimPatchBase p, PatchListFormat p, Conflict p, FromPrim p, Effect p, CommuteNoConflicts p, IsHunk p, Patchy p, ApplyState p ~ ApplyState (PrimOf p)) => Patchy (RebaseSelect p) instance ( PrimPatchBase p, Apply p, ApplyState p ~ ApplyState (PrimOf p)@@ -167,17 +172,15 @@ resolveConflicts (RSFwd _ toedit) = resolveConflicts toedit resolveConflicts (RSRev{}) = impossible + conflictedEffect (RSFwd _ toedit) = conflictedEffect toedit+ conflictedEffect (RSRev{}) = impossible+ -- newtypes to help the type-checker with the 'changeAsMerge' abstraction newtype ResolveConflictsResult p wY = ResolveConflictsResult { getResolveConflictsResult :: [[Sealed (FL (PrimOf p) wY)]] } -newtype ListConflictedFilesResult (p :: * -> * -> *) wY =- ListConflictedFilesResult {- getListConflictedFilesResult :: [FilePath]- }- newtype ConflictedEffectResult p wY = ConflictedEffectResult { getConflictedEffectResult :: [IsConflictedPrim (PrimOf p)]@@ -208,8 +211,6 @@ resolveConflicts = getResolveConflictsResult . changeAsMerge (ResolveConflictsResult . resolveConflicts) - listConflictedFiles =- getListConflictedFilesResult . changeAsMerge (ListConflictedFilesResult . listConflictedFiles) conflictedEffect = getConflictedEffectResult . changeAsMerge (ConflictedEffectResult . conflictedEffect)@@ -227,7 +228,7 @@ instance (PrimPatchBase p, PatchListFormat p, ShowPatchBasic p) => ShowPatchBasic (RebaseSelect p) where showPatch (RSFwd fixups toedit) =- showPatch (Suspended (mapFL_FL Fixup fixups +>+ ToEdit toedit :>: NilFL))+ showPatch (Items (mapFL_FL Fixup fixups +>+ ToEdit toedit :>: NilFL)) showPatch (RSRev {}) = impossible instance (PrimPatchBase p, PatchListFormat p, ShowPatchBasic p)@@ -298,20 +299,20 @@ toRebaseChanges :: PrimPatchBase p => FL (RebaseItem p) wX wY- -> FL (PatchInfoAnd (RebaseChange p)) wX wY+ -> FL (PatchInfoAnd ('RepoType 'IsRebase) (RebaseChange p)) wX wY toRebaseChanges = mapFL_FL toChange . toRebaseSelect -toChange :: RebaseSelect p wX wY -> PatchInfoAnd (RebaseChange p) wX wY+toChange :: RebaseSelect p wX wY -> PatchInfoAnd rt (RebaseChange p) wX wY toChange (RSFwd fixups named) = n2pia $- flip adddeps (getdeps named) $- infopatch (patch2patchinfo named) $+ flip Wrapped.adddeps (getdeps named) $+ Wrapped.infopatch (patch2patchinfo named) $ (:>: NilFL) $ RCFwd fixups (patchcontents named) toChange (RSRev fixups named) = n2pia $- flip adddeps (getdeps named) $- infopatch (patch2patchinfo named) $+ flip Wrapped.adddeps (getdeps named) $+ Wrapped.infopatch (patch2patchinfo named) $ (:>: NilFL) $ RCRev fixups (patchcontents named) @@ -335,7 +336,7 @@ _ =\/= _ = impossible -instance (PrimPatchBase p, FromPrim p, Effect p, Commute p, Invert p, NameHack p) => Commute (RebaseSelect p) where+instance (PrimPatchBase p, FromPrim p, Effect p, Commute p, Invert p) => Commute (RebaseSelect p) where commute (RSFwd {} :> RSRev {}) = impossible commute (RSRev {} :> RSFwd {}) = impossible commute (RSRev fixups1 edit1 :> RSRev fixups2 edit2) =@@ -370,12 +371,12 @@ -- |Split a list of rebase patches into those that will -- have conflicts if unsuspended and those that won't. partitionUnconflicted- :: (PrimPatchBase p, FromPrim p, Effect p, Commute p, Invert p, NameHack p)+ :: (PrimPatchBase p, FromPrim p, Effect p, Commute p, Invert p) => FL (RebaseSelect p) wX wY -> (FL (RebaseSelect p) :> RL (RebaseSelect p)) wX wY partitionUnconflicted = partitionUnconflictedAcc NilRL -partitionUnconflictedAcc :: (PrimPatchBase p, FromPrim p, Effect p, Commute p, Invert p, NameHack p)+partitionUnconflictedAcc :: (PrimPatchBase p, FromPrim p, Effect p, Commute p, Invert p) => RL (RebaseSelect p) wX wY -> FL (RebaseSelect p) wY wZ -> (FL (RebaseSelect p) :> RL (RebaseSelect p)) wX wZ partitionUnconflictedAcc right NilFL = NilFL :> right@@ -384,7 +385,7 @@ Just (p'@(RSFwd NilFL _) :> right') -> case partitionUnconflictedAcc right' ps of left' :> right'' -> (p' :>: left') :> right''- _ -> partitionUnconflictedAcc (p :<: right) ps+ _ -> partitionUnconflictedAcc (right :<: p) ps -- | A patch, together with a list of patch names that it used to depend on, -- but were lost during the rebasing process. The UI can use this information@@ -552,14 +553,11 @@ instance CommuteNoConflicts (RebaseChange p) where commuteNoConflicts _ = impossible -instance MaybeInternal (RebaseChange p)- instance IsHunk p => IsHunk (RebaseChange p) where -- RebaseChange is a compound patch, so it doesn't really make sense to -- ask whether it's a hunk. TODO: get rid of the need for this. isHunk _ = Nothing -instance NameHack (RebaseChange p) instance PatchListFormat (RebaseChange p) instance ( PrimPatchBase p, Apply p, Invert p
src/Darcs/Patch/RegChars.hs view
@@ -19,6 +19,9 @@ module Darcs.Patch.RegChars ( regChars, ) where +import Prelude ()+import Darcs.Prelude+ (&&&) :: (a -> Bool) -> (a -> Bool) -> a -> Bool (&&&) a b c = a c && b c
src/Darcs/Patch/Repair.hs view
@@ -2,6 +2,9 @@ ( Repair(..), RepairToFL(..), mapMaybeSnd, Check(..) ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Apply ( Apply(..) ) import Darcs.Patch.ApplyMonad ( ApplyMonad ) import Darcs.Patch.Witnesses.Ordered ( FL(..), RL(..), mapFL, mapRL, (+>+) )@@ -19,13 +22,13 @@ -- 'Repair' is implemented by collections of patches (FL, Named, PatchInfoAnd) that -- might need repairing. class Repair p where- applyAndTryToFix :: ApplyMonad m (ApplyState p) => p wX wY -> m (Maybe (String, p wX wY))+ applyAndTryToFix :: ApplyMonad (ApplyState p) m => p wX wY -> m (Maybe (String, p wX wY)) --- |'RepairToFL' is implemented by single patches that can be repaired (Prim, Patch, RealPatch)+-- |'RepairToFL' is implemented by single patches that can be repaired (Prim, Patch, RepoPatchV2) -- There is a default so that patch types with no current legacy problems don't need to -- have an implementation. class Apply p => RepairToFL p where- applyAndTryToFixFL :: ApplyMonad m (ApplyState p) => p wX wY -> m (Maybe (String, FL p wX wY))+ applyAndTryToFixFL :: ApplyMonad (ApplyState p) m => p wX wY -> m (Maybe (String, FL p wX wY)) applyAndTryToFixFL p = do apply p; return Nothing mapMaybeSnd :: (a -> b) -> Maybe (c, a) -> Maybe (c, b)
src/Darcs/Patch/RepoPatch.hs view
@@ -7,14 +7,11 @@ import Darcs.Patch.Format ( PatchListFormat ) import Darcs.Patch.Inspect ( PatchInspect ) import Darcs.Patch.Matchable ( Matchable )-import Darcs.Patch.MaybeInternal ( MaybeInternal ) import Darcs.Patch.Merge ( Merge ) import Darcs.Patch.Patchy ( Patchy ) import Darcs.Patch.Patchy.Instances () import Darcs.Patch.Prim ( PrimPatchBase, PrimOf, FromPrim ) import Darcs.Patch.Read ( ReadPatch )-import Darcs.Patch.Rebase.NameHack ( NameHack )-import Darcs.Patch.Rebase.Recontext ( RecontextRebase ) import Darcs.Patch.Repair ( RepairToFL, Check ) import Darcs.Patch.Show ( ShowPatch ) @@ -23,7 +20,6 @@ FromPrim p, Conflict p, CommuteNoConflicts p, Check p, RepairToFL p, PatchListFormat p, PrimPatchBase p, Patchy (PrimOf p), IsHunk (PrimOf p),- MaybeInternal p, RecontextRebase p, NameHack p, Matchable p ) => RepoPatch p
+ src/Darcs/Patch/RepoType.hs view
@@ -0,0 +1,48 @@+module Darcs.Patch.RepoType+ ( RepoType(..), IsRepoType(..), SRepoType(..)+ , RebaseType(..), IsRebaseType, RebaseTypeOf, SRebaseType(..)+ ) where++-- |This type is intended to be used as a phantom type via+-- the 'DataKinds' extension, normally as part of 'RepoType'.+-- Indicates whether or not a rebase is in progress.+data RebaseType = IsRebase | NoRebase++-- |A reflection of 'RebaseType' at the value level so that+-- code can explicitly switch on it.+data SRebaseType (rebaseType :: RebaseType) where+ SIsRebase :: SRebaseType 'IsRebase+ SNoRebase :: SRebaseType 'NoRebase++class IsRebaseType (rebaseType :: RebaseType) where+ -- |Reflect 'RebaseType' to the value level so that+ -- code can explicitly switch on it.+ singletonRebaseType :: SRebaseType rebaseType++instance IsRebaseType 'IsRebase where+ singletonRebaseType = SIsRebase++instance IsRebaseType 'NoRebase where+ singletonRebaseType = SNoRebase++-- |This type is intended to be used as a phantom type via the 'DataKinds'+-- extension. It tracks different types of repositories, e.g. to+-- indicate when a rebase is in progress.+data RepoType = RepoType { rebaseType :: RebaseType }++-- |Extract the 'RebaseType' from a 'RepoType'+type family RebaseTypeOf (rt :: RepoType) :: RebaseType+type instance RebaseTypeOf ('RepoType rebaseType) = rebaseType++class IsRepoType (rt :: RepoType) where+ -- |Reflect 'RepoType' to the value level so that+ -- code can explicitly switch on it.+ singletonRepoType :: SRepoType rt++-- |A reflection of 'RepoType' at the value level so that+-- code can explicitly switch on it.+data SRepoType (repoType :: RepoType) where+ SRepoType :: SRebaseType rebaseType -> SRepoType ('RepoType rebaseType)++instance IsRebaseType rebaseType => IsRepoType ('RepoType rebaseType) where+ singletonRepoType = SRepoType singletonRebaseType
src/Darcs/Patch/Set.hs view
@@ -28,8 +28,12 @@ , appendPSFL , newset2RL , newset2FL+ , patchSetfMap ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Info ( PatchInfo ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, info ) import Darcs.Patch.Witnesses.Sealed ( Sealed )@@ -43,7 +47,7 @@ -- |'Origin' is a type used to represent the initial context of a repo. data Origin -type SealedPatchSet p wStart = Sealed ((PatchSet p) wStart)+type SealedPatchSet rt p wStart = Sealed ((PatchSet rt p) wStart) -- |The patches in a repository are stored in chunks broken up at \"clean\" -- tags. A tag is clean if the only patches before it in the current@@ -52,73 +56,76 @@ -- -- A 'PatchSet' represents a repo's history as the list of patches since the -- last clean tag, and then a list of patch lists each delimited by clean tags.-data PatchSet p wStart wY where- PatchSet :: RL (PatchInfoAnd p) wX wY -> RL (Tagged p) wStart wX- -> PatchSet p wStart wY+data PatchSet rt p wStart wY where+ PatchSet :: RL (Tagged rt p) wStart wX -> RL (PatchInfoAnd rt p) wX wY+ -> PatchSet rt p wStart wY -deriving instance Show2 p => Show (PatchSet p wStart wY)+deriving instance Show2 p => Show (PatchSet rt p wStart wY) -instance Show2 p => Show1 (PatchSet p wStart) where+instance Show2 p => Show1 (PatchSet rt p wStart) where showDict1 = ShowDictClass -instance Show2 p => Show2 (PatchSet p) where+instance Show2 p => Show2 (PatchSet rt p) where showDict2 = ShowDictClass -emptyPatchSet :: PatchSet p wX wX+emptyPatchSet :: PatchSet rt p wX wX emptyPatchSet = PatchSet NilRL NilRL -- |A 'Tagged' is a single chunk of a 'PatchSet'. -- It has a 'PatchInfo' representing a clean tag, -- the hash of the previous inventory (if it exists), -- and the list of patches since that previous inventory.-data Tagged p wX wZ where- Tagged :: PatchInfoAnd p wY wZ -> Maybe String- -> RL (PatchInfoAnd p) wX wY -> Tagged p wX wZ+data Tagged rt p wX wZ where+ Tagged :: PatchInfoAnd rt p wY wZ -> Maybe String+ -> RL (PatchInfoAnd rt p) wX wY -> Tagged rt p wX wZ -deriving instance Show2 p => Show (Tagged p wX wZ)+deriving instance Show2 p => Show (Tagged rt p wX wZ) -instance Show2 p => Show1 (Tagged p wX) where+instance Show2 p => Show1 (Tagged rt p wX) where showDict1 = ShowDictClass -instance Show2 p => Show2 (Tagged p) where+instance Show2 p => Show2 (Tagged rt p) where showDict2 = ShowDictClass -- |'newset2RL' takes a 'PatchSet' and returns an equivalent, linear 'RL' of -- patches.-newset2RL :: PatchSet p wStart wX -> RL (PatchInfoAnd p) wStart wX-newset2RL (PatchSet ps ts) = ps +<+ concatRL (mapRL_RL ts2rl ts)+newset2RL :: PatchSet rt p wStart wX -> RL (PatchInfoAnd rt p) wStart wX+newset2RL (PatchSet ts ps) = concatRL (mapRL_RL ts2rl ts) +<+ ps where- ts2rl :: Tagged p wY wZ -> RL (PatchInfoAnd p) wY wZ- ts2rl (Tagged t _ ps2) = t :<: ps2+ ts2rl :: Tagged rt p wY wZ -> RL (PatchInfoAnd rt p) wY wZ+ ts2rl (Tagged t _ ps2) = ps2 :<: t -- |'newset2FL' takes a 'PatchSet' and returns an equivalent, linear 'FL' of -- patches.-newset2FL :: PatchSet p wStart wX -> FL (PatchInfoAnd p) wStart wX+newset2FL :: PatchSet rt p wStart wX -> FL (PatchInfoAnd rt p) wStart wX newset2FL = reverseRL . newset2RL -- |'appendPSFL' takes a 'PatchSet' and a 'FL' of patches that "follow" the -- PatchSet, and concatenates the patches into the PatchSet.-appendPSFL :: PatchSet p wStart wX -> FL (PatchInfoAnd p) wX wY- -> PatchSet p wStart wY-appendPSFL (PatchSet ps ts) newps = PatchSet (reverseFL newps +<+ ps) ts+appendPSFL :: PatchSet rt p wStart wX -> FL (PatchInfoAnd rt p) wX wY+ -> PatchSet rt p wStart wY+appendPSFL (PatchSet ts ps) newps = PatchSet ts (ps +<+ reverseFL newps) -- |Runs a progress action for each tag and patch in a given PatchSet, using -- the passed progress message. Does not alter the PatchSet.-progressPatchSet :: String -> PatchSet p wStart wX -> PatchSet p wStart wX-progressPatchSet k (PatchSet ps ts) =- PatchSet (mapRL_RL prog ps) $ mapRL_RL progressTagged ts+progressPatchSet :: String -> PatchSet rt p wStart wX -> PatchSet rt p wStart wX+progressPatchSet k (PatchSet ts ps) =+ PatchSet (mapRL_RL progressTagged ts) (mapRL_RL prog ps) where prog = progress k - progressTagged :: Tagged p wY wZ -> Tagged p wY wZ+ progressTagged :: Tagged rt p wY wZ -> Tagged rt p wY wZ progressTagged (Tagged t h tps) = Tagged (prog t) h (mapRL_RL prog tps) -- |'tags' returns the PatchInfos corresponding to the tags of a given -- 'PatchSet'.-tags :: PatchSet p wStart wX -> [PatchInfo]-tags (PatchSet _ ts) = mapRL taggedTagInfo ts+tags :: PatchSet rt p wStart wX -> [PatchInfo]+tags (PatchSet ts _) = mapRL taggedTagInfo ts where- taggedTagInfo :: Tagged p wY wZ -> PatchInfo+ taggedTagInfo :: Tagged rt p wY wZ -> PatchInfo taggedTagInfo (Tagged t _ _) = info t++patchSetfMap:: (forall wW wZ . PatchInfoAnd rt p wW wZ -> IO a) -> PatchSet rt p wW' wZ' -> IO [a]+patchSetfMap f = sequence . mapRL f . newset2RL
src/Darcs/Patch/Show.hs view
@@ -24,10 +24,13 @@ , formatFileName ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( pi ) import Darcs.Patch.Apply ( ApplyState )-import Darcs.Patch.ApplyMonad ( ApplyMonadTrans, ApplyMonad )+import Darcs.Patch.ApplyMonad ( ApplyMonad ) import Darcs.Patch.Format ( FileNameFormat(..) ) import Darcs.Patch.Info ( PatchInfo, showPatchInfo ) import Darcs.Patch.Witnesses.Ordered ( FL )@@ -54,8 +57,8 @@ -- used for instance before putting it into a bundle. As this -- unified context is not included in patch representation, this -- requires access to the tree.- showContextPatch :: (Monad m, ApplyMonadTrans m (ApplyState p),- ApplyMonad m (ApplyState p))+ showContextPatch :: (Monad m,+ ApplyMonad (ApplyState p) m) => p wX wY -> m Doc showContextPatch p = return $ showPatch p
src/Darcs/Patch/Split.hs view
@@ -24,6 +24,9 @@ module Darcs.Patch.Split ( Splitter(..), rawSplitter, noSplitter, primSplitter, reversePrimSplitter ) where +import Prelude ()+import Darcs.Prelude+ import Data.List ( intersperse ) import Darcs.Patch.Witnesses.Ordered@@ -62,7 +65,7 @@ -- splitters for Prim etc, and the generality doesn't cost anything. data Splitter p = Splitter {- applySplitter :: forall wX wY . D.DiffAlgorithm -> p wX wY+ applySplitter :: forall wX wY . p wX wY -> Maybe (B.ByteString, B.ByteString -> Maybe (FL p wX wY)) -- canonization is needed to undo the effects of splitting@@ -74,7 +77,7 @@ -- we might record (or whatever) a rather strange looking patch. -- This hook allows the splitter to provide an appropriate -- function for doing this.- ,canonizeSplit :: forall wX wY . D.DiffAlgorithm -> FL p wX wY+ ,canonizeSplit :: forall wX wY . FL p wX wY -> FL p wX wY } @@ -96,19 +99,19 @@ rawSplitter :: (ShowPatch p, ReadPatch p, Invert p) => Splitter p rawSplitter = Splitter { applySplitter =- \_ p -> Just (renderPS Standard . showPatch $ p,+ \p -> Just (renderPS Standard . showPatch $ p, \str -> case parseStrictly readPatch' str of Just (Sealed res, _) -> Just (withEditedHead p res) _ -> Nothing )- ,canonizeSplit = \_ b -> id b+ ,canonizeSplit = id } -- |Never splits. In other code we normally pass around Maybe Splitter instead of using this -- as the default, because it saves clients that don't care about splitting from having to -- import this module just to get noSplitter. noSplitter :: Splitter p-noSplitter = Splitter { applySplitter = \_ -> const Nothing, canonizeSplit = \_ b -> id b }+noSplitter = Splitter { applySplitter = const Nothing, canonizeSplit = id } doPrimSplit :: PrimPatch prim => D.DiffAlgorithm -> prim wX wY@@ -119,7 +122,7 @@ [ "Interactive hunk edit:" , " - Edit the section marked 'AFTER'" , " - Arbitrary editing is supported"- , " - This will only affect the patch, not your working copy"+ , " - This will only affect the patch, not your working tree" , " - Hints:" , " - To split added text, delete the part you want to postpone" , " - To split removed text, copy back the part you want to retain"@@ -161,9 +164,9 @@ -- |Split a primitive hunk patch up -- by allowing the user to edit both the before and after lines, then insert fixup patches -- to clean up the mess.-primSplitter :: PrimPatch p => Splitter p-primSplitter = Splitter { applySplitter = doPrimSplit- , canonizeSplit = canonizeFL }+primSplitter :: PrimPatch p => D.DiffAlgorithm -> Splitter p+primSplitter da = Splitter { applySplitter = doPrimSplit da+ , canonizeSplit = canonizeFL da } doReversePrimSplit :: PrimPatch prim => D.DiffAlgorithm -> prim wX wY -> Maybe (B.ByteString, B.ByteString -> Maybe (FL prim wX wY))@@ -178,7 +181,7 @@ map BC.pack [ "Interactive hunk edit:" , " - Edit the section marked 'AFTER' (representing the state to which you'll revert)" , " - Arbitrary editing is supported"- , " - Your working copy will be returned to the 'AFTER' state"+ , " - Your working tree will be returned to the 'AFTER' state" , " - Do not touch the 'BEFORE' section" , " - Hints:" , " - To revert only a part of a text addition, delete the part you want to get rid of"@@ -186,6 +189,6 @@ , "" ] -reversePrimSplitter :: PrimPatch prim => Splitter prim-reversePrimSplitter = Splitter { applySplitter = doReversePrimSplit- , canonizeSplit = canonizeFL}+reversePrimSplitter :: PrimPatch prim => D.DiffAlgorithm -> Splitter prim+reversePrimSplitter da = Splitter { applySplitter = doReversePrimSplit da+ , canonizeSplit = canonizeFL da }
src/Darcs/Patch/Summary.hs view
@@ -3,7 +3,10 @@ xmlSummary ) where -import Darcs.Util.Path ( fn2fp )+import Prelude ()+import Darcs.Prelude++import Darcs.Util.Path ( fn2fp, FileName ) import Darcs.Patch.Conflict ( Conflict(..), IsConflictedPrim(IsC), ConflictState(..) ) import Darcs.Patch.Effect ( Effect ) import Darcs.Patch.Prim.Class ( PrimDetails(..), PrimPatchBase )@@ -19,8 +22,13 @@ plainSummaryPrim :: PrimDetails prim => prim wX wY -> Doc plainSummaryPrim = vcat . map (summChunkToLine False) . genSummary . (:[]) . IsC Okay -plainSummaryPrims :: PrimDetails prim => Bool -> FL prim wX wY -> Doc-plainSummaryPrims machineReadable = vcat . map (summChunkToLine machineReadable). genSummary . mapFL (IsC Okay)+plainSummaryPrims :: PrimDetails prim => Bool -> [FileName] -> FL prim wX wY -> Doc+plainSummaryPrims machineReadable conflicting =+ vcat . map (summChunkToLine machineReadable . markConflict) . genSummary . mapFL (IsC Okay)+ where+ markConflict (SummChunk sf@(SummFile _ sfFn _ _ _) _)+ | sfFn `elem` conflicting = SummChunk sf Conflicted+ markConflict sc = sc plainSummary :: (Conflict e, Effect e, PrimPatchBase e) => e wX wY -> Doc plainSummary = vcat . map (summChunkToLine False) . genSummary . conflictedEffect
src/Darcs/Patch/SummaryData.hs view
@@ -1,5 +1,8 @@ module Darcs.Patch.SummaryData ( SummDetail(..), SummOp(..) ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Util.Path ( FileName ) data SummDetail = SummAddDir FileName
src/Darcs/Patch/TokenReplace.hs view
@@ -3,8 +3,13 @@ tryTokInternal , forceTokReplace , breakOutToken+ , breakToTokens+ , defaultToks ) where +import Prelude ()+import Darcs.Prelude+ import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as BC import Data.Maybe ( isNothing )@@ -55,3 +60,13 @@ replaceMatchingToken input | input == oldToken = newToken | otherwise = input++-- break a single bytestring into tokens+breakToTokens :: BC.ByteString -> [BC.ByteString]+breakToTokens input | B.null input = []+breakToTokens input =+ let (_, tok, remaining) = breakOutToken defaultToks input in+ tok : breakToTokens remaining++defaultToks :: String+defaultToks = "A-Za-z_0-9"
src/Darcs/Patch/TouchesFiles.hs view
@@ -22,7 +22,10 @@ selectTouching, deselectNotTouching, selectNotTouching, ) where-import Control.Applicative ( (<$>) )++import Prelude ()+import Darcs.Prelude+ import Data.List ( isSuffixOf, nub ) import Darcs.Patch.Choices ( PatchChoices, Label, LabelledPatch,@@ -34,7 +37,7 @@ import Darcs.Patch.Inspect ( PatchInspect ) import Darcs.Patch.Witnesses.Ordered ( FL(..), (:>)(..), mapFL_FL, (+>+) ) import Darcs.Patch.Witnesses.Sealed ( Sealed, seal )-import Storage.Hashed.Tree( Tree )+import Darcs.Util.Tree( Tree ) labelTouching :: (Patchy p, PatchInspect p, ApplyState p ~ Tree) => Bool -> [FilePath] -> FL (LabelledPatch p) wX wY -> [Label]
src/Darcs/Patch/Type.hs view
@@ -1,7 +1,9 @@ module Darcs.Patch.Type ( PatchType(..), patchType ) where +import Darcs.Patch.RepoType ( RepoType )+ -- |Used for indicating a patch type without having a concrete patch-data PatchType (p :: * -> * -> *) = PatchType+data PatchType (rt :: RepoType) (p :: * -> * -> *) = PatchType -patchType :: p wX wY -> PatchType p+patchType :: p wX wY -> PatchType rt p patchType _ = PatchType
src/Darcs/Patch/V1.hs view
@@ -1,5 +1,5 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}-module Darcs.Patch.V1 ( Patch ) where+module Darcs.Patch.V1 ( RepoPatchV1 ) where import Darcs.Patch.Matchable ( Matchable ) import Darcs.Patch.Patchy ( Patchy )@@ -8,11 +8,11 @@ import Darcs.Patch.V1.Apply () import Darcs.Patch.V1.Commute ()-import Darcs.Patch.V1.Core ( Patch )+import Darcs.Patch.V1.Core ( RepoPatchV1 ) import Darcs.Patch.V1.Read () import Darcs.Patch.V1.Show () import Darcs.Patch.V1.Viewing () -instance PrimPatch prim => Patchy (Patch prim)-instance PrimPatch prim => Matchable (Patch prim)-instance PrimPatch prim => RepoPatch (Patch prim)+instance PrimPatch prim => Patchy (RepoPatchV1 prim)+instance PrimPatch prim => Matchable (RepoPatchV1 prim)+instance PrimPatch prim => RepoPatch (RepoPatchV1 prim)
src/Darcs/Patch/V1/Apply.hs view
@@ -1,6 +1,9 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module Darcs.Patch.V1.Apply () where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Apply ( ApplyState, Apply, apply ) import Darcs.Patch.Prim ( PrimPatch, applyPrimFL ) import Darcs.Patch.Repair ( RepairToFL, applyAndTryToFixFL,@@ -8,15 +11,15 @@ import Darcs.Patch.Effect ( effect ) import Darcs.Patch.V1.Commute ()-import Darcs.Patch.V1.Core ( Patch(..) )+import Darcs.Patch.V1.Core ( RepoPatchV1(..) ) import Darcs.Patch.Witnesses.Ordered ( mapFL_FL ) -instance PrimPatch prim => Apply (Patch prim) where- type ApplyState (Patch prim) = ApplyState prim+instance PrimPatch prim => Apply (RepoPatchV1 prim) where+ type ApplyState (RepoPatchV1 prim) = ApplyState prim apply p = applyPrimFL $ effect p -instance PrimPatch prim => RepairToFL (Patch prim) where+instance PrimPatch prim => RepairToFL (RepoPatchV1 prim) where applyAndTryToFixFL (PP x) = mapMaybeSnd (mapFL_FL PP) `fmap` applyAndTryToFixFL x applyAndTryToFixFL x = do apply x; return Nothing
src/Darcs/Patch/V1/Commute.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-orphans -fno-warn-incomplete-patterns #-}+{-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE CPP #-} @@ -27,21 +27,26 @@ ) where +import Prelude ()+import Darcs.Prelude+ import Control.Monad ( MonadPlus, mplus, msum, mzero, guard )-import Control.Applicative ( Applicative(..), Alternative(..) )-import Data.Maybe ( isJust )+import Control.Applicative ( Alternative(..) ) -import Darcs.Patch.Commute ( toFwdCommute, selfCommuter )+import Darcs.Patch.Commute ( selfCommuter ) import Darcs.Patch.CommuteFn ( commuterIdFL, commuterFLId )-import Darcs.Patch.ConflictMarking ( mangleUnravelled ) import Darcs.Util.Path ( FileName ) import Darcs.Patch.Invert ( invertRL ) import Darcs.Patch.Merge ( Merge(..) ) import Darcs.Patch.Patchy ( Commute(..), PatchInspect(..), Invert(..) )-import Darcs.Patch.V1.Core ( Patch(..),+import Darcs.Patch.V1.Core ( RepoPatchV1(..), isMerger, mergerUndo )-import Darcs.Patch.Conflict ( Conflict(..), CommuteNoConflicts(..) )+import Darcs.Patch.Conflict+ ( Conflict(..), listConflictedFiles+ , IsConflictedPrim(..), ConflictState(..), CommuteNoConflicts(..)+ , mangleUnravelled+ ) import Darcs.Patch.Effect ( Effect(..) ) import Darcs.Patch.FileHunk ( IsHunk(..) ) import Darcs.Patch.Prim ( FromPrim(..), PrimPatch,@@ -61,9 +66,9 @@ ( unsafeCoerceP, unsafeCoercePStart , unsafeCoercePEnd ) import Darcs.Patch.Witnesses.Ordered- ( mapFL_FL,+ ( mapFL_FL, mapFL, FL(..), RL(..),- (:/\:)(..), (:<)(..), (:\/:)(..), (:>)(..),+ (:/\:)(..), (:\/:)(..), (:>)(..), lengthFL, mapRL, reverseFL, reverseRL, concatFL )@@ -117,34 +122,31 @@ -- TODO: when can the first attempt fail, but the second not? What's so clever -- in this function? cleverCommute :: Invert prim => CommuteFunction prim -> CommuteFunction prim-cleverCommute c (p1:<p2) =- case c (p1 :< p2) of+cleverCommute c (p1 :> p2) = case c (p1 :> p2) of Succeeded x -> Succeeded x Failed -> Failed- Unknown -> case c (invert p2 :< invert p1) of- Succeeded (p1' :< p2') -> Succeeded (invert p2' :< invert p1')- Failed -> Failed- Unknown -> Unknown+ Unknown -> case c (invert p2 :> invert p1) of+ Succeeded (ip1' :> ip2') -> Succeeded (invert ip2' :> invert ip1')+ Failed -> Failed+ Unknown -> Unknown -- | If we have two Filepatches which modify different files, we can return a -- result early, since the patches trivially commute. speedyCommute :: PrimPatch prim => CommuteFunction prim-speedyCommute (p1 :< p2)- | isJust p1_modifies && isJust p2_modifies &&- p1_modifies /= p2_modifies = Succeeded (unsafeCoerceP p2 :< unsafeCoerceP p1)+speedyCommute (p1 :> p2)+ | Just m1 <- isFilepatchMerger p1+ , Just m2 <- isFilepatchMerger p2+ , m1 /= m2 = Succeeded (unsafeCoerceP p2 :> unsafeCoerceP p1) | otherwise = Unknown- where p1_modifies = isFilepatchMerger p1- p2_modifies = isFilepatchMerger p2 everythingElseCommute :: forall prim . PrimPatch prim => CommuteFunction prim-everythingElseCommute (PP px :< PP py) = toPerhaps $ do- x' :> y' <- commute (py :> px)- return (PP y' :< PP x')-everythingElseCommute _xx =- msum [- cleverCommute commuteRecursiveMerger _xx- ,cleverCommute otherCommuteRecursiveMerger _xx- ]+everythingElseCommute (PP p1 :> PP p2) = toPerhaps $ do+ p2' :> p1' <- commute (p1 :> p2)+ return (PP p2' :> PP p1')+everythingElseCommute ps =+ msum [ cleverCommute commuteRecursiveMerger ps+ , cleverCommute otherCommuteRecursiveMerger ps+ ] {- Note that it must be true that@@ -157,24 +159,23 @@ then commutex (B^-1, A^-1) == Just (A'^-1, B'^-1) -} -unsafeMerger :: PrimPatch prim => String -> Patch prim wX wY -> Patch prim wX wZ -> Patch prim wA wB+unsafeMerger :: PrimPatch prim => String -> RepoPatchV1 prim wX wY -> RepoPatchV1 prim wX wZ -> RepoPatchV1 prim wA wB unsafeMerger x p1 p2 = unsafeCoercePStart $ unsafeUnseal $ merger x p1 p2 -- | Attempt to commute two patches, the first of which is a Merger patch.-mergerCommute :: PrimPatch prim => (Patch prim :< Patch prim) wX wY -> Perhaps ((Patch prim :< Patch prim) wX wY)-mergerCommute (Merger _ _ p1 p2 :< pA)- | unsafeCompare pA p1 = Succeeded (unsafeMerger "0.0" p2 p1 :< unsafeCoercePStart p2)+mergerCommute :: PrimPatch prim+ => (RepoPatchV1 prim :> RepoPatchV1 prim) wX wY -> Perhaps ((RepoPatchV1 prim :> RepoPatchV1 prim) wX wY)+mergerCommute (pA :> Merger _ _ p1 p2)+ | unsafeCompare pA p1 = Succeeded (unsafeCoercePStart p2 :> unsafeMerger "0.0" p2 p1) | unsafeCompare pA (invert (unsafeMerger "0.0" p2 p1)) = Failed-mergerCommute (Merger _ _- (Merger _ _ c b)- (Merger _ _ c' a) :<- Merger _ _ b' c'')+mergerCommute (Merger _ _ b' c'' :> Merger _ _ (Merger _ _ c b) (Merger _ _ c' a)) | unsafeCompare b' b && unsafeCompare c c' && unsafeCompare c c'' =- Succeeded (unsafeMerger "0.0" (unsafeMerger "0.0" b (unsafeCoercePStart a)) (unsafeMerger "0.0" b c) :<- unsafeMerger "0.0" b (unsafeCoercePStart a))+ Succeeded ( unsafeMerger "0.0" b (unsafeCoercePStart a) :>+ unsafeMerger "0.0" (unsafeMerger "0.0" b (unsafeCoercePStart a)) (unsafeMerger "0.0" b c)+ ) mergerCommute _ = Unknown -instance PrimPatch prim => Merge (Patch prim) where+instance PrimPatch prim => Merge (RepoPatchV1 prim) where merge (y :\/: z) = case actualMerge (y:\/:z) of -- actualMerge returns one "arm" of a merge result (@y'@, which applies@@ -187,14 +188,14 @@ $$ showPatch_ y' Just (_ :> z') -> unsafeCoercePStart z' :/\: y' -instance PrimPatch prim => Commute (Patch prim) where+instance PrimPatch prim => Commute (RepoPatchV1 prim) where commute x = toMaybe $ msum- [toFwdCommute speedyCommute x,- toFwdCommute (cleverCommute mergerCommute) x,- toFwdCommute everythingElseCommute x+ [speedyCommute x,+ (cleverCommute mergerCommute) x,+ everythingElseCommute x ] -instance PrimPatch prim => PatchInspect (Patch prim) where+instance PrimPatch prim => PatchInspect (RepoPatchV1 prim) where -- Recurse on everything, these are potentially spoofed patches listTouchedFiles (Merger _ _ p1 p2) = nubSort $ listTouchedFiles p1 ++ listTouchedFiles p2@@ -207,10 +208,11 @@ commuteNoMerger :: PrimPatch prim => MaybeCommute prim commuteNoMerger x =- toMaybe $ msum [speedyCommute x,- everythingElseCommute x]+ toMaybe $ msum [ speedyCommute x+ , everythingElseCommute x+ ] -isFilepatchMerger :: PrimPatch prim => Patch prim wX wY -> Maybe FileName+isFilepatchMerger :: PrimPatch prim => RepoPatchV1 prim wX wY -> Maybe FileName isFilepatchMerger (PP p) = is_filepatch p isFilepatchMerger (Merger _ _ p1 p2) = do f1 <- isFilepatchMerger p1@@ -219,8 +221,9 @@ isFilepatchMerger (Regrem und unw p1 p2) = isFilepatchMerger (Merger und unw p1 p2) -commuteRecursiveMerger :: PrimPatch prim => (Patch prim :< Patch prim) wX wY -> Perhaps ((Patch prim :< Patch prim) wX wY)-commuteRecursiveMerger (p@(Merger _ _ p1 p2) :< pA) = toPerhaps $+commuteRecursiveMerger :: PrimPatch prim+ => (RepoPatchV1 prim :> RepoPatchV1 prim) wX wY -> Perhaps ((RepoPatchV1 prim :> RepoPatchV1 prim) wX wY)+commuteRecursiveMerger (pA :> p@(Merger _ _ p1 p2)) = toPerhaps $ do (_ :> pA') <- commuterIdFL selfCommuter (pA :> undo) _ <- commuterIdFL selfCommuter (pA' :> invert undo) (_ :> pAmid) <- commute (pA :> unsafeCoercePStart (invert p1))@@ -235,13 +238,13 @@ undo' = mergerUndo p' (pAo :> _) <- commuterFLId selfCommuter (undo' :> pA') guard (pAo `unsafeCompare` pA)- return (pA' :< p')+ return (p' :> pA') where undo = mergerUndo p commuteRecursiveMerger _ = Unknown -otherCommuteRecursiveMerger :: PrimPatch prim => (Patch prim :< Patch prim) wX wY -> Perhaps ((Patch prim :< Patch prim) wX wY)-otherCommuteRecursiveMerger (pA':< p_old@(Merger _ _ p1' p2')) =- toPerhaps $+otherCommuteRecursiveMerger :: PrimPatch prim+ => (RepoPatchV1 prim :> RepoPatchV1 prim) wX wY -> Perhaps ((RepoPatchV1 prim :> RepoPatchV1 prim) wX wY)+otherCommuteRecursiveMerger (p_old@(Merger _ _ p1' p2') :> pA') = toPerhaps $ do (pA :> _) <- commuterFLId selfCommuter (mergerUndo p_old :> pA') (pAmid :> p1) <- commute (unsafeCoercePEnd p1' :> pA) (_ :> pAmido) <- commute (pA :> invert p1)@@ -256,24 +259,24 @@ guard (not $ pA `unsafeCompare` p1) -- special case here... (_ :> pAo') <- commuterIdFL selfCommuter (pA :> undo) guard (pAo' `unsafeCompare` pA')- return (p :< pA)+ return (pA :> p) otherCommuteRecursiveMerger _ = Unknown -type CommuteFunction prim = forall wX wY . (Patch prim :< Patch prim) wX wY -> Perhaps ((Patch prim :< Patch prim) wX wY)-type MaybeCommute prim = forall wX wY . (Patch prim :< Patch prim) wX wY -> Maybe ((Patch prim :< Patch prim) wX wY)+type CommuteFunction prim = forall wX wY . (RepoPatchV1 prim :> RepoPatchV1 prim) wX wY -> Perhaps ((RepoPatchV1 prim :> RepoPatchV1 prim) wX wY)+type MaybeCommute prim = forall wX wY . (RepoPatchV1 prim :> RepoPatchV1 prim) wX wY -> Maybe ((RepoPatchV1 prim :> RepoPatchV1 prim) wX wY) -revCommuteFLId :: MaybeCommute prim -> (FL (Patch prim) :< Patch prim) wX wY -> Maybe ((Patch prim :< FL (Patch prim)) wX wY)-revCommuteFLId _ (NilFL :< p) = return (p :< NilFL)-revCommuteFLId commuter ((q :>: qs) :< p) = do- p' :< q' <- commuter (q :< p)- p'' :< qs' <- revCommuteFLId commuter (qs :< p')- return (p'' :< (q' :>: qs'))+commuteFLId :: MaybeCommute prim -> (RepoPatchV1 prim :> FL (RepoPatchV1 prim)) wX wY -> Maybe ((FL (RepoPatchV1 prim) :> RepoPatchV1 prim) wX wY)+commuteFLId _ (p :> NilFL) = return (NilFL :> p)+commuteFLId commuter (p :> (q :>: qs)) = do+ q' :> p' <- commuter (p :> q)+ qs' :> p'' <- commuteFLId commuter (p' :> qs)+ return ((q' :>: qs') :> p'') -- | elegantMerge attempts to perform the "intuitive" merge of two patches, -- from a common starting context @wX@. elegantMerge :: PrimPatch prim- => (Patch prim :\/: Patch prim) wX wY- -> Maybe ((Patch prim :/\: Patch prim) wX wY)+ => (RepoPatchV1 prim :\/: RepoPatchV1 prim) wX wY+ -> Maybe ((RepoPatchV1 prim :/\: RepoPatchV1 prim) wX wY) elegantMerge (p1 :\/: p2) = do p1' :> ip2' <- commute (invert p2 :> p1) p1o :> _ <- commute (p2 :> p1')@@ -308,43 +311,45 @@ -- -- TODO: why does this code throw away the other branch, only for merge to -- rebuild it?-actualMerge :: PrimPatch prim => (Patch prim :\/: Patch prim) wX wY -> Sealed (Patch prim wY)+actualMerge :: PrimPatch prim+ => (RepoPatchV1 prim :\/: RepoPatchV1 prim) wX wY -> Sealed (RepoPatchV1 prim wY) actualMerge (p1 :\/: p2) = case elegantMerge (p1:\/:p2) of Just (_ :/\: p1') -> Sealed p1' Nothing -> merger "0.0" p2 p1 -- Recreates a patch history in reverse.-unwind :: Patch prim wX wY -> Sealed (RL (Patch prim) wX)+unwind :: RepoPatchV1 prim wX wY -> Sealed (RL (RepoPatchV1 prim) wX) unwind (Merger _ unwindings _ _) = Sealed unwindings-unwind p = Sealed (p :<: NilRL)+unwind p = Sealed (NilRL :<: p) -- Recreates a patch history in reverse. The patch being unwound is always at -- the start of the list of patches.-trueUnwind :: PrimPatch prim => Patch prim wX wY -> Sealed (RL (Patch prim) wX)+trueUnwind :: PrimPatch prim+ => RepoPatchV1 prim wX wY -> Sealed (RL (RepoPatchV1 prim) wX) trueUnwind p@(Merger _ _ p1 p2) = case (unwind p1, unwind p2) of- (Sealed (_:<:p1s),Sealed (_:<:p2s)) ->- Sealed (p :<: unsafeCoerceP p1 :<: unsafeUnsealFlipped (reconcileUnwindings p p1s (unsafeCoercePEnd p2s)))+ (Sealed (p1s:<:_),Sealed (p2s:<:_)) ->+ Sealed (unsafeUnsealFlipped (reconcileUnwindings p p1s (unsafeCoercePEnd p2s)) :<: unsafeCoerceP p1 :<: p) _ -> impossible trueUnwind _ = impossible reconcileUnwindings :: PrimPatch prim- => Patch prim wA wB -> RL (Patch prim) wX wZ -> RL (Patch prim) wY wZ -> FlippedSeal (RL (Patch prim)) wZ+ => RepoPatchV1 prim wA wB -> RL (RepoPatchV1 prim) wX wZ -> RL (RepoPatchV1 prim) wY wZ -> FlippedSeal (RL (RepoPatchV1 prim)) wZ reconcileUnwindings _ NilRL p2s = FlippedSeal p2s reconcileUnwindings _ p1s NilRL = FlippedSeal p1s-reconcileUnwindings p (p1:<:p1s) p2s@(p2:<:tp2s) =+reconcileUnwindings p (p1s:<:p1) p2s@(tp2s:<:p2) = case [(p1s', p2s')|- p1s'@(hp1s':<:_) <- headPermutationsRL (p1:<:p1s),- p2s'@(hp2s':<:_) <- headPermutationsRL p2s,+ p1s'@(_:<:hp1s') <- headPermutationsRL (p1s:<:p1),+ p2s'@(_:<:hp2s') <- headPermutationsRL p2s, hp1s' `unsafeCompare` hp2s'] of- ((p1':<:p1s', _:<:p2s'):_) ->- mapFlipped (p1' :<:) $ reconcileUnwindings p p1s' (unsafeCoercePEnd p2s')+ ((p1s':<:p1', p2s':<:_):_) ->+ mapFlipped (:<:p1') $ reconcileUnwindings p p1s' (unsafeCoercePEnd p2s') [] -> case reverseFL `fmap` putBefore p1 (reverseRL p2s) of- Just p2s' -> mapFlipped (p1 :<:) $ reconcileUnwindings p p1s p2s'+ Just p2s' -> mapFlipped (:<:p1) $ reconcileUnwindings p p1s p2s' Nothing -> case fmap reverseFL $ putBefore p2 $- reverseRL (p1:<:p1s) of- Just p1s' -> mapFlipped (p2 :<:) $+ reverseRL (p1s:<:p1) of+ Just p1s' -> mapFlipped (:<:p2) $ reconcileUnwindings p p1s' tp2s Nothing -> bugDoc $ text "in function reconcileUnwindings"@@ -356,44 +361,52 @@ -- 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 :: PrimPatch prim => Patch prim wY wZ -> FL (Patch prim) wX wZ -> Maybe (FL (Patch prim) wY wW)+putBefore :: PrimPatch prim+ => RepoPatchV1 prim wY wZ -> FL (RepoPatchV1 prim) wX wZ -> Maybe (FL (RepoPatchV1 prim) wY wW) putBefore p1 (p2:>:p2s) = do p1' :> p2' <- commute (unsafeCoerceP p2 :> invert p1) _ <- commute (p2' :> p1) (unsafeCoerceP p2' :>:) `fmap` putBefore p1' (unsafeCoerceP p2s) putBefore _ NilFL = Just (unsafeCoerceP NilFL) -instance PrimPatch prim => CommuteNoConflicts (Patch prim) where- commuteNoConflicts (x:>y) = do x' :< y' <- commuteNoMerger (y :< x)- return (y':>x')+instance PrimPatch prim => CommuteNoConflicts (RepoPatchV1 prim) where+ commuteNoConflicts (x :> y) = do y' :> x' <- commuteNoMerger (x :> y)+ return (y' :> x') -instance PrimPatch prim => Conflict (Patch prim) where- resolveConflicts patch = rcs NilFL (patch :<: NilRL)- where rcs :: FL (Patch prim) wY wW -> RL (Patch prim) wX wY -> [[Sealed (FL prim wW)]]+instance PrimPatch prim => Conflict (RepoPatchV1 prim) where+ resolveConflicts patch = rcs NilFL (NilRL :<: patch)+ where rcs :: FL (RepoPatchV1 prim) wY wW -> RL (RepoPatchV1 prim) wX wY -> [[Sealed (FL prim wW)]] rcs _ NilRL = []- rcs passedby (p@(Merger{}):<:ps) =- case revCommuteFLId commuteNoMerger (passedby:<p) of- Just (p'@(Merger _ _ p1 p2):<_) ->+ rcs passedby (ps:<:p@(Merger{})) =+ case commuteFLId commuteNoMerger (p :> passedby) of+ Just (_ :> p'@(Merger _ _ p1 p2)) -> 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 passedby (ps:<:p) = seq passedby $ rcs (p :>: passedby) ps + conflictedEffect x =+ case listConflictedFiles x of+ [] -> mapFL (IsC Okay) $ effect x+ _ -> mapFL (IsC Conflicted) $ effect x++ -- This type seems wrong - the most natural type for the result would seem to be -- [Sealed (FL Prim wX)], given the type of unwind. -- However downstream code in darcs convert assumes the wY type, and I was unable -- to figure out whether this could/should reasonably be changed -- Ganesh 13/4/10-publicUnravel :: PrimPatch prim => Patch prim wX wY -> [Sealed (FL prim wY)]+publicUnravel :: PrimPatch prim => RepoPatchV1 prim wX wY -> [Sealed (FL prim wY)] publicUnravel = map (mapSeal unsafeCoercePStart) . unravel -unravel :: PrimPatch prim => Patch prim wX wY -> [Sealed (FL prim wX)]+unravel :: PrimPatch prim => RepoPatchV1 prim wX wY -> [Sealed (FL prim wX)] unravel p = nub $ map (mapSeal (sortCoalesceFL . concatFL . mapFL_FL effect)) $ getSupers $ map (mapSeal reverseRL) $ unseal (newUr p) $ unwind p -getSupers :: PrimPatch prim => [Sealed (FL (Patch prim) wX)] -> [Sealed (FL (Patch prim) wX)]+getSupers :: PrimPatch prim+ => [Sealed (FL (RepoPatchV1 prim) wX)] -> [Sealed (FL (RepoPatchV1 prim) wX)] getSupers (x:xs) = case filter (not.(x `isSuperpatchOf`)) xs of xs' -> if any (`isSuperpatchOf` x) xs'@@ -401,10 +414,11 @@ else x : getSupers xs' getSupers [] = [] -isSuperpatchOf :: PrimPatch prim => Sealed (FL (Patch prim) wX) -> Sealed (FL (Patch prim) wX) -> Bool+isSuperpatchOf :: PrimPatch prim+ => Sealed (FL (RepoPatchV1 prim) wX) -> Sealed (FL (RepoPatchV1 prim) wX) -> 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 :: PrimPatch prim => FL (Patch prim) wX wY -> FL (Patch prim) wX wZ -> Bool+ where iso :: PrimPatch prim => FL (RepoPatchV1 prim) wX wY -> FL (RepoPatchV1 prim) wX wZ -> Bool _ `iso` NilFL = True NilFL `iso` _ = False a `iso` (b:>:bs) =@@ -414,7 +428,8 @@ -- constructs a Merger patch to represent the conflict. @p1@ is considered to -- be conflicting with @p2@ (@p1@ is the "first" patch in the repo ordering), -- the resulting Merger is therefore a representation of @p2@.-merger :: PrimPatch prim => String -> Patch prim wX wY -> Patch prim wX wZ -> Sealed (Patch prim wY)+merger :: PrimPatch prim+ => String -> RepoPatchV1 prim wX wY -> RepoPatchV1 prim wX wZ -> Sealed (RepoPatchV1 prim wY) merger "0.0" p1 p2 = Sealed $ Merger undoit unwindings p1 p2 where fake_p = Merger NilFL NilRL p1 p2 unwindings = unsafeUnseal (trueUnwind fake_p)@@ -422,7 +437,7 @@ undoit = case (isMerger p1, isMerger p2) of (True ,True ) -> case unwind p of- Sealed (_:<:t) -> unsafeCoerceP $ invertRL t+ Sealed (t:<:_) -> unsafeCoerceP $ invertRL t _ -> impossible (False,False) -> unsafeCoerceP $ invert p1 :>: NilFL (True ,False) -> unsafeCoerceP NilFL@@ -431,22 +446,23 @@ error $ "Cannot handle mergers other than version 0.0\n"++g ++ "\nPlease use darcs optimize --modernize with an older darcs." -glump09 :: PrimPatch prim => Patch prim wX wY -> Patch prim wX wZ -> Sealed (FL (Patch prim) wY)+glump09 :: PrimPatch prim => RepoPatchV1 prim wX wY -> RepoPatchV1 prim wX wZ -> Sealed (FL (RepoPatchV1 prim) wY) glump09 p1 p2 = mapSeal (mapFL_FL fromPrim) $ mangleUnravelled $ unseal unravel $ merger "0.0" p1 p2 -instance PrimPatch prim => Effect (Patch prim) where+instance PrimPatch prim => Effect (RepoPatchV1 prim) where effect p@(Merger{}) = sortCoalesceFL $ effect $ mergerUndo p effect p@(Regrem{}) = invert $ effect $ invert p effect (PP p) = p :>: NilFL -instance IsHunk prim => IsHunk (Patch prim) where+instance IsHunk prim => IsHunk (RepoPatchV1 prim) where isHunk p = do PP p' <- return p isHunk p' -newUr :: PrimPatch prim => Patch prim wA wB -> RL (Patch prim) wX wY -> [Sealed (RL (Patch prim) wX)]-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')+newUr :: PrimPatch prim+ => RepoPatchV1 prim wA wB -> RL (RepoPatchV1 prim) wX wY -> [Sealed (RL (RepoPatchV1 prim) wX)]+newUr p (ps :<: Merger _ _ p1 p2) =+ case filter (\(_:<:pp) -> pp `unsafeCompare` p1) $ headPermutationsRL ps of+ ((ps':<:_):_) -> newUr p (ps':<:unsafeCoercePStart p1) ++ newUr p (ps':<:unsafeCoercePStart p2) _ -> bugDoc $ text "in function newUr" $$ text "Original patch:" $$ showPatch_ p@@ -454,24 +470,24 @@ $$ vcat (unseal (mapRL showPatch_) $ unwind p) newUr op ps =- case filter (\(p:<:_) -> isMerger p) $ headPermutationsRL ps of+ case filter (\(_:<:p) -> isMerger p) $ headPermutationsRL ps of [] -> [Sealed ps] (ps':_) -> newUr op ps' -instance Invert prim => Invert (Patch prim) where+instance Invert prim => Invert (RepoPatchV1 prim) where invert (Merger undo unwindings p1 p2) = Regrem undo unwindings p1 p2 invert (Regrem undo unwindings p1 p2) = Merger undo unwindings p1 p2 invert (PP p) = PP (invert p) -instance MyEq prim => MyEq (Patch prim) where+instance MyEq prim => MyEq (RepoPatchV1 prim) where unsafeCompare = eqPatches -instance MyEq prim => Eq (Patch prim wX wY) where+instance MyEq prim => Eq (RepoPatchV1 prim wX wY) where (==) = unsafeCompare -eqPatches :: MyEq prim => Patch prim wX wY -> Patch prim wW wZ -> Bool+eqPatches :: MyEq prim => RepoPatchV1 prim wX wY -> RepoPatchV1 prim wW wZ -> Bool eqPatches (PP p1) (PP p2) = unsafeCompare p1 p2 eqPatches (Merger _ _ p1a p1b) (Merger _ _ p2a p2b) = eqPatches p1a p2a &&
src/Darcs/Patch/V1/Core.hs view
@@ -1,15 +1,15 @@ {-# LANGUAGE CPP #-} module Darcs.Patch.V1.Core- ( Patch(..),+ ( RepoPatchV1(..), isMerger, mergerUndo ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Format ( PatchListFormat(..), ListFormat(ListFormatV1) ) import Darcs.Patch.Debug ( PatchDebug(..) )-import Darcs.Patch.MaybeInternal ( MaybeInternal ) import Darcs.Patch.Prim ( FromPrim(..), PrimOf, PrimPatchBase, PrimPatch )-import Darcs.Patch.Rebase.NameHack ( NameHack )-import Darcs.Patch.Rebase.Recontext ( RecontextRebase ) import Darcs.Patch.Repair ( Check ) import Darcs.Patch.Witnesses.Ordered ( FL(..), RL )@@ -34,20 +34,20 @@ @original@ = the patch we really are -}-data Patch prim wX wY where- PP :: prim wX wY -> Patch prim wX wY- Merger :: FL (Patch prim) wX wY- -> RL (Patch prim) wX wB- -> Patch prim wC wB- -> Patch prim wC wD- -> Patch prim wX wY- Regrem :: FL (Patch prim) wX wY- -> RL (Patch prim) wX wB- -> Patch prim wC wB- -> Patch prim wC wA- -> Patch prim wY wX+data RepoPatchV1 prim wX wY where+ PP :: prim wX wY -> RepoPatchV1 prim wX wY+ Merger :: FL (RepoPatchV1 prim) wX wY+ -> RL (RepoPatchV1 prim) wX wB+ -> RepoPatchV1 prim wC wB+ -> RepoPatchV1 prim wC wD+ -> RepoPatchV1 prim wX wY+ Regrem :: FL (RepoPatchV1 prim) wX wY+ -> RL (RepoPatchV1 prim) wX wB+ -> RepoPatchV1 prim wC wB+ -> RepoPatchV1 prim wC wA+ -> RepoPatchV1 prim wY wX -instance Show2 prim => Show (Patch prim wX wY) where+instance Show2 prim => Show (RepoPatchV1 prim wX wY) where showsPrec d (PP p) = showParen (d > appPrec) $ showString "PP " . showsPrec2 (appPrec + 1) p showsPrec d (Merger undos unwindings conflicting original) =@@ -63,39 +63,35 @@ showString " " . showsPrec2 (appPrec + 1) conflicting . showString " " . showsPrec2 (appPrec + 1) original -instance Show2 prim => Show1 (Patch prim wX) where+instance Show2 prim => Show1 (RepoPatchV1 prim wX) where showDict1 = ShowDictClass -instance Show2 prim => Show2 (Patch prim) where+instance Show2 prim => Show2 (RepoPatchV1 prim) where showDict2 = ShowDictClass -instance MaybeInternal (Patch prim)-instance NameHack (Patch prim)-instance RecontextRebase (Patch prim)--instance PrimPatch prim => PrimPatchBase (Patch prim) where- type PrimOf (Patch prim) = prim+instance PrimPatch prim => PrimPatchBase (RepoPatchV1 prim) where+ type PrimOf (RepoPatchV1 prim) = prim -instance FromPrim (Patch prim) where+instance FromPrim (RepoPatchV1 prim) where fromPrim = PP -isMerger :: Patch prim wA wB -> Bool+isMerger :: RepoPatchV1 prim wA wB -> Bool isMerger (Merger{}) = True isMerger (Regrem{}) = True isMerger _ = False -mergerUndo :: Patch prim wX wY -> FL (Patch prim) wX wY+mergerUndo :: RepoPatchV1 prim wX wY -> FL (RepoPatchV1 prim) wX wY mergerUndo (Merger undo _ _ _) = undo mergerUndo _ = impossible -instance PatchListFormat (Patch prim) where+instance PatchListFormat (RepoPatchV1 prim) where -- In principle we could use ListFormatDefault when prim /= V1 Prim patches, -- as those are the only case where we need to support a legacy on-disk -- format. In practice we don't expect Patch to be used with any other argument -- anyway, so it doesn't matter. patchListFormat = ListFormatV1 -instance Check (Patch prim)+instance Check (RepoPatchV1 prim) -- no checks -instance PatchDebug prim => PatchDebug (Patch prim)+instance PatchDebug prim => PatchDebug (RepoPatchV1 prim)
src/Darcs/Patch/V1/Read.hs view
@@ -1,13 +1,16 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module Darcs.Patch.V1.Read () where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Invert ( invert ) import Darcs.Patch.Prim ( PrimPatch ) import Darcs.Patch.Read ( ReadPatch(..) ) import Darcs.Patch.ReadMonads ( ParserM, choice, string, lexChar, myLex', skipSpace ) -import Darcs.Patch.V1.Core ( Patch(..) )+import Darcs.Patch.V1.Core ( RepoPatchV1(..) ) import Darcs.Patch.V1.Commute ( merger ) import Darcs.Patch.Witnesses.Sealed ( Sealed(..), seal, mapSeal )@@ -18,13 +21,13 @@ import qualified Data.ByteString as B (ByteString ) -instance PrimPatch prim => ReadPatch (Patch prim) where+instance PrimPatch prim => ReadPatch (RepoPatchV1 prim) where readPatch' = choice [ liftM seal $ skipSpace >> readMerger True , liftM seal $ skipSpace >> readMerger False , liftM (mapSeal PP) readPatch' ]-readMerger :: (ParserM m, PrimPatch prim) => Bool -> m (Patch prim wX wY)+readMerger :: (ParserM m, PrimPatch prim) => Bool -> m (RepoPatchV1 prim wX wY) readMerger b = do string s g <- myLex' lexChar '('
src/Darcs/Patch/V1/Show.hs view
@@ -1,21 +1,24 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module Darcs.Patch.V1.Show ( showPatch_ ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Format ( FileNameFormat(..) ) import Darcs.Patch.Prim ( showPrim, PrimPatch ) -import Darcs.Patch.V1.Core ( Patch(..) )+import Darcs.Patch.V1.Core ( RepoPatchV1(..) ) import Darcs.Util.Printer ( Doc, text, blueText, ($$), (<+>) ) -showPatch_ :: PrimPatch prim => Patch prim wA wB -> Doc+showPatch_ :: PrimPatch prim => RepoPatchV1 prim wA wB -> Doc showPatch_ (PP p) = showPrim OldFormat p showPatch_ (Merger _ _ p1 p2) = showMerger "merger" p1 p2 showPatch_ (Regrem _ _ p1 p2) = showMerger "regrem" p1 p2 -showMerger :: PrimPatch prim => String -> Patch prim wA wB -> Patch prim wD wE -> Doc+showMerger :: PrimPatch prim => String -> RepoPatchV1 prim wA wB -> RepoPatchV1 prim wD wE -> Doc showMerger merger_name p1 p2 = blueText merger_name <+> text "0.0" <+> blueText "(" $$ showPatch_ p1
src/Darcs/Patch/V1/Viewing.hs view
@@ -1,19 +1,22 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module Darcs.Patch.V1.Viewing () where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Prim ( PrimPatch ) import Darcs.Patch.Show ( ShowPatchBasic(..), ShowPatch(..) ) import Darcs.Patch.Summary ( plainSummary ) import Darcs.Patch.V1.Apply ()-import Darcs.Patch.V1.Core ( Patch(..) )+import Darcs.Patch.V1.Core ( RepoPatchV1(..) ) import Darcs.Patch.V1.Show ( showPatch_ ) -instance PrimPatch prim => ShowPatchBasic (Patch prim) where+instance PrimPatch prim => ShowPatchBasic (RepoPatchV1 prim) where showPatch = showPatch_ -instance PrimPatch prim => ShowPatch (Patch prim) where+instance PrimPatch prim => ShowPatch (RepoPatchV1 prim) where showContextPatch (PP p) = showContextPatch p showContextPatch p = return $ showPatch p summary = plainSummary
src/Darcs/Patch/V2.hs view
@@ -1,11 +1,11 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}-module Darcs.Patch.V2 ( RealPatch, prim2real ) where+module Darcs.Patch.V2 ( RepoPatchV2, prim2repopatchV2 ) where import Darcs.Patch.Matchable ( Matchable ) import Darcs.Patch.Prim ( PrimPatch ) import Darcs.Patch.RepoPatch ( RepoPatch ) -import Darcs.Patch.V2.Real ( RealPatch, prim2real )+import Darcs.Patch.V2.RepoPatch ( RepoPatchV2, prim2repopatchV2 ) -instance PrimPatch prim => Matchable (RealPatch prim)-instance PrimPatch prim => RepoPatch (RealPatch prim)+instance PrimPatch prim => Matchable (RepoPatchV2 prim)+instance PrimPatch prim => RepoPatch (RepoPatchV2 prim)
src/Darcs/Patch/V2/Non.hs view
@@ -39,11 +39,9 @@ , (>>*) ) where -#if MIN_VERSION_base(4,8,0)-import Prelude hiding ( rem, (*>) )-#else-import Prelude hiding ( rem )-#endif+import Prelude ()+import Darcs.Prelude hiding ( (*>) )+ import Data.List ( delete ) import Control.Monad ( liftM, mzero ) import Darcs.Patch.Commute ( commuteFL )@@ -160,7 +158,7 @@ commuteOrAddToCtxRL :: (Patchy p, ToFromPrim p) => RL p wX wY -> Non p wY -> Non p wX commuteOrAddToCtxRL NilRL n = n-commuteOrAddToCtxRL (p:<:ps) n = commuteOrAddToCtxRL ps $ commuteOrAddToCtx p n+commuteOrAddToCtxRL (ps:<:p) n = commuteOrAddToCtxRL ps $ commuteOrAddToCtx p n -- |abstract over 'FL'/'RL' class WL l where@@ -268,4 +266,4 @@ commuteRLPastNon :: (Patchy p, ToFromPrim p) => RL (PrimOf p) wX wY -> Non p wY -> Maybe (Non p wX) commuteRLPastNon NilRL n = Just n- commuteRLPastNon (x:<:xs) n = fromPrim x >* n >>= commuteRLPastNon xs+ commuteRLPastNon (xs:<:x) n = fromPrim x >* n >>= commuteRLPastNon xs
− src/Darcs/Patch/V2/Real.hs
@@ -1,952 +0,0 @@--- Copyright (C) 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.--{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-name-shadowing #-}-{-# LANGUAGE CPP #-}---module Darcs.Patch.V2.Real- ( RealPatch(..)- , prim2real- , isConsistent- , isForward- , isDuplicate- , mergeUnravelled- ) where--#if MIN_VERSION_base(4,8,0)-import Prelude hiding ( (*>) )-#endif--import Control.Monad ( mplus, liftM )-import qualified Data.ByteString.Char8 as BC ( ByteString, pack )-import Data.Maybe ( fromMaybe )-import Data.List ( partition, nub )--import Darcs.Patch.Commute ( commuteFL, commuteFLorComplain, commuteRL- , commuteRLFL )-import Darcs.Patch.Conflict ( Conflict(..), CommuteNoConflicts(..)- , IsConflictedPrim(..), ConflictState(..) )-import Darcs.Patch.ConflictMarking ( mangleUnravelled )-import Darcs.Patch.Debug-import Darcs.Patch.Effect ( Effect(..) )-import Darcs.Patch.FileHunk ( IsHunk(..) )-import Darcs.Patch.Format ( PatchListFormat(..), ListFormat(..)- , FileNameFormat(NewFormat) )-import Darcs.Patch.Invert ( invertFL, invertRL )-import Darcs.Patch.MaybeInternal ( MaybeInternal )-import Darcs.Patch.Merge ( Merge(..) )-import Darcs.Patch.Prim ( FromPrim(..), ToFromPrim(..), showPrim, showPrimFL- , readPrim, PrimOf, PrimPatchBase, PrimPatch )-import Darcs.Patch.Read ( bracketedFL )-import Darcs.Patch.ReadMonads ( skipSpace, string, choice )-import Darcs.Patch.Rebase.NameHack ( NameHack )-import Darcs.Patch.Rebase.Recontext ( RecontextRebase )-import Darcs.Patch.Repair ( mapMaybeSnd, RepairToFL(..), Check(..) )-import Darcs.Patch.Patchy ( Patchy, Apply(..), Commute(..), PatchInspect(..)- , ReadPatch(..), ShowPatch(..), Invert(..) )-import Darcs.Patch.Permutations ( commuteWhatWeCanFL, commuteWhatWeCanRL- , genCommuteWhatWeCanRL, removeRL, removeFL- , removeSubsequenceFL )-import Darcs.Patch.Show ( ShowPatchBasic(..) )-import Darcs.Patch.Summary ( plainSummary )-import Darcs.Patch.V2.Non ( Non(..), Nonable(..), unNon, showNons, showNon- , readNons, readNon, commutePrimsOrAddToCtx- , commuteOrAddToCtx, commuteOrAddToCtxRL- , commuteOrRemFromCtx, commuteOrRemFromCtxFL- , remNons, (*>), (>*), (*>>), (>>*) )-import Data.List.Ordered ( nubSort )-import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )-import Darcs.Patch.Witnesses.Eq ( MyEq(..), EqCheck(..) )-import Darcs.Patch.Witnesses.Ordered- ( FL(..), RL(..), (:>)(..), (+>+), (+<+)- , mapFL_FL, reverseFL, (:\/:)(..), (:/\:)(..)- , reverseRL, lengthFL, lengthRL, nullFL )-import Darcs.Patch.Witnesses.Sealed- ( FlippedSeal(..), Sealed(Sealed), mapSeal- , unseal )-import Darcs.Patch.Witnesses.Show- ( Show1(..), Show2(..), ShowDict(..)- , showsPrec2, appPrec- )-import Darcs.Util.Printer.Color ( errorDoc, assertDoc )--import Darcs.Util.Printer ( Doc, blueText, redText, (<+>), ($$) )--#include "impossible.h"---- |'RealPatch' is used to represents prim patches that are duplicates of, or--- conflict with, another prim patch in the repository.------ @Normal prim@: A primitive patch------ @Duplicate x@: This patch has no effect since @x@ is already present in the--- repository.------ @Etacilpud x: invert (Duplicate x)@------ @Conflictor ix xx x@:--- @ix@ is the set of patches:--- * that conflict with @x@ and also conflict with another patch in the--- repository.--- * that conflict with a patch that conflict with @x@------ @xx@ is the sequence of patches that conflict *only* with @x@------ @x@ is the original, conflicting patch.------ @ix@ and @x@ are stored as @Non@ objects, which include any necessary--- context to uniquely define the patch that is referred to.------ The intuition is that a Conflictor should have the effect of inverting any--- patches that 'x' conflicts with, that haven't already been undone by another--- Conflictor in the repository.--- Therefore, the effect of a Conflictor is @invert xx@.------ @InvConflictor ix xx x@: like @invert (Conflictor ix xx x)@-data RealPatch prim wX wY where- Duplicate :: Non (RealPatch prim) wX -> RealPatch prim wX wX- Etacilpud :: Non (RealPatch prim) wX -> RealPatch prim wX wX- Normal :: prim wX wY -> RealPatch prim wX wY- Conflictor :: [Non (RealPatch prim) wX] -> FL prim wX wY- -> Non (RealPatch prim) wX -> RealPatch prim wY wX- InvConflictor :: [Non (RealPatch prim) wX] -> FL prim wX wY- -> Non (RealPatch prim) wX -> RealPatch prim wX wY--instance PrimPatch prim => PrimPatchBase (RealPatch prim) where- type PrimOf (RealPatch prim) = prim---- | 'isDuplicate' @p@ is @True@ if @p@ is either a 'Duplicate' or 'Etacilpud'--- patch.-isDuplicate :: RealPatch prim wS wY -> Bool-isDuplicate (Duplicate _) = True-isDuplicate (Etacilpud _) = True-isDuplicate _ = False---- | 'isForward' @p@ is @True@ if @p@ is either an 'InvConflictor' or--- 'Etacilpud'.-isForward :: PrimPatch prim => RealPatch prim wS wY -> Maybe Doc-isForward p = case p of- p@(InvConflictor{}) -> justRedP "An inverse conflictor" p- p@(Etacilpud _) -> justRedP "An inverse duplicate" p- _ -> Nothing- where- justRedP msg p = Just $ redText msg $$ showPatch p---- |'mergeUnravelled' is used when converting from Darcs V1 patches (Mergers)--- to Darcs V2 patches (Conflictors).-mergeUnravelled :: PrimPatch prim => [Sealed ((FL prim) wX)]- -> Maybe (FlippedSeal (RealPatch prim) wX)-mergeUnravelled [] = Nothing-mergeUnravelled [_] = Nothing-mergeUnravelled ws =- case mergeUnravelled_private ws of- Nothing -> Nothing- Just NilRL -> bug "found no patches in mergeUnravelled"- Just (z :<: _) -> Just $ FlippedSeal z- where- notNullS :: PrimPatch prim => Sealed ((FL prim) wX) -> Bool- notNullS (Sealed NilFL) = False- notNullS _ = True-- mergeUnravelled_private :: PrimPatch prim => [Sealed (FL prim wX)]- -> Maybe (RL (RealPatch prim) wX wX)- mergeUnravelled_private xs = let nonNullXs = filter notNullS xs in- reverseFL `fmap` mergeConflictingNons (map sealed2non nonNullXs)-- -- | 'sealed2non' @(Sealed xs)@ converts @xs@ to a 'Non'.- -- @xs@ must be non-empty since we split this list at the last patch,- -- taking @init xs@ as the context of @last xs@.- sealed2non :: Sealed ((FL prim) wX) -> Non (RealPatch prim) wX- sealed2non (Sealed xs) =- case reverseFL xs of- y :<: ys -> Non (mapFL_FL fromPrim $ reverseRL ys) y- NilRL -> bug "NilFL encountered in sealed2non"--mergeConflictingNons :: PrimPatch prim => [Non (RealPatch prim) wX]- -> Maybe (FL (RealPatch prim) wX wX)-mergeConflictingNons ns = mcn $ map unNon ns- where mcn :: PrimPatch prim => [Sealed (FL (RealPatch prim) wX)]- -> Maybe (FL (RealPatch prim) wX wX)- mcn [] = Just NilFL- -- Apparently, the joinEffects call is a safety check "and could be- -- removed when we're sure of the code"!- mcn [Sealed p] = case joinEffects p of- NilFL -> Just p- _ -> Nothing- mcn (Sealed p1:Sealed p2:zs) =- case pullCommon p1 p2 of- Common c ps qs ->- case merge (ps :\/: qs) of- qs' :/\: _ -> mcn (Sealed (c +>+ ps +>+ qs'):zs)--joinEffects :: forall p wX wY . (Effect p, Invert (PrimOf p),- Commute (PrimOf p), MyEq (PrimOf p)) => p wX wY- -> FL (PrimOf p) wX wY-joinEffects = joinInverses . effect- where joinInverses :: FL (PrimOf p) wA wB -> FL (PrimOf p) wA wB- joinInverses NilFL = NilFL- joinInverses (p :>: ps) =- let ps' = joinInverses ps in- fromMaybe (p :>: ps') $ removeFL (invert p) ps'--assertConsistent :: PrimPatch prim => RealPatch prim wX wY- -> RealPatch prim wX wY-assertConsistent x = flip assertDoc x $ do- e <- isConsistent x- Just (redText "Inconsistent patch:" $$ showPatch x $$ e)---- | @mergeAfterConflicting@ takes as input a sequence of conflicting patches--- @xxx@ (which therefore have no effect) and a sequence of primitive patches--- @yyy@ that follow said sequence of conflicting patches, and may depend upon--- some of the conflicting patches (as a resolution).---- The output is two sequences of patches the first consisting of a set of--- mutually-conflicting patches, and the second having the same effect as the--- original primitive patch sequence in the input.---- So far as I can tell, the second output is always identical to @mapFL Normal--- yyy@---- The first output is the set of patches from @xxx@ that are depended upon by--- @yyy@.-mergeAfterConflicting :: PrimPatch prim => FL (RealPatch prim) wX wX- -> FL prim wX wY -> Maybe ( FL (RealPatch prim) wX wX- , FL (RealPatch prim) wX wY)-mergeAfterConflicting xxx yyy = mac (reverseFL xxx) yyy NilFL- where- mac :: PrimPatch prim- => RL (RealPatch prim) wX wY -> FL prim wY wZ- -> FL (RealPatch prim) wZ wA- -> Maybe (FL (RealPatch prim) wX wX, FL (RealPatch prim) wX wA)- mac NilRL xs goneby = case joinEffects goneby of- NilFL -> Just (NilFL, mapFL_FL Normal xs)- _ -> Nothing- mac (p :<: ps) xs goneby =- case commuteFLorComplain (p :> mapFL_FL Normal xs) of- Left _ ->- case genCommuteWhatWeCanRL commuteNoConflicts (ps :> p) of- a :> p' :> b ->- do (b', xs') <- mac b xs goneby- let pa = joinEffects $ p' :<: a- NilFL <- return pa- return (reverseRL (p' :<: a) +>+ b', xs')- `mplus`- do NilFL <- return goneby- NilFL <- return $ joinEffects (p :<: ps)- return (reverseRL (p :<: ps), mapFL_FL Normal xs)- Right (l :> p'') ->- case allNormal l of- Just xs'' -> mac ps xs'' (p'' :>: goneby)- Nothing ->- case genCommuteWhatWeCanRL commuteNoConflicts (ps :> p) of- a :> p' :> b ->- do (b', xs') <- mac b xs goneby- let pa = joinEffects $ p' :<: a- NilFL <- return pa- return (reverseRL (p' :<: a) +>+ b', xs')--geteff :: PrimPatch prim => [Non (RealPatch prim) wX] -> FL prim wX wY- -> ([Non (RealPatch prim) wX], FL (RealPatch prim) wX wY)-geteff _ NilFL = ([], NilFL)-geteff ix (x :>: xs) | Just ix' <- mapM (commuteOrRemFromCtx (Normal x)) ix =- case geteff ix' xs of- (ns, xs') -> ( non (Normal x) : map (commuteOrAddToCtx (Normal x)) ns- , Normal x :>: xs')-geteff ix xx =- case mergeConflictingNons ix of- Nothing -> errorDoc $- redText "mergeConflictingNons failed in geteff: ix" $$- showNons ix $$ redText "xx" $$ showPatch xx- Just rix ->- case mergeAfterConflicting rix xx of- Just (a, x) ->- ( map (commuteOrAddToCtxRL (reverseFL a)) $ toNons x- , a +>+ x)- Nothing ->- errorDoc $- redText "mergeAfterConflicting failed in geteff" $$- redText "where ix" $$ showNons ix $$- redText "and xx" $$ showPatch xx $$- redText "and rix" $$ showPatch rix--xx2nons :: PrimPatch prim => [Non (RealPatch prim) wX] -> FL prim wX wY- -> [Non (RealPatch prim) wX]-xx2nons ix xx = fst $ geteff ix xx--xx2patches :: PrimPatch prim => [Non (RealPatch prim) wX] -> FL prim wX wY- -> FL (RealPatch prim) wX wY-xx2patches ix xx = snd $ geteff ix xx---- | If @xs@ consists only of 'Normal' patches, 'allNormal' @xs@ returns--- @Just pxs@ those patches (so @lengthFL pxs == lengthFL xs@).--- Otherwise, it returns 'Nothing'.-allNormal :: FL (RealPatch prim) wX wY -> Maybe (FL prim wX wY)-allNormal (Normal x :>: xs) = (x :>: ) `fmap` allNormal xs-allNormal NilFL = Just NilFL-allNormal _ = Nothing---- | This is used for unit-testing and for internal sanity checks-isConsistent :: PrimPatch prim => RealPatch prim wX wY -> Maybe Doc-isConsistent (Normal _) = Nothing-isConsistent (Duplicate _) = Nothing-isConsistent (Etacilpud _) = Nothing-isConsistent c@(InvConflictor{}) = isConsistent (invert c)-isConsistent (Conflictor im mm m@(Non deps _))- | not $ everyoneConflicts im =- Just $ redText "Someone doesn't conflict in im in isConsistent"- | Just _ <- commuteOrRemFromCtxFL rmm m, _ :>: _ <- mm =- Just $ redText "m doesn't conflict with mm in isConsistent"- | any (\x -> any (x `conflictsWith`) nmm) im =- Just $ redText "mm conflicts with im in isConsistent where nmm is" $$- showNons nmm- | Nothing <- (nmm ++ im) `minus` toNons deps =- Just $ redText "dependencies not in conflict:" $$- showNons (toNons deps) $$- redText "compared with deps itself:" $$- showPatch deps- | otherwise =- case allConflictsWith m im of- (im1, []) | im1 `eqSet` im -> Nothing- (_, imnc) -> Just $ redText ("m doesn't conflict with im in "- ++ "isConsistent. unconflicting:") $$- showNons imnc- where (nmm, rmm) = geteff im mm--everyoneConflicts :: PrimPatch prim => [Non (RealPatch prim) wX] -> Bool-everyoneConflicts [] = True-everyoneConflicts (x : xs) = case allConflictsWith x xs of- ([], _) -> False- (_, xs') -> everyoneConflicts xs'--prim2real :: prim wX wY -> RealPatch prim wX wY-prim2real = Normal--instance MaybeInternal (RealPatch prim)-instance NameHack (RealPatch prim)-instance RecontextRebase (RealPatch prim)-instance PrimPatch prim => Patchy (RealPatch prim)-instance PatchDebug prim => PatchDebug (RealPatch prim)--mergeWith :: PrimPatch prim => Non (RealPatch prim) wX- -> [Non (RealPatch prim) wX] -> Sealed (FL prim wX)-mergeWith p [] = effect `mapSeal` unNon p-mergeWith p xs =- mergeall . map unNon . (p :) . unconflicting_of $ nonDependsOrConflictsP xs- where- nonDependsOrConflictsP =- filter (\x -> not ((p `dependsUpon` x) || (p `conflictsWith` x)))- mergeall :: PrimPatch prim => [Sealed (FL (RealPatch prim) wX)]- -> Sealed (FL prim wX)- mergeall [Sealed x] = Sealed $ effect x- mergeall [] = Sealed NilFL- mergeall (Sealed x : Sealed y : rest) =- case merge (x :\/: y) of- y' :/\: _ -> mergeall (Sealed (x +>+ y') : rest)- unconflicting_of [] = []- unconflicting_of (q : qs) = case allConflictsWith q qs of- ([], _) -> q : qs- (_, nc) -> unconflicting_of nc--instance PrimPatch prim => Conflict (RealPatch prim) where- conflictedEffect (Duplicate (Non _ x)) = [IsC Duplicated x]- conflictedEffect (Etacilpud _) = impossible- conflictedEffect (Conflictor _ _ (Non _ x)) = [IsC Conflicted x]- conflictedEffect (InvConflictor{}) = impossible- conflictedEffect (Normal x) = [IsC Okay x]- resolveConflicts (Conflictor ix xx x) = [mangledUnravelled : unravelled]- where- mangledUnravelled = mangleUnravelled unravelled- unravelled = nub $ filter isCons $ map (`mergeWith` xIxNonXX) xIxNonXX- xIxNonXX = x : ix ++ nonxx- nonxx = nonxx_ (reverseFL $ xx2patches ix xx)- -- |nonxx_ takes an RL of patches, and returns a singleton list- -- containing a Non, in the case where we have a Normal patch at the- -- end of the list (using the rest of the RL as context), and an empty- -- list otherwise.- nonxx_ :: RL (RealPatch prim) wX wY -> [Non (RealPatch prim) wX]- nonxx_ (Normal q :<: qs) = [Non (reverseRL qs) q]- nonxx_ _ = []- isCons = unseal (not . nullFL)- resolveConflicts _ = []--instance PrimPatch prim => CommuteNoConflicts (RealPatch prim) where- commuteNoConflicts (d1@(Duplicate _) :> d2@(Duplicate _)) = Just (d2 :> d1)- commuteNoConflicts (e@(Etacilpud _) :> d@(Duplicate _)) = Just (d :> e)- commuteNoConflicts (d@(Duplicate _) :> e@(Etacilpud _)) = Just (e :> d)- commuteNoConflicts (e1@(Etacilpud _) :> e2@(Etacilpud _)) = Just (e2 :> e1)-- -- If the duplicate is @x@, as a 'Non', with @invert x@ as the context,- -- then it is the patch the duplicate @d@ represents, so commuting results- -- in the same two patches (since we'd make one a duplicate, and the other- -- would become @x@ as it would no longer be duplicated).- -- Otherwise, we commute past, or remove @invert x@ from the context of @d@- -- to obtain a new Duplicate.- commuteNoConflicts orig@(x :> Duplicate d) =- if d == commuteOrAddToCtx (invert x) (non x)- then Just orig- else do d' <- commuteOrRemFromCtx (invert x) d- return (Duplicate d' :> x)-- -- Commuting a Duplicate and any other patch simply places @invert x@ into- -- the context of the non @d@, by commuting past, or adding to the context.- commuteNoConflicts (Duplicate d :> x) =- Just (x :> Duplicate (commuteOrAddToCtx (invert x) d))-- -- handle Etacilpud cases by first inverting, then using the previous- -- definitions.- commuteNoConflicts c@(Etacilpud _ :> _) = invertCommuteNC c- commuteNoConflicts c@(_ :> Etacilpud _) = invertCommuteNC c-- -- Two normal patches should be simply commuted (assuming the can).- commuteNoConflicts (Normal x :> Normal y) = do- y' :> x' <- commute (x :> y)- return (Normal y' :> Normal x')-- -- Commuting a Normal patch past a Conflictor first commutes @x@ past the- -- effect of the Conflictor, then commutes the resulting @x'@ past the- -- conflicting patch and the already-undone patches. The commuting must be- -- done in this order to make the contexts match up (@iy@ and @y@ are made- -- in the context before @yy@ have their effect, so we need to commute past- -- the effect of @yy@ first).- commuteNoConflicts (Normal x :> Conflictor iy yy y) = do- iyy' :> x' <- commuteFL (x :> invert yy)- y' : iy' <- mapM (Normal x' >*) (y : iy)- return (Conflictor iy' (invert iyy') y' :> Normal x')-- -- Handle via the previous case, using the inverting commuter.- commuteNoConflicts c@(InvConflictor{} :> Normal _) = invertCommuteNC c-- -- Commuting a Conflictor past a Normal patch is the dual operation to- -- commuting a Normal patch past a Conflictor.- commuteNoConflicts (Conflictor iy yy y :> Normal x) = do- y' : iy' <- mapM (*> Normal x) (y : iy)- x' :> iyy' <- commuteRL (invertFL yy :> x)- return (Normal x' :> Conflictor iy' (invertRL iyy') y')-- -- Handle via the previous case, using the inverting commuter.- commuteNoConflicts c@(Normal _ :> InvConflictor{}) = invertCommuteNC c-- -- Commuting two Conflictors, c1 and c2, first commutes the Conflictors'- -- effects, then commutes the effect of c1 and c2 and the other's- -- already-undone, and conflicting patch, to bring the already-undone and- -- conflicting patch into the context of the commuted effects.- commuteNoConflicts (Conflictor ix xx x :> Conflictor iy yy y) = do- xx' :> yy' <- commute (yy :> xx)- x':ix' <- mapM (yy >>*) (x:ix)- y':iy' <- mapM (*>> xx') (y:iy)- False <- return $ any (conflictsWith y) (x':ix')- False <- return $ any (conflictsWith x') iy- return (Conflictor iy' yy' y' :> Conflictor ix' xx' x')-- -- Handle via the previous case, using the inverting commuter.- commuteNoConflicts c@(InvConflictor{} :> InvConflictor{}) =- invertCommuteNC c-- commuteNoConflicts (InvConflictor ix xx x :> Conflictor iy yy y) = do- iyy' :> xx' <- commute (xx :> invert yy)- y':iy' <- mapM (xx' >>*) (y:iy)- x':ix' <- mapM (invertFL iyy' >>*) (x:ix)- False <- return $ any (conflictsWith y') (x':ix')- False <- return $ any (conflictsWith x') iy'- return (Conflictor iy' (invert iyy') y' :> InvConflictor ix' xx' x')-- commuteNoConflicts (Conflictor iy' yy' y' :> InvConflictor ix' xx' x') = do- xx :> iyy <- commute (invert yy' :> xx')- y:iy <- mapM (*>> xx') (y':iy')- x:ix <- mapM (*>> yy') (x':ix')- False <- return $ any (conflictsWith y') (x':ix')- False <- return $ any (conflictsWith x') iy'- return (InvConflictor ix xx x :> Conflictor iy (invert iyy) y)--instance PrimPatch prim => Check (RealPatch prim) where- isInconsistent = isConsistent--instance FromPrim (RealPatch prim) where- fromPrim = prim2real--instance ToFromPrim (RealPatch prim) where- toPrim (Normal p) = Just p- toPrim _ = Nothing--instance PrimPatch prim => MyEq (RealPatch prim) where- (Duplicate x) =\/= (Duplicate y) | x == y = IsEq- (Etacilpud x) =\/= (Etacilpud y) | x == y = IsEq- (Normal x) =\/= (Normal y) = x =\/= y- (Conflictor cx xx x) =\/= (Conflictor cy yy y)- | map commuteOrAddIXX cx `eqSet` map commuteOrAddIYY cy- && commuteOrAddIXX x == commuteOrAddIYY y = xx =/\= yy- where- commuteOrAddIXX = commutePrimsOrAddToCtx (invertFL xx)- commuteOrAddIYY = commutePrimsOrAddToCtx (invertFL yy)- (InvConflictor cx xx x) =\/= (InvConflictor cy yy y)- | cx `eqSet` cy && x == y = xx =\/= yy- _ =\/= _ = NotEq--eqSet :: Eq a => [a] -> [a] -> Bool-eqSet [] [] = True-eqSet (x:xs) xys | Just ys <- remove1 x xys = eqSet xs ys-eqSet _ _ = False--remove1 :: Eq a => a -> [a] -> Maybe [a]-remove1 x (y : ys) = if x == y then Just ys else (y :) `fmap` remove1 x ys-remove1 _ [] = Nothing--minus :: Eq a => [a] -> [a] -> Maybe [a]-minus xs [] = Just xs-minus xs (y:ys) = do xs' <- remove1 y xs- xs' `minus` ys--invertNon :: PrimPatch prim => Non (RealPatch prim) wX- -> Non (RealPatch prim) wX-invertNon (Non c x)- | Just rc' <- removeRL nix (reverseFL c) = Non (reverseRL rc') (invert x)- | otherwise = commuteOrAddToCtxRL (Normal x :<: reverseFL c) $ non nix- where- nix = Normal $ invert x--nonTouches :: PatchInspect prim => Non (RealPatch prim) wX -> [FilePath]-nonTouches (Non c x) = listTouchedFiles (c +>+ fromPrim x :>: NilFL)--nonHunkMatches :: PatchInspect prim => (BC.ByteString -> Bool)- -> Non (RealPatch prim) wX -> Bool-nonHunkMatches f (Non c x) = hunkMatches f c || hunkMatches f x--toNons :: forall p wX wY . (Conflict p, Patchy p, PatchListFormat p,- ToFromPrim p, Nonable p, ShowPatchBasic (PrimOf p), ShowPatchBasic p)- => FL p wX wY -> [Non p wX]-toNons xs = map lastNon $ initsFL xs- where lastNon :: Sealed ((p :> FL p) wX) -> Non p wX- lastNon (Sealed xxx) =- case lastNon_aux xxx of- deps :> p :> _ ->- case non p of- Non NilFL pp -> Non (reverseRL deps) pp- Non ds pp ->- errorDoc $ redText "Weird case in toNons" $$- redText "please report this bug!" $$- (case xxx of- z :> zs -> showPatch (z :>: zs)) $$- redText "ds are" $$ showPatch ds $$- redText "pp is" $$ showPatch pp-- reverseFoo :: (p :> FL p) wX wZ -> (RL p :> p) wX wZ- reverseFoo (p :> ps) = rf NilRL p ps- where- rf :: RL p wA wB -> p wB wC -> FL p wC wD- -> (RL p :> p) wA wD- rf rs l NilFL = rs :> l- rf rs x (y :>: ys) = rf (x :<: rs) y ys-- lastNon_aux :: (p :> FL p) wX wZ -> (RL p :> p :> RL p) wX wZ- lastNon_aux = commuteWhatWeCanRL . reverseFoo--initsFL :: Patchy p => FL p wX wY -> [Sealed ((p :> FL p) wX)]-initsFL NilFL = []-initsFL (x :>: xs) =- Sealed (x :> NilFL) :- map (\(Sealed (y :> xs')) -> Sealed (x :> y :>: xs')) (initsFL xs)--filterConflictsFL :: PrimPatch prim => Non (RealPatch prim) wX- -> FL prim wX wY -> (FL prim :> FL prim) wX wY-filterConflictsFL _ NilFL = NilFL :> NilFL-filterConflictsFL n (p :>: ps)- | Just n' <- commuteOrRemFromCtx (fromPrim p) n =- case filterConflictsFL n' ps of- p1 :> p2 -> p :>: p1 :> p2- | otherwise = case commuteWhatWeCanFL (p :> ps) of- p1 :> p' :> p2 ->- case filterConflictsFL n p1 of- p1a :> p1b -> p1a :> p1b +>+ p' :>: p2--instance Invert prim => Invert (RealPatch prim) where- invert (Duplicate d) = Etacilpud d- invert (Etacilpud d) = Duplicate d- invert (Normal p) = Normal (invert p)- invert (Conflictor x c p) = InvConflictor x c p- invert (InvConflictor x c p) = Conflictor x c p--instance PrimPatch prim => Commute (RealPatch prim) where- commute (x :> y) | Just (y' :> x') <-- commuteNoConflicts (assertConsistent x :> assertConsistent y) =- Just (y' :> x')-- -- These patches conflicted, since we failed to commuteNoConflicts in the- -- case above.- commute (Normal x :> Conflictor a1'nop2 n1'x p1')- | Just rn1' <- removeRL x (reverseFL n1'x) = do- let p2 : n1nons = reverse $ xx2nons a1'nop2 $ reverseRL (x :<: rn1')- a2 = p1' : a1'nop2 ++ n1nons- case (a1'nop2, reverseRL rn1', p1') of- ([], NilFL, Non c y) | NilFL <- joinEffects c ->- Just (Normal y :> Conflictor a1'nop2 (y :>: NilFL) p2)- (a1, n1, _) ->- Just (Conflictor a1 n1 p1' :> Conflictor a2 NilFL p2)-- -- Handle using the inverting commuter, and the previous case. N.b. this- -- is innefficient, since we'll have to also try commuteNoConflicts again- -- (which we know will fail, since we got here).- commute c@(InvConflictor{} :> Normal _) = invertCommute c-- commute (Conflictor a1 n1 p1 :> Conflictor a2 n2 p2)- | Just a2_minus_p1 <- remove1 p1' a2- , not (p2 `dependsUpon` p1') = do- let n1nons = map (commutePrimsOrAddToCtx n2) $ xx2nons a1 n1- n2nons = xx2nons a2 n2- Just a2_minus_p1n1 = a2_minus_p1 `minus` n1nons- n2n1 = n2 +>+ n1- a1' = map (commutePrimsOrAddToCtx n2) a1- p2ooo = remNons a1' p2- n1' :> n2' <- return $ filterConflictsFL p2ooo n2n1- let n1'n2'nons = xx2nons a2_minus_p1n1 (n1' +>+ n2')- n1'nons = take (lengthFL n1') n1'n2'nons- n2'nons = drop (lengthFL n1') n1'n2'nons- Just a1'nop2 = (a2 ++ n2nons) `minus` (p1' : n1'nons)- Just a2'o =- fst (allConflictsWith p2 $ a2_minus_p1 ++ n2nons)- `minus` n2'nons- Just a2' =- mapM (commuteOrRemFromCtxFL (xx2patches a1'nop2 n1')) a2'o- Just p2' = commuteOrRemFromCtxFL (xx2patches a1'nop2 n1') p2- case (a2', n2', p2') of- ([], NilFL, Non c x) ->- case joinEffects c of- NilFL -> let n1'x = n1' +>+ x :>: NilFL in- Just (Normal x :> Conflictor a1'nop2 n1'x p1')- _ -> impossible- _ -> Just (c1 :> c2)- where- c1 = Conflictor a2' n2' p2'- c2 = Conflictor (p2 : a1'nop2) n1' p1'-- where (_, rpn2) = geteff a2 n2- p1' = commuteOrAddToCtxRL (reverseFL rpn2) p1-- -- Handle using the inverting commuter, and the previous case. This is also- -- innefficient, since we'll have to also try commuteNoConflicts again- -- (which we know will fail, since we got here).- commute c@(InvConflictor{} :> InvConflictor{}) = invertCommute c-- commute _ = Nothing--instance PrimPatch prim => Merge (RealPatch prim) where- merge (InvConflictor{} :\/: _) = impossible- merge (_ :\/: InvConflictor{}) = impossible- merge (Etacilpud _ :\/: _) = impossible- merge (_ :\/: Etacilpud _) = impossible--- merge (Duplicate a :\/: Duplicate b) = Duplicate b :/\: Duplicate a- -- We had a FIXME comment on this case, why?- merge (Duplicate a :\/: b) =- b :/\: Duplicate (commuteOrAddToCtx (invert b) a)-- -- Handle using the swap merge and the previous case.- merge m@(_ :\/: Duplicate _) = swapMerge m-- -- When merging x and y, we do a bunch of what look like "consistency"- -- check merges. If the resulting y'' and y are equal, then we succeed.- -- If the first case fails, we check for equal patches (which wouldn't- -- commute) and return a Duplicate on both sides of the merge, in that- -- case.- merge (x :\/: y)- | Just (y' :> ix') <-- commute (invert (assertConsistent x) :> assertConsistent y)- , Just (y'' :> _) <- commute (x :> y')- , IsEq <- y'' =\/= y =- assertConsistent y' :/\: invert (assertConsistent ix')- -- If we detect equal patches, we have a duplicate.- | IsEq <- x =\/= y- , n <- commuteOrAddToCtx (invert x) $ non x =- Duplicate n :/\: Duplicate n-- -- We know that these two patches conflict, and aren't Duplicates, since we- -- failed the previous case. We therefore create basic Conflictors, which- -- undo the other patch.- merge (nx@(Normal x) :\/: ny@(Normal y)) = cy :/\: cx- where- cy = Conflictor [] (x :>: NilFL) (non ny)- cx = Conflictor [] (y :>: NilFL) (non nx)-- -- If a Normal patch @x@ and a Conflictor @cy@ conflict, we add @x@ to the- -- effect of @cy@ on one side, and create a Conflictor that has no effect,- -- but has the already-undone and conflicted patch of @cy@ and some foos as- -- the already-undone on the other side.- --- -- TODO: what is foo?- -- Why do we need nyy? I think @x'@ is @x@ in the context of @yy@.- merge (Normal x :\/: Conflictor iy yy y) =- Conflictor iy yyx y :/\: Conflictor (y : iy ++ nyy) NilFL x'- where yyx = yy +>+ x :>: NilFL- (x' : nyy) = reverse $ xx2nons iy yyx-- -- Handle using the swap merge and the previous case.- merge m@(Conflictor{} :\/: Normal _) = swapMerge m-- -- mH see also cH- merge (Conflictor ix xx x :\/: Conflictor iy yy y) =- case pullCommonRL (reverseFL xx) (reverseFL yy) of- CommonRL rxx1 ryy1 c ->- case commuteRLFL (ryy1 :> invertRL rxx1) of- Just (ixx' :> ryy') ->- let xx' = invert ixx'- yy' = reverseRL ryy'- y' : iy' =- map (commutePrimsOrAddToCtx xx') (y : iy)- x' : ix' =- map (commutePrimsOrAddToCtx ryy') (x : ix)- nyy' = xx2nons iy' yy'- nxx' = xx2nons ix' xx'- icx = drop (lengthRL rxx1) $- xx2nons ix (reverseRL $ c +<+ rxx1)- ic' = map (commutePrimsOrAddToCtx ryy') icx- -- +++ is a more efficient version of nub (iy' ++- -- ix') given that we know each element shows up- -- only once in either list.- ixy' = ic' ++ (iy' +++ ix')- c1 = Conflictor (x' : ixy' ++ nxx') yy' y'- c2 = Conflictor (y' : ixy' ++ nyy') xx' x' in- c1 :/\: c2- Nothing -> impossible--instance PatchInspect prim => PatchInspect (RealPatch prim) where- listTouchedFiles (Duplicate p) = nonTouches p- listTouchedFiles (Etacilpud p) = nonTouches p- listTouchedFiles (Normal p) = listTouchedFiles p- listTouchedFiles (Conflictor x c p) =- nubSort $ concatMap nonTouches x ++ listTouchedFiles c ++ nonTouches p- listTouchedFiles (InvConflictor x c p) =- nubSort $ concatMap nonTouches x ++ listTouchedFiles c ++ nonTouches p-- hunkMatches f (Duplicate p) = nonHunkMatches f p- hunkMatches f (Etacilpud p) = nonHunkMatches f p- hunkMatches f (Normal p) = hunkMatches f p- hunkMatches f (Conflictor x c p) =- any (nonHunkMatches f) x || hunkMatches f c || nonHunkMatches f p- hunkMatches f (InvConflictor x c p) =- any (nonHunkMatches f) x || hunkMatches f c || nonHunkMatches f p--allConflictsWith :: PrimPatch prim => Non (RealPatch prim) wX- -> [Non (RealPatch prim) wX]- -> ([Non (RealPatch prim) wX], [Non (RealPatch prim) wX])-allConflictsWith x ys = acw $ partition (conflictsWith x) ys- where- acw ([], nc) = ([], nc)- acw (c:cs, nc) = case allConflictsWith c nc of- (c1, nc1) -> case acw (cs, nc1) of- (xs', nc') -> (c : c1 ++ xs', nc')--conflictsWith :: PrimPatch prim => Non (RealPatch prim) wX- -> Non (RealPatch prim) wX -> Bool-conflictsWith x y | x `dependsUpon` y || y `dependsUpon` x = False-conflictsWith x (Non cy y) =- case commuteOrRemFromCtxFL cy x of- Just (Non cx' x') ->- let iy = fromPrim $ invert y in- case commuteFLorComplain (iy :> cx' +>+ fromPrim x' :>: NilFL) of- Right _ -> False- Left _ -> True- Nothing -> True--dependsUpon :: PrimPatch prim => Non (RealPatch prim) wX- -> Non (RealPatch prim) wX -> Bool-dependsUpon (Non xs _) (Non ys y) =- case removeSubsequenceFL (ys +>+ fromPrim y :>: NilFL) xs of- Just _ -> True- Nothing -> False--(+++) :: Eq a => [a] -> [a] -> [a]-[] +++ x = x-x +++ [] = x-(x:xs) +++ xys | Just ys <- remove1 x xys = x : (xs +++ ys)- | otherwise = x : (xs +++ xys)--swapMerge :: PrimPatch prim => (RealPatch prim :\/: RealPatch prim) wX wY- -> (RealPatch prim :/\: RealPatch prim) wX wY-swapMerge (x :\/: y) = case merge (y :\/: x) of x' :/\: y' -> y' :/\: x'--invertCommute :: PrimPatch prim => (RealPatch prim :> RealPatch prim) wX wY- -> Maybe ((RealPatch prim :> RealPatch prim) wX wY)-invertCommute (x :> y) = do ix' :> iy' <- commute (invert y :> invert x)- return (invert iy' :> invert ix')--invertCommuteNC :: PrimPatch prim => (RealPatch prim :> RealPatch prim) wX wY- -> Maybe ((RealPatch prim :> RealPatch prim) wX wY)-invertCommuteNC (x :> y) = do- ix' :> iy' <- commuteNoConflicts (invert y :> invert x)- return (invert iy' :> invert ix')---- | 'pullCommon' @xs ys@ returns the set of patches that can be commuted out--- of both @xs@ and @ys@ along with the remnants of both lists-pullCommon :: (Patchy p, MyEq p) => FL p wO wX -> FL p wO wY -> Common p wO wX wY-pullCommon NilFL ys = Common NilFL NilFL ys-pullCommon xs NilFL = Common NilFL xs NilFL-pullCommon (x :>: xs) xys | Just ys <- removeFL x xys =- case pullCommon xs ys of- Common c xs' ys' -> Common (x :>: c) xs' ys'-pullCommon (x :>: xs) ys =- case commuteWhatWeCanFL (x :> xs) of- xs1 :> x' :> xs2 -> case pullCommon xs1 ys of- Common c xs1' ys' -> Common c (xs1' +>+ x' :>: xs2) ys'---- | 'Common' @cs xs ys@ represents two sequences of patches that have @cs@ in--- common, in other words @cs +>+ xs@ and @cs +>+ ys@-data Common p wO wX wY where- Common :: FL p wO wI -> FL p wI wX -> FL p wI wY -> Common p wO wX wY---- | 'pullCommonRL' @xs ys@ returns the set of patches that can be commuted--- out of both @xs@ and @ys@ along with the remnants of both lists-pullCommonRL :: (Patchy p, MyEq p) => RL p wX wO -> RL p wY wO -> CommonRL p wX wY wO-pullCommonRL NilRL ys = CommonRL NilRL ys NilRL-pullCommonRL xs NilRL = CommonRL xs NilRL NilRL-pullCommonRL (x :<: xs) xys | Just ys <- removeRL x xys =- case pullCommonRL xs ys of- CommonRL xs' ys' c -> CommonRL xs' ys' (x :<: c)-pullCommonRL (x :<: xs) ys =- case commuteWhatWeCanRL (xs :> x) of- xs1 :> x' :> xs2 ->- case pullCommonRL xs2 ys of- CommonRL xs2' ys' c -> CommonRL (xs2' +<+ x' :<: xs1) ys' c---- | 'CommonRL' @xs ys cs@' represents two sequences of patches that have @cs@--- in common, in other words @xs +<+ cs@ and @ys +<+ cs@-data CommonRL p wX wY wF where- CommonRL :: RL p wX wI -> RL p wY wI -> RL p wI wF -> CommonRL p wX wY wF--instance PrimPatch prim => Apply (RealPatch prim) where- type ApplyState (RealPatch prim) = ApplyState prim- apply p = apply (effect p)--instance PrimPatch prim => RepairToFL (RealPatch prim) where- applyAndTryToFixFL (Normal p) =- mapMaybeSnd (mapFL_FL Normal) `liftM` applyAndTryToFixFL p- applyAndTryToFixFL x = do apply x; return Nothing--instance PatchListFormat (RealPatch prim) where- -- In principle we could use ListFormatDefault when prim /= V1 Prim patches,- -- as those are the only case where we need to support a legacy on-disk- -- format. In practice we don't expect RealPatch to be used with any other- -- argument anyway, so it doesn't matter.- patchListFormat = ListFormatV2--duplicate, etacilpud, conflictor, rotcilfnoc :: String-duplicate = "duplicate"-etacilpud = "etacilpud"-conflictor = "conflictor"-rotcilfnoc = "rotcilfnoc"--instance PrimPatch prim => ShowPatchBasic (RealPatch prim) where- showPatch (Duplicate d) = blueText duplicate $$ showNon d- showPatch (Etacilpud d) = blueText etacilpud $$ showNon d- showPatch (Normal p) = showPrim NewFormat p- showPatch (Conflictor i NilFL p) =- blueText conflictor <+> showNons i <+> blueText "[]" $$ showNon p- showPatch (Conflictor i cs p) =- blueText conflictor <+> showNons i <+> blueText "[" $$- showPrimFL NewFormat cs $$- blueText "]" $$- showNon p- showPatch (InvConflictor i NilFL p) =- blueText rotcilfnoc <+> showNons i <+> blueText "[]" $$ showNon p- showPatch (InvConflictor i cs p) =- blueText rotcilfnoc <+> showNons i <+> blueText "[" $$- showPrimFL NewFormat cs $$- blueText "]" $$- showNon p--instance PrimPatch prim => ShowPatch (RealPatch prim) where- showContextPatch (Normal p) = showContextPatch p- showContextPatch c = return $ showPatch c- summary = plainSummary- summaryFL = plainSummary- thing _ = "change"--instance PrimPatch prim => ReadPatch (RealPatch prim) where- readPatch' = do- skipSpace- let str = string . BC.pack- readConflictorPs = do- i <- readNons- ps <- bracketedFL (readPrim NewFormat) '[' ']'- p <- readNon- return (i, ps, p)- choice [ do str duplicate- p <- readNon- return $ Sealed $ Duplicate p- , do str etacilpud- p <- readNon- return $ Sealed $ Etacilpud p- , do str conflictor- (i, Sealed ps, p) <- readConflictorPs- return $ Sealed $ Conflictor i (unsafeCoerceP ps) p- , do str rotcilfnoc- (i, Sealed ps, p) <- readConflictorPs- return $ Sealed $ InvConflictor i ps p- , do Sealed p <- readPrim NewFormat- return $ Sealed $ Normal p- ]--instance Show2 prim => Show (RealPatch prim wX wY) where- showsPrec d (Normal prim) =- showParen (d > appPrec) $ showString "Darcs.Patch.V2.Real.Normal " . showsPrec2 (appPrec + 1) prim-- showsPrec d (Duplicate x) =- showParen (d > appPrec) $ showString "Darcs.Patch.V2.Real.Duplicate " . showsPrec (appPrec + 1) x-- showsPrec d (Etacilpud x) =- showParen (d > appPrec) $ showString "Darcs.Patch.V2.Etacilpud " . showsPrec (appPrec + 1) x-- showsPrec d (Conflictor ix xx x) =- showParen (d > appPrec) $- showString "Darcs.Patch.V2.Real.Conflictor " . showsPrec (appPrec + 1) ix .- showString " " . showsPrec (appPrec + 1) xx .- showString " " . showsPrec (appPrec + 1) x-- showsPrec d (InvConflictor ix xx x) =- showParen (d > appPrec) $- showString "Darcs.Patch.V2.Real.InvConflictor " . showsPrec (appPrec + 1) ix .- showString " " . showsPrec (appPrec + 1) xx .- showString " " . showsPrec (appPrec + 1) x--instance Show2 prim => Show1 (RealPatch prim wX) where- showDict1 = ShowDictClass--instance Show2 prim => Show2 (RealPatch prim) where- showDict2 = ShowDictClass--instance PrimPatch prim => Nonable (RealPatch prim) where- non (Duplicate d) = d- non (Etacilpud d) = invertNon d -- FIXME !!! ???- non (Normal p) = Non NilFL p- non (Conflictor _ xx x) = commutePrimsOrAddToCtx (invertFL xx) x- non (InvConflictor _ _ n) = invertNon n--instance PrimPatch prim => Effect (RealPatch prim) where- effect (Duplicate _) = NilFL- effect (Etacilpud _) = NilFL- effect (Normal p) = p :>: NilFL- effect (Conflictor _ e _) = invert e- effect (InvConflictor _ e _) = e- effectRL (Duplicate _) = NilRL- effectRL (Etacilpud _) = NilRL- effectRL (Normal p) = p :<: NilRL- effectRL (Conflictor _ e _) = invertFL e- effectRL (InvConflictor _ e _) = reverseFL e--instance IsHunk prim => IsHunk (RealPatch prim) where- isHunk rp = do Normal p <- return rp- isHunk p
+ src/Darcs/Patch/V2/RepoPatch.hs view
@@ -0,0 +1,945 @@+-- Copyright (C) 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.++{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-name-shadowing #-}+{-# LANGUAGE CPP #-}+++module Darcs.Patch.V2.RepoPatch+ ( RepoPatchV2(..)+ , prim2repopatchV2+ , isConsistent+ , isForward+ , isDuplicate+ , mergeUnravelled+ ) where++import Prelude ()+import Darcs.Prelude hiding ( (*>) )++import Control.Monad ( mplus, liftM )+import qualified Data.ByteString.Char8 as BC ( ByteString, pack )+import Data.Maybe ( fromMaybe )+import Data.List ( partition, nub )++import Darcs.Patch.Commute ( commuteFL, commuteFLorComplain, commuteRL+ , commuteRLFL )+import Darcs.Patch.Conflict ( Conflict(..), CommuteNoConflicts(..)+ , IsConflictedPrim(..), ConflictState(..)+ , mangleUnravelled )+import Darcs.Patch.Debug+import Darcs.Patch.Effect ( Effect(..) )+import Darcs.Patch.FileHunk ( IsHunk(..) )+import Darcs.Patch.Format ( PatchListFormat(..), ListFormat(..)+ , FileNameFormat(NewFormat) )+import Darcs.Patch.Invert ( invertFL, invertRL )+import Darcs.Patch.Merge ( Merge(..) )+import Darcs.Patch.Prim ( FromPrim(..), ToFromPrim(..), showPrim, showPrimFL+ , readPrim, PrimOf, PrimPatchBase, PrimPatch )+import Darcs.Patch.Read ( bracketedFL )+import Darcs.Patch.ReadMonads ( skipSpace, string, choice )+import Darcs.Patch.Repair ( mapMaybeSnd, RepairToFL(..), Check(..) )+import Darcs.Patch.Patchy ( Patchy, Apply(..), Commute(..), PatchInspect(..)+ , ReadPatch(..), ShowPatch(..), Invert(..) )+import Darcs.Patch.Permutations ( commuteWhatWeCanFL, commuteWhatWeCanRL+ , genCommuteWhatWeCanRL, removeRL, removeFL+ , removeSubsequenceFL )+import Darcs.Patch.Show ( ShowPatchBasic(..) )+import Darcs.Patch.Summary ( plainSummary )+import Darcs.Patch.V2.Non ( Non(..), Nonable(..), unNon, showNons, showNon+ , readNons, readNon, commutePrimsOrAddToCtx+ , commuteOrAddToCtx, commuteOrAddToCtxRL+ , commuteOrRemFromCtx, commuteOrRemFromCtxFL+ , remNons, (*>), (>*), (*>>), (>>*) )+import Data.List.Ordered ( nubSort )+import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )+import Darcs.Patch.Witnesses.Eq ( MyEq(..), EqCheck(..) )+import Darcs.Patch.Witnesses.Ordered+ ( FL(..), RL(..), (:>)(..), (+>+), (+<+)+ , mapFL_FL, reverseFL, (:\/:)(..), (:/\:)(..)+ , reverseRL, lengthFL, lengthRL, nullFL )+import Darcs.Patch.Witnesses.Sealed+ ( FlippedSeal(..), Sealed(Sealed), mapSeal+ , unseal )+import Darcs.Patch.Witnesses.Show+ ( Show1(..), Show2(..), ShowDict(..)+ , showsPrec2, appPrec+ )+import Darcs.Util.Printer.Color ( errorDoc, assertDoc )++import Darcs.Util.Printer ( Doc, blueText, redText, (<+>), ($$) )++#include "impossible.h"++-- |'RepoPatchV2' is used to represents prim patches that are duplicates of, or+-- conflict with, another prim patch in the repository.+--+-- @Normal prim@: A primitive patch+--+-- @Duplicate x@: This patch has no effect since @x@ is already present in the+-- repository.+--+-- @Etacilpud x: invert (Duplicate x)@+--+-- @Conflictor ix xx x@:+-- @ix@ is the set of patches:+-- * that conflict with @x@ and also conflict with another patch in the+-- repository.+-- * that conflict with a patch that conflict with @x@+--+-- @xx@ is the sequence of patches that conflict *only* with @x@+--+-- @x@ is the original, conflicting patch.+--+-- @ix@ and @x@ are stored as @Non@ objects, which include any necessary+-- context to uniquely define the patch that is referred to.+--+-- The intuition is that a Conflictor should have the effect of inverting any+-- patches that 'x' conflicts with, that haven't already been undone by another+-- Conflictor in the repository.+-- Therefore, the effect of a Conflictor is @invert xx@.+--+-- @InvConflictor ix xx x@: like @invert (Conflictor ix xx x)@+data RepoPatchV2 prim wX wY where+ Duplicate :: Non (RepoPatchV2 prim) wX -> RepoPatchV2 prim wX wX+ Etacilpud :: Non (RepoPatchV2 prim) wX -> RepoPatchV2 prim wX wX+ Normal :: prim wX wY -> RepoPatchV2 prim wX wY+ Conflictor :: [Non (RepoPatchV2 prim) wX] -> FL prim wX wY+ -> Non (RepoPatchV2 prim) wX -> RepoPatchV2 prim wY wX+ InvConflictor :: [Non (RepoPatchV2 prim) wX] -> FL prim wX wY+ -> Non (RepoPatchV2 prim) wX -> RepoPatchV2 prim wX wY++instance PrimPatch prim => PrimPatchBase (RepoPatchV2 prim) where+ type PrimOf (RepoPatchV2 prim) = prim++-- | 'isDuplicate' @p@ is @True@ if @p@ is either a 'Duplicate' or 'Etacilpud'+-- patch.+isDuplicate :: RepoPatchV2 prim wS wY -> Bool+isDuplicate (Duplicate _) = True+isDuplicate (Etacilpud _) = True+isDuplicate _ = False++-- | 'isForward' @p@ is @True@ if @p@ is either an 'InvConflictor' or+-- 'Etacilpud'.+isForward :: PrimPatch prim => RepoPatchV2 prim wS wY -> Maybe Doc+isForward p = case p of+ p@(InvConflictor{}) -> justRedP "An inverse conflictor" p+ p@(Etacilpud _) -> justRedP "An inverse duplicate" p+ _ -> Nothing+ where+ justRedP msg p = Just $ redText msg $$ showPatch p++-- |'mergeUnravelled' is used when converting from Darcs V1 patches (Mergers)+-- to Darcs V2 patches (Conflictors).+mergeUnravelled :: PrimPatch prim => [Sealed ((FL prim) wX)]+ -> Maybe (FlippedSeal (RepoPatchV2 prim) wX)+mergeUnravelled [] = Nothing+mergeUnravelled [_] = Nothing+mergeUnravelled ws =+ case mergeUnravelled_private ws of+ Nothing -> Nothing+ Just NilRL -> bug "found no patches in mergeUnravelled"+ Just (_ :<: z) -> Just $ FlippedSeal z+ where+ notNullS :: PrimPatch prim => Sealed ((FL prim) wX) -> Bool+ notNullS (Sealed NilFL) = False+ notNullS _ = True++ mergeUnravelled_private :: PrimPatch prim => [Sealed (FL prim wX)]+ -> Maybe (RL (RepoPatchV2 prim) wX wX)+ mergeUnravelled_private xs = let nonNullXs = filter notNullS xs in+ reverseFL `fmap` mergeConflictingNons (map sealed2non nonNullXs)++ -- | 'sealed2non' @(Sealed xs)@ converts @xs@ to a 'Non'.+ -- @xs@ must be non-empty since we split this list at the last patch,+ -- taking @init xs@ as the context of @last xs@.+ sealed2non :: Sealed ((FL prim) wX) -> Non (RepoPatchV2 prim) wX+ sealed2non (Sealed xs) =+ case reverseFL xs of+ ys :<: y -> Non (mapFL_FL fromPrim $ reverseRL ys) y+ NilRL -> bug "NilFL encountered in sealed2non"++mergeConflictingNons :: PrimPatch prim => [Non (RepoPatchV2 prim) wX]+ -> Maybe (FL (RepoPatchV2 prim) wX wX)+mergeConflictingNons ns = mcn $ map unNon ns+ where mcn :: PrimPatch prim => [Sealed (FL (RepoPatchV2 prim) wX)]+ -> Maybe (FL (RepoPatchV2 prim) wX wX)+ mcn [] = Just NilFL+ -- Apparently, the joinEffects call is a safety check "and could be+ -- removed when we're sure of the code"!+ mcn [Sealed p] = case joinEffects p of+ NilFL -> Just p+ _ -> Nothing+ mcn (Sealed p1:Sealed p2:zs) =+ case pullCommon p1 p2 of+ Common c ps qs ->+ case merge (ps :\/: qs) of+ qs' :/\: _ -> mcn (Sealed (c +>+ ps +>+ qs'):zs)++joinEffects :: forall p wX wY . (Effect p, Invert (PrimOf p),+ Commute (PrimOf p), MyEq (PrimOf p)) => p wX wY+ -> FL (PrimOf p) wX wY+joinEffects = joinInverses . effect+ where joinInverses :: FL (PrimOf p) wA wB -> FL (PrimOf p) wA wB+ joinInverses NilFL = NilFL+ joinInverses (p :>: ps) =+ let ps' = joinInverses ps in+ fromMaybe (p :>: ps') $ removeFL (invert p) ps'++assertConsistent :: PrimPatch prim => RepoPatchV2 prim wX wY+ -> RepoPatchV2 prim wX wY+assertConsistent x = flip assertDoc x $ do+ e <- isConsistent x+ Just (redText "Inconsistent patch:" $$ showPatch x $$ e)++-- | @mergeAfterConflicting@ takes as input a sequence of conflicting patches+-- @xxx@ (which therefore have no effect) and a sequence of primitive patches+-- @yyy@ that follow said sequence of conflicting patches, and may depend upon+-- some of the conflicting patches (as a resolution).++-- The output is two sequences of patches the first consisting of a set of+-- mutually-conflicting patches, and the second having the same effect as the+-- original primitive patch sequence in the input.++-- So far as I can tell, the second output is always identical to @mapFL Normal+-- yyy@++-- The first output is the set of patches from @xxx@ that are depended upon by+-- @yyy@.+mergeAfterConflicting :: PrimPatch prim => FL (RepoPatchV2 prim) wX wX+ -> FL prim wX wY -> Maybe ( FL (RepoPatchV2 prim) wX wX+ , FL (RepoPatchV2 prim) wX wY)+mergeAfterConflicting xxx yyy = mac (reverseFL xxx) yyy NilFL+ where+ mac :: PrimPatch prim+ => RL (RepoPatchV2 prim) wX wY -> FL prim wY wZ+ -> FL (RepoPatchV2 prim) wZ wA+ -> Maybe (FL (RepoPatchV2 prim) wX wX, FL (RepoPatchV2 prim) wX wA)+ mac NilRL xs goneby = case joinEffects goneby of+ NilFL -> Just (NilFL, mapFL_FL Normal xs)+ _ -> Nothing+ mac (ps :<: p) xs goneby =+ case commuteFLorComplain (p :> mapFL_FL Normal xs) of+ Left _ ->+ case genCommuteWhatWeCanRL commuteNoConflicts (ps :> p) of+ a :> p' :> b ->+ do (b', xs') <- mac b xs goneby+ let pa = joinEffects $ a :<: p'+ NilFL <- return pa+ return (reverseRL (a :<: p') +>+ b', xs')+ `mplus`+ do NilFL <- return goneby+ NilFL <- return $ joinEffects (ps :<: p)+ return (reverseRL (ps :<: p), mapFL_FL Normal xs)+ Right (l :> p'') ->+ case allNormal l of+ Just xs'' -> mac ps xs'' (p'' :>: goneby)+ Nothing ->+ case genCommuteWhatWeCanRL commuteNoConflicts (ps :> p) of+ a :> p' :> b ->+ do (b', xs') <- mac b xs goneby+ let pa = joinEffects $ a :<: p'+ NilFL <- return pa+ return (reverseRL (a :<: p') +>+ b', xs')++geteff :: PrimPatch prim => [Non (RepoPatchV2 prim) wX] -> FL prim wX wY+ -> ([Non (RepoPatchV2 prim) wX], FL (RepoPatchV2 prim) wX wY)+geteff _ NilFL = ([], NilFL)+geteff ix (x :>: xs) | Just ix' <- mapM (commuteOrRemFromCtx (Normal x)) ix =+ case geteff ix' xs of+ (ns, xs') -> ( non (Normal x) : map (commuteOrAddToCtx (Normal x)) ns+ , Normal x :>: xs')+geteff ix xx =+ case mergeConflictingNons ix of+ Nothing -> errorDoc $+ redText "mergeConflictingNons failed in geteff: ix" $$+ showNons ix $$ redText "xx" $$ showPatch xx+ Just rix ->+ case mergeAfterConflicting rix xx of+ Just (a, x) ->+ ( map (commuteOrAddToCtxRL (reverseFL a)) $ toNons x+ , a +>+ x)+ Nothing ->+ errorDoc $+ redText "mergeAfterConflicting failed in geteff" $$+ redText "where ix" $$ showNons ix $$+ redText "and xx" $$ showPatch xx $$+ redText "and rix" $$ showPatch rix++xx2nons :: PrimPatch prim => [Non (RepoPatchV2 prim) wX] -> FL prim wX wY+ -> [Non (RepoPatchV2 prim) wX]+xx2nons ix xx = fst $ geteff ix xx++xx2patches :: PrimPatch prim => [Non (RepoPatchV2 prim) wX] -> FL prim wX wY+ -> FL (RepoPatchV2 prim) wX wY+xx2patches ix xx = snd $ geteff ix xx++-- | If @xs@ consists only of 'Normal' patches, 'allNormal' @xs@ returns+-- @Just pxs@ those patches (so @lengthFL pxs == lengthFL xs@).+-- Otherwise, it returns 'Nothing'.+allNormal :: FL (RepoPatchV2 prim) wX wY -> Maybe (FL prim wX wY)+allNormal (Normal x :>: xs) = (x :>: ) `fmap` allNormal xs+allNormal NilFL = Just NilFL+allNormal _ = Nothing++-- | This is used for unit-testing and for internal sanity checks+isConsistent :: PrimPatch prim => RepoPatchV2 prim wX wY -> Maybe Doc+isConsistent (Normal _) = Nothing+isConsistent (Duplicate _) = Nothing+isConsistent (Etacilpud _) = Nothing+isConsistent c@(InvConflictor{}) = isConsistent (invert c)+isConsistent (Conflictor im mm m@(Non deps _))+ | not $ everyoneConflicts im =+ Just $ redText "Someone doesn't conflict in im in isConsistent"+ | Just _ <- commuteOrRemFromCtxFL rmm m, _ :>: _ <- mm =+ Just $ redText "m doesn't conflict with mm in isConsistent"+ | any (\x -> any (x `conflictsWith`) nmm) im =+ Just $ redText "mm conflicts with im in isConsistent where nmm is" $$+ showNons nmm+ | Nothing <- (nmm ++ im) `minus` toNons deps =+ Just $ redText "dependencies not in conflict:" $$+ showNons (toNons deps) $$+ redText "compared with deps itself:" $$+ showPatch deps+ | otherwise =+ case allConflictsWith m im of+ (im1, []) | im1 `eqSet` im -> Nothing+ (_, imnc) -> Just $ redText ("m doesn't conflict with im in "+ ++ "isConsistent. unconflicting:") $$+ showNons imnc+ where (nmm, rmm) = geteff im mm++everyoneConflicts :: PrimPatch prim => [Non (RepoPatchV2 prim) wX] -> Bool+everyoneConflicts [] = True+everyoneConflicts (x : xs) = case allConflictsWith x xs of+ ([], _) -> False+ (_, xs') -> everyoneConflicts xs'++prim2repopatchV2 :: prim wX wY -> RepoPatchV2 prim wX wY+prim2repopatchV2 = Normal++instance PrimPatch prim => Patchy (RepoPatchV2 prim)+instance PatchDebug prim => PatchDebug (RepoPatchV2 prim)++mergeWith :: PrimPatch prim => Non (RepoPatchV2 prim) wX+ -> [Non (RepoPatchV2 prim) wX] -> Sealed (FL prim wX)+mergeWith p [] = effect `mapSeal` unNon p+mergeWith p xs =+ mergeall . map unNon . (p :) . unconflicting_of $ nonDependsOrConflictsP xs+ where+ nonDependsOrConflictsP =+ filter (\x -> not ((p `dependsUpon` x) || (p `conflictsWith` x)))+ mergeall :: PrimPatch prim => [Sealed (FL (RepoPatchV2 prim) wX)]+ -> Sealed (FL prim wX)+ mergeall [Sealed x] = Sealed $ effect x+ mergeall [] = Sealed NilFL+ mergeall (Sealed x : Sealed y : rest) =+ case merge (x :\/: y) of+ y' :/\: _ -> mergeall (Sealed (x +>+ y') : rest)+ unconflicting_of [] = []+ unconflicting_of (q : qs) = case allConflictsWith q qs of+ ([], _) -> q : qs+ (_, nc) -> unconflicting_of nc++instance PrimPatch prim => Conflict (RepoPatchV2 prim) where+ conflictedEffect (Duplicate (Non _ x)) = [IsC Duplicated x]+ conflictedEffect (Etacilpud _) = impossible+ conflictedEffect (Conflictor _ _ (Non _ x)) = [IsC Conflicted x]+ conflictedEffect (InvConflictor{}) = impossible+ conflictedEffect (Normal x) = [IsC Okay x]+ resolveConflicts (Conflictor ix xx x) = [mangledUnravelled : unravelled]+ where+ mangledUnravelled = mangleUnravelled unravelled+ unravelled = nub $ filter isCons $ map (`mergeWith` xIxNonXX) xIxNonXX+ xIxNonXX = x : ix ++ nonxx+ nonxx = nonxx_ (reverseFL $ xx2patches ix xx)+ -- |nonxx_ takes an RL of patches, and returns a singleton list+ -- containing a Non, in the case where we have a Normal patch at the+ -- end of the list (using the rest of the RL as context), and an empty+ -- list otherwise.+ nonxx_ :: RL (RepoPatchV2 prim) wX wY -> [Non (RepoPatchV2 prim) wX]+ nonxx_ (qs :<: Normal q) = [Non (reverseRL qs) q]+ nonxx_ _ = []+ isCons = unseal (not . nullFL)+ resolveConflicts _ = []++instance PrimPatch prim => CommuteNoConflicts (RepoPatchV2 prim) where+ commuteNoConflicts (d1@(Duplicate _) :> d2@(Duplicate _)) = Just (d2 :> d1)+ commuteNoConflicts (e@(Etacilpud _) :> d@(Duplicate _)) = Just (d :> e)+ commuteNoConflicts (d@(Duplicate _) :> e@(Etacilpud _)) = Just (e :> d)+ commuteNoConflicts (e1@(Etacilpud _) :> e2@(Etacilpud _)) = Just (e2 :> e1)++ -- If the duplicate is @x@, as a 'Non', with @invert x@ as the context,+ -- then it is the patch the duplicate @d@ represents, so commuting results+ -- in the same two patches (since we'd make one a duplicate, and the other+ -- would become @x@ as it would no longer be duplicated).+ -- Otherwise, we commute past, or remove @invert x@ from the context of @d@+ -- to obtain a new Duplicate.+ commuteNoConflicts orig@(x :> Duplicate d) =+ if d == commuteOrAddToCtx (invert x) (non x)+ then Just orig+ else do d' <- commuteOrRemFromCtx (invert x) d+ return (Duplicate d' :> x)++ -- Commuting a Duplicate and any other patch simply places @invert x@ into+ -- the context of the non @d@, by commuting past, or adding to the context.+ commuteNoConflicts (Duplicate d :> x) =+ Just (x :> Duplicate (commuteOrAddToCtx (invert x) d))++ -- handle Etacilpud cases by first inverting, then using the previous+ -- definitions.+ commuteNoConflicts c@(Etacilpud _ :> _) = invertCommuteNC c+ commuteNoConflicts c@(_ :> Etacilpud _) = invertCommuteNC c++ -- Two normal patches should be simply commuted (assuming the can).+ commuteNoConflicts (Normal x :> Normal y) = do+ y' :> x' <- commute (x :> y)+ return (Normal y' :> Normal x')++ -- Commuting a Normal patch past a Conflictor first commutes @x@ past the+ -- effect of the Conflictor, then commutes the resulting @x'@ past the+ -- conflicting patch and the already-undone patches. The commuting must be+ -- done in this order to make the contexts match up (@iy@ and @y@ are made+ -- in the context before @yy@ have their effect, so we need to commute past+ -- the effect of @yy@ first).+ commuteNoConflicts (Normal x :> Conflictor iy yy y) = do+ iyy' :> x' <- commuteFL (x :> invert yy)+ y' : iy' <- mapM (Normal x' >*) (y : iy)+ return (Conflictor iy' (invert iyy') y' :> Normal x')++ -- Handle via the previous case, using the inverting commuter.+ commuteNoConflicts c@(InvConflictor{} :> Normal _) = invertCommuteNC c++ -- Commuting a Conflictor past a Normal patch is the dual operation to+ -- commuting a Normal patch past a Conflictor.+ commuteNoConflicts (Conflictor iy yy y :> Normal x) = do+ y' : iy' <- mapM (*> Normal x) (y : iy)+ x' :> iyy' <- commuteRL (invertFL yy :> x)+ return (Normal x' :> Conflictor iy' (invertRL iyy') y')++ -- Handle via the previous case, using the inverting commuter.+ commuteNoConflicts c@(Normal _ :> InvConflictor{}) = invertCommuteNC c++ -- Commuting two Conflictors, c1 and c2, first commutes the Conflictors'+ -- effects, then commutes the effect of c1 and c2 and the other's+ -- already-undone, and conflicting patch, to bring the already-undone and+ -- conflicting patch into the context of the commuted effects.+ commuteNoConflicts (Conflictor ix xx x :> Conflictor iy yy y) = do+ xx' :> yy' <- commute (yy :> xx)+ x':ix' <- mapM (yy >>*) (x:ix)+ y':iy' <- mapM (*>> xx') (y:iy)+ False <- return $ any (conflictsWith y) (x':ix')+ False <- return $ any (conflictsWith x') iy+ return (Conflictor iy' yy' y' :> Conflictor ix' xx' x')++ -- Handle via the previous case, using the inverting commuter.+ commuteNoConflicts c@(InvConflictor{} :> InvConflictor{}) =+ invertCommuteNC c++ commuteNoConflicts (InvConflictor ix xx x :> Conflictor iy yy y) = do+ iyy' :> xx' <- commute (xx :> invert yy)+ y':iy' <- mapM (xx' >>*) (y:iy)+ x':ix' <- mapM (invertFL iyy' >>*) (x:ix)+ False <- return $ any (conflictsWith y') (x':ix')+ False <- return $ any (conflictsWith x') iy'+ return (Conflictor iy' (invert iyy') y' :> InvConflictor ix' xx' x')++ commuteNoConflicts (Conflictor iy' yy' y' :> InvConflictor ix' xx' x') = do+ xx :> iyy <- commute (invert yy' :> xx')+ y:iy <- mapM (*>> xx') (y':iy')+ x:ix <- mapM (*>> yy') (x':ix')+ False <- return $ any (conflictsWith y') (x':ix')+ False <- return $ any (conflictsWith x') iy'+ return (InvConflictor ix xx x :> Conflictor iy (invert iyy) y)++instance PrimPatch prim => Check (RepoPatchV2 prim) where+ isInconsistent = isConsistent++instance FromPrim (RepoPatchV2 prim) where+ fromPrim = prim2repopatchV2++instance ToFromPrim (RepoPatchV2 prim) where+ toPrim (Normal p) = Just p+ toPrim _ = Nothing++instance PrimPatch prim => MyEq (RepoPatchV2 prim) where+ (Duplicate x) =\/= (Duplicate y) | x == y = IsEq+ (Etacilpud x) =\/= (Etacilpud y) | x == y = IsEq+ (Normal x) =\/= (Normal y) = x =\/= y+ (Conflictor cx xx x) =\/= (Conflictor cy yy y)+ | map commuteOrAddIXX cx `eqSet` map commuteOrAddIYY cy+ && commuteOrAddIXX x == commuteOrAddIYY y = xx =/\= yy+ where+ commuteOrAddIXX = commutePrimsOrAddToCtx (invertFL xx)+ commuteOrAddIYY = commutePrimsOrAddToCtx (invertFL yy)+ (InvConflictor cx xx x) =\/= (InvConflictor cy yy y)+ | cx `eqSet` cy && x == y = xx =\/= yy+ _ =\/= _ = NotEq++eqSet :: Eq a => [a] -> [a] -> Bool+eqSet [] [] = True+eqSet (x:xs) xys | Just ys <- remove1 x xys = eqSet xs ys+eqSet _ _ = False++remove1 :: Eq a => a -> [a] -> Maybe [a]+remove1 x (y : ys) = if x == y then Just ys else (y :) `fmap` remove1 x ys+remove1 _ [] = Nothing++minus :: Eq a => [a] -> [a] -> Maybe [a]+minus xs [] = Just xs+minus xs (y:ys) = do xs' <- remove1 y xs+ xs' `minus` ys++invertNon :: PrimPatch prim => Non (RepoPatchV2 prim) wX+ -> Non (RepoPatchV2 prim) wX+invertNon (Non c x)+ | Just rc' <- removeRL nix (reverseFL c) = Non (reverseRL rc') (invert x)+ | otherwise = commuteOrAddToCtxRL (reverseFL c :<: Normal x) $ non nix+ where+ nix = Normal $ invert x++nonTouches :: PatchInspect prim => Non (RepoPatchV2 prim) wX -> [FilePath]+nonTouches (Non c x) = listTouchedFiles (c +>+ fromPrim x :>: NilFL)++nonHunkMatches :: PatchInspect prim => (BC.ByteString -> Bool)+ -> Non (RepoPatchV2 prim) wX -> Bool+nonHunkMatches f (Non c x) = hunkMatches f c || hunkMatches f x++toNons :: forall p wX wY . (Conflict p, Patchy p, PatchListFormat p,+ ToFromPrim p, Nonable p, ShowPatchBasic (PrimOf p), ShowPatchBasic p)+ => FL p wX wY -> [Non p wX]+toNons xs = map lastNon $ initsFL xs+ where lastNon :: Sealed ((p :> FL p) wX) -> Non p wX+ lastNon (Sealed xxx) =+ case lastNon_aux xxx of+ deps :> p :> _ ->+ case non p of+ Non NilFL pp -> Non (reverseRL deps) pp+ Non ds pp ->+ errorDoc $ redText "Weird case in toNons" $$+ redText "please report this bug!" $$+ (case xxx of+ z :> zs -> showPatch (z :>: zs)) $$+ redText "ds are" $$ showPatch ds $$+ redText "pp is" $$ showPatch pp++ reverseFoo :: (p :> FL p) wX wZ -> (RL p :> p) wX wZ+ reverseFoo (p :> ps) = rf NilRL p ps+ where+ rf :: RL p wA wB -> p wB wC -> FL p wC wD+ -> (RL p :> p) wA wD+ rf rs l NilFL = rs :> l+ rf rs x (y :>: ys) = rf (rs :<: x) y ys++ lastNon_aux :: (p :> FL p) wX wZ -> (RL p :> p :> RL p) wX wZ+ lastNon_aux = commuteWhatWeCanRL . reverseFoo++initsFL :: Patchy p => FL p wX wY -> [Sealed ((p :> FL p) wX)]+initsFL NilFL = []+initsFL (x :>: xs) =+ Sealed (x :> NilFL) :+ map (\(Sealed (y :> xs')) -> Sealed (x :> y :>: xs')) (initsFL xs)++filterConflictsFL :: PrimPatch prim => Non (RepoPatchV2 prim) wX+ -> FL prim wX wY -> (FL prim :> FL prim) wX wY+filterConflictsFL _ NilFL = NilFL :> NilFL+filterConflictsFL n (p :>: ps)+ | Just n' <- commuteOrRemFromCtx (fromPrim p) n =+ case filterConflictsFL n' ps of+ p1 :> p2 -> p :>: p1 :> p2+ | otherwise = case commuteWhatWeCanFL (p :> ps) of+ p1 :> p' :> p2 ->+ case filterConflictsFL n p1 of+ p1a :> p1b -> p1a :> p1b +>+ p' :>: p2++instance Invert prim => Invert (RepoPatchV2 prim) where+ invert (Duplicate d) = Etacilpud d+ invert (Etacilpud d) = Duplicate d+ invert (Normal p) = Normal (invert p)+ invert (Conflictor x c p) = InvConflictor x c p+ invert (InvConflictor x c p) = Conflictor x c p++instance PrimPatch prim => Commute (RepoPatchV2 prim) where+ commute (x :> y) | Just (y' :> x') <-+ commuteNoConflicts (assertConsistent x :> assertConsistent y) =+ Just (y' :> x')++ -- These patches conflicted, since we failed to commuteNoConflicts in the+ -- case above.+ commute (Normal x :> Conflictor a1'nop2 n1'x p1')+ | Just rn1' <- removeRL x (reverseFL n1'x) = do+ let p2 : n1nons = reverse $ xx2nons a1'nop2 $ reverseRL (rn1' :<: x)+ a2 = p1' : a1'nop2 ++ n1nons+ case (a1'nop2, reverseRL rn1', p1') of+ ([], NilFL, Non c y) | NilFL <- joinEffects c ->+ Just (Normal y :> Conflictor a1'nop2 (y :>: NilFL) p2)+ (a1, n1, _) ->+ Just (Conflictor a1 n1 p1' :> Conflictor a2 NilFL p2)++ -- Handle using the inverting commuter, and the previous case. N.b. this+ -- is innefficient, since we'll have to also try commuteNoConflicts again+ -- (which we know will fail, since we got here).+ commute c@(InvConflictor{} :> Normal _) = invertCommute c++ commute (Conflictor a1 n1 p1 :> Conflictor a2 n2 p2)+ | Just a2_minus_p1 <- remove1 p1' a2+ , not (p2 `dependsUpon` p1') = do+ let n1nons = map (commutePrimsOrAddToCtx n2) $ xx2nons a1 n1+ n2nons = xx2nons a2 n2+ Just a2_minus_p1n1 = a2_minus_p1 `minus` n1nons+ n2n1 = n2 +>+ n1+ a1' = map (commutePrimsOrAddToCtx n2) a1+ p2ooo = remNons a1' p2+ n1' :> n2' <- return $ filterConflictsFL p2ooo n2n1+ let n1'n2'nons = xx2nons a2_minus_p1n1 (n1' +>+ n2')+ n1'nons = take (lengthFL n1') n1'n2'nons+ n2'nons = drop (lengthFL n1') n1'n2'nons+ Just a1'nop2 = (a2 ++ n2nons) `minus` (p1' : n1'nons)+ Just a2'o =+ fst (allConflictsWith p2 $ a2_minus_p1 ++ n2nons)+ `minus` n2'nons+ Just a2' =+ mapM (commuteOrRemFromCtxFL (xx2patches a1'nop2 n1')) a2'o+ Just p2' = commuteOrRemFromCtxFL (xx2patches a1'nop2 n1') p2+ case (a2', n2', p2') of+ ([], NilFL, Non c x) ->+ case joinEffects c of+ NilFL -> let n1'x = n1' +>+ x :>: NilFL in+ Just (Normal x :> Conflictor a1'nop2 n1'x p1')+ _ -> impossible+ _ -> Just (c1 :> c2)+ where+ c1 = Conflictor a2' n2' p2'+ c2 = Conflictor (p2 : a1'nop2) n1' p1'++ where (_, rpn2) = geteff a2 n2+ p1' = commuteOrAddToCtxRL (reverseFL rpn2) p1++ -- Handle using the inverting commuter, and the previous case. This is also+ -- innefficient, since we'll have to also try commuteNoConflicts again+ -- (which we know will fail, since we got here).+ commute c@(InvConflictor{} :> InvConflictor{}) = invertCommute c++ commute _ = Nothing++instance PrimPatch prim => Merge (RepoPatchV2 prim) where+ merge (InvConflictor{} :\/: _) = impossible+ merge (_ :\/: InvConflictor{}) = impossible+ merge (Etacilpud _ :\/: _) = impossible+ merge (_ :\/: Etacilpud _) = impossible+++ merge (Duplicate a :\/: Duplicate b) = Duplicate b :/\: Duplicate a+ -- We had a FIXME comment on this case, why?+ merge (Duplicate a :\/: b) =+ b :/\: Duplicate (commuteOrAddToCtx (invert b) a)++ -- Handle using the swap merge and the previous case.+ merge m@(_ :\/: Duplicate _) = swapMerge m++ -- When merging x and y, we do a bunch of what look like "consistency"+ -- check merges. If the resulting y'' and y are equal, then we succeed.+ -- If the first case fails, we check for equal patches (which wouldn't+ -- commute) and return a Duplicate on both sides of the merge, in that+ -- case.+ merge (x :\/: y)+ | Just (y' :> ix') <-+ commute (invert (assertConsistent x) :> assertConsistent y)+ , Just (y'' :> _) <- commute (x :> y')+ , IsEq <- y'' =\/= y =+ assertConsistent y' :/\: invert (assertConsistent ix')+ -- If we detect equal patches, we have a duplicate.+ | IsEq <- x =\/= y+ , n <- commuteOrAddToCtx (invert x) $ non x =+ Duplicate n :/\: Duplicate n++ -- We know that these two patches conflict, and aren't Duplicates, since we+ -- failed the previous case. We therefore create basic Conflictors, which+ -- undo the other patch.+ merge (nx@(Normal x) :\/: ny@(Normal y)) = cy :/\: cx+ where+ cy = Conflictor [] (x :>: NilFL) (non ny)+ cx = Conflictor [] (y :>: NilFL) (non nx)++ -- If a Normal patch @x@ and a Conflictor @cy@ conflict, we add @x@ to the+ -- effect of @cy@ on one side, and create a Conflictor that has no effect,+ -- but has the already-undone and conflicted patch of @cy@ and some foos as+ -- the already-undone on the other side.+ --+ -- TODO: what is foo?+ -- Why do we need nyy? I think @x'@ is @x@ in the context of @yy@.+ merge (Normal x :\/: Conflictor iy yy y) =+ Conflictor iy yyx y :/\: Conflictor (y : iy ++ nyy) NilFL x'+ where yyx = yy +>+ x :>: NilFL+ (x' : nyy) = reverse $ xx2nons iy yyx++ -- Handle using the swap merge and the previous case.+ merge m@(Conflictor{} :\/: Normal _) = swapMerge m++ -- mH see also cH+ merge (Conflictor ix xx x :\/: Conflictor iy yy y) =+ case pullCommonRL (reverseFL xx) (reverseFL yy) of+ CommonRL rxx1 ryy1 c ->+ case commuteRLFL (ryy1 :> invertRL rxx1) of+ Just (ixx' :> ryy') ->+ let xx' = invert ixx'+ yy' = reverseRL ryy'+ y' : iy' =+ map (commutePrimsOrAddToCtx xx') (y : iy)+ x' : ix' =+ map (commutePrimsOrAddToCtx ryy') (x : ix)+ nyy' = xx2nons iy' yy'+ nxx' = xx2nons ix' xx'+ icx = drop (lengthRL rxx1) $+ xx2nons ix (reverseRL $ rxx1 +<+ c)+ ic' = map (commutePrimsOrAddToCtx ryy') icx+ -- +++ is a more efficient version of nub (iy' +++ -- ix') given that we know each element shows up+ -- only once in either list.+ ixy' = ic' ++ (iy' +++ ix')+ c1 = Conflictor (x' : ixy' ++ nxx') yy' y'+ c2 = Conflictor (y' : ixy' ++ nyy') xx' x' in+ c1 :/\: c2+ Nothing -> impossible++instance PatchInspect prim => PatchInspect (RepoPatchV2 prim) where+ listTouchedFiles (Duplicate p) = nonTouches p+ listTouchedFiles (Etacilpud p) = nonTouches p+ listTouchedFiles (Normal p) = listTouchedFiles p+ listTouchedFiles (Conflictor x c p) =+ nubSort $ concatMap nonTouches x ++ listTouchedFiles c ++ nonTouches p+ listTouchedFiles (InvConflictor x c p) =+ nubSort $ concatMap nonTouches x ++ listTouchedFiles c ++ nonTouches p++ hunkMatches f (Duplicate p) = nonHunkMatches f p+ hunkMatches f (Etacilpud p) = nonHunkMatches f p+ hunkMatches f (Normal p) = hunkMatches f p+ hunkMatches f (Conflictor x c p) =+ any (nonHunkMatches f) x || hunkMatches f c || nonHunkMatches f p+ hunkMatches f (InvConflictor x c p) =+ any (nonHunkMatches f) x || hunkMatches f c || nonHunkMatches f p++allConflictsWith :: PrimPatch prim => Non (RepoPatchV2 prim) wX+ -> [Non (RepoPatchV2 prim) wX]+ -> ([Non (RepoPatchV2 prim) wX], [Non (RepoPatchV2 prim) wX])+allConflictsWith x ys = acw $ partition (conflictsWith x) ys+ where+ acw ([], nc) = ([], nc)+ acw (c:cs, nc) = case allConflictsWith c nc of+ (c1, nc1) -> case acw (cs, nc1) of+ (xs', nc') -> (c : c1 ++ xs', nc')++conflictsWith :: PrimPatch prim => Non (RepoPatchV2 prim) wX+ -> Non (RepoPatchV2 prim) wX -> Bool+conflictsWith x y | x `dependsUpon` y || y `dependsUpon` x = False+conflictsWith x (Non cy y) =+ case commuteOrRemFromCtxFL cy x of+ Just (Non cx' x') ->+ let iy = fromPrim $ invert y in+ case commuteFLorComplain (iy :> cx' +>+ fromPrim x' :>: NilFL) of+ Right _ -> False+ Left _ -> True+ Nothing -> True++dependsUpon :: PrimPatch prim => Non (RepoPatchV2 prim) wX+ -> Non (RepoPatchV2 prim) wX -> Bool+dependsUpon (Non xs _) (Non ys y) =+ case removeSubsequenceFL (ys +>+ fromPrim y :>: NilFL) xs of+ Just _ -> True+ Nothing -> False++(+++) :: Eq a => [a] -> [a] -> [a]+[] +++ x = x+x +++ [] = x+(x:xs) +++ xys | Just ys <- remove1 x xys = x : (xs +++ ys)+ | otherwise = x : (xs +++ xys)++swapMerge :: PrimPatch prim => (RepoPatchV2 prim :\/: RepoPatchV2 prim) wX wY+ -> (RepoPatchV2 prim :/\: RepoPatchV2 prim) wX wY+swapMerge (x :\/: y) = case merge (y :\/: x) of x' :/\: y' -> y' :/\: x'++invertCommute :: PrimPatch prim => (RepoPatchV2 prim :> RepoPatchV2 prim) wX wY+ -> Maybe ((RepoPatchV2 prim :> RepoPatchV2 prim) wX wY)+invertCommute (x :> y) = do ix' :> iy' <- commute (invert y :> invert x)+ return (invert iy' :> invert ix')++invertCommuteNC :: PrimPatch prim => (RepoPatchV2 prim :> RepoPatchV2 prim) wX wY+ -> Maybe ((RepoPatchV2 prim :> RepoPatchV2 prim) wX wY)+invertCommuteNC (x :> y) = do+ ix' :> iy' <- commuteNoConflicts (invert y :> invert x)+ return (invert iy' :> invert ix')++-- | 'pullCommon' @xs ys@ returns the set of patches that can be commuted out+-- of both @xs@ and @ys@ along with the remnants of both lists+pullCommon :: (Patchy p, MyEq p) => FL p wO wX -> FL p wO wY -> Common p wO wX wY+pullCommon NilFL ys = Common NilFL NilFL ys+pullCommon xs NilFL = Common NilFL xs NilFL+pullCommon (x :>: xs) xys | Just ys <- removeFL x xys =+ case pullCommon xs ys of+ Common c xs' ys' -> Common (x :>: c) xs' ys'+pullCommon (x :>: xs) ys =+ case commuteWhatWeCanFL (x :> xs) of+ xs1 :> x' :> xs2 -> case pullCommon xs1 ys of+ Common c xs1' ys' -> Common c (xs1' +>+ x' :>: xs2) ys'++-- | 'Common' @cs xs ys@ represents two sequences of patches that have @cs@ in+-- common, in other words @cs +>+ xs@ and @cs +>+ ys@+data Common p wO wX wY where+ Common :: FL p wO wI -> FL p wI wX -> FL p wI wY -> Common p wO wX wY++-- | 'pullCommonRL' @xs ys@ returns the set of patches that can be commuted+-- out of both @xs@ and @ys@ along with the remnants of both lists+pullCommonRL :: (Patchy p, MyEq p) => RL p wX wO -> RL p wY wO -> CommonRL p wX wY wO+pullCommonRL NilRL ys = CommonRL NilRL ys NilRL+pullCommonRL xs NilRL = CommonRL xs NilRL NilRL+pullCommonRL (xs :<: x) xys | Just ys <- removeRL x xys =+ case pullCommonRL xs ys of+ CommonRL xs' ys' c -> CommonRL xs' ys' (c :<: x)+pullCommonRL (xs :<: x) ys =+ case commuteWhatWeCanRL (xs :> x) of+ xs1 :> x' :> xs2 ->+ case pullCommonRL xs2 ys of+ CommonRL xs2' ys' c -> CommonRL (xs1 :<: x' +<+ xs2') ys' c++-- | 'CommonRL' @xs ys cs@' represents two sequences of patches that have @cs@+-- in common, in other words @xs +<+ cs@ and @ys +<+ cs@+data CommonRL p wX wY wF where+ CommonRL :: RL p wX wI -> RL p wY wI -> RL p wI wF -> CommonRL p wX wY wF++instance PrimPatch prim => Apply (RepoPatchV2 prim) where+ type ApplyState (RepoPatchV2 prim) = ApplyState prim+ apply p = apply (effect p)++instance PrimPatch prim => RepairToFL (RepoPatchV2 prim) where+ applyAndTryToFixFL (Normal p) =+ mapMaybeSnd (mapFL_FL Normal) `liftM` applyAndTryToFixFL p+ applyAndTryToFixFL x = do apply x; return Nothing++instance PatchListFormat (RepoPatchV2 prim) where+ -- In principle we could use ListFormatDefault when prim /= V1 Prim patches,+ -- as those are the only case where we need to support a legacy on-disk+ -- format. In practice we don't expect RepoPatchV2 to be used with any other+ -- argument anyway, so it doesn't matter.+ patchListFormat = ListFormatV2++duplicate, etacilpud, conflictor, rotcilfnoc :: String+duplicate = "duplicate"+etacilpud = "etacilpud"+conflictor = "conflictor"+rotcilfnoc = "rotcilfnoc"++instance PrimPatch prim => ShowPatchBasic (RepoPatchV2 prim) where+ showPatch (Duplicate d) = blueText duplicate $$ showNon d+ showPatch (Etacilpud d) = blueText etacilpud $$ showNon d+ showPatch (Normal p) = showPrim NewFormat p+ showPatch (Conflictor i NilFL p) =+ blueText conflictor <+> showNons i <+> blueText "[]" $$ showNon p+ showPatch (Conflictor i cs p) =+ blueText conflictor <+> showNons i <+> blueText "[" $$+ showPrimFL NewFormat cs $$+ blueText "]" $$+ showNon p+ showPatch (InvConflictor i NilFL p) =+ blueText rotcilfnoc <+> showNons i <+> blueText "[]" $$ showNon p+ showPatch (InvConflictor i cs p) =+ blueText rotcilfnoc <+> showNons i <+> blueText "[" $$+ showPrimFL NewFormat cs $$+ blueText "]" $$+ showNon p++instance PrimPatch prim => ShowPatch (RepoPatchV2 prim) where+ showContextPatch (Normal p) = showContextPatch p+ showContextPatch c = return $ showPatch c+ summary = plainSummary+ summaryFL = plainSummary+ thing _ = "change"++instance PrimPatch prim => ReadPatch (RepoPatchV2 prim) where+ readPatch' = do+ skipSpace+ let str = string . BC.pack+ readConflictorPs = do+ i <- readNons+ ps <- bracketedFL (readPrim NewFormat) '[' ']'+ p <- readNon+ return (i, ps, p)+ choice [ do str duplicate+ p <- readNon+ return $ Sealed $ Duplicate p+ , do str etacilpud+ p <- readNon+ return $ Sealed $ Etacilpud p+ , do str conflictor+ (i, Sealed ps, p) <- readConflictorPs+ return $ Sealed $ Conflictor i (unsafeCoerceP ps) p+ , do str rotcilfnoc+ (i, Sealed ps, p) <- readConflictorPs+ return $ Sealed $ InvConflictor i ps p+ , do Sealed p <- readPrim NewFormat+ return $ Sealed $ Normal p+ ]++instance Show2 prim => Show (RepoPatchV2 prim wX wY) where+ showsPrec d (Normal prim) =+ showParen (d > appPrec) $ showString "Darcs.Patch.V2.RepoPatch.Normal " . showsPrec2 (appPrec + 1) prim++ showsPrec d (Duplicate x) =+ showParen (d > appPrec) $ showString "Darcs.Patch.V2.RepoPatch.Duplicate " . showsPrec (appPrec + 1) x++ showsPrec d (Etacilpud x) =+ showParen (d > appPrec) $ showString "Darcs.Patch.V2.Etacilpud " . showsPrec (appPrec + 1) x++ showsPrec d (Conflictor ix xx x) =+ showParen (d > appPrec) $+ showString "Darcs.Patch.V2.RepoPatch.Conflictor " . showsPrec (appPrec + 1) ix .+ showString " " . showsPrec (appPrec + 1) xx .+ showString " " . showsPrec (appPrec + 1) x++ showsPrec d (InvConflictor ix xx x) =+ showParen (d > appPrec) $+ showString "Darcs.Patch.V2.RepoPatch.InvConflictor " . showsPrec (appPrec + 1) ix .+ showString " " . showsPrec (appPrec + 1) xx .+ showString " " . showsPrec (appPrec + 1) x++instance Show2 prim => Show1 (RepoPatchV2 prim wX) where+ showDict1 = ShowDictClass++instance Show2 prim => Show2 (RepoPatchV2 prim) where+ showDict2 = ShowDictClass++instance PrimPatch prim => Nonable (RepoPatchV2 prim) where+ non (Duplicate d) = d+ non (Etacilpud d) = invertNon d -- FIXME !!! ???+ non (Normal p) = Non NilFL p+ non (Conflictor _ xx x) = commutePrimsOrAddToCtx (invertFL xx) x+ non (InvConflictor _ _ n) = invertNon n++instance PrimPatch prim => Effect (RepoPatchV2 prim) where+ effect (Duplicate _) = NilFL+ effect (Etacilpud _) = NilFL+ effect (Normal p) = p :>: NilFL+ effect (Conflictor _ e _) = invert e+ effect (InvConflictor _ e _) = e+ effectRL (Duplicate _) = NilRL+ effectRL (Etacilpud _) = NilRL+ effectRL (Normal p) = NilRL :<: p+ effectRL (Conflictor _ e _) = invertFL e+ effectRL (InvConflictor _ e _) = reverseFL e++instance IsHunk prim => IsHunk (RepoPatchV2 prim) where+ isHunk rp = do Normal p <- return rp+ isHunk p
src/Darcs/Patch/Viewing.hs view
@@ -23,15 +23,18 @@ , showContextSeries ) where +import Prelude ()+import Darcs.Prelude+ import Control.Applicative( (<$>) ) import qualified Data.ByteString as BS ( null ) import Prelude hiding ( pi, readFile )-import Storage.Hashed.Monad ( virtualTreeMonad )-import Storage.Hashed.Tree ( Tree )+import Darcs.Util.Tree ( Tree )+import Darcs.Util.Tree.Monad ( virtualTreeMonad ) import Darcs.Patch.Apply ( Apply(..) )-import Darcs.Patch.ApplyMonad ( ApplyMonadTrans, getApplyState,- ApplyMonad(..), toTree )+import Darcs.Patch.ApplyMonad ( getApplyState,+ ApplyMonad(..), ApplyMonadTree(..), toTree ) import Darcs.Patch.FileHunk ( IsHunk(..), FileHunk(..), showFileHunk ) import Darcs.Patch.Format ( PatchListFormat(..), ListFormat(..), FileNameFormat(..) )@@ -43,15 +46,11 @@ lineColor, ($$), (<+>), prefix, userchunkPS ) showContextSeries :: forall p m wX wY . (Apply p, ShowPatch p, IsHunk p,- ApplyMonadTrans m (ApplyState p),- ApplyMonad m (ApplyState p))+ ApplyMonad (ApplyState p) m) => FL p wX wY -> m Doc showContextSeries = scs Nothing where- scs :: forall m' wWw wXx wYy . (ApplyMonadTrans m' (ApplyState p),- ApplyMonad m' (ApplyState p),- ApplyMonadBase m ~ ApplyMonadBase m')- => Maybe (FileHunk wWw wXx) -> FL p wXx wYy -> m' Doc+ scs :: forall wWw wXx wYy . Maybe (FileHunk wWw wXx) -> FL p wXx wYy -> m Doc scs pold (p :>: ps) = do (_, s') <- nestedApply (apply p) =<< getApplyState case isHunk p of@@ -72,10 +71,10 @@ cool pold fh ps s = fst <$> virtualTreeMonad (coolContextHunk pold fh ps) (toTree s) -showContextHunk :: (ApplyMonad m Tree) => FileHunk wX wY -> m Doc+showContextHunk :: (ApplyMonad Tree m) => FileHunk wX wY -> m Doc showContextHunk h = coolContextHunk Nothing h Nothing -coolContextHunk :: (ApplyMonad m Tree, ApplyMonadTrans m Tree)+coolContextHunk :: (ApplyMonad Tree m) => Maybe (FileHunk wA wB) -> FileHunk wB wC -> Maybe (FileHunk wC wD) -> m Doc coolContextHunk prev fh@(FileHunk f l o n) next = do@@ -127,8 +126,7 @@ => ShowPatch (FL p) where showContextPatch = showContextPatchInternal patchListFormat where- showContextPatchInternal :: (ApplyMonadTrans m (ApplyState p),- ApplyMonad m (ApplyState (FL p)))+ showContextPatchInternal :: (ApplyMonad (ApplyState (FL p)) m) => ListFormat p -> FL p wX wY -> m Doc showContextPatchInternal ListFormatV1 (p :>: NilFL) = showContextPatch p
src/Darcs/Patch/Witnesses/Eq.hs view
@@ -4,6 +4,9 @@ , isIsEq ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP ) -- |'EqCheck' is used to pass around evidence (or lack thereof) of
src/Darcs/Patch/Witnesses/Ordered.hs view
@@ -22,7 +22,6 @@ -- * Directed Types -- $DirectedTypes (:>)(..)- , (:<)(..) , FL(..) , RL(..) -- * Merge Types@@ -59,7 +58,7 @@ , nullFL , concatFL , concatRL- , consRLSealed+ , snocRLSealed , nullRL , toFL , dropWhileFL@@ -71,6 +70,9 @@ , eqFLUnsafe ) where +import Prelude ()+import Darcs.Prelude+ #include "impossible.h" import Darcs.Patch.Witnesses.Show import Darcs.Patch.Witnesses.Sealed@@ -104,10 +106,6 @@ data (a1 :> a2) wX wY = forall wZ . (a1 wX wZ) :> (a2 wZ wY) infixr 1 :> --- | Directed Reverse Pairs-data (a1 :< a2) wX wY = forall wZ . (a1 wZ wY) :< (a2 wX wZ)-infix 1 :<- -- | Forward lists data FL a wX wZ where (:>:) :: a wX wY -> FL a wY wZ -> FL a wX wZ@@ -115,7 +113,7 @@ -- | Reverse lists data RL a wX wZ where- (:<:) :: a wY wZ -> RL a wX wY -> RL a wX wZ+ (:<:) :: RL a wX wY -> a wY wZ -> RL a wX wZ NilRL :: RL a wX wX instance Show2 a => Show (FL a wX wZ) where@@ -132,8 +130,8 @@ instance Show2 a => Show (RL a wX wZ) where showsPrec _ NilRL = showString "NilRL"- showsPrec d (x :<: xs) = showParen (d > prec) $ showsPrec2 (prec + 1) x .- showString " :<: " . showsPrec (prec + 1) xs+ showsPrec d (xs :<: x) = showParen (d > prec) $ showsPrec (prec + 1) xs .+ showString " :<: " . showsPrec2 (prec + 1) x where prec = 5 instance Show2 a => Show1 (RL a wX) where@@ -230,13 +228,6 @@ instance (MyEq a, MyEq b) => Eq ((a :> b) wX wY) where (==) = unsafeCompare -instance (MyEq a, MyEq b) => MyEq (a :< b) where- (a1 :< b1) =\/= (a2 :< b2) | IsEq <- b1 =\/= b2 = a1 =\/= a2- | otherwise = NotEq--instance (MyEq a, MyEq b) => Eq ((a :< b) wX wY) where- (==) = unsafeCompare- instance (Show2 a, Show2 b) => Show2 (a :> b) where showDict2 = ShowDictClass @@ -254,7 +245,8 @@ -- * Functions -infixr 5 :>:, :<:, +>+, +<++infixr 5 :>:, +>++infixl 5 :<:, +<+ nullFL :: FL a wX wZ -> Bool nullFL NilFL = True@@ -274,33 +266,33 @@ filterOutRLRL :: (forall wX wY . p wX wY -> EqCheck wX wY) -> RL p wW wZ -> RL p wW wZ filterOutRLRL _ NilRL = NilRL-filterOutRLRL f (x:<:xs) | IsEq <- f x = filterOutRLRL f xs- | otherwise = x :<: filterOutRLRL f xs+filterOutRLRL f (xs:<:x) | IsEq <- f x = filterOutRLRL f xs+ | otherwise = filterOutRLRL f xs :<: x filterRL :: (forall wX wY . p wX wY -> Bool) -> RL p wA wB -> [Sealed2 p] filterRL _ NilRL = []-filterRL f (x :<: xs) | f x = Sealed2 x : (filterRL f xs)+filterRL f (xs :<: x) | f x = Sealed2 x : (filterRL f xs) | otherwise = filterRL f xs (+>+) :: FL a wX wY -> FL a wY wZ -> FL a wX wZ NilFL +>+ ys = ys (x:>:xs) +>+ ys = x :>: xs +>+ ys -(+<+) :: RL a wY wZ -> RL a wX wY -> RL a wX wZ-NilRL +<+ ys = ys-(x:<:xs) +<+ ys = x :<: xs +<+ ys+(+<+) :: RL a wX wY -> RL a wY wZ -> RL a wX wZ+xs +<+ NilRL = xs+xs +<+ (ys:<:y) = xs +<+ ys :<: y reverseFL :: FL a wX wZ -> RL a wX wZ reverseFL xs = r NilRL xs where r :: RL a wL wM -> FL a wM wO -> RL a wL wO r ls NilFL = ls- r ls (a:>:as) = r (a:<:ls) as+ r ls (a:>:as) = r (ls:<:a) as reverseRL :: RL a wX wZ -> FL a wX wZ-reverseRL xs = r NilFL xs -- r (xs :> NilFL)+reverseRL xs = r NilFL xs where r :: FL a wM wO -> RL a wL wM -> FL a wL wO r ls NilRL = ls- r ls (a:<:as) = r (a:>:ls) as+ r ls (as:<:a) = r (a:>:ls) as concatFL :: FL (FL a) wX wZ -> FL a wX wZ concatFL NilFL = NilFL@@ -308,7 +300,7 @@ concatRL :: RL (RL a) wX wZ -> RL a wX wZ concatRL NilRL = NilRL-concatRL (a:<:as) = a +<+ concatRL as+concatRL (as:<:a) = concatRL as +<+ a spanFL :: (forall wW wY . a wW wY -> Bool) -> FL a wX wZ -> (FL a :> FL a) wX wZ spanFL f (x:>:xs) | f x = case spanFL f xs of@@ -334,11 +326,11 @@ splitAtFL n (x:>:xs) = case splitAtFL (n-1) xs of (xs':>xs'') -> (x:>:xs' :> xs'') -splitAtRL :: Int -> RL a wX wZ -> (RL a :< RL a) wX wZ-splitAtRL 0 xs = NilRL :< xs-splitAtRL _ NilRL = NilRL :< NilRL-splitAtRL n (x:<:xs) = case splitAtRL (n-1) xs of- (xs':<xs'') -> (x:<:xs' :< xs'')+splitAtRL :: Int -> RL a wX wZ -> (RL a :> RL a) wX wZ+splitAtRL 0 xs = xs :> NilRL+splitAtRL _ NilRL = NilRL :> NilRL+splitAtRL n (xs:<:x) = case splitAtRL (n-1) xs of+ (xs'':>xs') -> (xs'':> xs':<:x) -- '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@@ -365,7 +357,7 @@ foldlRL :: (forall wW wY . a -> b wW wY -> a) -> a -> RL b wX wZ -> a foldlRL _ x NilRL = x-foldlRL f x (y:<:ys) = foldlRL f (f x y) ys+foldlRL f x (ys:<:y) = foldlRL f (f x y) ys mapFL_FL :: (forall wW wY . a wW wY -> b wW wY) -> FL a wX wZ -> FL b wX wZ mapFL_FL _ NilFL = NilFL@@ -385,7 +377,7 @@ mapRL_RL :: (forall wW wY . a wW wY -> b wW wY) -> RL a wX wZ -> RL b wX wZ mapRL_RL _ NilRL = NilRL-mapRL_RL f (a:<:as) = f a :<: mapRL_RL f as+mapRL_RL f (as:<:a) = mapRL_RL f as :<: f a mapFL :: (forall wW wZ . a wW wZ -> b) -> FL a wX wY -> [b] mapFL _ NilFL = []@@ -399,7 +391,7 @@ mapRL :: (forall wW wZ . a wW wZ -> b) -> RL a wX wY -> [b] mapRL _ NilRL = []-mapRL f (a :<: b) = f a : mapRL f b+mapRL f (as :<: a) = f a : mapRL f as lengthFL :: FL a wX wZ -> Int lengthFL xs = l xs 0@@ -411,15 +403,15 @@ lengthRL xs = l xs 0 where l :: RL a wX wZ -> Int -> Int l NilRL n = n- l (_:<:as) n = l as $! n+1+ l (as:<:_) n = l as $! n+1 isShorterThanRL :: RL a wX wY -> Int -> Bool isShorterThanRL _ n | n <= 0 = False isShorterThanRL NilRL _ = True-isShorterThanRL (_:<:xs) n = isShorterThanRL xs (n-1)+isShorterThanRL (xs:<:_) n = isShorterThanRL xs (n-1) -consRLSealed :: a wY wZ -> FlippedSeal (RL a) wY -> FlippedSeal (RL a) wZ-consRLSealed a (FlippedSeal as) = flipSeal $ a :<: as+snocRLSealed :: FlippedSeal (RL a) wY -> a wY wZ -> FlippedSeal (RL a) wZ+snocRLSealed (FlippedSeal as) a = flipSeal $ as :<: a toFL :: [FreeLeft a] -> Sealed (FL a wX) toFL [] = Sealed NilFL@@ -433,7 +425,7 @@ dropWhileRL :: (forall wX wY . a wX wY -> Bool) -> RL a wR wV -> Sealed (RL a wR) dropWhileRL _ NilRL = seal NilRL-dropWhileRL p xs@(x:<:xs')+dropWhileRL p xs@(xs':<:x) | p x = dropWhileRL p xs' | otherwise = seal xs
src/Darcs/Patch/Witnesses/Sealed.hs view
@@ -43,6 +43,9 @@ , unFreeRight ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Witnesses.Eq ( MyEq, EqCheck(..) ) import Darcs.Patch.Witnesses.Show import Darcs.Patch.Witnesses.Eq ( (=\/=) )
src/Darcs/Patch/Witnesses/Show.hs view
@@ -15,6 +15,9 @@ , appPrec ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Util.Show ( appPrec ) data ShowDict a where
src/Darcs/Patch/Witnesses/WZipper.hs view
@@ -33,6 +33,10 @@ , toStart ) where++import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Witnesses.Ordered ( FL(..) , RL(..)@@ -81,7 +85,7 @@ rightmost _ = False right :: FZipper p wX wY -> FZipper p wX wY-right (FZipper l (b:>:r)) = FZipper (b :<: l) r+right (FZipper l (m:>:r)) = FZipper (l :<: m) r right x@(FZipper _ NilFL) = x leftmost :: FZipper p wX wY -> Bool@@ -89,11 +93,11 @@ leftmost _ = False left :: FZipper p wX wY -> FZipper p wX wY-left (FZipper (b :<: l) r) = FZipper l (b :>: r)+left (FZipper (l :<: m) r) = FZipper l (m :>: r) left x@(FZipper NilRL _) = x toEnd :: FZipper p wX wY -> FZipper p wX wY-toEnd (FZipper l r) = FZipper (reverseFL r +<+ l) NilFL+toEnd (FZipper l r) = FZipper (l +<+ reverseFL r) NilFL toStart :: FZipper p wX wY -> FZipper p wX wY toStart (FZipper l r) = FZipper NilRL $ reverseRL l +>+ r
+ src/Darcs/Prelude.hs view
@@ -0,0 +1,54 @@+-- Copyright (C) 2015 Ganesh Sittampalam+-- BSD3++{-+This module abstracts over the differences in the Haskell Prelude over multiple GHC versions,+and also hides some symbols that are exported by the Prelude but clash with common names in+the Darcs code.++Broadly it exports everything that the latest Prelude supports, minus the things we explicitly exclude+By default it should be imported with+ import Prelude ()+ import Darcs.Prelude++If necessary more things can be hidden in the 'Darcs.Prelude' import if they clash with something local,+but consider whether to either hide them globally instead or to choose a different name for the local thing.++If something is needed from the Prelude that's hidden by default, then add it to the Prelude import.++-}++module Darcs.Prelude+ ( module Prelude+ , module Control.Applicative+ , module Data.Monoid+ , module Data.Traversable+ ) where++import Prelude hiding+ (+ -- because it's a good name for a PatchInfo+ pi+ ,+ -- because they're in the new Prelude but only in Control.Applicative in older GHCs+ Applicative(..), (<$>), (<*>)+ ,+ -- because it's in the new Prelude but only in Data.Monoid in older GHCs+ Monoid(..)+ ,+ -- because it's in the new Prelude but only in Data.Traversable in older GHCs+ traverse+ ,+ -- because it's a good name for a patch log+ log+ ,+ -- used by the options system+ (^)+ ,+ -- used by various code for no particularly good reason+ lookup, pred+ )++import Control.Applicative ( Applicative(..), (<$>), (<*>) )+import Data.Monoid ( Monoid(..) )+import Data.Traversable ( traverse )
src/Darcs/Repository.hs view
@@ -17,8 +17,6 @@ -- Boston, MA 02110-1301, USA. {-# LANGUAGE CPP, ScopedTypeVariables #-}-- module Darcs.Repository ( Repository , HashedDir(..)@@ -48,8 +46,8 @@ , tentativelyAddPatch , tentativelyRemovePatches , tentativelyAddToPending- , tentativelyReplacePatches , readTentativeRepo+ , RebaseJobFlags(..) , withManualRebaseUpdate , tentativelyMergePatches , considerMergeToWorking@@ -60,7 +58,6 @@ , patchSetToRepository , unrevertUrl , applyToWorking- , patchSetToPatches , createPristineDirectoryTree , createPartialsPristineDirectoryTree , reorderInventory@@ -91,16 +88,16 @@ , listUnregisteredFiles ) where -import Prelude hiding ( catch, pi )+import Prelude ()+import Darcs.Prelude +import Control.Monad ( unless, when )+import Data.List ( (\\) ) import System.Exit ( exitSuccess )-import Data.List ( (\\), isPrefixOf )-import Data.Maybe( catMaybes, isJust, listToMaybe ) import Darcs.Repository.State ( readRecorded , readUnrecorded- , readWorking , unrecordedChanges , unrecordedChangesWithPatches , readPendingAndWorking@@ -115,11 +112,9 @@ ) import Darcs.Repository.Internal- (Repository(..)+ ( Repository(..) , maybeIdentifyRepository , identifyRepositoryFor- , identifyRepository- , IdentifyRepo(..) , findRepository , amInRepository , amNotInRepository@@ -131,13 +126,11 @@ , withRecorded , tentativelyAddPatch , tentativelyRemovePatches- , tentativelyReplacePatches , tentativelyAddToPending , revertRepositoryChanges , finalizeRepositoryChanges , unrevertUrl , applyToWorking- , patchSetToPatches , createPristineDirectoryTree , createPartialsPristineDirectoryTree , reorderInventory@@ -145,7 +138,7 @@ , setScriptsExecutable , setScriptsExecutablePatches , makeNewPending- , seekRepo+ , repoPatchType ) import Darcs.Repository.Job ( RepoJob(..)@@ -154,616 +147,63 @@ , withRepository , withRepositoryDirectory )-import Darcs.Repository.Rebase ( withManualRebaseUpdate )-import Darcs.Repository.Test- ( testTentative )-+import Darcs.Repository.Rebase ( RebaseJobFlags(..), withManualRebaseUpdate )+import Darcs.Repository.Test ( testTentative ) import Darcs.Repository.Merge( tentativelyMergePatches , considerMergeToWorking )-import Darcs.Repository.Cache ( unionRemoteCaches- , fetchFileUsingCache- , speculateFileUsingCache- , HashedDir(..)+import Darcs.Repository.Cache ( HashedDir(..) , Cache(..) , CacheLoc(..) , WritableOrNot(..)- , hashedDir- , bucketFolder- , CacheType(Directory) , reportBadSources )+import Darcs.Repository.InternalTypes ( modifyCache )+import Darcs.Repository.Flags+ ( DiffAlgorithm (..)+ , ScanKnown(..)+ , UpdateWorking(..)+ , UseCache(..)+ , UseIndex(..)+ )+import Darcs.Repository.Clone+ ( createRepository+ , cloneRepository+ , replacePristine+ , writePatchSet+ , patchSetToRepository+ ) import Darcs.Patch ( RepoPatch- , apply- , invert- , effect , PrimOf )-import Darcs.Patch.Set ( Origin- , PatchSet(..)+import Darcs.Patch.Set ( PatchSet(..) , SealedPatchSet- , newset2RL- , newset2FL- , progressPatchSet )-import Darcs.Patch.Match ( MatchFlag(..), havePatchsetMatch ) import Darcs.Patch.Commute( commuteFL ) import Darcs.Patch.Permutations ( genCommuteWhatWeCanRL )-import Control.Exception ( catch, Exception, throwIO, finally, IOException )-import Control.Concurrent ( forkIO )-import Control.Concurrent.MVar ( MVar- , newMVar- , putMVar- , takeMVar- )-import Control.Monad ( unless, when, void )-import Control.Applicative( (<$>) )-import System.Directory ( createDirectory- , createDirectoryIfMissing- , renameFile- , doesFileExist- , removeFile- , getDirectoryContents- , getCurrentDirectory- , setCurrentDirectory- )-import System.IO ( stderr )-import System.IO.Error ( isAlreadyExistsError )-import System.Posix.Files ( createLink )--import qualified Darcs.Repository.HashedRepo as HashedRepo--import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, extractHash, hopefully )-import Darcs.Repository.ApplyPatches ( applyPatches, runDefault )-import Darcs.Repository.HashedRepo ( applyToTentativePristine- , pris2inv- , inv2pris- , revertTentativeChanges- , copySources- )-import Darcs.Repository.InternalTypes ( modifyCache )+import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Witnesses.Sealed ( Sealed(..), FreeLeft, unFreeLeft ) import Darcs.Patch.Witnesses.Ordered- ((:>)(..), reverseRL, reverseFL, lengthFL, mapFL_FL, FL(..),- RL(..), bunchFL, mapFL, mapRL, lengthRL, (+>+), (:\/:)(..))-import Darcs.Repository.Format ( RepoProperty ( HashedInventory, Darcs2 )- , RepoFormat- , createRepoFormat- , formatHas- , writeRepoFormat- , readProblem- )-import Darcs.Repository.Prefs ( writeDefaultPrefs, addRepoSource, deleteSources )-import Darcs.Repository.Match ( getOnePatchset )-import Darcs.Patch.Depends ( areUnrelatedRepos, findUncommon, findCommonWithThem- , countUsThem )-import Darcs.Patch.Type ( PatchType(..) )+ ( (:>)(..)+ , reverseRL+ , reverseFL+ , FL(..)+ , (+>+)+ )+import Darcs.Patch.Depends ( areUnrelatedRepos ) -import Darcs.Util.Exception ( catchall )-import Darcs.Util.File ( withCurrentDirectory ) import Darcs.Util.Prompt ( promptYorn )-import Darcs.Util.English ( englishNum, Noun(..) )-import Darcs.Repository.External- ( copyFileOrUrl- , Cachable(..)- , fetchFileLazyPS- , gzFetchFilePS- )-import Darcs.Util.Progress ( debugMessage- , tediousSize- , beginTedious- , endTedious- )-import Darcs.Patch.Progress- ( progressRLShowTags- , progressFL- )-import Darcs.Repository.Lock- ( writeBinFile- , writeDocBinFile- , withTemp- )-import Darcs.Repository.Flags- ( UpdateWorking(..)- , UseCache(..)- , UseIndex(..)- , ScanKnown(..)- , RemoteDarcs (..)- , Reorder (..)- , Compression (..)- , CloneKind (..)- , Verbosity (..)- , DryRun (..)- , UMask (..)- , AllowConflicts (..)- , ExternalMerge (..)- , WantGuiPause (..)- , SetScriptsExecutable (..)- , RemoteRepos (..)- , SetDefault (..)- , DiffAlgorithm (..)- , WithWorkingDir (..)- , ForgetParent (..)- , WithPatchIndex (..)- )--import Darcs.Util.Download ( maxPipelineLength )-import Darcs.Util.Global ( darcsdir )-import Darcs.Util.URL ( isValidLocalPath )-import Darcs.Util.SignalHandler ( catchInterrupt )-import Darcs.Util.Printer ( Doc, text, hPutDocLn, putDocLn, errorDoc, RenderMode(..) )--import Storage.Hashed.Plain( readPlainTree )-import Storage.Hashed.Tree( Tree, emptyTree, expand, list )-import Storage.Hashed.Hash( encodeBase16 ) import Darcs.Util.Path( anchorPath )-import Storage.Hashed.Darcs( writeDarcsHashed, darcsAddMissingHashes )-import Darcs.Util.ByteString( gzReadFilePS ) -import System.FilePath( (</>)- , takeFileName- , splitPath- , joinPath- , takeDirectory- )-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.Char8 as BL-import Darcs.Repository.PatchIndex (createOrUpdatePatchIndexDisk, doesPatchIndexExist, createPIWithInterrupt)-#include "impossible.h"----- @createRepository useFormat1 useNoWorkingDir patchIndex@-createRepository :: Bool -> WithWorkingDir -> WithPatchIndex -> IO ()-createRepository useFormat1 withWorkingDir createPatchIndex = do- createDirectory darcsdir `catch`- (\e-> if isAlreadyExistsError e- then fail "Tree has already been initialized!"- else fail $ "Error creating directory `"++darcsdir++"'.")- cwd <- getCurrentDirectory- x <- seekRepo- when (isJust x) $ do- setCurrentDirectory cwd- putStrLn "WARNING: creating a nested repository."- createDirectory $ darcsdir ++ "/pristine.hashed"- createDirectory $ darcsdir ++ "/patches"- createDirectory $ darcsdir ++ "/inventories"- createDirectory $ darcsdir ++ "/prefs"- writeDefaultPrefs- let repoFormat = createRepoFormat useFormat1 withWorkingDir- writeRepoFormat repoFormat (darcsdir++"/format")- writeBinFile (darcsdir++"/hashed_inventory") ""- writePristine "." emptyTree- withRepository NoUseCache $ RepoJob $ \repo -> case createPatchIndex of- NoPatchIndex -> return () -- default- YesPatchIndex -> createOrUpdatePatchIndexDisk repo--repoPatchType :: Repository p wR wU wT -> PatchType p-repoPatchType _ = PatchType--cloneRepository ::- String -- origin repository path- -> String -- new repository name (for relative path)- -> Verbosity -> UseCache- -> CloneKind- -> UMask -> RemoteDarcs- -> SetScriptsExecutable- -> RemoteRepos -> SetDefault- -> [MatchFlag]- -> RepoFormat- -> WithWorkingDir- -> WithPatchIndex -- use patch index- -> Bool -- use packs- -> Bool -- --to-match given- -> ForgetParent- -> IO ()-cloneRepository repodir mysimplename v uc cloneKind um rdarcs sse remoteRepos- setDefault matchFlags rfsource withWorkingDir usePatchIndex usePacks toMatch forget = do- createDirectory mysimplename- setCurrentDirectory mysimplename- createRepository (not $ formatHas Darcs2 rfsource)- withWorkingDir- (if cloneKind == LazyClone then NoPatchIndex else usePatchIndex)- debugMessage "Finished initializing new directory."- addRepoSource repodir NoDryRun remoteRepos setDefault-- if toMatch && cloneKind /= LazyClone- then withRepository uc $ RepoJob $ \repository -> do- debugMessage "Using economical clone --to-match handling"- fromrepo <- identifyRepositoryFor repository uc repodir- Sealed patches_to_get <- getOnePatchset fromrepo matchFlags- patchSetToRepository fromrepo patches_to_get uc rdarcs- debugMessage "Finished converting selected patch set to new repository"- else copyRepoAndGoToChosenVersion repodir v uc cloneKind um rdarcs sse- matchFlags withWorkingDir usePacks forget---- assumes that the target repo of the get is the current directory,--- and that an inventory in the right format has already been created.-copyRepoAndGoToChosenVersion ::- String -- repository directory- -> Verbosity -> UseCache- -> CloneKind- -> UMask -> RemoteDarcs- -> SetScriptsExecutable- -> [MatchFlag]- -> WithWorkingDir -> Bool- -> ForgetParent- -> IO ()-copyRepoAndGoToChosenVersion repodir v uc gk um rdarcs sse matchFlags withWorkingDir usePacks forget =- withRepository uc $ RepoJob $ \repository -> do- debugMessage "Identifying and copying repository..."- fromRepo@(Repo fromDir rffrom _ _) <- identifyRepositoryFor repository uc repodir- case readProblem rffrom of- Just e -> fail $ "Incompatibility with repository " ++ fromDir ++ ":\n" ++ e- Nothing -> return ()- debugMessage "Copying prefs"- copyFileOrUrl rdarcs (fromDir ++ "/" ++ darcsdir ++ "/prefs/prefs")- (darcsdir ++ "/prefs/prefs") (MaxAge 600) `catchall` return ()- if formatHas HashedInventory rffrom- then do- -- copying basic repository (hashed_inventory and pristine)- if usePacks && (not . isValidLocalPath) fromDir- then copyBasicRepoPacked fromRepo v uc um rdarcs withWorkingDir- else copyBasicRepoNotPacked fromRepo v uc um rdarcs withWorkingDir- when (gk /= LazyClone) $ do- when (gk /= CompleteClone) $- putInfo v $ text "Copying patches, to get lazy repository hit ctrl-C..."- -- copying complete repository (inventories and patches)- if usePacks && (not . isValidLocalPath) fromDir- then copyCompleteRepoPacked fromRepo v uc um gk- else copyCompleteRepoNotPacked fromRepo v uc um gk- else- -- old-fashioned repositories are cloned diferently since- -- we need to copy all patches first and then build pristine- copyRepoOldFashioned fromRepo v uc um withWorkingDir- when (sse == YesSetScriptsExecutable) setScriptsExecutable- when (havePatchsetMatch matchFlags) $ do- putStrLn "Going to specified version..."- -- read again repository on disk to get caches and sources right- withRepoLock NoDryRun uc YesUpdateWorking um $ RepoJob $ \repository' -> do- patches <- readRepo repository'- Sealed context <- getOnePatchset repository' matchFlags- 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 v $ text $ "Unapplying " ++ show (lengthFL ps) ++ " " ++- englishNum (lengthFL ps) (Noun "patch") ""- invalidateIndex repository'- _ <- tentativelyRemovePatches repository' GzipCompression YesUpdateWorking us'- tentativelyAddToPending repository' YesUpdateWorking $ invert $ effect us'- finalizeRepositoryChanges repository' YesUpdateWorking GzipCompression- runDefault (apply (invert $ effect ps)) `catch` \(e :: IOException) ->- fail ("Couldn't undo patch in working dir.\n" ++ show e)- when (sse == YesSetScriptsExecutable) $ setScriptsExecutablePatches (invert $ effect ps)- when (forget == YesForgetParent) deleteSources--putInfo :: Verbosity -> Doc -> IO ()-putInfo Quiet _ = return ()-putInfo _ d = hPutDocLn Encode stderr d--putVerbose :: Verbosity -> Doc -> IO ()-putVerbose Verbose d = putDocLn d-putVerbose _ _ = return ()--copyBasicRepoNotPacked :: forall p wR wU wT. (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT- -> Verbosity -> UseCache- -> UMask -> RemoteDarcs- -> WithWorkingDir- -> IO ()-copyBasicRepoNotPacked (Repo fromDir _ _ fromCache) verb useCache umask rdarcs withWorkingDir = do- toRepo@(Repo toDir toFormat toPristine toCache) <- identifyRepository useCache "."- let (_dummy :: Repository p wR wU wT) = toRepo --The witnesses are wrong, but cannot escape- toCache2 <- unionRemoteCaches toCache fromCache fromDir- let toRepo2 :: Repository p wR wU wT- toRepo2 = Repo toDir toFormat toPristine toCache2- HashedRepo.copyHashedInventory toRepo2 rdarcs fromDir- HashedRepo.copySources toRepo2 fromDir- debugMessage "Grabbing lock in new repository to copy basic repo..."- withRepoLock NoDryRun useCache YesUpdateWorking umask- $ RepoJob $ \torepository -> do- putVerbose verb $ text "Writing pristine and working directory contents..."- createPristineDirectoryTree torepository "." withWorkingDir---copyCompleteRepoNotPacked :: forall p wR wU wT. (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT- -> Verbosity -> UseCache- -> UMask -> CloneKind- -> IO ()-copyCompleteRepoNotPacked _ verb useCache umask cloneKind = do- debugMessage "Grabbing lock in new repository to copy complete repo..."- withRepoLock NoDryRun useCache YesUpdateWorking umask- $ RepoJob $ \torepository@(Repo todir _ _ _) -> do- let cleanup = putInfo verb $ text "Using lazy repository."- allowCtrlC cloneKind cleanup $ do- fetchPatchesIfNecessary torepository- pi <- doesPatchIndexExist todir- when pi $ createPIWithInterrupt torepository--packsDir :: String-packsDir = "/" ++ darcsdir ++ "/packs/"--copyBasicRepoPacked ::- forall p wR wU wT. (RepoPatch p, ApplyState (PrimOf p) ~ Tree, ApplyState p ~ Tree)- => Repository p wR wU wT- -> Verbosity -> UseCache- -> UMask -> RemoteDarcs- -> WithWorkingDir- -> IO ()-copyBasicRepoPacked r@(Repo fromDir _ _ _) verb useCache umask rdarcs withWorkingDir =- do let hashURL = fromDir ++ packsDir ++ "pristine"- mPackHash <- (Just <$> gzFetchFilePS hashURL Uncachable) `catchall` (return Nothing)- let hiURL = fromDir ++ "/" ++ darcsdir ++ "/hashed_inventory"- i <- gzFetchFilePS hiURL Uncachable- let currentHash = BS.pack $ inv2pris i- let copyNormally = copyBasicRepoNotPacked r verb useCache umask rdarcs withWorkingDir- case mPackHash of- Just packHash | packHash == currentHash- -> ( copyBasicRepoPacked2 r verb useCache withWorkingDir- `catchall` do putStrLn "Problem while copying basic pack, copying normally."- copyNormally)- _ -> do putVerbose verb $ text "Remote repo has no basic pack or outdated basic pack, copying normally."- copyNormally--copyBasicRepoPacked2 ::- forall p wR wU wT. (RepoPatch p, ApplyState (PrimOf p) ~ Tree, ApplyState p ~ Tree)- => Repository p wR wU wT- -> Verbosity -> UseCache- -> WithWorkingDir -> IO ()-copyBasicRepoPacked2 fromRepo@(Repo fromDir _ _ fromCache) verb useCache withWorkingDir = do- b <- fetchFileLazyPS (fromDir ++ packsDir ++ "basic.tar.gz") Uncachable- putVerbose verb $ text "Cloning packed basic repository."- Repo toDir toFormat toPristine toCache <-- identifyRepositoryFor fromRepo useCache "."- toCache2 <- unionRemoteCaches toCache fromCache fromDir- let toRepo :: Repository p wR wU wR -- In empty repo, t(entative) = r(ecorded)- toRepo = Repo toDir toFormat toPristine toCache2- copySources toRepo fromDir- Repo _ _ _ toCache3 <-- identifyRepositoryFor toRepo useCache "."- -- unpack inventory & pristine cache- cleanDir "pristine.hashed"- removeFile $ darcsdir </> "hashed_inventory"- unpackBasic toCache3 . Tar.read $ decompress b- createPristineDirectoryTree toRepo "." withWorkingDir- putVerbose verb $ text "Basic repository unpacked. Will now see if there are new patches."- -- pull new patches- us <- readRepo toRepo- them <- readRepo fromRepo- us' :\/: them' <- return $ findUncommon us them- revertTentativeChanges- Sealed pw <- tentativelyMergePatches toRepo "clone" NoAllowConflicts YesUpdateWorking NoExternalMerge NoWantGuiPause GzipCompression verb NoReorder ( UseIndex, ScanKnown, MyersDiff ) us' them'- invalidateIndex toRepo- finalizeRepositoryChanges toRepo YesUpdateWorking GzipCompression- when (withWorkingDir == WithWorkingDir) $ void $ applyToWorking toRepo verb pw- where- cleanDir d = mapM_ (\x -> removeFile $ darcsdir </> d </> x) .- filter (\x -> head x /= '.') =<< getDirectoryContents (darcsdir </> d)--copyCompleteRepoPacked ::- forall p wR wU wT. (RepoPatch p, ApplyState (PrimOf p) ~ Tree, ApplyState p ~ Tree)- => Repository p wR wU wT- -> Verbosity -> UseCache- -> UMask- -> CloneKind- -> IO ()-copyCompleteRepoPacked r verb useCache umask cloneKind =- ( copyCompleteRepoPacked2 r verb useCache cloneKind- `catchall` do putVerbose verb $ text "Problem while copying patches pack, copying normally."- copyCompleteRepoNotPacked r verb useCache umask cloneKind )--copyCompleteRepoPacked2 ::- forall p wR wU wT. (RepoPatch p, ApplyState (PrimOf p) ~ Tree, ApplyState p ~ Tree)- => Repository p wR wU wT- -> Verbosity -> UseCache- -> CloneKind- -> IO ()-copyCompleteRepoPacked2 fromRepo@(Repo fromDir _ _ fromCache) verb useCache cloneKind = do- Repo toDir toFormat toPristine toCache <- identifyRepositoryFor fromRepo useCache "."- toCache2 <- unionRemoteCaches toCache fromCache fromDir- let toRepo :: Repository p wR wU wR -- In empty repo, t(entative) = r(ecorded)- toRepo = Repo toDir toFormat toPristine toCache2- Repo _ _ _ toCache3 <- identifyRepositoryFor toRepo useCache "."- us <- readRepo toRepo- -- get old patches- let cleanup = putInfo verb $ text "Using lazy repository."- allowCtrlC cloneKind cleanup $ do- cleanDir "patches"- putVerbose verb $ text "Using patches pack."- unpackPatches toCache3 (mapRL hashedPatchFileName $ newset2RL us) .- Tar.read . decompress =<< fetchFileLazyPS (fromDir ++ packsDir ++ "patches.tar.gz") Uncachable- pi <- doesPatchIndexExist toDir- when pi $ createPIWithInterrupt toRepo- where- cleanDir d = mapM_ (\x -> removeFile $ darcsdir </> d </> x) .- filter (\x -> head x /= '.') =<< getDirectoryContents (darcsdir </> d)--allowCtrlC :: CloneKind -> IO () -> IO () -> IO ()-allowCtrlC CompleteClone _ action = action-allowCtrlC _ cleanup action = action `catchInterrupt` cleanup--copyRepoOldFashioned :: forall p wR wU wT. (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT- -> Verbosity -> UseCache- -> UMask- -> WithWorkingDir- -> IO ()-copyRepoOldFashioned fromrepository verb useCache umask withWorkingDir = do- toRepo@(Repo _ _ _ toCache) <- identifyRepository useCache "."- let (_dummy :: Repository p wR wU wT) = toRepo --The witnesses are wrong, but cannot escape- -- copy all patches from remote- HashedRepo.revertTentativeChanges- patches <- readRepo fromrepository- let k = "Copying patch"- beginTedious k- tediousSize k (lengthRL $ newset2RL patches)- let patches' = progressPatchSet k patches- HashedRepo.writeTentativeInventory toCache GzipCompression patches'- endTedious k- HashedRepo.finalizeTentativeChanges toRepo GzipCompression- -- apply all patches into current hashed repository- debugMessage "Grabbing lock in new repository..."- withRepoLock NoDryRun useCache YesUpdateWorking umask- $ RepoJob $ \torepository -> do- local_patches <- readRepo torepository- replacePristine torepository emptyTree- let patchesToApply = progressFL "Applying patch" $ newset2FL local_patches- sequence_ $ mapFL applyToTentativePristine $ bunchFL 100 patchesToApply- finalizeRepositoryChanges torepository YesUpdateWorking GzipCompression- putVerbose verb $ text "Writing pristine and working directory contents..."- createPristineDirectoryTree torepository "." withWorkingDir--withControlMVar :: (MVar () -> IO ()) -> IO ()-withControlMVar f = do- mv <- newMVar ()- f mv- takeMVar mv--forkWithControlMVar :: MVar () -> IO () -> IO ()-forkWithControlMVar mv f = do- takeMVar mv- _ <- forkIO $ finally f (putMVar mv ())- return ()--removeMetaFiles :: IO ()-removeMetaFiles = mapM_ (removeFile . (darcsdir </>)) .- filter ("meta-" `isPrefixOf`) =<< getDirectoryContents darcsdir--unpackBasic :: Exception e => Cache -> Tar.Entries e -> IO ()-unpackBasic c x = do- withControlMVar $ \mv -> unpackTar c (basicMetaHandler c mv) x- removeMetaFiles--unpackPatches :: Exception e => Cache -> [String] -> Tar.Entries e -> IO ()-unpackPatches c ps x = do- withControlMVar $ \mv -> unpackTar c (patchesMetaHandler c ps mv) x- removeMetaFiles--unpackTar :: Exception e => Cache -> IO () -> Tar.Entries e -> IO ()-unpackTar _ _ Tar.Done = return ()-unpackTar _ _ (Tar.Fail e)= throwIO e-unpackTar c mh (Tar.Next x xs) = case Tar.entryContent x of- Tar.NormalFile x' _ -> do- let p = Tar.entryPath x- if "meta-" `isPrefixOf` takeFileName p- then do- BL.writeFile p x'- mh- unpackTar c mh xs- else do- ex <- doesFileExist p- if ex- then debugMessage $ "Tar thread: STOP " ++ p- else do- if p == darcsdir </> "hashed_inventory"- then writeFile' Nothing p x'- else writeFile' (cacheDir c) p $ compress x'- debugMessage $ "Tar thread: GET " ++ p- unpackTar c mh xs- _ -> fail "Unexpected non-file tar entry"- where- writeFile' Nothing path content = withTemp $ \tmp -> do- BL.writeFile tmp content- renameFile tmp path- writeFile' (Just ca) path content = do- let fileFullPath = case splitPath path of- _:hDir:hFile:_ -> joinPath [ca, hDir, bucketFolder hFile, hFile]- _ -> fail "Unexpected file path"- createDirectoryIfMissing True $ takeDirectory path- createLink fileFullPath path `catch` (\(ex :: IOException) -> do- if isAlreadyExistsError ex then- return () -- so much the better- else- -- ignore cache if we cannot link- writeFile' Nothing path content)--basicMetaHandler :: Cache -> MVar () -> IO ()-basicMetaHandler ca mv = do- ex <- doesFileExist $ darcsdir </> "meta-filelist-pristine"- when ex . forkWithControlMVar mv $- fetchFilesUsingCache ca HashedPristineDir . lines =<<- readFile (darcsdir </> "meta-filelist-pristine")--patchesMetaHandler :: Cache -> [String] -> MVar () -> IO ()-patchesMetaHandler ca ps mv = do- ex <- doesFileExist $ darcsdir </> "meta-filelist-inventories"- when ex $ do- forkWithControlMVar mv $ fetchFilesUsingCache ca HashedPristineDir .- lines =<< readFile (darcsdir </> "meta-filelist-inventories")- forkWithControlMVar mv $ fetchFilesUsingCache ca HashedPatchesDir ps--cacheDir :: Cache -> Maybe String-cacheDir (Ca cs) = listToMaybe . catMaybes .flip map cs $ \x -> case x of- Cache Directory Writable x' -> Just x'- _ -> Nothing--hashedPatchFileName :: PatchInfoAnd p wA wB -> String-hashedPatchFileName x = case extractHash x of- Left _ -> fail "unexpected unhashed patch"- Right h -> h---- | fetchFilesUsingCache is similar to mapM fetchFileUsingCache, exepts--- it stops execution if file it's going to fetch already exists.-fetchFilesUsingCache :: Cache -> HashedDir -> [FilePath] -> IO ()-fetchFilesUsingCache _ _ [] = return ()-fetchFilesUsingCache c d (f:fs) = do- ex <- doesFileExist $ darcsdir </> hashedDir d </> f- if ex- then debugMessage $ "Cache thread: STOP " ++- (darcsdir </> hashedDir d </> f)- else do- debugMessage $ "Cache thread: GET " ++- (darcsdir </> hashedDir d </> f)- _ <- fetchFileUsingCache c d f- fetchFilesUsingCache c d fs---- | writePatchSet is like patchSetToRepository, except that it doesn't--- touch the working directory or pristine cache.-writePatchSet :: (RepoPatch p, ApplyState p ~ Tree)- => PatchSet p Origin wX- -> UseCache- -> IO (Repository p wR wU wT)-writePatchSet patchset useCache = do- maybeRepo <- maybeIdentifyRepository useCache "."- let repo@(Repo _ _ _ c) =- case maybeRepo of- 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"- HashedRepo.writeTentativeInventory c GzipCompression patchset- HashedRepo.finalizeTentativeChanges repo GzipCompression- 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.-patchSetToRepository :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR1 wU1 wR1- -> PatchSet p Origin wX- -> UseCache -> RemoteDarcs- -> IO ()-patchSetToRepository (Repo fromrepo rf _ _) patchset useCache remoteDarcs = do- when (formatHas HashedInventory rf) $ -- set up sources and all that- do writeFile (darcsdir </> "tentative_pristine") "" -- this is hokey- repox <- writePatchSet patchset useCache- HashedRepo.copyHashedInventory repox remoteDarcs fromrepo- HashedRepo.copySources repox fromrepo- repo <- writePatchSet patchset useCache- readRepo repo >>= (runDefault . applyPatches . newset2FL)- debugMessage "Writing the pristine"- pristineFromWorking repo+import Darcs.Util.Tree( Tree, emptyTree, expand, list )+import Darcs.Util.Tree.Plain( readPlainTree ) checkUnrelatedRepos :: RepoPatch p => Bool- -> PatchSet p wStart wX- -> PatchSet p wStart wY+ -> PatchSet rt p wStart wX+ -> PatchSet rt p wStart wY -> IO () checkUnrelatedRepos allowUnrelatedRepos us them = when ( not allowUnrelatedRepos && areUnrelatedRepos us them ) $@@ -771,32 +211,11 @@ unless confirmed $ do putStrLn "Cancelled." exitSuccess --- | This function fetches all patches that the given repository has--- with fetchFileUsingCache, unless --lazy is passed.-fetchPatchesIfNecessary :: forall p wR wU wT. (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT- -> IO ()-fetchPatchesIfNecessary torepository@(Repo _ _ _ c) =- do 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 hashes :: forall wX wY . RL (PatchInfoAnd p) wX wY -> [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 an FL of patches started from the pending state to the pending patch. -- TODO: add witnesses for pending so we can make the types precise: currently -- the passed patch can be applied in any context, not just after pending.-addPendingDiffToPending :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> UpdateWorking+addPendingDiffToPending :: (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => Repository rt p wR wU wT -> UpdateWorking -> FreeLeft (FL (PrimOf p)) -> IO () addPendingDiffToPending _ NoUpdateWorking _ = return () addPendingDiffToPending repo@(Repo{}) uw@YesUpdateWorking newP = do@@ -811,8 +230,8 @@ -- dependencies), by commuting the patches to be added past as much of the -- changes between pending and working as is possible, and including anything -- that doesn't commute, and the patch itself in the new pending patch.-addToPending :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> UpdateWorking -> FL (PrimOf p) wU wY -> IO ()+addToPending :: (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => Repository rt p wR wU wT -> UpdateWorking -> FL (PrimOf p) wU wY -> IO () addToPending _ NoUpdateWorking _ = return () addToPending repo@(Repo{}) uw@YesUpdateWorking p = do (toPend :> toUnrec) <- readPendingAndWorking (UseIndex, ScanKnown, MyersDiff) repo Nothing@@ -820,22 +239,6 @@ case genCommuteWhatWeCanRL commuteFL (reverseFL toUnrec :> p) of (toP' :> p' :> _excessUnrec) -> makeNewPending repo uw $ toPend +>+ reverseRL toP' +>+ p'---- | Replace the existing pristine with a new one (loaded up in a Tree object).-replacePristine :: Repository p wR wU wT -> Tree IO -> IO ()-replacePristine (Repo r _ _ _) = writePristine r--writePristine :: FilePath -> Tree IO -> IO ()-writePristine r tree = withCurrentDirectory r $- do let t = darcsdir </> "hashed_inventory"- i <- gzReadFilePS t- tree' <- darcsAddMissingHashes tree- root <- writeDarcsHashed tree' $ darcsdir </> "pristine.hashed"- writeDocBinFile t $ pris2inv (BS.unpack $ encodeBase16 root) i--pristineFromWorking :: RepoPatch p => Repository p wR wU wT -> IO ()-pristineFromWorking repo@(Repo dir _ _ _) =- withCurrentDirectory dir $ readWorking >>= replacePristine repo -- | Get a list of all files and directories in the working copy, including -- boring files if necessary
src/Darcs/Repository/ApplyPatches.hs view
@@ -25,7 +25,7 @@ , DefaultIO, runDefault ) where -import Prelude hiding ( catch )+import Prelude hiding ( Applicative ) import Control.Exception ( catch, SomeException, IOException ) import Data.Char ( toLower ) import Data.List ( isSuffixOf )@@ -39,21 +39,21 @@ doesDirectoryExist, doesFileExist ) -import Darcs.Patch.ApplyMonad( ApplyMonad(..) )+import Darcs.Patch.ApplyMonad( ApplyMonad(..), ApplyMonadTree(..) ) import Darcs.Patch.ApplyPatches ( applyPatches ) import Darcs.Patch.MonadProgress ( MonadProgress(..), ProgressAction(..) ) import Darcs.Repository.Prefs( changePrefval )-import Darcs.Repository.Lock ( writeAtomicFilePS )+import Darcs.Util.Lock ( writeAtomicFilePS ) import Darcs.Util.Exception ( prettyException ) import Darcs.Util.Progress ( beginTedious, endTedious, tediousSize, finishedOneIO ) import Darcs.Util.Printer ( hPutDocLn, RenderMode(..) ) import Darcs.Util.Printer.Color ( showDoc )-import Darcs.Repository.External ( backupByCopying, backupByRenaming )+import Darcs.Util.External ( backupByCopying, backupByRenaming ) import Darcs.Util.Path ( FileName, fn2fp ) import qualified Data.ByteString as B (empty, null, readFile) -import Storage.Hashed.Tree( Tree )+import Darcs.Util.Tree( Tree ) newtype DefaultIO a = DefaultIO { runDefaultIO :: IO a } deriving (Functor, Applicative, Monad)@@ -71,8 +71,10 @@ do hPutDocLn Encode stderr $ paOnError item ioError e -instance ApplyMonad DefaultIO Tree where+instance ApplyMonad Tree DefaultIO where type ApplyMonadBase DefaultIO = IO++instance ApplyMonadTree DefaultIO where mDoesDirectoryExist = DefaultIO . doesDirectoryExist . fn2fp mChangePref a b c = DefaultIO $ changePrefval a b c mModifyFilePS f j = DefaultIO $ B.readFile (fn2fp f) >>= runDefaultIO . j >>= writeAtomicFilePS (fn2fp f)@@ -137,8 +139,10 @@ runDefault :: DefaultIO a -> IO a runDefault = runDefaultIO -instance TolerantMonad m => ApplyMonad (TolerantWrapper m) Tree where+instance TolerantMonad m => ApplyMonad Tree (TolerantWrapper m) where type ApplyMonadBase (TolerantWrapper m) = IO++instance TolerantMonad m => ApplyMonadTree (TolerantWrapper m) where mDoesDirectoryExist d = runTM $ runDefaultIO $ mDoesDirectoryExist d mReadFilePS f = runTM $ runDefaultIO $ mReadFilePS f mChangePref a b c = warning $ runDefaultIO $ mChangePref a b c
src/Darcs/Repository/Cache.hs view
@@ -44,13 +44,13 @@ import Darcs.Util.ByteString ( gzWriteFilePS, linesPS ) import Darcs.Util.Global ( darcsdir, addBadSource, isBadSource, addReachableSource,- isReachableSource, getBadSourcesList )-import Darcs.Repository.External ( gzFetchFilePS, fetchFilePS,- speculateFileOrUrl, copyFileOrUrl,- Cachable( Cachable ) )-import Darcs.Repository.Flags ( Compression(..), RemoteDarcs(..) )-import Darcs.Repository.Lock ( writeAtomicFilePS, gzWriteAtomicFilePS,- withTemp )+ isReachableSource, getBadSourcesList, defaultRemoteDarcsCmd )+import Darcs.Util.External ( gzFetchFilePS, fetchFilePS+ , speculateFileOrUrl, copyFileOrUrl+ , Cachable( Cachable ) )+import Darcs.Repository.Flags ( Compression(..) )+import Darcs.Util.Lock ( writeAtomicFilePS, gzWriteAtomicFilePS,+ withTemp ) import Darcs.Util.SignalHandler ( catchNonSignal ) import Darcs.Util.URL ( isValidLocalPath, isHttpUrl, isSshUrl ) import Darcs.Util.File ( withCurrentDirectory )@@ -318,7 +318,7 @@ `catchNonSignal` \e -> checkCacheReachability (show e) c else do debugMessage $ "Copying from " ++ show cacheFile ++ " to " ++ show out- copyFileOrUrl DefaultRemoteDarcs cacheFile out Cachable+ copyFileOrUrl defaultRemoteDarcsCmd cacheFile out Cachable `catchNonSignal` (\e -> do checkCacheReachability (show e) c sfuc out cs) -- try another read-only location@@ -382,7 +382,7 @@ checkHashedInventoryReachability :: CacheLoc -> IO Bool checkHashedInventoryReachability cache = withTemp $ \tempout -> do let f = cacheSource cache </> darcsdir </> "hashed_inventory"- copyFileOrUrl DefaultRemoteDarcs f tempout Cachable+ copyFileOrUrl defaultRemoteDarcsCmd f tempout Cachable return True `catchNonSignal` const (return False)
+ src/Darcs/Repository/Clone.hs view
@@ -0,0 +1,507 @@+{-# LANGUAGE CPP, ScopedTypeVariables #-}+module Darcs.Repository.Clone+ ( createRepository+ , cloneRepository+ , replacePristine+ , writePatchSet+ , patchSetToRepository+ ) where++import Prelude ()+import Darcs.Prelude++import Control.Exception ( catch, SomeException )+import Control.Monad ( when, void )+import qualified Data.ByteString.Char8 as BS+import Data.Maybe( catMaybes, isJust )+import System.FilePath( (</>) )+import System.Directory+ ( createDirectory+ , removeFile+ , getDirectoryContents+ , getCurrentDirectory+ , setCurrentDirectory+ )+import System.IO ( stderr )+import System.IO.Error ( isAlreadyExistsError )++import Darcs.Repository.State ( invalidateIndex, readWorking )++import Darcs.Repository.Internal+ ( Repository(..)+ , IdentifyRepo(..)+ , identifyRepositoryFor+ , identifyRepository+ , maybeIdentifyRepository+ , readRepo+ , tentativelyRemovePatches+ , tentativelyAddToPending+ , finalizeRepositoryChanges+ , createPristineDirectoryTree+ , setScriptsExecutable+ , setScriptsExecutablePatches+ , seekRepo+ , repoPatchType+ , revertRepositoryChanges+ )+import Darcs.Repository.InternalTypes+ ( modifyCache )+import Darcs.Repository.Job ( RepoJob(..), withRepoLock, withRepository )+import Darcs.Repository.Cache+ ( unionRemoteCaches+ , unionCaches+ , fetchFileUsingCache+ , speculateFileUsingCache+ , HashedDir(..)+ , Cache(..)+ , CacheLoc(..)+ , repo2cache+ )+import qualified Darcs.Repository.Cache as DarcsCache++import qualified Darcs.Repository.HashedRepo as HashedRepo+import Darcs.Repository.ApplyPatches ( applyPatches, runDefault )+import Darcs.Repository.HashedRepo+ ( applyToTentativePristine+ , pris2inv+ , inv2pris+ )+import Darcs.Repository.Format+ ( RepoProperty ( HashedInventory, Darcs2 )+ , RepoFormat+ , createRepoFormat+ , formatHas+ , writeRepoFormat+ , readProblem+ )+import Darcs.Repository.Prefs ( writeDefaultPrefs, addRepoSource, deleteSources )+import Darcs.Repository.Match ( getOnePatchset )+import Darcs.Util.External+ ( copyFileOrUrl+ , Cachable(..)+ , gzFetchFilePS+ )+import Darcs.Repository.PatchIndex+ ( createOrUpdatePatchIndexDisk+ , doesPatchIndexExist+ , createPIWithInterrupt+ )+import Darcs.Repository.Packs+ ( fetchAndUnpackBasic+ , fetchAndUnpackPatches+ , packsDir+ )+import Darcs.Util.Lock+ ( writeBinFile+ , writeDocBinFile+ , appendBinFile+ )+import Darcs.Repository.Flags+ ( UpdateWorking(..)+ , UseCache(..)+ , RemoteDarcs (..)+ , remoteDarcs+ , Compression (..)+ , CloneKind (..)+ , Verbosity (..)+ , DryRun (..)+ , UMask (..)+ , SetScriptsExecutable (..)+ , RemoteRepos (..)+ , SetDefault (..)+ , WithWorkingDir (..)+ , ForgetParent (..)+ , WithPatchIndex (..)+ , PatchFormat (..)+ )++import Darcs.Patch ( RepoPatch, IsRepoType, apply, invert, effect, PrimOf )+import Darcs.Patch.Depends ( findCommonWithThem, countUsThem )+import Darcs.Patch.Set ( Origin+ , PatchSet(..)+ , newset2RL+ , newset2FL+ , progressPatchSet+ )+import Darcs.Patch.Match ( MatchFlag(..), havePatchsetMatch )+import Darcs.Patch.Progress ( progressRLShowTags, progressFL )+import Darcs.Patch.Apply( ApplyState )+import Darcs.Patch.Witnesses.Sealed ( Sealed(..) )+import Darcs.Patch.Witnesses.Ordered+ ( (:>)(..)+ , lengthFL+ , mapFL_FL+ , RL(..)+ , bunchFL+ , mapFL+ , mapRL+ , lengthRL+ )+import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, extractHash, hopefully )++import Darcs.Util.Hash( encodeBase16 )+import Darcs.Util.Tree( Tree, emptyTree )+import Darcs.Util.Tree.Hashed( writeDarcsHashed, darcsAddMissingHashes )++import Darcs.Util.ByteString( gzReadFilePS )+import Darcs.Util.Download ( maxPipelineLength )+import Darcs.Util.Exception ( catchall )+import Darcs.Util.File ( withCurrentDirectory )+import Darcs.Util.English ( englishNum, Noun(..) )+import Darcs.Util.Global ( darcsdir )+import Darcs.Util.URL ( isValidLocalPath )+import Darcs.Util.SignalHandler ( catchInterrupt )+import Darcs.Util.Printer ( Doc, text, hPutDocLn, putDocLn, errorDoc, RenderMode(..) )+import Darcs.Util.Progress+ ( debugMessage+ , tediousSize+ , beginTedious+ , endTedious+ )++#include "impossible.h"++createRepository :: PatchFormat -> WithWorkingDir -> WithPatchIndex -> IO ()+createRepository patchfmt withWorkingDir createPatchIndex = do+ createDirectory darcsdir `catch`+ (\e-> if isAlreadyExistsError e+ then fail "Tree has already been initialized!"+ else fail $ "Error creating directory `"++darcsdir++"'.")+ cwd <- getCurrentDirectory+ x <- seekRepo+ when (isJust x) $ do+ setCurrentDirectory cwd+ putStrLn "WARNING: creating a nested repository."+ createDirectory $ darcsdir </> "pristine.hashed"+ createDirectory $ darcsdir </> "patches"+ createDirectory $ darcsdir </> "inventories"+ createDirectory $ darcsdir </> "prefs"+ writeDefaultPrefs+ writeRepoFormat (createRepoFormat patchfmt withWorkingDir) (darcsdir </> "format")+ writeBinFile (darcsdir </> "hashed_inventory") ""+ writePristine "." emptyTree+ withRepository NoUseCache $ RepoJob $ \repo -> case createPatchIndex of+ NoPatchIndex -> return () -- default+ YesPatchIndex -> createOrUpdatePatchIndexDisk repo++cloneRepository ::+ String -- origin repository path+ -> String -- new repository name (for relative path)+ -> Verbosity -> UseCache+ -> CloneKind+ -> UMask -> RemoteDarcs+ -> SetScriptsExecutable+ -> RemoteRepos -> SetDefault+ -> [MatchFlag]+ -> RepoFormat+ -> WithWorkingDir+ -> WithPatchIndex -- use patch index+ -> Bool -- use packs+ -> ForgetParent+ -> IO ()+cloneRepository repodir mysimplename v uc cloneKind um rdarcs sse remoteRepos+ setDefault matchFlags rfsource withWorkingDir usePatchIndex usePacks forget = do+ createDirectory mysimplename+ setCurrentDirectory mysimplename+ createRepository (if formatHas Darcs2 rfsource then PatchFormat2 else PatchFormat1)+ withWorkingDir+ (if cloneKind == LazyClone then NoPatchIndex else usePatchIndex)+ debugMessage "Finished initializing new repository."+ addRepoSource repodir NoDryRun remoteRepos setDefault++ debugMessage "Grabbing lock in new repository."+ withRepoLock NoDryRun uc YesUpdateWorking um+ $ RepoJob $ \repository -> do+ debugMessage "Identifying and copying repository..."+ fromRepo@(Repo fromDir rffrom _ fromCache) <- identifyRepositoryFor repository uc repodir+ case readProblem rffrom of+ Just e -> fail $ "Incompatibility with repository " ++ fromDir ++ ":\n" ++ e+ Nothing -> return ()+ debugMessage "Copying prefs"+ copyFileOrUrl (remoteDarcs rdarcs) (fromDir </> darcsdir </> "prefs" </> "prefs")+ (darcsdir </> "prefs/prefs") (MaxAge 600) `catchall` return ()+ -- prepare sources and cache+ (Repo toDir toFormat toPristine toCache) <- identifyRepository uc "."+ toCache2 <- unionRemoteCaches toCache fromCache fromDir+ toRepo <- copySources (Repo toDir toFormat toPristine toCache2) fromDir+ if formatHas HashedInventory rffrom then do+ -- copying basic repository (hashed_inventory and pristine)+ if usePacks && (not . isValidLocalPath) fromDir+ then copyBasicRepoPacked fromRepo toRepo v rdarcs withWorkingDir+ else copyBasicRepoNotPacked fromRepo toRepo v rdarcs withWorkingDir+ when (cloneKind /= LazyClone) $ do+ when (cloneKind /= CompleteClone) $+ putInfo v $ text "Copying patches, to get lazy repository hit ctrl-C..."+ -- copying complete repository (inventories and patches)+ if usePacks && (not . isValidLocalPath) fromDir+ then copyCompleteRepoPacked fromRepo toRepo v cloneKind+ else copyCompleteRepoNotPacked fromRepo toRepo v cloneKind+ else+ -- old-fashioned repositories are cloned diferently since+ -- we need to copy all patches first and then build pristine+ copyRepoOldFashioned fromRepo toRepo v withWorkingDir+ when (sse == YesSetScriptsExecutable) setScriptsExecutable+ when (havePatchsetMatch (repoPatchType repository) matchFlags) $ do+ putStrLn "Going to specified version..."+ -- the following is necessary to be able to read repo's patches+ revertRepositoryChanges toRepo YesUpdateWorking+ patches <- readRepo toRepo+ Sealed context <- getOnePatchset toRepo matchFlags+ 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 v $ text $ "Unapplying " ++ show (lengthFL ps) ++ " " +++ englishNum (lengthFL ps) (Noun "patch") ""+ invalidateIndex toRepo+ _ <- tentativelyRemovePatches toRepo GzipCompression YesUpdateWorking us'+ tentativelyAddToPending toRepo YesUpdateWorking $ invert $ effect us'+ finalizeRepositoryChanges toRepo YesUpdateWorking GzipCompression+ runDefault (apply (invert $ effect ps)) `catch` \(e :: SomeException) ->+ fail ("Couldn't undo patch in working dir.\n" ++ show e)+ when (sse == YesSetScriptsExecutable) $ setScriptsExecutablePatches (invert $ effect ps)+ when (forget == YesForgetParent) deleteSources++putInfo :: Verbosity -> Doc -> IO ()+putInfo Quiet _ = return ()+putInfo _ d = hPutDocLn Encode stderr d++putVerbose :: Verbosity -> Doc -> IO ()+putVerbose Verbose d = putDocLn d+putVerbose _ _ = return ()++copyBasicRepoNotPacked :: forall rt p wR wU wT. (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -- remote+ -> Repository rt p wR wU wT -- existing empty local+ -> Verbosity+ -> RemoteDarcs+ -> WithWorkingDir+ -> IO ()+copyBasicRepoNotPacked (Repo fromDir _ _ _) toRepo verb rdarcs withWorkingDir = do+ putVerbose verb $ text "Copying hashed inventory from remote repo..."+ HashedRepo.copyHashedInventory toRepo rdarcs fromDir+ putVerbose verb $ text "Writing pristine and working directory contents..."+ createPristineDirectoryTree toRepo "." withWorkingDir++copyCompleteRepoNotPacked :: forall rt p wR wU wT. (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -- remote+ -> Repository rt p wR wU wT -- existing basic local+ -> Verbosity+ -> CloneKind+ -> IO ()+copyCompleteRepoNotPacked _ torepository@(Repo todir _ _ _) verb cloneKind = do+ let cleanup = putInfo verb $ text "Using lazy repository."+ allowCtrlC cloneKind cleanup $ do+ fetchPatchesIfNecessary torepository+ pi <- doesPatchIndexExist todir+ when pi $ createPIWithInterrupt torepository++copyBasicRepoPacked ::+ forall rt p wR wU wT. (IsRepoType rt, RepoPatch p, ApplyState (PrimOf p) ~ Tree, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -- remote+ -> Repository rt p wR wU wT -- existing empty local repository+ -> Verbosity+ -> RemoteDarcs+ -> WithWorkingDir+ -> IO ()+copyBasicRepoPacked r@(Repo fromDir _ _ _) toRepo verb rdarcs withWorkingDir =+ do let hashURL = fromDir </> darcsdir </> packsDir </> "pristine"+ mPackHash <- (Just <$> gzFetchFilePS hashURL Uncachable) `catchall` (return Nothing)+ let hiURL = fromDir </> darcsdir </> "hashed_inventory"+ i <- gzFetchFilePS hiURL Uncachable+ let currentHash = BS.pack $ inv2pris i+ let copyNormally = copyBasicRepoNotPacked r toRepo verb rdarcs withWorkingDir+ case mPackHash of+ Just packHash | packHash == currentHash+ -> ( copyBasicRepoPacked2 r toRepo verb withWorkingDir+ `catch` \(e :: SomeException) ->+ do putStrLn ("Exception while getting basic pack:\n" ++ show e)+ copyNormally)+ _ -> do putVerbose verb $ text "Remote repo has no basic pack or outdated basic pack, copying normally."+ copyNormally++copyBasicRepoPacked2 ::+ forall rt p wR wU wT. (IsRepoType rt, RepoPatch p, ApplyState (PrimOf p) ~ Tree, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -- remote+ -> Repository rt p wR wU wT -- existing empty local repository+ -> Verbosity+ -> WithWorkingDir+ -> IO ()+copyBasicRepoPacked2 (Repo fromDir _ _ _) toRepo@(Repo _ _ _ toCache) verb withWorkingDir = do+ putVerbose verb $ text "Cloning packed basic repository."+ -- unpack inventory & pristine cache+ cleanDir $ darcsdir </> "pristine.hashed"+ removeFile $ darcsdir </> "hashed_inventory"+ fetchAndUnpackBasic toCache fromDir+ putInfo verb $ text "Done fetching and unpacking basic pack."+ createPristineDirectoryTree toRepo "." withWorkingDir++copyCompleteRepoPacked ::+ forall rt p wR wU wT. (IsRepoType rt, RepoPatch p, ApplyState (PrimOf p) ~ Tree, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -- remote+ -> Repository rt p wR wU wT -- existing basic local repository+ -> Verbosity+ -> CloneKind+ -> IO ()+copyCompleteRepoPacked r to verb cloneKind =+ ( copyCompleteRepoPacked2 r to verb cloneKind+ `catch` \(e :: SomeException) ->+ do putStrLn ("Exception while getting patches pack:\n" ++ show e)+ putVerbose verb $ text "Problem while copying patches pack, copying normally."+ copyCompleteRepoNotPacked r to verb cloneKind )++copyCompleteRepoPacked2 ::+ forall rt p wR wU wT. (IsRepoType rt, RepoPatch p, ApplyState (PrimOf p) ~ Tree, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> Repository rt p wR wU wT+ -> Verbosity+ -> CloneKind+ -> IO ()+copyCompleteRepoPacked2 (Repo fromDir _ _ _)+ toRepo@(Repo toDir _ _ toCache)+ verb cloneKind = do+ us <- readRepo toRepo+ -- get old patches+ let cleanup = putInfo verb $ text "Using lazy repository."+ allowCtrlC cloneKind cleanup $ do+ putVerbose verb $ text "Using patches pack."+ fetchAndUnpackPatches (mapRL hashedPatchFileName $ newset2RL us) toCache fromDir+ pi <- doesPatchIndexExist toDir+ when pi $ createPIWithInterrupt toRepo++cleanDir :: FilePath -> IO ()+cleanDir d = mapM_ (\x -> removeFile $ d </> x) .+ filter (\x -> head x /= '.') =<< getDirectoryContents d++copyRepoOldFashioned :: forall rt p wR wU wT. (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -- remote repo+ -> Repository rt p wR wU wT -- local empty repo+ -> Verbosity+ -> WithWorkingDir+ -> IO ()+copyRepoOldFashioned fromrepository toRepo@(Repo _ _ _ toCache) verb withWorkingDir = do+ HashedRepo.revertTentativeChanges+ patches <- readRepo fromrepository+ let k = "Copying patch"+ beginTedious k+ tediousSize k (lengthRL $ newset2RL patches)+ let patches' = progressPatchSet k patches+ HashedRepo.writeTentativeInventory toCache GzipCompression patches'+ endTedious k+ HashedRepo.finalizeTentativeChanges toRepo GzipCompression+ -- apply all patches into current hashed repository+ HashedRepo.revertTentativeChanges+ local_patches <- readRepo toRepo+ replacePristine toRepo emptyTree+ let patchesToApply = progressFL "Applying patch" $ newset2FL local_patches+ sequence_ $ mapFL applyToTentativePristine $ bunchFL 100 patchesToApply+ finalizeRepositoryChanges toRepo YesUpdateWorking GzipCompression+ putVerbose verb $ text "Writing pristine and working directory contents..."+ createPristineDirectoryTree toRepo "." withWorkingDir++-- | This function fetches all patches that the given repository has+-- with fetchFileUsingCache, unless --lazy is passed.+fetchPatchesIfNecessary :: forall rt p wR wU wT. (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> IO ()+fetchPatchesIfNecessary torepository@(Repo _ _ _ c) =+ do 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 hashes :: forall wX wY . RL (PatchInfoAnd rt p) wX wY -> [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++-- | 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.+patchSetToRepository :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR1 wU1 wR1+ -> PatchSet rt p Origin wX+ -> UseCache -> RemoteDarcs+ -> IO ()+patchSetToRepository (Repo fromrepo rf _ _) patchset useCache rDarcs = do+ when (formatHas HashedInventory rf) $ -- set up sources and all that+ do writeFile (darcsdir </> "tentative_pristine") "" -- this is hokey+ repox <- writePatchSet patchset useCache+ HashedRepo.copyHashedInventory repox rDarcs fromrepo+ void $ copySources repox fromrepo+ repo@(Repo dir _ _ _) <- writePatchSet patchset useCache+ readRepo repo >>= (runDefault . applyPatches . newset2FL)+ debugMessage "Writing the pristine"+ withCurrentDirectory dir $ readWorking >>= replacePristine repo++-- | writePatchSet is like patchSetToRepository, except that it doesn't+-- touch the working directory or pristine cache.+writePatchSet :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => PatchSet rt p Origin wX+ -> UseCache+ -> IO (Repository rt p wR wU wT)+writePatchSet patchset useCache = do+ maybeRepo <- maybeIdentifyRepository useCache "."+ let repo@(Repo _ _ _ c) =+ case maybeRepo of+ 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"+ HashedRepo.writeTentativeInventory c GzipCompression patchset+ HashedRepo.finalizeTentativeChanges repo GzipCompression+ return repo++-- | Replace the existing pristine with a new one (loaded up in a Tree object).+replacePristine :: Repository rt p wR wU wT -> Tree IO -> IO ()+replacePristine (Repo r _ _ _) = writePristine r++writePristine :: FilePath -> Tree IO -> IO ()+writePristine r tree = withCurrentDirectory r $+ do let t = darcsdir </> "hashed_inventory"+ i <- gzReadFilePS t+ tree' <- darcsAddMissingHashes tree+ root <- writeDarcsHashed tree' $ darcsdir </> "pristine.hashed"+ writeDocBinFile t $ pris2inv (BS.unpack $ encodeBase16 root) i++allowCtrlC :: CloneKind -> IO () -> IO () -> IO ()+allowCtrlC CompleteClone _ action = action+allowCtrlC _ cleanup action = action `catchInterrupt` cleanup++hashedPatchFileName :: PatchInfoAnd rt p wA wB -> String+hashedPatchFileName x = case extractHash x of+ Left _ -> fail "unexpected unhashed patch"+ Right h -> h++-- |'copySources' does two things:+-- * it copies the prefs/sources file to the local repo, from the+-- remote, having first filtered the local filesystem sources.+-- * it returns the original list of sources of the local repo+-- updated with the remote repo as an additional source+copySources :: RepoPatch p+ => Repository rt p wR wU wT+ -> String+ -> IO (Repository rt p wR wU wT)+copySources repo@(Repo outr _ _ cache0) inr = do+ let (Repo s f p newCache1) = modifyCache repo dropNonRepos+ let sourcesToWrite = repo2cache inr `unionCaches` newCache1+ appendBinFile (outr ++ "/" ++ darcsdir ++ "/prefs/sources")+ (show sourcesToWrite)+ debugMessage "Done copying and filtering pref/sources."++ -- put remote source last:+ let newSources = cache0 `unionCaches` repo2cache inr+ return (Repo s f p newSources)+ where+ dropNonRepos (Ca cache) = Ca $ filter notRepo cache+ notRepo xs = case xs of+ Cache DarcsCache.Directory _ _ -> False+ -- we don't want to write thisrepo: entries to the disk+ Cache DarcsCache.Repo DarcsCache.Writable _ -> False+ _ -> True
− src/Darcs/Repository/Compat.hs
@@ -1,141 +0,0 @@-{-# OPTIONS_GHC -fno-warn-dodgy-imports #-} -- needed for GHC 7.0/7.2-{-# LANGUAGE CPP, ForeignFunctionInterface #-}--module Darcs.Repository.Compat- ( stdoutIsAPipe- , mkStdoutTemp- , canonFilename- , maybeRelink- , atomicCreate- , sloppyAtomicCreate- ) where--import Prelude hiding ( catch )--import Darcs.Util.File ( withCurrentDirectory )-#ifdef WIN32-import Data.Bits ( (.&.) )-import System.Random ( randomIO )-import Numeric ( showHex )-#else-#endif--import Control.Monad ( unless )-import Foreign.C.Types ( CInt(..) )-import Foreign.C.String ( CString, withCString-#ifndef WIN32- , peekCString-#endif- )--import Foreign.C.Error ( throwErrno, eEXIST, getErrno )-import System.Directory ( getCurrentDirectory )-import System.IO ( hFlush, stdout, stderr, hSetBuffering,- BufferMode(NoBuffering) )-import System.IO.Error ( mkIOError, alreadyExistsErrorType )-import System.Posix.Files ( stdFileMode )-import System.Posix.IO ( openFd, closeFd, stdOutput, stdError,- dupTo, defaultFileFlags, exclusive,- OpenMode(WriteOnly) )-import System.Posix.Types ( Fd(..) )--import Darcs.Util.SignalHandler ( stdoutIsAPipe )--canonFilename :: FilePath -> IO FilePath-canonFilename f@(_:':':_) = return f -- absolute windows paths-canonFilename f@('/':_) = return f-canonFilename ('.':'/':f) = do cd <- getCurrentDirectory- return $ cd ++ "/" ++ f-canonFilename f = case reverse $ dropWhile (/='/') $ reverse f of- "" -> fmap (++('/':f)) getCurrentDirectory- rd -> withCurrentDirectory rd $- do fd <- getCurrentDirectory- return $ fd ++ "/" ++ simplefilename- where- simplefilename = reverse $ takeWhile (/='/') $ reverse f--#ifdef WIN32-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 }--showHexLen :: (Integral a, Show a)- => Int- -> a- -> String-showHexLen n x = let s = showHex x ""- in replicate (n - length s) ' ' ++ s-#else-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- else do str' <- peekCString cstr- fname <- canonFilename str'- return (Fd fd, fname)--foreign import ccall unsafe "static stdlib.h mkstemp"- c_mkstemp :: CString -> IO CInt-#endif--mkStdoutTemp :: String -> IO String-mkStdoutTemp str = do (fd, fn) <- mkstempCore str- hFlush stdout- hFlush stderr- _ <- dupTo fd stdOutput- _ <- dupTo fd stdError- hFlush stdout- hFlush stderr- hSetBuffering stdout NoBuffering- hSetBuffering stderr NoBuffering- return fn----foreign import ccall unsafe "maybe_relink.h maybe_relink" maybe_relink- :: CString -> CString -> CInt -> IO CInt---- Checks whether src and dst are identical. If so, makes dst into a--- link to src. Returns True if dst is a link to src (either because--- we linked it or it already was). Safe against changes to src if--- they are not in place, but not to dst.-maybeRelink :: String -> String -> IO Bool-maybeRelink src dst =- withCString src $ \csrc ->- withCString dst $ \cdst ->- do rc <- maybe_relink csrc cdst 1- case rc of- 0 -> return True- 1 -> return True- -1 -> throwErrno ("Relinking " ++ dst)- -2 -> return False- -3 -> do putStrLn ("Relinking: race condition avoided on file " ++- dst)- return False- _ -> fail ("Unexpected situation when relinking " ++ dst)--sloppyAtomicCreate :: FilePath -> IO ()-sloppyAtomicCreate fp- = do fd <- openFd fp WriteOnly (Just stdFileMode) flags- closeFd fd- where flags = defaultFileFlags { exclusive = True }--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- ("atomicCreate in "++pwd)- Nothing (Just fp)- else throwErrno $ "atomicCreate "++fp++" in "++pwd--foreign import ccall unsafe "atomic_create.h atomic_create" c_atomic_create- :: CString -> IO CInt
src/Darcs/Repository/Diff.hs view
@@ -34,6 +34,8 @@ treeDiff ) where +import Prelude ()+import Darcs.Prelude import qualified Data.ByteString.Lazy.Char8 as BLC import qualified Data.ByteString as BS@@ -41,7 +43,7 @@ import Data.List ( sortBy ) -import Storage.Hashed.Tree ( diffTrees+import Darcs.Util.Tree ( diffTrees , zipTrees , TreeItem(..) , Tree
− src/Darcs/Repository/External.hs
@@ -1,297 +0,0 @@-{-# LANGUAGE CPP #-}-module Darcs.Repository.External- ( cloneTree- , cloneFile- , fetchFilePS- , fetchFileLazyPS- , gzFetchFilePS- , speculateFileOrUrl- , copyFileOrUrl- , Cachable(..)- , backupByRenaming- , backupByCopying- , environmentHelpProtocols- ) where--import Prelude hiding ( catch )-import Control.Exception ( catch, IOException )--import System.Posix.Files- ( getSymbolicLinkStatus- , isRegularFile- , isDirectory- , createLink- )-import System.Directory- ( createDirectory- , getDirectoryContents- , doesDirectoryExist- , doesFileExist- , renameFile- , renameDirectory- , copyFile- )--import System.Exit ( ExitCode(..) )-import System.Environment ( getEnv )-import System.FilePath.Posix ( (</>), normalise )-import System.IO.Error ( isDoesNotExistError )-import Control.Monad- ( unless- , when- , zipWithM_- )--import Data.Char ( toUpper )--import Darcs.Util.Exec ( exec, Redirect(..) )-import Darcs.Util.Download- ( copyUrl- , copyUrlFirst- , waitUrl- , Cachable(..)- )--import Darcs.Util.URL- ( isValidLocalPath- , isHttpUrl- , isSshUrl- , splitSshUrl- )-import Darcs.Util.Text ( breakCommand )-import Darcs.Util.Exception ( catchall )-import Darcs.Repository.Flags ( RemoteDarcs(..) )-import Darcs.Repository.Lock ( withTemp )-import Darcs.Repository.Ssh ( copySSH )--import Darcs.Util.ByteString ( gzReadFilePS )-import qualified Data.ByteString as B (ByteString, readFile )-import qualified Data.ByteString.Lazy as BL--#ifdef HAVE_HTTP-import Network.Browser- ( browse- , request- , setErrHandler- , setOutHandler- , setAllowRedirects- )-import Network.HTTP- ( RequestMethod(GET)- , rspCode- , rspBody- , rspReason- , mkRequest- )-import Network.URI- ( parseURI- , uriScheme- )-#endif--copyFileOrUrl :: RemoteDarcs -> FilePath -> FilePath -> Cachable -> IO ()-copyFileOrUrl _ fou out _ | isValidLocalPath fou = copyLocal fou out-copyFileOrUrl _ fou out cache | isHttpUrl fou = copyRemote fou out cache-copyFileOrUrl rd fou out _ | isSshUrl fou = copySSH rd (splitSshUrl fou) out-copyFileOrUrl _ fou _ _ = fail $ "unknown transport protocol: " ++ fou--copyLocal :: String -> FilePath -> IO ()-copyLocal fou out = createLink fou out `catchall` cloneFile fou out--copyRemote :: String -> FilePath -> Cachable -> IO ()-copyRemote u v cache =- do maybeget <- maybeURLCmd "GET" u- case maybeget of- Nothing -> copyRemoteNormal u v cache- Just get ->- do let (cmd,args) = breakCommand get- r <- exec cmd (args++[u]) (Null, File v, AsIs)- when (r /= ExitSuccess) $- fail $ "(" ++ get ++ ") failed to fetch: " ++ u--cloneTree :: FilePath -> FilePath -> IO ()-cloneTree = cloneTreeExcept []--cloneTreeExcept :: [FilePath] -> FilePath -> FilePath -> IO ()-cloneTreeExcept except source dest =- do fs <- getSymbolicLinkStatus source- if isDirectory fs then do- fps <- getDirectoryContents source- let fps' = filter (`notElem` (".":"..":except)) fps- mk_source fp = source </> fp- mk_dest fp = dest </> fp- zipWithM_ cloneSubTree (map mk_source fps') (map mk_dest fps')- else fail ("cloneTreeExcept: Bad source " ++ source)- `catch` \(_ :: IOException) -> fail ("cloneTreeExcept: Bad source " ++ source)--cloneSubTree :: FilePath -> FilePath -> IO ()-cloneSubTree source dest =- do fs <- getSymbolicLinkStatus source- if isDirectory fs then do- createDirectory dest- fps <- getDirectoryContents source- let fps' = filter (`notElem` [".", ".."]) fps- mk_source fp = source </> fp- mk_dest fp = dest </> fp- zipWithM_ cloneSubTree (map mk_source fps') (map mk_dest fps')- else if isRegularFile fs then- cloneFile source dest- else fail ("cloneSubTree: Bad source "++ source)- `catch` (\e -> unless (isDoesNotExistError e) $ ioError e)--cloneFile :: FilePath -> FilePath -> IO ()-cloneFile = copyFile--backupByRenaming :: FilePath -> IO ()-backupByRenaming = backupBy rename- where rename x y = do- isD <- doesDirectoryExist x- if isD then renameDirectory x y else renameFile x y--backupByCopying :: FilePath -> IO ()-backupByCopying = backupBy copy- where- copy x y = do- isD <- doesDirectoryExist x- if isD then do createDirectory y- cloneTree (normalise x) (normalise y)- else copyFile x y--backupBy :: (FilePath -> FilePath -> IO ()) -> FilePath -> IO ()-backupBy backup f =- do hasBF <- doesFileExist f- hasBD <- doesDirectoryExist f- when (hasBF || hasBD) $ helper 0- where- helper :: Int -> IO ()- helper i = do existsF <- doesFileExist next- existsD <- doesDirectoryExist next- if existsF || existsD- then helper (i + 1)- else do putStrLn $ "Backing up " ++ f ++ "(" ++ suffix ++ ")"- backup f next- where next = f ++ suffix- suffix = ".~" ++ show i ++ "~"--copyAndReadFile :: (FilePath -> IO a) -> String -> Cachable -> IO a-copyAndReadFile readfn fou _ | isValidLocalPath fou = readfn fou-copyAndReadFile readfn fou cache = withTemp $ \t -> do- copyFileOrUrl DefaultRemoteDarcs fou t cache- readfn t---- | @fetchFile fileOrUrl cache@ returns the content of its argument (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.------ We always use default remote darcs, since it is not fatal if the remote--- darcs does not exist or is too old -- anything that supports transfer-mode--- should do, and if not, we will fall back to SFTP or SCP.-fetchFilePS :: String -> Cachable -> IO B.ByteString-fetchFilePS = copyAndReadFile (B.readFile)---- | @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. See--- "fetchFilePS" for details.-fetchFileLazyPS :: String -> Cachable -> IO BL.ByteString-#ifdef HAVE_HTTP-fetchFileLazyPS x c = case parseURI x of- Just x' | uriScheme x' == "http:" -> do- rsp <- fmap snd . browse $ do- setErrHandler . const $ return ()- setOutHandler . const $ return ()- setAllowRedirects True- request $ mkRequest GET x'- if rspCode rsp /= (2, 0, 0)- then fail $ "fetchFileLazyPS: " ++ rspReason rsp- else return $ rspBody rsp- _ -> copyAndReadFile BL.readFile x c-#else-fetchFileLazyPS = copyAndReadFile BL.readFile-#endif--gzFetchFilePS :: String -> Cachable -> IO B.ByteString-gzFetchFilePS = copyAndReadFile gzReadFilePS--maybeURLCmd :: String -> String -> IO (Maybe String)-maybeURLCmd what url =- do let prot = map toUpper $ takeWhile (/= ':') url- fmap Just (getEnv ("DARCS_" ++ what ++ "_" ++ prot))- `catch` \(_ :: IOException) -> return Nothing--copyRemoteNormal :: String -> FilePath -> Cachable -> IO ()-copyRemoteNormal u v cache = copyUrlFirst u v cache >> waitUrl u--speculateFileOrUrl :: String -> FilePath -> IO ()-speculateFileOrUrl fou out | isHttpUrl fou = speculateRemote fou out- | otherwise = return ()--speculateRemote :: String -> FilePath -> IO () -- speculations are always Cachable-#if defined(HAVE_CURL) || defined(HAVE_HTTP)-speculateRemote u v =- do maybeget <- maybeURLCmd "GET" u- case maybeget of- Just _ -> return () -- can't pipeline these- Nothing -> copyUrl u v Cachable-#else-speculateRemote u _ = const () `fmap` maybeURLCmd "GET" u-#endif--environmentHelpProtocols :: ([String], [String])-environmentHelpProtocols = (["DARCS_GET_FOO", "DARCS_APPLY_FOO"],[- "When trying to access a repository with a URL beginning foo://,",- "darcs will invoke the program specified by the DARCS_GET_FOO",- "environment variable (if defined) to download each file, and the",- "command specified by the DARCS_APPLY_FOO environment variable (if",- "defined) when pushing to a foo:// URL.",- "",- "This method overrides all other ways of getting `foo://xxx` URLs.",- "",- "Note that each command should be constructed so that it sends the downloaded",- "content to STDOUT, and the next argument to it should be the URL.",- "Here are some examples that should work for DARCS_GET_HTTP:",- "",- " fetch -q -o -",- " curl -s -f",- " lynx -source",- " wget -q -O -",- "",- "Apart from such toy examples, it is likely that you will need to",- "manipulate the argument before passing it to the actual fetcher",- "program. For example, consider the problem of getting read access to",- "a repository on a CIFS (SMB) share without mount privileges:",- "",- " export DARCS_GET_SMB='smbclient -c get'",- " darcs get smb://fs/twb/Desktop/hello-world",- "",- "The above command will not work for several reasons. Firstly, Darcs",- "will pass it an argument beginning with `smb:`, which smbclient does",- "not understand. Secondly, the host and share `//fs/twb` must be",- "presented as a separate argument to the path `Desktop/hello-world`.",- "Thirdly, smbclient requires that `get` and the path be a single",- "argument (including a space), rather than two separate arguments.",- "Finally, smbclient's `get` command writes the file to disk, while",- "Darcs expects it to be printed to standard output.",- "",- "In principle, we could get around such problems by making the variable",- "contain a shell script, unfortunately, Darcs splits the command on",- "whitespace and does not understand quotation or escaping. Therefore,",- "we instead need to put commands in separate, executable scripts.",- "",- "Continuing our smbclient example, we create an executable script",- "`~/.darcs/libexec/get_smb` with the following contents:",- "",- " #!/bin/bash -e",- " IFS=/ read host share file <<<'${1#smb://}'",- " smbclient //$host/$share -c 'get $file -'",- "",- "And at last we can say",- "",- " export DARCS_GET_SMB=~/.darcs/libexec/get_smb",- " darcs get smb://fs/twb/Desktop/hello-world",- "",- "The APPLY command will be called with a darcs patchfile piped into",- "its standard input."- ])--
src/Darcs/Repository/Flags.hs view
@@ -2,6 +2,7 @@ ( Compression (..) , RemoteDarcs (..)+ , remoteDarcs , Reorder (..) , Verbosity (..) , UpdateWorking (..)@@ -27,10 +28,14 @@ , WithPatchIndex (..) , WithWorkingDir (..) , ForgetParent (..)+ , PatchFormat (..)+ , IncludeBoring (..) ) where import Darcs.Util.Diff ( DiffAlgorithm(..) )+import Darcs.Util.Global ( defaultRemoteDarcsCmd ) + data Verbosity = Quiet | NormalVerbosity | Verbose deriving ( Eq, Show ) @@ -45,6 +50,10 @@ | DefaultRemoteDarcs deriving ( Eq, Show ) +remoteDarcs :: RemoteDarcs -> String+remoteDarcs DefaultRemoteDarcs = defaultRemoteDarcsCmd+remoteDarcs (RemoteDarcs x) = x+ data Reorder = NoReorder | Reorder deriving ( Eq ) @@ -69,6 +78,9 @@ data LookForMoves = YesLookForMoves | NoLookForMoves deriving ( Eq, Show ) +data IncludeBoring = YesIncludeBoring | NoIncludeBoring+ deriving ( Eq, Show )+ data RunTest = YesRunTest | NoRunTest deriving ( Eq, Show ) @@ -89,6 +101,7 @@ data ScanKnown = ScanKnown -- ^Just files already known to darcs | ScanAll -- ^All files, i.e. look for new ones | ScanBoring -- ^All files, even boring ones+ deriving ( Eq, Show ) -- Various kinds of getting repositories data CloneKind = LazyClone -- ^Just copy pristine and inventories@@ -112,4 +125,7 @@ deriving ( Eq, Show ) data ForgetParent = YesForgetParent | NoForgetParent+ deriving ( Eq, Show )++data PatchFormat = PatchFormat1 | PatchFormat2 deriving ( Eq, Show )
src/Darcs/Repository/Format.hs view
@@ -19,6 +19,9 @@ , removeFromFormat ) where +import Prelude ()+import Darcs.Prelude+ #include "impossible.h" import Control.Monad ( mplus, (<=<) )@@ -27,13 +30,13 @@ import Data.List ( partition, intercalate, (\\) ) import Data.Maybe ( isJust, mapMaybe ) -import Darcs.Repository.External+import Darcs.Util.External ( fetchFilePS , Cachable( Cachable ) ) import Darcs.Util.Global ( darcsdir )-import Darcs.Repository.Lock ( writeBinFile )-import qualified Darcs.Repository.Flags as F ( WithWorkingDir (..) )+import Darcs.Util.Lock ( writeBinFile )+import qualified Darcs.Repository.Flags as F ( WithWorkingDir (..), PatchFormat (..) ) import Darcs.Util.SignalHandler ( catchNonSignal ) import Darcs.Util.Exception ( catchall, prettyException ) @@ -148,17 +151,18 @@ writeRepoFormat :: RepoFormat -> FilePath -> IO () writeRepoFormat rf loc = writeBinFile loc $ show rf --- | @createRepoFormat useFormat1 useNoWorkingDir@ create a repo format-createRepoFormat :: Bool -> F.WithWorkingDir -> RepoFormat-createRepoFormat useFormat1 wwd = RF $ (HashedInventory : flags2wd wwd) : flags2format+-- | Create a repo format. The first argument is whether to use the old (darcs-1)+-- format; the second says whether the repo has a working tree.+createRepoFormat :: F.PatchFormat -> F.WithWorkingDir -> RepoFormat+createRepoFormat fmt wwd = RF $ (HashedInventory : flags2wd wwd) : flags2format fmt where- flags2format = if useFormat1 then [] else [[Darcs2]]- flags2wd F.NoWorkingDir = [NoWorkingDir]- flags2wd _ = []+ flags2format F.PatchFormat1 = []+ flags2format F.PatchFormat2 = [[Darcs2]]+ flags2wd F.NoWorkingDir = [NoWorkingDir]+ flags2wd F.WithWorkingDir = [] --- | @writeProblem form@ tells if we can write to a repo in format @form@,--- first checking if we can read that repo It returns @Nothing@ if there's no--- problem writing to such a repository.+-- | @'writeProblem' source@ returns 'Just' an error message if we cannot write+-- to a repo in format @source@, or 'Nothing' if there's no such problem. writeProblem :: RepoFormat -> Maybe String writeProblem target = readProblem target `mplus` findProblems target wp where
src/Darcs/Repository/HashedIO.hs view
@@ -25,21 +25,23 @@ , pathsAndContents ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Util.Global ( darcsdir ) import qualified Data.Set as Set import System.Directory ( getDirectoryContents, createDirectoryIfMissing ) import Control.Monad.State ( StateT, runStateT, modify, get, put, gets, lift, evalStateT ) import Control.Monad ( when, void, unless )-import Control.Applicative ( (<$>) ) import Data.Maybe ( isJust ) import System.IO.Unsafe ( unsafeInterleaveIO ) import Darcs.Repository.Cache ( Cache(..), fetchFileUsingCache, writeFileUsingCache, peekInCache, speculateFileUsingCache, okayHash, cleanCachesWithHint, HashedDir(..), hashedDir )-import Darcs.Patch.ApplyMonad ( ApplyMonad(..) )+import Darcs.Patch.ApplyMonad ( ApplyMonad(..), ApplyMonadTree(..) ) import Darcs.Repository.Flags ( Compression( .. ), WithWorkingDir (..) )-import Darcs.Repository.Lock ( writeAtomicFilePS, removeFileMayNotExist )+import Darcs.Util.Lock ( writeAtomicFilePS, removeFileMayNotExist ) import Darcs.Util.File ( withCurrentDirectory ) import Darcs.Util.Progress ( debugMessage, tediousSize, finishedOneIO ) import Darcs.Util.Path@@ -61,9 +63,9 @@ import qualified Data.ByteString as B (ByteString, length, empty) import qualified Data.ByteString.Char8 as BC (unpack, pack) -import Storage.Hashed.Darcs( readDarcsHashedDir, darcsLocation,+import Darcs.Util.Tree.Hashed( readDarcsHashedDir, darcsLocation, decodeDarcsHash, decodeDarcsSize )-import Storage.Hashed.Tree( ItemType(..), Tree )+import Darcs.Util.Tree( ItemType(..), Tree ) -- | @readHashFile c subdir hash@ reads the file with hash @hash@ in dir subdir, -- fetching it from 'Cache' @c@ if needed.@@ -119,8 +121,10 @@ Just h -> inh h $ mInCurrentDirectory fn'' j where fn' = normPath fn -instance ApplyMonad (HashedIO p) Tree where+instance ApplyMonad Tree (HashedIO p) where type ApplyMonadBase (HashedIO p) = IO++instance ApplyMonadTree (HashedIO p) where mDoesDirectoryExist fn = do thing <- identifyThing fn case thing of Just (D,_) -> return True _ -> return False
src/Darcs/Repository/HashedRepo.hs view
@@ -41,7 +41,6 @@ , readHashedPristineRoot , pris2inv , inv2pris- , copySources , listInventories , listInventoriesLocal , listInventoriesRepoDir@@ -55,9 +54,9 @@ #include "impossible.h" -import Prelude hiding ( catch )+import Prelude ()+import Darcs.Prelude -import Control.Applicative ( (<$>) ) import Control.Arrow ( (&&&) ) import Control.Exception ( catch, IOException ) import Control.Monad ( unless )@@ -66,13 +65,12 @@ ByteString, splitAt ) import qualified Data.ByteString.Char8 as BC ( unpack, dropWhile, break, pack, ByteString )-import Data.List ( delete ) import qualified Data.Set as Set-import Storage.Hashed.Darcs( hashedTreeIO, readDarcsHashedNosize,+import Darcs.Util.Hash( encodeBase16, Hash(..) )+import Darcs.Util.Tree( treeHash, Tree )+import Darcs.Util.Tree.Hashed( hashedTreeIO, readDarcsHashedNosize, readDarcsHashed, writeDarcsHashed, decodeDarcsHash, decodeDarcsSize )-import Storage.Hashed.Tree( treeHash, Tree )-import Storage.Hashed.Hash( encodeBase16, Hash(..) ) import System.Directory ( createDirectoryIfMissing, getDirectoryContents , doesFileExist, doesDirectoryExist ) import System.FilePath.Posix( (</>) )@@ -80,16 +78,16 @@ import System.IO ( stderr, hPutStrLn ) import Darcs.Util.Printer.Color ( showDoc )-import Darcs.Repository.External+import Darcs.Util.External ( copyFileOrUrl , cloneFile , fetchFilePS , gzFetchFilePS , Cachable( Uncachable ) )-import Darcs.Repository.Flags ( Compression, RemoteDarcs, WithWorkingDir )+import Darcs.Repository.Flags ( Compression, RemoteDarcs, remoteDarcs, WithWorkingDir ) import Darcs.Util.Global ( darcsdir )-import Darcs.Repository.Lock+import Darcs.Util.Lock ( writeBinFile , writeDocBinFile , writeAtomicFilePS@@ -101,27 +99,24 @@ import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, patchInfoAndPatch, info, extractHash, createHashed )-import Darcs.Patch ( RepoPatch, Patchy, showPatch, readPatch, apply )+import Darcs.Patch ( IsRepoType, RepoPatch, Patchy, showPatch, readPatch, apply ) import Darcs.Patch.Apply ( ApplyState ) import Darcs.Patch.Read ( ReadPatch ) import Darcs.Patch.ReadMonads ( parseStrictly )-import Darcs.Patch.Depends ( commuteToEnd, slightlyOptimizePatchset )+import Darcs.Patch.Depends ( removeFromPatchSet, slightlyOptimizePatchset ) import Darcs.Patch.Info ( PatchInfo, showPatchInfo, showPatchInfoUI, readPatchInfo ) import Darcs.Util.Path ( FilePathLike, ioAbsoluteOrRemote, toPath )-import Darcs.Repository.Cache ( Cache(..), CacheLoc(..), fetchFileUsingCache,+import Darcs.Repository.Cache ( Cache(..), fetchFileUsingCache, speculateFilesUsingCache, writeFileUsingCache,- unionCaches, repo2cache, okayHash, takeHash,+ okayHash, takeHash, HashedDir(..), hashedDir, peekInCache, bucketFolder )-import qualified Darcs.Repository.Cache as DarcsCache import Darcs.Repository.HashedIO ( copyHashed, copyPartialsHashed, cleanHashdir )-import Darcs.Repository.InternalTypes ( Repository(..), extractCache,- modifyCache )+import Darcs.Repository.InternalTypes ( Repository(..), extractCache ) import Darcs.Util.File ( withCurrentDirectory ) import Darcs.Patch.Witnesses.Ordered- ( reverseRL, reverseFL, (+<+), FL(..), RL(..),- (:>)(..), mapRL, mapFL )+ ( (+<+), FL(..), RL(..), mapRL ) import Darcs.Patch.Witnesses.Sealed ( Sealed(..), seal, unseal, mapSeal ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP ) @@ -224,8 +219,8 @@ -- then writes out the new tentative inventory, and finally does the atomic -- swap. In general, we can't clean the pristine cache at the same time, since -- a simultaneous get might be in progress.-finalizeTentativeChanges :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> Compression -> IO ()+finalizeTentativeChanges :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> Compression -> IO () finalizeTentativeChanges r compr = do debugMessage "Optimizing the inventory..." -- Read the tentative patches@@ -240,7 +235,7 @@ -- |readHashedPristineRoot attempts to read the pristine hash from the current -- inventory, returning Nothing if it cannot do so.-readHashedPristineRoot :: Repository p wR wU wT -> IO (Maybe String)+readHashedPristineRoot :: Repository rt p wR wU wT -> IO (Maybe String) readHashedPristineRoot (Repo d _ _ _) = withCurrentDirectory d $ do i <- (Just <$> gzReadFilePS hashedInventoryPath) `catch` (\(_ :: IOException) -> return Nothing)@@ -248,7 +243,7 @@ -- |cleanPristine removes any obsolete (unreferenced) entries in the pristine -- cache.-cleanPristine :: Repository p wR wU wT -> IO ()+cleanPristine :: Repository rt p wR wU wT -> IO () cleanPristine r@(Repo d _ _ _) = withCurrentDirectory d $ do debugMessage "Cleaning out the pristine cache..." i <- gzReadFilePS hashedInventoryPath@@ -278,7 +273,7 @@ -- |cleanInventories removes any obsolete (unreferenced) files in the -- inventories directory.-cleanInventories :: Repository p wR wU wT -> IO ()+cleanInventories :: Repository rt p wR wU wT -> IO () cleanInventories _ = do debugMessage "Cleaning out inventories..." hs <- listInventoriesLocal@@ -293,7 +288,7 @@ -- |cleanPatches removes any obsolete (unreferenced) files in the -- patches directory.-cleanPatches :: Repository p wR wU wT -> IO ()+cleanPatches :: Repository rt p wR wU wT -> IO () cleanPatches _ = do debugMessage "Cleaning out patches..." hs <- listPatchesLocal darcsdir@@ -305,7 +300,7 @@ -- |addToSpecificInventory adds a patch to a specific inventory file, and -- returns the FilePath whichs corresponds to the written-out patch. addToSpecificInventory :: RepoPatch p => String -> Cache -> Compression- -> PatchInfoAnd p wX wY -> IO FilePath+ -> PatchInfoAnd rt p wX wY -> IO FilePath addToSpecificInventory invPath c compr p = do let invFile = darcsdir </> invPath hash <- snd <$> writePatchIfNecessary c compr p@@ -314,44 +309,28 @@ return $ darcsdir </> "patches" </> hash addToTentativeInventory :: RepoPatch p => Cache -> Compression- -> PatchInfoAnd p wX wY -> IO FilePath+ -> PatchInfoAnd rt p wX wY -> IO FilePath addToTentativeInventory = addToSpecificInventory tentativeHashedInventory --- |removeFromTentativeInventory attempts to remove an FL of patches from the--- tentative inventory. This is used for commands that wish to modify--- already-recorded patches.-removeFromTentativeInventory :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> Compression- -> FL (PatchInfoAnd p) wX wT -> IO ()+-- | Attempt to remove an FL of patches from the tentative inventory.+-- This is used for commands that wish to modify already-recorded patches.+--+-- Precondition: it must be possible to remove the patches, i.e.+--+-- * the patches are in the repository+--+-- * any necessary commutations will succeed+removeFromTentativeInventory :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> Compression+ -> FL (PatchInfoAnd rt p) wX wT -> IO () removeFromTentativeInventory repo compr to_remove = do- -- 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.+ debugMessage $ "Start removeFromTentativeInventory" allpatches <- readTentativeRepo repo "."- _ :> skipped <- return $ commuteToEnd (reverseFL to_remove) allpatches- okay <- simpleRemoveFromTentativeInventory $- mapFL info to_remove ++ mapRL info skipped- unless okay $ bug "bug in HashedRepo.removeFromTentativeInventory"- sequence_ $ mapFL (addToTentativeInventory (extractCache repo) compr)- (reverseRL skipped)- where- simpleRemoveFromTentativeInventory :: [PatchInfo] -> IO Bool- simpleRemoveFromTentativeInventory pis = do- inv <- readTentativeRepo repo "."- case cut_inv pis inv of- Nothing -> return False- Just (Sealed inv') -> do- writeTentativeInventory (extractCache repo) compr inv'- return True- cut_inv :: [PatchInfo] -> PatchSet p wStart wX- -> Maybe (SealedPatchSet p wStart)- cut_inv [] x = Just $ seal x- 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+ remaining <- case removeFromPatchSet to_remove allpatches of+ Nothing -> bug "HashedRepo.removeFromTentativeInventory: precondition violated"+ Just r -> return r+ writeTentativeInventory (extractCache repo) compr remaining+ debugMessage $ "Done removeFromTentativeInventory" -- |writeHashFile takes a Doc and writes it as a hash-named file, returning the -- filename that the contents were written to.@@ -361,21 +340,21 @@ writeFileUsingCache c compr subdir $ renderPS Standard d -- |readRepo returns the "current" repo patchset.-readRepo :: (RepoPatch p, ApplyState p ~ Tree) => Repository p wR wU wT- -> String -> IO (PatchSet p Origin wR)+readRepo :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT+ -> String -> IO (PatchSet rt p Origin wR) readRepo = readRepoUsingSpecificInventory hashedInventory -- |readRepo returns the tentative repo patchset.-readTentativeRepo :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> String- -> IO (PatchSet p Origin wT)+readTentativeRepo :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> String+ -> IO (PatchSet rt p Origin wT) readTentativeRepo = readRepoUsingSpecificInventory tentativeHashedInventory -- |readRepoUsingSpecificInventory uses the inventory at @invPath@ to read the -- repository @repo@.-readRepoUsingSpecificInventory :: (RepoPatch p, ApplyState p ~ Tree)- => String -> Repository p wR wU wT- -> String -> IO (PatchSet p Origin wS)+readRepoUsingSpecificInventory :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => String -> Repository rt p wR wU wT+ -> String -> IO (PatchSet rt p Origin wS) readRepoUsingSpecificInventory invPath repo dir = do realdir <- toPath <$> ioAbsoluteOrRemote dir Sealed ps <- readRepoPrivate (extractCache repo) realdir invPath@@ -384,8 +363,8 @@ ioError e return $ unsafeCoerceP ps where- readRepoPrivate :: (RepoPatch p, ApplyState p ~ Tree) => Cache -> FilePath- -> FilePath -> IO (SealedPatchSet p Origin)+ readRepoPrivate :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => Cache -> FilePath+ -> FilePath -> IO (SealedPatchSet rt p Origin) readRepoPrivate cache d iname = do inventory <- readInventoryPrivate (d </> darcsdir) iname readRepoFromInventoryList cache inventory@@ -393,35 +372,35 @@ -- |readRepoFromInventoryList allows the caller to provide an optional "from -- inventory" hash, and a list of info/hash pairs that identify a list of -- patches, returning a patchset of the resulting repo.-readRepoFromInventoryList :: (RepoPatch p, ApplyState p ~ Tree) => Cache+readRepoFromInventoryList :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => Cache -> (Maybe String, [(PatchInfo, String)])- -> IO (SealedPatchSet p Origin)+ -> IO (SealedPatchSet rt p Origin) readRepoFromInventoryList cache = parseinvs where speculateAndParse h is i = speculate h is >> parse i h - read_patches :: (RepoPatch p, ApplyState p ~ Tree) => [(PatchInfo, String)]- -> IO (Sealed (RL (PatchInfoAnd p) wX))+ read_patches :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => [(PatchInfo, String)]+ -> IO (Sealed (RL (PatchInfoAnd rt p) wX)) read_patches [] = return $ seal NilRL read_patches allis@((i1, h1) : is1) =- lift2Sealed (\p rest -> i1 `patchInfoAndPatch` p :<: rest) (rp is1)+ lift2Sealed (\p rest -> rest :<: i1 `patchInfoAndPatch` p) (rp is1) (createHashed h1 (const $ speculateAndParse h1 allis i1)) where- rp :: (RepoPatch p, ApplyState p ~ Tree) => [(PatchInfo, String)]- -> IO (Sealed (RL (PatchInfoAnd p) wX))+ rp :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => [(PatchInfo, String)]+ -> IO (Sealed (RL (PatchInfoAnd rt p) wX)) rp [] = return $ seal NilRL rp [(i, h), (il, hl)] =- lift2Sealed (\p rest -> i `patchInfoAndPatch` p :<: rest)+ lift2Sealed (\p rest -> rest :<: i `patchInfoAndPatch` p) (rp [(il, hl)]) (createHashed h (const $ speculateAndParse h (reverse allis) i)) rp ((i, h) : is) =- lift2Sealed (\p rest -> i `patchInfoAndPatch` p :<: rest)+ lift2Sealed (\p rest -> rest :<: i `patchInfoAndPatch` p) (rp is) (createHashed h (parse i)) - read_tag :: (RepoPatch p, ApplyState p ~ Tree) => (PatchInfo, String)- -> IO (Sealed (PatchInfoAnd p wX))+ read_tag :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => (PatchInfo, String)+ -> IO (Sealed (PatchInfoAnd rt p wX)) read_tag (i, h) = mapSeal (patchInfoAndPatch i) <$> createHashed h (parse i) @@ -441,21 +420,21 @@ , "which is patch" , renderString Encode $ showPatchInfoUI i ] - parseinvs :: (RepoPatch p, ApplyState p ~ Tree)+ parseinvs :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => (Maybe String, [(PatchInfo, String)])- -> IO (SealedPatchSet p Origin)+ -> IO (SealedPatchSet rt p Origin) parseinvs (Nothing, ris) =- mapSeal (flip PatchSet NilRL) <$> read_patches (reverse ris)+ mapSeal (PatchSet NilRL) <$> 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 <$> unsafeInterleaveIO (read_ts t h) Sealed ps <- unseal seal <$> unsafeInterleaveIO (read_patches $ reverse ris)- return $ seal $ PatchSet ps ts+ return $ seal $ PatchSet ts ps - read_ts :: (RepoPatch p, ApplyState p ~ Tree) => (PatchInfo, String)- -> String -> IO (Sealed (RL (Tagged p) Origin))+ read_ts :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => (PatchInfo, String)+ -> String -> IO (Sealed (RL (Tagged rt p) Origin)) read_ts tag0 h0 = do contents <- unsafeInterleaveIO $ readTaggedInventoryFromHash h0 let is = reverse $ case contents of@@ -470,7 +449,7 @@ (Nothing, _) -> return $ seal NilRL) Sealed ps <- unseal seal <$> unsafeInterleaveIO (read_patches is) Sealed tag00 <- read_tag tag0- return $ seal $ Tagged tag00 (Just h0) ps :<: ts+ return $ seal $ ts :<: Tagged tag00 (Just h0) ps readTaggedInventoryFromHash :: String -> IO (Maybe String, [(PatchInfo, String)])@@ -515,33 +494,18 @@ -- |copyRepo copies the hashed inventory of @repo@ to the repository located at -- @remote@.-copyHashedInventory :: RepoPatch p => Repository p wR wU wT -> RemoteDarcs -> String -> IO ()-copyHashedInventory (Repo outr _ _ _) remote inr = do+copyHashedInventory :: RepoPatch p => Repository rt p wR wU wT -> RemoteDarcs -> String -> IO ()+copyHashedInventory (Repo outr _ _ _) rdarcs inr | remote <- remoteDarcs rdarcs = do createDirectoryIfMissing False (outr ++ "/" ++ inventoriesDirPath) copyFileOrUrl remote (inr </> darcsdir </> hashedInventory) (outr </> darcsdir </> hashedInventory) Uncachable -- no need to copy anything but hashed_inventory! debugMessage "Done copying hashed inventory." --- |'copySources' copies the prefs/sources file to the local repo, from the--- remote, having first filtered the local filesystem sources.-copySources :: RepoPatch p => Repository p wR wU wT -> String -> IO ()-copySources repo@(Repo outr _ _ _) inr = do- let repoCache = extractCache $ modifyCache repo dropNonRepos- appendBinFile (outr ++ "/" ++ darcsdir ++ "/prefs/sources")- (show $ repo2cache inr `unionCaches` repoCache )- debugMessage "Done copying and filtering pref/sources."- where- dropNonRepos (Ca cache) = Ca $ filter notRepo cache- notRepo xs = case xs of- Cache DarcsCache.Directory _ _ -> False- Cache _ DarcsCache.Writable _ -> False- _ -> True- -- |writeAndReadPatch makes a patch lazy, by writing it out to disk (thus -- forcing it), and then re-reads the patch lazily.-writeAndReadPatch :: RepoPatch p => Cache -> Compression- -> PatchInfoAnd p wX wY -> IO (PatchInfoAnd p wX wY)+writeAndReadPatch :: (IsRepoType rt, RepoPatch p) => Cache -> Compression+ -> PatchInfoAnd rt p wX wY -> IO (PatchInfoAnd rt p wX wY) writeAndReadPatch c compr p = do (i, h) <- writePatchIfNecessary c compr p unsafeInterleaveIO $ readp h i@@ -560,7 +524,7 @@ -- | writeTentativeInventory writes @patchSet@ as the tentative inventory. writeTentativeInventory :: RepoPatch p => Cache -> Compression- -> PatchSet p Origin wX -> IO ()+ -> PatchSet rt p Origin wX -> IO () writeTentativeInventory cache compr patchSet = do debugMessage "in writeTentativeInventory..." createDirectoryIfMissing False inventoriesDirPath@@ -575,20 +539,20 @@ writeAtomicFilePS (darcsdir </> tentativeHashedInventory) content where tediousName = "Writing inventory"- writeInventoryPrivate :: RepoPatch p => PatchSet p Origin wX+ writeInventoryPrivate :: RepoPatch p => PatchSet rt p Origin wX -> IO (Maybe String) writeInventoryPrivate (PatchSet NilRL NilRL) = return Nothing- writeInventoryPrivate (PatchSet ps NilRL) = do+ writeInventoryPrivate (PatchSet NilRL ps) = do inventory <- sequence $ mapRL (writePatchIfNecessary cache compr) ps let inventorylist = hcat (map pihash $ reverse inventory) hash <- writeHashFile cache compr HashedInventoriesDir inventorylist return $ Just hash writeInventoryPrivate- (PatchSet x xs@(Tagged t _ _ :<: _)) = do+ (PatchSet xs@(_ :<: Tagged t _ _) x) = do resthash <- write_ts xs finishedOneIO tediousName $ fromMaybe "" resthash inventory <- sequence $ mapRL (writePatchIfNecessary cache compr)- (x +<+ t :<: NilRL)+ (NilRL :<: t +<+ x) let inventorylist = hcat (map pihash $ reverse inventory) inventorycontents = case resthash of@@ -600,18 +564,18 @@ where -- | write_ts writes out a tagged patchset. If it has already been -- written, we'll have the hash, so we can immediately return it.- write_ts :: RepoPatch p => RL (Tagged p) Origin wX+ write_ts :: RepoPatch p => RL (Tagged rt p) Origin wX -> IO (Maybe String)- write_ts (Tagged _ (Just h) _ :<: _) = return (Just h)- write_ts (Tagged _ Nothing pps :<: tts) =- writeInventoryPrivate $ PatchSet pps tts+ write_ts (_ :<: Tagged _ (Just h) _) = return (Just h)+ write_ts (tts :<: Tagged _ Nothing pps) =+ writeInventoryPrivate $ PatchSet tts pps write_ts NilRL = return Nothing -- |writeHashIfNecessary writes the patch and returns the resulting info/hash, -- if it has not already been written. If it has been written, we have the hash -- in the PatchInfoAnd, so we extract and return the info/hash. writePatchIfNecessary :: RepoPatch p => Cache -> Compression- -> PatchInfoAnd p wX wY -> IO (PatchInfo, String)+ -> PatchInfoAnd rt p wX wY -> IO (PatchInfo, String) writePatchIfNecessary c compr hp = infohp `seq` case extractHash hp of Right h -> return (infohp, h)
src/Darcs/Repository/Internal.hs view
@@ -49,7 +49,6 @@ , finalizeRepositoryChanges , unrevertUrl , applyToWorking- , patchSetToPatches , createPristineDirectoryTree , createPartialsPristineDirectoryTree , reorderInventory@@ -61,9 +60,12 @@ , applyToTentativePristine , makeNewPending , seekRepo+ , repoPatchType+ , repoXor ) where -import Prelude hiding ( catch )+import Prelude ()+import Darcs.Prelude import Darcs.Util.Printer ( putDocLn , (<+>)@@ -74,12 +76,12 @@ , ($$) ) import Darcs.Util.Printer.Color (fancyPrinters)-+import Darcs.Util.Crypt.SHA1 ( SHA1, sha1Xor, zero ) import Darcs.Repository.State ( readRecorded , readWorking , updateIndex )-import Darcs.Repository.LowLevel+import Darcs.Repository.Pending ( readPending , readTentativePending , writeTentativePending@@ -115,13 +117,13 @@ , void ) -import Control.Applicative ( (<$>) ) import Control.Exception ( catch, IOException ) import qualified Data.ByteString as B ( readFile , isPrefixOf ) import qualified Data.ByteString.Char8 as BC (pack)+import Data.List( foldl' ) import Data.List.Ordered ( nubSort ) import Data.Maybe ( fromMaybe ) import Darcs.Patch ( Effect@@ -133,11 +135,12 @@ , commute , fromPrim , RepoPatch+ , IsRepoType , Patchy , merge , listConflictedFiles , listTouchedFiles- , Named+ , WrappedNamed , commuteRL , fromPrims , readPatch@@ -163,13 +166,14 @@ import Darcs.Patch.Bundle ( scanBundle , makeBundleN )-import Darcs.Patch.Info ( isTag )-import Darcs.Patch.MaybeInternal ( flIsInternal )-import Darcs.Patch.Named ( patchcontents )+import Darcs.Patch.Info ( isTag, makePatchname )+import Darcs.Patch.Named.Wrapped ( namedIsInternal ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd , hopefully , info )+import Darcs.Patch.Type ( PatchType(..) )+ import qualified Darcs.Repository.HashedRepo as HashedRepo ( revertTentativeChanges , finalizeTentativeChanges@@ -178,6 +182,7 @@ , copyPartialsPristine , applyToTentativePristine , addToTentativeInventory+ , readRepo , readTentativeRepo , readRepoUsingSpecificInventory , cleanPristine@@ -186,6 +191,7 @@ ) import qualified Darcs.Repository.Old as Old ( revertTentativeChanges+ , readOldRepo , oldRepoFailMsg ) import Darcs.Repository.Flags@@ -223,7 +229,6 @@ ) import Darcs.Patch.Set ( PatchSet(..) , SealedPatchSet- , newset2FL , newset2RL , Origin )@@ -252,7 +257,7 @@ , setExecutable ) import Darcs.Repository.Prefs ( getCaches )-import Darcs.Repository.Lock+import Darcs.Util.Lock ( writeDocBinFile , removeFileMayNotExist )@@ -263,19 +268,19 @@ import System.Mem( performGC ) -import qualified Storage.Hashed.Tree as Tree-import Storage.Hashed.Tree ( Tree )+import Darcs.Util.Tree ( Tree )+import qualified Darcs.Util.Tree as Tree import Darcs.Repository.PatchIndex ( createOrUpdatePatchIndexDisk, doesPatchIndexExist )-import Darcs.Repository.Read ( readRepo ) #include "impossible.h" -- | The status of a given directory: is it a darcs repository?-data IdentifyRepo p wR wU wT = BadRepository String -- ^ looks like a repository with some error- | NonRepository String -- ^ safest guess- | GoodRepository (Repository p wR wU wT)+data IdentifyRepo rt p wR wU wT+ = BadRepository String -- ^ looks like a repository with some error+ | NonRepository String -- ^ safest guess+ | GoodRepository (Repository rt p wR wU wT) -- | Tries to identify the repository in a given directory-maybeIdentifyRepository :: UseCache -> String -> IO (IdentifyRepo p wR wU wT)+maybeIdentifyRepository :: UseCache -> String -> IO (IdentifyRepo rt p wR wU wT) maybeIdentifyRepository useCache "." = do darcs <- doesDirectoryExist darcsdir if not darcs@@ -314,8 +319,8 @@ -- | identifyRepository identifies the repo at 'url'. Warning: -- you have to know what kind of patches are found in that repo.-identifyRepository :: forall p wR wU wT. UseCache -> String- -> IO (Repository p wR wU wT)+identifyRepository :: forall rt p wR wU wT. UseCache -> String+ -> IO (Repository rt p wR wU wT) identifyRepository useCache url = do er <- maybeIdentifyRepository useCache url case er of@@ -325,11 +330,11 @@ -- | @identifyRepositoryFor repo url@ identifies (and returns) the repo at 'url', -- but fails if it is not compatible for reading from and writing to.-identifyRepositoryFor :: forall p wR wU wT vR vU vT. RepoPatch p- => Repository p wR wU wT+identifyRepositoryFor :: forall rt p wR wU wT vR vU vT. RepoPatch p+ => Repository rt p wR wU wT -> UseCache -> String- -> IO (Repository p vR vU vT)+ -> IO (Repository rt p vR vU vT) identifyRepositoryFor (Repo _ source _ _) useCache url = do Repo absurl target x c <- identifyRepository useCache url case transferProblem target source of@@ -357,6 +362,9 @@ _ -> return (Left Old.oldRepoFailMsg) left -> return left +repoPatchType :: Repository rt p wR wU wT -> PatchType rt p+repoPatchType _ = PatchType+ -- | hunt upwards for the darcs repository -- This keeps changing up one parent directory, testing at each -- step if the current directory is a repository or not. $@@ -409,7 +417,7 @@ -- TODO: see also Repository.State.readPendingLL ... to be removed after GHC 7.2 readNewPendingLL :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> IO (Sealed ((FL p) wT))+ => Repository rt p wR wU wT -> IO (Sealed ((FL p) wT)) readNewPendingLL repo = mapSeal (mapFL_FL fromPrim) `fmap` readNewPending repo @@ -417,8 +425,8 @@ -- @pendPs@ could be applied to pristine if we wanted to, and if so -- writes it to disk. If it can't be applied, @pendPs@ must -- be somehow buggy, so we save it for forensics and crash.-makeNewPending :: forall p wR wU wT wY. (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT+makeNewPending :: forall rt p wR wU wT wY. (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> UpdateWorking -> FL (PrimOf p) wT wY -> IO ()@@ -470,37 +478,37 @@ -- we can nuke the hunk, but not so in (hunk :> replace) sift :: FL prim wA wB -> RL prim wC wA -> Sealed (FL prim wC) sift sofar NilRL = seal sofar- sift sofar (p:<:ps) | primIsHunk p || primIsBinary p =+ sift sofar (ps:<:p) | primIsHunk p || primIsBinary p = case commuteFLorComplain (p :> sofar) of Right (sofar' :> _) -> sift sofar' ps Left _ -> sift (p:>:sofar) ps- sift sofar (p:<:ps) = sift (p:>:sofar) ps+ sift sofar (ps:<:p) = sift (p:>:sofar) ps -readTentativeRepo :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT- -> IO (PatchSet p Origin wT)+readTentativeRepo :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> IO (PatchSet rt p Origin wT) readTentativeRepo repo@(Repo r rf _ _) | formatHas HashedInventory rf = HashedRepo.readTentativeRepo repo r | otherwise = fail Old.oldRepoFailMsg -readRepoUsingSpecificInventory :: (RepoPatch p, ApplyState p ~ Tree)+readRepoUsingSpecificInventory :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => String- -> Repository p wR wU wT- -> IO (PatchSet p Origin wT)+ -> Repository rt p wR wU wT+ -> IO (PatchSet rt p Origin wT) readRepoUsingSpecificInventory invPath repo@(Repo r rf _ _) | formatHas HashedInventory rf = HashedRepo.readRepoUsingSpecificInventory invPath repo r | otherwise = fail Old.oldRepoFailMsg -prefsUrl :: Repository p wR wU wT -> String+prefsUrl :: Repository rt p wR wU wT -> String prefsUrl (Repo r _ _ _) = r ++ "/"++darcsdir++"/prefs" -unrevertUrl :: Repository p wR wU wT -> String+unrevertUrl :: Repository rt p wR wU wT -> String unrevertUrl (Repo r _ _ _) = r ++ "/"++darcsdir++"/patches/unrevert" applyToWorking :: (ApplyState (PrimOf p) ~ Tree, RepoPatch p)- => Repository p wR wU wT -> Verbosity -> FL (PrimOf p) wU wY- -> IO (Repository p wR wY wT)+ => Repository rt p wR wU wT -> Verbosity -> FL (PrimOf p) wU wY+ -> IO (Repository rt p wR wY wT) applyToWorking (Repo r rf t c) verb patch = do unless (formatHas NoWorkingDir rf) $@@ -515,10 +523,10 @@ -- somewhere else. -- -- Question (Eric Kow): how do we detect patch equivalence?-tentativelyRemoveFromPending :: forall p wR wU wT wX wY. (RepoPatch p)- => Repository p wR wU wT+tentativelyRemoveFromPending :: forall rt p wR wU wT wX wY. (RepoPatch p)+ => Repository rt p wR wU wT -> UpdateWorking- -> PatchInfoAnd p wX wY+ -> PatchInfoAnd rt p wX wY -> IO () tentativelyRemoveFromPending _ NoUpdateWorking _ = return () tentativelyRemoveFromPending repo YesUpdateWorking p = do@@ -527,7 +535,7 @@ -- changepref patches in p? isSimple includes changepref, so what do -- adddir/etc have to do with it? Why don't we we systematically -- crudeSift/not?- let effectp = if allFL isSimple pend+ let effectp = if isSimple pend then crudeSift $ effect p else effect p Sealed newpend <- return $ rmpend (progressFL "Removing from pending:" effectp)@@ -556,19 +564,38 @@ -- DJR: I don't think this last case should be -- reached, but it also shouldn't lead to corruption. -isSimple :: PrimPatch prim => prim wX wY -> Bool-isSimple x = primIsHunk x || primIsBinary x || primIsSetpref x---- This seems to do the opposite of sifting, ie. we retain hunk/binary patches--- but delete changepref patches.+-- | A sequence of primitive patches (candidates for the pending patch)+-- is considered simple if we can reason about their continued status as+-- pending patches solely on the basis of them being hunk/binary patches. ----- Why not just filterOutFLFL (not . primIsSetpref)? Is it important to only--- have this behaviour when all other patches are either hunk or binary?+-- Simple here seems to mean that all patches are either hunk/binary+-- patches, or patches that cannot (indirectly) depend on hunk/binary+-- patches. For now, the only other kinds of patches in this category+-- are changepref patches.+--+-- It might be tempting to add, say, adddir patches but it's probably not a+-- good idea because Darcs also inverts patches a lot in its reasoning so an+-- innocent addir may be inverted to a rmdir which in turn may depend on+-- a rmfile, which in turn depends on a hunk/binary. Likewise, we would+-- not want to add move patches to this category for similar reasons of+-- a potential dependency chain forming.+isSimple :: PrimPatch prim => FL prim wX wY -> Bool+isSimple =+ allFL isSimp+ where+ isSimp x = primIsHunk x || primIsBinary x || primIsSetpref x++-- | 'crudeSift' can be seen as a first pass approximation of 'siftForPending'+-- that works without having to do any commutation. It either returns a+-- sifted pending (if the input is simple enough for this crude approach)+-- or has no effect. crudeSift :: forall prim wX wY . PrimPatch prim => FL prim wX wY -> FL prim wX wY-crudeSift xs = if allFL isSimple xs then filterOutFLFL ishunkbinary xs else xs- where ishunkbinary :: prim wA wB -> EqCheck wA wB- ishunkbinary x | primIsHunk x || primIsBinary x = unsafeCoerceP IsEq- | otherwise = NotEq+crudeSift xs =+ if isSimple xs then filterOutFLFL ishunkbinary xs else xs+ where+ ishunkbinary :: prim wA wB -> EqCheck wA wB+ ishunkbinary x | primIsHunk x || primIsBinary x = unsafeCoerceP IsEq+ | otherwise = NotEq data HashedVsOld a = HvsO { old, hashed :: a } @@ -602,16 +629,16 @@ " "++cmd++" mark-conflicts\n"++ "to "++darcsdir++"/prefs/defaults in the target repo. " -checkUnrecordedConflicts :: forall p wT wY. RepoPatch p+checkUnrecordedConflicts :: forall rt p wT wY. RepoPatch p => UpdateWorking- -> FL (Named p) wT wY+ -> FL (WrappedNamed rt p) wT wY -> IO Bool checkUnrecordedConflicts NoUpdateWorking _ = return False -- because we are called by `darcs convert` hence we don't care checkUnrecordedConflicts _ pc = do repository <- identifyRepository NoUseCache "." cuc repository- where cuc :: Repository p wR wU wT -> IO Bool+ where cuc :: Repository rt p wR wU wT -> IO Bool cuc r = do Sealed (mpend :: FL (PrimOf p) wT wX) <- readPending r :: IO (Sealed (FL (PrimOf p) wT)) case mpend of NilFL -> return False@@ -631,12 +658,12 @@ fromPrims_ = fromPrims tentativelyAddPatch :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT+ => Repository rt p wR wU wT -> Compression -> Verbosity -> UpdateWorking- -> PatchInfoAnd p wT wY- -> IO (Repository p wR wU wY)+ -> PatchInfoAnd rt p wT wY+ -> IO (Repository rt p wR wU wY) tentativelyAddPatch = tentativelyAddPatch_ UpdatePristine data UpdatePristine = UpdatePristine @@ -644,15 +671,15 @@ | DontUpdatePristineNorRevert deriving Eq tentativelyAddPatches_- :: forall p wR wU wT wY+ :: forall rt p wR wU wT wY . (RepoPatch p, ApplyState p ~ Tree) => UpdatePristine- -> Repository p wR wU wT+ -> Repository rt p wR wU wT -> Compression -> Verbosity -> UpdateWorking- -> FL (PatchInfoAnd p) wT wY- -> IO (Repository p wR wU wY)+ -> FL (PatchInfoAnd rt p) wT wY+ -> IO (Repository rt p wR wU wY) tentativelyAddPatches_ _up r _compr _verb _uw NilFL = return r tentativelyAddPatches_ up r compr verb uw (p:>:ps) = do r' <- tentativelyAddPatch_ up r compr verb uw p@@ -660,15 +687,15 @@ -- TODO re-add a safety catch for --dry-run? Maybe using a global, like dryRun -- :: Bool, with dryRun = unsafePerformIO $ readIORef ...-tentativelyAddPatch_ :: forall p wR wU wT wY+tentativelyAddPatch_ :: forall rt p wR wU wT wY . (RepoPatch p, ApplyState p ~ Tree) => UpdatePristine- -> Repository p wR wU wT+ -> Repository rt p wR wU wT -> Compression -> Verbosity -> UpdateWorking- -> PatchInfoAnd p wT wY- -> IO (Repository p wR wU wY)+ -> PatchInfoAnd rt p wT wY+ -> IO (Repository rt p wR wU wY) tentativelyAddPatch_ up r@(Repo dir rf t c) compr verb uw p = withCurrentDirectory dir $ do@@ -682,7 +709,7 @@ return (Repo dir rf t c) applyToTentativePristine :: (ApplyState q ~ Tree, Effect q, Patchy q, ShowPatch q, PrimPatchBase q)- => Repository p wR wU wT+ => Repository rt p wR wU wT -> Verbosity -> q wT wY -> IO ()@@ -700,8 +727,8 @@ -- 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.-tentativelyAddToPending :: forall p wR wU wT wX wY. RepoPatch p- => Repository p wR wU wT+tentativelyAddToPending :: forall rt p wR wU wT wX wY. RepoPatch p+ => Repository rt p wR wU wT -> UpdateWorking -> FL (PrimOf p) wX wY -> IO ()@@ -719,8 +746,8 @@ -- | setTentativePending is basically unsafe. It overwrites the pending -- state with a new one, not related to the repository state.-setTentativePending :: forall p wR wU wT wX wY. RepoPatch p- => Repository p wR wU wT+setTentativePending :: forall rt p wR wU wT wX wY. RepoPatch p+ => Repository rt p wR wU wT -> UpdateWorking -> FL (PrimOf p) wX wY -> IO ()@@ -735,8 +762,8 @@ -- -- This function is basically unsafe. It overwrites the pending state -- with a new one, not related to the repository state.-prepend :: forall p wR wU wT wX wY. RepoPatch p- => Repository p wR wU wT+prepend :: forall rt p wR wU wT wX wY. RepoPatch p+ => Repository rt p wR wU wT -> UpdateWorking -> FL (PrimOf p) wX wY -> IO ()@@ -750,21 +777,22 @@ newpend NilFL patch_ = seal patch_ newpend p patch_ = seal $ patch_ +>+ p -tentativelyRemovePatches :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT+tentativelyRemovePatches :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> Compression -> UpdateWorking- -> FL (PatchInfoAnd p) wX wT- -> IO (Repository p wR wU wX)+ -> FL (PatchInfoAnd rt p) wX wT+ -> IO (Repository rt p wR wU wX) tentativelyRemovePatches = tentativelyRemovePatches_ UpdatePristine -tentativelyRemovePatches_ :: forall p wR wU wT wX. (RepoPatch p, ApplyState p ~ Tree)+tentativelyRemovePatches_ :: forall rt p wR wU wT wX+ . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => UpdatePristine- -> Repository p wR wU wT+ -> Repository rt p wR wU wT -> Compression -> UpdateWorking- -> FL (PatchInfoAnd p) wX wT- -> IO (Repository p wR wU wX)+ -> FL (PatchInfoAnd rt p) wX wT+ -> IO (Repository rt p wR wU wX) tentativelyRemovePatches_ up repository@(Repo dir rf t c) compr uw ps = withCurrentDirectory dir $ do when (up == UpdatePristine) $ do debugMessage "Adding changes to pending..."@@ -789,19 +817,20 @@ -- with the same name and then adding the passed in sequence. -- Typically callers will have obtained the passed in sequence using -- 'findCommon' and friends.-tentativelyReplacePatches :: forall p wR wU wT wX. (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT+tentativelyReplacePatches :: forall rt p wR wU wT wX+ . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> Compression -> UpdateWorking -> Verbosity- -> FL (PatchInfoAnd p) wX wT+ -> FL (PatchInfoAnd rt p) wX wT -> IO () tentativelyReplacePatches repository compr uw verb ps =- do let ps' = filterOutFLFL (flIsInternal . patchcontents . hopefully) ps+ do let ps' = filterOutFLFL (namedIsInternal . hopefully) ps repository' <- tentativelyRemovePatches_ DontUpdatePristineNorRevert repository compr uw ps' mapAdd repository' ps'- where mapAdd :: Repository p wM wL wI- -> FL (PatchInfoAnd p) wI wJ+ where mapAdd :: Repository rt p wM wL wI+ -> FL (PatchInfoAnd rt p) wI wJ -> IO () mapAdd _ NilFL = return () mapAdd r (a:>:as) =@@ -817,7 +846,7 @@ -- inconsistency of the @NoUpdateWorking@ doing deletion, but -- @YesUpdateWorking@ not bothering. finalizePending :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT+ => Repository rt p wR wU wT -> UpdateWorking -> IO () finalizePending (Repo dir _ _ _) NoUpdateWorking =@@ -828,8 +857,8 @@ Sealed new_pending <- return $ siftForPending tpend makeNewPending repository updateWorking new_pending -finalizeRepositoryChanges :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT+finalizeRepositoryChanges :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> UpdateWorking -> Compression -> IO ()@@ -851,7 +880,7 @@ -- changes, revertRepositoryChanges also re-initialises the tentative state. -- It's therefore used before makign any changes to the repo. revertRepositoryChanges :: RepoPatch p- => Repository p wR wU wT+ => Repository rt p wR wU wT -> UpdateWorking -> IO () revertRepositoryChanges r@(Repo dir rf _ _) uw =@@ -863,12 +892,10 @@ decideHashedOrNormal rf HvsO { hashed = HashedRepo.revertTentativeChanges, old = Old.revertTentativeChanges } -patchSetToPatches :: RepoPatch p => PatchSet p wX wY -> FL (Named p) wX wY-patchSetToPatches patchSet = mapFL_FL hopefully $ newset2FL patchSet--removeFromUnrevertContext :: forall p wR wU wT wX. (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT- -> FL (PatchInfoAnd p) wX wT+removeFromUnrevertContext :: forall rt p wR wU wT wX+ . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> FL (PatchInfoAnd rt p) wX wT -> IO () removeFromUnrevertContext repository ps = do Sealed bundle <- unrevert_patch_bundle `catchall` return (seal (PatchSet NilRL NilRL))@@ -877,12 +904,12 @@ do confirmed <- promptYorn "This operation will make unrevert impossible!\nProceed?" if confirmed then removeFileMayNotExist (unrevertUrl repository) else fail "Cancelled."- unrevert_patch_bundle :: IO (SealedPatchSet p Origin)+ unrevert_patch_bundle :: IO (SealedPatchSet rt p Origin) unrevert_patch_bundle = do pf <- B.readFile (unrevertUrl repository) case scanBundle pf of Right foo -> return foo Left err -> fail $ "Couldn't parse unrevert patch:\n" ++ err- remove_from_unrevert_context_ :: PatchSet p Origin wZ -> IO ()+ remove_from_unrevert_context_ :: PatchSet rt p Origin wZ -> IO () remove_from_unrevert_context_ (PatchSet NilRL NilRL) = return () remove_from_unrevert_context_ bundle = do debugMessage "Adjusting the context of the unrevert changes..."@@ -905,7 +932,7 @@ writeDocBinFile (unrevertUrl repository) bundle' debugMessage "Done adjusting the context of the unrevert changes!" -cleanRepository :: RepoPatch p => Repository p wR wU wT -> IO ()+cleanRepository :: RepoPatch p => Repository rt p wR wU wT -> IO () cleanRepository repository@(Repo _ rf _ _) = decideHashedOrNormal rf HvsO { hashed = cleanHashedRepo repository,@@ -919,7 +946,7 @@ -- | grab the pristine hash of _darcs/hash_inventory, and retrieve whole pristine tree, -- possibly writing a clean working copy in the process.-createPristineDirectoryTree :: RepoPatch p => Repository p wR wU wT -> FilePath -> WithWorkingDir -> IO ()+createPristineDirectoryTree :: RepoPatch p => Repository rt p wR wU wT -> FilePath -> WithWorkingDir -> IO () createPristineDirectoryTree (Repo r rf _ c) reldir wwd | formatHas HashedInventory rf = do createDirectoryIfMissing True reldir@@ -929,7 +956,7 @@ -- fp below really should be FileName -- | Used by the commands dist and diff createPartialsPristineDirectoryTree :: (FilePathLike fp, RepoPatch p)- => Repository p wR wU wT+ => Repository rt p wR wU wT -> [fp] -> FilePath -> IO ()@@ -941,7 +968,7 @@ | otherwise = fail Old.oldRepoFailMsg withRecorded :: RepoPatch p- => Repository p wR wU wT+ => Repository rt p wR wU wT -> ((AbsolutePath -> IO a) -> IO a) -> (AbsolutePath -> IO a) -> IO a@@ -949,8 +976,8 @@ = mk_dir $ \d -> do createPristineDirectoryTree repository (toFilePath d) WithWorkingDir f d -withTentative :: forall p a wR wU wT. (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT+withTentative :: forall rt p a wR wU wT. (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> ((AbsolutePath -> IO a) -> IO a) -> (AbsolutePath -> IO a) -> IO a@@ -1006,8 +1033,8 @@ -- to a given tag are included in that tag, so less commutation and -- history traversal is needed. This latter issue can become very -- important in large repositories.-reorderInventory :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wR+reorderInventory :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wR -> Compression -> UpdateWorking -> Verbosity@@ -1016,16 +1043,16 @@ decideHashedOrNormal rf HvsO { hashed = do debugMessage "Reordering the inventory."- PatchSet ps _ <- misplacedPatches `fmap` readRepo repository+ PatchSet _ ps <- misplacedPatches `fmap` readRepo repository tentativelyReplacePatches repository compr uw verb $ reverseRL ps HashedRepo.finalizeTentativeChanges repository compr debugMessage "Done reordering the inventory.", old = fail Old.oldRepoFailMsg } -- | Returns the patches that make the most recent tag dirty.-misplacedPatches :: forall p wS wX . RepoPatch p- => PatchSet p wS wX- -> PatchSet p wS wX+misplacedPatches :: forall rt p wS wX . RepoPatch p+ => PatchSet rt p wS wX+ -> PatchSet rt p wS wX misplacedPatches ps = -- Filter the repository keeping only with the tags, ordered from the -- most recent.@@ -1036,7 +1063,30 @@ -- the clean PatchSet "up to" the tag (ts), and a RL of -- patches after the tag (r). case splitOnTag lt ps of- Just (PatchSet xs ts :> r) -> PatchSet (r+<+xs) ts+ Just (PatchSet ts xs :> r) -> PatchSet ts (xs+<+r) _ -> impossible -- Because the tag is in ps. +-- @todo: we should not have to open the result of HashedRepo and+-- seal it. Instead, update this function to work with type witnesses+-- by fixing DarcsRepo to match HashedRepo in the handling of+-- Repository state.+readRepo :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> IO (PatchSet rt p Origin wR)+readRepo repo@(Repo r rf _ _)+ | formatHas HashedInventory rf = HashedRepo.readRepo repo r+ | otherwise = do Sealed ps <- Old.readOldRepo r+ return $ unsafeCoerceP ps +-- | XOR of all hashes of the patches' metadata.+-- It enables to quickly see whether two repositories+-- have the same patches, independently of their order.+-- It relies on the assumption that the same patch cannot+-- be present twice in a repository.+-- This checksum is not cryptographically secure,+-- see http://robotics.stanford.edu/~xb/crypto06b/ .+repoXor :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wR -> IO SHA1+repoXor repo = do+ hashes <- mapRL (makePatchname . info) . newset2RL <$> readRepo repo+ return $ foldl' sha1Xor zero hashes
src/Darcs/Repository/InternalTypes.hs view
@@ -21,10 +21,13 @@ , extractCache, modifyCache ) where +import Prelude ()+import Darcs.Prelude+ import Data.List ( nub, sortBy ) import Darcs.Repository.Cache ( Cache (..) , compareByLocality ) import Darcs.Repository.Format ( RepoFormat )-import Darcs.Patch ( RepoPatch )+import Darcs.Patch ( RepoPatch, RepoType ) data Pristine = NoPristine@@ -38,15 +41,15 @@ -- the unrecorded state (what's in the working directory now), -- and the tentative state, which represents work in progress that will -- eventually become the new recorded state unless something goes wrong.-data Repository (p :: * -> * -> *) wRecordedstate wUnrecordedstate wTentativestate =+data Repository (rt :: RepoType) (p :: * -> * -> *) wRecordedstate wUnrecordedstate wTentativestate = Repo !String !RepoFormat !Pristine Cache deriving ( Show ) -extractCache :: Repository p wR wU wT -> Cache+extractCache :: Repository rt p wR wU wT -> Cache extractCache (Repo _ _ _ c) = c -- | 'modifyCache' @repository function@ modifies the cache of -- @repository@ with @function@, remove duplicates and sort the results with 'compareByLocality'.-modifyCache :: forall p wR wU wT . (RepoPatch p) => Repository p wR wU wT -> (Cache -> Cache) -> Repository p wR wU wT+modifyCache :: forall rt p wR wU wT . (RepoPatch p) => Repository rt p wR wU wT -> (Cache -> Cache) -> Repository rt p wR wU wT modifyCache (Repo dir rf pristine cache) f = Repo dir rf pristine $ cmap ( sortBy compareByLocality . nub ) $ f cache where cmap g (Ca c) = Ca (g c)
src/Darcs/Repository/Job.hs view
@@ -27,21 +27,25 @@ , withRepositoryDirectory ) where +import Prelude ()+import Darcs.Prelude import Darcs.Util.Global ( darcsdir ) import Darcs.Patch.Apply ( ApplyState )-import Darcs.Patch.V1 ( Patch )-import Darcs.Patch.V2 ( RealPatch )-import Darcs.Patch.Named ( Named )+import Darcs.Patch.V1 ( RepoPatchV1 )+import Darcs.Patch.V2 ( RepoPatchV2 ) import Darcs.Patch.Prim.V1 ( Prim ) import Darcs.Patch.Prim ( PrimOf )-import Darcs.Patch.Rebase ( Rebasing ) import Darcs.Patch.RepoPatch ( RepoPatch )+import Darcs.Patch.RepoType+ ( RepoType(..), SRepoType(..), IsRepoType+ , RebaseType(..), SRebaseType(..), IsRebaseType+ ) import Darcs.Repository.Flags ( UseCache(..), UpdateWorking(..), DryRun(..), UMask (..)- , Compression, Verbosity )+ ) import Darcs.Repository.Format ( RepoProperty( Darcs2 , RebaseInProgress@@ -55,22 +59,24 @@ ) import Darcs.Repository.InternalTypes ( Repository(..) ) import Darcs.Repository.Rebase- ( repoJobOnRebaseRepo+ ( RebaseJobFlags , startRebaseJob , rebaseJob )-import Darcs.Repository.Lock ( withLock, withLockCanFail )+import qualified Darcs.Repository.Rebase as Rebase ( maybeDisplaySuspendedStatus )+import Darcs.Util.Lock ( withLock, withLockCanFail ) import Darcs.Util.Progress ( debugMessage ) import Control.Monad ( when )-import Control.Exception ( bracket_ )+import Control.Exception ( bracket_, finally )+import Data.List ( intercalate ) import Foreign.C.String ( CString, withCString ) import Foreign.C.Error ( throwErrno ) import Foreign.C.Types ( CInt(..) ) -import Storage.Hashed.Tree ( Tree )+import Darcs.Util.Tree ( Tree ) #include "impossible.h" @@ -108,44 +114,67 @@ = -- |The most common @RepoJob@; the underlying action can accept any patch type that -- a darcs repository may use.- RepoJob (forall p wR wU . (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)- => Repository p wR wU wR -> IO a)+ RepoJob (forall rt p wR wU . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => Repository rt p wR wU wR -> IO a) -- |A job that only works on darcs 1 patches- | V1Job (forall wR wU . Repository (Patch Prim) wR wU wR -> IO a)+ | V1Job (forall wR wU . Repository ('RepoType 'NoRebase) (RepoPatchV1 Prim) wR wU wR -> IO a) -- |A job that only works on darcs 2 patches- | V2Job (forall wR wU . Repository (RealPatch Prim) wR wU wR -> IO a)+ | V2Job (forall rt wR wU . Repository rt (RepoPatchV2 Prim) wR wU wR -> IO a) -- |A job that works on any repository where the patch type @p@ has 'PrimOf' @p@ = 'Prim'. -- -- This was added to support darcsden, which inspects the internals of V1 prim patches. -- -- In future this should be replaced with a more abstract inspection API as part of 'PrimPatch'.- | PrimV1Job (forall p wR wU . (RepoPatch p, ApplyState p ~ Tree, PrimOf p ~ Prim)- => Repository p wR wU wR -> IO a)+ | PrimV1Job (forall rt p wR wU . (RepoPatch p, ApplyState p ~ Tree, PrimOf p ~ Prim)+ => Repository rt p wR wU wR -> IO a) -- A job that works on normal darcs repositories, but will want access to the rebase patch if it exists.- | RebaseAwareJob Compression Verbosity UpdateWorking (forall p wR wU . (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree, PrimOf (Named p) ~ PrimOf p) => Repository p wR wU wR -> IO a)- | RebaseJob Compression Verbosity UpdateWorking (forall p wR wU . (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree, PrimOf (Named p) ~ PrimOf p) => Repository (Rebasing p) wR wU wR -> IO a)- | StartRebaseJob Compression Verbosity UpdateWorking (forall p wR wU . (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree, PrimOf (Named p) ~ PrimOf p) => Repository (Rebasing p) wR wU wR -> IO a)+ | RebaseAwareJob RebaseJobFlags (forall rt p wR wU . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree) => Repository rt p wR wU wR -> IO a)+ | RebaseJob RebaseJobFlags (forall p wR wU . (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree) => Repository ('RepoType 'IsRebase) p wR wU wR -> IO a)+ | StartRebaseJob RebaseJobFlags (forall p wR wU . (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree) => Repository ('RepoType 'IsRebase) p wR wU wR -> IO a) onRepoJob :: RepoJob a- -> (forall p wR wU . (RepoPatch p, ApplyState p ~ Tree) => (Repository p wR wU wR -> IO a) -> Repository p wR wU wR -> IO a)+ -> (forall rt p wR wU . (RepoPatch p, ApplyState p ~ Tree) => (Repository rt p wR wU wR -> IO a) -> Repository rt p wR wU wR -> IO a) -> RepoJob a onRepoJob (RepoJob job) f = RepoJob (f job)--- onRepoJob (TreeJob job) f = TreeJob (f job) onRepoJob (V1Job job) f = V1Job (f job) onRepoJob (V2Job job) f = V2Job (f job) onRepoJob (PrimV1Job job) f = PrimV1Job (f job)-onRepoJob (RebaseAwareJob compr verb uw job) f = RebaseAwareJob compr verb uw (f job)-onRepoJob (RebaseJob compr verb uw job) f = RebaseJob compr verb uw (f job)-onRepoJob (StartRebaseJob compr verb uw job) f = StartRebaseJob compr verb uw (f job)+onRepoJob (RebaseAwareJob flags job) f = RebaseAwareJob flags (f job)+onRepoJob (RebaseJob flags job) f = RebaseJob flags (f job)+onRepoJob (StartRebaseJob flags job) f = StartRebaseJob flags (f job) -- | apply a given RepoJob to a repository in the current working directory withRepository :: UseCache -> RepoJob a -> IO a withRepository useCache = withRepositoryDirectory useCache "." +-- | This is just an internal type to Darcs.Repository.Job for+-- calling runJob in a strongly-typed way+data RepoPatchType p where+ RepoV1 :: RepoPatchType (RepoPatchV1 Prim)+ RepoV2 :: RepoPatchType (RepoPatchV2 Prim)++-- | This type allows us to check multiple patch types against the+-- constraints required by most repository jobs+data IsTree p where+ IsTree :: (ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree) => IsTree p++checkTree :: RepoPatchType p -> IsTree p+checkTree RepoV1 = IsTree+checkTree RepoV2 = IsTree++-- | This type allows us to check multiple patch types against the+-- constraints required by 'PrimV1Job'+data UsesPrimV1 p where+ UsesPrimV1 :: (ApplyState p ~ Tree, PrimOf p ~ Prim) => UsesPrimV1 p++checkPrimV1 :: RepoPatchType p -> UsesPrimV1 p+checkPrimV1 RepoV1 = UsesPrimV1+checkPrimV1 RepoV2 = UsesPrimV1+ -- | apply a given RepoJob to a repository in a given url withRepositoryDirectory :: UseCache -> String -> RepoJob a -> IO a withRepositoryDirectory useCache url repojob = do- Repo dir rf t c <- identifyRepository useCache url+ repo@(Repo _ rf _ _) <- identifyRepository useCache url let startRebase =@@ -153,61 +182,103 @@ StartRebaseJob {} -> True _ -> False - case (formatHas Darcs2 rf, startRebase || formatHas RebaseInProgress rf) of+ -- in order to pass SRepoType and RepoPatchType at different types, we need a polymorphic+ -- function that we call in two different ways, rather than directly varying the argument.+ runJob1+ :: IsRebaseType rebaseType+ => SRebaseType rebaseType -> Repository rtDummy pDummy wR wU wR -> RepoJob a -> IO a+ runJob1 isRebase =+ if formatHas Darcs2 rf+ then runJob RepoV2 (SRepoType isRebase)+ else runJob RepoV1 (SRepoType isRebase) - (True, False) -> do- debugMessage $ "Identified darcs-2 repo: " ++ dir- let therepo = Repo dir rf t c :: Repository (RealPatch Prim) wR wU wR- case repojob of- RepoJob job -> job therepo- PrimV1Job job -> job therepo- -- TreeJob job -> job therepo- V2Job job -> job therepo- V1Job _ -> fail $ "This repository contains darcs v1 patches,"- ++ " but the command requires darcs v2 patches."- RebaseAwareJob _compr _verb _uw job -> job therepo- RebaseJob {} -> fail "No rebase in progress. Try 'darcs rebase suspend' first."- StartRebaseJob {} -> impossible+ runJob2 :: Repository rtDummy pDummy wR wU wR -> RepoJob a -> IO a+ runJob2 =+ if startRebase || formatHas RebaseInProgress rf+ then runJob1 SIsRebase+ else runJob1 SNoRebase - (False, False) -> do- debugMessage $ "Identified darcs-1 repo: " ++ dir- let therepo = Repo dir rf t c :: Repository (Patch Prim) wR wU wR- case repojob of- RepoJob job -> job therepo- PrimV1Job job -> job therepo- V1Job job -> job therepo- V2Job _ -> fail $ "This repository contains darcs v2 patches,"- ++ " but the command requires darcs v1 patches."- RebaseAwareJob _compr _verb _uw job -> job therepo- RebaseJob {} -> fail "No rebase in progress. Try 'darcs rebase suspend' first."- StartRebaseJob {} -> impossible+ runJob2 repo repojob - (True, True ) -> do- debugMessage $ "Identified darcs-2 rebase repo: " ++ dir- let therepo = Repo dir rf t c :: Repository (Rebasing (RealPatch Prim)) wR wU wR- case repojob of- RepoJob job -> repoJobOnRebaseRepo job therepo- PrimV1Job job -> repoJobOnRebaseRepo job therepo- -- TreeJob job -> job therepo- V2Job _ -> fail "This command is not supported while a rebase is in progress."- V1Job _ -> fail $ "This repository contains darcs v1 patches,"- ++ " but the command requires darcs v2 patches."- RebaseAwareJob compr verb uw job -> rebaseJob job therepo compr verb uw- RebaseJob compr verb uw job -> rebaseJob job therepo compr verb uw- StartRebaseJob compr verb uw job -> startRebaseJob job therepo compr verb uw - (False, True ) -> do- debugMessage $ "Identified darcs-1 rebase repo: " ++ dir- let therepo = Repo dir rf t c :: Repository (Rebasing (Patch Prim)) wR wU wR- case repojob of- RepoJob job -> repoJobOnRebaseRepo job therepo- PrimV1Job job -> repoJobOnRebaseRepo job therepo- V1Job _ -> fail "This command is not supported while a rebase is in progress."- V2Job _ -> fail $ "This repository contains darcs v2 patches,"- ++ " but the command requires darcs v1 patches."- RebaseAwareJob compr verb uw job -> rebaseJob job therepo compr verb uw- RebaseJob compr verb uw job -> rebaseJob job therepo compr verb uw- StartRebaseJob compr verb uw job -> startRebaseJob job therepo compr verb uw+runJob+ :: forall rt p rtDummy pDummy wR wU a+ . (IsRepoType rt, RepoPatch p)+ => RepoPatchType p -> SRepoType rt -> Repository rtDummy pDummy wR wU wR -> RepoJob a -> IO a+runJob patchType (SRepoType isRebase) (Repo dir rf t c) repojob = do++ -- The actual type the repository should have is only known when+ -- when this function is called, so we need to "cast" it to its proper type+ let+ therepo = Repo dir rf t c :: Repository rt p wR wU wR++ patchTypeString :: String+ patchTypeString =+ case patchType of+ RepoV2 -> "darcs-2"+ RepoV1 -> "darcs-1"++ repoAttributes :: [String]+ repoAttributes =+ case isRebase of+ SIsRebase -> ["rebase"]+ SNoRebase -> []++ repoAttributesString :: String+ repoAttributesString =+ case repoAttributes of+ [] -> ""+ _ -> " " ++ intercalate "+" repoAttributes++ debugMessage $ "Identified " ++ patchTypeString ++ repoAttributesString ++ " repo: " ++ dir++ case repojob of+ RepoJob job ->+ case checkTree patchType of+ IsTree ->+ job therepo+ `finally`+ Rebase.maybeDisplaySuspendedStatus isRebase therepo++ PrimV1Job job ->+ case checkPrimV1 patchType of+ UsesPrimV1 -> do+ job therepo+ `finally`+ Rebase.maybeDisplaySuspendedStatus isRebase therepo++ V2Job job ->+ case (patchType, isRebase) of+ (RepoV2, SNoRebase) -> job therepo+ (RepoV1, _ ) ->+ fail $ "This repository contains darcs v1 patches,"+ ++ " but the command requires darcs v2 patches."+ (RepoV2, SIsRebase) ->+ fail "This command is not supported while a rebase is in progress."++ V1Job job ->+ case (patchType, isRebase) of+ (RepoV1, SNoRebase) -> job therepo+ (RepoV2, _ ) ->+ fail $ "This repository contains darcs v2 patches,"+ ++ " but the command requires darcs v1 patches."+ (RepoV1, SIsRebase) ->+ fail "This command is not supported while a rebase is in progress."++ RebaseAwareJob flags job ->+ case (checkTree patchType, isRebase) of+ (IsTree, SNoRebase) -> job therepo+ (IsTree, SIsRebase) -> rebaseJob job therepo flags++ RebaseJob flags job ->+ case (checkTree patchType, isRebase) of+ (_ , SNoRebase) -> fail "No rebase in progress. Try 'darcs rebase suspend' first."+ (IsTree, SIsRebase) -> rebaseJob job therepo flags++ StartRebaseJob flags job ->+ case (checkTree patchType, isRebase) of+ (_ , SNoRebase) -> impossible+ (IsTree, SIsRebase) -> startRebaseJob job therepo flags -- | apply a given RepoJob to a repository in the current working directory, -- taking a lock
− src/Darcs/Repository/Lock.hs
@@ -1,382 +0,0 @@--- Copyright (C) 2003 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.--{-# LANGUAGE CPP #-}--module Darcs.Repository.Lock- ( withLock- , withLockCanFail- , environmentHelpLocks- , withTemp- , withOpenTemp- , withStdoutTemp- , withTempDir- , withPermDir- , withDelayedDir- , withNamedTemp- , writeToFile- , appendToFile- , writeBinFile- , writeLocaleFile- , writeDocBinFile- , appendBinFile- , appendDocBinFile- , readBinFile- , readLocaleFile- , readDocBinFile- , writeAtomicFilePS- , gzWriteAtomicFilePS- , gzWriteAtomicFilePSs- , gzWriteDocFile- , rmRecursive- , removeFileMayNotExist- , canonFilename- , maybeRelink- , worldReadableTemp- , tempdirLoc- , environmentHelpTmpdir- , environmentHelpKeepTmpdir- , addToErrorLoc- ) where--import Prelude hiding ( catch )-import Data.List ( inits )-import Data.Maybe ( isJust, listToMaybe )-import System.Exit ( exitWith, ExitCode(..) )-import System.IO ( withBinaryFile, openBinaryTempFile,- hClose, hPutStr, Handle,- IOMode(WriteMode, AppendMode), hFlush, stdout )-import System.IO.Error- ( isAlreadyExistsError- , annotateIOError- )-import Control.Exception- ( IOException- , bracket- , throwIO- , catch- , try- , SomeException- )-import System.Directory ( removeFile, removeDirectory,- doesFileExist, doesDirectoryExist,- getDirectoryContents, createDirectory,- getTemporaryDirectory,- )-import System.FilePath.Posix ( splitDirectories )-import Control.Concurrent ( threadDelay )-import Control.Monad ( unless, when, liftM )--import Darcs.Util.URL ( isRelative )-import Darcs.Util.Environment ( maybeGetEnv )-import Darcs.Util.Exception- ( firstJustIO- , catchall- )-import Darcs.Util.File ( withCurrentDirectory- , doesDirectoryReallyExist, removeFileMayNotExist )-import Darcs.Util.Path ( AbsolutePath, FilePathLike, toFilePath,- getCurrentDirectory, setCurrentDirectory )--import Darcs.Util.ByteString ( gzWriteFilePSs, decodeLocale, encodeLocale )-import qualified Data.ByteString as B (null, readFile, hPut, ByteString)-import qualified Data.ByteString.Char8 as BC (unpack)--import Darcs.Util.SignalHandler ( withSignalsBlocked )-import Darcs.Util.Printer ( Doc, hPutDoc, packedString, empty, renderPSs, RenderMode(..) )-import Darcs.Util.AtExit ( atexit )-import Darcs.Util.Global ( darcsdir )-import Darcs.Util.Workaround ( renameFile )-import Darcs.Repository.Compat- ( mkStdoutTemp- , canonFilename- , maybeRelink- , atomicCreate- , sloppyAtomicCreate- )-import System.Posix.Files ( fileMode, getFileStatus, setFileMode )-#include "impossible.h"--withLock :: String -> IO a -> IO a-withLock s job = bracket (getlock s 30) releaseLock (\_ -> job)--releaseLock :: String -> IO ()-releaseLock = removeFileMayNotExist---- | Tries to perform some task if it can obtain the lock,--- Otherwise, just gives up without doing the task-withLockCanFail :: String -> IO a -> IO (Either () a)-withLockCanFail s job =- bracket (takeLock s)- (\l -> when l $ releaseLock s)- (\l -> if l then liftM Right job- else return $ Left ())--getlock :: String -> Int -> IO String-getlock l 0 = do putStrLn $ "Couldn't get lock "++l- exitWith $ ExitFailure 1-getlock lbad tl = do l <- canonFilename lbad- gotit <- takeLock l- if gotit then return l- else do putStrLn $ "Waiting for lock "++l- hFlush stdout -- for Windows- threadDelay 2000000- getlock l (tl - 1)---takeLock :: FilePathLike p => p -> IO Bool-takeLock fp =- do atomicCreate $ toFilePath fp- return True- `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 sloppyAtomicCreate fp- return True- `catch` \e -> if isAlreadyExistsError e- then return False- else do pwd <- getCurrentDirectory- throwIO $ addToErrorLoc e- ("takeFile "++fp++" in "++toFilePath pwd)--environmentHelpLocks :: ([String],[String])-environmentHelpLocks = (["DARCS_SLOPPY_LOCKS"],[- "If on some filesystems you get an error of the kind:",- "",- " darcs: takeLock [...]: atomic_create [...]: unsupported operation",- "",- "you may want to try to export DARCS_SLOPPY_LOCKS=True."])---- |'withTemp' safely creates an empty file (not open for writing) and--- returns its name.------ The temp file operations are rather similar to the locking operations, in--- that they both should always try to clean up, so exitWith causes trouble.-withTemp :: (String -> IO a) -> IO a-withTemp = bracket get_empty_file removeFileMayNotExist- where get_empty_file = do (f,h) <- openBinaryTempFile "." "darcs"- hClose h- return f---- |'withOpenTemp' creates a temporary file, and opens it.--- Both of them run their argument and then delete the file. Also,--- both of them (to my knowledge) are not susceptible to race conditions on--- the temporary file (as long as you never delete the temporary file; that--- 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) :: 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 (mkStdoutTemp "stdout_") removeFileMayNotExist--tempdirLoc :: IO FilePath-tempdirLoc = liftM fromJust $- firstJustIO [ liftM (Just . head . words) (readBinFile (darcsdir++"/prefs/tmpdir")) >>= chkdir,- maybeGetEnv "DARCS_TMPDIR" >>= chkdir,- getTemporaryDirectory >>= chkdir . Just,- getCurrentDirectorySansDarcs,- return $ Just "." -- always returns a Just- ]- where chkdir Nothing = return Nothing- chkdir (Just d) = liftM (\e -> if e then Just (d++"/") else Nothing) $ doesDirectoryExist d--environmentHelpTmpdir :: ([String], [String])-environmentHelpTmpdir = (["DARCS_TMPDIR", "TMPDIR"], [- "Darcs often creates temporary directories. For example, the `darcs",- "diff` command creates two for the working trees to be diffed. By",- "default temporary directories are created in /tmp, or if that doesn't",- "exist, in _darcs (within the current repo). This can be overridden by",- "specifying some other directory in the file _darcs/prefs/tmpdir or the",- "environment variable $DARCS_TMPDIR or $TMPDIR."])--getCurrentDirectorySansDarcs :: IO (Maybe FilePath)-getCurrentDirectorySansDarcs = do- c <- getCurrentDirectory- return $ listToMaybe $ drop 5 $ reverse $ takeWhile no_darcs $ inits $ toFilePath c- where no_darcs x = darcsdir `notElem` splitDirectories x--data WithDirKind = Perm | Temp | Delayed---- | Creates a directory based on the path parameter;--- if a relative path is given the dir is created in the darcs temp dir.--- If an absolute path is given this dir will be created if it doesn't exist.--- If it is specified as a temporary dir, it is deleted after finishing the job.-withDir :: WithDirKind -- specifies if and when directory will be deleted- -> String -- path parameter- -> (AbsolutePath -> IO a) -> IO a-withDir _ "" _ = bug "withDir called with empty directory name"-withDir kind absoluteOrRelativeName job = do- absoluteName <- if isRelative absoluteOrRelativeName- then fmap (++ absoluteOrRelativeName) tempdirLoc- else return absoluteOrRelativeName- formerdir <- getCurrentDirectory- bracket (createDir absoluteName 0)- (\dir -> do setCurrentDirectory formerdir- k <- keepTempDir- unless k $ case kind of- Perm -> return ()- Temp -> rmRecursive (toFilePath dir)- Delayed -> atexit $ rmRecursive (toFilePath dir))- job- where newname name 0 = name- newname name n = name ++ "-" ++ show n- createDir :: FilePath -> Int -> IO AbsolutePath- createDir name n- = do createDirectory $ newname name n- setCurrentDirectory $ newname name n- getCurrentDirectory- `catch` (\e -> if isAlreadyExistsError e- then createDir name (n+1)- else throwIO e)- keepTempDir = isJust `fmap` maybeGetEnv "DARCS_KEEP_TMPDIR"--environmentHelpKeepTmpdir :: ([String], [String])-environmentHelpKeepTmpdir = (["DARCS_KEEP_TMPDIR"],[- "If the environment variable DARCS_KEEP_TMPDIR is defined, darcs will",- "not remove the temporary directories it creates. This is intended",- "primarily for debugging Darcs itself, but it can also be useful, for",- "example, to determine why your test preference (see `darcs setpref`)",- "is failing when you run `darcs record`, but working when run manually."])---- |'withPermDir' is like 'withTempDir', except that it doesn't--- delete the directory afterwards.-withPermDir :: String -> (AbsolutePath -> IO a) -> IO a-withPermDir = withDir Perm---- |'withTempDir' creates an empty directory and then removes it when it--- is no longer needed. withTempDir creates a temporary directory. The--- location of that directory is determined by the contents of--- _darcs/prefs/tmpdir, if it exists, otherwise by @$DARCS_TMPDIR@, and if--- that doesn't exist then whatever your operating system considers to be a--- a temporary directory (e.g. @$TMPDIR@ under Unix, @$TEMP@ under--- Windows).------ If none of those exist it creates the temporary directory--- in the current directory, unless the current directory is under a _darcs--- directory, in which case the temporary directory in the parent of the highest--- _darcs directory to avoid accidentally corrupting darcs's internals.--- This should not fail, but if it does indeed fail, we go ahead and use the--- current directory anyway. If @$DARCS_KEEP_TMPDIR@ variable is set--- temporary directory is not removed, this can be useful for debugging.-withTempDir :: String -> (AbsolutePath -> IO a) -> IO a-withTempDir = withDir Temp--withDelayedDir :: String -> (AbsolutePath -> IO a) -> IO a-withDelayedDir = withDir Delayed--rmRecursive :: FilePath -> IO ()-rmRecursive d =- do isd <- doesDirectoryReallyExist d- if not isd- then removeFile d- else do conts <- actual_dir_contents- withCurrentDirectory d $- mapM_ rmRecursive conts- removeDirectory d- where actual_dir_contents = -- doesn't include . or ..- do c <- getDirectoryContents d- return $ filter (/=".") $ filter (/="..") c--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- in do ok <- takeFile f_new- if ok then return f_new- else wrt (n+1)--withNamedTemp :: String -> (String -> IO a) -> IO a-withNamedTemp n = bracket get_empty_file removeFileMayNotExist- where get_empty_file = worldReadableTemp n--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--appendBinFile :: FilePathLike p => p -> String -> IO ()-appendBinFile f s = appendToFile f $ \h -> hPutStr h s--appendDocBinFile :: FilePathLike p => p -> Doc -> IO ()-appendDocBinFile f d = appendToFile f $ \h -> hPutDoc Standard h d--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 Standard h d--writeAtomicFilePS :: FilePathLike p => p -> B.ByteString -> IO ()-writeAtomicFilePS f ps = writeToFile f $ \h -> B.hPut h ps--gzWriteAtomicFilePS :: FilePathLike p => p -> B.ByteString -> IO ()-gzWriteAtomicFilePS f ps = gzWriteAtomicFilePSs f [ps]--gzWriteAtomicFilePSs :: FilePathLike p => p -> [B.ByteString] -> IO ()-gzWriteAtomicFilePSs f pss =- withSignalsBlocked $ withNamedTemp (toFilePath f) $ \newf -> do- gzWriteFilePSs newf pss- already_exists <- doesFileExist $ toFilePath f- when already_exists $ do mode <- fileMode `fmap` getFileStatus (toFilePath f)- setFileMode newf mode- `catchall` return ()- renameFile newf (toFilePath f)--gzWriteDocFile :: FilePathLike p => p -> Doc -> IO ()-gzWriteDocFile f d = gzWriteAtomicFilePSs f $ renderPSs Standard d--writeToFile :: FilePathLike p => p -> (Handle -> IO ()) -> IO ()-writeToFile f job =- withSignalsBlocked $ withNamedTemp (toFilePath f) $ \newf -> do- withBinaryFile newf WriteMode job- already_exists <- doesFileExist (toFilePath f)- when already_exists $ do mode <- fileMode `fmap` getFileStatus (toFilePath f)- setFileMode newf mode- `catchall` return ()- renameFile newf (toFilePath f)--appendToFile :: FilePathLike p => p -> (Handle -> IO ()) -> IO ()-appendToFile f job = withSignalsBlocked $- withBinaryFile (toFilePath f) AppendMode job---addToErrorLoc :: IOException- -> String- -> IOException-addToErrorLoc ioe s = annotateIOError ioe s Nothing Nothing
− src/Darcs/Repository/LowLevel.hs
@@ -1,126 +0,0 @@--- Copyright (C) 2002-2004,2007-2008 David Roundy--- Copyright (C) 2005 Juliusz Chroboczek------ 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.--{-# LANGUAGE CPP #-}--module Darcs.Repository.LowLevel- ( readPending- , readTentativePending- , writeTentativePending- -- deprecated interface:- , readNewPending- , writeNewPending- , pendingName- ) where--import Control.Applicative-import qualified Data.ByteString as BS ( empty )--import Darcs.Util.Global ( darcsdir )-import Darcs.Repository.Lock ( writeDocBinFile )-import Darcs.Repository.InternalTypes ( Repository(..) )-import Darcs.Patch ( readPatch, RepoPatch, PrimOf )-import Darcs.Patch.Read ( ReadPatch(..), bracketedFL )-import Darcs.Patch.ReadMonads ( ParserM )-import Darcs.Patch.Show ( ShowPatchBasic(..) )-import Darcs.Util.Exception ( catchall )-import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed), mapSeal )-import Darcs.Patch.Witnesses.Ordered ( FL(..), mapFL )--import Darcs.Util.ByteString ( gzReadFilePS )-import Darcs.Util.Printer ( Doc, ($$), (<>), text, vcat )--pendingName :: String-pendingName = darcsdir ++ "/patches/pending"--newSuffix, tentativeSuffix :: String-newSuffix = ".new"-tentativeSuffix = ".tentative"---- | Read the contents of pending.--- The return type is currently incorrect as it refers to the tentative--- state rather than the recorded state.-readPending :: RepoPatch p => Repository p wR wU wT- -> IO (Sealed (FL (PrimOf p) wT))-readPending = readPendingFile ""---- |Read the contents of tentative pending.-readTentativePending :: RepoPatch p => Repository p wR wU wT- -> IO (Sealed (FL (PrimOf p) wT))-readTentativePending = readPendingFile tentativeSuffix---- |Read the contents of tentative pending.-readNewPending :: RepoPatch p => Repository p wR wU wT- -> IO (Sealed (FL (PrimOf p) wT))-readNewPending = readPendingFile newSuffix---- |Read the pending file with the given suffix. CWD should be the repository--- directory.-readPendingFile :: ReadPatch prim => String -> Repository p wR wU wT- -> IO (Sealed (FL prim wX))-readPendingFile suffix _ = do- pend <- gzReadFilePS (pendingName ++ suffix) `catchall` return BS.empty- return . maybe (Sealed NilFL) (mapSeal unFLM) . readPatch $ pend---- Wrapper around FL where printed format uses { } except around singletons.--- Now that the Show behaviour of FL p can be customised (using--- showFLBehavior), we could instead change the general behaviour of FL Prim;--- but since the pending code can be kept nicely compartmentalised, it's nicer--- to do it this way.-newtype FLM p wX wY = FLM { unFLM :: FL p wX wY }--instance ReadPatch p => ReadPatch (FLM p) where- readPatch' = mapSeal FLM <$> readMaybeBracketedFL readPatch' '{' '}'--instance ShowPatchBasic p => ShowPatchBasic (FLM p) where- showPatch = showMaybeBracketedFL showPatch '{' '}' . unFLM--readMaybeBracketedFL :: forall m p wX . ParserM m- => (forall wY . m (Sealed (p wY))) -> Char -> Char- -> m (Sealed (FL p wX))-readMaybeBracketedFL parser pre post =- bracketedFL parser pre post <|> (mapSeal (:>:NilFL) <$> parser)--showMaybeBracketedFL :: (forall wX wY . p wX wY -> Doc) -> Char -> Char- -> FL p wA wB -> Doc-showMaybeBracketedFL _ pre post NilFL = text [pre] $$ text [post]-showMaybeBracketedFL printer _ _ (p :>: NilFL) = printer p-showMaybeBracketedFL printer pre post ps = text [pre] $$- vcat (mapFL printer ps) $$- text [post]---- |Write the contents of tentative pending.-writeTentativePending :: RepoPatch p => Repository p wR wU wT- -> FL (PrimOf p) wT wY -> IO ()-writeTentativePending = writePendingFile tentativeSuffix---- |Write the contents of new pending. CWD should be the repository directory.-writeNewPending :: RepoPatch p => Repository p wR wU wT- -> FL (PrimOf p) wT wY -> IO ()-writeNewPending = writePendingFile newSuffix---- Write a pending file, with the given suffix. CWD should be the repository--- directory.-writePendingFile :: ShowPatchBasic prim => String -> Repository p wR wU wT- -> FL prim wX wY -> IO ()-writePendingFile suffix _ = writePatch name . FLM- where- name = pendingName ++ suffix--writePatch :: ShowPatchBasic p => FilePath -> p wX wY -> IO ()-writePatch f p = writeDocBinFile f $ showPatch p <> text "\n"
src/Darcs/Repository/Match.hs view
@@ -20,11 +20,13 @@ module Darcs.Repository.Match ( getNonrangeMatch- , getPartialNonrangeMatch , getFirstMatch , getOnePatchset ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Patch.Match ( getNonrangeMatchS , getFirstMatchS@@ -40,7 +42,7 @@ import Darcs.Patch.Bundle ( scanContextFile ) import Darcs.Patch.ApplyMonad ( ApplyMonad(..) ) import Darcs.Patch.Apply( ApplyState )-import Darcs.Patch ( RepoPatch )+import Darcs.Patch ( RepoPatch, IsRepoType ) import Darcs.Patch.Set ( PatchSet(..), SealedPatchSet, Origin ) import Darcs.Patch.Witnesses.Sealed ( seal ) @@ -50,14 +52,14 @@ import Darcs.Repository.Internal ( Repository, readRepo, createPristineDirectoryTree ) -import Storage.Hashed.Tree ( Tree )+import Darcs.Util.Tree ( Tree ) -import Darcs.Util.Path ( FileName, toFilePath )+import Darcs.Util.Path ( toFilePath ) #include "impossible.h" -getNonrangeMatch :: (ApplyMonad DefaultIO (ApplyState p), RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT+getNonrangeMatch :: (ApplyMonad (ApplyState p) DefaultIO, IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> [MatchFlag] -> IO () getNonrangeMatch r = withRecordedMatch r . getMatch where@@ -66,24 +68,16 @@ | otherwise -> fail "Index range is not allowed for this command." _ -> getNonrangeMatchS fs -getPartialNonrangeMatch :: (RepoPatch p, ApplyMonad DefaultIO (ApplyState p), ApplyState p ~ Tree)- => Repository p wR wU wT- -> [MatchFlag]- -> [FileName]- -> IO ()-getPartialNonrangeMatch r fs _ =- withRecordedMatch r (getNonrangeMatchS fs)--getFirstMatch :: (ApplyMonad DefaultIO (ApplyState p), RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT+getFirstMatch :: (ApplyMonad (ApplyState p) DefaultIO, IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> [MatchFlag] -> IO () getFirstMatch r fs = withRecordedMatch r (getFirstMatchS fs) -getOnePatchset :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT+getOnePatchset :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> [MatchFlag]- -> IO (SealedPatchSet p Origin)+ -> IO (SealedPatchSet rt p Origin) getOnePatchset repository fs = case nonrangeMatcher fs of Just m -> do ps <- readRepo repository@@ -95,9 +89,9 @@ context_f (Context f:_) = f context_f (_:xs) = context_f xs -withRecordedMatch :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT- -> (PatchSet p Origin wR -> DefaultIO ())+withRecordedMatch :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> (PatchSet rt p Origin wR -> DefaultIO ()) -> IO () withRecordedMatch r job = do createPristineDirectoryTree r "." WithWorkingDir
src/Darcs/Repository/Merge.hs view
@@ -23,10 +23,13 @@ , considerMergeToWorking ) where +import Prelude ()+import Darcs.Prelude+ import Control.Monad ( when )-import Storage.Hashed.Tree( Tree )+import Darcs.Util.Tree( Tree ) -import Darcs.Repository.External ( backupByCopying )+import Darcs.Util.External ( backupByCopying ) import Darcs.Repository.Flags ( UseIndex , ScanKnown@@ -39,10 +42,11 @@ , WantGuiPause (..) , DiffAlgorithm (..) )-import Darcs.Patch ( RepoPatch, PrimOf, merge, listTouchedFiles, patchcontents,- anonymous, fromPrims, effect )+import Darcs.Patch ( RepoPatch, IsRepoType, PrimOf, merge, listTouchedFiles,+ fromPrims, effect ) import Darcs.Patch.Apply ( ApplyState ) import Darcs.Patch.Depends( merge2FL )+import Darcs.Patch.Named.Wrapped ( activecontents, anonymous ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, n2pia, hopefully ) import Darcs.Patch.Progress( progressFL ) import Darcs.Patch.Witnesses.Ordered@@ -61,27 +65,29 @@ UpdatePristine(..) ) import Darcs.Util.Progress( debugMessage ) -tentativelyMergePatches_ :: forall p wR wU wT wY wX. (RepoPatch p,- ApplyState p ~ Tree) => MakeChanges- -> Repository p wR wU wT -> String+tentativelyMergePatches_ :: forall rt p wR wU wT wY wX+ . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => MakeChanges+ -> Repository rt p wR wU wT -> String -> AllowConflicts -> UpdateWorking -> ExternalMerge -> WantGuiPause -> Compression -> Verbosity -> Reorder -> ( UseIndex, ScanKnown, DiffAlgorithm )- -> FL (PatchInfoAnd p) wX wT- -> FL (PatchInfoAnd p) wX wY+ -> FL (PatchInfoAnd rt p) wX wT+ -> FL (PatchInfoAnd rt p) wX wY -> IO (Sealed (FL (PrimOf p) wU))-tentativelyMergePatches_ mc r cmd allowConflicts updateWorking externalMerge wantGuiPause compression verbosity reorder diffingOpts@(_, _, dflag) usi themi = do+tentativelyMergePatches_ mc r cmd allowConflicts updateWorking externalMerge wantGuiPause+ compression verbosity reorder diffingOpts@(_, _, dflag) usi themi = do let us = mapFL_FL hopefully usi them = mapFL_FL hopefully themi- ((pc :: FL (PatchInfoAnd p) wT wMerged) :/\: us_merged)+ ((pc :: FL (PatchInfoAnd rt p) wT wMerged) :/\: us_merged) <- return $ merge2FL (progressFL "Merging us" usi) (progressFL "Merging them" themi) pend <- unrecordedChanges diffingOpts r Nothing anonpend <- n2pia `fmap` anonymous (fromPrims pend) pend' :/\: pw <- return $ merge (pc :\/: anonpend :>: NilFL) let pwprim = concatFL $ progressFL "Examining patches for conflicts" $- mapFL_FL (patchcontents . hopefully) pw+ mapFL_FL (activecontents . hopefully) pw Sealed standard_resolved_pw <- return $ standardResolution pwprim debugMessage "Checking for conflicts..." when (allowConflicts == YesAllowConflictsAndMark) $@@ -105,7 +111,7 @@ (effect us +>+ pend) (effect them) pwprim debugMessage "Applying patches to the local directories..." when (mc == MakeChanges) $ do- let doChanges :: FL (PatchInfoAnd p) wX wT -> IO (Repository p wR wU wMerged)+ let doChanges :: FL (PatchInfoAnd rt p) wX wT -> IO (Repository rt p wR wU wMerged) -- This first case is a possible optimisation: if 'usi' is empty, then -- the merge2FL call above will return pc = themi, but the wMerged -- witness is quantified in the :/\: constructor so we lose the@@ -129,14 +135,15 @@ tentativelyReplacePatches r' compression YesUpdateWorking verbosity us_merged return $ seal (effect pwprim +>+ pw_resolution) where- mapAdd :: Repository p wM wL wI -> FL (PatchInfoAnd p) wI wJ- -> IO (Repository p wM wL wJ)+ mapAdd :: Repository rt p wM wL wI -> FL (PatchInfoAnd rt p) wI wJ+ -> IO (Repository rt p wM wL wJ) mapAdd repo NilFL = return repo mapAdd repo (a:>:as) = do repo' <- tentativelyAddPatch_ DontUpdatePristine repo compression verbosity updateWorking a mapAdd repo' as- applyps :: Repository p wM wL wI -> FL (PatchInfoAnd p) wI wJ -> IO (Repository p wM wL wJ)+ applyps :: Repository rt p wM wL wI -> FL (PatchInfoAnd rt p) wI wJ+ -> IO (Repository rt p wM wL wJ) applyps repo ps = do debugMessage "Adding patches to inventory..." repo' <- mapAdd repo ps@@ -144,26 +151,26 @@ applyToTentativePristine repo verbosity ps return repo' -tentativelyMergePatches :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> String+tentativelyMergePatches :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => Repository rt p wR wU wT -> String -> AllowConflicts -> UpdateWorking -> ExternalMerge -> WantGuiPause -> Compression -> Verbosity -> Reorder -> ( UseIndex, ScanKnown, DiffAlgorithm )- -> FL (PatchInfoAnd p) wX wT- -> FL (PatchInfoAnd p) wX wY+ -> FL (PatchInfoAnd rt p) wX wT+ -> FL (PatchInfoAnd rt p) wX wY -> IO (Sealed (FL (PrimOf p) wU)) tentativelyMergePatches = tentativelyMergePatches_ MakeChanges -considerMergeToWorking :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> String+considerMergeToWorking :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => Repository rt p wR wU wT -> String -> AllowConflicts -> UpdateWorking -> ExternalMerge -> WantGuiPause -> Compression -> Verbosity -> Reorder -> ( UseIndex, ScanKnown, DiffAlgorithm )- -> FL (PatchInfoAnd p) wX wT- -> FL (PatchInfoAnd p) wX wY+ -> FL (PatchInfoAnd rt p) wX wT+ -> FL (PatchInfoAnd rt p) wX wY -> IO (Sealed (FL (PrimOf p) wU)) considerMergeToWorking = tentativelyMergePatches_ DontMakeChanges
src/Darcs/Repository/Motd.hs view
@@ -20,11 +20,14 @@ , showMotd ) where +import Prelude ()+import Darcs.Prelude+ import Control.Monad ( unless ) import qualified Data.ByteString as B (null, hPut, empty, ByteString) import System.IO ( stdout ) -import Darcs.Repository.External ( fetchFilePS, Cachable(..) )+import Darcs.Util.External ( fetchFilePS, Cachable(..) ) import Darcs.Util.Global ( darcsdir ) import Darcs.Util.Exception ( catchall )
src/Darcs/Repository/Old.hs view
@@ -21,7 +21,8 @@ module Darcs.Repository.Old ( readOldRepo, revertTentativeChanges, oldRepoFailMsg ) where -import Prelude hiding ( catch )+import Prelude ()+import Darcs.Prelude import Darcs.Util.Progress ( debugMessage, beginTedious, endTedious, finishedOneIO ) import Darcs.Util.Path ( ioAbsoluteOrRemote, toPath )@@ -34,19 +35,19 @@ import qualified Data.ByteString.Char8 as BC (break, pack) -import Darcs.Patch ( RepoPatch, Named,+import Darcs.Patch ( RepoPatch, IsRepoType, WrappedNamed, readPatch ) import Darcs.Patch.Witnesses.Ordered ( RL(..) ) import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed), seal, unseal, mapSeal ) import Darcs.Patch.Info ( PatchInfo, makeFilename, readPatchInfos, showPatchInfo ) import Darcs.Patch.Set ( PatchSet(..), Tagged(..), SealedPatchSet, Origin )-import Darcs.Repository.External+import Darcs.Util.External ( gzFetchFilePS , Cachable(..) , cloneFile )-import Darcs.Repository.Lock ( writeBinFile )+import Darcs.Util.Lock ( writeBinFile ) import Darcs.Util.Printer ( renderString, RenderMode(..) ) import Darcs.Util.Global ( darcsdir ) @@ -54,7 +55,7 @@ #include "impossible.h" -readOldRepo :: RepoPatch p => String -> IO (SealedPatchSet p Origin)+readOldRepo :: (IsRepoType rt, RepoPatch p) => String -> IO (SealedPatchSet rt p Origin) readOldRepo d = do realdir <- toPath `fmap` ioAbsoluteOrRemote d let k = "Reading inventory of repository "++d@@ -63,7 +64,8 @@ (\e -> do hPutStrLn stderr ("Invalid repository: " ++ realdir) ioError e) -readRepoPrivate :: RepoPatch p => String -> FilePath -> FilePath -> IO (SealedPatchSet p Origin)+readRepoPrivate :: (IsRepoType rt, RepoPatch p)+ => String -> FilePath -> FilePath -> IO (SealedPatchSet rt p Origin) readRepoPrivate k d iname = do i <- gzFetchFilePS (d </> darcsdir </> iname) Uncachable finishedOneIO k iname@@ -76,10 +78,10 @@ _ -> (Nothing, reverse $ readPatchInfos 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)+ return $ seal (PatchSet ts ps) where read_ts :: RepoPatch p =>- (forall wB . PatchInfo -> IO (Sealed (PatchInfoAnd p wB)))- -> Maybe PatchInfo -> IO (Sealed (RL (Tagged p) Origin))+ (forall wB . PatchInfo -> IO (Sealed (PatchInfoAnd rt p wB)))+ -> Maybe PatchInfo -> IO (Sealed (RL (Tagged rt p) Origin)) read_ts _ Nothing = do endTedious k return $ seal NilRL read_ts parse (Just tag0) =@@ -100,22 +102,23 @@ \(e :: IOException) -> return $ seal $ patchInfoAndPatch tag0 $ unavailable $ show e- return $ seal $ Tagged tag00 Nothing ps :<: ts- parse2 :: RepoPatch p => PatchInfo -> FilePath- -> IO (Sealed (PatchInfoAnd p wX))+ return $ seal $ ts :<: Tagged tag00 Nothing ps+ parse2 :: (IsRepoType rt, RepoPatch p)+ => PatchInfo -> FilePath+ -> IO (Sealed (PatchInfoAnd rt p wX)) parse2 i fn = do ps <- unsafeInterleaveIO $ gzFetchFilePS fn Cachable return $ patchInfoAndPatch i `mapSeal` hopefullyNoParseError (toPath fn) (readPatch ps)- hopefullyNoParseError :: String -> Maybe (Sealed (Named a1dr wX))- -> Sealed (Hopefully (Named a1dr) wX)+ hopefullyNoParseError :: String -> Maybe (Sealed (WrappedNamed rt a1dr wX))+ -> Sealed (Hopefully (WrappedNamed rt a1dr) wX) hopefullyNoParseError _ (Just (Sealed x)) = seal $ actually x hopefullyNoParseError s Nothing = seal $ unavailable $ "Couldn't parse file "++s read_patches :: RepoPatch p =>- (forall wB . PatchInfo -> IO (Sealed (PatchInfoAnd p wB)))- -> [PatchInfo] -> IO (Sealed (RL (PatchInfoAnd p) wX))+ (forall wB . PatchInfo -> IO (Sealed (PatchInfoAnd rt p wB)))+ -> [PatchInfo] -> IO (Sealed (RL (PatchInfoAnd rt p) wX)) read_patches _ [] = return $ seal NilRL read_patches parse (i:is) =- lift2Sealed (:<:)+ lift2Sealed (flip (:<:)) (read_patches parse is) (parse i `catch` \(e :: IOException) -> return $ seal $ patchInfoAndPatch i $ unavailable $ show e)
+ src/Darcs/Repository/Packs.hs view
@@ -0,0 +1,117 @@+module Darcs.Repository.Packs+ ( fetchAndUnpackBasic+ , fetchAndUnpackPatches+ , packsDir+ ) where++import qualified Codec.Archive.Tar as Tar+import Codec.Compression.GZip as GZ ( compress, decompress )++import Control.Concurrent.Async ( withAsync )+import Control.Exception ( Exception, IOException, throwIO, catch )+import Control.Monad ( void )+import System.IO.Error ( isAlreadyExistsError )++import qualified Data.ByteString.Lazy.Char8 as BL+import Data.List ( isPrefixOf )+import Data.Maybe( catMaybes, listToMaybe )++import System.Directory ( createDirectoryIfMissing+ , renameFile+ , doesFileExist+ )+import System.FilePath ( (</>)+ , takeFileName+ , splitPath+ , joinPath+ , takeDirectory+ )+import System.Posix.Files ( createLink )++import Darcs.Util.Lock ( withTemp )+import Darcs.Util.External ( Cachable(..), fetchFileLazyPS )+import Darcs.Repository.Cache ( fetchFileUsingCache+ , HashedDir(..)+ , Cache(..)+ , CacheLoc(..)+ , WritableOrNot(..)+ , hashedDir+ , bucketFolder+ , CacheType(Directory)+ )++import Darcs.Util.Global ( darcsdir )+import Darcs.Util.Progress ( debugMessage )++packsDir :: String+packsDir = "packs"++fetchAndUnpack :: FilePath+ -> HashedDir+ -> Cache+ -> FilePath+ -> IO ()+fetchAndUnpack filename dir cache remote = do+ unpackTar cache dir . Tar.read . GZ.decompress =<<+ fetchFileLazyPS (remote </> darcsdir </> packsDir </> filename) Uncachable++fetchAndUnpackPatches :: [String] -> Cache -> FilePath -> IO ()+fetchAndUnpackPatches paths cache remote =+ -- Patches pack can miss some new patches of the repository.+ -- So we download pack asynchonously and alway do a complete pass+ -- of individual patch files.+ withAsync (fetchAndUnpack "patches.tar.gz" HashedInventoriesDir cache remote) $ \_ -> do+ fetchFilesUsingCache cache HashedPatchesDir paths++fetchAndUnpackBasic :: Cache -> FilePath -> IO ()+fetchAndUnpackBasic = fetchAndUnpack "basic.tar.gz" HashedPristineDir++unpackTar :: Exception e => Cache -> HashedDir -> Tar.Entries e -> IO ()+unpackTar _ _ Tar.Done = return ()+unpackTar _ _ (Tar.Fail e) = throwIO e+unpackTar c dir (Tar.Next e es) = case Tar.entryContent e of+ Tar.NormalFile bs _ -> do+ let p = Tar.entryPath e+ if "meta-" `isPrefixOf` takeFileName p+ then unpackTar c dir es -- just ignore them+ else do+ ex <- doesFileExist p+ if ex+ then debugMessage $ "TAR thread: exists " ++ p ++ "\nStopping TAR thread."+ else do+ if p == darcsdir </> "hashed_inventory"+ then writeFile' Nothing p bs+ else writeFile' (cacheDir c) p $ GZ.compress bs+ debugMessage $ "TAR thread: GET " ++ p+ unpackTar c dir es+ _ -> fail "Unexpected non-file tar entry"+ where+ writeFile' Nothing path content = withTemp $ \tmp -> do+ BL.writeFile tmp content+ renameFile tmp path+ writeFile' (Just ca) path content = do+ let fileFullPath = case splitPath path of+ _:hDir:hFile:_ -> joinPath [ca, hDir, bucketFolder hFile, hFile]+ _ -> fail "Unexpected file path"+ createDirectoryIfMissing True $ takeDirectory path+ createLink fileFullPath path `catch` (\(ex :: IOException) -> do+ if isAlreadyExistsError ex then+ return () -- so much the better+ else+ -- ignore cache if we cannot link+ writeFile' Nothing path content)++-- | Similar to @'mapM_' ('void' 'fetchFileUsingCache')@, exepts+-- it stops execution if file it's going to fetch already exists.+fetchFilesUsingCache :: Cache -> HashedDir -> [FilePath] -> IO ()+fetchFilesUsingCache cache dir = mapM_ go where+ go path = do+ ex <- doesFileExist $ darcsdir </> hashedDir dir </> path+ if ex+ then debugMessage $ "FILE thread: exists " ++ path+ else void $ fetchFileUsingCache cache dir path++cacheDir :: Cache -> Maybe String+cacheDir (Ca cs) = listToMaybe . catMaybes .flip map cs $ \x -> case x of+ Cache Directory Writable x' -> Just x'+ _ -> Nothing
src/Darcs/Repository/PatchIndex.hs view
@@ -27,7 +27,6 @@ createOrUpdatePatchIndexDisk, deletePatchIndex, dumpPatchIndex,- dumpPatchIndexFiles, filterPatches, PatchFilter, maybeFilterPatches,@@ -36,7 +35,7 @@ attemptCreatePatchIndex ) where -import Prelude hiding ( catch, pi )+import Prelude hiding ( pi, (<$>) ) import Data.Binary ( encodeFile, decodeFile ) import Data.Word ( Word32 ) import Data.Int ( Int8 )@@ -53,12 +52,12 @@ import System.Directory ( createDirectory, renameDirectory, doesFileExist, doesDirectoryExist ) import Darcs.Repository.Format ( formatHas, RepoProperty( HashedInventory ) ) import Darcs.Repository.InternalTypes ( Repository(..) )-import Darcs.Repository.Read ( readRepo )+import Darcs.Repository.HashedRepo ( readRepo ) import Darcs.Patch.Witnesses.Ordered ( mapFL, RL(..), FL(..), reverseRL ) import Darcs.Patch.Witnesses.Sealed ( Sealed2(..), Sealed(..), seal, seal2, unsafeUnseal ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd(..), info )-import Darcs.Repository.Lock ( withPermDir, rmRecursive )-import Darcs.Patch ( RepoPatch, listTouchedFiles )+import Darcs.Util.Lock ( withPermDir, rmRecursive )+import Darcs.Patch ( RepoPatch, IsRepoType, listTouchedFiles ) import Darcs.Util.Path ( FileName, fp2fn, fn2fp, toFilePath ) import Darcs.Patch.Apply ( applyToFileMods, ApplyState(..) ) import Darcs.Patch.Set ( newset2FL, Origin, newset2FL )@@ -72,7 +71,7 @@ import qualified Data.ByteString as B import Darcs.Util.Crypt.SHA256 (sha256sum ) import Darcs.Util.Crypt.SHA1 ( SHA1(..), showAsHex )-import Storage.Hashed.Tree ( Tree(..) )+import Darcs.Util.Tree ( Tree(..) ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP ) import Darcs.Util.SignalHandler ( catchInterrupt ) #include "impossible.h"@@ -318,15 +317,17 @@ -- Create/Update patch-index on disk -- | create patch index that corresponds to all patches in repo-createPatchIndexDisk :: (RepoPatch p, ApplyState p ~ Tree) => Repository p wR wU wT -> IO ()-createPatchIndexDisk repository = do- rawpatches <- newset2FL `fmap` readRepo repository+createPatchIndexDisk+ :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> IO ()+createPatchIndexDisk repository@(Repo r _ _ _) = do+ rawpatches <- newset2FL `fmap` readRepo repository r let patches = mapFL Sealed2 rawpatches createPatchIndexFrom repository $ patches2patchMods patches S.empty -- | convert patches to patchmods patches2patchMods :: (Apply p, Commute p, PatchInspect p, ApplyState p ~ Tree)- => [Sealed2 (PatchInfoAnd p)] -> Set FileName -> [(PatchId, [PatchMod FileName])]+ => [Sealed2 (PatchInfoAnd rt p)] -> Set FileName -> [(PatchId, [PatchMod FileName])] patches2patchMods patches fns = snd $ mapAccumL go fns patches where go filenames (Sealed2 p) = (filenames', (pid, pmods_effect ++ pmods_dup))@@ -385,18 +386,20 @@ | otherwise = [] -- | update the patch index to the current state of the repository-updatePatchIndexDisk :: (RepoPatch p, ApplyState p ~ Tree) => Repository p wR wU wT -> IO ()+updatePatchIndexDisk+ :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> IO () updatePatchIndexDisk repo@(Repo repodir _ _ _) = do (_,_,pid2idx,pindex) <- loadPatchIndex repodir -- check that patch index is up to date- patches <- newset2FL `fmap` readRepo repo+ patches <- newset2FL `fmap` readRepo repo repodir let pidsrepo = mapFL (makePatchID . info) patches (oldpids,_,len_common) = uncommon (reverse $ pids pindex) pidsrepo pindex' = removePidSuffix pid2idx oldpids pindex filenames = fpSpans2fileNames (fpspans pindex') cdir = repodir </> indexDir -- reread to prevent holding onto patches for too long- rawpatches <- newset2FL `fmap` readRepo repo+ rawpatches <- newset2FL `fmap` readRepo repo repodir let newpatches = drop len_common $ mapFL seal2 rawpatches newpmods = patches2patchMods newpatches filenames inv_hash <- getInventoryHash repodir@@ -411,7 +414,7 @@ -- | 'createPatchIndexFrom repo pmods' creates a patch index from the given -- patchmods.-createPatchIndexFrom :: RepoPatch p => Repository p wR wU wT+createPatchIndexFrom :: RepoPatch p => Repository rt p wR wU wT -> [(PatchId, [PatchMod FileName])] -> IO () createPatchIndexFrom (Repo repodir _ _ _) pmods = do inv_hash <- getInventoryHash repodir@@ -438,8 +441,8 @@ -- | ensuring that whenever loaded, the patch-index -- | can actually be read by the current version of darcs, -- | and up to date.-loadSafePatchIndex :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT+loadSafePatchIndex :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> IO (Map PatchId Int, PatchIndex) loadSafePatchIndex repo@(Repo repodir _ _ _) = do can_use <- isPatchIndexInSync repo@@ -466,8 +469,8 @@ isPatchIndexDisabled repodir = doesFileExist (repodir </> darcsdir </> noPatchIndex) -- | create or update patch index-createOrUpdatePatchIndexDisk :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> IO ()+createOrUpdatePatchIndexDisk :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> IO () createOrUpdatePatchIndexDisk repo@(Repo repodir _ _ _)= do rmRecursive (repodir </> darcsdir </> noPatchIndex) `catch` \(_ :: IOError) -> return () dpie <- doesPatchIndexExist repodir@@ -478,7 +481,7 @@ -- | Checks whether a patch index can (and should) be created. If we are not in -- an old-fashioned repo, and if we haven't been told not to, then we should -- create a patch index if it doesn't already exist.-canCreatePI :: (RepoPatch p, ApplyState p ~ Tree) => Repository p wR wU wT+canCreatePI :: (RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT -> IO Bool canCreatePI (Repo repodir format _ _) = (not . or) <$> sequence [ doesntHaveHashedInventory format@@ -491,7 +494,7 @@ -- | see if the default is to use patch index or not -- | creates Patch index, if it does not exist, and noPatchIndex is not set canUsePatchIndex :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> IO Bool+ => Repository rt p wR wU wT -> IO Bool canUsePatchIndex (Repo repodir _ _ _) = do piExists <- doesPatchIndexExist repodir piDisabled <- isPatchIndexDisabled repodir@@ -501,8 +504,8 @@ (True, True) -> error "patch index exists, and patch index is disabled. run optimize enable-patch-index or disable-patch-index to rectify." (False, False) -> return False -createPIWithInterrupt :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> IO ()+createPIWithInterrupt :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> IO () createPIWithInterrupt repo@(Repo repodir _ _ _) = do putStrLn "Creating a patch index, please wait. To stop press Ctrl-C" (do@@ -511,7 +514,7 @@ -- | check if patch-index is in sync with repository isPatchIndexInSync :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> IO Bool+ => Repository rt p wR wU wT -> IO Bool isPatchIndexInSync (Repo repodir _ _ _) = do dpie <- doesPatchIndexExist repodir if dpie@@ -666,17 +669,11 @@ putStrLn "==============" putStrLn $ unlines $ fpSpans2filePaths fpspans infom --dumpPatchIndexFiles :: FilePath -> IO ()-dumpPatchIndexFiles repodir = do- (_,_,_,PatchIndex _ _ fpspans infom) <- loadPatchIndex repodir- putStr $ unlines $ fpSpans2filePaths fpspans infom- ----------------------------------------------------------------------- -- Filtering functions based on FilePaths -- returns an RL in which the order of patches matters, for annotate to use-getRelevantSubsequence :: (RepoPatch p, ApplyState p ~ Tree, a ~ PatchInfoAnd p)- => Sealed ((RL a) wK) -> Repository p wR wU wR -> [FileName] -> IO (Sealed ((RL a) Origin))+getRelevantSubsequence :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, a ~ PatchInfoAnd rt p)+ => Sealed ((RL a) wK) -> Repository rt p wR wU wR -> [FileName] -> IO (Sealed ((RL a) Origin)) getRelevantSubsequence pxes repository fns = do (_, pi@(PatchIndex _ _ _ infom)) <- loadSafePatchIndex repository let fids = map (\fn -> evalState (lookupFid fn) pi) fns@@ -685,29 +682,29 @@ let flpxes = reverseRL $ unsafeUnseal pxes return.seal $ keepElems flpxes NilRL pids - where keepElems :: (RepoPatch p, ApplyState p ~ Tree, a ~ PatchInfoAnd p)+ where keepElems :: (RepoPatch p, ApplyState p ~ Tree, a ~ PatchInfoAnd rt p) => FL a wX wY -> RL a wB wX -> S.Set Word32 -> RL a wP wQ keepElems NilFL acc _ = unsafeCoerceP acc keepElems (x:>:xs) acc pids- | (short $ makePatchID $ info x) `S.member` pids = keepElems xs (x:<:acc) pids+ | (short $ makePatchID $ info x) `S.member` pids = keepElems xs (acc:<:x) pids | otherwise = keepElems (unsafeCoerceP xs) acc pids -- | filter given patches so as to keep only the patches that modify the given files-filterPatches :: (RepoPatch p, ApplyState p ~ Tree, a ~ PatchInfoAnd p) => Repository p wR wU wT -> [FilePath] -> [Sealed2 a] -> IO [Sealed2 a]+filterPatches :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, a ~ PatchInfoAnd rt p) => Repository rt p wR wU wT -> [FilePath] -> [Sealed2 a] -> IO [Sealed2 a] filterPatches repository fps ops = do (_, pi@(PatchIndex _ _ _ infom)) <- loadSafePatchIndex repository let fids = concatMap ((\fn -> evalState (lookupFids' fn) pi). fp2fn) fps npids = S.unions $ map (touching.fromJust.(`M.lookup` infom)) fids return $ filter (flip S.member npids . (\(Sealed2 (PIAP pin _)) -> short $ makePatchID pin)) ops -type PatchFilter p = [FilePath] -> [Sealed2 (PatchInfoAnd p)] -> IO [Sealed2 (PatchInfoAnd p)]+type PatchFilter rt p = [FilePath] -> [Sealed2 (PatchInfoAnd rt p)] -> IO [Sealed2 (PatchInfoAnd rt p)] -- | If a patch index is available, filter given patches so as to keep only the patches that -- modify the given files. If none is available, return the original input. maybeFilterPatches- :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT- -> PatchFilter p+ :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> PatchFilter rt p maybeFilterPatches repo fps ops = do usePI <- canUsePatchIndex repo -- in theory we could change the type signature to make this function staged,@@ -718,7 +715,7 @@ ----------------------------------------------------------------------- -- Test patch index -piTest :: (RepoPatch p, ApplyState p ~ Tree) => Repository p wR wU wT -> IO ()+piTest :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT -> IO () piTest repository = do (_, PatchIndex rpids fidspans fpspans infom) <- loadSafePatchIndex repository let pids = reverse rpids@@ -764,7 +761,8 @@ isInOrder _ [] = False -- | Check if patch index can be created and build it with interrupt.-attemptCreatePatchIndex :: (RepoPatch p, ApplyState p ~ Tree) => Repository p wR wU wT -> IO ()+attemptCreatePatchIndex+ :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT -> IO () attemptCreatePatchIndex repo = do canCreate <- canCreatePI repo when canCreate $ createPIWithInterrupt repo
+ src/Darcs/Repository/Pending.hs view
@@ -0,0 +1,129 @@+-- Copyright (C) 2002-2004,2007-2008 David Roundy+-- Copyright (C) 2005 Juliusz Chroboczek+--+-- 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.++{-# LANGUAGE CPP #-}++module Darcs.Repository.Pending+ ( readPending+ , readTentativePending+ , writeTentativePending+ -- deprecated interface:+ , readNewPending+ , writeNewPending+ , pendingName+ ) where++import Prelude ()+import Darcs.Prelude++import Control.Applicative+import qualified Data.ByteString as BS ( empty )++import Darcs.Util.Global ( darcsdir )+import Darcs.Util.Lock ( writeDocBinFile )+import Darcs.Repository.InternalTypes ( Repository(..) )+import Darcs.Patch ( readPatch, RepoPatch, PrimOf )+import Darcs.Patch.Read ( ReadPatch(..), bracketedFL )+import Darcs.Patch.ReadMonads ( ParserM )+import Darcs.Patch.Show ( ShowPatchBasic(..) )+import Darcs.Util.Exception ( catchall )+import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed), mapSeal )+import Darcs.Patch.Witnesses.Ordered ( FL(..), mapFL )++import Darcs.Util.ByteString ( gzReadFilePS )+import Darcs.Util.Printer ( Doc, ($$), (<>), text, vcat )++pendingName :: String+pendingName = darcsdir ++ "/patches/pending"++newSuffix, tentativeSuffix :: String+newSuffix = ".new"+tentativeSuffix = ".tentative"++-- | Read the contents of pending.+-- The return type is currently incorrect as it refers to the tentative+-- state rather than the recorded state.+readPending :: RepoPatch p => Repository rt p wR wU wT+ -> IO (Sealed (FL (PrimOf p) wT))+readPending = readPendingFile ""++-- |Read the contents of tentative pending.+readTentativePending :: RepoPatch p => Repository rt p wR wU wT+ -> IO (Sealed (FL (PrimOf p) wT))+readTentativePending = readPendingFile tentativeSuffix++-- |Read the contents of tentative pending.+readNewPending :: RepoPatch p => Repository rt p wR wU wT+ -> IO (Sealed (FL (PrimOf p) wT))+readNewPending = readPendingFile newSuffix++-- |Read the pending file with the given suffix. CWD should be the repository+-- directory.+readPendingFile :: ReadPatch prim => String -> Repository rt p wR wU wT+ -> IO (Sealed (FL prim wX))+readPendingFile suffix _ = do+ pend <- gzReadFilePS (pendingName ++ suffix) `catchall` return BS.empty+ return . maybe (Sealed NilFL) (mapSeal unFLM) . readPatch $ pend++-- Wrapper around FL where printed format uses { } except around singletons.+-- Now that the Show behaviour of FL p can be customised (using+-- showFLBehavior), we could instead change the general behaviour of FL Prim;+-- but since the pending code can be kept nicely compartmentalised, it's nicer+-- to do it this way.+newtype FLM p wX wY = FLM { unFLM :: FL p wX wY }++instance ReadPatch p => ReadPatch (FLM p) where+ readPatch' = mapSeal FLM <$> readMaybeBracketedFL readPatch' '{' '}'++instance ShowPatchBasic p => ShowPatchBasic (FLM p) where+ showPatch = showMaybeBracketedFL showPatch '{' '}' . unFLM++readMaybeBracketedFL :: forall m p wX . ParserM m+ => (forall wY . m (Sealed (p wY))) -> Char -> Char+ -> m (Sealed (FL p wX))+readMaybeBracketedFL parser pre post =+ bracketedFL parser pre post <|> (mapSeal (:>:NilFL) <$> parser)++showMaybeBracketedFL :: (forall wX wY . p wX wY -> Doc) -> Char -> Char+ -> FL p wA wB -> Doc+showMaybeBracketedFL _ pre post NilFL = text [pre] $$ text [post]+showMaybeBracketedFL printer _ _ (p :>: NilFL) = printer p+showMaybeBracketedFL printer pre post ps = text [pre] $$+ vcat (mapFL printer ps) $$+ text [post]++-- |Write the contents of tentative pending.+writeTentativePending :: RepoPatch p => Repository rt p wR wU wT+ -> FL (PrimOf p) wT wY -> IO ()+writeTentativePending = writePendingFile tentativeSuffix++-- |Write the contents of new pending. CWD should be the repository directory.+writeNewPending :: RepoPatch p => Repository rt p wR wU wT+ -> FL (PrimOf p) wT wY -> IO ()+writeNewPending = writePendingFile newSuffix++-- Write a pending file, with the given suffix. CWD should be the repository+-- directory.+writePendingFile :: ShowPatchBasic prim => String -> Repository rt p wR wU wT+ -> FL prim wX wY -> IO ()+writePendingFile suffix _ = writePatch name . FLM+ where+ name = pendingName ++ suffix++writePatch :: ShowPatchBasic p => FilePath -> p wX wY -> IO ()+writePatch f p = writeDocBinFile f $ showPatch p <> text "\n"
src/Darcs/Repository/Prefs.hs view
@@ -47,12 +47,14 @@ , oldGlobalCacheDir ) where +import Prelude ()+import Darcs.Prelude+ import Control.Exception ( catch ) import Control.Monad ( unless, when, liftM ) import Data.Char ( toUpper )-import Data.List ( nub, isPrefixOf, union, sortBy )+import Data.List ( nub, isPrefixOf, union, sortBy, lookup ) import Data.Maybe ( isJust, fromMaybe, mapMaybe, catMaybes, maybeToList )-import Prelude hiding ( catch ) import qualified Control.Exception as C import qualified Data.ByteString as B ( empty ) import qualified Data.ByteString.Char8 as BC ( unpack )@@ -67,10 +69,10 @@ import Darcs.Repository.Cache ( Cache(..), CacheType(..), CacheLoc(..), WritableOrNot(..), compareByLocality )-import Darcs.Repository.External ( gzFetchFilePS , Cachable( Cachable ))+import Darcs.Util.External ( gzFetchFilePS , Cachable( Cachable )) import Darcs.Repository.Flags( UseCache (..), DryRun (..), SetDefault (..), RemoteRepos (..) )-import Darcs.Repository.Lock( readBinFile, writeBinFile )+import Darcs.Util.Lock( readBinFile, writeBinFile ) import Darcs.Util.Exception ( catchall ) import Darcs.Util.Global ( darcsdir ) import Darcs.Util.Path ( AbsolutePath, ioAbsolute, toFilePath,
− src/Darcs/Repository/Read.hs
@@ -1,25 +0,0 @@-module Darcs.Repository.Read ( readRepo ) where--import Darcs.Patch (RepoPatch)-import Darcs.Patch.Apply ( ApplyState )-import Storage.Hashed.Tree ( Tree )-import Darcs.Repository.InternalTypes ( Repository(Repo) )-import Darcs.Patch.Set ( PatchSet, Origin )-import Darcs.Repository.Format ( formatHas, RepoProperty(HashedInventory) )-import qualified Darcs.Repository.HashedRepo as HashedRepo ( readRepo )-import qualified Darcs.Repository.Old as Old ( readOldRepo )-import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed) )-import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )-- --- @todo: we should not have to open the result of HashedRepo and--- seal it. Instead, update this function to work with type witnesses--- by fixing DarcsRepo to match HashedRepo in the handling of--- Repository state.-readRepo :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT- -> IO (PatchSet p Origin wR)-readRepo repo@(Repo r rf _ _)- | formatHas HashedInventory rf = HashedRepo.readRepo repo r- | otherwise = do Sealed ps <- Old.readOldRepo r- return $ unsafeCoerceP ps
src/Darcs/Repository/Rebase.hs view
@@ -3,34 +3,36 @@ -- BSD3 {-# LANGUAGE CPP #-} module Darcs.Repository.Rebase- (- withManualRebaseUpdate+ ( RebaseJobFlags(..)+ , withManualRebaseUpdate , rebaseJob , startRebaseJob- , repoJobOnRebaseRepo+ , maybeDisplaySuspendedStatus ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Util.Global ( darcsdir ) import Darcs.Patch.Apply ( ApplyState ) import Darcs.Patch.CommuteFn ( commuterIdRL ) import Darcs.Patch.Commute ( selfCommuter )+import Darcs.Patch.Named.Wrapped ( WrappedNamed(..), mkRebase ) import Darcs.Patch.PatchInfoAnd ( n2pia, hopefully )-import Darcs.Patch.Rebase ( RebaseFixup- , Rebasing- , mkSuspended- , takeHeadRebase- , takeAnyRebase- , takeAnyRebaseAndTrailingPatches- , countToEdit- )-import Darcs.Patch.Rebase.Recontext ( RecontextRebase(..)- , RecontextRebase1(..)- , RecontextRebase2(..)- )+import Darcs.Patch.Rebase+ ( takeHeadRebase+ , takeAnyRebase+ , takeAnyRebaseAndTrailingPatches+ )+import Darcs.Patch.Rebase.Container ( Suspended(..), countToEdit, simplifyPushes )+import Darcs.Patch.Rebase.Fixup ( RebaseFixup(..) ) import Darcs.Patch.RepoPatch ( RepoPatch )+import Darcs.Patch.RepoType+ ( RepoType(..), IsRepoType(..), SRepoType(..)+ , RebaseType(..), SRebaseType(..)+ ) import Darcs.Patch.Set ( PatchSet(..) )-import Darcs.Patch.Witnesses.Eq ( EqCheck(..) ) import Darcs.Patch.Witnesses.Ordered ( FL(..), (:>)(..), RL(..), reverseRL )@@ -64,66 +66,64 @@ ) import Darcs.Repository.InternalTypes ( Repository(..) ) +import qualified Darcs.Util.Diff as D ( DiffAlgorithm(MyersDiff) ) import Darcs.Util.Progress ( debugMessage ) -import Storage.Hashed.Tree ( Tree )+import Darcs.Util.Tree ( Tree ) -import Control.Applicative ( (<$>) ) import Control.Exception ( finally ) import System.FilePath.Posix ( (</>) ) #include "impossible.h" +-- | Some common flags that are needed to run rebase jobs.+-- Normally flags are captured directly by the implementation of the specific+-- job's function, but the rebase infrastructure needs to do work on the repository+-- directly that sometimes needs these options, so they have to be passed+-- as part of the job definition.+data RebaseJobFlags =+ RebaseJobFlags+ { rjoCompression :: Compression+ , rjoVerbosity :: Verbosity+ , rjoUpdateWorking :: UpdateWorking+ }+ withManualRebaseUpdate- :: forall p x wR wU wT1 wT2- . (RepoPatch p, ApplyState p ~ Tree)- => Compression- -> Verbosity- -> UpdateWorking- -> Repository p wR wU wT1- -> (Repository p wR wU wT1 -> IO (Repository p wR wU wT2, FL (RebaseFixup p) wT2 wT1, x))- -> IO (Repository p wR wU wT2, x)-withManualRebaseUpdate compr verb uw r subFunc- | Just (RecontextRebase1 recontext1) <- recontextRebase :: Maybe (RecontextRebase1 p)+ :: forall rt p x wR wU wT1 wT2+ . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => RebaseJobFlags+ -> Repository rt p wR wU wT1+ -> (Repository rt p wR wU wT1 -> IO (Repository rt p wR wU wT2, FL (RebaseFixup p) wT2 wT1, x))+ -> IO (Repository rt p wR wU wT2, x)+withManualRebaseUpdate (RebaseJobFlags compr verb uw) r subFunc+ | SRepoType SIsRebase <- singletonRepoType :: SRepoType rt = do patches <- readTentativeRepo r- let go :: PatchSet p wS wT1 -> IO (Repository p wR wU wT2, x)- go (PatchSet NilRL _) = bug "trying to recontext rebase without rebase patch at head (tag)"- go (PatchSet (q :<: _) _) =- case recontext1 (hopefully q) of- (NotEq, _) -> bug "trying to recontext rebase without rebase patch at head (not match)"- (IsEq, recontext2) -> do- r' <- tentativelyRemovePatches r compr uw (q :>: NilFL)- (r'', fixups, x) <- subFunc r'- q' <- n2pia <$> recontextFunc2 recontext2 fixups- r''' <- tentativelyAddPatch r'' compr verb uw q'- return (r''', x)+ let go :: PatchSet rt p wS wT1 -> IO (Repository rt p wR wU wT2, x)+ go (PatchSet _ NilRL) = bug "trying to recontext rebase without rebase patch at head (tag)"+ go (PatchSet _ (_ :<: q)) =+ case hopefully q of+ NormalP {} ->+ bug "trying to recontext rebase without a rebase patch at head (not match)"+ RebaseP _ s -> do+ r' <- tentativelyRemovePatches r compr uw (q :>: NilFL)+ (r'', fixups, x) <- subFunc r'+ q' <- n2pia <$> mkRebase (simplifyPushes D.MyersDiff fixups s)+ r''' <- tentativelyAddPatch r'' compr verb uw q'+ return (r''', x) go patches-withManualRebaseUpdate _compr _verb _uw r subFunc+withManualRebaseUpdate _flags r subFunc = do (r', _, x) <- subFunc r return (r', x) ---- got a normal darcs operation to run on a repo that happens to have a rebase in progress-repoJobOnRebaseRepo :: (RepoPatch p, ApplyState p ~ Tree)- => (Repository (Rebasing p) wR wU wR -> IO a)- -> Repository (Rebasing p) wR wU wR- -> IO a-repoJobOnRebaseRepo job repo = do- res <- job repo -- TODO can we munge the repo here to hide the rebase patch?- displaySuspendedStatus repo- return res- -- got a rebase operation to run where it is required that a rebase is already in progress rebaseJob :: (RepoPatch p, ApplyState p ~ Tree)- => (Repository (Rebasing p) wR wU wR -> IO a)- -> Repository (Rebasing p) wR wU wR- -> Compression- -> Verbosity- -> UpdateWorking+ => (Repository ('RepoType 'IsRebase) p wR wU wR -> IO a)+ -> Repository ('RepoType 'IsRebase) p wR wU wR+ -> RebaseJobFlags -> IO a-rebaseJob job repo compr verb uw = do- repo' <- moveRebaseToEnd repo compr verb uw+rebaseJob job repo flags = do+ repo' <- moveRebaseToEnd repo flags job repo' -- the use of finally here is because various things in job -- might cause an "expected" early exit leaving us needing@@ -135,27 +135,23 @@ -- The better fix would be to standardise expected early exits -- e.g. using a layer on top of IO or a common Exception type -- and then just catch those.- `finally` checkSuspendedStatus repo' compr verb uw+ `finally` checkSuspendedStatus repo' flags -- got a rebase operation to run where we may need to initialise the rebase state first startRebaseJob :: (RepoPatch p, ApplyState p ~ Tree)- => (Repository (Rebasing p) wR wU wR -> IO a)- -> Repository (Rebasing p) wR wU wR- -> Compression- -> Verbosity- -> UpdateWorking+ => (Repository ('RepoType 'IsRebase) p wR wU wR -> IO a)+ -> Repository ('RepoType 'IsRebase) p wR wU wR+ -> RebaseJobFlags -> IO a-startRebaseJob job repo compr verb uw = do- repo' <- startRebaseIfNecessary repo compr verb uw- rebaseJob job repo' compr verb uw+startRebaseJob job repo flags = do+ repo' <- startRebaseIfNecessary repo flags+ rebaseJob job repo' flags checkSuspendedStatus :: (RepoPatch p, ApplyState p ~ Tree)- => Repository (Rebasing p) wR wU wR- -> Compression- -> Verbosity- -> UpdateWorking+ => Repository ('RepoType 'IsRebase) p wR wU wR+ -> RebaseJobFlags -> IO ()-checkSuspendedStatus repo@(Repo _ rf _ _) compr verb uw = do+checkSuspendedStatus repo@(Repo _ rf _ _) flags@(RebaseJobFlags compr _verb uw) = do allpatches <- readRepo repo (_, Sealed2 ps) <- return $ takeAnyRebase allpatches case countToEdit ps of@@ -164,7 +160,7 @@ -- this shouldn't actually be necessary since the count should -- only go to zero after an actual rebase operation which would -- leave the patch at the end anyway, but be defensive.- repo' <- moveRebaseToEnd repo compr verb uw+ repo' <- moveRebaseToEnd repo flags revertRepositoryChanges repo' uw -- in theory moveRebaseToEnd could just return the commuted one, -- but since the repository has been committed and re-opened@@ -177,12 +173,10 @@ n -> putStrLn $ "Rebase in progress: " ++ show n ++ " suspended patches" moveRebaseToEnd :: (RepoPatch p, ApplyState p ~ Tree)- => Repository (Rebasing p) wR wU wR- -> Compression- -> Verbosity- -> UpdateWorking- -> IO (Repository (Rebasing p) wR wU wR)-moveRebaseToEnd repo compr verb uw = do+ => Repository ('RepoType 'IsRebase) p wR wU wR+ -> RebaseJobFlags+ -> IO (Repository ('RepoType 'IsRebase) p wR wU wR)+moveRebaseToEnd repo (RebaseJobFlags compr verb uw) = do allpatches <- readRepo repo case takeAnyRebaseAndTrailingPatches allpatches of FlippedSeal (_ :> NilRL) -> return repo -- already at head@@ -197,26 +191,30 @@ finalizeRepositoryChanges repo'''' uw compr return repo'''' -displaySuspendedStatus :: (RepoPatch p, ApplyState p ~ Tree) => Repository (Rebasing p) wR wU wR -> IO ()+displaySuspendedStatus :: (RepoPatch p, ApplyState p ~ Tree) => Repository ('RepoType 'IsRebase) p wR wU wR -> IO () displaySuspendedStatus repo = do allpatches <- readRepo repo (_, Sealed2 ps) <- return $ takeAnyRebase allpatches putStrLn $ "Rebase in progress: " ++ show (countToEdit ps) ++ " suspended patches" +maybeDisplaySuspendedStatus+ :: (RepoPatch p, ApplyState p ~ Tree)+ => SRebaseType rebaseType -> Repository ('RepoType rebaseType) p wR wU wR -> IO ()+maybeDisplaySuspendedStatus SIsRebase repo = displaySuspendedStatus repo+maybeDisplaySuspendedStatus SNoRebase _ = return ()+ startRebaseIfNecessary :: (RepoPatch p, ApplyState p ~ Tree)- => Repository (Rebasing p) wR wU wT- -> Compression- -> Verbosity- -> UpdateWorking- -> IO (Repository (Rebasing p) wR wU wT)-startRebaseIfNecessary repo@(Repo _ rf _ _) compr verb uw =+ => Repository ('RepoType 'IsRebase) p wR wU wT+ -> RebaseJobFlags+ -> IO (Repository ('RepoType 'IsRebase) p wR wU wT)+startRebaseIfNecessary repo@(Repo _ rf _ _) (RebaseJobFlags compr verb uw) = if formatHas RebaseInProgress rf then return repo else do -- TODO this isn't under the repo lock, and it should be writeRepoFormat (addToFormat RebaseInProgress rf) (darcsdir </> "format") debugMessage "Writing the rebase patch file..." revertRepositoryChanges repo uw- mypatch <- mkSuspended NilFL+ mypatch <- mkRebase (Items NilFL) repo' <- tentativelyAddPatch_ UpdatePristine repo compr verb uw $ n2pia mypatch finalizeRepositoryChanges repo' uw compr return repo'
src/Darcs/Repository/Repair.hs view
@@ -5,11 +5,11 @@ RepositoryConsistency(..) ) where -import Prelude hiding ( catch )+import Prelude ()+import Darcs.Prelude import Control.Monad ( when, unless ) import Control.Monad.Trans ( liftIO )-import Control.Applicative( (<$>) ) import Control.Exception ( catch, finally, IOException ) import Data.Maybe ( catMaybes ) import Data.List ( sort, (\\) )@@ -28,8 +28,7 @@ import Darcs.Patch.Repair ( Repair(applyAndTryToFix) ) import Darcs.Patch.Info ( showPatchInfoUI ) import Darcs.Patch.Set ( Origin, PatchSet(..), newset2FL, newset2RL )-import Darcs.Patch ( RepoPatch, PrimOf, isInconsistent )-import Darcs.Patch.Named ( patchcontents )+import Darcs.Patch ( RepoPatch, IsRepoType, PrimOf, isInconsistent ) import Darcs.Repository.Flags ( Verbosity(..), Compression, DiffAlgorithm )@@ -52,33 +51,33 @@ import Darcs.Util.File ( withCurrentDirectory ) import Darcs.Util.Exception ( catchall ) import Darcs.Util.Global ( darcsdir )-import Darcs.Repository.Lock( rmRecursive, withTempDir )+import Darcs.Util.Lock( rmRecursive, withTempDir ) import Darcs.Util.Printer ( Doc, putDocLn, text, RenderMode(..) ) import Darcs.Util.Printer.Color ( showDoc ) -import Storage.Hashed.Monad( TreeIO )-import Storage.Hashed.Darcs( darcsUpdateHashes, hashedTreeIO )-import Storage.Hashed.Hash( Hash(NoHash), encodeBase16 )-import Storage.Hashed.Tree( Tree, emptyTree, list, restrict, expand, itemHash, zipTrees )-import Storage.Hashed.Index( updateIndex )-import Storage.Hashed( readPlainTree )+import Darcs.Util.Hash( Hash(NoHash), encodeBase16 )+import Darcs.Util.Tree( Tree, emptyTree, list, restrict, expand, itemHash, zipTrees )+import Darcs.Util.Tree.Monad( TreeIO )+import Darcs.Util.Tree.Hashed( darcsUpdateHashes, hashedTreeIO )+import Darcs.Util.Tree.Plain( readPlainTree )+import Darcs.Util.Index( updateIndex ) import qualified Data.ByteString.Char8 as BS #include "impossible.h" -replaceInFL :: FL (PatchInfoAnd a) wX wY- -> [Sealed2 (WPatchInfo :||: PatchInfoAnd a)]- -> FL (PatchInfoAnd a) wX wY+replaceInFL :: FL (PatchInfoAnd rt a) wX wY+ -> [Sealed2 (WPatchInfo :||: PatchInfoAnd rt a)]+ -> FL (PatchInfoAnd rt a) wX wY replaceInFL orig [] = orig replaceInFL NilFL _ = impossible 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 wR wU wT. (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> Compression -> FL (PatchInfoAnd p) Origin wR- -> TreeIO (FL (PatchInfoAnd p) Origin wR, Bool)+applyAndFix :: forall rt p wR wU wT. (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> Compression -> FL (PatchInfoAnd rt p) Origin wR+ -> TreeIO (FL (PatchInfoAnd rt p) Origin wR, Bool) applyAndFix _ _ NilFL = return (NilFL, True) applyAndFix r@(Repo r' _ _ c) compr psin = do liftIO $ beginTedious k@@ -88,11 +87,11 @@ orig <- liftIO $ newset2FL `fmap` readRepo r return (replaceInFL orig repaired, ok) where k = "Replaying patch"- aaf :: FL (PatchInfoAnd p) wW wZ -> TreeIO ([Sealed2 (WPatchInfo :||: PatchInfoAnd p)], Bool)+ aaf :: FL (PatchInfoAnd rt p) wW wZ -> TreeIO ([Sealed2 (WPatchInfo :||: PatchInfoAnd rt p)], Bool) aaf NilFL = return ([], True) aaf (p:>:ps) = do mp' <- applyAndTryToFix p- case isInconsistent . patchcontents . hopefully $ p of+ case isInconsistent . hopefully $ p of Just err -> liftIO $ putDocLn err Nothing -> return () let !winfp = winfo p -- assure that 'p' can be garbage collected.@@ -104,13 +103,13 @@ p' <- withCurrentDirectory r' $ writeAndReadPatch c compr pp return (Sealed2 (winfp :||: p'):ps', False) -data RepositoryConsistency p wX =+data RepositoryConsistency rt p wX = RepositoryConsistent | BrokenPristine (Tree IO)- | BrokenPatches (Tree IO) (PatchSet p Origin wX)+ | BrokenPatches (Tree IO) (PatchSet rt p Origin wX) -checkUniqueness :: (RepoPatch p, ApplyState p ~ Tree)- => (Doc -> IO ()) -> (Doc -> IO ()) -> Repository p wR wU wT -> IO ()+checkUniqueness :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => (Doc -> IO ()) -> (Doc -> IO ()) -> Repository rt p wR wU wT -> IO () checkUniqueness putVerbose putInfo repository = do putVerbose $ text "Checking that patch names are unique..." r <- readRepo repository@@ -127,8 +126,8 @@ hd (x1:x2:xs) | x1 == x2 = Just x1 | otherwise = hd (x2:xs) replayRepository' ::- forall p wR wU wT . (RepoPatch p, ApplyState p ~ Tree)- => DiffAlgorithm -> AbsolutePath -> Repository p wR wU wT -> Compression -> Verbosity -> IO (RepositoryConsistency p wR)+ forall rt p wR wU wT . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => DiffAlgorithm -> AbsolutePath -> Repository rt p wR wU wT -> Compression -> Verbosity -> IO (RepositoryConsistency rt p wR) replayRepository' dflag whereToReplay' repo compr verbosity = do let whereToReplay = toFilePath whereToReplay' putVerbose s = when (verbosity == Verbose) $ putDocLn s@@ -145,7 +144,7 @@ ((ps, patches_ok), newpris) <- hashedTreeIO repair emptyTree whereToReplay debugMessage "Done fixing broken patches..."- let newpatches = PatchSet (reverseFL ps) NilRL+ let newpatches = PatchSet NilRL (reverseFL ps) debugMessage "Checking pristine against slurpy" ftf <- filetypeFunction@@ -160,7 +159,7 @@ then BrokenPristine newpris else BrokenPatches newpris newpatches) -cleanupRepositoryReplay :: Repository p wR wU wT -> IO ()+cleanupRepositoryReplay :: Repository rt p wR wU wT -> IO () cleanupRepositoryReplay r = do let c = extractCache r rf <- identifyRepoFormat "."@@ -170,18 +169,18 @@ current <- readHashedPristineRoot r cleanHashdir c HashedPristineDir $ catMaybes [current] -replayRepositoryInTemp :: (RepoPatch p, ApplyState p ~ Tree)- => DiffAlgorithm -> Repository p wR wU wT -> Compression -> Verbosity- -> IO (RepositoryConsistency p wR)+replayRepositoryInTemp :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => DiffAlgorithm -> Repository rt p wR wU wT -> Compression -> Verbosity+ -> IO (RepositoryConsistency rt p wR) replayRepositoryInTemp dflag r compr verb = do repodir <- getCurrentDirectory withTempDir "darcs-check" $ \tmpDir -> do setCurrentDirectory repodir replayRepository' dflag tmpDir r compr verb -replayRepository :: (RepoPatch p, ApplyState p ~ Tree)- => DiffAlgorithm -> Repository p wR wU wT -> Compression -> Verbosity- -> (RepositoryConsistency p wR -> IO a) -> IO a+replayRepository :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => DiffAlgorithm -> Repository rt p wR wU wT -> Compression -> Verbosity+ -> (RepositoryConsistency rt p wR -> IO a) -> IO a replayRepository dflag r compr verb f = run `finally` cleanupRepositoryReplay r where run = do@@ -190,7 +189,7 @@ st <- replayRepository' dflag hashedPristine r compr verb f st -checkIndex :: (RepoPatch p, ApplyState p ~ Tree) => Repository p wR wU wT -> Bool -> IO Bool+checkIndex :: (RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT -> Bool -> IO Bool checkIndex repo quiet = do index <- updateIndex =<< readIndex repo pristine <- expand =<< readRecordedAndPending repo
src/Darcs/Repository/Resolution.hs view
@@ -24,6 +24,9 @@ , patchsetConflictResolutions ) where +import Prelude ()+import Darcs.Prelude+ import System.FilePath.Posix ( (</>) ) import System.Exit ( ExitCode( ExitSuccess ) ) import System.Directory ( setCurrentDirectory, getCurrentDirectory )@@ -32,10 +35,11 @@ import Darcs.Repository.Diff( treeDiff ) import Darcs.Patch ( PrimOf, PrimPatch, RepoPatch, resolveConflicts,- effectOnFilePaths, patchcontents,+ effectOnFilePaths, invert, listConflictedFiles, commute, applyToTree, fromPrim ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Conflict ( Conflict, CommuteNoConflicts )+import Darcs.Patch.Named.Wrapped ( activecontents ) import Darcs.Patch.Prim ( PrimPatchBase ) import Darcs.Util.Path ( toFilePath, filterFilePaths ) import Darcs.Patch.Witnesses.Ordered@@ -49,12 +53,12 @@ import Darcs.Patch.Set ( PatchSet(..), Origin ) import Darcs.Repository.Prefs ( filetypeFunction ) import Darcs.Util.Exec ( exec, Redirect(..) )-import Darcs.Repository.Lock ( withTempDir )-import Darcs.Repository.External ( cloneTree )+import Darcs.Util.Lock ( withTempDir )+import Darcs.Util.External ( cloneTree ) import Darcs.Repository.Flags ( WantGuiPause(..), DiffAlgorithm(..) ) -import qualified Storage.Hashed.Tree as Tree-import Storage.Hashed ( writePlainTree, readPlainTree )+import qualified Darcs.Util.Tree as Tree+import Darcs.Util.Tree.Plain ( writePlainTree, readPlainTree ) --import Darcs.Util.Printer.Color ( traceDoc ) --import Darcs.Util.Printer ( greenText, ($$), Doc )@@ -62,7 +66,7 @@ standardResolution :: (PrimPatchBase p, Conflict p, CommuteNoConflicts p) => FL p wX wY -> Sealed (FL (PrimOf p) wY)-standardResolution p = mergeList $ map head $ resolveConflicts p+standardResolution = mergeList . map head . resolveConflicts mergeList :: forall prim wX . PrimPatch prim => [Sealed (FL prim wX)] -> Sealed (FL prim wX) mergeList = doml NilFL@@ -148,14 +152,14 @@ exec command args (Null,Null,Null) rr [] = return ExitSuccess -patchsetConflictResolutions :: RepoPatch p => PatchSet p Origin wX -> Sealed (FL (PrimOf p) wX)-patchsetConflictResolutions (PatchSet NilRL _) = Sealed NilFL-patchsetConflictResolutions (PatchSet xs _)+patchsetConflictResolutions :: RepoPatch p => PatchSet rt p Origin wX -> Sealed (FL (PrimOf p) wX)+patchsetConflictResolutions (PatchSet _ NilRL) = Sealed NilFL+patchsetConflictResolutions (PatchSet _ xs) = --traceDoc (greenText "looking at resolutions" $$ -- (sh $ resolveConflicts $ joinPatches $ -- mapFL_FL (patchcontents . hopefully) $ reverseRL xs )) $- mergeList $ map head $ resolveConflicts $ concatFL $- mapFL_FL (patchcontents . hopefully) $ reverseRL xs+ standardResolution $ concatFL $+ mapFL_FL (activecontents . hopefully) $ reverseRL xs --where sh :: [[Sealed (FL Prim)]] -> Doc -- sh [] = greenText "no more conflicts" -- sh (x:ps) = greenText "one conflict" $$ sh1 x $$ sh ps
− src/Darcs/Repository/Ssh.hs
@@ -1,233 +0,0 @@-{-# LANGUAGE CPP #-}---- |--- Module : Darcs.Repository.Ssh--- Maintainer : darcs-devel@darcs.net--- Stability : experimental--- Portability : portable--module Darcs.Repository.Ssh- (- copySSH- , SSHCmd(..)- , getSSH- , environmentHelpSsh- , environmentHelpScp- , environmentHelpSshPort- , remoteDarcs- ) where---import Prelude hiding ( lookup, catch )--import System.Environment ( getEnv )-import System.Exit ( ExitCode(..) )--import Control.Exception ( throwIO )-import Control.Monad ( unless, (>=>) )-import Control.Monad.IO.Class ( liftIO )--import qualified Data.ByteString as B (ByteString, hGet, writeFile )--import Data.Map ( Map, empty, insert, lookup )-import Data.IORef ( IORef, newIORef, readIORef, modifyIORef )--import System.IO ( Handle, hSetBinaryMode, hPutStrLn, hGetLine, hFlush )-import System.IO.Unsafe ( unsafePerformIO )-import System.Process ( runInteractiveProcess )--import Darcs.Util.SignalHandler ( catchNonSignal )-import Darcs.Repository.Flags( RemoteDarcs(..) )-import Darcs.Util.Ssh ( defaultSsh, SshSettings)-import Darcs.Util.URL (SshFilePath(..), urlOf)-import Darcs.Util.Text ( breakCommand )-import Darcs.Util.Exception ( prettyException, catchall )-import Darcs.Util.Exec ( readInteractiveProcess, ExecException(..), Redirect(AsIs) )-import Darcs.Util.Progress ( withoutProgress, debugMessage, debugFail )--import qualified Darcs.Util.Ssh as Settings-import qualified Darcs.Util.Ratified as Ratified ( hGetContents )--sshConnections :: IORef (Map String (Maybe Connection))-sshConnections = unsafePerformIO $ newIORef empty-{-# NOINLINE sshConnections #-}---data Connection = C- { inp :: !Handle- , out :: !Handle- , err :: !Handle- , deb :: String- }----- | @withSSHConnection rdarcs repoid withconnection withoutconnection@--- performs an action on a remote host. If we are already connected to @repoid @,--- then it does @withconnection@, else @withoutconnection@.-withSSHConnection :: String -- ^ rdarcs- -> SshFilePath -- ^ Destination repo id- -> (Connection -> IO a) -- ^ withconnection- -> IO a -- ^ withoutconnection- -> IO a-withSSHConnection rdarcs repoid withconnection withoutconnection =- withoutProgress $- do cs <- liftIO $ readIORef sshConnections- case lookup (sshUhost repoid) (cs :: Map String (Maybe Connection)) of- Just Nothing -> withoutconnection- Just (Just c) -> withconnection c- Nothing ->- do mc <- do (ssh,sshargs_) <- liftIO $ getSSH SSH- let sshargs = sshargs_ ++ [sshUhost repoid, rdarcs,- "transfer-mode","--repodir",sshRepo repoid]- liftIO $ debugMessage $ unwords (ssh : sshargs)- (i,o,e,_) <- liftIO $ runInteractiveProcess ssh sshargs Nothing Nothing- liftIO $ (do- hSetBinaryMode i True- hSetBinaryMode o True- l <- hGetLine o- unless (l == "Hello user, I am darcs transfer mode") $- debugFail "Couldn't start darcs transfer-mode on server"- let c = C { inp = i, out = o, err = e,- deb = "with ssh (transfer-mode) " ++ sshUhost repoid }- modifyIORef sshConnections (insert (sshUhost repoid) (Just c))- return $ Just c) `catchNonSignal`- \exn -> do- debugMessage $ "Failed to start ssh connection:\n "++- prettyException exn- severSSHConnection repoid- debugMessage $ unlines- [ "NOTE: the server may be running a version of darcs prior to 2.0.0."- , ""- , "Installing darcs 2 on the server will speed up ssh-based commands."- ]- return Nothing- maybe withoutconnection withconnection mc--severSSHConnection :: SshFilePath- -> IO ()-severSSHConnection x = do- debugMessage $ "Severing ssh failed connection to " ++ sshUhost x- modifyIORef sshConnections (insert (urlOf x) Nothing)---grabSSH :: SshFilePath -> Connection -> IO B.ByteString-grabSSH dest c = do- debugMessage $ "grabSSH dest=" ++ urlOf dest- let failwith e = do severSSHConnection dest- -- hGetContents is ok here because we're- -- only grabbing stderr, and we're also- -- about to throw the contents.- eee <- Ratified.hGetContents (err c)- debugFail $ e ++ " grabbing ssh file "++- urlOf dest++"/"++ file ++"\n"++eee- file = sshFile dest- debugMessage (deb c ++ " get "++ file)- hPutStrLn (inp c) $ "get " ++ file- hFlush (inp c)- l2 <- hGetLine (out c)- if l2 == "got "++file- then do showlen <- hGetLine (out c)- case reads showlen of- [(len,"")] -> B.hGet (out c) len- _ -> failwith "Couldn't get length"- else if l2 == "error "++file- then do e <- hGetLine (out c)- case reads e of- (msg,_):_ -> debugFail $ "Error reading file remotely:\n"++msg- [] -> failwith "An error occurred"- else failwith "Error"--remoteDarcs :: RemoteDarcs- -> String-remoteDarcs DefaultRemoteDarcs = "darcs"-remoteDarcs (RemoteDarcs x) = x---copySSH :: RemoteDarcs- -> SshFilePath- -> FilePath- -> IO ()-copySSH remote dest to | rdarcs <- remoteDarcs remote = do- debugMessage $ "copySSH file: " ++ urlOf dest- withSSHConnection rdarcs dest (grabSSH dest >=> B.writeFile to) $ do- let u = escape_dollar $ urlOf dest- (scp, args) <- getSSH SCP- let scp_args = filter (/="-q") args ++ [u, to]- (r, scp_err) <- readInteractiveProcess scp scp_args- unless (r == ExitSuccess) $- throwIO $ ExecException scp scp_args (AsIs,AsIs,AsIs) scp_err- where- -- '$' in filenames is troublesome for scp, for some reason.- escape_dollar :: String -> String- escape_dollar = concatMap tr- where- tr '$' = "\\$"- tr c = [c]----- ------------------------------------------------------------------------ older ssh helper functions--- -----------------------------------------------------------------------data SSHCmd = SSH- | SCP- | SFTP---fromSshCmd :: SshSettings- -> SSHCmd- -> String-fromSshCmd s SSH = Settings.ssh s-fromSshCmd s SCP = Settings.scp s-fromSshCmd s SFTP = Settings.sftp s----- | Return the command and arguments needed to run an ssh command--- First try the appropriate darcs environment variable and SSH_PORT--- defaulting to "ssh" and no specified port.-getSSH :: SSHCmd- -> IO (String, [String])-getSSH cmd = do- port <- (portFlag cmd `fmap` getEnv "SSH_PORT") `catchall` return []- let (ssh, ssh_args) = breakCommand command- return (ssh, ssh_args ++ port)- where- command = fromSshCmd defaultSsh cmd- portFlag SSH x = ["-p", x]- portFlag SCP x = ["-P", x]- portFlag SFTP x = ["-oPort=" ++ x]---environmentHelpSsh :: ([String], [String])-environmentHelpSsh = (["DARCS_SSH"], [- "Repositories of the form [user@]host:[dir] are taken to be remote",- "repositories, which Darcs accesses with the external program ssh(1).",- "",- "The environment variable $DARCS_SSH can be used to specify an",- "alternative SSH client. Arguments may be included, separated by",- "whitespace. The value is not interpreted by a shell, so shell",- "constructs cannot be used; in particular, it is not possible for the",- "program name to contain whitespace by using quoting or escaping."])---environmentHelpScp :: ([String], [String])-environmentHelpScp = (["DARCS_SCP", "DARCS_SFTP"], [- "When reading from a remote repository, Darcs will attempt to run",- "`darcs transfer-mode` on the remote host. This will fail if the",- "remote host only has Darcs 1 installed, doesn't have Darcs installed",- "at all, or only allows SFTP.",- "",- "If transfer-mode fails, Darcs will fall back on scp(1) and sftp(1).",- "The commands invoked can be customized with the environment variables",- "$DARCS_SCP and $DARCS_SFTP respectively, which behave like $DARCS_SSH.",- "If the remote end allows only sftp, try setting DARCS_SCP=sftp."])---environmentHelpSshPort :: ([String], [String])-environmentHelpSshPort = (["SSH_PORT"], [- "If this environment variable is set, it will be used as the port",- "number for all SSH calls made by Darcs (when accessing remote",- "repositories over SSH). This is useful if your SSH server does not",- "run on the default port, and your SSH client does not support",- "ssh_config(5). OpenSSH users will probably prefer to put something",- "like `Host *.example.net Port 443` into their ~/.ssh/config file."])
src/Darcs/Repository/State.hs view
@@ -1,7 +1,5 @@-{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-incomplete-patterns #-}-{-# LANGUAGE CPP #-}- -- Copyright (C) 2009 Petr Rockai+-- (C) 2012 José Neder -- -- Permission is hereby granted, free of charge, to any person -- obtaining a copy of this software and associated documentation@@ -25,68 +23,81 @@ module Darcs.Repository.State ( restrictSubpaths, restrictBoring, TreeFilter(..), restrictDarcsdir- -- * Diffs.+ , maybeRestrictSubpaths+ -- * Diffs , unrecordedChanges, unrecordedChangesWithPatches, readPending- -- * Trees.+ -- * Trees , readRecorded, readUnrecorded, readRecordedAndPending, readWorking- , readPendingAndWorking- -- * Index.+ , readPendingAndWorking, readUnrecordedFiltered+ -- * Index , readIndex, updateIndex, invalidateIndex, UseIndex(..), ScanKnown(..) -- * Utilities , filterOutConflicts- -- * Patches- , repoXor ) where+ -- * Detection of changes+ , getMovesPs, getReplaces+ ) where -import Prelude hiding ( filter, catch )-import Control.Monad( when )-import Control.Applicative( (<$>) )+import Prelude ()+import Darcs.Prelude++import Control.Monad( when, foldM ) import Control.Exception ( catch, IOException )-import Data.Maybe( isJust )-import Data.List( union, foldl' )+import Data.Maybe ( isJust, fromJust )+import Data.Ord ( comparing )+import Data.List ( sortBy, union, delete ) import Text.Regex( matchRegex ) import System.Directory( removeFile, doesFileExist, doesDirectoryExist, renameFile ) import System.FilePath ( (</>) )-import qualified Data.ByteString as BS- ( readFile, drop, writeFile, empty )-import qualified Data.ByteString.Char8 as BSC- ( pack, split )+import qualified Data.ByteString as B+ ( readFile, drop, writeFile, empty, concat )+import qualified Data.ByteString.Char8 as BC+ ( pack, unpack, split )+import qualified Data.ByteString.Lazy as BL ( toChunks ) -import Darcs.Patch ( RepoPatch, PrimOf, sortCoalesceFL, fromPrim, fromPrims- , effect, anonymous )+import Darcs.Patch ( effect, RepoPatch, PrimOf, sortCoalesceFL, fromPrim, fromPrims+ , PrimPatch, primIsHunk, maybeApplyToTree+ , tokreplace, forceTokReplace, move )+import Darcs.Patch.Named.Wrapped ( anonymous ) import Darcs.Patch.Apply ( ApplyState, applyToTree, effectOnFilePaths ) import Darcs.Patch.Witnesses.Ordered ( RL(..), FL(..), (+>+), mapFL_FL- , (:>)(..), mapRL )+ , (:>)(..), reverseRL, reverseFL+ , mapFL, concatFL, toFL ) import Darcs.Patch.Witnesses.Eq ( EqCheck(IsEq, NotEq) ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )-import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed), seal, unFreeLeft, mapSeal )+import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed), seal, unFreeLeft, mapSeal+ , freeGap, emptyGap, joinGap, FreeLeft, Gap(..) ) import Darcs.Patch.Commute ( selfCommuter ) import Darcs.Patch.CommuteFn ( commuterIdRL )-import Darcs.Patch.Permutations ( partitionConflictingFL )-import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, n2pia, info )-import Darcs.Patch.Info ( makePatchname )-import Darcs.Patch.Set ( newset2RL )+import Darcs.Patch.Permutations ( partitionConflictingFL, partitionRL )+import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, n2pia )+import Darcs.Patch.Prim.V1 () -- instances Commute Prim and PrimPatch Prim+import Darcs.Patch.Prim.V1.Core ( FilePatchType( Hunk ), Prim(..) )+import Darcs.Patch.Prim.Class ( PrimConstruct, PrimCanonize )+import Darcs.Patch.TokenReplace ( breakToTokens, defaultToks ) import Darcs.Repository.Flags ( UseIndex(..), ScanKnown(..), DiffAlgorithm(..) ) import Darcs.Util.Global ( darcsdir )-import Darcs.Util.Crypt.SHA1 ( SHA1, sha1Xor, zero ) import Darcs.Repository.InternalTypes ( Repository(..) ) import Darcs.Repository.Format(formatHas, RepoProperty(NoWorkingDir))-import qualified Darcs.Repository.LowLevel as LowLevel+import qualified Darcs.Repository.Pending as Pending import Darcs.Repository.Prefs ( filetypeFunction, boringRegexps ) import Darcs.Repository.Diff ( treeDiff )-import Darcs.Repository.Read ( readRepo ) import Darcs.Util.Path( AnchoredPath(..), anchorPath, floatPath, Name(..), fn2fp,- SubPath, sp2fn, filterPaths )-import Storage.Hashed.Tree( Tree, restrict, FilterTree, expand, filter, emptyTree, overlay, find )-import Storage.Hashed.Plain( readPlainTree )-import Storage.Hashed.Darcs( darcsTreeHash, readDarcsHashed, decodeDarcsHash, decodeDarcsSize )-import Storage.Hashed.Hash( Hash( NoHash ) )-import qualified Storage.Hashed.Index as I-import qualified Storage.Hashed.Tree as Tree-+ SubPath, sp2fn, filterPaths+ , parents, replacePrefixPath, anchoredRoot+ , toFilePath, simpleSubPath, normPath, floatSubPath )+import Darcs.Util.Hash( Hash( NoHash ) )+import Darcs.Util.Tree( Tree, restrict, FilterTree, expand, emptyTree, overlay, find+ , ItemType(..), itemType, readBlob, modifyTree, findFile, TreeItem(..)+ , makeBlobBS, expandPath )+import Darcs.Util.Tree.Plain( readPlainTree )+import Darcs.Util.Tree.Hashed( darcsTreeHash, readDarcsHashed, decodeDarcsHash, decodeDarcsSize )+import qualified Darcs.Util.Index as I+import qualified Darcs.Util.Tree as Tree+import Darcs.Util.Index ( listFileIDs, getFileID ) newtype TreeFilter m = TreeFilter { applyTreeFilter :: forall tr . FilterTree tr m => tr m -> tr m } @@ -95,15 +106,15 @@ -- constraint everywhere. When we have GHC 7.2 as a minimum requirement, we can -- lift this constraint into RepoPatch superclass context and remove this hack. readPendingLL :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> IO (Sealed ((FL p) wT))-readPendingLL repo = mapSeal (mapFL_FL fromPrim) `fmap` LowLevel.readPending repo+ => Repository rt p wR wU wT -> IO (Sealed ((FL p) wT))+readPendingLL repo = mapSeal (mapFL_FL fromPrim) `fmap` Pending.readPending repo -- | 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 -- translated correctly relative to pending move patches.-restrictSubpaths :: forall p m wR wU wT. (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> [SubPath]+restrictSubpaths :: forall rt p m wR wU wT. (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> [SubPath] -> IO (TreeFilter m) restrictSubpaths repo subpaths = do Sealed pending <- readPendingLL repo@@ -111,12 +122,18 @@ paths' = paths `union` effectOnFilePaths pending paths anchored = map floatPath paths' restrictPaths :: FilterTree tree m => tree m -> tree m- restrictPaths = filter (filterPaths anchored)+ restrictPaths = Tree.filter (filterPaths anchored) return (TreeFilter restrictPaths) +maybeRestrictSubpaths :: forall rt p m wR wU wT. (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> Maybe [SubPath]+ -> IO (TreeFilter m)+maybeRestrictSubpaths repo = maybe (return $ TreeFilter id) (restrictSubpaths repo)+ -- |Is the given path in (or equal to) the _darcs metadata directory? inDarcsDir :: AnchoredPath -> Bool-inDarcsDir (AnchoredPath (Name x:_)) | x == BSC.pack darcsdir = True+inDarcsDir (AnchoredPath (Name x:_)) | x == BC.pack darcsdir = True inDarcsDir _ = False -- | Construct a Tree filter that removes any boring files the Tree might have@@ -128,8 +145,7 @@ -- -- This function is most useful when you have a plain Tree corresponding to the -- full working copy of the repository, including untracked--- files. Cf. whatsnew, record --look-for-adds. NB. Assumes that our CWD is--- the repository root.+-- files. Cf. whatsnew, record --look-for-adds. restrictBoring :: forall m . Tree m -> IO (TreeFilter m) restrictBoring guide = do boring <- boringRegexps@@ -137,15 +153,15 @@ boring' p = not $ any (\rx -> isJust $ matchRegex rx p') boring where p' = anchorPath "" p restrictTree :: FilterTree t m => t m -> t m- restrictTree = filter $ \p _ -> case find guide p of- Nothing -> boring' p- _ -> True+ restrictTree = Tree.filter $ \p _ -> case find guide p of+ Nothing -> boring' p+ _ -> True return (TreeFilter restrictTree) -- | Construct a Tree filter that removes any darcs metadata files the -- Tree might have contained. restrictDarcsdir :: forall m . TreeFilter m-restrictDarcsdir = TreeFilter $ filter $ \p _ -> not (inDarcsDir p)+restrictDarcsdir = TreeFilter $ Tree.filter $ \p _ -> not (inDarcsDir p) -- | For a repository and an optional list of paths (when Nothing, take -- everything) compute a (forward) list of prims (i.e. a patch) going from the@@ -167,98 +183,82 @@ -- 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 :: forall p wR wU wT . (RepoPatch p, ApplyState p ~ Tree)- => (UseIndex, ScanKnown, DiffAlgorithm) -> Repository p wR wU wT+unrecordedChanges :: forall rt p wR wU wT . (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => (UseIndex, ScanKnown, DiffAlgorithm) -> Repository rt p wR wU wT -> Maybe [SubPath] -> IO (FL (PrimOf p) wT wU)-unrecordedChanges opts r paths = do- (pending :> working) <- readPendingAndWorking opts r paths- return $ sortCoalesceFL (pending +>+ working)+unrecordedChanges = unrecordedChangesWithPatches NilFL NilFL -unrecordedChangesWithPatches :: forall p wR wU wT wX. (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)- => (UseIndex, ScanKnown, DiffAlgorithm) -> Repository p wR wU wT- -> Maybe [SubPath]- -> FL (PrimOf p) wX wT -- look-for-moves patches+unrecordedChangesWithPatches :: forall rt p wR wU wT wX. (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => FL (PrimOf p) wX wT -- look-for-moves patches -> FL (PrimOf p) wT wT -- look-for-replaces patches+ -> (UseIndex, ScanKnown, DiffAlgorithm) -> Repository rt p wR wU wT+ -> Maybe [SubPath] -> IO (FL (PrimOf p) wT wU)-unrecordedChangesWithPatches opts r paths movesPs replacesPs = do- (pending :> working) <- readPendingAndWorkingWithPatches opts r paths movesPs replacesPs- return $ sortCoalesceFL (pending +>+ unsafeCoerceP (movesPs +>+ replacesPs) +>+ working)+unrecordedChangesWithPatches movPs replPs opts r paths = do+ (pending :> working) <- readPendingAndWorkingWithPatches movPs replPs opts r paths+ return $ sortCoalesceFL (pending +>+ unsafeCoerceP (movPs +>+ replPs) +>+ working) -- | Mostly a helper function to 'unrecordedChangesWithPatches', returning the pending -- patch plus `patches` and the subsequent diff from working as two different patches-readPendingAndWorkingWithPatches :: forall p wR wU wT wZ. (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)- => (UseIndex, ScanKnown, DiffAlgorithm)- -> Repository p wR wU wT- -> Maybe [SubPath]- -> FL (PrimOf p) wZ wT -- look-for-moves patches+readPendingAndWorkingWithPatches :: forall rt p wR wU wT wZ. (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => FL (PrimOf p) wZ wT -- look-for-moves patches -> FL (PrimOf p) wT wT -- look-for-replaces patches+ -> (UseIndex, ScanKnown, DiffAlgorithm)+ -> Repository rt p wR wU wT+ -> Maybe [SubPath] -> IO ((FL (PrimOf p) :> FL (PrimOf p)) wT wU)-readPendingAndWorkingWithPatches _ r@(Repo _ rf _ _) _ _ _ | (formatHas NoWorkingDir rf) = do+readPendingAndWorkingWithPatches _ _ _ r@(Repo _ rf _ _) _ | formatHas NoWorkingDir rf = do IsEq <- return $ workDirLessRepoWitness r return (NilFL :> NilFL)-readPendingAndWorkingWithPatches (useidx', scan, dflag) repo mbpaths movesPs replacesPs = do- let allPatches = movesPs +>+ replacesPs+readPendingAndWorkingWithPatches movPs replPs (useidx', scan, dflag) repo mbpaths = do+ let allPatches = movPs +>+ replPs let useidx = case allPatches of NilFL -> useidx' _ -> IgnoreIndex (all_current, Sealed (pending :: FL p wT wX)) <- readPending repo all_current_with_patches <- applyToTree allPatches all_current - relevant <- maybe (return $ TreeFilter id) (restrictSubpaths repo) mbpaths- let getIndex = applyToTree movesPs =<< I.updateIndex =<< (applyTreeFilter relevant <$> readIndex repo)+ relevant <- maybeRestrictSubpaths repo mbpaths+ let getIndex = applyToTree movPs =<< I.updateIndex =<< (applyTreeFilter relevant <$> readIndex repo) current = applyTreeFilter relevant all_current_with_patches - index <- getIndex- working <- applyTreeFilter restrictDarcsdir <$> case scan of- ScanKnown -> case useidx of- UseIndex -> getIndex- IgnoreIndex -> do- guide <- expand current- applyTreeFilter relevant <$> restrict guide <$> readPlainTree "."- ScanAll -> do- nonboring <- restrictBoring index- plain <- applyTreeFilter relevant <$> applyTreeFilter nonboring <$> readPlainTree "."- return $ case useidx of- UseIndex -> plain `overlay` index- IgnoreIndex -> plain- ScanBoring -> do- plain <- applyTreeFilter relevant <$> readPlainTree "."- return $ case useidx of- UseIndex -> plain `overlay` index- IgnoreIndex -> plain-+ working <- filteredWorking useidx scan relevant getIndex current ft <- filetypeFunction Sealed (diff :: FL (PrimOf p) wX wY) <- (unFreeLeft `fmap` treeDiff dflag ft current working) :: IO (Sealed (FL (PrimOf p) wX)) IsEq <- return (unsafeCoerceP IsEq) :: IO (EqCheck wY wU) return (effect pending :> diff) --- | Mostly a helper function to 'unrecordedChanges', returning the pending--- patch and the subsequent diff from working as two different patches-readPendingAndWorking :: forall p wR wU wT . (RepoPatch p, ApplyState p ~ Tree)+readPendingAndWorking :: forall rt p wR wU wT . (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree) => (UseIndex, ScanKnown, DiffAlgorithm)- -> Repository p wR wU wT+ -> Repository rt p wR wU wT -> Maybe [SubPath] -> IO ((FL (PrimOf p) :> FL (PrimOf p)) wT wU)-readPendingAndWorking _ r@(Repo _ rf _ _) _ | (formatHas NoWorkingDir rf) = do- IsEq <- return $ workDirLessRepoWitness r- return (NilFL :> NilFL)-readPendingAndWorking (useidx, scan, dflag) repo mbpaths = do- (all_current, Sealed (pending :: FL p wT wX)) <- readPending repo+readPendingAndWorking = readPendingAndWorkingWithPatches NilFL NilFL - relevant <- maybe (return $ TreeFilter id) (restrictSubpaths repo) mbpaths- let getIndex = I.updateIndex =<< (applyTreeFilter relevant <$> readIndex repo)- current = applyTreeFilter relevant all_current+-- | @filteredWorking useidx scan relevant getIndex pending_tree@ reads the+-- working tree and filters it according to options and @relevant@ file paths.+-- The @pending_tree@ is understood to have @relevant@ already applied and is+-- used (only) if @useidx == 'IgnoreIndex'@ and @scan == 'ScanKnown'@ to act as+-- a guide for filtering the working tree. +-- TODO: untangle the arguments and make this more orthogonal+filteredWorking :: UseIndex+ -> ScanKnown+ -> TreeFilter IO+ -> IO (Tree IO)+ -> Tree IO+ -> IO (Tree IO)+filteredWorking useidx scan relevant getIndex pending_tree = do index <- getIndex- working <- applyTreeFilter restrictDarcsdir <$> case scan of+ applyTreeFilter restrictDarcsdir <$> case scan of ScanKnown -> case useidx of UseIndex -> getIndex IgnoreIndex -> do- guide <- expand current- applyTreeFilter relevant <$> restrict guide <$> readPlainTree "."+ guide <- expand pending_tree+ applyTreeFilter relevant . restrict guide <$> readPlainTree "." ScanAll -> do nonboring <- restrictBoring index- plain <- applyTreeFilter relevant <$> applyTreeFilter nonboring <$> readPlainTree "."+ plain <- applyTreeFilter relevant . applyTreeFilter nonboring <$> readPlainTree "." return $ case useidx of UseIndex -> plain `overlay` index IgnoreIndex -> plain@@ -268,14 +268,9 @@ UseIndex -> plain `overlay` index IgnoreIndex -> plain - ft <- filetypeFunction- Sealed (diff :: FL (PrimOf p) wX wY) <- (unFreeLeft `fmap` treeDiff dflag ft current working) :: IO (Sealed (FL (PrimOf p) wX))- IsEq <- return (unsafeCoerceP IsEq) :: IO (EqCheck wY wU)- return (effect pending :> diff)- -- | Witnesses the fact that in the absence of a working directory, we -- pretend that the working dir updates magically to the tentative state.-workDirLessRepoWitness :: Repository p wR wU wT -> EqCheck wU wT+workDirLessRepoWitness :: Repository rt p wR wU wT -> EqCheck wU wT workDirLessRepoWitness (Repo _ rf _ _) | formatHas NoWorkingDir rf = unsafeCoerceP IsEq | otherwise = NotEq@@ -283,23 +278,18 @@ -- | Obtains a Tree corresponding to the "recorded" state of the repository: -- this is the same as the pristine cache, which is the same as the result of -- applying all the repository's patches to an empty directory.------ Handles the plain and hashed pristine cases. Currently does not handle the--- no-pristine case, as that requires replaying patches. Cf. 'readDarcsHashed'--- and 'readPlainTree' in hashed-storage that are used to do the actual 'Tree'--- construction.-readRecorded :: (RepoPatch p, ApplyState p ~ Tree) => Repository p wR wU wT -> IO (Tree IO)+readRecorded :: (RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT -> IO (Tree IO) readRecorded _repo = do let h_inventory = darcsdir </> "hashed_inventory" hashed <- doesFileExist h_inventory if hashed- then do inv <- BS.readFile h_inventory- let linesInv = BSC.split '\n' inv+ then do inv <- B.readFile h_inventory+ let linesInv = BC.split '\n' inv case linesInv of [] -> return emptyTree (pris_line:_) -> do- let hash = decodeDarcsHash $ BS.drop 9 pris_line- size = decodeDarcsSize $ BS.drop 9 pris_line+ let hash = decodeDarcsHash $ B.drop 9 pris_line+ size = decodeDarcsSize $ B.drop 9 pris_line when (hash == NoHash) $ fail $ "Bad pristine root: " ++ show pris_line readDarcsHashed (darcsdir </> "pristine.hashed") (size, hash) else do have_pristine <- doesDirectoryExist $ darcsdir </> "pristine"@@ -310,32 +300,56 @@ (_, _) -> fail "No pristine tree is available!" -- | Obtains a Tree corresponding to the "unrecorded" state of the repository:--- the working tree plus the "pending" patch. The optional list of paths allows--- to restrict the query to a subtree.+-- the modified files of the working tree plus the "pending" patch.+-- The optional list of paths 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, ApplyState p ~ Tree)- => Repository p wR wU wT -> Maybe [SubPath] -> IO (Tree IO)+ => Repository rt p wR wU wT -> Maybe [SubPath] -> IO (Tree IO) readUnrecorded repo mbpaths = do- relevant <- maybe (return $ TreeFilter id) (restrictSubpaths repo) mbpaths+ relevant <- maybeRestrictSubpaths repo mbpaths 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--- choice. This function is only useful in not-completely-constructed--- repositories.+-- | A variant of 'readUnrecorded' that takes the UseIndex and ScanKnown+-- options into account, similar to 'readPendingAndWorking'. We are only+-- interested in the resulting tree, not the patch, so the 'DiffAlgorithm' option+-- is irrelevant.+readUnrecordedFiltered :: (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> UseIndex+ -> ScanKnown+ -> Maybe [SubPath] -> IO (Tree IO)+readUnrecordedFiltered repo useidx scan mbpaths = do+ (all_current, _) <- readPending repo -- we have no need for the pending patch+ relevant <- maybeRestrictSubpaths repo mbpaths+ let getIndex = I.updateIndex =<< (applyTreeFilter relevant <$> readIndex repo)+ current = applyTreeFilter relevant all_current+ filteredWorking useidx scan relevant getIndex current++-- | Obtains a Tree corresponding to the complete working copy of the+-- repository (modified and non-modified files). readWorking :: IO (Tree IO) readWorking = expand =<< (nodarcs `fmap` readPlainTree ".")- where nodarcs = Tree.filter (\(AnchoredPath (Name x:_)) _ -> x /= BSC.pack darcsdir)+ where nodarcs = Tree.filter (\(AnchoredPath (Name x:_)) _ -> x /= BC.pack darcsdir) +-- | Obtains the same Tree as 'readRecorded' would but with the additional side+-- effect of reading/checking the pending patch. readRecordedAndPending :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> IO (Tree IO)+ => Repository rt p wR wU wT -> IO (Tree IO) readRecordedAndPending repo = fst `fmap` readPending repo +-- | Obtains a Tree corresponding to the recorded state of the repository+-- and a pending patch to go with it. The pending patch should start at the+-- recorded state (we even verify that it applies, and degrade to+-- renaming pending and starting afresh if it doesn't), but we've set to+-- say it starts at the tentative state.+--+-- Question (Eric Kow) Is this a bug? Darcs.Repository.Pending.readPending+-- says it is readPending :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> IO (Tree IO, Sealed (FL p wT))+ => Repository rt p wR wU wT -> IO (Tree IO, Sealed (FL p wT)) readPending repo = do Sealed pending <- readPendingLL repo pristine <- readRecorded repo@@ -352,21 +366,16 @@ -- pristine: changes to file content in either pristine or working are handled -- transparently by the index reading code.) invalidateIndex :: t -> IO ()-invalidateIndex _ = BS.writeFile (darcsdir </> "index_invalid") BS.empty+invalidateIndex _ = B.writeFile (darcsdir </> "index_invalid") B.empty -readIndex :: (RepoPatch p, ApplyState p ~ Tree) => Repository p wR wU wT -> IO I.Index+readIndex :: (RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT -> IO I.Index readIndex repo = do invalid <- doesFileExist $ darcsdir </> "index_invalid" exists <- doesFileExist $ darcsdir </> "index" formatValid <- if exists then I.indexFormatValid $ darcsdir </> "index" else return True- when (exists && not formatValid) $-#if mingw32_HOST_OS- renameFile (darcsdir </> "index") (darcsdir </> "index.old")-#else- removeFile $ darcsdir </> "index"-#endif+ when (exists && not formatValid) $ removeFile $ darcsdir </> "index" if not exists || invalid || not formatValid then do pris <- readRecordedAndPending repo idx <- I.updateIndexFrom (darcsdir </> "index") darcsTreeHash pris@@ -374,50 +383,178 @@ return idx else I.readIndex (darcsdir </> "index") darcsTreeHash -updateIndex :: (RepoPatch p, ApplyState p ~ Tree) => Repository p wR wU wT -> IO ()+updateIndex :: (RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT -> IO () updateIndex repo = do invalid <- doesFileExist $ darcsdir </> "index_invalid" exists <- doesFileExist $ darcsdir </> "index" formatValid <- if exists then I.indexFormatValid $ darcsdir </> "index" else return True- when (exists && not formatValid) $-#if mingw32_HOST_OS- renameFile (darcsdir </> "index") (darcsdir </> "index.old")-#else- removeFile $ darcsdir </> "index"-#endif+ when (exists && not formatValid) $ removeFile $ darcsdir </> "index" pris <- readRecordedAndPending repo _ <- I.updateIndexFrom (darcsdir </> "index") darcsTreeHash pris when invalid $ removeFile $ darcsdir </> "index_invalid" -- |Remove any patches (+dependencies) from a sequence that -- conflict with the recorded or unrecorded changes in a repo-filterOutConflicts :: (RepoPatch p, ApplyState p ~ Tree)- => RL (PatchInfoAnd p) wX wT -- ^Recorded patches from repository, starting from+filterOutConflicts :: (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => RL (PatchInfoAnd rt p) wX wT -- ^Recorded patches from repository, starting from -- same context as the patches to filter- -> Repository p wR wU wT -- ^Repository itself, used for grabbing unrecorded changes- -> FL (PatchInfoAnd p) wX wZ -- ^Patches to filter- -> IO (Bool, Sealed (FL (PatchInfoAnd p) wX)) -- ^(True iff any patches were removed, possibly filtered patches)+ -> Repository rt p wR wU wT -- ^Repository itself, used for grabbing unrecorded changes+ -> FL (PatchInfoAnd rt p) wX wZ -- ^Patches to filter+ -> IO (Bool, Sealed (FL (PatchInfoAnd rt p) wX)) -- ^(True iff any patches were removed, possibly filtered patches) filterOutConflicts us repository them = do let commuter = commuterIdRL selfCommuter unrec <- fmap n2pia . anonymous . fromPrims =<< unrecordedChanges (UseIndex, ScanKnown, MyersDiff) repository Nothing- them' :> rest <- return $ partitionConflictingFL commuter them (unrec :<: us)+ them' :> rest <- return $ partitionConflictingFL commuter them (us :<: unrec) return (check rest, Sealed them') where check :: FL p wA wB -> Bool check NilFL = False check _ = True --- | XOR of all hashes of the patches' metadata.--- It enables to quickly see whether two repositories--- have the same patches, independently of their order.--- It relies on the assumption that the same patch cannot--- be present twice in a repository.--- This checksum is not cryptographically secure,--- see http://robotics.stanford.edu/~xb/crypto06b/ .-repoXor :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wR -> IO SHA1-repoXor repo = do- hashes <- mapRL (makePatchname . info) . newset2RL <$> readRepo repo- return $ foldl' sha1Xor zero hashes+-- |Automatically detect file moves using the index+getMovesPs :: forall rt p wR wU wB prim.+ (PrimConstruct prim, PrimCanonize prim, RepoPatch p,+ ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => Repository rt p wR wU wR+ -> Maybe [SubPath]+ -> IO (FL prim wB wB)+getMovesPs repository files = mkMovesFL <$> getMovedFiles repository files+ where+ mkMovesFL [] = NilFL+ mkMovesFL ((a,b,_):xs) = move (anchorPath "" a) (anchorPath "" b) :>: mkMovesFL xs++ getMovedFiles :: (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => Repository rt p wR wU wR+ -> Maybe [SubPath]+ -> IO [(AnchoredPath, AnchoredPath, ItemType)]+ getMovedFiles repo fs = do+ old <- sortBy (comparing snd) <$> (listFileIDs =<< readIndex repo)+ nonboring <- restrictBoring emptyTree+ let addIDs = foldM (\xs (p, it)-> do mfid <- getFileID p+ return $ case mfid of+ Nothing -> xs+ Just fid -> ((p, it), fid):xs) []+ new <- sortBy (comparing snd) <$>+ (addIDs . map (\(a,b) -> (a, itemType b)) . Tree.list =<<+ expand =<< applyTreeFilter nonboring <$> readPlainTree ".")+ let match (x:xs) (y:ys) | snd x > snd y = match (x:xs) ys+ | snd x < snd y = match xs (y:ys)+ | snd (fst x) /= snd (fst y) = match xs ys+ | otherwise = (fst (fst x), fst (fst y), snd (fst x)):match xs ys+ match _ _ = []+ movedfiles = match old new+ fmovedfiles = case fs of+ Nothing -> movedfiles+ Just subpath -> filter (\(f1,f2,_) -> any (`elem` selfiles) [f1,f2]) movedfiles+ where selfiles = map (floatPath . toFilePath) subpath+ return (resolve fmovedfiles)++ resolve :: [(AnchoredPath, AnchoredPath, ItemType)] -> [(AnchoredPath, AnchoredPath, ItemType)]+ resolve xs = fixPaths $ sortMoves $ deleteCycles xs+ where+ -- Input relation is left-and-right-unique. Makes cycle detection easier.+ deleteCycles [] = []+ deleteCycles whole@( x@(start,_,_):rest)+ = if hasCycle start whole start+ then deleteCycles (deleteFrom start whole [])+ else x:deleteCycles rest+ where hasCycle current ((a',b',_):rest') first+ | a' == current = b' == first || hasCycle b' whole first+ | otherwise = hasCycle current rest' first + hasCycle _ [] _ = False+ deleteFrom a (y@(a',b',_):ys) seen+ | a == a' = deleteFrom b' (seen++ys) []+ | otherwise = deleteFrom a ys (y:seen)+ deleteFrom _ [] seen = seen++ sortMoves [] = []+ sortMoves whole@(current@(_,dest,_):_) =+ smallest:sortMoves (delete smallest whole)+ where+ smallest = follow dest whole current+ follow prevDest (y@(s,d,_):ys) currentSmallest+ -- destination is source of another move+ | prevDest == s = follow d whole y+ -- parent of destination is also destination of a move+ | d `elem` parents prevDest = follow d whole y+ | otherwise = follow prevDest ys currentSmallest+ follow _ [] currentSmallest = currentSmallest++ -- rewrite [d/ -> e/, .., d/f -> e/h] to [d/ -> e/, .., e/f -> e/h]+ fixPaths [] = []+ fixPaths (y@(f1,f2,t):ys)+ | f1 == f2 = fixPaths ys+ | TreeType <- t = y:fixPaths (map replacepp ys)+ | otherwise = y:fixPaths ys+ where replacepp i@(if1,if2,it) | nfst == anchoredRoot = i+ | otherwise = (nfst, if2, it)+ where nfst = replacePrefixPath f1 f2 if1++-- | Search for possible replaces between the recordedAndPending state+-- and the unrecorded (or working) state. Return a Sealed FL list of+-- replace patches to be applied to the recordedAndPending state.+getReplaces :: forall rt p wR wU wT wX. (RepoPatch p, ApplyState p ~ Tree,+ ApplyState (PrimOf p) ~ Tree, wX ~ wR)+ => (UseIndex, ScanKnown, DiffAlgorithm)+ -> Repository rt p wR wU wT+ -> Maybe [SubPath]+ -> IO (Sealed (FL (PrimOf p) wX))+getReplaces (useindex, scan, dopts) repo files = do+ relevant <- maybeRestrictSubpaths repo files+ working <- readUnrecordedFiltered repo useindex scan files+ pending <- applyTreeFilter relevant <$> readRecordedAndPending repo+ ftf <- filetypeFunction++ Sealed changes <- unFreeLeft <$> treeDiff dopts ftf pending working+ _ :> hunks <- return $ partitionRL primIsHunk $ reverseFL changes+ let allModifiedTokens = concat $ mapFL modifiedTokens (reverseRL hunks)+ replaces = rmInvalidReplaces allModifiedTokens+ mapSeal concatFL . toFL <$>+ mapM (\(f,a,b) -> doReplace defaultToks pending+ (fromJust $ simpleSubPath $ fn2fp $ normPath f)+ (BC.unpack a) (BC.unpack b)) replaces+ where -- get individual tokens that have been modified+ modifiedTokens (FP f (Hunk _ old new)) = -- old and new are list of lines (= 1 bytestring per line)+ map (\(a,b) -> (f, a, b)) (concatMap checkModified $+ filter (\(a,b) -> length a == length b) -- only keep lines with same number of tokens+ $ zip (map breakToTokens old) (map breakToTokens new))+ modifiedTokens _ = error "modifiedTokens: Not Hunk patch"++ -- from a pair of token lists, create a pair of modified token lists+ checkModified = filter (\(a,b) -> a/=b) . uncurry zip++ rmInvalidReplaces [] = []+ rmInvalidReplaces ((f,old,new):rs)+ | any (\(f',a,b) -> f' == f && old == a && b /= new) rs = -- inconsistency detected+ rmInvalidReplaces $ filter (\(f'',a',_) -> f'' /= f || a' /= old) rs+ rmInvalidReplaces (r:rs) = r:rmInvalidReplaces (filter (/=r) rs)++ doReplace toks pend f old new = do+ let maybeReplace p = isJust <$> maybeApplyToTree replacePatch p+ pendReplaced <- maybeReplace pend+ if pendReplaced+ then return $ joinGap (:>:) (freeGap replacePatch) (emptyGap NilFL)+ else getForceReplace f toks pend old new+ where+ replacePatch = tokreplace (toFilePath f) toks old new++ getForceReplace :: PrimPatch prim => SubPath -> String -> Tree IO -> String -> String+ -> IO (FreeLeft (FL prim))+ getForceReplace f toks tree old new = do+ let path = floatSubPath f+ -- It would be nice if we could fuse the two traversals here, that is,+ -- expandPath and findFile. OTOH it is debatable whether adding a new+ -- effectful version of findFile to Darcs.Util.Tree is justified.+ expandedTree <- expandPath tree path+ content <- case findFile expandedTree path of+ Just blob -> readBlob blob+ Nothing -> error $ "getForceReplace: not in tree: " ++ show path+ let newcontent = forceTokReplace toks (BC.pack new) (BC.pack old)+ (B.concat $ BL.toChunks content)+ tree' = modifyTree expandedTree path . Just . File $ makeBlobBS newcontent+ ftf <- filetypeFunction+ normaliseNewTokPatch <- treeDiff dopts ftf expandedTree tree'+ return . joinGap (+>+) normaliseNewTokPatch $ freeGap $+ tokreplace (toFilePath f) toks old new :>: NilFL
src/Darcs/Repository/Test.hs view
@@ -23,6 +23,9 @@ ) where +import Prelude ()+import Darcs.Prelude+ import System.Exit ( ExitCode(..) ) import System.Process ( system ) import System.IO ( hPutStrLn, stderr )@@ -48,12 +51,12 @@ import Darcs.Repository.InternalTypes ( Repository(..) ) import Darcs.Util.Progress ( debugMessage )-import Darcs.Repository.Lock+import Darcs.Util.Lock ( withTempDir , withPermDir ) import Darcs.Patch.Apply ( ApplyState )-import Storage.Hashed.Tree ( Tree )+import Darcs.Util.Tree ( Tree ) getTest :: Verbosity -> IO (IO ExitCode)@@ -119,7 +122,7 @@ return ec testTentative :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT+ => Repository rt p wR wU wT -> RunTest -> LeaveTestDir -> SetScriptsExecutable@@ -137,11 +140,11 @@ testAny :: RepoPatch p- => (Repository p wR wU wT+ => (Repository rt p wR wU wT -> ((AbsolutePath -> IO ExitCode) -> IO ExitCode) -> (AbsolutePath -> IO ExitCode) -> IO ExitCode )- -> Repository p wR wU wT+ -> Repository rt p wR wU wT -> RunTest -> LeaveTestDir -> SetScriptsExecutable
− src/Darcs/Repository/Util.hs
@@ -1,340 +0,0 @@--- Copyright (C) 2013 Jose Neder------ 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.--{-# LANGUAGE CPP #-}-module Darcs.Repository.Util- ( getReplaces- , floatSubPath- , maybeApplyToTree- , defaultToks- , getMovesPs- , patchSetfMap- , getRecursiveDarcsRepos- ) where--import Prelude hiding ( catch )-import Control.Applicative ( (<$>) )-import Control.Monad ( foldM, forM )-import Control.Exception ( catch, IOException )-import qualified Data.ByteString as B ( null, concat )-import qualified Data.ByteString.Char8 as BC ( unpack, pack )-import qualified Data.ByteString.Lazy as BL ( toChunks )-import Data.Maybe ( isJust, fromJust, catMaybes )-import Data.Ord ( comparing )-import Data.List ( sortBy )-#ifdef USE_LOCAL_DATA_MAP_STRICT-import qualified Darcs.Data.Map.Strict as M ( Map, lookup, fromList, insert, map,- empty, assocs, size, findWithDefault, delete )-#else-import qualified Data.Map.Strict as M ( Map, lookup, fromList, insert, map,- empty, assocs, size, findWithDefault, delete )-#endif--import Storage.Hashed( floatPath, readPlainTree )-import Storage.Hashed.Tree ( Tree, emptyTree, expand, ItemType(..), itemType,- readBlob, modifyTree, findFile, TreeItem(..),- makeBlobBS, expandPath )-import Storage.Hashed.AnchoredPath ( AnchoredPath, anchorPath, parents,- replacePrefixPath, anchoredRoot )-import qualified Storage.Hashed.Tree as T ( list )-import Storage.Hashed.Index ( listFileIDs, getFileID )-import System.Posix.Types ( FileID )-import System.Directory ( getDirectoryContents, doesDirectoryExist )-import System.FilePath.Posix ( (</>) )-import Darcs.Patch ( RepoPatch, PrimPatch, PrimOf, primIsHunk, applyToTree,- tokreplace, forceTokReplace, move )-import Darcs.Patch.Set ( newset2RL, PatchSet(..) )-import Darcs.Patch.Apply ( ApplyState )-import Darcs.Patch.Patchy ( Apply )-import Darcs.Patch.Prim.V1.Core ( FilePatchType( Hunk ), Prim(..) )-import Darcs.Patch.Prim.Class ( PrimConstruct, PrimCanonize )-import Darcs.Patch.Permutations ( partitionRL )-import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd )-import Darcs.Patch.TokenReplace ( breakOutToken )-import Darcs.Patch.Witnesses.Ordered ( FL(..), reverseRL, reverseFL, (:>)(..),- foldlFL, concatFL, toFL, (+>+), mapRL )-import Darcs.Patch.Witnesses.Sealed ( Sealed(..), unFreeLeft, mapSeal, freeGap,- emptyGap, joinGap, FreeLeft, Gap(..) )-import Darcs.Repository- ( Repository- , readUnrecorded- , readRecordedAndPending- , maybeIdentifyRepository- )-import Darcs.Repository.Internal ( IdentifyRepo(..) )-import Darcs.Repository.InternalTypes ( Repository(..), Pristine(..) )-import Darcs.Repository.Diff( treeDiff )-import Darcs.Repository.Flags ( UseIndex(..), ScanKnown, DiffAlgorithm(..), UseCache(..) )-import Darcs.Repository.Prefs ( filetypeFunction )-import Darcs.Repository.State ( TreeFilter(..), applyTreeFilter,- restrictSubpaths, readWorking, restrictBoring,- readIndex )-import Darcs.Util.Path( fn2fp, SubPath, toFilePath, simpleSubPath, normPath,- floatSubPath )--getMovesPs :: forall p wR wU wB prim.- (PrimConstruct prim, PrimCanonize prim, RepoPatch p,- ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)- => Repository p wR wU wR- -> Maybe [SubPath]- -> IO (FL prim wB wB)-getMovesPs repository files = mkMovesFL <$> getMovedFiles repository files- where- mkMovesFL [] = NilFL- mkMovesFL ((a,b,_):xs) = move (anchorPath "" a) (anchorPath "" b) :>: mkMovesFL xs-- getMovedFiles :: (RepoPatch p, ApplyState p ~ Tree,- ApplyState (PrimOf p) ~ Tree) =>- Repository p wR wU wR ->- Maybe [SubPath] ->- IO [(AnchoredPath, AnchoredPath, ItemType)]- getMovedFiles repo fs = do- old <- sortBy (comparing snd) <$> (listFileIDs =<< readIndex repo)- nonboring <- restrictBoring emptyTree- new <- sortBy (comparing snd) <$>- (addFileIDs . (map (\(a,b) -> (a, itemType b)) . T.list) =<<- expand =<<- applyTreeFilter nonboring <$> readPlainTree ".")- let movedfiles = matchFileLists old new- fmovedfiles = case fs of- Nothing -> movedfiles- Just subpath -> filter (\(old',new',_) -> old' `elem` selfiles- || new' `elem` selfiles) movedfiles- where selfiles = map (floatPath . toFilePath) subpath- return (resolveMoves fmovedfiles)-- resolveMoves :: [(AnchoredPath, AnchoredPath, ItemType)]- -> [(AnchoredPath, AnchoredPath, ItemType)]- resolveMoves xs = changePaths $ resolveDeps 0 (M.size movesMap) visited movesMap movesDepsMap- where- changePaths [] = []- changePaths (y:ys) | fst' y == snd' y = changePaths $ map replacepp ys- | isPath y = y:changePaths (map replacepp ys)- | otherwise = y:changePaths ys- where replacepp i | nfst == anchoredRoot = i- | otherwise = (nfst, snd' i, thd' i)- where nfst = replacePrefixPath (fst' y) (snd' y) (fst' i)-- -- sort and index moves- movesMap = M.fromList $ zip [0..] $ sortBy (comparing thd') xs-- movesIDMap :: M.Map (AnchoredPath,AnchoredPath,ItemType) Int- movesIDMap = M.fromList $ zip (sortBy (comparing thd') $ xs) [0..]-- -- establish a relation of dependencies between moves (destination or parent of destination is moved again)- movesDepsMap :: M.Map Int [Int]- movesDepsMap = M.map (getMoveDeps (M.fromList (map (\x -> (fst' x,x)) xs))- (M.fromList (map (\x -> (snd' x,x)) xs))) movesMap-- getMoveDeps :: M.Map AnchoredPath (AnchoredPath, AnchoredPath, ItemType) -- source to move- -> M.Map AnchoredPath (AnchoredPath, AnchoredPath, ItemType) -- destin to move- -> (AnchoredPath, AnchoredPath, ItemType) -- some move- -> [Int]- getMoveDeps am bm y = catMaybes $- map (`M.lookup` movesIDMap) $ -- retrieve mode ID of deps- catMaybes $- byname ++ map (`M.lookup` bm) (parents $ snd' y) -- see if current move is moved to moved dir- where byname | fst' y == snd' y = []- | otherwise = [M.lookup (snd' y) am] -- see if current move is moved again-- fst' (a,_,_) = a- snd' (_,a,_) = a- thd' (_,_,a) = a-- resolveDeps :: Int -> Int -> M.Map Int (Int,Bool)- -> M.Map Int (AnchoredPath, AnchoredPath, ItemType)- -> M.Map Int [Int]- -> [(AnchoredPath, AnchoredPath, ItemType)]- resolveDeps n end v mm mdm- | n == end = reverse $- catMaybes $- map (flip M.lookup mm . abs) $- getMoves (map fst (filter (\(_,(_,f)) -> f) $- sortBy (comparing (fst . snd)) (M.assocs v))) mdm- | M.lookup n v /= Nothing = resolveDeps (n+1) end v mm mdm- | otherwise = resolveDeps (n+1) end nv nmm nmdm- where (nv, nmm, nmdm) = walk True n n v mm mdm-- getMoves [] _ = []- getMoves (r:roots) mdm = [r]++bds r++getMoves roots mdm- where bds r' = lookupList r' mdm ++ concatMap bds (map abs $ lookupList r' mdm)-- lookupList x mdm = M.findWithDefault [] x mdm-- walk b n x v mm mdm- | x < 0 = (v, mm, mdm)- | Just n == (fst <$> M.lookup x v) = resolveClashName n x v mm mdm- | otherwise = foldl (\(v',mm', mdm') dep ->- walk False n dep v' mm' mdm')- (M.insert x (n,b) v, mm, mdm)- (lookupList x mdm)-- -- Ignore swap moves- -- Currently, handling them would involve introducing intermediate file names.- -- When darcs has swapmove primitive hunk we may fix this.- resolveClashName n x v mm mdm = (v', mm', mdm')- where v' = M.insert x (n,False) $- foldl addvisited v (lookupList x mdm)- mm' = M.delete x mm -- forget about x- mdm' = M.insert x [] mdm -- remove dependencies for x- addvisited nv k | (fst <$> M.lookup k nv) /= Just n = foldl addvisited (M.insert k (n, False) nv) (lookupList k mdm)- | otherwise = nv-- visited = M.empty :: M.Map Int (Int, Bool)-- isPath (_, _, TreeType) = True- isPath _ = False-- addFileIDs :: [(AnchoredPath, ItemType)] -> IO [((AnchoredPath, ItemType),FileID)]- addFileIDs = foldM (\xs (apath, it)-> do fid <- getFileID apath- return $ case fid of- Nothing -> xs- Just fileid -> ((apath, it), fileid):xs) []-- matchFileLists :: [((AnchoredPath, ItemType),FileID)]- -> [((AnchoredPath, ItemType),FileID)]- -> [(AnchoredPath, AnchoredPath, ItemType)]- matchFileLists [] _ = []- matchFileLists _ [] = []- matchFileLists (x:xs) (y:ys) | snd x > snd y = matchFileLists (x:xs) ys- | snd x < snd y = matchFileLists xs (y:ys)- | snd (fst x) /= snd (fst y) = matchFileLists xs ys- | otherwise = (fst (fst x), fst (fst y), snd (fst x)):matchFileLists xs ys----- | Search for possible replaces between the recordedAndPending state--- and the unrecorded (or working) state. Return a Sealed FL list of--- replace patches to be applied to the recordedAndPending state.-getReplaces :: forall p wR wU wT wX. (RepoPatch p, ApplyState p ~ Tree,- ApplyState (PrimOf p) ~ Tree, wX ~ wR)- => (UseIndex, ScanKnown, DiffAlgorithm)- -> Repository p wR wU wT- -> Maybe [SubPath]- -> IO (Sealed (FL (PrimOf p) wX))-getReplaces (useindex, _, dopts) repo files = do- relevant <- maybe (return $ TreeFilter id) (restrictSubpaths repo) files- working <- applyTreeFilter relevant <$> case useindex of- UseIndex -> readUnrecorded repo Nothing- IgnoreIndex -> readWorking- pending <- applyTreeFilter relevant <$> readRecordedAndPending repo- ftf <- filetypeFunction-- Sealed changes <- unFreeLeft <$> treeDiff dopts ftf pending working- _ :> hunks <- return $ partitionRL primIsHunk $ reverseFL changes- let unfilteredReplaces = foldlFL modifiedTokens [] (reverseRL hunks)- replaces = filterInvalidReplaces unfilteredReplaces- mapSeal concatFL . toFL <$>- mapM (\(f,a,b) -> doReplace defaultToks pending- (fromJust $ simpleSubPath $ fn2fp $ normPath f)- (BC.unpack a) (BC.unpack b)) replaces- where -- get individual tokens that have been modified- modifiedTokens xs (FP f (Hunk _ old new)) =- (map (\(a,b) -> (f, a, b)) $ concatMap checkForReplaces $- filter (\(a,b) -> length a == length b)- $ zip (map breakToTokens old) (map breakToTokens new)) ++xs- modifiedTokens _ _ = error "modifiedTokens: Not Hunk patch"-- -- from a pair of token lists, create a pair of modified token lists- checkForReplaces ([],[]) = []- checkForReplaces ((a:as),(b:bs)) | a == b = checkForReplaces (as,bs)- | otherwise = (a,b):checkForReplaces (as,bs)- checkForReplaces _ = error "checkForReplaces: Lists are not of the same length"-- -- keep tokens that have been consistently replaced- filterInvalidReplaces [] = []- filterInvalidReplaces ((f,old,new):rs)- | any (\(f',a,b) -> f' == f && old == a && b /= new) rs =- filterInvalidReplaces $ filter (\(f'',a',_) -> f'' == f && a' /= old) rs- filterInvalidReplaces (r:rs) = r:filterInvalidReplaces (filter (/=r) rs)-- -- break a single bytestring into tokens- breakToTokens input | B.null input = []- breakToTokens input =- let (_, tok, remaining) = breakOutToken defaultToks input in- tok : breakToTokens remaining-- doReplace toks pend f old new = do- let maybeReplace p = isJust <$> maybeApplyToTree replacePatch p- pendReplaced <- maybeReplace pend- if pendReplaced- then return $ joinGap (:>:) (freeGap replacePatch) gapNilFL- else getForceReplace f toks pend old new- where- gapNilFL = emptyGap NilFL- fp = toFilePath f- replacePatch = tokreplace fp toks old new-- getForceReplace :: PrimPatch prim => SubPath -> String -> Tree IO -> String -> String- -> IO (FreeLeft (FL prim))- getForceReplace f toks tree old new = do- let path = floatSubPath f- -- It would be nice if we could fuse the two traversals here, that is,- -- expandPath and findFile. OTOH it is debatable whether adding a new- -- effectful version of findFile to Storage.Hashed.Tree is justified.- expandedTree <- expandPath tree path- content <- case findFile expandedTree path of- Just blob -> readBlob blob- Nothing -> do- error $ "getForceReplace: not in tree: " ++ show path- let newcontent = forceTokReplace toks (BC.pack new) (BC.pack old)- (B.concat $ BL.toChunks content)- tree' = modifyTree expandedTree path . Just . File $ makeBlobBS newcontent- ftf <- filetypeFunction- normaliseNewTokPatch <- treeDiff dopts ftf expandedTree tree'- return . joinGap (+>+) normaliseNewTokPatch $ freeGap $- tokreplace (toFilePath f) toks old new :>: NilFL---maybeApplyToTree :: (Apply p, ApplyState p ~ Tree) => p wX wY -> Tree IO- -> IO (Maybe (Tree IO))-maybeApplyToTree patch tree =- (Just `fmap` applyToTree patch tree) `catch` (\(_ :: IOException) -> return Nothing)--patchSetfMap:: (forall wW wZ . PatchInfoAnd p wW wZ -> IO a) -> PatchSet p wW' wZ' -> IO [a]-patchSetfMap f = sequence . mapRL f . newset2RL--defaultToks :: String-defaultToks = "A-Za-z_0-9"---- |getRecursiveDarcsRepos returns all paths to repositories under topdir.-getRecursiveDarcsRepos :: FilePath -> IO [FilePath]-getRecursiveDarcsRepos topdir = do- isDir <- doesDirectoryExist topdir- if isDir- then do- status <- maybeIdentifyRepository NoUseCache topdir- case status of- GoodRepository (Repo _ _ pris _) ->- case pris of- HashedPristine -> return [topdir]- _ -> return [] -- old fashioned or broken repo- _ -> getRecursiveDarcsRepos' topdir- else return []-- where- getRecursiveDarcsRepos' d = do- names <- getDirectoryContents d- let properNames = filter (\x -> head x /= '.') names- paths <- forM properNames $ \name -> do- let path = d </> name- getRecursiveDarcsRepos path- return (concat paths)
src/Darcs/UI/ApplyPatches.hs view
@@ -3,8 +3,10 @@ , StandardPatchApplier(..) ) where +import Prelude ()+import Darcs.Prelude+ import System.Exit ( ExitCode ( ExitSuccess ), exitSuccess )-import Prelude hiding ( catch ) import System.IO ( hClose, stdout, stderr ) import Control.Exception ( catch, fromException, SomeException, throwIO )@@ -36,59 +38,62 @@ , setScriptsExecutablePatches ) import Darcs.Repository.Job ( RepoJob(RepoJob) )-import Darcs.Patch ( RepoPatch, description, PrimOf )+import Darcs.Patch ( RepoPatch, RepoType, IsRepoType, description, PrimOf ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Witnesses.Ordered ( FL, mapFL, nullFL ) import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed) ) import Darcs.UI.External ( sendEmail )-import Darcs.Repository.Lock ( withStdoutTemp, readBinFile )+import Darcs.Util.Lock ( withStdoutTemp, readBinFile ) import Darcs.Util.Printer ( vcat, text )-import Storage.Hashed.Tree( Tree )+import Darcs.Util.Tree( Tree ) +import GHC.Exts ( Constraint )+ data PatchProxy (p :: * -> * -> *) = PatchProxy -- |This class is a hack to abstract over pull/apply and rebase pull/apply. class PatchApplier pa where - -- |'CarrierType pa p' resolves to either 'p' or 'Rebasing p' - type CarrierType pa (p :: * -> * -> *) :: * -> * -> *+ type ApplierRepoTypeConstraint pa (rt :: RepoType) :: Constraint repoJob :: pa -> [DarcsFlag]- -> (forall p wR wU- . ( RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree- , RepoPatch (CarrierType pa p), ApplyState (CarrierType pa p) ~ Tree- , ApplyState (PrimOf (CarrierType pa p)) ~ Tree+ -> (forall rt p wR wU+ . ( IsRepoType rt, ApplierRepoTypeConstraint pa rt+ , RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree )- => (PatchProxy p -> Repository (CarrierType pa p) wR wU wR -> IO ()))+ => (PatchProxy p -> Repository rt p wR wU wR -> IO ())) -> RepoJob () applyPatches- :: forall p wR wU wT wX wZ- . (RepoPatch p, ApplyState (PrimOf p) ~ Tree, ApplyState p ~ Tree)+ :: forall rt p wR wU wT wX wZ+ . ( ApplierRepoTypeConstraint pa rt, IsRepoType rt+ , RepoPatch p, ApplyState (PrimOf p) ~ Tree, ApplyState p ~ Tree+ ) => pa -> PatchProxy p -> String -> [DarcsFlag] -> String- -> Repository (CarrierType pa p) wR wU wT- -> FL (PatchInfoAnd (CarrierType pa p)) wX wT- -> FL (PatchInfoAnd (CarrierType pa p)) wX wZ -> IO ()+ -> Repository rt p wR wU wT+ -> FL (PatchInfoAnd rt p) wX wT+ -> FL (PatchInfoAnd rt p) wX wZ -> IO () data StandardPatchApplier = StandardPatchApplier instance PatchApplier StandardPatchApplier where- type CarrierType StandardPatchApplier p = p+ type ApplierRepoTypeConstraint StandardPatchApplier rt = () repoJob StandardPatchApplier _opts f = RepoJob (f PatchProxy) applyPatches StandardPatchApplier PatchProxy = standardApplyPatches standardApplyPatches- :: forall p wR wU wT wX wZ . (RepoPatch p, ApplyState (PrimOf p) ~ Tree, ApplyState p ~ Tree)- => String -> [DarcsFlag] -> String -> Repository p wR wU wT- -> FL (PatchInfoAnd p) wX wT -> FL (PatchInfoAnd p) wX wZ -> IO ()+ :: forall rt p wR wU wT wX wZ+ . (IsRepoType rt, RepoPatch p, ApplyState (PrimOf p) ~ Tree, ApplyState p ~ Tree)+ => String -> [DarcsFlag] -> String -> Repository rt p wR wU wT+ -> FL (PatchInfoAnd rt p) wX wT -> FL (PatchInfoAnd rt p) wX wZ -> IO () standardApplyPatches cmdName opts from_whom repository us' to_be_applied = do printDryRunMessageAndExit cmdName (verbosity opts)
src/Darcs/UI/Commands.hs view
@@ -49,7 +49,6 @@ , printDryRunMessageAndExit , setEnvDarcsPatches , setEnvDarcsFiles- , formatPath , defaultRepo , amInHashedRepository , amInRepository@@ -57,11 +56,14 @@ , findRepository ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( (^) ) import Control.Monad ( when, unless ) import Data.List ( sort, isPrefixOf ) import Data.Maybe ( catMaybes )-import Storage.Hashed.Tree ( Tree )+import Darcs.Util.Tree ( Tree ) import System.Console.GetOpt ( OptDescr ) import System.Exit ( exitSuccess ) import System.IO ( stderr )@@ -397,7 +399,7 @@ => String -> Verbosity -> Summary -> DryRun -> XmlOutput -> Bool -- interactive- -> FL (PatchInfoAnd p) wX wY+ -> FL (PatchInfoAnd rt p) wX wY -> IO () printDryRunMessageAndExit action v s d x interactive patches = do when (d == YesDryRun) $ do@@ -437,7 +439,7 @@ -- | Set the DARCS_PATCHES and DARCS_PATCHES_XML environment variables with -- info about the given patches, for use in post-hooks. setEnvDarcsPatches :: (RepoPatch p, ApplyState p ~ Tree)- => FL (PatchInfoAnd p) wX wY -> IO ()+ => FL (PatchInfoAnd rt p) wX wY -> IO () #ifndef WIN32 setEnvDarcsPatches ps = do let k = "Defining set of chosen patches"@@ -461,6 +463,7 @@ | toobig (10 * 1024) v = return () | otherwise = setEnv e v True where+ -- note: not using (length v) because we want to be more lazy than that toobig :: Int -> [a] -> Bool toobig 0 _ = True toobig _ [] = False@@ -478,17 +481,6 @@ #else setEnvDarcsFiles _ = return () #endif---- | Format a path for screen output, so that the user sees where the path--- begins and ends. Could (should?) also warn about unprintable characters--- here.-formatPath :: String -> String-formatPath path = "\"" ++ quote path ++ "\""- where- quote "" = ""- quote (c:cs) = if c `elem` ['\\', '"']- then '\\' : c : quote cs- else c : quote cs defaultRepo :: [DarcsFlag] -> AbsolutePath -> [String] -> IO [String] defaultRepo fs = defaultrepo (remoteRepos fs)
src/Darcs/UI/Commands/Add.hs view
@@ -30,10 +30,12 @@ , expandDirs ) where +import Prelude ()+import Darcs.Prelude #include "impossible.h" -import Prelude hiding ( (^), catch )+import Prelude hiding ( (^) ) import Control.Exception ( catch, IOException ) import Control.Monad ( when, unless, liftM )@@ -41,7 +43,7 @@ import Data.List.Ordered ( nubSort ) import Data.Maybe ( isNothing, maybeToList ) import Darcs.Util.Printer ( text )-import Storage.Hashed.Tree ( Tree, findTree, expand )+import Darcs.Util.Tree ( Tree, findTree, expand ) import Darcs.Util.Path ( floatPath, anchorPath, parents, SubPath, toFilePath, simpleSubPath, toPath, AbsolutePath ) import System.FilePath.Posix ( takeDirectory, (</>) )@@ -80,12 +82,12 @@ addDescription :: String-addDescription = "Add one or more new files or directories."+addDescription = "Add new files to version control." addHelp :: String addHelp =- "Generally a repository contains both files that should be version\n" +++ "Generally the working tree contains both files that should be version\n" ++ "controlled (such as source code) and files that Darcs should ignore\n" ++ "(such as executables compiled from the source code). The `darcs add`\n" ++ "command is used to tell Darcs which files to version control.\n" ++@@ -110,7 +112,7 @@ "unusable on those systems!\n\n" addBasicOpts :: DarcsOption a- (Bool -> Bool -> Bool -> Bool -> Maybe String -> O.DryRun -> a)+ (O.IncludeBoring -> Bool -> Bool -> Bool -> Maybe String -> O.DryRun -> a) addBasicOpts = O.includeBoring ^ O.allowProblematicFilenames ^ O.recursive@@ -121,7 +123,7 @@ addAdvancedOpts = O.umask addOpts :: DarcsOption a- (Bool+ (O.IncludeBoring -> Bool -> Bool -> Bool
src/Darcs/UI/Commands/Amend.hs view
@@ -26,15 +26,13 @@ ( amend , amendrecord- , updatePatchHeader ) where -import Prelude hiding ( (^) )+import Prelude ()+import Darcs.Prelude import Data.Maybe ( isNothing, isJust )-import Control.Applicative ( (<$>) )-import Control.Monad ( unless, when )-import System.Exit ( exitSuccess )+import Control.Monad ( when ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts@@ -44,24 +42,21 @@ , setEnvDarcsPatches , amInHashedRepository )-import Darcs.UI.Commands.Record ( getLog ) import Darcs.UI.Commands.Util ( announceFiles, testTentativeAndMaybeExit )-import Darcs.UI.Flags- ( DarcsFlag- , diffOpts, fixSubPaths, getEasyAuthor, promptAuthor, getDate )+import Darcs.UI.Flags ( DarcsFlag, diffOpts, fixSubPaths ) import Darcs.UI.Options ( DarcsOption, (^), oparse, odesc, ocheck, defaultFlags ) import qualified Darcs.UI.Options.All as O+import Darcs.UI.PatchHeader ( updatePatchHeader, AskAboutDeps(..)+ , HijackOptions(..)+ , runHijackT ) import Darcs.Repository.Flags ( UpdateWorking(..), DryRun(NoDryRun) )-import Darcs.Patch ( RepoPatch, description, PrimOf, fromPrims,- infopatch, getdeps, adddeps, effect, invert, invertFL+import Darcs.Patch ( IsRepoType, RepoPatch, description, PrimOf+ , effect, invert, invertFL ) import Darcs.Patch.Apply ( ApplyState )-import Darcs.Patch.Info ( piAuthor, piName, piLog, piDateString,- patchinfo, isInverted, isTag, invertName,- )-import Darcs.Patch.Prim ( canonizeFL )+import Darcs.Patch.Info ( isTag ) import Darcs.Patch.Split ( primSplitter )-import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, n2pia, hopefully, info, patchDesc )+import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, info, patchDesc ) import Darcs.Patch.Rebase.Fixup ( RebaseFixup(..) ) import Darcs.Patch.Rebase.Name ( RebaseName(..) ) import Darcs.Util.Path ( toFilePath, SubPath(), AbsolutePath )@@ -69,6 +64,7 @@ ( Repository , withRepoLock , RepoJob(..)+ , RebaseJobFlags(..) , tentativelyRemovePatches , tentativelyAddPatch , withManualRebaseUpdate@@ -79,27 +75,24 @@ , listRegisteredFiles ) import Darcs.Repository.Prefs ( globalPrefsDirDoc )-import Darcs.Repository.Util ( getMovesPs, getReplaces )+import Darcs.Repository.State ( getMovesPs, getReplaces ) import Darcs.UI.SelectChanges- ( selectChanges- , WhichChanges(..)+ ( WhichChanges(..) , selectionContextPrim , runSelection , withSelectedPatchFromRepo- , askAboutDepends ) import qualified Darcs.UI.SelectChanges as S ( PatchSelectionOptions(..) ) import Darcs.Util.Exception ( clarifyErrors )-import Darcs.Util.Prompt ( askUser ) import Darcs.Patch.Witnesses.Ordered ( FL(..), (:>)(..), (+>+), nullFL, reverseRL, mapFL_FL ) import Darcs.Patch.Witnesses.Sealed ( Sealed(..) ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP ) import Darcs.Util.Printer ( putDocLn )-import Storage.Hashed.Tree( Tree )+import Darcs.Util.Tree( Tree ) import Darcs.Repository.Internal ( tentativelyRemoveFromPending ) @@ -270,10 +263,11 @@ doAmend :: AmendConfig -> Maybe [SubPath] -> IO () doAmend cfg files =+ let rebaseJobFlags = RebaseJobFlags (compress cfg) (verbosity cfg) YesUpdateWorking in withRepoLock NoDryRun (useCache cfg) YesUpdateWorking (umask cfg) $- RebaseAwareJob (compress cfg) (verbosity cfg) YesUpdateWorking $ \(repository :: Repository p wR wU wR) ->+ RebaseAwareJob rebaseJobFlags $ \(repository :: Repository rt p wR wU wR) -> withSelectedPatchFromRepo "amend" repository (patchSelOpts cfg) $ \ (_ :> oldp) -> do- announceFiles files "Amending changes in"+ announceFiles (verbosity cfg) files "Amending changes in" -- auxiliary function needed because the witness types differ for the isTag case pristine <- readRecorded repository let go :: forall wU1 . FL (PrimOf p) wR wU1 -> IO ()@@ -282,22 +276,21 @@ do let context = selectionContextPrim First "record" (patchSelOpts cfg) --([All,Unified] `intersect` opts)- (Just primSplitter)+ (Just (primSplitter (diffAlgorithm cfg))) (map toFilePath <$> files) (Just pristine)- (chosenPatches :> _) <- runSelection (selectChanges ch) context+ (chosenPatches :> _) <- runSelection ch context addChangesToPatch cfg repository oldp chosenPatches if not (isTag (info oldp)) -- amending a normal patch then if amendUnrecord cfg- then do let sel = selectChanges (effect oldp)- context = selectionContextPrim Last "unrecord"+ then do let context = selectionContextPrim Last "unrecord" (patchSelOpts cfg) -- ([All,Unified] `intersect` opts)- (Just primSplitter)+ (Just (primSplitter (diffAlgorithm cfg))) (map toFilePath <$> files) (Just pristine)- (_ :> chosenPrims) <- runSelection sel context+ (_ :> chosenPrims) <- runSelection (effect oldp) context let invPrims = reverseRL (invertFL chosenPrims) addChangesToPatch cfg repository oldp invPrims else do Sealed replacePs <- if O.replaces (lookfor cfg) == O.YesLookForReplaces@@ -306,8 +299,9 @@ movesPs <- if O.moves (lookfor cfg) == O.YesLookForMoves then getMovesPs repository files else return NilFL- go =<< unrecordedChangesWithPatches (diffingOpts cfg) repository files- movesPs (unsafeCoerceP replacePs :: FL (PrimOf p) wR wR)+ go =<< unrecordedChangesWithPatches+ movesPs (unsafeCoerceP replacePs :: FL (PrimOf p) wR wR)+ (diffingOpts cfg) repository files -- amending a tag else if hasEditMetadata cfg && isNothing files -- the user is not trying to add new changes to the tag so there is@@ -323,13 +317,15 @@ go NilFL -addChangesToPatch :: forall p wR wU wT wX wY . (RepoPatch p, ApplyState p ~ Tree)+addChangesToPatch :: forall rt p wR wU wT wX wY+ . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => AmendConfig- -> Repository p wR wU wT- -> PatchInfoAnd p wX wT+ -> Repository rt p wR wU wT+ -> PatchInfoAnd rt p wX wT -> FL (PrimOf p) wT wY -> IO () addChangesToPatch cfg repository oldp chs =+ let rebaseJobFlags = RebaseJobFlags (compress cfg) (verbosity cfg) YesUpdateWorking in if nullFL chs && not (hasEditMetadata cfg) then putStrLn "You don't want to record anything!" else do@@ -344,18 +340,19 @@ -- -- We can also signal that any explicit dependencies of the old patch should be rewritten -- for the new patch using a 'NameFixup'.- (repository''', (mlogf, newp)) <- withManualRebaseUpdate (compress cfg) (verbosity cfg) YesUpdateWorking repository $ \repository' -> do+ (repository''', (mlogf, newp)) <- withManualRebaseUpdate rebaseJobFlags repository $ \repository' -> do repository'' <- tentativelyRemovePatches repository' (compress cfg) YesUpdateWorking (oldp :>: NilFL)- (mlogf, newp) <- updatePatchHeader- (askDeps cfg)+ (mlogf, newp) <- runHijackT AlwaysRequestHijackPermission $ updatePatchHeader "amend"+ (if askDeps cfg then AskAboutDeps repository'' else NoAskAboutDeps) (patchSelOpts cfg)+ (diffAlgorithm cfg) (keepDate cfg) (selectAuthor cfg) (author cfg) (patchname cfg) (askLongComment cfg)- repository'' oldp chs+ oldp chs let fixups = mapFL_FL PrimFixup (invert chs) +>+ NameFixup (Rename (info newp) (info oldp)) :>:@@ -364,7 +361,7 @@ repository''' <- tentativelyAddPatch repository'' (compress cfg) (verbosity cfg) YesUpdateWorking newp return (repository''', fixups, (mlogf, newp)) let failmsg = maybe "" (\lf -> "\nLogfile left in "++lf++".") mlogf- testTentativeAndMaybeExit repository''' (verbosity cfg) (testChanges cfg) (sse cfg) (isInteractive True cfg)+ testTentativeAndMaybeExit repository''' (verbosity cfg) (testChanges cfg) (sse cfg) (isInteractive cfg) ("you have a bad patch: '" ++ patchDesc newp ++ "'") "amend it" (Just failmsg) when (O.moves (lookfor cfg) == O.YesLookForMoves || O.replaces (lookfor cfg) == O.YesLookForReplaces)@@ -375,56 +372,6 @@ setEnvDarcsPatches (newp :>: NilFL) -updatePatchHeader :: forall p wX wY wR wU wT . (RepoPatch p, ApplyState p ~ Tree)- => Bool -- askDeps- -> S.PatchSelectionOptions- -> Bool -- keepDate- -> Bool -- selectAuthor- -> Maybe String -- author- -> Maybe String -- patchname- -> Maybe O.AskLongComment- -> Repository p wR wU wT- -> PatchInfoAnd p wT wX- -> FL (PrimOf p) wX wY- -> IO (Maybe String, PatchInfoAnd p wT wY)-updatePatchHeader ask_deps pSelOpts nKeepDate nSelectAuthor nAuthor nPatchname nAskLongComment repository oldp chs = do-- let newchs = canonizeFL (S.diffAlgorithm pSelOpts) (effect oldp +>+ chs)-- let old_pdeps = getdeps $ hopefully oldp- newdeps <- if ask_deps- then askAboutDepends repository newchs pSelOpts old_pdeps- else return old_pdeps-- let old_pinf = info oldp- prior = (piName old_pinf, piLog old_pinf)- old_author = piAuthor old_pinf- date <- if nKeepDate then return (piDateString old_pinf) else getDate False- (new_author,edit_author) <- getAuthor nSelectAuthor nAuthor old_author- warnIfHijacking old_author edit_author- (new_name, new_log, mlogf) <- getLog - nPatchname False (O.Logfile Nothing False) nAskLongComment (Just prior) chs- let maybe_invert = if isInverted old_pinf then invertName else id- new_pinf <- maybe_invert `fmap` patchinfo date new_name- new_author new_log-- let newp = n2pia (adddeps (infopatch new_pinf (fromPrims newchs)) newdeps)-- return (mlogf, newp)---warnIfHijacking :: String -- Original author- -> Bool -- Author change requested by options- -> IO ()-warnIfHijacking old_author edit_author = do- authors_here <- getEasyAuthor- unless (edit_author || old_author `elem` authors_here) $ do- yorn <- askUser $- "You're not " ++ old_author ++"! Amend anyway? "- case yorn of ('y':_) -> return ()- _ -> exitSuccess-- hasEditMetadata :: AmendConfig -> Bool hasEditMetadata cfg = isJust (author cfg) || selectAuthor cfg@@ -444,33 +391,18 @@ -- hasEditMetadata (_:fs) = hasEditMetadata fs -getAuthor :: Bool -> Maybe String -> String -> IO (String,Bool)-getAuthor True _ _ = do- a <- promptAuthor False True- return (a,True)-getAuthor False (Just a) _ = return (a,True)-getAuthor False Nothing old = return (old,False)---- getAuthor (SelectAuthor:_) _ = do--- a <- promptAuthor False True--- return (a,True)--- getAuthor (Author a:_) _ = return (a,True)--- getAuthor (_:as) old = getAuthor as old--- getAuthor [] old = return (old,False)- patchSelOpts :: AmendConfig -> S.PatchSelectionOptions patchSelOpts cfg = S.PatchSelectionOptions { S.verbosity = verbosity cfg , S.matchFlags = matchFlags cfg- , S.diffAlgorithm = diffAlgorithm cfg- , S.interactive = isInteractive True cfg+ , S.interactive = isInteractive cfg , S.selectDeps = O.PromptDeps -- option not supported, use default , S.summary = O.NoSummary -- option not supported, use default , S.withContext = withContext cfg } diffingOpts :: AmendConfig -> (O.UseIndex, O.ScanKnown, O.DiffAlgorithm)-diffingOpts cfg = diffOpts (useIndex cfg) (O.adds (lookfor cfg)) False (diffAlgorithm cfg)+diffingOpts cfg = diffOpts (useIndex cfg) (O.adds (lookfor cfg)) O.NoIncludeBoring (diffAlgorithm cfg) -isInteractive :: Bool -> AmendConfig -> Bool-isInteractive def = maybe def id . interactive+isInteractive :: AmendConfig -> Bool+isInteractive = maybe True id . interactive
src/Darcs/UI/Commands/Annotate.hs view
@@ -18,17 +18,19 @@ module Darcs.UI.Commands.Annotate ( annotate ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( (^) ) import Control.Arrow ( first ) import Control.Monad ( unless ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository )-import Darcs.UI.Flags ( DarcsFlag(NoPatchIndexFlag), isUnified, useCache, fixSubPaths, hasSummary, umask )+import Darcs.UI.Flags ( DarcsFlag(NoPatchIndexFlag), useCache, fixSubPaths, umask ) import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise , defaultFlags, parseFlags ) import qualified Darcs.UI.Options.All as O-import Storage.Hashed.Plain( readPlainTree ) import Darcs.Repository.State ( readRecorded ) import Darcs.Repository ( withRepository@@ -41,26 +43,19 @@ import Darcs.Repository.Flags ( UpdateWorking(..) ) import Darcs.Repository.PatchIndex ( attemptCreatePatchIndex ) import Darcs.Patch.Set ( newset2RL )-import Darcs.Patch ( RepoPatch, Named, patch2patchinfo, invertRL )-import qualified Darcs.Patch ( summary )-import Darcs.Patch.Type ( PatchType(..) )-import Darcs.Patch.Dummy ( DummyPatch )+import Darcs.Patch ( invertRL ) import qualified Data.ByteString.Char8 as BC ( pack, concat, intercalate ) import Data.ByteString.Lazy ( toChunks )-import Darcs.UI.PrintPatch ( printPatch, contextualPrintPatch ) import Darcs.Patch.ApplyMonad( withFileNames ) import System.FilePath.Posix ( (</>) )-import Darcs.Patch.Info ( showPatchInfoUI, showPatchInfo )-import Darcs.Patch.Match ( matchPatch, haveNonrangeMatch, getNonrangeMatchS )-import Darcs.Repository.Match ( getFirstMatch, getOnePatchset )-import Darcs.Repository.Lock ( withTempDir )+import Darcs.Patch.Match ( haveNonrangeMatch, getNonrangeMatchS )+import Darcs.Repository.Match ( getOnePatchset ) import Darcs.Repository.PatchIndex ( getRelevantSubsequence, canUsePatchIndex )-import Darcs.Patch.Witnesses.Sealed ( Sealed2(..), Sealed(..), seal )+import Darcs.Patch.Witnesses.Sealed ( Sealed(..), seal ) import qualified Darcs.Patch.Annotate as A-import Darcs.Util.Printer ( putDocLn, Doc ) -import Storage.Hashed.Tree( TreeItem(..), readBlob, list, expand )-import Storage.Hashed.Monad( findM, virtualTreeIO )+import Darcs.Util.Tree( TreeItem(..), readBlob, list, expand )+import Darcs.Util.Tree.Monad( findM, virtualTreeIO ) import Darcs.Util.Path( floatPath, anchorPath, fp2fn, toFilePath , AbsolutePath ) import qualified Darcs.Util.Diff as D ( DiffAlgorithm(MyersDiff) )@@ -68,42 +63,31 @@ #include "impossible.h" annotateDescription :: String-annotateDescription = "Display which patch last modified something."+annotateDescription = "Annotate lines of a file with the last patch that modified it." annotateHelp :: String annotateHelp = unlines- [ "The `darcs annotate` command provides two unrelated operations. When"- , "called on a file, it will find the patch that last modified each line"- , "in that file. When called on a patch (e.g. using `--patch`), it will"- , "print the internal representation of that patch."- , ""- , "The `--summary` option will result in a summarized patch annotation,"- , "similar to `darcs whatsnew`. It has no effect on file annotations."+ [ "When `darcs annotate` is called on a file, it will find the patch that"+ , "last modified each line in that file. This also works on directories." , ""- , "By default, output is in a human-readable format. The `--machine-readable`"- , "option can be used to generate output for machine postprocessing."+ , "The `--machine-readable` option can be used to generate output for"+ , "machine postprocessing." ] annotateBasicOpts :: DarcsOption a- (Maybe O.Summary- -> O.WithContext- -> Bool+ (Bool -> [O.MatchFlag] -> Maybe String -> a)-annotateBasicOpts = O.summary- ^ O.withContext- ^ O.machineReadable- ^ O.matchOne+annotateBasicOpts = O.machineReadable+ ^ O.matchUpToOne ^ O.workingRepoDir annotateAdvancedOpts :: DarcsOption a (O.WithPatchIndex -> a) annotateAdvancedOpts = O.patchIndexYes annotateOpts :: DarcsOption a- (Maybe O.Summary- -> O.WithContext- -> Bool+ ( Bool -> [O.MatchFlag] -> Maybe String -> Maybe O.StdCmdAction@@ -126,8 +110,8 @@ , commandName = "annotate" , commandHelp = annotateHelp , commandDescription = annotateDescription- , commandExtraArgs = -1- , commandExtraArgHelp = ["[FILE or DIRECTORY]..."]+ , commandExtraArgs = 1+ , commandExtraArgHelp = ["[FILE or DIRECTORY]"] , commandCommand = annotateCmd , commandPrereq = amInHashedRepository , commandGetArgPossibilities = listRegisteredFiles@@ -140,39 +124,12 @@ } annotateCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-annotateCmd fps opts [""] = annotate' fps opts [] -- when does that happen?-annotateCmd fps opts [] = do- let matchFlags = parseFlags O.matchOne opts- unless (haveNonrangeMatch (PatchType :: PatchType DummyPatch) matchFlags) $- fail $ "Annotate requires either a patch pattern or a " ++- "file or directory argument."- annotate' fps opts []-annotateCmd fps opts args = annotate' fps opts args--annotate' :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-annotate' _ opts [] = -- annotating a patch (ie, showing its contents)- withRepository (useCache opts) $ RepoJob $ \repository -> do- let matchFlags = parseFlags O.matchOne opts- Sealed2 p <- matchPatch matchFlags `fmap` readRepo repository- if hasSummary O.NoSummary opts == O.YesSummary- then do putDocLn $ showpi $ patch2patchinfo p- putDocLn $ show_summary p- else if isUnified opts == O.YesContext- then withTempDir "context" $ \_ ->- do getFirstMatch repository matchFlags- c <- readPlainTree "."- contextualPrintPatch c p- else printPatch p- where showpi | parseFlags O.machineReadable opts = showPatchInfo- | otherwise = showPatchInfoUI- show_summary :: RepoPatch p => Named p wX wY -> Doc- show_summary = Darcs.Patch.summary--annotate' fps opts args@[_] = do -- annotating a file or a directory+annotateCmd _ _ [""] = fail "No filename argument given to annotate!"+annotateCmd fps opts args = do+ let matchFlags = parseFlags O.matchUpToOne opts unless (NoPatchIndexFlag `elem` opts) $ withRepoLockCanFail (useCache opts) YesUpdateWorking (umask opts) $ RepoJob attemptCreatePatchIndex withRepository (useCache opts) $ RepoJob $ \repository -> do- let matchFlags = parseFlags O.matchOne opts r <- readRepo repository (origpath:_) <- fixSubPaths fps args recorded <- readRecorded repository@@ -214,5 +171,3 @@ con <- BC.concat `fmap` toChunks `fmap` readBlob b putStrLn $ fmt con $ A.annotate D.MyersDiff (invertRL ans_patches) (fp2fn path) con Just (Stub _ _) -> impossible--annotate' _ _ _ = fail "annotate accepts at most one argument"
src/Darcs/UI/Commands/Apply.hs view
@@ -15,15 +15,18 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP, PatternGuards #-}+{-# LANGUAGE CPP #-} module Darcs.UI.Commands.Apply ( apply, applyCmd , getPatchBundle -- used by darcsden ) where +import Prelude ()+import Darcs.Prelude+ import System.Exit ( exitSuccess )-import Prelude hiding ( (^), catch )+import Prelude hiding ( (^) ) import Control.Monad ( when ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, hopefullyM, info )@@ -37,7 +40,7 @@ , doHappyForwarding, doReverse, verbosity, useCache, dryRun , reorder, umask , fixUrl, getCc, getSendmailCmd- , diffAlgorithm, isUnified, getReply+ , isUnified, getReply ) import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags, parseFlags ) import qualified Darcs.UI.Options.All as O@@ -51,7 +54,7 @@ , filterOutConflicts ) import Darcs.Patch.Set ( Origin, newset2RL )-import Darcs.Patch ( RepoPatch, PrimOf )+import Darcs.Patch ( IsRepoType, RepoPatch, PrimOf ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Info ( PatchInfo, showPatchInfoUI ) import Darcs.Patch.Witnesses.Ordered@@ -64,7 +67,7 @@ import qualified Data.ByteString.Char8 as BC (unpack, last, pack) import Darcs.Util.Download ( Cachable(Uncachable) )-import Darcs.Repository.External ( gzFetchFilePS )+import Darcs.Util.External ( gzFetchFilePS ) import Darcs.UI.External ( sendEmailDoc , resendEmail@@ -74,8 +77,7 @@ import Darcs.Patch.Depends ( findUncommon, findCommonWithThem ) import Darcs.UI.ApplyPatches ( PatchApplier(..), StandardPatchApplier(..), PatchProxy ) import Darcs.UI.SelectChanges- ( selectChanges- , WhichChanges(..)+ ( WhichChanges(..) , runSelection , selectionContext )@@ -85,7 +87,7 @@ ( packedString, vcat, text, empty , renderString, RenderMode(..) )-import Storage.Hashed.Tree( Tree )+import Darcs.Util.Tree( Tree ) #include "impossible.h" @@ -264,15 +266,15 @@ applyCmd _ _ _ _ = impossible applyCmdCommon- :: forall pa p wR wU+ :: forall rt pa p wR wU . ( PatchApplier pa, RepoPatch p, ApplyState (PrimOf p) ~ Tree, ApplyState p ~ Tree- , RepoPatch (CarrierType pa p), ApplyState (CarrierType pa p) ~ Tree + , ApplierRepoTypeConstraint pa rt, IsRepoType rt ) => pa -> PatchProxy p -> [DarcsFlag] -> B.ByteString- -> Repository (CarrierType pa p) wR wU wR+ -> Repository rt p wR wU wR -> IO () applyCmdCommon patchApplier patchProxy opts bundle repository = do let from_whom = getFrom bundle@@ -291,8 +293,8 @@ let common_i = mapRL info $ newset2RL common them_i = mapRL info $ newset2RL them required = them_i \\ common_i -- FIXME quadratic?- check :: RL (PatchInfoAnd (CarrierType pa p)) wX wY -> [PatchInfo] -> IO ()- check (p :<: ps') bad = case hopefullyM p of+ check :: RL (PatchInfoAnd rt p) wX wY -> [PatchInfo] -> IO ()+ check (ps' :<: p) bad = case hopefullyM p of Nothing | info p `elem` required -> check ps' (info p : bad) _ -> check ps' bad check NilRL [] = return ()@@ -316,7 +318,7 @@ let direction = if doReverse opts then FirstReversed else First context = selectionContext direction "apply" (patchSelOpts opts) Nothing Nothing- (to_be_applied :> _) <- runSelection (selectChanges their_ps) context+ (to_be_applied :> _) <- runSelection their_ps context applyPatches patchApplier patchProxy "apply" opts from_whom repository us' to_be_applied -- see the default (False) for the option -- where fixed_opts = if Interactive `elem` opts@@ -324,7 +326,7 @@ -- else All : opts getPatchBundle :: RepoPatch p => [DarcsFlag] -> B.ByteString- -> IO (Either String (SealedPatchSet p Origin))+ -> IO (Either String (SealedPatchSet rt p Origin)) getPatchBundle opts fps = do let opt_verify = parseFlags O.verify opts mps <- verifyPS opt_verify $ readEmail fps@@ -421,7 +423,6 @@ patchSelOpts flags = S.PatchSelectionOptions { S.verbosity = verbosity flags , S.matchFlags = parseFlags O.matchSeveral flags- , S.diffAlgorithm = diffAlgorithm flags , S.interactive = maybeIsInteractive flags , S.selectDeps = O.PromptDeps -- option not supported, use default , S.summary = O.NoSummary -- option not supported, use default
src/Darcs/UI/Commands/Clone.hs view
@@ -25,8 +25,11 @@ , cloneToSSH ) where -import Prelude hiding ( (^), catch )+import Prelude ()+import Darcs.Prelude +import Prelude hiding ( (^) )+ import System.Directory ( doesDirectoryExist, doesFileExist , setCurrentDirectory ) import System.Exit ( ExitCode(..) )@@ -60,8 +63,8 @@ ) , formatHas )-import Darcs.Repository.Lock ( withTempDir )-import Darcs.Repository.Ssh ( getSSH, SSHCmd(SCP) )+import Darcs.Util.Lock ( withTempDir )+import Darcs.Util.Ssh ( getSSH, SSHCmd(SCP) ) import Darcs.Repository.Flags ( CloneKind(CompleteClone), SetDefault(NoSetDefault), ForgetParent(..) ) import Darcs.Patch.Bundle ( scanContextFile )@@ -86,10 +89,8 @@ , "if omitted, it is inferred from the source location." , "" , "By default Darcs will copy every patch from the original repository."- , "This means the copy is completely independent of the original; you can"- , "operate on the new repository even when the original is inaccessible." , "If you expect the original repository to remain accessible, you can"- , "use `--lazy` to avoid copying patches until they are needed (`copy on"+ , "use `--lazy` to avoid copying patches until they are needed ('copy on" , "demand'). This is particularly useful when copying a remote" , "repository with a long history that you don't care about." , ""@@ -103,8 +104,8 @@ , "" , "When cloning from a remote location, Darcs will look for and attempt" , "to use packs created by `darcs optimize http` in the remote repository."- , "Packs are single big files that can be downloaded instead of many"- , "little files, which makes cloning faster over HTTP."+ , "Packs are single big files that can be downloaded faster than many"+ , "little files." , "" , "Darcs clone will not copy unrecorded changes to the source repository's" , "working tree."@@ -234,7 +235,6 @@ (withWorkingDir opts) (runPatchIndex opts) (usePacks opts)- (not $ null [p | UpToPattern p <- opts] ) -- --to-match given YesForgetParent setCurrentDirectory currentDir (scp, args) <- getSSH SCP@@ -253,7 +253,6 @@ (withWorkingDir opts) (runPatchIndex opts) (usePacks opts)- (not $ null [p | UpToPattern p <- opts] ) -- --to-match given NoForgetParent putInfo opts $ text "Finished cloning." @@ -302,7 +301,7 @@ , "`--to-match` options, which exclude patches *after* the first matching" , "patch. Because these options treat the set of patches as an ordered" , "sequence, you may get different results after reordering with `darcs"- , "optimize`, so tagging is preferred."+ , "optimize reorder`." , "" ] @@ -323,7 +322,7 @@ exists <- doesFileExist ctxFilePath if exists then do- (ps :: PatchSet DummyPatch Origin wX) <-+ (ps :: PatchSet rt DummyPatch Origin wX) <- scanContextFile ctxFilePath (ps `seq` return $ Right ()) `catch` \(_ :: SomeException) -> return . Left $ "File " ++ ctxFilePath
src/Darcs/UI/Commands/Convert.hs view
@@ -15,11 +15,12 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP, MagicHash, OverloadedStrings, DeriveDataTypeable #-}+{-# LANGUAGE CPP, MagicHash, OverloadedStrings #-} module Darcs.UI.Commands.Convert ( convert ) where -import Prelude hiding ( (^), readFile, log, lex )+import Prelude ( lookup )+import Darcs.Prelude hiding ( readFile, lex ) import System.FilePath.Posix ( (</>) ) import System.Directory ( setCurrentDirectory, doesDirectoryExist, doesFileExist,@@ -27,12 +28,12 @@ import System.IO ( stdin ) import Data.IORef ( newIORef, modifyIORef, readIORef ) import Data.Char ( isSpace )-import Control.Arrow ( (&&&) )+import Control.Arrow ( second, (&&&) ) import Control.Monad ( when, unless, void, forM_ ) import Control.Monad.Trans ( liftIO ) import Control.Monad.State.Strict ( gets, modify ) import Control.Exception ( finally )-import Control.Applicative ( (<|>), (<$>) )+import Control.Applicative ( (<|>) ) import GHC.Base ( unsafeCoerce# ) import System.Time ( toClockTime )@@ -48,21 +49,21 @@ import qualified Data.Attoparsec.ByteString.Char8 as A import Data.Attoparsec.ByteString.Char8( (<?>) ) -import qualified Storage.Hashed.Tree as T-import qualified Storage.Hashed.Monad as TM-import Storage.Hashed.Monad hiding ( createDirectory, exists, rename )-import Storage.Hashed.Darcs ( hashedTreeIO, darcsAddMissingHashes )-import Storage.Hashed.Tree( Tree, treeHash, readBlob, TreeItem(..)- , emptyTree, listImmediate, findTree )-import Storage.Hashed.AnchoredPath( anchorPath, appendPath, floatPath- , parent, anchoredRoot- , AnchoredPath(..), Name(..) )-import Storage.Hashed.Hash( encodeBase16, sha256, Hash(..) )+import qualified Darcs.Util.Tree as T+import qualified Darcs.Util.Tree.Monad as TM+import Darcs.Util.Tree.Monad hiding ( createDirectory, exists, rename )+import Darcs.Util.Tree.Hashed ( hashedTreeIO, darcsAddMissingHashes )+import Darcs.Util.Tree( Tree, treeHash, readBlob, TreeItem(..)+ , emptyTree, listImmediate, findTree )+import Darcs.Util.Path( anchorPath, appendPath, floatPath+ , parent, anchoredRoot+ , AnchoredPath(..), Name(..)+ , ioAbsoluteOrRemote, toPath, AbsolutePath )+import Darcs.Util.Hash( encodeBase16, sha256, Hash(..) ) import Darcs.Util.DateTime ( formatDateTime, fromClockTime, parseDateTime, startOfTime ) import Darcs.Util.Global ( darcsdir ) import Darcs.Util.File ( withCurrentDirectory )-import Darcs.Util.Path ( ioAbsoluteOrRemote, toPath, AbsolutePath ) import Darcs.Util.Exception ( clarifyErrors ) import Darcs.Util.Prompt ( askUser ) import Darcs.Util.Printer ( text, ($$) )@@ -71,35 +72,45 @@ import Darcs.Patch.Depends ( getUncovered ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, n2pia, info, hopefully )-import Darcs.Patch ( Named, showPatch, patch2patchinfo, fromPrim, fromPrims,- infopatch, adddeps, getdeps, effect, patchcontents,- RepoPatch, apply, listTouchedFiles )+import Darcs.Patch ( IsRepoType, showPatch, fromPrim, fromPrims,+ effect,+ RepoPatch, apply, listTouchedFiles+ , move ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Effect ( Effect )+import Darcs.Patch.Named+ ( patch2patchinfo+ , infopatch, adddeps, getdeps, patchcontents+ )+import Darcs.Patch.Named.Wrapped ( WrappedNamed(..) )+import qualified Darcs.Patch.Named.Wrapped as Wrapped ( getdeps ) import Darcs.Patch.Witnesses.Eq ( EqCheck(..), (=/\=) ) import Darcs.Patch.Witnesses.Ordered ( FL(..), RL(..), bunchFL, mapFL, mapFL_FL,- concatFL, mapRL, nullFL, (+>+) )+ concatFL, mapRL, nullFL, (+>+), (+<+)+ , reverseRL, reverseFL ) import Darcs.Patch.Witnesses.Sealed ( FlippedSeal(..), Sealed(..), unFreeLeft , flipSeal, unsafeUnsealFlipped ) import Darcs.Patch.Info ( piRename, piTag, isTag, PatchInfo, patchinfo, piName, piLog, piDate, piAuthor, makePatchname )-import Darcs.Patch.V1 ( Patch )-import Darcs.Patch.V2 ( RealPatch )+import Darcs.Patch.V1 ( RepoPatchV1 )+import Darcs.Patch.V2 ( RepoPatchV2 ) import Darcs.Patch.V1.Commute ( publicUnravel )-import Darcs.Patch.V1.Core ( Patch(PP), isMerger )-import Darcs.Patch.V2.Real ( mergeUnravelled )+import Darcs.Patch.V1.Core ( RepoPatchV1(PP), isMerger )+import Darcs.Patch.V2.RepoPatch ( mergeUnravelled ) import Darcs.Patch.Prim ( sortCoalesceFL )+import Darcs.Patch.Prim.Class ( PrimOf ) import Darcs.Patch.Prim.V1 ( Prim )+import Darcs.Patch.RepoType ( RepoType(..), RebaseType(..) ) import Darcs.Patch.Set ( PatchSet(..), Tagged(..), newset2RL, newset2FL ) import Darcs.Patch.Progress ( progressFL ) import Darcs.Repository.Flags ( UpdateWorking(..), Reorder (..), UseIndex(..), ScanKnown(..)- , AllowConflicts(..), ExternalMerge(..), WantGuiPause(..)+ , AllowConflicts(..), ExternalMerge(..), WantGuiPause(..), PatchFormat(..) , Compression(..), DryRun(NoDryRun), DiffAlgorithm(MyersDiff, PatienceDiff) ) import Darcs.Repository ( Repository, withRepoLock, RepoJob(..), withRepositoryDirectory, createRepository, invalidateIndex,- tentativelyMergePatches, patchSetToPatches,+ tentativelyMergePatches, createPristineDirectoryTree, revertRepositoryChanges, finalizeRepositoryChanges, applyToWorking@@ -113,8 +124,8 @@ import Darcs.Repository.Prefs( FileType(..) ) import Darcs.Repository.Format(identifyRepoFormat, formatHas, RepoProperty(Darcs2)) import Darcs.Repository.Motd ( showMotd )-import Darcs.Repository.Lock ( writeBinFile )-import Darcs.Repository.External ( fetchFilePS, Cachable(Uncachable) )+import Darcs.Util.Lock ( writeBinFile )+import Darcs.Util.External ( fetchFilePS, Cachable(Uncachable) ) import Darcs.Repository.Diff( treeDiff ) @@ -126,6 +137,7 @@ ) import Darcs.UI.Commands ( DarcsCommand(..), amInRepository, nodefaults, putInfo , normalCommand, withStdOpts )+import Darcs.UI.Commands.Util.Tree ( treeHasDir, treeHasFile ) import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags, parseFlags ) import qualified Darcs.UI.Options.All as O @@ -191,7 +203,7 @@ , "Further options are accepted (see `darcs help init`)." , "" , "To convert a git repo to a new darcs one you may run:"- , " $ (cd gitrepo && git fast-export --all) | darcs convert import darcsmirror"+ , " $ (cd gitrepo && git fast-export --all -M) | darcs convert import darcsmirror" , "" , "WARNING: git repositories with branches will produce weird results," , " use at your own risks."@@ -377,20 +389,20 @@ mysimplename <- makeRepoName opts repodir createDirectory mysimplename setCurrentDirectory mysimplename- createRepository False (withWorkingDir opts) (runPatchIndex opts)+ createRepository PatchFormat2 (withWorkingDir opts) (runPatchIndex opts) writeBinFile (darcsdir++"/hashed_inventory") "" withRepoLock NoDryRun (useCache opts) NoUpdateWorking (umask opts) $ V2Job $ \repository -> withRepositoryDirectory (useCache opts) repodir $ V1Job $ \themrepo -> do theirstuff <- readRepo themrepo- let patches = mapFL_FL convertNamed $ patchSetToPatches theirstuff+ let patches = mapFL_FL (convertNamed . hopefully) $ newset2FL theirstuff outOfOrderTags = catMaybes $ mapRL oot $ newset2RL theirstuff where oot t = if isTag (info t) && info t `notElem` inOrderTags theirstuff- then Just (info t, getdeps $ hopefully t)+ then Just (info t, Wrapped.getdeps $ hopefully t) else Nothing fixDep p = case lookup p outOfOrderTags of Just d -> p : concatMap fixDep d Nothing -> [p]- convertOne :: Patch Prim wX wY -> FL (RealPatch Prim) wX wY+ convertOne :: RepoPatchV1 Prim wX wY -> FL (RepoPatchV2 Prim) wX wY convertOne x | isMerger x = case mergeUnravelled $ publicUnravel x of Just (FlippedSeal y) -> case effect y =/\= effect x of@@ -405,10 +417,12 @@ fromPrims (effect x) convertOne (PP x) = fromPrim x :>: NilFL convertOne _ = impossible- convertFL :: FL (Patch Prim) wX wY -> FL (RealPatch Prim) wX wY+ convertFL :: FL (RepoPatchV1 Prim) wX wY -> FL (RepoPatchV2 Prim) wX wY convertFL = concatFL . mapFL_FL convertOne- convertNamed :: Named (Patch Prim) wX wY -> PatchInfoAnd (RealPatch Prim) wX wY- convertNamed n = n2pia $+ convertNamed :: WrappedNamed ('RepoType 'NoRebase) (RepoPatchV1 Prim) wX wY+ -> PatchInfoAnd ('RepoType 'NoRebase) (RepoPatchV2 Prim) wX wY+ convertNamed (NormalP n)+ = n2pia $ NormalP $ adddeps (infopatch (convertInfo $ patch2patchinfo n) $ convertFL $ patchcontents n) (map convertInfo $ concatMap fixDep $ getdeps n)@@ -417,7 +431,7 @@ applySome xs = do -- TODO this unsafeCoerce hack is because we don't keep track of the repository state properly -- Really sequence_ $ mapFL applySome below should instead be a repeated add operation - -- there doesn't seem to be any reason we need to do a merge here.- let repository2 = unsafeCoerce# repository :: Repository (RealPatch Prim) wA wB wA+ let repository2 = unsafeCoerce# repository :: Repository ('RepoType 'NoRebase) (RepoPatchV2 Prim) wA wB wA Sealed pw <- tentativelyMergePatches repository2 "convert" YesAllowConflicts NoUpdateWorking NoExternalMerge NoWantGuiPause@@ -494,27 +508,28 @@ Nothing -> return () Just f -> writeMarks f newMarks -fastExport' :: (RepoPatch p, ApplyState p ~ Tree) => Repository p r u r -> Marks -> IO Marks+fastExport' :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p r u r -> Marks -> IO Marks fastExport' repo marks = do putStrLn "progress (reading repository)" patchset <- readRepo repo marksref <- newIORef marks let patches = newset2FL patchset tags = inOrderTags patchset- mark :: (PatchInfoAnd p) x y -> Int -> TreeIO ()+ mark :: (PatchInfoAnd rt p) x y -> Int -> TreeIO () mark p n = liftIO $ do putStrLn $ "mark :" ++ show n modifyIORef marksref $ \m -> addMark m n (patchHash p) -- apply a single patch to build the working tree of the last exported version checkOne :: (RepoPatch p, ApplyState p ~ Tree)- => Int -> (PatchInfoAnd p) x y -> TreeIO ()+ => Int -> (PatchInfoAnd rt p) x y -> TreeIO () checkOne n p = do apply p unless (inOrderTag tags p || (getMark marks n == Just (patchHash p))) $ fail $ "FATAL: Marks do not correspond: expected " ++- (show $ getMark marks n) ++ ", got " ++ (BC.unpack $ patchHash p)+ show (getMark marks n) ++ ", got " ++ BC.unpack (patchHash p) -- build the working tree of the last version exported by convert --export check :: (RepoPatch p, ApplyState p ~ Tree)- => Int -> FL (PatchInfoAnd p) x y -> TreeIO (Int, FlippedSeal( FL (PatchInfoAnd p)) y) + => Int -> FL (PatchInfoAnd rt p) x y -> TreeIO (Int, FlippedSeal( FL (PatchInfoAnd rt p)) y) check _ NilFL = return (1, flipSeal NilFL) check n allps@(p:>:ps) | n <= lastMark marks = checkOne n p >> check (next tags n p) ps@@ -533,8 +548,8 @@ dumpPatches :: (RepoPatch p, ApplyState p ~ Tree) => [PatchInfo]- -> (forall p0 x0 y0 . (PatchInfoAnd p0) x0 y0 -> Int -> TreeIO ())- -> Int -> FL (PatchInfoAnd p) x y -> TreeIO ()+ -> (forall p0 x0 y0 . (PatchInfoAnd rt p0) x0 y0 -> Int -> TreeIO ())+ -> Int -> FL (PatchInfoAnd rt p) x y -> TreeIO () dumpPatches _ _ _ NilFL = liftIO $ putStrLn "progress (patches converted)" dumpPatches tags mark n (p:>:ps) = do apply p@@ -544,7 +559,7 @@ dumpFiles $ map floatPath $ listTouchedFiles p dumpPatches tags mark (next tags n p) ps -dumpTag :: (PatchInfoAnd p) x y -> Int -> TreeIO () +dumpTag :: (PatchInfoAnd rt p) x y -> Int -> TreeIO () dumpTag p n = dumpBits [ BLU.fromString $ "progress TAG " ++ cleanTagName p , BLU.fromString $ "tag " ++ cleanTagName p -- FIXME is this valid?@@ -604,8 +619,8 @@ _ -> ([c], False) -dumpPatch :: (forall p0 x0 y0 . (PatchInfoAnd p0) x0 y0 -> Int -> TreeIO ())- -> (PatchInfoAnd p) x y -> Int+dumpPatch :: (forall p0 x0 y0 . (PatchInfoAnd rt p0) x0 y0 -> Int -> TreeIO ())+ -> (PatchInfoAnd rt p) x y -> Int -> TreeIO () dumpPatch mark p n = do dumpBits [ BLC.pack $ "progress " ++ show n ++ ": " ++ piName (info p)@@ -627,7 +642,7 @@ -- john <john@home> -> john <john@home> -- john <john@home -> john <john@home> -- <john> -> john <unknown>-patchAuthor :: (PatchInfoAnd p) x y -> String+patchAuthor :: (PatchInfoAnd rt p) x y -> String patchAuthor p | null author = unknownEmail "unknown" | otherwise = case span (/='<') author of@@ -652,11 +667,11 @@ emailPad email = "<" ++ email ++ ">" mkAuthor name email = name ++ " " ++ email -patchDate :: (PatchInfoAnd p) x y -> String+patchDate :: (PatchInfoAnd rt p) x y -> String patchDate = formatDateTime "%s +0000" . fromClockTime . toClockTime . piDate . info -patchMessage :: (PatchInfoAnd p) x y -> BLU.ByteString+patchMessage :: (PatchInfoAnd rt p) x y -> BLU.ByteString patchMessage p = BL.concat [ BLU.fromString (piName $ info p) , case unlines . piLog $ info p of "" -> BL.empty@@ -672,12 +687,21 @@ data RefId = MarkId Int | HashId B.ByteString | Inline deriving Show +-- Newish (> 1.7.6.1) Git either quotes filenames or has two+-- non-special-char-containing paths. Older git doesn't do any quoting, so+-- we'll have to manually try and find the correct paths, when we use the+-- paths.+data CopyRenameNames = Quoted B.ByteString B.ByteString+ | Unquoted B.ByteString deriving Show+ data Object = Blob (Maybe Int) Content | Reset Branch (Maybe RefId) | Commit Branch Marked AuthorInfo Message | Tag Int AuthorInfo Message | Modify (Either Int Content) B.ByteString -- (mark or content), filename | Gitlink B.ByteString+ | Copy CopyRenameNames+ | Rename CopyRenameNames | Delete B.ByteString -- filename | From Int | Merge Int@@ -686,20 +710,21 @@ deriving Show type Ancestors = (Marked, [Int])-data State = Toplevel Marked Branch- | InCommit Marked Ancestors Branch (Tree IO) PatchInfo- | Done+data State p where+ Toplevel :: Marked -> Branch -> State p+ InCommit :: Marked -> Ancestors -> Branch -> Tree IO -> RL (PrimOf p) cX cY -> PatchInfo -> State p+ Done :: State p -instance Show State where- show (Toplevel _ _) = "Toplevel"- show (InCommit _ _ _ _ _) = "InCommit"+instance Show (State p) where+ show Toplevel {} = "Toplevel"+ show InCommit {} = "InCommit" show Done = "Done" fastImport :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () fastImport _ opts [outrepo] = do createDirectory outrepo withCurrentDirectory outrepo $ do- createRepository (patchFormat opts == O.PatchFormat1) (withWorkingDir opts) (runPatchIndex opts)+ createRepository (patchFormat opts) (withWorkingDir opts) (runPatchIndex opts) withRepoLock NoDryRun (useCache opts) NoUpdateWorking (umask opts) $ RepoJob $ \repo -> do -- TODO implement --dry-run, which would be read-only? marks <- fastImport' repo emptyMarks@@ -707,14 +732,14 @@ return marks fastImport _ _ _ = fail "I need exactly one output repository." -fastImport' :: forall p r u . (RepoPatch p, ApplyState p ~ Tree) =>- Repository p r u r -> Marks -> IO ()+fastImport' :: forall rt p r u . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) =>+ Repository rt p r u r -> Marks -> IO () fastImport' repo marks = do pristine <- readRecorded repo marksref <- newIORef marks let initial = Toplevel Nothing $ BC.pack "refs/branches/master" - go :: State -> B.ByteString -> TreeIO ()+ go :: State p -> B.ByteString -> TreeIO () go state rest = do (rest', item) <- parseObject rest state' <- process state item case state' of@@ -734,9 +759,7 @@ (author'', date'') = span (/='>') $ BC.unpack author date' = dropWhile (`notElem` ("0123456789" :: String)) date'' author' = author'' ++ ">"- date = formatDateTime "%Y%m%d%H%M%S" $ case (parseDateTime "%s %z" date') of- Just x -> x- Nothing -> startOfTime+ date = formatDateTime "%Y%m%d%H%M%S" $ fromMaybe startOfTime (parseDateTime "%s %z" date') liftIO $ patchinfo date (if tag then "TAG " ++ name else name) author' log addtag author msg =@@ -744,14 +767,14 @@ gotany <- liftIO $ doesFileExist $ darcsdir </> "tentative_hashed_pristine" deps <- if gotany then liftIO $ getUncovered `fmap` readTentativeRepo repo else return []- let ident = NilFL :: FL (RealPatch Prim) cX cX- patch = adddeps (infopatch info_ ident) deps+ let ident = NilFL :: FL (RepoPatchV2 Prim) cX cX+ patch = NormalP (adddeps (infopatch info_ ident) deps) void $ liftIO $ addToTentativeInventory (extractCache repo) GzipCompression (n2pia patch) -- processing items updateHashes = do- let nodarcs = (\(AnchoredPath (Name x:_)) _ -> x /= BC.pack darcsdir)+ let nodarcs = \(AnchoredPath (Name x:_)) _ -> x /= BC.pack darcsdir hashblobs (File blob@(T.Blob con NoHash)) = do hash <- sha256 `fmap` readBlob blob return $ File (T.Blob con hash)@@ -774,7 +797,17 @@ -- Either missing (not possible) or non-empty. _ -> return () - process :: State -> Object -> TreeIO State+ -- generate a Hunk primitive patch from diffing+ diffCurrent :: State p -> TreeIO (State p)+ diffCurrent (InCommit mark ancestors branch start ps info_) = do+ current <- updateHashes+ Sealed diff <- unFreeLeft `fmap`+ liftIO (treeDiff PatienceDiff (const TextFile) start current)+ let newps = ps +<+ reverseFL diff+ return $ InCommit mark ancestors branch current newps info_+ diffCurrent _ = error "This is never valid outside of a commit."++ process :: State p -> Object -> TreeIO (State p) process s (Progress p) = do liftIO $ putStrLn ("progress " ++ BC.unpack p) return s@@ -784,7 +817,7 @@ modify $ \s -> s { tree = tree' } -- lets dump the right tree, without _darcs let root = encodeBase16 $ treeHash tree' liftIO $ do- putStrLn $ "\\o/ It seems we survived. Enjoy your new repo."+ putStrLn "\\o/ It seems we survived. Enjoy your new repo." B.writeFile (darcsdir </> "tentative_pristine") $ BC.concat [BC.pack "pristine:", root] return Done@@ -793,7 +826,7 @@ if Just what == n then addtag author msg else liftIO $ putStrLn $ "WARNING: Ignoring out-of-order tag " ++- (head $ lines $ BC.unpack msg)+ head (lines $ BC.unpack msg) return (Toplevel n b) process (Toplevel n _) (Reset branch from) =@@ -805,7 +838,7 @@ return $ Toplevel n branch process (Toplevel n b) (Blob (Just m) bits) = do- TM.writeFile (markpath m) $ (BLC.fromChunks [bits])+ TM.writeFile (markpath m) (BLC.fromChunks [bits]) return $ Toplevel n b process x (Gitlink link) = do@@ -818,29 +851,61 @@ addtag author pbranch info_ <- makeinfo author message False startstate <- updateHashes- return $ InCommit mark (previous, []) branch startstate info_+ return $ InCommit mark (previous, []) branch startstate NilRL info_ - process s@(InCommit _ _ _ _ _) (Modify (Left m) path) = do+ process s@InCommit {} (Modify (Left m) path) = do TM.copy (markpath m) (floatPath $ BC.unpack path)- return s+ diffCurrent s - process s@(InCommit _ _ _ _ _) (Modify (Right bits) path) = do+ process s@InCommit {} (Modify (Right bits) path) = do TM.writeFile (floatPath $ BC.unpack path) (BLC.fromChunks [bits])- return s+ diffCurrent s - process s@(InCommit _ _ _ _ _) (Delete path) = do- let floatedPath = floatPath . BC.unpack $ path+ process s@InCommit {} (Delete path) = do+ let floatedPath = floatPath $ BC.unpack path TM.unlink floatedPath deleteEmptyParents floatedPath- return s+ diffCurrent s - process (InCommit mark (prev, current) branch start info_) (From from) = do- return $ InCommit mark (prev, from:current) branch start info_+ process (InCommit mark (prev, current) branch start ps info_) (From from) =+ return $ InCommit mark (prev, from:current) branch start ps info_ - process (InCommit mark (prev, current) branch start info_) (Merge from) = do- return $ InCommit mark (prev, from:current) branch start info_+ process (InCommit mark (prev, current) branch start ps info_) (Merge from) =+ return $ InCommit mark (prev, from:current) branch start ps info_ - process (InCommit mark ancestors branch start info_) x = do+ process s@InCommit {} (Copy names) = do+ (from, to) <- extractNames names+ TM.copy (floatPath $ BC.unpack from) (floatPath $ BC.unpack to)+ -- We can't tell Darcs that a file has been copied, so it'll+ -- show as an addfile.+ diffCurrent s++ process s@(InCommit mark ancestors branch start _ info_) (Rename names) = do+ (from, to) <- extractNames names+ let uFrom = BC.unpack from+ uTo = BC.unpack to+ parentDir = parent $ floatPath uTo+ targetDirExists <- liftIO $ treeHasDir start uTo+ targetFileExists <- liftIO $ treeHasFile start uTo+ parentDirExists <-+ liftIO $ treeHasDir start (anchorPath "" parentDir)+ -- If the target exists, remove it; if it doesn't, add all+ -- its parent directories.+ if targetDirExists || targetFileExists+ then TM.unlink $ floatPath uTo+ else unless parentDirExists $ TM.createDirectory parentDir+ (InCommit _ _ _ _ newPs _) <- diffCurrent s+ TM.rename (floatPath uFrom) (floatPath uTo)+ let ps' = newPs :<: move uFrom uTo+ current <- updateHashes+ -- ensure empty dirs get deleted+ deleteEmptyParents (floatPath uFrom)+ -- run diffCurrent to add the dir deletions prims+ diffCurrent (InCommit mark ancestors branch current ps' info_)++ -- When we leave the commit, create a patch for the cumulated+ -- prims.+ process (InCommit mark ancestors branch _ ps info_) x = do case ancestors of (_, []) -> return () -- OK, previous commit is the ancestor (Just n, list)@@ -851,11 +916,9 @@ (Nothing, list) -> liftIO $ putStrLn $ "WARNING: Linearising non-linear ancestry " ++ show list - current <- updateHashes- Sealed diff- <- unFreeLeft `fmap` (liftIO $ treeDiff PatienceDiff (const TextFile) start current)- prims <- return $ fromPrims $ sortCoalesceFL diff- let patch = infopatch info_ ((NilFL :: FL p cX cX) +>+ prims)+ {- current <- updateHashes -} -- why not?+ (prims :: FL p cX cY) <- return $ fromPrims $ sortCoalesceFL $ reverseRL ps+ let patch = NormalP (infopatch info_ ((NilFL :: FL p cX cX) +>+ prims)) void $ liftIO $ addToTentativeInventory (extractCache repo) GzipCompression (n2pia patch) case mark of@@ -869,6 +932,36 @@ liftIO $ print obj fail $ "Unexpected object in state " ++ show state + extractNames :: CopyRenameNames+ -> TreeIO (BC.ByteString, BC.ByteString)+ extractNames names = case names of+ Quoted f t -> return (f, t)+ Unquoted uqNames -> do+ let spaceIndices = BC.elemIndices ' ' uqNames+ splitStr = second (BC.drop 1) . flip BC.splitAt uqNames+ -- Reverse the components, so we find the longest+ -- prefix existing name.+ spaceComponents = reverse $ map splitStr spaceIndices+ componentCount = length spaceComponents+ if componentCount == 1+ then return $ head spaceComponents+ else do+ let dieMessage = unwords+ [ "Couldn't determine move/rename"+ , "source/destination filenames, with the"+ , "data produced by this (old) version of"+ , "git, since it uses unquoted, but"+ , "special-character-containing paths."+ ]+ floatUnpack = floatPath . BC.unpack+ lPathExists (l,_) =+ TM.fileExists $ floatUnpack l+ finder [] = error dieMessage+ finder (x : rest) = do+ xExists <- lPathExists x+ if xExists then return x else finder rest+ finder spaceComponents+ void $ hashedTreeIO (go initial B.empty) pristine $ darcsdir </> "pristine.hashed" finalizeRepositoryChanges repo YesUpdateWorking GzipCompression cleanRepository repo@@ -891,6 +984,8 @@ <|> p_commit <|> p_tag <|> p_modify+ <|> p_rename+ <|> p_copy <|> p_from <|> p_merge <|> p_delete@@ -942,6 +1037,12 @@ p_from = lexString "from" >> From `fmap` p_marked p_merge = lexString "merge" >> Merge `fmap` p_marked p_delete = lexString "D" >> Delete `fmap` p_maybeQuotedName+ p_rename = do lexString "R"+ names <- p_maybeQuotedCopyRenameNames+ return $ Rename names+ p_copy = do lexString "C"+ names <- p_maybeQuotedCopyRenameNames+ return $ Copy names p_modify = do lexString "M" mode <- lex $ A.takeWhile (A.inClass "01234567890") mark <- p_refid@@ -952,6 +1053,12 @@ MarkId n -> return $ Modify (Left n) path Inline -> do bits <- p_data return $ Modify (Right bits) path+ p_maybeQuotedCopyRenameNames =+ p_lexTwoQuotedNames <|> Unquoted `fmap` line+ p_lexTwoQuotedNames = do+ n1 <- lex p_quotedName+ n2 <- lex p_quotedName+ return $ Quoted n1 n2 p_maybeQuotedName = lex (p_quotedName <|> line) p_quotedName = do _ <- A.char '"'@@ -979,19 +1086,19 @@ fail $ "Error parsing stream. " ++ err ++ "\nContext: " ++ show ctx -patchHash :: PatchInfoAnd p cX cY -> BC.ByteString+patchHash :: PatchInfoAnd rt p cX cY -> BC.ByteString patchHash p = BC.pack $ show $ makePatchname (info p) -inOrderTag :: (Effect p) => [PatchInfo] -> PatchInfoAnd p wX wZ -> Bool+inOrderTag :: (Effect p) => [PatchInfo] -> PatchInfoAnd rt p wX wZ -> Bool inOrderTag tags p = isTag (info p) && info p `elem` tags && nullFL (effect p) -next :: (Effect p) => [PatchInfo] -> Int -> PatchInfoAnd p x y -> Int+next :: (Effect p) => [PatchInfo] -> Int -> PatchInfoAnd rt p x y -> Int next tags n p = if inOrderTag tags p then n else n + 1 -inOrderTags :: PatchSet p wS wX -> [PatchInfo]-inOrderTags (PatchSet _ ts) = go ts- where go :: RL(Tagged t1) wT wY -> [PatchInfo]- go (Tagged t _ _ :<: ts') = info t : go ts'+inOrderTags :: PatchSet rt p wS wX -> [PatchInfo]+inOrderTags (PatchSet ts _) = go ts+ where go :: RL(Tagged rt t1) wT wY -> [PatchInfo]+ go (ts' :<: Tagged t _ _) = info t : go ts' go NilRL = [] type Marks = M.IntMap BC.ByteString@@ -1012,7 +1119,7 @@ readMarks p = do lines' <- BC.split '\n' `fmap` BC.readFile p return $ foldl merge M.empty lines' `catchall` return emptyMarks- where merge set line = case (BC.split ':' line) of+ where merge set line = case BC.split ':' line of [i, hash] -> M.insert (read $ BC.unpack i) (BC.dropWhile (== ' ') hash) set _ -> set -- ignore, although it is maybe not such a great idea...
src/Darcs/UI/Commands/Diff.hs view
@@ -19,15 +19,17 @@ module Darcs.UI.Commands.Diff ( diffCommand, getDiffDoc ) where -import Prelude hiding ( (^), all )+import Prelude ()+import Darcs.Prelude hiding ( all )+ import System.FilePath.Posix ( takeFileName, (</>) ) import Darcs.Util.Workaround ( getCurrentDirectory ) import Darcs.Util.File ( withCurrentDirectory ) import Darcs.Util.Prompt ( askEnter ) import Control.Monad ( when ) import Data.List ( (\\) )-import Storage.Hashed.Plain( writePlainTree )-import Storage.Hashed.Darcs( hashedTreeIO )+import Darcs.Util.Tree.Plain( writePlainTree )+import Darcs.Util.Tree.Hashed( hashedTreeIO ) import Data.Maybe ( isJust ) import System.Directory ( findExecutable ) @@ -59,11 +61,12 @@ import Darcs.Patch.Witnesses.Ordered ( mapRL, (:>)(..), (+>+), RL(..) ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoercePEnd ) import Darcs.Patch.Witnesses.Sealed ( unseal, Sealed(..), seal )-import Darcs.Patch ( RepoPatch, apply, listTouchedFiles, invert, fromPrims, anonymous )+import Darcs.Patch ( RepoPatch, IsRepoType, apply, listTouchedFiles, invert, fromPrims ) import Darcs.Patch.Depends ( findCommonWithThem )+import Darcs.Patch.Named.Wrapped ( anonymous ) import Darcs.Patch.Set ( PatchSet(..), newset2RL ) import Darcs.Patch.Info ( PatchInfo, showPatchInfoUI )-import Darcs.Repository.Lock ( withTempDir )+import Darcs.Util.Lock ( withTempDir ) import Darcs.Util.Printer ( Doc, putDoc, vcat, empty, RenderMode(..), ($$) ) #include "impossible.h"@@ -91,18 +94,21 @@ "The associated tag and match options are also understood, e.g. `darcs\n" ++ "diff --from-tag 1.0 --to-tag 1.1`. All these options assume an\n" ++ "ordering of the patch set, so results may be affected by operations\n" ++- "such as `darcs optimize --reorder`.\n" +++ "such as `darcs optimize reorder`.\n" ++ "\n" ++ "diff(1) is called with the arguments `-rN`. The `--unified` option causes\n" ++ "`-u` to be passed to diff(1). An additional argument can be passed\n" ++ "using `--diff-opts`, such as `--diff-opts=-ud` or `--diff-opts=-wU9`.\n" ++ "\n" ++ "The `--diff-command` option can be used to specify an alternative\n" ++- "utility, such as meld (GNOME) or opendiff (OS X). Arguments may be\n" ++- "included, separated by whitespace. The value is not interpreted by a\n" ++- "shell, so shell constructs cannot be used. The arguments %1 and %2\n" ++- "MUST be included, these are substituted for the two working trees\n" ++- "being compared. If this option is used, `--diff-opts` is ignored.\n"+ "utility. Arguments may be included, separated by whitespace. The value\n" +++ "is not interpreted by a shell, so shell constructs cannot be used. The\n" +++ "arguments %1 and %2 MUST be included, these are substituted for the two\n" +++ "working trees being compared. For instance:\n" +++ "\n" +++ " darcs diff -p . --diff-command \"meld %1 %2\"\n" +++ "\n" +++ "If this option is used, `--diff-opts` is ignored.\n" diffBasicOpts :: DarcsOption a ([O.MatchFlag]@@ -209,7 +215,7 @@ let matchFlags = parseFlags O.matchRange opts Sealed all <- return $ case (secondMatch matchFlags, patchset) of (True, _) -> seal patchset- (False, PatchSet untagged tagged) -> seal $ PatchSet (unrecorded' :<: untagged) tagged+ (False, PatchSet tagged untagged) -> seal $ PatchSet tagged (untagged :<: unrecorded') Sealed ctx <- return $ if firstMatch matchFlags then matchFirstPatchset matchFlags patchset@@ -269,7 +275,7 @@ askEnter "Hit return to move on..." return output -getDiffInfo :: RepoPatch p => [DarcsFlag] -> PatchSet p wStart wX -> [PatchInfo]+getDiffInfo :: (IsRepoType rt, RepoPatch p) => [DarcsFlag] -> PatchSet rt p wStart wX -> [PatchInfo] getDiffInfo opts ps = let matchFlags = parseFlags O.matchRange opts infos = mapRL info . newset2RL
src/Darcs/UI/Commands/Dist.hs view
@@ -30,7 +30,8 @@ , doFastZip' ) where -import Prelude hiding ( (^), writeFile )+import Prelude ()+import Darcs.Prelude hiding ( writeFile ) import Data.ByteString.Lazy ( writeFile ) import Data.Char ( isAlphaNum )@@ -46,7 +47,7 @@ import Codec.Compression.GZip ( compress ) import Codec.Archive.Zip ( emptyArchive, fromArchive, addEntryToArchive, toEntry )-import Darcs.Repository.External ( fetchFilePS, Cachable( Uncachable ) )+import Darcs.Util.External ( fetchFilePS, Cachable( Uncachable ) ) import Darcs.Util.Global ( darcsdir ) import Darcs.Repository.HashedRepo ( inv2pris ) import Darcs.Repository.HashedIO ( pathsAndContents )@@ -62,15 +63,9 @@ import qualified Darcs.UI.Options.All as O import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository )-import Darcs.Repository.Lock ( withTempDir )-import Darcs.Patch.Match- ( haveNonrangeMatch- , firstMatch- )-import Darcs.Repository.Match- ( getFirstMatch- , getNonrangeMatch- )+import Darcs.Util.Lock ( withTempDir )+import Darcs.Patch.Match ( haveNonrangeMatch )+import Darcs.Repository.Match ( getNonrangeMatch ) import Darcs.Repository ( withRepository, withRepositoryDirectory, RepoJob(..), setScriptsExecutable, repoPatchType, createPartialsPristineDirectoryTree )@@ -116,7 +111,7 @@ = O.distname ^ O.distzip ^ O.workingRepoDir- ^ O.matchOne+ ^ O.matchUpToOne ^ O.setScriptsExecutable ^ O.storeInMemory @@ -165,7 +160,7 @@ -> IO () distCmd _ opts _ | DistZip `elem` opts = doFastZip opts distCmd _ opts _ = withRepository (useCache opts) $ RepoJob $ \repository -> do- let matchFlags = parseFlags O.matchOne opts+ let matchFlags = parseFlags O.matchUpToOne opts formerdir <- getCurrentDirectory let distname = getDistName formerdir [x | DistName x <- opts] predist <- getPrefval "predist"@@ -174,10 +169,7 @@ setCurrentDirectory formerdir withTempDir (toFilePath tempdir </> takeFileName distname) $ \ddir -> do if haveNonrangeMatch (repoPatchType repository) matchFlags- then- if firstMatch matchFlags- then withCurrentDirectory ddir $ getFirstMatch repository matchFlags- else withCurrentDirectory ddir $ getNonrangeMatch repository matchFlags+ then withCurrentDirectory ddir $ getNonrangeMatch repository matchFlags else createPartialsPristineDirectoryTree repository [""] (toFilePath ddir) ec <- case predist of Nothing -> return ExitSuccess Just pd -> system pd
src/Darcs/UI/Commands/GZCRCs.hs view
@@ -27,6 +27,9 @@ , doCRCWarnings ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( (^) ) import Control.Monad ( when, unless, forM_ ) import Control.Monad.Trans ( liftIO )@@ -49,7 +52,7 @@ import Darcs.Repository.Cache ( Cache(..), writable, isThisRepo, hashedFilePath, allHashedDirs ) import Darcs.Repository.InternalTypes ( extractCache )-import Darcs.Repository.Lock ( gzWriteAtomicFilePSs )+import Darcs.Util.Lock ( gzWriteAtomicFilePSs ) import Darcs.Patch ( RepoPatch ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInRepository ) import Darcs.UI.Options ( DarcsOption, (^), oid, odesc, ocheck, onormalise, defaultFlags )@@ -153,7 +156,7 @@ withRepository (useCache opts) (RepoJob (gzcrcs' opts)) gzcrcsCmd _ _ _ = error "You must specify --check or --repair for gzcrcs" -gzcrcs' :: (RepoPatch p) => [DarcsFlag] -> Repository p wR wU wT -> IO ()+gzcrcs' :: (RepoPatch p) => [DarcsFlag] -> Repository rt p wR wU wT -> IO () gzcrcs' opts repo = do -- Somewhat ugly IORef use here because it's convenient, would be nicer to -- pre-filter the list of locs to check and then decide whether to print
src/Darcs/UI/Commands/Help.hs view
@@ -21,6 +21,9 @@ printVersion, listAvailableCommands ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.UI.Flags ( DarcsFlag , environmentHelpEmail@@ -41,12 +44,11 @@ , usage ) import Darcs.UI.External ( viewDoc )-import Darcs.Repository.Lock ( environmentHelpTmpdir, environmentHelpKeepTmpdir- , environmentHelpLocks )+import Darcs.Util.Lock ( environmentHelpTmpdir, environmentHelpKeepTmpdir+ , environmentHelpLocks ) import Darcs.Patch.Match ( helpOnMatchers ) import Darcs.Repository.Prefs ( boringFileHelp, binariesFileHelp, environmentHelpHome )-import Darcs.Repository.Ssh ( environmentHelpSsh, environmentHelpScp, environmentHelpSshPort )-import Darcs.Repository.External ( environmentHelpProtocols )+import Darcs.Util.Ssh ( environmentHelpSsh, environmentHelpScp, environmentHelpSshPort ) import Darcs.Util.File ( withCurrentDirectory ) import Darcs.Util.Path ( AbsolutePath ) import Control.Arrow ( (***) )@@ -202,7 +204,6 @@ environmentHelpSshPort, environmentHelpProxy, environmentHelpProxyPassword,- environmentHelpProtocols, environmentHelpTimeout] -- | This module is responsible for emitting a darcs "man-page", a@@ -405,8 +406,8 @@ environmentHelpPager :: ([String], [String]) environmentHelpPager = (["DARCS_PAGER", "PAGER"],[- "Darcs will sometimes invoke a pager if it deems output to be too long",- "to fit onscreen. Darcs will use the pager specified by $DARCS_PAGER",+ "Darcs will invoke a pager if the output of some command is longer",+ "than 20 lines. Darcs will use the pager specified by $DARCS_PAGER", "or $PAGER. If neither are set, `less` will be used."]) environmentHelpTimeout :: ([String], [String])
src/Darcs/UI/Commands/Init.hs view
@@ -17,9 +17,12 @@ module Darcs.UI.Commands.Init ( initialize, initializeCmd ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( (^) ) import Darcs.UI.Commands- ( DarcsCommand(..), withStdOpts, nodefaults, amNotInRepository, putInfo, formatPath )+ ( DarcsCommand(..), withStdOpts, nodefaults, amNotInRepository, putInfo ) import Darcs.UI.Flags ( DarcsFlag( WorkRepoDir ), withWorkingDir, patchFormat, runPatchIndex ) import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags ) import qualified Darcs.UI.Options.All as O@@ -30,20 +33,20 @@ import Darcs.UI.Options.All ( ) import Darcs.Util.Printer ( text ) import Darcs.Util.Path ( AbsolutePath )+import Darcs.Util.Text ( quote ) import Darcs.Repository ( createRepository ) initializeDescription :: String-initializeDescription = "Make the current directory or the specified directory a repository."+initializeDescription = "Create an empty repository." initializeHelp :: String initializeHelp =- "The `darcs initialize` command turns the current directory into a\n" ++- "Darcs repository. Any existing files and subdirectories become\n" ++- "UNSAVED changes: record them with `darcs record --look-for-adds`.\n" +++ "The `darcs initialize` command creates an empty repository in the\n" +++ "current directory. This repository lives in a new `_darcs` directory,\n"+++ "which stores version control metadata and settings.\n" ++ "\n" ++- "This command creates the `_darcs` directory, which stores version\n" ++- "control metadata. It also contains per-repository settings in\n" ++- "`_darcs/prefs/`, which you can read about in the user manual.\n" +++ "Any existing files and subdirectories become UNSAVED changes:\n" +++ "record them with `darcs record --look-for-adds`.\n" ++ "\n" ++ "By default, patches of the new repository are in the darcs-2 semantics.\n" ++ "However it is possible to create a repository in darcs-1 semantics with\n" ++@@ -102,9 +105,9 @@ initializeCmd _ opts [] = do location <- amNotInRepository opts case location of- Left msg -> fail $ "Unable to " ++ formatPath ("darcs " ++ commandName initialize)+ Left msg -> fail $ "Unable to " ++ quote ("darcs " ++ commandName initialize) ++ " here.\n\n" ++ msg Right () -> do- createRepository (patchFormat opts == O.PatchFormat1) (withWorkingDir opts) (runPatchIndex opts)+ createRepository (patchFormat opts) (withWorkingDir opts) (runPatchIndex opts) putInfo opts $ text "Repository initialized." initializeCmd _ _ _ = fail "You must provide 'initialize' with either zero or one argument."
src/Darcs/UI/Commands/Log.hs view
@@ -23,7 +23,8 @@ , changelog, getLogInfo ) where -import Prelude hiding ( (^), log, catch )+import Prelude ()+import Darcs.Prelude import Unsafe.Coerce (unsafeCoerce) import Data.List ( intersect, sort, nub, find )@@ -31,20 +32,19 @@ import Control.Arrow ( second ) import Control.Exception ( catch, IOException ) import Control.Monad.State.Strict-import Control.Applicative ((<$>)) import Darcs.UI.PrintPatch ( showFriendly ) import Darcs.Patch.PatchInfoAnd ( fmapFLPIAP, hopefullyM, info ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, commandAlias, findRepository ) import Darcs.UI.External ( viewDocWith ) import Darcs.UI.Flags- ( DarcsFlag(GenContext, HumanReadable,+ ( DarcsFlag(GenContext, MachineReadable, Count, Interactive, NumberPatches, XMLOutput, Summary, Verbose, Debug, NoPatchIndexFlag) , doReverse, showChangesOnlyToFiles , useCache, maxCount, umask- , verbosity, isUnified, isInteractive, diffAlgorithm, hasSummary+ , verbosity, isUnified, isInteractive, hasSummary , fixSubPaths, getRepourl ) import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags, parseFlags ) import qualified Darcs.UI.Options.All as O@@ -55,7 +55,7 @@ readRepo, unrecordedChanges, withRepoLockCanFail ) import Darcs.Repository.Flags ( UseIndex(..), ScanKnown(..), DiffAlgorithm(MyersDiff), UpdateWorking(..) )-import Darcs.Repository.Lock ( withTempDir )+import Darcs.Util.Lock ( withTempDir ) import Darcs.Patch.Set ( PatchSet(..), newset2RL ) import Darcs.Patch.Conflict ( Conflict, CommuteNoConflicts ) import Darcs.Patch.Format ( PatchListFormat )@@ -68,8 +68,9 @@ import Darcs.Patch.TouchesFiles ( lookTouch ) import Darcs.Patch.Type ( PatchType(PatchType) ) import Darcs.Patch.Apply ( Apply, ApplyState )-import Darcs.Patch ( invert, xmlSummary, description,- effectOnFilePaths, listTouchedFiles )+import Darcs.Patch ( IsRepoType, invert, xmlSummary, description,+ effectOnFilePaths, listTouchedFiles, showPatch )+import Darcs.Patch.Named.Wrapped ( (:~:)(..) ) import Darcs.Patch.Witnesses.Eq ( EqCheck(..) ) import Darcs.Patch.Witnesses.Ordered ( FL(NilFL), RL(..), filterOutFLFL, filterRL,@@ -93,15 +94,16 @@ import Darcs.UI.SelectChanges ( viewChanges ) import qualified Darcs.UI.SelectChanges as S ( PatchSelectionOptions (..) ) import Darcs.Repository.PatchIndex ( PatchFilter, maybeFilterPatches, attemptCreatePatchIndex )-import Storage.Hashed.Tree( Tree )+import Darcs.Util.Tree( Tree ) logHelp :: String-logHelp =- "The `darcs log` command lists the patches that constitute the\n" ++- "current repository or, with `--repo`, a remote repository. Without\n" ++- "options or arguments, ALL patches will be listed.\n" ++- "\n" ++ logHelp' ++- "\n" ++ logHelp''+logHelp = unlines+ [ "The `darcs log` command lists patches of the current repository or,"+ , "with `--repo`, a remote repository. Without options or arguments,"+ , "ALL patches will be listed."+ , ""]+ ++ logHelp'+ ++ logHelp'' logBasicOpts :: DarcsOption a ([O.MatchFlag]@@ -251,33 +253,32 @@ then (reverse xs, b, c) else (xs, b, c) ---- FIXME: this prose is unreadable. --twb, 2009-08 logHelp' :: String-logHelp' =- "When given one or more files or directories as arguments, only\n" ++- "patches which affect those files or directories are listed. This\n" ++- "includes patches that happened to files before they were moved or\n" ++- "renamed.\n" ++- "\n" ++- "When given a `--from-tag`, `--from-patch` or `--from-match`, only patches\n" ++- "since that tag or patch are listed. Similarly, the `--to-tag`,\n" ++- "`--to-patch` and `--to-match` options restrict the list to older patches.\n" ++- "\n" ++- "The `--last` and `--max-count` options both limit the number of patches\n" ++- "listed. The former applies BEFORE other filters, whereas the latter\n" ++- "applies AFTER other filters. For example `darcs log foo.c\n" ++- "--max-count 3` will print the last three patches that affect foo.c,\n" ++- "whereas `darcs log --last 3 foo.c` will, of the last three\n" ++- "patches, print only those that affect foo.c.\n"+logHelp' = unlines+ [ "When given files or directories paths as arguments, only patches which"+ , "affect those paths are listed. This includes patches that happened to"+ , "files before they were moved or renamed."+ , ""+ , "When given `--from-tag` or `--from-patch`, only patches since that tag"+ , "or patch are listed. Similarly, the `--to-tag` and `--to-patch`"+ , "options restrict the list to older patches."+ , ""+ , "The `--last` and `--max-count` options both limit the number of patches"+ , "listed. The former applies BEFORE other filters, whereas the latter"+ , "applies AFTER other filters. For example `darcs log foo.c"+ , "--max-count 3` will print the last three patches that affect foo.c,"+ , "whereas `darcs log --last 3 foo.c` will, of the last three"+ , "patches, print only those that affect foo.c."+ , ""+ ] -getLogInfo :: forall p wX wY- . (Matchable p, ApplyState p ~ Tree)+getLogInfo :: forall rt p wX wY+ . (IsRepoType rt, Matchable p, ApplyState p ~ Tree) => Maybe Int -> [MatchFlag] -> Bool -> Maybe [FilePath]- -> PatchFilter p- -> PatchSet p wX wY- -> IO ( [(Sealed2 (PatchInfoAnd p), [FilePath])]+ -> PatchFilter rt p+ -> PatchSet rt p wX wY+ -> IO ( [(Sealed2 (PatchInfoAnd rt p), [FilePath])] , [(FilePath, FilePath)] , Maybe Doc ) getLogInfo maxCountFlag matchFlags onlyToFilesFlag plain_fs patchFilter ps =@@ -300,7 +301,7 @@ sp2s = if secondMatch matchFlags then matchSecondPatchset matchFlags ps else Sealed ps- pf = if haveNonrangeMatch (PatchType :: PatchType p) matchFlags+ pf = if haveNonrangeMatch (PatchType :: PatchType rt p) matchFlags then matchAPatchread matchFlags else \_ -> True @@ -309,7 +310,7 @@ | otherwise = (pfs, renames, doc) onlyRelated (Sealed2 p, fs) =- (Sealed2 $ fmapFLPIAP (filterOutFLFL (unrelated fs)) p, fs)+ (Sealed2 $ fmapFLPIAP (filterOutFLFL (unrelated fs)) (\_ -> ReflPatch) p, fs) unrelated fs p -- If the change does not affect the patches we are looking at,@@ -325,12 +326,12 @@ -- "Just n" -- returns at most n patches touching the file (starting from the -- beginning of the patch list). filterPatchesByNames- :: forall p+ :: forall rt p . (Matchable p, ApplyState p ~ Tree) => Maybe Int -- ^ maxcount -> [FilePath] -- ^ filenames- -> [Sealed2 (PatchInfoAnd p)] -- ^ patchlist- -> ([(Sealed2 (PatchInfoAnd p),[FilePath])], [(FilePath, FilePath)], Maybe Doc)+ -> [Sealed2 (PatchInfoAnd rt p)] -- ^ patchlist+ -> ([(Sealed2 (PatchInfoAnd rt p),[FilePath])], [(FilePath, FilePath)], Maybe Doc) filterPatchesByNames maxcount fns patches = removeNonRenames $ evalState (filterPatchesByNames' fns patches) (maxcount, initRenames) where removeNonRenames (ps, renames, doc) = (ps, removeIds renames, doc)@@ -372,18 +373,16 @@ (-:-) :: a -> ([a],b,c) -> ([a],b,c) x -:- ~(xs,y,z) = (x:xs,y,z) -changelog :: forall p wStart wX+changelog :: forall rt p wStart wX . ( Apply p, ApplyState p ~ Tree, ShowPatch p, IsHunk p , PrimPatchBase p, PatchListFormat p , Conflict p, CommuteNoConflicts p )- => [DarcsFlag] -> PatchSet p wStart wX- -> ([(Sealed2 (PatchInfoAnd p), [FilePath])], [(FilePath, FilePath)], Maybe Doc)+ => [DarcsFlag] -> PatchSet rt p wStart wX+ -> ([(Sealed2 (PatchInfoAnd rt p), [FilePath])], [(FilePath, FilePath)], Maybe Doc) -> Doc changelog opts patchset (pis_and_fs, createdAsFs, mbErr) | Count `elem` opts = text $ show $ length pis_and_fs- | MachineReadable `elem` opts =- maybe (vsep $ map (unseal2 (showPatchInfo.info)) pis) errorDoc mbErr | XMLOutput `elem` opts = text "<changelog>" $$ vcat created_as_xml@@ -393,9 +392,11 @@ mbAppendErr $ vsep (map (number_patch change_with_summary) pis) | otherwise = mbAppendErr $ vsep (map (number_patch description') pis) where mbAppendErr = maybe id (\err -> ($$ err)) mbErr- change_with_summary :: Sealed2 (PatchInfoAnd p) -> Doc+ change_with_summary :: Sealed2 (PatchInfoAnd rt p) -> Doc change_with_summary (Sealed2 hp)- | Just p <- hopefullyM hp = showFriendly (verbosity opts) (hasSummary O.NoSummary opts) p+ | Just p <- hopefullyM hp = if MachineReadable `elem` opts+ then showPatch p+ else showFriendly (verbosity opts) (hasSummary O.NoSummary opts) p | otherwise = description hp $$ indent (text "[this patch is unavailable]") @@ -426,46 +427,37 @@ Just n -> text (show n++":") <+> f x Nothing -> f x else f x- get_number :: Sealed2 (PatchInfoAnd p) -> Maybe Int+ get_number :: Sealed2 (PatchInfoAnd re p) -> Maybe Int get_number (Sealed2 y) = gn 1 (newset2RL patchset) where iy = info y- gn :: Int -> RL (PatchInfoAnd p) wStart wY -> Maybe Int- gn n (b:<:bs) | seq n (info b) == iy = Just n+ gn :: Int -> RL (PatchInfoAnd rt p) wStart wY -> Maybe Int+ gn n (bs:<:b) | seq n (info b) == iy = Just n | otherwise = gn (n+1) bs gn _ NilRL = Nothing pis = map fst pis_and_fs description' = unseal2 description --- FIXME: this prose is unreadable. --twb, 2009-08 logHelp'' :: String-logHelp'' =- "Three output formats exist. The default is `--human-readable`. You can\n" ++- "also select `--context`, which is the internal format (as seen in patch\n" ++- "bundles) that can be re-read by Darcs (e.g. `darcs clone --context`).\n" ++- "\n" ++- "Finally, there is `--xml-output`, which emits valid XML... unless a the\n" ++- "patch metadata (author, name or description) contains a non-ASCII\n" ++- "character and was recorded in a non-UTF8 locale.\n" ++- "\n" ++- -- FIXME: can't we just disallow the following usage?- "Note that while the `--context` flag may be used in conjunction with\n" ++- "`--xml-output` or `--human-readable`, in neither case will darcs clone be\n" ++- "able to read the output. On the other hand, sufficient information\n" ++- "WILL be output for a knowledgeable human to recreate the current state\n" ++- "of the repository.\n"+logHelp'' = unlines+ [ "Four output formats exist. The default is `--human-readable`. The slightly"+ , "different `--machine-readable` format enables to see patch dependencies in"+ , "non-interactive mode. You can also select `--context`, which is an internal"+ , "format that can be re-read by Darcs (e.g. `darcs clone --context`)."+ , ""+ , "Finally, there is `--xml-output`, which emits valid XML... unless a the"+ , "patch metadata (author, name or description) contains a non-ASCII"+ , "character and was recorded in a non-UTF8 locale."+ ] logContext :: [DarcsFlag] -> IO () logContext opts = do let repodir = fromMaybe "." $ getRepourl opts withRepositoryDirectory (useCache opts) repodir $ RepoJob $ \repository -> do (_ :> ps') <- contextPatches `fmap` readRepo repository- let ps = mapRL (\p -> (seal2 p, [])) ps'- let header = if fancy then empty else text "\nContext:\n"- let logOutput = changelog opts' emptyset (ps, [], Nothing)+ let pis = mapRL seal2 ps'+ let header = text "\nContext:\n"+ let logOutput = maybe (vsep $ map (unseal2 (showPatchInfo.info)) pis) errorDoc Nothing viewDocWith simplePrinters Encode $ header $$ logOutput- where opts' = if fancy then opts else MachineReadable : opts- fancy = HumanReadable `elem` opts || XMLOutput `elem` opts- emptyset = PatchSet NilRL NilRL -- | changes is an alias for log changes :: DarcsCommand [DarcsFlag]@@ -485,7 +477,6 @@ logPatchSelOpts flags = S.PatchSelectionOptions { S.verbosity = verbosity flags , S.matchFlags = parseFlags O.matchSeveralOrRange flags- , S.diffAlgorithm = diffAlgorithm flags , S.interactive = isInteractive False flags , S.selectDeps = O.PromptDeps -- option not supported, use default , S.summary = hasSummary O.NoSummary flags
src/Darcs/UI/Commands/MarkConflicts.hs view
@@ -19,8 +19,11 @@ module Darcs.UI.Commands.MarkConflicts ( markconflicts ) where -import Prelude hiding ( (^), catch )+import Prelude ()+import Darcs.Prelude +import Prelude hiding ( (^) )+ import System.Exit ( exitSuccess ) import Data.List.Ordered ( nubSort ) import Control.Monad ( when, unless )@@ -133,7 +136,7 @@ } markconflictsCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-markconflictsCmd _ opts [] = withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \(repository :: Repository p wR wU wR) -> do+markconflictsCmd _ opts [] = withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \(repository :: Repository rt p wR wU wR) -> do pend <- unrecordedChanges (diffingOpts opts) repository Nothing r <- readRepo repository Sealed res <- return $ patchsetConflictResolutions r@@ -144,7 +147,7 @@ when (dryRun opts == O.YesDryRun) $ do putDocLn $ text "Conflicts will not be marked: this is a dry run." exitSuccess- let undoUnrec :: FL (PrimOf p) wR wU -> IO (Repository p wR wR wR)+ let undoUnrec :: FL (PrimOf p) wR wU -> IO (Repository rt p wR wR wR) undoUnrec NilFL = return repository undoUnrec pend' = do putStrLn ("This will trash any unrecorded changes"++
src/Darcs/UI/Commands/Move.hs view
@@ -20,9 +20,9 @@ module Darcs.UI.Commands.Move ( move, mv ) where -import Prelude hiding ( (^) )+import Prelude ()+import Darcs.Prelude -import Control.Applicative ( (<$>) ) import Control.Monad ( when, unless, forM_, forM ) import Data.Maybe ( fromMaybe ) import Darcs.Util.SignalHandler ( withSignalsBlocked )@@ -51,14 +51,14 @@ import Darcs.Patch.Witnesses.Sealed ( emptyGap, freeGap, joinGap, FreeLeft ) import Darcs.Util.Global ( debugMessage ) import qualified Darcs.Patch-import Darcs.Patch ( RepoPatch, PrimPatch )+import Darcs.Patch ( RepoPatch, PrimPatch, PrimOf ) import Darcs.Patch.Apply( ApplyState ) import Data.List ( nub, sort ) import qualified System.FilePath.Windows as WindowsFilePath import Darcs.UI.Commands.Util.Tree ( treeHas, treeHasDir, treeHasAnycase, treeHasFile )-import Storage.Hashed.Tree( Tree, modifyTree )-import Storage.Hashed.Plain( readPlainTree )+import Darcs.Util.Tree( Tree, modifyTree )+import Darcs.Util.Tree.Plain( readPlainTree ) import Darcs.Util.Path ( floatPath , fp2fn@@ -137,11 +137,14 @@ | length args < 2 = fail "The `darcs move' command requires at least two arguments." | length args == 2 = do+ -- NOTE: The extra case for two arguments is necessary because+ -- in this case we allow file -> file moves. Whereas with 3 or+ -- more arguments the last one (i.e. the target) must be a directory. xs <- maybeFixSubPaths fps args case xs of [Just from, Just to]- | from == to -> fail "Cannot rename a file or directory onto itself!"- | toFilePath from == "" -> fail "Cannot move the root of the repository"+ | from == to -> fail "Cannot rename a file or directory onto itself."+ | toFilePath from == "" -> fail "Cannot move the root of the repository." | otherwise -> moveFile opts from to _ -> fail "Both source and destination must be valid." | otherwise = let (froms, to) = (init args, last args) in do@@ -151,10 +154,14 @@ Just to' -> do xs <- nub . sort <$> fixSubPaths fps froms if to' `elem` xs- then fail "Cannot rename a file or directory onto itself!"+ then fail "Cannot rename a file or directory onto itself." else case xs of [] -> fail "Nothing to move."- froms' -> moveFilesToDir opts froms' to'+ froms' ->+ if or (map (null . toFilePath) froms') then+ fail "Cannot move the root of the repository."+ else+ moveFilesToDir opts froms' to' data FileKind = Dir | File deriving (Show, Eq)@@ -227,9 +234,9 @@ moveToDir repo opts cur work (map toFilePath froms) $ toFilePath to withRepoAndState :: [DarcsFlag]- -> (forall p wR wU .- (ApplyState p ~ Tree, RepoPatch p) =>- (Repository p wR wU wR, Tree IO, Tree IO, Tree IO)+ -> (forall rt p wR wU .+ (ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree, RepoPatch p) =>+ (Repository rt p wR wU wR, Tree IO, Tree IO, Tree IO) -> IO ()) -> IO () withRepoAndState opts f =@@ -243,7 +250,8 @@ uc = useCache opts um = umask opts -simpleMove :: (RepoPatch p, ApplyState p ~ Tree) => Repository p wR wU wT+simpleMove :: (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => Repository rt p wR wU wT -> [DarcsFlag] -> Tree IO -> Tree IO -> FilePath -> FilePath -> IO () simpleMove repository opts cur work old_fp new_fp = do@@ -251,7 +259,8 @@ unless (Quiet `elem` opts) $ putStrLn $ unwords ["Moved:", old_fp, "to:", new_fp] -moveToDir :: (RepoPatch p, ApplyState p ~ Tree) => Repository p wR wU wT+moveToDir :: (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => Repository rt p wR wU wT -> [DarcsFlag] -> Tree IO -> Tree IO -> [FilePath] -> FilePath -> IO () moveToDir repository opts cur work moved finaldir = do@@ -261,7 +270,8 @@ unless (Quiet `elem` opts) $ putStrLn $ unwords $ ["Moved:"] ++ moved ++ ["to:", finaldir] -doMoves :: (RepoPatch p, ApplyState p ~ Tree) => Repository p wR wU wT+doMoves :: (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => Repository rt p wR wU wT -> [DarcsFlag] -> Tree IO -> Tree IO -> [(FilePath, FilePath)] -> IO () doMoves repository opts cur work moves = do
src/Darcs/UI/Commands/Optimize.hs view
@@ -19,10 +19,11 @@ module Darcs.UI.Commands.Optimize ( optimize, doOptimizeHTTP ) where -import Prelude hiding ( (^) )-import Control.Applicative ( (<$>) )+import Prelude ()+import Darcs.Prelude+ import Control.Exception ( finally )-import Control.Monad ( when, unless, forM_ )+import Control.Monad ( when, unless, forM, forM_ ) import Data.Maybe ( isJust, fromJust ) import Data.List ( sort ) import Data.Set ( difference )@@ -42,7 +43,8 @@ import Darcs.Patch.PatchInfoAnd ( extractHash )-import Darcs.UI.Commands ( DarcsCommand(..), nodefaults, amInRepository, putInfo+import Darcs.UI.Commands ( DarcsCommand(..), nodefaults+ , amInHashedRepository, amInRepository, putInfo , normalCommand, withStdOpts ) import Darcs.Repository.Prefs ( getPreflist, getCaches, globalCacheDir, oldGlobalCacheDir ) import Darcs.Repository@@ -53,21 +55,22 @@ , reorderInventory , cleanRepository , replacePristine+ , maybeIdentifyRepository )+import Darcs.Repository.Internal ( IdentifyRepo(..) ) import Darcs.Repository.HashedRepo ( inventoriesDir, patchesDir, pristineDir, hashedInventory, filterDirContents, readHashedPristineRoot, listInventoriesRepoDir, listPatchesLocalBucketed, set, unset, inv2pris ) import Darcs.Repository.HashedIO ( getHashedFiles ) import Darcs.Repository.Old ( oldRepoFailMsg )-import Darcs.Repository.InternalTypes ( Repository(..) )-import Darcs.Repository.Util ( getRecursiveDarcsRepos )+import Darcs.Repository.InternalTypes ( Repository(..), Pristine(..) ) import Darcs.Patch.Witnesses.Ordered ( mapFL , bunchFL , lengthRL )-import Darcs.Patch ( RepoPatch )+import Darcs.Patch ( IsRepoType, RepoPatch ) import Darcs.Patch.Set ( newset2RL , newset2FL@@ -76,7 +79,7 @@ import Darcs.Patch.Apply( ApplyState ) import Darcs.Util.ByteString ( gzReadFilePS ) import Darcs.Util.Printer ( text )-import Darcs.Repository.Lock+import Darcs.Util.Lock ( maybeRelink , gzWriteAtomicFilePS , writeAtomicFilePS@@ -110,7 +113,7 @@ import qualified Darcs.UI.Options.All as O import Darcs.Repository.Flags ( UpdateWorking (..), DryRun ( NoDryRun ), UseCache (..), UMask (..)- , WithWorkingDir(WithWorkingDir) )+ , WithWorkingDir(WithWorkingDir), PatchFormat(PatchFormat1) ) import Darcs.Patch.Progress ( progressFL ) import Darcs.Repository.Cache ( hashedDir, bucketFolder, HashedDir(HashedPristineDir) )@@ -125,7 +128,7 @@ import qualified Darcs.Repository.HashedRepo as HashedRepo import Darcs.Repository.State ( readRecorded ) -import Storage.Hashed.Tree+import Darcs.Util.Tree ( Tree , TreeItem(..) , list@@ -133,8 +136,8 @@ , emptyTree ) import Darcs.Util.Path( anchorPath, toFilePath, AbsolutePath )-import Storage.Hashed.Plain( readPlainTree )-import Storage.Hashed.Darcs+import Darcs.Util.Tree.Plain( readPlainTree )+import Darcs.Util.Tree.Hashed ( writeDarcsHashed , decodeDarcsSize )@@ -197,7 +200,7 @@ { commandProgramName = "darcs" , commandExtraArgs = 0 , commandExtraArgHelp = []- , commandPrereq = amInRepository+ , commandPrereq = amInHashedRepository , commandArgdefaults = nodefaults , commandName = undefined , commandHelp = undefined@@ -221,7 +224,7 @@ } optimizeCleanCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-optimizeCleanCmd _ opts _ = do+optimizeCleanCmd _ opts _ = withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do cleanRepository repository -- garbage collect pristine.hashed, inventories and patches directories putInfo opts "Done cleaning repository!"@@ -231,6 +234,7 @@ { commandName = "upgrade" , commandHelp = "Convert old-fashioned repositories to the current default hashed format." , commandDescription = "upgrade repository to latest compatible format"+ , commandPrereq = amInRepository , commandCommand = optimizeUpgradeCmd } @@ -243,7 +247,7 @@ } optimizeHttpCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-optimizeHttpCmd _ opts _ = do+optimizeHttpCmd _ opts _ = withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do cleanRepository repository -- garbage collect pristine.hashed, inventories and patches directories doOptimizeHTTP repository@@ -259,7 +263,7 @@ } optimizePristineCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-optimizePristineCmd _ opts _ = do+optimizePristineCmd _ opts _ = withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do cleanRepository repository -- garbage collect pristine.hashed, inventories and patches directories doOptimizePristine opts repository@@ -282,14 +286,14 @@ } optimizeCompressCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-optimizeCompressCmd _ opts _ = do+optimizeCompressCmd _ opts _ = withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do cleanRepository repository -- garbage collect pristine.hashed, inventories and patches directories optimizeCompression [Compress] putInfo opts "Done optimizing by compression!" optimizeUncompressCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-optimizeUncompressCmd _ opts _ = do+optimizeUncompressCmd _ opts _ = withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do cleanRepository repository -- garbage collect pristine.hashed, inventories and patches directories optimizeCompression []@@ -331,13 +335,13 @@ } optimizeEnablePatchIndexCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-optimizeEnablePatchIndexCmd _ opts _ = do+optimizeEnablePatchIndexCmd _ opts _ = withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do createOrUpdatePatchIndexDisk repository putInfo opts "Done enabling patch index!" optimizeDisablePatchIndexCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-optimizeDisablePatchIndexCmd _ opts _ = do+optimizeDisablePatchIndexCmd _ opts _ = withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \(Repo repodir _ _ _) -> do deletePatchIndex repodir putInfo opts "Done disabling patch index!"@@ -354,7 +358,7 @@ } optimizeReorderCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-optimizeReorderCmd _ opts _ = do+optimizeReorderCmd _ opts _ = withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do reorderInventory repository (compression opts) YesUpdateWorking (verbosity opts) putInfo opts "Done reordering!"@@ -393,7 +397,7 @@ } optimizeRelinkCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-optimizeRelinkCmd _ opts _ = do+optimizeRelinkCmd _ opts _ = withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do cleanRepository repository -- garbage collect pristine.hashed, inventories and patches directories doRelink opts@@ -430,10 +434,8 @@ "repository, or if you pulled the same patch from a remote repository\n" ++ "into multiple local repositories." -doOptimizePristine :: (RepoPatch p, ApplyState p ~ Tree) => [DarcsFlag] -> Repository p wR wU wT -> IO ()+doOptimizePristine :: (RepoPatch p, ApplyState p ~ Tree) => [DarcsFlag] -> Repository rt p wR wU wT -> IO () doOptimizePristine opts repo = do- hashed <- doesFileExist $ darcsdir </> "hashed_inventory"- when hashed $ do inv <- BS.readFile (darcsdir </> "hashed_inventory") let linesInv = BS.split '\n' inv case linesInv of@@ -468,6 +470,7 @@ unless done $ maybeRelinkFile t f +-- Only 'optimize' commands that works on old-fashionned repositories optimizeUpgradeCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () optimizeUpgradeCmd _ opts _ = do debugMessage "Upgrading to hashed..."@@ -479,7 +482,9 @@ withRepoLock NoDryRun YesUseCache YesUpdateWorking NoUMask $ RepoJob $ \repository -> actuallyUpgradeFormat repository -actuallyUpgradeFormat :: (RepoPatch p, ApplyState p ~ Tree) => Repository p wR wU wT -> IO ()+actuallyUpgradeFormat+ :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> IO () actuallyUpgradeFormat repository = do -- convert patches/inventory patches <- readRepo repository@@ -500,7 +505,7 @@ sequence_ $ mapFL HashedRepo.applyToTentativePristine $ bunchFL 100 patchesToApply -- now make it official HashedRepo.finalizeTentativeChanges repository compr- writeRepoFormat (createRepoFormat True WithWorkingDir) (darcsdir </> "format")+ writeRepoFormat (createRepoFormat PatchFormat1 WithWorkingDir) (darcsdir </> "format") -- clean out old-fashioned junk debugMessage "Cleaning out old-fashioned repository files..." removeFile $ darcsdir </> "inventory"@@ -517,7 +522,9 @@ gzs <- filter ((== ".gz") . takeExtension) `fmap` getDirectoryContents "." mapM_ removeFile gzs -doOptimizeHTTP :: (RepoPatch p, ApplyState p ~ Tree) => Repository p wR wU wT -> IO ()+doOptimizeHTTP+ :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> IO () doOptimizeHTTP repo = flip finally (mapM_ removeFileIfExists [ darcsdir </> "meta-filelist-inventories" , darcsdir </> "meta-filelist-pristine"@@ -525,6 +532,7 @@ , patchesTar <.> "part" ]) $ do rf <- identifyRepoFormat "."+ -- function is exposed in API so could be called on non-hashed repo unless (formatHas HashedInventory rf) $ fail oldRepoFailMsg createDirectoryIfMissing False packsDir -- pristine hash@@ -535,6 +543,9 @@ is <- map ((darcsdir </> "inventories") </>) <$> HashedRepo.listInventories writeFile (darcsdir </> "meta-filelist-inventories") . unlines $ map takeFileName is+ -- tinkering with zlib's compression parameters does not make+ -- any noticeable difference in generated archive size.+ -- switching to bzip2 does OTOH (~25% size gain). BL.writeFile (patchesTar <.> "part") . compress . write =<< mapM fileEntry' ((darcsdir </> "meta-filelist-inventories") : ps ++ reverse is)@@ -557,7 +568,7 @@ tp <- either fail return $ toTarPath False x return $ fileEntry tp content dirContents d = map ((darcsdir </> d) </>) <$>- (filterDirContents d $ const True)+ filterDirContents d (const True) hashedPatchFileName x = case extractHash x of Left _ -> fail "unexpected unhashed patch" Right h -> darcsdir </> "patches" </> h@@ -672,7 +683,7 @@ createDirectoryIfMissing True gCachePristineDir remove listInventoriesRepoDir gCacheInvDir repoPaths- remove ((listPatchesLocalBucketed gCache) . (</> darcsdir)) gCachePatchesDir repoPaths+ remove (listPatchesLocalBucketed gCache . (</> darcsdir)) gCachePatchesDir repoPaths remove getPristine gCachePristineDir repoPaths where@@ -682,13 +693,36 @@ remove' cacheSubDir s2 (concat s1) remove' :: String -> [String] -> [String] -> IO ()- remove' dir s1 s2 = do+ remove' dir s1 s2 = mapM_ (removeFileMayNotExist . (\hashedFile -> dir </> bucketFolder hashedFile </> hashedFile))- (unset $ (set s1) `difference` (set s2))+ (unset $ set s1 `difference` set s2) getPristine :: String -> IO [String] getPristine darcsDir = do i <- gzReadFilePS (darcsDir </> darcsdir </> hashedInventory)- priss <- getHashedFiles (darcsDir </> darcsdir </> pristineDir) [inv2pris i]- return priss+ getHashedFiles (darcsDir </> darcsdir </> pristineDir) [inv2pris i]++-- |getRecursiveDarcsRepos returns all paths to repositories under topdir.+getRecursiveDarcsRepos :: FilePath -> IO [FilePath]+getRecursiveDarcsRepos topdir = do+ isDir <- doesDirectoryExist topdir+ if isDir+ then do+ status <- maybeIdentifyRepository NoUseCache topdir+ case status of+ GoodRepository (Repo _ _ pris _) ->+ case pris of+ HashedPristine -> return [topdir]+ _ -> return [] -- old fashioned or broken repo+ _ -> getRecursiveDarcsRepos' topdir+ else return []++ where+ getRecursiveDarcsRepos' d = do+ names <- getDirectoryContents d+ let properNames = filter (\x -> head x /= '.') names+ paths <- forM properNames $ \name -> do+ let path = d </> name+ getRecursiveDarcsRepos path+ return (concat paths)
src/Darcs/UI/Commands/Pull.hs view
@@ -24,6 +24,9 @@ fetchPatches, revertable ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( (^) ) import System.Exit ( exitSuccess )@@ -35,7 +38,6 @@ ( DarcsCommand(..), withStdOpts , putInfo , setEnvDarcsPatches- , formatPath , defaultRepo , amInHashedRepository )@@ -57,7 +59,8 @@ , doReverse, verbosity, dryRun, umask, useCache, selectDeps , remoteRepos, reorder, setDefault , isUnified, hasSummary- , diffAlgorithm, isInteractive )+ , isInteractive+ ) import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise , defaultFlags, parseFlags@@ -81,7 +84,7 @@ import qualified Darcs.Repository.Cache as DarcsCache import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, info, hopefully, patchDesc )-import Darcs.Patch ( RepoPatch, description )+import Darcs.Patch ( IsRepoType, RepoPatch, description, PrimOf ) import Darcs.Patch.Bundle( makeBundleN, patchFilename ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Set ( Origin, PatchSet(..), SealedPatchSet )@@ -97,18 +100,18 @@ newsetIntersection, newsetUnion ) import Darcs.UI.ApplyPatches ( PatchApplier(..), StandardPatchApplier(..) ) import Darcs.UI.SelectChanges- ( selectChanges- , WhichChanges(..)+ ( WhichChanges(..) , runSelection , selectionContext ) import qualified Darcs.UI.SelectChanges as S ( PatchSelectionOptions (..) ) import Darcs.Util.Exception ( clarifyErrors ) import Darcs.Util.Printer ( putDocLn, vcat, ($$), text, putDoc )-import Darcs.Repository.Lock ( writeDocBinFile )+import Darcs.Util.Lock ( writeDocBinFile ) import Darcs.Util.Path ( useAbsoluteOrStd, stdOut, AbsolutePath ) import Darcs.Util.Workaround ( getCurrentDirectory )-import Storage.Hashed.Tree( Tree )+import Darcs.Util.Text ( quote )+import Darcs.Util.Tree( Tree ) #include "impossible.h" @@ -124,11 +127,9 @@ pullHelp = unlines [ "Pull is used to bring patches made in another repository into the current" , "repository (that is, either the one in the current directory, or the one"- , "specified with the `--repodir` option). Pull allows you to bring over all or"- , "some of the patches that are in that repository but not in this one. Pull"- , "accepts arguments, which are URLs from which to pull, and when called"- , "without an argument, pull will use the repository from which you have most"- , "recently either pushed or pulled."+ , "specified with the `--repodir` option). Pull accepts arguments, which are"+ , "URLs from which to pull, and when called without an argument, pull will"+ , "use the repository specified at `_darcs/prefs/defaultrepo`." , "" , "The default (`--union`) behavior is to pull any patches that are in any of" , "the specified repositories. If you specify the `--intersection` flag, darcs"@@ -370,11 +371,11 @@ withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ fetchPatches o opts repos "fetch" >=> makeBundle opts -fetchPatches :: forall p wR wU . (RepoPatch p, ApplyState p ~ Tree)+fetchPatches :: forall rt p wR wU . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree) => AbsolutePath -> [DarcsFlag] -> [String] -> String- -> Repository p wR wU wR- -> IO (SealedPatchSet p Origin,- Sealed ((FL (PatchInfoAnd p) :\/: FL (PatchInfoAnd p)) wR))+ -> Repository rt p wR wU wR+ -> IO (SealedPatchSet rt p Origin,+ Sealed ((FL (PatchInfoAnd rt p) :\/: FL (PatchInfoAnd rt p)) wR)) fetchPatches o opts unfixedrepodirs@(_:_) jobname repository = do here <- getCurrentDirectory repodirs <- (nub . filter (/= here)) `fmap` mapM (fixUrl o) unfixedrepodirs@@ -384,7 +385,7 @@ old_default <- getPreflist "defaultrepo" when (old_default == repodirs && XMLOutput `notElem` opts) $ let pulling = if DryRun `elem` opts then "Would pull" else "Pulling"- in putInfo opts $ text $ pulling++" from "++concatMap formatPath repodirs++"..."+ in putInfo opts $ text $ pulling++" from "++concatMap quote repodirs++"..." (Sealed them, Sealed compl) <- readRepos repository opts repodirs addRepoSource (head repodirs) (dryRun opts) (remoteRepos opts) (setDefault False opts) mapM_ (addToPreflist "repos") repodirs@@ -415,16 +416,16 @@ when (reorder opts /= O.Reorder) exitSuccess let direction = if doReverse opts then FirstReversed else First context = selectionContext direction jobname (pullPatchSelOpts opts) Nothing Nothing- (to_be_pulled :> _) <- runSelection (selectChanges psFiltered) context+ (to_be_pulled :> _) <- runSelection psFiltered context return (seal common, seal $ us' :\/: to_be_pulled) fetchPatches _ _ [] jobname _ = fail $ "No default repository to " ++ jobname ++ " from, please specify one" -makeBundle :: forall p wR . (RepoPatch p, ApplyState p ~ Tree)+makeBundle :: forall rt p wR . (RepoPatch p, ApplyState p ~ Tree) => [DarcsFlag]- -> (SealedPatchSet p Origin,- Sealed ((FL (PatchInfoAnd p) :\/: FL (PatchInfoAnd p)) wR))+ -> (SealedPatchSet rt p Origin,+ Sealed ((FL (PatchInfoAnd rt p) :\/: FL (PatchInfoAnd rt p)) wR)) -> IO () makeBundle opts (Sealed common, Sealed (_ :\/: to_be_fetched)) = do@@ -463,9 +464,9 @@ the second patchset(s) to be complemented against Rc. -} -readRepos :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> [DarcsFlag] -> [String]- -> IO (SealedPatchSet p Origin,SealedPatchSet p Origin)+readRepos :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> [DarcsFlag] -> [String]+ -> IO (SealedPatchSet rt p Origin,SealedPatchSet rt p Origin) readRepos _ _ [] = impossible readRepos to_repo opts us = do rs <- mapM (\u -> do r <- identifyRepositoryFor to_repo (useCache opts) u@@ -481,7 +482,6 @@ pullPatchSelOpts flags = S.PatchSelectionOptions { S.verbosity = verbosity flags , S.matchFlags = parseFlags O.matchSeveral flags- , S.diffAlgorithm = diffAlgorithm flags , S.interactive = isInteractive True flags , S.selectDeps = selectDeps flags , S.summary = hasSummary O.NoSummary flags
src/Darcs/UI/Commands/Push.hs view
@@ -19,12 +19,14 @@ module Darcs.UI.Commands.Push ( push ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( (^) ) import System.Exit ( exitWith, ExitCode( ExitSuccess, ExitFailure ), exitSuccess ) import Control.Monad ( when, unless )-import Data.Char ( toUpper )-import Data.Maybe ( isJust, isNothing )+import Data.Maybe ( isJust ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts , putVerbose@@ -32,35 +34,36 @@ , abortRun , printDryRunMessageAndExit , setEnvDarcsPatches- , formatPath , defaultRepo , amInHashedRepository ) import Darcs.UI.Flags ( DarcsFlag- , isInteractive, verbosity, isUnified, hasSummary, diffAlgorithm- , hasXmlOutput, selectDeps, applyAs+ , isInteractive, verbosity, isUnified, hasSummary+ , hasXmlOutput, selectDeps, applyAs, remoteDarcs , doReverse, dryRun, useCache, remoteRepos, setDefault, fixUrl ) import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise , defaultFlags, parseFlags ) import qualified Darcs.UI.Options.All as O import Darcs.Repository.Flags ( DryRun (..) )+import qualified Darcs.Repository.Flags as R ( remoteDarcs ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, hopefully ) import Darcs.Repository ( Repository, withRepository, RepoJob(..), identifyRepositoryFor, readRepo, checkUnrelatedRepos )-import Darcs.Patch ( RepoPatch, description )+import Darcs.Patch ( IsRepoType, RepoPatch, description ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Witnesses.Ordered ( (:>)(..), RL, FL, nullRL, nullFL, reverseFL, mapFL_FL, mapRL ) import Darcs.Repository.Prefs ( addRepoSource, getPreflist )-import Darcs.UI.External ( maybeURLCmd, signString )-import Darcs.Util.URL ( isHttpUrl, isValidLocalPath )+import Darcs.UI.External ( signString, darcsProgram+ , pipeDoc, pipeDocSSH )+import Darcs.Util.URL ( isHttpUrl, isValidLocalPath+ , isSshUrl, splitSshUrl, SshFilePath(..) ) import Darcs.Util.Path ( AbsolutePath ) import Darcs.UI.SelectChanges- ( selectChanges- , WhichChanges(..)+ ( WhichChanges(..) , selectionContext , runSelection )@@ -69,12 +72,12 @@ import Darcs.Patch.Bundle ( makeBundleN ) import Darcs.Patch.Patchy( ShowPatch ) import Darcs.Patch.Set ( PatchSet, Origin )-import Darcs.Util.Printer ( Doc, vcat, empty, text, ($$) )-import Darcs.UI.RemoteApply ( remoteApply )+import Darcs.Util.Printer ( Doc, vcat, empty, text, ($$), RenderMode(..) ) import Darcs.UI.Email ( makeEmail ) import Darcs.Util.English (englishNum, Noun(..)) import Darcs.Util.Workaround ( getCurrentDirectory )-import Storage.Hashed.Tree( Tree )+import Darcs.Util.Text ( quote )+import Darcs.Util.Tree( Tree ) #include "impossible.h" @@ -87,19 +90,17 @@ [ "Push is the opposite of pull. Push allows you to copy patches from the" , "current repository into another repository." , ""- , "If you give the `--apply-as` flag, darcs will use sudo to apply the"+ , "If you give the `--apply-as` flag, darcs will use `sudo` to apply the" , "patches as a different user. This can be useful if you want to set up a" , "system where several users can modify the same repository, but you don't" , "want to allow them full write access. This isn't secure against skilled" , "malicious attackers, but at least can protect your repository from clumsy," , "inept or lazy users." , ""- , "Darcs push will by default compress the patch data before sending it to a"- , "remote location via ssh. This works as long as the remote darcs is not"- , "older than version 2.5. If you get errors that indicate a corrupt patch"- , "bundle, you should try again with the `--no-compress` option to send the"- , "data in un-compressed form (which is a lot slower for large patches, but"- , "should always work)."+ , "`darcs push` will compress the patch data before sending it to a remote"+ , "location via ssh. This works as long as the remote darcs is not older"+ , "than version 2.5. If you get errors that indicate a corrupt patch bundle,"+ , "you should try again with the `--no-compress` option." ] pushBasicOpts :: DarcsOption a@@ -205,13 +206,13 @@ ExitSuccess -> putInfo opts $ text "Push successful." pushCmd _ _ _ = impossible -prepareBundle :: forall p wR wU wT. (RepoPatch p, ApplyState p ~ Tree)- => [DarcsFlag] -> String -> Repository p wR wU wT -> IO Doc+prepareBundle :: forall rt p wR wU wT. (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => [DarcsFlag] -> String -> Repository rt p wR wU wT -> IO Doc prepareBundle opts repodir repository = do old_default <- getPreflist "defaultrepo" when (old_default == [repodir]) $ let pushing = if dryRun opts == YesDryRun then "Would push" else "Pushing"- in putInfo opts $ text $ pushing++" to "++formatPath repodir++"..."+ in putInfo opts $ text $ pushing++" to "++quote repodir++"..." them <- identifyRepositoryFor repository (useCache opts) repodir >>= readRepo addRepoSource repodir (dryRun opts) (remoteRepos opts) (setDefault False opts) us <- readRepo repository@@ -219,12 +220,12 @@ prePushChatter opts us (reverseFL us') them let direction = if doReverse opts then FirstReversed else First context = selectionContext direction "push" (pushPatchSelOpts opts) Nothing Nothing- runSelection (selectChanges us') context+ runSelection us' context >>= bundlePatches opts common -prePushChatter :: forall p a wX wY wT . (RepoPatch p, ShowPatch a) =>- [DarcsFlag] -> PatchSet p Origin wX ->- RL a wT wX -> PatchSet p Origin wY -> IO ()+prePushChatter :: forall rt p a wX wY wT . (RepoPatch p, ShowPatch a) =>+ [DarcsFlag] -> PatchSet rt p Origin wX ->+ RL a wT wX -> PatchSet rt p Origin wY -> IO () prePushChatter opts us us' them = do checkUnrelatedRepos (parseFlags O.allowUnrelatedRepos opts) us them let num_to_pull = snd $ countUsThem us them@@ -237,9 +238,9 @@ when (nullRL us') $ do putInfo opts $ text "No recorded local patches to push!" exitSuccess -bundlePatches :: forall t p wZ wW wA. (RepoPatch p, ApplyState p ~ Tree)- => [DarcsFlag] -> PatchSet p wA wZ- -> (FL (PatchInfoAnd p) :> t) wZ wW+bundlePatches :: forall t rt p wZ wW wA. (RepoPatch p, ApplyState p ~ Tree)+ => [DarcsFlag] -> PatchSet rt p wA wZ+ -> (FL (PatchInfoAnd rt p) :> t) wZ wW -> IO Doc bundlePatches opts common (to_be_pushed :> _) = do@@ -262,14 +263,9 @@ if isHttpUrl repodir then do when (isJust $ applyAs opts) $ abortRun opts $ text "Cannot --apply-as when pushing to URLs"- maybeapply <- maybeURLCmd "APPLY" repodir- when (isNothing maybeapply) $- let lprot = takeWhile (/= ':') repodir- prot = map toUpper lprot- msg = text ("Pushing to "++lprot++" URLs is not supported.\n"++- "You may be able to hack this to work"++- " using DARCS_APPLY_"++prot) in- abortRun opts msg+ let lprot = takeWhile (/= ':') repodir+ msg = text ("Pushing to "++lprot++" URLs is not supported.")+ abortRun opts msg else when (parseFlags O.sign opts /= O.NoSign) $ abortRun opts $ text "Signing doesn't make sense for local repositories or when pushing over ssh." @@ -278,9 +274,43 @@ pushPatchSelOpts flags = S.PatchSelectionOptions { S.verbosity = verbosity flags , S.matchFlags = parseFlags O.matchSeveral flags- , S.diffAlgorithm = diffAlgorithm flags , S.interactive = isInteractive True flags , S.selectDeps = selectDeps flags , S.summary = hasSummary O.NoSummary flags , S.withContext = isUnified flags }++remoteApply :: [DarcsFlag] -> String -> Doc -> IO ExitCode+remoteApply opts repodir bundle+ = case applyAs opts of+ Nothing+ | isSshUrl repodir -> applyViaSsh opts (splitSshUrl repodir) bundle+ | otherwise -> applyViaLocal opts repodir bundle+ Just un+ | isSshUrl repodir -> applyViaSshAndSudo opts (splitSshUrl repodir) un bundle+ | otherwise -> applyViaSudo un repodir bundle++applyViaSudo :: String -> String -> Doc -> IO ExitCode+applyViaSudo user repo bundle =+ darcsProgram >>= \darcs ->+ pipeDoc Standard "sudo" ["-u",user,darcs,"apply","--all","--repodir",repo] bundle++applyViaLocal :: [DarcsFlag] -> String -> Doc -> IO ExitCode+applyViaLocal opts repo bundle =+ darcsProgram >>= \darcs ->+ pipeDoc Standard darcs ("apply":"--all":"--repodir":repo:applyopts opts) bundle++applyViaSsh :: [DarcsFlag] -> SshFilePath -> Doc -> IO ExitCode+applyViaSsh opts repo =+ pipeDocSSH (parseFlags O.compress opts) Standard repo+ [R.remoteDarcs (remoteDarcs opts) ++" apply --all "++unwords (applyopts opts)+++ " --repodir '"++sshRepo repo++"'"]++applyViaSshAndSudo :: [DarcsFlag] -> SshFilePath -> String -> Doc -> IO ExitCode+applyViaSshAndSudo opts repo username =+ pipeDocSSH (parseFlags O.compress opts) Standard repo+ ["sudo -u "++username++" "++R.remoteDarcs (remoteDarcs opts)+++ " apply --all --repodir '"++sshRepo repo++"'"]++applyopts :: [DarcsFlag] -> [String]+applyopts opts = if parseFlags O.debug opts then ["--debug"] else []
src/Darcs/UI/Commands/Rebase.hs view
@@ -6,7 +6,8 @@ module Darcs.UI.Commands.Rebase ( rebase ) where -import Prelude hiding ( (^), catch, log )+import Prelude ()+import Darcs.Prelude import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts@@ -18,7 +19,6 @@ , printDryRunMessageAndExit , amInHashedRepository )-import Darcs.UI.Commands.Amend ( updatePatchHeader ) import Darcs.UI.Commands.Apply ( applyCmd ) import Darcs.UI.Commands.Log ( changelog, getLogInfo ) import Darcs.UI.Commands.Pull ( pullCmd, revertable )@@ -46,8 +46,12 @@ , defaultFlags, parseFlags ) import qualified Darcs.UI.Options.All as O+import Darcs.UI.PatchHeader ( HijackT, HijackOptions(..), runHijackT+ , getAuthor+ , updatePatchHeader, AskAboutDeps(..) ) import Darcs.Repository ( Repository, RepoJob(..), withRepoLock, withRepository+ , RebaseJobFlags(..) , tentativelyAddPatch, finalizeRepositoryChanges , invalidateIndex , tentativelyRemovePatches, readRepo@@ -67,20 +71,14 @@ import Darcs.Patch.CommuteFn ( commuterIdFL ) import Darcs.Patch.Info ( showPatchInfo ) import Darcs.Patch.Match ( firstMatch, secondMatch, splitSecondFL )-import Darcs.Patch.Named- ( patchcontents- , Named, fmapNamed, fmapFL_Named- , patch2patchinfo- )+import Darcs.Patch.Named ( Named, fmapFL_Named, patchcontents, patch2patchinfo )+import Darcs.Patch.Named.Wrapped ( mkRebase, toRebasing, fromRebasing ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, info, n2pia, hopefully ) import Darcs.Patch.Prim ( PrimOf, canonizeFL, fromPrim )-import Darcs.Patch.Rebase- ( Rebasing(..), RebaseItem(..)- , mkSuspended- , simplifyPush, simplifyPushes- , takeHeadRebase, takeHeadRebaseFL- )+import Darcs.Patch.Rebase ( takeHeadRebase, takeHeadRebaseFL )+import Darcs.Patch.Rebase.Container ( Suspended(..) ) import Darcs.Patch.Rebase.Fixup ( RebaseFixup(..), flToNamesPrims )+import Darcs.Patch.Rebase.Item ( RebaseItem(..), simplifyPush, simplifyPushes ) import Darcs.Patch.Rebase.Name ( RebaseName(..), commuteNameNamed ) import Darcs.Patch.Rebase.Viewing ( RebaseSelect(RSFwd), rsToPia@@ -91,12 +89,13 @@ ) import Darcs.Patch.Permutations ( partitionConflictingFL ) import Darcs.Patch.Progress ( progressFL )+import Darcs.Patch.RepoType ( RepoType(..), RebaseType(..) ) import Darcs.Patch.Set ( PatchSet(..), appendPSFL ) import Darcs.Patch.Show ( showNicely ) import Darcs.Patch.Split ( primSplitter ) import Darcs.UI.ApplyPatches ( PatchApplier(..), PatchProxy(..) ) import Darcs.UI.SelectChanges- ( selectChanges, runSelection+ ( runSelection , selectionContext, selectionContextGeneric, selectionContextPrim , WhichChanges(First, Last, LastReversed) , viewChanges@@ -125,11 +124,11 @@ import Darcs.Util.Progress ( debugMessage ) import Darcs.Util.Path ( AbsolutePath ) -import Storage.Hashed.Tree ( Tree )+import Darcs.Util.Tree ( Tree ) -import Control.Applicative ( (<$>) ) import Control.Exception ( catch, IOException ) import Control.Monad ( when )+import Control.Monad.Trans ( liftIO ) import System.Exit ( exitSuccess ) #include "impossible.h"@@ -223,9 +222,10 @@ suspendCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () suspendCmd _ opts _args = withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $- StartRebaseJob (compression opts) (verbosity opts) YesUpdateWorking $ \repository -> do+ StartRebaseJob (RebaseJobFlags (compression opts) (verbosity opts) YesUpdateWorking) $+ \repository -> do allpatches <- readRepo repository- (rOld, Sealed qs, allpatches_tail) <- return $ takeHeadRebase allpatches+ (rOld, suspended, allpatches_tail) <- return $ takeHeadRebase allpatches (_ :> patches) <- return $ if firstMatch (parseFlags O.matchSeveralOrLast opts) then getLastPatches (parseFlags O.matchSeveralOrLast opts) allpatches_tail@@ -234,25 +234,29 @@ patches_context = selectionContext direction "suspend" (patchSelOpts True opts) Nothing Nothing (_ :> psToSuspend) <- runSelection- (selectChanges patches)+ patches patches_context when (nullFL psToSuspend) $ do putStrLn "No patches selected!" exitSuccess- repository' <- doSuspend opts repository qs rOld psToSuspend+ -- test all patches for hijacking and abort if rejected+ runHijackT RequestHijackPermission+ $ mapM_ (getAuthor "suspend" False Nothing)+ $ mapFL info psToSuspend+ repository' <- doSuspend opts repository suspended rOld psToSuspend finalizeRepositoryChanges repository' YesUpdateWorking (compression opts) return () doSuspend- :: forall p wR wU wT wX wY+ :: forall p wR wU wT wX . (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree) => [DarcsFlag]- -> Repository (Rebasing p) wR wU wT- -> FL (RebaseItem p) wT wY- -> PatchInfoAnd (Rebasing p) wT wT- -> FL (PatchInfoAnd (Rebasing p)) wX wT- -> IO (Repository (Rebasing p) wR wU wX)-doSuspend opts repository qs rOld psToSuspend = do+ -> Repository ('RepoType 'IsRebase) p wR wU wT+ -> Suspended p wT wT+ -> PatchInfoAnd ('RepoType 'IsRebase) p wT wT+ -> FL (PatchInfoAnd ('RepoType 'IsRebase) p) wX wT+ -> IO (Repository ('RepoType 'IsRebase) p wR wU wX)+doSuspend opts repository (Items qs) rOld psToSuspend = do pend <- unrecordedChanges (diffingOpts opts) repository Nothing FlippedSeal psAfterPending <- let effectPsToSuspend = effect psToSuspend in@@ -273,7 +277,7 @@ fail $ "Can't suspend selected patches without reverting some unrecorded change. Use --verbose to see the details." - rNew <- mkSuspended (mapFL_FL (ToEdit . fmapNamed unNormal . hopefully) psToSuspend +>+ qs)+ rNew <- mkRebase (Items (mapFL_FL (ToEdit . fromRebasing . hopefully) psToSuspend +>+ qs)) invalidateIndex repository repository' <- tentativelyRemovePatches repository (compression opts) YesUpdateWorking (psToSuspend +>+ (rOld :>: NilFL)) tentativelyAddToPending repository' YesUpdateWorking $ invert $ effect psToSuspend@@ -281,9 +285,6 @@ _ <- applyToWorking repository'' (verbosity opts) (invert psAfterPending) `catch` \(e :: IOException) -> fail ("Couldn't undo patch in working dir.\n" ++ show e) return repository''- where unNormal :: Rebasing p wA wB -> p wA wB- unNormal (Normal q) = q- unNormal (Suspended _) = error "Can't suspend a rebase patch" unsuspendBasicOpts :: DarcsOption a (Maybe O.AllowConflicts@@ -400,15 +401,15 @@ unsuspendCmd :: Bool -> (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () unsuspendCmd reifyFixups _ opts _args = withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $- RebaseJob (compression opts) (verbosity opts) YesUpdateWorking $- \(repository :: Repository (Rebasing p) wR wU wR) -> (do+ RebaseJob (RebaseJobFlags (compression opts) (verbosity opts) YesUpdateWorking) $+ \(repository :: Repository ('RepoType 'IsRebase) p wR wU wR) -> (do patches <- readRepo repository pend <- unrecordedChanges (diffingOpts opts) repository Nothing let checkChanges :: FL (PrimOf p) wA wB -> IO (EqCheck wA wB) checkChanges NilFL = return IsEq checkChanges _ = error "can't unsuspend when there are unrecorded changes" IsEq <- checkChanges pend :: IO (EqCheck wR wU)- (rOld, Sealed ps, _) <- return $ takeHeadRebase patches+ (rOld, Items ps, _) <- return $ takeHeadRebase patches let selects = toRebaseSelect ps @@ -432,7 +433,7 @@ warnSkip dontoffer let patches_context = selectionContextGeneric rsToPia First "unsuspend" (patchSelOpts True opts) Nothing- (chosen :> keep) <- runSelection (selectChanges offer) patches_context+ (chosen :> keep) <- runSelection offer patches_context when (nullFL chosen) $ do putStrLn "No patches selected!" exitSuccess @@ -467,8 +468,9 @@ repository' <- tentativelyRemovePatches repository (compression opts) YesUpdateWorking (rOld :>: NilFL) -- TODO should catch logfiles (fst value from updatePatchHeader) and clean them up as in AmendRecord tentativelyAddToPending repository' YesUpdateWorking effect_to_apply- (repository'', renames) <- doAdd repository' ps_to_unsuspend- rNew <- unseal mkSuspended . unseal (simplifyPushes da (mapFL_FL NameFixup renames)) $ ps_to_keep+ -- we can just let hijack attempts through here because we already asked about them on suspend time+ (repository'', renames) <- runHijackT IgnoreHijack $ doAdd repository' ps_to_unsuspend+ rNew <- unseal (mkRebase . Items) . unseal (simplifyPushes da (mapFL_FL NameFixup renames)) $ ps_to_keep repository''' <- tentativelyAddPatch repository'' (compression opts) (verbosity opts) YesUpdateWorking (n2pia rNew) finalizeRepositoryChanges repository''' YesUpdateWorking (compression opts) _ <- applyToWorking repository''' (verbosity opts) effect_to_apply `catch` \(e :: IOException) ->@@ -476,14 +478,14 @@ return () ) :: IO () where doAdd :: (RepoPatch p, ApplyState p ~ Tree)- => Repository (Rebasing p) wR wU wT+ => Repository ('RepoType 'IsRebase) p wR wU wT -> FL (WDDNamed p) wT wT2- -> IO (Repository (Rebasing p) wR wU wT2, FL (RebaseName p) wT2 wT2)+ -> HijackT IO (Repository ('RepoType 'IsRebase) p wR wU wT2, FL (RebaseName p) wT2 wT2) doAdd repo NilFL = return (repo, NilFL) doAdd repo ((p :: WDDNamed p wT wU) :>:ps) = do case wddDependedOn p of [] -> return ()- deps -> do+ deps -> liftIO $ do -- It might make sense to only print out this message once, but we might find -- that the dropped dependencies are interspersed with other output, -- e.g. if running with --ask-deps@@ -499,16 +501,17 @@ putStr "\n" -- TODO should catch logfiles (fst value from updatePatchHeader) and clean them up as in AmendRecord- p' <- snd <$> updatePatchHeader- False -- askDeps+ p' <- snd <$> updatePatchHeader "unsuspend"+ NoAskAboutDeps (patchSelOpts True opts)+ (diffAlgorithm opts) (parseFlags O.keepDate opts) (parseFlags O.selectAuthor opts) (parseFlags O.author opts) (parseFlags O.patchname opts) (parseFlags O.askLongComment opts)- repo (n2pia (fmapNamed Normal (wddPatch p))) NilFL- repo' <- tentativelyAddPatch repo (compression opts) (verbosity opts) YesUpdateWorking p'+ (n2pia (toRebasing (wddPatch p))) NilFL+ repo' <- liftIO $ tentativelyAddPatch repo (compression opts) (verbosity opts) YesUpdateWorking p' -- create a rename that undoes the change we just made, so the contexts match up let rename :: RebaseName p wU wU rename = Rename (info p') (patch2patchinfo (wddPatch p))@@ -563,18 +566,18 @@ injectCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () injectCmd _ opts _args = withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $- RebaseJob (compression opts) (verbosity opts) YesUpdateWorking $- \(repository :: Repository (Rebasing p) wR wU wR) -> do+ RebaseJob (RebaseJobFlags (compression opts) (verbosity opts) YesUpdateWorking) $+ \(repository :: Repository ('RepoType 'IsRebase) p wR wU wR) -> do patches <- readRepo repository - (rOld, Sealed ps, _) <- return $ takeHeadRebase patches+ (rOld, Items ps, _) <- return $ takeHeadRebase patches let selects = toRebaseSelect ps -- TODO this selection doesn't need to respect dependencies -- TODO we only want to select one patch: generalise withSelectedPatchFromRepo let patches_context = selectionContextGeneric rsToPia First "inject into" (patchSelOpts True opts) Nothing- (chosens :> rest_selects) <- runSelection (selectChanges selects) patches_context+ (chosens :> rest_selects) <- runSelection selects patches_context let extractSingle :: FL (RebaseSelect p) wX wY -> (FL (RebaseFixup p) :> Named p) wX wY extractSingle (RSFwd fixups toedit :>: NilFL) = fixups :> toedit@@ -585,8 +588,8 @@ name_fixups :> prim_fixups <- return $ flToNamesPrims fixups - let changes_context = selectionContextPrim Last "inject" (patchSelOpts True opts) (Just primSplitter) Nothing Nothing- (rest_fixups :> injects) <- runSelection (selectChanges prim_fixups) changes_context+ let changes_context = selectionContextPrim Last "inject" (patchSelOpts True opts) (Just (primSplitter (diffAlgorithm opts))) Nothing Nothing+ (rest_fixups :> injects) <- runSelection prim_fixups changes_context when (nullFL injects) $ do putStrLn "No changes selected!"@@ -595,7 +598,8 @@ -- Don't bother to update patch header since unsuspend will do that later let da = diffAlgorithm opts toeditNew = fmapFL_Named (mapFL_FL fromPrim . canonizeFL da . (injects +>+) . effect) toedit- rNew <- unseal mkSuspended $ unseal (simplifyPushes da (mapFL_FL NameFixup name_fixups))+ rNew <- unseal (mkRebase . Items)+ $ unseal (simplifyPushes da (mapFL_FL NameFixup name_fixups)) $ simplifyPushes da (mapFL_FL PrimFixup rest_fixups) $ ToEdit toeditNew :>: fromRebaseSelect rest_selects @@ -644,17 +648,17 @@ obliterateCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () obliterateCmd _ opts _args = withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $- RebaseJob (compression opts) (verbosity opts) YesUpdateWorking $- \(repository :: Repository (Rebasing p) wR wU wR) -> (do+ RebaseJob (RebaseJobFlags (compression opts) (verbosity opts) YesUpdateWorking) $+ \(repository :: Repository ('RepoType 'IsRebase) p wR wU wR) -> (do patches <- readRepo repository - (rOld, Sealed ps, _) <- return $ takeHeadRebase patches+ (rOld, Items ps, _) <- return $ takeHeadRebase patches let selects = toRebaseSelect ps -- TODO this selection doesn't need to respect dependencies let patches_context = selectionContextGeneric rsToPia First "obliterate" (obliteratePatchSelOpts opts) Nothing- (chosen :> keep) <- runSelection (selectChanges selects) patches_context+ (chosen :> keep) <- runSelection selects patches_context when (nullFL chosen) $ do putStrLn "No patches selected!" exitSuccess @@ -669,7 +673,7 @@ do_obliterate qs let ps_to_keep = do_obliterate (fromRebaseSelect chosen) (fromRebaseSelect keep)- rNew <- unseal mkSuspended ps_to_keep+ rNew <- unseal (mkRebase . Items) ps_to_keep repository' <- tentativelyRemovePatches repository (compression opts) YesUpdateWorking (rOld :>: NilFL) repository'' <- tentativelyAddPatch repository' (compression opts) (verbosity opts) YesUpdateWorking (n2pia rNew)@@ -906,21 +910,24 @@ data RebasePatchApplier = RebasePatchApplier instance PatchApplier RebasePatchApplier where- type CarrierType RebasePatchApplier p = Rebasing p+ type ApplierRepoTypeConstraint RebasePatchApplier rt = rt ~ 'RepoType 'IsRebase repoJob RebasePatchApplier opts f =- StartRebaseJob (compression opts) (verbosity opts) YesUpdateWorking (f PatchProxy)+ StartRebaseJob+ (RebaseJobFlags (compression opts) (verbosity opts) YesUpdateWorking)+ (f PatchProxy) applyPatches RebasePatchApplier PatchProxy = applyPatchesForRebaseCmd applyPatchesForRebaseCmd :: forall p wR wU wX wT wZ- . (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ . ( RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree+ ) => String -> [DarcsFlag] -> String- -> Repository (Rebasing p) wR wU wT- -> FL (PatchInfoAnd (Rebasing p)) wX wT- -> FL (PatchInfoAnd (Rebasing p)) wX wZ+ -> Repository ('RepoType 'IsRebase) p wR wU wT+ -> FL (PatchInfoAnd ('RepoType 'IsRebase) p) wX wT+ -> FL (PatchInfoAnd ('RepoType 'IsRebase) p) wX wZ -> IO () applyPatchesForRebaseCmd cmdName opts _from_whom repository us' to_be_applied = do printDryRunMessageAndExit cmdName@@ -946,11 +953,16 @@ -- command, review if there are any we should keep let patches_context = selectionContext LastReversed "suspend" applyPatchSelOpts Nothing Nothing - (usKeep :> usToSuspend) <- runSelection (selectChanges usConflicted) patches_context+ (usKeep :> usToSuspend) <- runSelection usConflicted patches_context - (rOld, Sealed qs, _) <- return $ takeHeadRebaseFL us'- repository' <- doSuspend opts repository qs rOld usToSuspend+ -- test all patches for hijacking and abort if rejected+ runHijackT RequestHijackPermission+ $ mapM_ (getAuthor "suspend" False Nothing)+ $ mapFL info usToSuspend + (rOld, suspended, _) <- return $ takeHeadRebaseFL us'+ repository' <- doSuspend opts repository suspended rOld usToSuspend+ -- TODO This is a nasty hack, caused by the fact that readUnrecorded -- claims to read the tentative state but actual reads the committed state -- as a result we have to commit here so that tentativelyMergePatches does@@ -978,7 +990,6 @@ applyPatchSelOpts = S.PatchSelectionOptions { S.verbosity = O.NormalVerbosity , S.matchFlags = []- , S.diffAlgorithm = O.PatienceDiff , S.interactive = True , S.selectDeps = O.PromptDeps -- option not supported, use default , S.summary = O.NoSummary@@ -994,7 +1005,6 @@ patchSelOpts defInteractive flags = S.PatchSelectionOptions { S.verbosity = verbosity flags , S.matchFlags = parseFlags O.matchSeveralOrLast flags- , S.diffAlgorithm = diffAlgorithm flags , S.interactive = isInteractive defInteractive flags , S.selectDeps = selectDeps flags , S.summary = hasSummary O.NoSummary flags@@ -1051,9 +1061,9 @@ logCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () logCmd _ opts _files = withRepository (useCache opts) $- RebaseJob (compression opts) (verbosity opts) YesUpdateWorking $ \repository -> do+ RebaseJob (RebaseJobFlags (compression opts) (verbosity opts) YesUpdateWorking) $ \repository -> do patches <- readRepo repository- (_, Sealed ps, _) <- return $ takeHeadRebase patches+ (_, Items ps, _) <- return $ takeHeadRebase patches let psToShow = toRebaseChanges ps if isInteractive False opts then viewChanges (patchSelOpts False opts) (mapFL Sealed2 psToShow)
src/Darcs/UI/Commands/Record.hs view
@@ -15,32 +15,26 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP, PatternGuards #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-} module Darcs.UI.Commands.Record ( record , commit- , getLog -- used by amend and tag, too , recordConfig, RecordConfig(..) -- needed for darcsden ) where -import Prelude hiding ( (^), catch )+import Prelude ()+import Darcs.Prelude+import Data.Foldable ( traverse_ ) -import Control.Applicative ( (<$>) )-import Control.Exception ( handleJust, catch, IOException )+import Control.Exception ( handleJust ) import Control.Monad ( when, unless, void )-import System.IO ( stdin )-import Data.List ( sort, isPrefixOf )+import Data.List ( sort ) import Data.Char ( ord ) import System.Exit ( exitFailure, exitSuccess, ExitCode(..) ) import System.Directory ( removeFile )-import qualified Data.ByteString as B ( hPut ) -import Darcs.Repository.Lock- ( readLocaleFile- , writeLocaleFile- , appendToFile- ) import Darcs.Patch.PatchInfoAnd ( n2pia ) import Darcs.Repository ( Repository@@ -53,9 +47,8 @@ , readRecorded , listRegisteredFiles )-import Darcs.Patch- ( RepoPatch, Patchy, PrimOf, PrimPatch- , namepatch, summaryFL, adddeps, fromPrims )+import Darcs.Patch ( IsRepoType, RepoPatch, PrimOf, fromPrims )+import Darcs.Patch.Named.Wrapped ( namepatch, adddeps ) import Darcs.Patch.Witnesses.Sealed import Darcs.Patch.Witnesses.Ordered ( FL(..), (:>)(..), nullFL )@@ -63,16 +56,14 @@ import Darcs.Patch.Info ( PatchInfo ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Split ( primSplitter )-import Darcs.UI.External ( editFile ) import Darcs.UI.SelectChanges- ( selectChanges- , WhichChanges(..)+ ( WhichChanges(..) , selectionContextPrim , runSelection , askAboutDepends ) import qualified Darcs.UI.SelectChanges as S ( PatchSelectionOptions(..) )-import Darcs.Util.Path ( FilePathLike, SubPath, toFilePath, AbsolutePath )+import Darcs.Util.Path ( SubPath, toFilePath, AbsolutePath ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts , nodefaults@@ -81,7 +72,7 @@ , setEnvDarcsPatches , amInHashedRepository )-import Darcs.UI.Commands.Util ( announceFiles, filterExistingFiles,+import Darcs.UI.Commands.Util ( announceFiles, filterExistingPaths, testTentativeAndMaybeExit ) import Darcs.UI.Flags ( DarcsFlag@@ -89,21 +80,22 @@ , getAuthor , getDate , diffOpts+ , scanKnown , fixSubPaths ) import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, oparse, defaultFlags )+import Darcs.UI.PatchHeader ( getLog ) import qualified Darcs.UI.Options.All as O-import Darcs.Repository.Flags ( UpdateWorking (..), DryRun(NoDryRun) )-import Darcs.Repository.Util ( getMovesPs, getReplaces )+import Darcs.Repository.Flags ( UpdateWorking (..), DryRun(NoDryRun), ScanKnown(..) )+import Darcs.Repository.State ( getMovesPs, getReplaces ) import Darcs.Util.Exception ( clarifyErrors )-import Darcs.Util.Prompt ( askUser, promptYorn )+import Darcs.Util.Prompt ( promptYorn ) import Darcs.Util.Progress ( debugMessage ) import Darcs.Util.Global ( darcsLastMessage ) import Darcs.Patch.Progress ( progressFL )-import Darcs.Util.Printer ( putDocLn, hPutDocLn, text, ($$), prefixLines, RenderMode(..) )-import Darcs.Util.ByteString ( encodeLocale )-import qualified Darcs.Util.Ratified as Ratified ( hGetContents )-import Storage.Hashed.Tree( Tree )+import Darcs.Util.Printer ( putDocLn, text, (<+>) )+import Darcs.Util.Text ( pathlist )+import Darcs.Util.Tree( Tree ) recordDescription :: String recordDescription = "Create a patch from unrecorded changes."@@ -143,8 +135,8 @@ ^ O.diffAlgorithm recordAdvancedOpts :: DarcsOption a- (O.Logfile -> O.Compression -> O.UseIndex -> O.UMask -> O.SetScriptsExecutable -> a)-recordAdvancedOpts = O.logfile ^ O.compress ^ O.useIndex ^ O.umask ^ O.setScriptsExecutable+ (O.Logfile -> O.Compression -> O.UseIndex -> O.UMask -> O.SetScriptsExecutable -> O.IncludeBoring -> a)+recordAdvancedOpts = O.logfile ^ O.compress ^ O.useIndex ^ O.umask ^ O.setScriptsExecutable ^ O.includeBoring recordOpts :: DarcsOption a (Maybe String@@ -168,6 +160,7 @@ -> O.UseIndex -> O.UMask -> O.SetScriptsExecutable+ -> O.IncludeBoring -> O.UseCache -> Maybe String -> Bool@@ -194,6 +187,7 @@ , useIndex :: O.UseIndex , umask :: O.UMask , sse :: O.SetScriptsExecutable+ , includeBoring :: O.IncludeBoring , useCache :: O.UseCache } @@ -223,27 +217,37 @@ commit :: DarcsCommand RecordConfig commit = commandAlias "commit" Nothing record +reportNonExisting :: ScanKnown -> ([SubPath], [SubPath]) -> IO ()+reportNonExisting scan (paths_only_in_working, _) = do+ unless (scan /= ScanKnown || null paths_only_in_working) $ putDocLn $+ "These paths are not yet in the repository and will be added:" <+>+ pathlist (map toFilePath paths_only_in_working)+ recordCmd :: (AbsolutePath, AbsolutePath) -> RecordConfig -> [String] -> IO () recordCmd fps cfg args = do- checkNameIsNotOption (patchname cfg) (isInteractive True cfg)- withRepoLock NoDryRun (useCache cfg) YesUpdateWorking (umask cfg) $ RepoJob $ \(repository :: Repository p wR wU wR) -> do+ checkNameIsNotOption (patchname cfg) (isInteractive cfg)+ withRepoLock NoDryRun (useCache cfg) YesUpdateWorking (umask cfg) $ RepoJob $ \(repository :: Repository rt p wR wU wR) -> do files <- if null args then return Nothing else Just . sort <$> fixSubPaths fps args when (files == Just []) $ fail "No valid arguments were given."- announceFiles files "Recording changes in"- existing_files <- maybe (return Nothing)- (fmap Just . filterExistingFiles repository (O.adds (lookfor cfg))) files+ let scan = scanKnown (O.adds (lookfor cfg)) (includeBoring cfg)+ files' <- traverse (filterExistingPaths repository (verbosity cfg) (useIndex cfg) scan) files+ when (verbosity cfg /= O.Quiet) $+ traverse_ (reportNonExisting scan) files'+ let existing_files = fmap snd files' when (existing_files == Just []) $- fail "None of the files you specified exist!"+ fail "None of the files you specified exist."+ announceFiles (verbosity cfg) existing_files "Recording changes in" debugMessage "About to get the unrecorded changes." Sealed replacePs <- if O.replaces (lookfor cfg) == O.YesLookForReplaces- then getReplaces (diffingOpts cfg) repository files- else return (Sealed NilFL)+ then getReplaces (diffingOpts cfg) repository files+ else return (Sealed NilFL) movesPs <- if O.moves (lookfor cfg) == O.YesLookForMoves then getMovesPs repository files else return NilFL- changes <- unrecordedChangesWithPatches (diffingOpts cfg) repository files+ changes <- unrecordedChangesWithPatches movesPs (unsafeCoerceP replacePs :: FL (PrimOf p) wR wR)+ (diffingOpts cfg) repository files debugMessage "I've got unrecorded changes." case changes of NilFL | not (askDeps cfg) -> do@@ -264,19 +268,20 @@ confirmed <- promptYorn $ "You specified " ++ show name ++ " as the patch name. Is that really what you want?" unless confirmed $ putStrLn "Okay, aborting the record." >> exitFailure -doRecord :: (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)- => Repository p wR wU wR -> RecordConfig -> Maybe [SubPath] -> FL (PrimOf p) wR wX -> IO ()+doRecord :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => Repository rt p wR wU wR -> RecordConfig -> Maybe [SubPath] -> FL (PrimOf p) wR wX -> IO () doRecord repository cfg files ps = do date <- getDate (pipe cfg) my_author <- getAuthor (author cfg) (pipe cfg) debugMessage "I'm slurping the repository."- debugMessage "About to select changes..." pristine <- readRecorded repository- (chs :> _ ) <- runSelection (selectChanges ps) $- selectionContextPrim First "record" (patchSelOpts cfg) (Just primSplitter)+ debugMessage "About to select changes..."+ (chs :> _ ) <- runSelection ps $+ selectionContextPrim First "record" (patchSelOpts cfg)+ (Just (primSplitter (diffAlgorithm cfg))) (map toFilePath <$> files) (Just pristine)- when (is_empty_but_not_askdeps chs) $+ when (not (askDeps cfg) && nullFL chs) $ do putStrLn "Ok, if you don't want to record anything, that's fine!" exitSuccess handleJust onlySuccessfulExits (\_ -> return ()) $@@ -290,13 +295,9 @@ (name, my_log, logf) <- getLog (patchname cfg) (pipe cfg) (logfile cfg) (askLongComment cfg) Nothing chs debugMessage ("Patch name as received from getLog: " ++ show (map ord name)) doActualRecord repository cfg name date my_author my_log logf deps chs- where is_empty_but_not_askdeps l- | askDeps cfg = False- -- a "partial tag" patch; see below.- | otherwise = nullFL l -doActualRecord :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wR+doActualRecord :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wR -> RecordConfig -> String -> String -> String -> [String] -> Maybe String@@ -315,7 +316,7 @@ (verbosity cfg) (testChanges cfg) (sse cfg)- (isInteractive True cfg)+ (isInteractive cfg) ("you have a bad patch: '" ++ name ++ "'") "record it" (Just failuremessage) finalizeRepositoryChanges repository YesUpdateWorking (compress cfg)@@ -342,10 +343,8 @@ , "interactive selection, to let you enter the patch name (first line) and" , "the patch description (subsequent lines)." , ""- , "The patch name should be a short sentence that concisely describes the"- , "patch, such as \"Add error handling to main event loop.\" You can"- , "supply it in advance with the `-m` option, in which case no text editor"- , "is launched, unless you use the `--edit-long-comment` option."+ , "You can supply the patch name in advance with the `-m` option, in which"+ , "case no text editor is launched, unless you use `--edit-long-comment`." , "" , "The patch description is an optional block of free-form text. It is" , "used to supply additional information that doesn't fit in the patch"@@ -355,169 +354,32 @@ , "A technical difference between patch name and patch description, is" , "that matching with the flag `-p` is only done on patch names." , ""- , "Finally, the `--logfile` option allows you to supply a file that"- , "already contains the patch name and patch description. This is"- , "useful if a previous record failed and left a `darcs-record-0` file."+ , "Finally, the `--logfile` option allows you to supply a file that already"+ , "contains the patch name and description. This is useful if a previous"+ , "record failed and left a `_darcs/patch_description.txt` file." , "" , unlines fileHelpAuthor- , "" , "If you want to manually define any extra dependencies for your patch,"- , "you can use the `--ask-deps` flag, and darcs will ask you for the patch's"- , "dependencies. Some dependencies may be automatically inferred from the"- , "patch's content and cannot be removed. "- , "A patch with specific dependencies can be empty."+ , "you can use the `--ask-deps` flag. Some dependencies may be automatically"+ , "inferred from the patch's content and cannot be removed. A patch with"+ , "specific dependencies can be empty." , "" , "The patch date is generated automatically. It can only be spoofed by" , "using the `--pipe` option." , "" , "If you run record with the `--pipe` option, you will be prompted for" , "the patch date, author, and the long comment. The long comment will extend"- , "until the end of file or stdin is reached (ctrl-D on Unixy systems, ctrl-Z"- , "on systems running a Microsoft OS)."- , ""- , "This interface is intended for scripting darcs, in particular for writing"- , "repository conversion scripts. The prompts are intended mostly as a useful"- , "guide (since scripts won't need them), to help you understand the format in"- , "which to provide the input. Here's an example of what the `--pipe`"- , "prompts look like:"+ , "until the end of file or stdin is reached. This interface is intended for"+ , "scripting darcs, in particular for writing repository conversion scripts."+ , "The prompts are intended mostly as a useful guide (since scripts won't"+ , "need them), to help you understand the input format. Here's an example of"+ , "what the `--pipe` prompts look like:" , "" , " What is the date? Mon Nov 15 13:38:01 EST 2004" , " Who is the author? David Roundy" , " What is the log? One or more comment lines" ] -data PName = FlagPatchName String | PriorPatchName String | NoPatchName---- | Get the patch name and long description from one of------ * the configuration (flags, defaults, hard-coded)------ * an existing log file------ * stdin (e.g. a pipe)------ * a text editor------ It ensures the patch name is not empty nor starts with the prefix TAG.------ The last result component is a possible path to a temporary file that should be removed later.-getLog :: forall prim wX wY . (Patchy prim, PrimPatch prim)- => Maybe String -- ^ patchname option- -> Bool -- ^ pipe option- -> O.Logfile -- ^ logfile option- -> Maybe O.AskLongComment -- ^ askLongComment option- -> Maybe (String, [String]) -- ^ possibly an existing patch name and long description- -> FL prim wX wY -- ^ changes to record- -> IO (String, [String], Maybe String) -- ^ patch name, long description and possibly the path- -- to the temporary file that should be removed later-getLog m_name has_pipe log_file ask_long m_old chs = go has_pipe log_file ask_long where- go True _ _ = do- p <- case patchname_specified of- FlagPatchName p -> return p- PriorPatchName p -> return p- NoPatchName -> prompt_patchname False- putStrLn "What is the log?"- thelog <- lines `fmap` Ratified.hGetContents stdin- return (p, thelog, Nothing)- go _ (O.Logfile { O._logfile = Just f }) _ = do- mlp <- lines `fmap` readLocaleFile f `catch` (\(_ :: IOException) -> return [])- firstname <- case (patchname_specified, mlp) of- (FlagPatchName p, []) -> return p- (_, p:_) -> if badName p- then prompt_patchname True- else return p -- logfile trumps prior!- (PriorPatchName p, []) -> return p- (NoPatchName, []) -> prompt_patchname True- append_info f firstname- when (ask_long == Just O.YesEditLongComment) (void $ editFile f)- (name, thelog) <- read_long_comment f firstname- return (name, thelog, if O._rmlogfile log_file then Just $ toFilePath f else Nothing)- go _ _ (Just O.YesEditLongComment) =- case patchname_specified of- FlagPatchName p -> actually_get_log p- PriorPatchName p -> actually_get_log p- NoPatchName -> actually_get_log ""- go _ _ (Just O.NoEditLongComment) =- case patchname_specified of- FlagPatchName p -> return (p, default_log, Nothing) -- record (or amend) -m- PriorPatchName p -> return (p, default_log, Nothing) -- amend- NoPatchName -> do p <- prompt_patchname True -- record- return (p, [], Nothing)- go _ _ (Just O.PromptLongComment) =- case patchname_specified of- FlagPatchName p -> prompt_long_comment p -- record (or amend) -m- PriorPatchName p -> prompt_long_comment p- NoPatchName -> prompt_patchname True >>= prompt_long_comment- go _ _ Nothing =- case patchname_specified of- FlagPatchName p -> return (p, default_log, Nothing) -- record (or amend) -m- PriorPatchName "" -> actually_get_log ""- PriorPatchName p -> return (p, default_log, Nothing)- NoPatchName -> actually_get_log ""-- patchname_specified = case (m_name, m_old) of- (Just name, _) | badName name -> NoPatchName- | otherwise -> FlagPatchName name- (Nothing, Just (name,_)) -> PriorPatchName name- (Nothing, Nothing) -> NoPatchName-- badName "" = True- badName n = "TAG" `isPrefixOf` n-- default_log = case m_old of- Nothing -> []- Just (_,l) -> l-- prompt_patchname retry =- do n <- askUser "What is the patch name? "- if badName n- then if retry then prompt_patchname retry- else fail "Bad patch name!"- else return n-- prompt_long_comment oldname =- do y <- promptYorn "Do you want to add a long comment?"- if y then actually_get_log oldname- else return (oldname, [], Nothing)-- actually_get_log p = do let logf = darcsLastMessage- -- 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- _ <- editFile logf- (name,long) <- read_long_comment logf p- if badName name- then do putStrLn "WARNING: empty or incorrect patch name!"- pn <- prompt_patchname True- return (pn, long, Nothing)- else return (name,long,Just logf)-- read_long_comment :: FilePathLike p => p -> String -> IO (String, [String])- read_long_comment f oldname =- do f' <- readLocaleFile f- let t = filter (not.("#" `isPrefixOf`)) $ (lines.filter (/='\r')) f'- case t of [] -> return (oldname, [])- (n:ls) -> return (n, ls)-- append_info f oldname =- do fc <- readLocaleFile f- appendToFile f $ \h ->- do case fc of- _ | null (lines fc) -> B.hPut h (encodeLocale (oldname ++ "\n"))- | last fc /= '\n' -> B.hPut h (encodeLocale "\n")- | otherwise -> return ()- hPutDocLn Encode h- $ text "# Please enter the patch name in the first line, and"- $$ text "# optionally, a long description in the following lines."- $$ text "#"- $$ text "# Lines starting with '#' will be ignored."- $$ text "#"- $$ text "#"- $$ text "# This patch contains the following changes:"- $$ text "#"- $$ prefixLines (text "#") (summaryFL chs)- onlySuccessfulExits :: ExitCode -> Maybe () onlySuccessfulExits ExitSuccess = Just () onlySuccessfulExits _ = Nothing@@ -544,15 +406,14 @@ patchSelOpts cfg = S.PatchSelectionOptions { S.verbosity = verbosity cfg , S.matchFlags = []- , S.diffAlgorithm = diffAlgorithm cfg- , S.interactive = isInteractive True cfg+ , S.interactive = isInteractive cfg , S.selectDeps = O.PromptDeps -- option not supported, use default , S.summary = O.NoSummary -- option not supported, use default , S.withContext = withContext cfg } diffingOpts :: RecordConfig -> (O.UseIndex, O.ScanKnown, O.DiffAlgorithm)-diffingOpts cfg = diffOpts (useIndex cfg) (O.adds (lookfor cfg)) False (diffAlgorithm cfg)+diffingOpts cfg = diffOpts (useIndex cfg) (O.adds (lookfor cfg)) O.NoIncludeBoring (diffAlgorithm cfg) -isInteractive :: Bool -> RecordConfig -> Bool-isInteractive def = maybe def id . interactive+isInteractive :: RecordConfig -> Bool+isInteractive = maybe True id . interactive
src/Darcs/UI/Commands/Remove.hs view
@@ -19,6 +19,9 @@ module Darcs.UI.Commands.Remove ( remove, rm, unadd ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( (^) ) import Control.Monad ( when, foldM )@@ -49,11 +52,9 @@ import Darcs.Patch.Witnesses.Ordered ( FL(..), (+>+), nullFL ) import Darcs.Patch.Witnesses.Sealed ( Sealed(..), Gap(..), FreeLeft, unFreeLeft ) import Darcs.Repository.Prefs ( filetypeFunction, FileType )-import Storage.Hashed.Tree( Tree, TreeItem(..), find, modifyTree, expand, list )+import Darcs.Util.Tree( Tree, TreeItem(..), find, modifyTree, expand, list ) import Darcs.Util.Path( anchorPath, AnchoredPath, fn2fp, SubPath, sp2fn- , AbsolutePath )-import Storage.Hashed( floatPath )-+ , AbsolutePath, floatPath ) import Darcs.Util.Printer ( text ) @@ -137,7 +138,7 @@ -- flag should be handled by the caller by adding all offspring of a directory -- to the files list. makeRemovePatch :: (RepoPatch p, ApplyState p ~ Tree)- => [DarcsFlag] -> Repository p wR wU wT+ => [DarcsFlag] -> Repository rt p wR wU wT -> [SubPath] -> IO (Sealed (FL (PrimOf p) wU)) makeRemovePatch opts repository files = do recorded <- expand =<< readRecordedAndPending repository
src/Darcs/UI/Commands/Repair.hs view
@@ -21,10 +21,10 @@ , check ) where -import Prelude hiding ( (^), catch )+import Prelude ()+import Darcs.Prelude import Control.Monad ( when, unless )-import Control.Applicative( (<$>) ) import Control.Exception ( catch, IOException ) import System.Exit ( ExitCode(..), exitWith ) import System.Directory( renameFile )@@ -44,7 +44,7 @@ import Darcs.Repository ( Repository, withRepository, readRecorded, RepoJob(..), withRepoLock, replacePristine, writePatchSet )-import Darcs.Patch ( RepoPatch, showPatch, PrimOf )+import Darcs.Patch ( IsRepoType, RepoPatch, showPatch, PrimOf ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Witnesses.Ordered ( FL(..) ) import Darcs.Patch.Witnesses.Sealed ( Sealed(..), unFreeLeft )@@ -53,7 +53,7 @@ import Darcs.Util.Global ( darcsdir ) import Darcs.Util.Printer ( text, ($$), (<+>) ) import Darcs.Util.Path ( AbsolutePath )-import Storage.Hashed.Tree( Tree )+import Darcs.Util.Tree( Tree ) repairDescription :: String@@ -137,8 +137,8 @@ putStrLn "Bad index discarded." check'- :: forall p wR wU wT . (RepoPatch p, ApplyState p ~ Tree)- => [DarcsFlag] -> Repository p wR wU wT -> IO ()+ :: forall rt p wR wU wT . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => [DarcsFlag] -> Repository rt p wR wU wT -> IO () check' opts repository = do state <- replayRepositoryInTemp (F.diffAlgorithm opts) repository (compression opts) (verbosity opts) failed <-
src/Darcs/UI/Commands/Replace.hs view
@@ -22,18 +22,18 @@ , defaultToks ) where -import Prelude hiding ( (^), catch )+import Prelude ()+import Darcs.Prelude import qualified Data.ByteString.Char8 as BC import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString as BS import Data.Char ( isSpace ) import Data.Maybe ( isJust )-import Control.Applicative( (<$>) ) import Control.Exception ( catch, IOException ) import Control.Monad ( unless, filterM, void )-import Storage.Hashed.Tree( readBlob, modifyTree, findFile, TreeItem(..), Tree- , makeBlobBS )+import Darcs.Util.Tree( readBlob, modifyTree, findFile, TreeItem(..), Tree+ , makeBlobBS ) import Darcs.Util.Path( SubPath, toFilePath, AbsolutePath ) import Darcs.UI.Flags ( DarcsFlag( ForceReplace, Toks )@@ -44,9 +44,8 @@ import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository ) import Darcs.Repository.Diff( treeDiff ) import Darcs.Patch ( Patchy, PrimPatch, tokreplace, forceTokReplace- , applyToTree )+ , maybeApplyToTree ) import Darcs.Patch.Apply( ApplyState )-import Darcs.Patch.Patchy ( Apply ) import Darcs.Patch.RegChars ( regChars ) import Darcs.Repository ( withRepoLock@@ -57,8 +56,9 @@ , readRecordedAndPending , listRegisteredFiles )+import Darcs.Patch.TokenReplace ( defaultToks ) import Darcs.Repository.Prefs ( FileType(TextFile) )-import Darcs.Repository.Util ( floatSubPath, defaultToks )+import Darcs.Util.Path ( floatSubPath ) import Darcs.Patch.Witnesses.Ordered ( FL(..), (+>+), concatFL, toFL, nullFL ) import Darcs.Patch.Witnesses.Sealed ( Sealed(..), mapSeal, FreeLeft, Gap(..), unFreeLeft, unseal ) @@ -244,14 +244,6 @@ tokreplace (toFilePath f) toks old new :>: NilFL replaceCmd _ _ [_, _] = fail "You need to supply a list of files to replace in!" replaceCmd _ _ _ = fail "Usage: darcs replace OLD NEW [FILES]"---- | Attempts to apply a given replace patch to a Tree. If the apply fails (if--- the file the patch applies to already contains the target token), we return--- Nothing, otherwise we return the updated Tree.-maybeApplyToTree :: (Apply p, ApplyState p ~ Tree) => p wX wY -> Tree IO- -> IO (Maybe (Tree IO))-maybeApplyToTree patch tree =- (Just `fmap` applyToTree patch tree) `catch` (\(_ :: IOException) -> return Nothing) filenameToks :: String filenameToks = "A-Za-z_0-9\\-\\."
src/Darcs/UI/Commands/Revert.hs view
@@ -15,24 +15,25 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. +{-# LANGUAGE OverloadedStrings #-} module Darcs.UI.Commands.Revert ( revert ) where -import Prelude hiding ( (^), catch )+import Prelude ()+import Darcs.Prelude -import Control.Applicative ( (<$>) ) import Control.Exception ( catch, IOException )-import Control.Monad ( when ) import Data.List ( sort ) import Darcs.UI.Flags- ( DarcsFlag( Debug ), diffingOpts, verbosity, diffAlgorithm, isInteractive, isUnified+ ( DarcsFlag, diffingOpts, verbosity, diffAlgorithm, isInteractive, isUnified , dryRun, umask, useCache, fixSubPaths ) import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags ) import qualified Darcs.UI.Options.All as O import Darcs.Repository.Flags ( UpdateWorking(..) )-import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository )+import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository, putInfo ) import Darcs.UI.Commands.Util ( announceFiles ) import Darcs.UI.Commands.Unrevert ( writeUnrevert )+import Darcs.Util.Global ( debugMessage ) import Darcs.Util.Path ( toFilePath, AbsolutePath ) import Darcs.Repository ( withRepoLock@@ -48,8 +49,7 @@ import Darcs.Patch.Witnesses.Ordered ( FL(..), (:>)(..), nullFL, (+>+) ) import Darcs.Patch.Witnesses.Sealed ( Sealed(..) ) import Darcs.UI.SelectChanges- ( selectChanges- , WhichChanges(Last)+ ( WhichChanges(Last) , selectionContextPrim , runSelection )@@ -109,7 +109,6 @@ patchSelOpts flags = S.PatchSelectionOptions { S.verbosity = verbosity flags , S.matchFlags = []- , S.diffAlgorithm = diffAlgorithm flags , S.interactive = isInteractive True flags , S.selectDeps = O.PromptDeps -- option not supported, use default , S.summary = O.NoSummary -- option not supported, use default@@ -140,30 +139,29 @@ withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do files <- if null args then return Nothing else Just . sort <$> fixSubPaths fps args- announceFiles files "Reverting changes in"+ announceFiles (verbosity opts) files "Reverting changes in" changes <- unrecordedChanges (diffingOpts opts {- always ScanKnown here -}) repository files let pre_changed_files = effectOnFilePaths (invert changes) . map toFilePath <$> files recorded <- readRecorded repository Sealed touching_changes <- return (chooseTouching pre_changed_files changes)- (case touching_changes of- NilFL -> putStrLn "There are no changes to revert!"+ case touching_changes of+ NilFL -> putInfo opts "There are no changes to revert!" _ -> do let context = selectionContextPrim Last "revert" (patchSelOpts opts)- (Just reversePrimSplitter) pre_changed_files (Just recorded)- (norevert:>p) <- runSelection (selectChanges changes) context+ (Just (reversePrimSplitter (diffAlgorithm opts)))+ pre_changed_files (Just recorded)+ (norevert:>p) <- runSelection changes context if nullFL p- then putStrLn $ "If you don't want to revert after all," ++- " that's fine with me!"+ then putInfo opts $ "If you don't want to revert after all, that's fine with me!" else do addToPending repository YesUpdateWorking $ invert p- when (Debug `elem` opts) $ putStrLn "About to write the unrevert file."+ debugMessage "About to write the unrevert file." case commute (norevert:>p) of Just (p':>_) -> writeUnrevert repository p' recorded NilFL Nothing -> writeUnrevert repository (norevert+>+p) recorded NilFL- when (Debug `elem` opts) $ putStrLn "About to apply to the working directory."+ debugMessage "About to apply to the working directory." _ <- applyToWorking repository (verbosity opts) (invert p) `catch` \(e :: IOException) -> fail ("Unable to apply inverse patch!" ++ show e)- return ()) :: IO ()- putStrLn "Finished reverting."-+ return ()+ putInfo opts "Finished reverting."
src/Darcs/UI/Commands/Rollback.hs view
@@ -19,20 +19,21 @@ module Darcs.UI.Commands.Rollback ( rollback ) where -import Prelude hiding ( (^), catch )+import Prelude ()+import Darcs.Prelude -import Control.Applicative ( (<$>) ) import Control.Exception ( catch, IOException ) import Control.Monad ( when ) import Data.List ( sort )-import Storage.Hashed.Tree( Tree )+import Darcs.Util.Tree( Tree ) import System.Exit ( exitSuccess ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Match ( firstMatch ) import Darcs.Patch.PatchInfoAnd ( n2pia )-import Darcs.Patch ( RepoPatch, invert, effect, fromPrims, sortCoalesceFL,- canonize, anonymous, PrimOf )+import Darcs.Patch ( IsRepoType, RepoPatch, invert, effect, fromPrims, sortCoalesceFL,+ canonize, PrimOf )+import Darcs.Patch.Named.Wrapped ( anonymous ) import Darcs.Patch.Set ( PatchSet(..), newset2FL ) import Darcs.Patch.Split ( reversePrimSplitter ) import Darcs.Patch.Witnesses.Ordered ( FL(..), (:>)(..), RL(..), concatFL,@@ -56,7 +57,7 @@ , defaultFlags, parseFlags ) import qualified Darcs.UI.Options.All as O-import Darcs.UI.SelectChanges ( selectChanges, WhichChanges(..),+import Darcs.UI.SelectChanges ( WhichChanges(..), selectionContext, selectionContextPrim, runSelection ) import qualified Darcs.UI.SelectChanges as S ( PatchSelectionOptions(..) )@@ -65,20 +66,20 @@ rollbackDescription :: String rollbackDescription =- "Apply the inverse of recorded changes to the working copy."+ "Apply the inverse of recorded changes to the working tree." rollbackHelp :: String rollbackHelp = unlines [ "Rollback is used to undo the effects of some changes from patches" , "in the repository. The selected changes are undone in your working"- , "copy, but the repository is left unchanged. First you are offered a"+ , "tree, but the repository is left unchanged. First you are offered a" , "choice of which patches to undo, then which changes within the" , "patches to undo." , "" , "Before doing `rollback`, you may want to temporarily undo the changes"- , "of your working copy (if there are) and save them for later use."+ , "of your working tree (if there are) and save them for later use." , "To do so, you can run `revert`, then run `rollback`, record a patch,"- , "and run `unrevert` to restore the saved changes into your working copy."+ , "and run `unrevert` to restore the saved changes into your working tree." ] rollbackBasicOpts :: DarcsOption a@@ -116,7 +117,6 @@ patchSelOpts flags = S.PatchSelectionOptions { S.verbosity = verbosity flags , S.matchFlags = parseFlags O.matchSeveralOrLast flags- , S.diffAlgorithm = diffAlgorithm flags , S.interactive = isInteractive True flags , S.selectDeps = O.PromptDeps , S.summary = O.NoSummary@@ -154,7 +154,7 @@ else Just . sort <$> fixSubPaths fps args when (files == Just []) $ fail "No valid arguments were given."- announceFiles files "Rolling back changes in"+ announceFiles (verbosity opts) files "Rolling back changes in" allpatches <- readRepo repository let matchFlags = parseFlags O.matchSeveralOrLast opts (_ :> patches) <- return $@@ -164,17 +164,18 @@ let filesFps = map toFilePath <$> files patchCtx = selectionContext LastReversed "rollback" (patchSelOpts opts) Nothing filesFps (_ :> ps) <-- runSelection (selectChanges patches) patchCtx+ runSelection patches patchCtx exitIfNothingSelected ps "patches" setEnvDarcsPatches ps let hunkContext = selectionContextPrim Last "rollback" (patchSelOpts opts)- (Just reversePrimSplitter) filesFps Nothing+ (Just (reversePrimSplitter (diffAlgorithm opts)))+ filesFps Nothing hunks = concatFL . mapFL_FL (canonize $ F.diffAlgorithm opts) . sortCoalesceFL . effect $ ps- whatToUndo <- runSelection (selectChanges hunks) hunkContext+ whatToUndo <- runSelection hunks hunkContext undoItNow opts repository whatToUndo -undoItNow :: (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)- => [DarcsFlag] -> Repository p wR wU wT+undoItNow :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => [DarcsFlag] -> Repository rt p wR wU wT -> (q :> FL (PrimOf p)) wA wT -> IO () undoItNow opts repo (_ :> prims) = do exitIfNothingSelected prims "changes"
src/Darcs/UI/Commands/Send.hs view
@@ -19,8 +19,11 @@ module Darcs.UI.Commands.Send ( send ) where -import Prelude hiding ( (^), catch )+import Prelude ()+import Darcs.Prelude +import Prelude hiding ( (^) )+ import System.Exit ( exitSuccess #ifndef HAVE_MAPI@@ -32,7 +35,7 @@ import System.IO ( hClose ) import Control.Exception ( catch, IOException ) import Control.Monad ( when, unless, forM_ )-import Storage.Hashed.Tree ( Tree )+import Darcs.Util.Tree ( Tree ) import Data.List ( intercalate, isPrefixOf ) #ifdef HAVE_HTTP import Data.List ( stripPrefix )@@ -86,7 +89,7 @@ readRepo, readRecorded, prefsUrl, checkUnrelatedRepos ) import Darcs.Patch.Set ( Origin ) import Darcs.Patch.Apply( ApplyState )-import Darcs.Patch ( RepoPatch, description, applyToTree, invert )+import Darcs.Patch ( IsRepoType, RepoPatch, description, applyToTree, invert ) import Darcs.Patch.Witnesses.Sealed ( Sealed(..) ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP ) import Darcs.Patch.Witnesses.Ordered@@ -94,7 +97,7 @@ mapFL, mapFL_FL, lengthFL, nullFL ) import Darcs.Patch.Bundle ( minContext, makeBundleN, scanContextFile, patchFilename ) import Darcs.Repository.Prefs ( addRepoSource, getPreflist )-import Darcs.Repository.External ( fetchFilePS, Cachable(..) )+import Darcs.Util.External ( fetchFilePS, Cachable(..) ) import Darcs.UI.External ( signString , sendEmailDoc@@ -109,15 +112,14 @@ ) import Darcs.Util.ByteString ( mmapFilePS, isAscii ) import qualified Data.ByteString.Char8 as BC (unpack)-import Darcs.Repository.Lock+import Darcs.Util.Lock ( withOpenTemp , writeDocBinFile , readDocBinFile , removeFileMayNotExist ) import Darcs.UI.SelectChanges- ( selectChanges- , WhichChanges(..)+ ( WhichChanges(..) , selectionContext , runSelection )@@ -233,7 +235,6 @@ patchSelOpts flags = S.PatchSelectionOptions { S.verbosity = verbosity flags , S.matchFlags = parseFlags O.matchSeveral flags- , S.diffAlgorithm = O.PatienceDiff , S.interactive = isInteractive True flags , S.selectDeps = selectDeps flags , S.summary = hasSummary O.NoSummary flags@@ -263,11 +264,11 @@ sendCmd fps input_opts [""] = sendCmd fps input_opts [] sendCmd (_,o) input_opts [unfixedrepodir] = withRepository (useCache input_opts) $ RepoJob $- \(repository :: Repository p wR wU wR) -> do+ \(repository :: Repository rt p wR wU wR) -> do context_ps <- the_context input_opts case context_ps of Just them -> do- wtds <- decideOnBehavior input_opts (Nothing :: Maybe (Repository p wR wU wR))+ wtds <- decideOnBehavior input_opts (Nothing :: Maybe (Repository rt p wR wU wR)) sendToThem repository input_opts wtds "CONTEXT" them Nothing -> do repodir <- fixUrl o unfixedrepodir@@ -289,9 +290,9 @@ the_context (_:fs) = the_context fs sendCmd _ _ _ = impossible -sendToThem :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> [DarcsFlag] -> [WhatToDo] -> String- -> PatchSet p Origin wX -> IO ()+sendToThem :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> [DarcsFlag] -> [WhatToDo] -> String+ -> PatchSet rt p Origin wX -> IO () sendToThem repo opts wtds their_name them = do #ifndef HAVE_MAPI -- Check if the user has sendmail or provided a --sendmail-cmd@@ -313,7 +314,7 @@ pristine <- readRecorded repo let direction = if doReverse opts then FirstReversed else First context = selectionContext direction "send" (patchSelOpts opts) Nothing Nothing- (to_be_sent :> _) <- runSelection (selectChanges us') context+ (to_be_sent :> _) <- runSelection us' context printDryRunMessageAndExit "send" (verbosity opts) (hasSummary O.NoSummary opts)@@ -347,10 +348,10 @@ Nothing -> sendBundle opts to_be_sent bundle fname wtds their_name -prepareBundle :: forall p wX wY wZ. (RepoPatch p, ApplyState p ~ Tree)- => [DarcsFlag] -> PatchSet p Origin wZ- -> Either (FL (PatchInfoAnd p) wX wY)- (Tree IO, (FL (PatchInfoAnd p) :\/: FL (PatchInfoAnd p)) wX wY)+prepareBundle :: forall rt p wX wY wZ. (RepoPatch p, ApplyState p ~ Tree)+ => [DarcsFlag] -> PatchSet rt p Origin wZ+ -> Either (FL (PatchInfoAnd rt p) wX wY)+ (Tree IO, (FL (PatchInfoAnd rt p) :\/: FL (PatchInfoAnd rt p)) wX wY) -> IO Doc prepareBundle opts common e = do unsig_bundle <-@@ -365,12 +366,12 @@ (mapFL_FL hopefully to_be_sent) signString (parseFlags O.sign opts) unsig_bundle -sendBundle :: forall p wX wY . (RepoPatch p, ApplyState p ~ Tree)- => [DarcsFlag] -> FL (PatchInfoAnd p) wX wY+sendBundle :: forall rt p wX wY . (RepoPatch p, ApplyState p ~ Tree)+ => [DarcsFlag] -> FL (PatchInfoAnd rt p) wX wY -> Doc -> String -> [WhatToDo] -> String -> IO () sendBundle opts to_be_sent bundle fname wtds their_name= let- auto_subject :: forall pp wA wB . FL (PatchInfoAnd pp) wA wB -> String+ auto_subject :: forall pp wA wB . FL (PatchInfoAnd rt pp) wA wB -> String auto_subject (p:>:NilFL) = "darcs patch: " ++ trim (patchDesc p) 57 auto_subject (p:>:ps) = "darcs patch: " ++ trim (patchDesc p) 43 ++ " (and " ++ show (lengthFL ps) ++ " more)"@@ -453,8 +454,8 @@ removeFileMayNotExist mailfile cleanup _ Nothing = return () -writeBundleToFile :: forall p wX wY . (RepoPatch p, ApplyState p ~ Tree)- => [DarcsFlag] -> FL (PatchInfoAnd p) wX wY -> Doc ->+writeBundleToFile :: forall rt p wX wY . (RepoPatch p, ApplyState p ~ Tree)+ => [DarcsFlag] -> FL (PatchInfoAnd rt p) wX wY -> Doc -> AbsolutePathOrStd -> [WhatToDo] -> String -> IO () writeBundleToFile opts to_be_sent bundle fname wtds their_name = do (d,f,_) <- getDescription opts their_name to_be_sent@@ -470,7 +471,7 @@ = Post String -- ^ POST the patch via HTTP | SendMail String -- ^ send patch via email -decideOnBehavior :: RepoPatch p => [DarcsFlag] -> Maybe (Repository p wR wU wT) -> IO [WhatToDo]+decideOnBehavior :: RepoPatch p => [DarcsFlag] -> Maybe (Repository rt p wR wU wT) -> IO [WhatToDo] decideOnBehavior opts remote_repo = case the_targets of [] -> do wtds <- case remote_repo of@@ -522,7 +523,7 @@ f em = SendMail em getDescription :: (RepoPatch p, ApplyState p ~ Tree)- => [DarcsFlag] -> String -> FL (PatchInfoAnd p) wX wY -> IO (Doc, Maybe String, Maybe String)+ => [DarcsFlag] -> String -> FL (PatchInfoAnd rt p) wX wY -> IO (Doc, Maybe String, Maybe String) getDescription opts their_name patches = case get_filename of Just file -> do
src/Darcs/UI/Commands/SetPref.hs view
@@ -19,6 +19,9 @@ module Darcs.UI.Commands.SetPref ( setpref ) where +import Prelude ()+import Darcs.Prelude+ import System.Exit ( exitWith, ExitCode(..) ) import Control.Monad (when) import Data.Maybe (fromMaybe)
src/Darcs/UI/Commands/Show.hs view
@@ -15,8 +15,11 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -module Darcs.UI.Commands.Show ( showCommand, list, query ) where+module Darcs.UI.Commands.Show ( showCommand ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.UI.Commands ( DarcsCommand(..) , normalCommand, hiddenCommand , commandAlias, amInRepository@@ -24,23 +27,21 @@ import Darcs.UI.Commands.ShowAuthors ( showAuthors ) import Darcs.UI.Commands.ShowBug ( showBug ) import Darcs.UI.Commands.ShowContents ( showContents )-import Darcs.UI.Commands.ShowFiles ( showFiles, manifestCmd, toListManifest )+import Darcs.UI.Commands.ShowDependencies ( showDeps )+import Darcs.UI.Commands.ShowFiles ( showFiles ) import Darcs.UI.Commands.ShowTags ( showTags ) import Darcs.UI.Commands.ShowRepo ( showRepo ) import Darcs.UI.Commands.ShowIndex ( showIndex, showPristineCmd )-import Darcs.UI.Commands.ShowPatchIndex ( showPatchIndexAll, showPatchIndexFiles, showPatchIndexStatus, patchIndexTest )+import Darcs.UI.Commands.ShowPatchIndex ( showPatchIndex ) import Darcs.UI.Flags ( DarcsFlag ) showDescription :: String-showDescription = "Show information which is stored by darcs."+showDescription = "Show information about the given repository." showHelp :: String showHelp = "Use the `--help` option with the subcommands to obtain help for\n"++- "subcommands (for example, `darcs show files --help`).\n" ++- "\n" ++- "In previous releases, this command was called `darcs query`.\n" ++- "Currently this is a deprecated alias.\n"+ "subcommands (for example, `darcs show files --help`).\n" showCommand :: DarcsCommand [DarcsFlag] showCommand = SuperCommand@@ -52,24 +53,16 @@ , commandSubCommands = [ hiddenCommand showBug , normalCommand showContents- , normalCommand showFiles, hiddenCommand showManifest+ , normalCommand showDeps+ , normalCommand showFiles , normalCommand showIndex , normalCommand showPristine , normalCommand showRepo , normalCommand showAuthors , normalCommand showTags- , normalCommand showPatchIndexAll- , normalCommand showPatchIndexFiles- , normalCommand showPatchIndexStatus- , normalCommand patchIndexTest ]+ , normalCommand showPatchIndex ] } -query :: DarcsCommand [DarcsFlag]-query = commandAlias "query" Nothing showCommand--list :: DarcsCommand [DarcsFlag]-list = commandAlias "list" Nothing showCommand- -- unfortunately, aliases for sub-commands have to live in their parent command -- to avoid an import cycle showPristine :: DarcsCommand [DarcsFlag]@@ -82,8 +75,4 @@ "For files, the fields correspond to file size, sha256 of the pristine " ++ "file content and the filename." } -showManifest :: DarcsCommand [DarcsFlag]-showManifest = (commandAlias "manifest" (Just showCommand) showFiles) {- commandCommand = manifestCmd toListManifest-}
src/Darcs/UI/Commands/ShowAuthors.hs view
@@ -19,6 +19,9 @@ ( showAuthors, Spelling, compiledAuthorSpellings, canonizeAuthor, rankAuthors ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( (^), catch ) import Control.Arrow ( (&&&), (***) )
src/Darcs/UI/Commands/ShowBug.hs view
@@ -19,6 +19,9 @@ module Darcs.UI.Commands.ShowBug ( showBug ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, findRepository ) import Darcs.UI.Flags ( DarcsFlag ) import Darcs.UI.Options ( DarcsOption, oid, odesc, ocheck, onormalise, defaultFlags )
src/Darcs/UI/Commands/ShowContents.hs view
@@ -17,6 +17,9 @@ module Darcs.UI.Commands.ShowContents ( showContents ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( (^) ) import Control.Monad ( filterM, forM_, forM )@@ -30,17 +33,17 @@ import Darcs.UI.Options ( DarcsOption, (^), oid, odesc, ocheck, onormalise, defaultFlags, parseFlags ) import qualified Darcs.UI.Options.All as O ( MatchFlag- , matchOne+ , matchUpToOne , workingRepoDir , StdCmdAction , Verbosity , UseCache ) import Darcs.Patch.Match ( haveNonrangeMatch ) import Darcs.Repository ( withRepository, RepoJob(..), readRecorded, repoPatchType )-import Darcs.Repository.Lock ( withDelayedDir )+import Darcs.Util.Lock ( withDelayedDir ) import Darcs.Repository.Match ( getNonrangeMatch )-import Storage.Hashed.Plain( readPlainTree )-import qualified Storage.Hashed.Monad as HSM+import Darcs.Util.Tree.Plain( readPlainTree )+import qualified Darcs.Util.Tree.Monad as TM import Darcs.Util.Path( floatPath, sp2fn, toFilePath, AbsolutePath ) showContentsDescription :: String@@ -53,7 +56,7 @@ "version of the file(s).\n" showContentsBasicOpts :: DarcsOption a ([O.MatchFlag] -> Maybe String -> a)-showContentsBasicOpts = O.matchOne ^ O.workingRepoDir+showContentsBasicOpts = O.matchUpToOne ^ O.workingRepoDir showContentsOpts :: DarcsOption a ([O.MatchFlag]@@ -69,7 +72,7 @@ -> Maybe String -> Bool -> a)-showContentsOpts = O.matchOne ^ O.workingRepoDir `withStdOpts` oid+showContentsOpts = O.matchUpToOne ^ O.workingRepoDir `withStdOpts` oid showContents :: DarcsCommand [DarcsFlag] showContents = DarcsCommand@@ -94,15 +97,15 @@ showContentsCmd _ _ [] = fail "show contents needs at least one argument." showContentsCmd fps opts args = do floatedPaths <- map (floatPath . toFilePath . sp2fn) `fmap` fixSubPaths fps args- let matchFlags = parseFlags O.matchOne opts+ let matchFlags = parseFlags O.matchUpToOne opts withRepository (useCache opts) $ RepoJob $ \repository -> do let readContents = do- okpaths <- filterM HSM.fileExists floatedPaths- forM okpaths $ \f -> (B.concat . BL.toChunks) `fmap` HSM.readFile f+ okpaths <- filterM TM.fileExists floatedPaths+ forM okpaths $ \f -> (B.concat . BL.toChunks) `fmap` TM.readFile f -- Note: The two calls to execReadContents below are from -- different working directories. This matters despite our -- use of virtualTreeIO.- execReadContents tree = fst `fmap` HSM.virtualTreeIO readContents tree+ execReadContents tree = fst `fmap` TM.virtualTreeIO readContents tree files <- if haveNonrangeMatch (repoPatchType repository) matchFlags then withDelayedDir "show.contents" $ \_ -> do -- this call populates our temporary directory, but note that
+ src/Darcs/UI/Commands/ShowDependencies.hs view
@@ -0,0 +1,148 @@+module Darcs.UI.Commands.ShowDependencies+ ( showDeps )+where++import Control.Arrow ( (***) )++import Data.Maybe( fromMaybe )+import Data.GraphViz+import Data.GraphViz.Algorithms ( transitiveReduction )+import Data.GraphViz.Attributes.Complete+import Data.Graph.Inductive.Graph ( Graph(..), mkGraph, LNode, UEdge )+import Data.Graph.Inductive.PatriciaTree ( Gr )++import qualified Data.Text.Lazy as T+import qualified Data.ByteString.Char8 as BC ( unpack )++import Darcs.Util.Tree ( Tree )++import Darcs.Repository ( readRepo, withRepositoryDirectory, RepoJob(..) )+import Darcs.UI.Flags ( DarcsFlag(..), getRepourl+ , useCache, toMatchFlags )+import Darcs.UI.Options ( PrimDarcsOption, oid, odesc, ocheck, onormalise, defaultFlags )+import qualified Darcs.UI.Options.All as O+import Darcs.UI.Commands ( DarcsCommand(..), nodefaults, findRepository, withStdOpts )+import Darcs.UI.Commands.Unrecord ( matchingHead )++import Darcs.Util.Text ( formatText )+import Darcs.Util.Path ( AbsolutePath )++import Darcs.Patch ( RepoPatch )+import Darcs.Patch.Set ( PatchSet(..), newset2FL )+import Darcs.Patch.Info ( _piName )+import Darcs.Patch.PatchInfoAnd ( hopefully )+import Darcs.Patch.Apply( ApplyState )+import Darcs.Patch.Named ( Named (..), patch2patchinfo )+import Darcs.Patch.Named.Wrapped ( removeInternalFL )+import Darcs.Patch.Match ( firstMatch, matchFirstPatchset )+import Darcs.Patch.Choices ( lpPatch, LabelledPatch, label, getLabelInt )+import Darcs.Patch.Depends ( SPatchAndDeps, getDeps, findCommonWithThem )+import Darcs.Patch.Witnesses.Sealed ( Sealed2(..), seal2, Sealed(..) )+import Darcs.Patch.Witnesses.Ordered ( (:>)(..), (:>)(..), RL(..)+ , reverseFL, foldlFL, mapFL_FL )+import Darcs.Repository.Flags ( Verbosity(..), UseCache(..) ) ++showDepsDescription :: String+showDepsDescription = "Generate the graph of dependencies."++showDepsHelp :: String+showDepsHelp = formatText 80+ [ unwords [ "The `darcs show dependencies` command is used to create"+ , "a graph of the dependencies between patches of the"+ , "repository (by default up to last tag)."+ ]+ , unwords [ "The resulting graph is described in Dot Language, a"+ , "general example of use could be:"+ ]+ , "darcs show dependencies | dot -Tpdf -o FILE.pdf"+ ]++showDepsBasicOpts :: PrimDarcsOption [O.MatchFlag]+showDepsBasicOpts = O.matchSeveralOrLast++showDepsOpts :: O.DarcsOption+ a+ ([O.MatchFlag]+ -> Maybe O.StdCmdAction+ -> Bool+ -> Bool+ -> Verbosity+ -> Bool+ -> UseCache+ -> Maybe String+ -> Bool+ -> Maybe String+ -> Bool+ -> a)+showDepsOpts = showDepsBasicOpts `withStdOpts` oid++showDeps :: DarcsCommand [DarcsFlag]+showDeps = DarcsCommand+ { commandProgramName = "darcs"+ , commandName = "dependencies"+ , commandHelp = showDepsHelp+ , commandDescription = showDepsDescription+ , commandExtraArgs = 0+ , commandExtraArgHelp = []+ , commandCommand = depsCmd+ , commandPrereq = findRepository+ , commandGetArgPossibilities = return []+ , commandArgdefaults = nodefaults+ , commandAdvancedOptions = []+ , commandBasicOptions = odesc showDepsBasicOpts+ , commandDefaults = defaultFlags showDepsOpts+ , commandCheckOptions = ocheck showDepsOpts+ , commandParseOptions = onormalise showDepsOpts+ }++type DepsGraph = Gr String ()++depsCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()+depsCmd _ opts _ = do+ let repodir = fromMaybe "." (getRepourl opts)+ withRepositoryDirectory (useCache opts) repodir $ RepoJob $ \repo -> do+ Sealed2 r <- readRepo repo >>= pruneRepo+ let rFl = newset2FL r+ deps = getDeps+ (removeInternalFL . mapFL_FL hopefully $ rFl)+ rFl+ dGraph = transitiveReduction $+ graphToDot nodeLabeledParams $ makeGraph deps+ putStrLn $ T.unpack $ printDotGraph dGraph+ where+ nodeLabeledParams :: GraphvizParams n String el () String+ nodeLabeledParams =+ defaultParams { globalAttributes =+ [GraphAttrs {attrs = [RankDir FromLeft]}]+ , fmtNode = \(_,l) ->+ [ toLabel l+ , ImageScale UniformScale+ ]+ }+ pruneRepo r = let matchFlags = toMatchFlags opts in+ if firstMatch matchFlags+ then case getLastPatches matchFlags r of+ Sealed2 ps -> return $ seal2 $ PatchSet NilRL ps+ else case matchingHead matchFlags r of+ _ :> patches -> return $ seal2 $ PatchSet NilRL $ reverseFL patches+ getLastPatches matchFlags ps =+ case matchFirstPatchset matchFlags ps of+ Sealed p1s -> case findCommonWithThem ps p1s of+ _ :> ps' -> seal2 $ reverseFL ps'++makeGraph :: (RepoPatch p,ApplyState p ~ Tree) => [SPatchAndDeps p] -> DepsGraph+makeGraph = uncurry mkGraph . (id *** concat) . unzip . map mkNodeWithEdges+ where+ mkNodeWithEdges :: SPatchAndDeps p -> (LNode String, [UEdge])+ mkNodeWithEdges (Sealed2 father, Sealed2 childs) = (mkLNode father,mkUEdges)+ where+ mkNode :: LabelledPatch (Named p) wX wY -> Int+ mkNode = fromInteger . getLabelInt . label+ mkUEdge :: [UEdge] -> LabelledPatch (Named p) wX wY -> [UEdge]+ mkUEdge les child = (mkNode father, mkNode child,()) : les+ mkLabel :: LabelledPatch (Named p) wX wY -> String+ mkLabel = formatText 20 . (:[]) . BC.unpack . _piName . patch2patchinfo . lpPatch+ mkLNode :: LabelledPatch (Named p) wX wY -> LNode String+ mkLNode p = (mkNode p, mkLabel p)+ mkUEdges :: [UEdge]+ mkUEdges = foldlFL mkUEdge [] childs
src/Darcs/UI/Commands/ShowFiles.hs view
@@ -16,11 +16,11 @@ -- Boston, MA 02110-1301, USA. {-# LANGUAGE CPP #-}-module Darcs.UI.Commands.ShowFiles ( showFiles- , manifestCmd, toListManifest -- for alias- , manifest- ) where+module Darcs.UI.Commands.ShowFiles ( showFiles ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( (^) ) import Darcs.UI.Flags ( DarcsFlag, useCache ) import Darcs.UI.Options ( DarcsOption, (^), oid, odesc, ocheck, onormalise@@ -29,22 +29,22 @@ import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInRepository ) import Darcs.Repository ( Repository, withRepository, RepoJob(..), repoPatchType )-import Darcs.Patch ( RepoPatch )+import Darcs.Patch ( IsRepoType, RepoPatch ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Repository.State ( readRecorded, readRecordedAndPending )-import Storage.Hashed.Tree( Tree, TreeItem(..), list, expand )+import Darcs.Util.Tree( Tree, TreeItem(..), list, expand )+import Darcs.Util.Tree.Plain( readPlainTree ) import Darcs.Util.Path( anchorPath, AbsolutePath )-import Storage.Hashed.Plain( readPlainTree ) import System.FilePath ( splitDirectories ) import Data.List( isPrefixOf ) import Darcs.Patch.Match ( haveNonrangeMatch ) import Darcs.Repository.Match ( getNonrangeMatch )-import Darcs.Repository.Lock ( withDelayedDir )+import Darcs.Util.Lock ( withDelayedDir ) showFilesDescription :: String-showFilesDescription = "Show version-controlled files in the working copy."+showFilesDescription = "Show version-controlled files in the working tree." showFilesHelp :: String showFilesHelp =@@ -57,9 +57,8 @@ "default, pending files (and directories) are listed; the `--no-pending`\n" ++ "option prevents this.\n" ++ "\n" ++- "By default `darcs show files` lists both files and directories, but\n" ++- "the alias `darcs show manifest` only lists files. The `--files`,\n" ++- "`--directories`, `--no-files` and `--no-directories` modify this behaviour.\n" +++ "By default `darcs show files` lists both files and directories, but the\n" +++ "`--no-files` and `--no-directories` flags modify this behaviour.\n" ++ "\n" ++ "By default entries are one-per-line (i.e. newline separated). This\n" ++ "can cause problems if the files themselves contain newlines or other\n" ++@@ -78,7 +77,7 @@ ^ O.directories ^ O.pending ^ O.nullFlag- ^ O.matchOne+ ^ O.matchUpToOne ^ O.workingRepoDir showFilesOpts :: DarcsOption a@@ -119,9 +118,8 @@ commandCheckOptions = ocheck showFilesOpts, commandParseOptions = onormalise showFilesOpts } -toListFiles, toListManifest :: [DarcsFlag] -> Tree m -> [FilePath]+toListFiles :: [DarcsFlag] -> Tree m -> [FilePath] toListFiles opts = filesDirs (parseFlags O.files opts) (parseFlags O.directories opts)-toListManifest opts = filesDirs (parseFlags O.files opts) (parseFlags O.directories opts) filesDirs :: Bool -> Bool -> Tree m -> [FilePath] filesDirs False False _ = []@@ -129,9 +127,6 @@ filesDirs True False t = [ anchorPath "." p | (p, File _) <- list t ] filesDirs True True t = "." : map (anchorPath "." . fst) (list t) -manifest :: [DarcsFlag] -> [String] -> IO [FilePath]-manifest = manifestHelper toListFiles- manifestCmd :: ([DarcsFlag] -> Tree IO -> [FilePath]) -> (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () manifestCmd to_list _ opts argList =@@ -142,28 +137,33 @@ manifestHelper :: ([DarcsFlag] -> Tree IO -> [FilePath]) -> [DarcsFlag] -> [String] -> IO [FilePath] manifestHelper to_list opts argList = do- list' <- to_list opts `fmap` withRepository (useCache opts) (RepoJob myslurp)+ list' <- to_list opts `fmap` withRepository (useCache opts) (RepoJob slurp) case argList of [] -> return list' prefixes -> return (onlysubdirs prefixes list')- where myslurp :: (RepoPatch p, ApplyState p ~ Tree) => Repository p wR wU wR -> IO (Tree IO)- myslurp r = do let fRevisioned = haveNonrangeMatch (repoPatchType r) (parseFlags O.matchOne opts)- fPending = parseFlags O.pending opts- -- this covers all 4 possibilities- expand =<< case (fRevisioned,fPending) of- (True,False) -> slurpRevision opts r- (True,True) -> error "can't mix revisioned and pending flags"- (False,False) -> readRecorded r- (False,True) -> readRecordedAndPending r -- pending is default- isParentDir a' b' =- let a = splitDirectories a'- b = splitDirectories b'- in (a `isPrefixOf` b) || (("." : a) `isPrefixOf` b)- onlysubdirs dirs = filter (\p -> any (`isParentDir` p) dirs)+ where+ matchFlags = parseFlags O.matchUpToOne opts+ slurp :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wR -> IO (Tree IO)+ slurp r = do+ let fUpto = haveNonrangeMatch (repoPatchType r) matchFlags+ fPending = parseFlags O.pending opts+ -- this covers all 4 possibilities+ case (fUpto,fPending) of+ (True, False) -> slurpUpto matchFlags r+ (True, True) -> error "can't mix match and pending flags"+ (False,False) -> expand =<< readRecorded r+ (False,True) -> expand =<< readRecordedAndPending r -- pending is default+ isParentDir a' b' =+ let a = splitDirectories a'+ b = splitDirectories b'+ in (a `isPrefixOf` b) || (("." : a) `isPrefixOf` b)+ onlysubdirs dirs = filter (\p -> any (`isParentDir` p) dirs) -slurpRevision :: (RepoPatch p, ApplyState p ~ Tree)- => [DarcsFlag] -> Repository p wR wU wR -> IO (Tree IO)-slurpRevision opts r = withDelayedDir "revisioned.showfiles" $ \_ -> do- getNonrangeMatch r (parseFlags O.matchOne opts)+slurpUpto :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => [O.MatchFlag] -> Repository rt p wR wU wR -> IO (Tree IO)+slurpUpto matchFlags r = withDelayedDir "show.files" $ \_ -> do+ getNonrangeMatch r matchFlags+ -- note: it is important that we expand the tree from inside the+ -- withDelayedDir action, else it has no effect. expand =<< readPlainTree "."-
src/Darcs/UI/Commands/ShowIndex.hs view
@@ -26,21 +26,21 @@ , showPristineCmd -- for alias ) where -import Control.Applicative ( (<$>) )+import Prelude ()+import Darcs.Prelude+ import Control.Monad ( (>=>) ) import Darcs.UI.Flags ( DarcsFlag(NullFlag), useCache ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInRepository )-import Prelude hiding ( (^) ) import Darcs.UI.Options ( DarcsOption, (^), oid, odesc, ocheck, onormalise, defaultFlags ) import qualified Darcs.UI.Options.All as O import Darcs.Repository ( withRepository, RepoJob(..), readIndex ) import Darcs.Repository.State ( readRecorded ) -import Storage.Hashed( floatPath )-import Storage.Hashed.Hash( encodeBase16, Hash( NoHash ) )-import Storage.Hashed.Tree( list, expand, itemHash, Tree, TreeItem( SubTree ) )-import Storage.Hashed.Index( updateIndex, listFileIDs )-import Darcs.Util.Path( anchorPath, AbsolutePath )+import Darcs.Util.Hash( encodeBase16, Hash( NoHash ) )+import Darcs.Util.Tree( list, expand, itemHash, Tree, TreeItem( SubTree ) )+import Darcs.Util.Index( updateIndex, listFileIDs )+import Darcs.Util.Path( anchorPath, AbsolutePath, floatPath ) import System.Posix.Types ( FileID )
src/Darcs/UI/Commands/ShowPatchIndex.hs view
@@ -1,6 +1,10 @@-module Darcs.UI.Commands.ShowPatchIndex ( showPatchIndexFiles, showPatchIndexAll, showPatchIndexStatus, patchIndexTest ) where+module Darcs.UI.Commands.ShowPatchIndex ( showPatchIndex ) where++import Prelude ()+import Darcs.Prelude+ import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository )-import Darcs.UI.Flags ( DarcsFlag, useCache )+import Darcs.UI.Flags ( DarcsFlag(Verbose), useCache ) import Prelude hiding ( (^) ) import Darcs.UI.Options ( DarcsOption, (^), oid, odesc, ocheck, onormalise, defaultFlags ) import qualified Darcs.UI.Options.All as O@@ -32,73 +36,17 @@ -> a) showPatchIndexOpts = showPatchIndexBasicOpts `withStdOpts` oid -showPatchIndexAll :: DarcsCommand [DarcsFlag]-showPatchIndexAll = DarcsCommand {- commandProgramName = "darcs",- commandName = "patch-index-all",- commandDescription = "Dump complete content of patch index.",- commandHelp =- "The `darcs show patch-index all' command lists all information in the patch index",- commandExtraArgs = 0,- commandExtraArgHelp = [],- commandCommand = showPatchIndexAllCmd,- commandPrereq = amInHashedRepository,- commandGetArgPossibilities = return [],- commandArgdefaults = nodefaults,- commandAdvancedOptions = [],- commandBasicOptions = odesc showPatchIndexBasicOpts,- commandDefaults = defaultFlags showPatchIndexOpts,- commandCheckOptions = ocheck showPatchIndexOpts,- commandParseOptions = onormalise showPatchIndexOpts }--showPatchIndexFiles :: DarcsCommand [DarcsFlag]-showPatchIndexFiles = DarcsCommand {- commandProgramName = "darcs",- commandName = "patch-index-files",- commandDescription = "Dump current files registered in patch index.",- commandHelp =- "The `darcs show patch-index files' command lists all current files registered in the patch index",- commandExtraArgs = 0,- commandExtraArgHelp = [],- commandCommand = showPatchIndexFilesCmd,- commandPrereq = amInHashedRepository,- commandGetArgPossibilities = return [],- commandArgdefaults = nodefaults,- commandAdvancedOptions = [],- commandBasicOptions = odesc showPatchIndexBasicOpts,- commandDefaults = defaultFlags showPatchIndexOpts,- commandCheckOptions = ocheck showPatchIndexOpts,- commandParseOptions = onormalise showPatchIndexOpts }--showPatchIndexStatus :: DarcsCommand [DarcsFlag]-showPatchIndexStatus = DarcsCommand {- commandProgramName = "darcs",- commandName = "patch-index-status",- commandDescription = " Report patch-index status",- commandHelp =- "The `darcs show patch-index-status' reports if the patch index is in sync, out of sync, or does not exist",- commandExtraArgs = 0,- commandExtraArgHelp = [],- commandCommand = showPatchIndexStatus',- commandPrereq = amInHashedRepository,- commandGetArgPossibilities = return [],- commandArgdefaults = nodefaults,- commandAdvancedOptions = [],- commandBasicOptions = odesc showPatchIndexBasicOpts,- commandDefaults = defaultFlags showPatchIndexOpts,- commandCheckOptions = ocheck showPatchIndexOpts,- commandParseOptions = onormalise showPatchIndexOpts }--patchIndexTest :: DarcsCommand [DarcsFlag]-patchIndexTest = DarcsCommand {+showPatchIndex :: DarcsCommand [DarcsFlag]+showPatchIndex = DarcsCommand { commandProgramName = "darcs",- commandName = "patch-index-test",- commandDescription = "Test patch-index",+ commandName = "patch-index",+ commandDescription = "Check integrity of patch index", commandHelp =- "The `darcs show patch-index-test' tests patch index",+ "When given the `--verbose` flag, the command dumps the complete content\n"+ ++ "of the patch index and checks its integrity.", commandExtraArgs = 0, commandExtraArgHelp = [],- commandCommand = piTest',+ commandCommand = showPatchIndexCmd, commandPrereq = amInHashedRepository, commandGetArgPossibilities = return [], commandArgdefaults = nodefaults,@@ -108,25 +56,18 @@ commandCheckOptions = ocheck showPatchIndexOpts, commandParseOptions = onormalise showPatchIndexOpts } -showPatchIndexAllCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-showPatchIndexAllCmd _ opts _ =- withRepository (useCache opts) $ RepoJob $ \(Repo repodir _ _ _) ->- dumpPatchIndex repodir--showPatchIndexFilesCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-showPatchIndexFilesCmd _ opts _ = withRepository (useCache opts) $ RepoJob $ \(Repo repodir _ _ _) ->- dumpPatchIndexFiles repodir--showPatchIndexStatus' :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-showPatchIndexStatus' _ opts _ = withRepository (useCache opts) $ RepoJob $ \(repo@(Repo repodir _ _ _)) -> do- ex <- doesPatchIndexExist repodir- if ex- then do+showPatchIndexCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()+showPatchIndexCmd _ opts _+ | Verbose `elem` opts = do+ withRepository (useCache opts) $ RepoJob $ \repo@(Repo repodir _ _ _) ->+ dumpPatchIndex repodir >> piTest repo+ | otherwise =+ withRepository (useCache opts) $ RepoJob $ \(repo@(Repo repodir _ _ _)) -> do+ ex <- doesPatchIndexExist repodir+ if ex then do sy <- isPatchIndexInSync repo if sy then putStrLn "Patch Index is in sync with repo." else putStrLn "Patch Index is outdated. Run darcs optimize enable-patch-index"- else putStrLn "Patch Index is not yet created. Run darcs optimize enable-patch-index"+ else putStrLn "Patch Index is not yet created. Run darcs optimize enable-patch-index" -piTest' :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-piTest' _ opts _ = withRepository (useCache opts) $ RepoJob piTest
src/Darcs/UI/Commands/ShowRepo.hs view
@@ -18,6 +18,9 @@ {-# LANGUAGE CPP #-} module Darcs.UI.Commands.ShowRepo ( showRepo ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( (^) ) import Data.Char ( toLower, isSpace ) import Data.List ( intercalate )@@ -29,19 +32,18 @@ import qualified Darcs.UI.Options.All as O import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInRepository ) import Darcs.Repository ( withRepository, RepoJob(..), readRepo )-import Darcs.Repository.Internal ( Repository(..) )+import Darcs.Repository.Internal ( Repository(..), repoXor ) import Darcs.Repository.InternalTypes ( Pristine(..) ) import Darcs.Repository.Cache ( Cache(..) ) import Darcs.Repository.Format ( RepoFormat(..) ) import Darcs.Repository.Prefs ( getPreflist ) import Darcs.Repository.Motd ( getMotd )-import Darcs.Repository.State ( repoXor )-import Darcs.Patch ( RepoPatch )+import Darcs.Patch ( IsRepoType, RepoPatch ) import Darcs.Patch.Set ( newset2RL ) import Darcs.Patch.Witnesses.Ordered ( lengthRL ) import qualified Data.ByteString.Char8 as BC (unpack) import Darcs.Patch.Apply( ApplyState )-import Storage.Hashed.Tree ( Tree )+import Darcs.Util.Tree ( Tree ) showRepoHelp :: String showRepoHelp =@@ -53,6 +55,10 @@ "By default, the number of patches is shown. If this data isn't\n" ++ "needed, use `--no-files` to accelerate this command from O(n) to O(1).\n" ++ "\n" +++ "The 'Weak Hash' identifies the set of patches of a repository independently\n" +++ "of ordering. It can be used to easily compare two repositories of a same\n" +++ "project. It is not cryptographically secure.\n" +++ "\n" ++ "By default, output is in a human-readable format. The `--xml-output`\n" ++ "option can be used to generate output for machine postprocessing.\n" @@ -131,8 +137,8 @@ -- sub-displays. The `out' argument is one of the above operations to -- output a labelled text string or an XML tag and contained value. -actuallyShowRepo :: (RepoPatch p, ApplyState p ~ Tree)- => PutInfo -> Repository p wR wU wR -> [DarcsFlag] -> IO ()+actuallyShowRepo :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => PutInfo -> Repository rt p wR wU wR -> [DarcsFlag] -> IO () actuallyShowRepo out r@(Repo loc rf pris cs) opts = do when (XMLOutput `elem` opts) (putStr "<repository>\n") when (Verbose `elem` opts) (out "Show" $ show r)@@ -145,8 +151,8 @@ showRepoMOTD out r when (XMLOutput `elem` opts) (putStr "</repository>\n") -showXor :: (RepoPatch p, ApplyState p ~ Tree)- => PutInfo -> Repository p wR wU wR -> IO ()+showXor :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => PutInfo -> Repository rt p wR wU wR -> IO () showXor out repo = do theXor <- repoXor repo out "Weak Hash" (show theXor)@@ -172,11 +178,11 @@ getPreflist "defaultrepo" >>= out "Default Remote" . unlines where prefOut = uncurry out . (\(p,v) -> (p++" Pref", dropWhile isSpace v)) . break isSpace -showRepoMOTD :: RepoPatch p => PutInfo -> Repository p wR wU wR -> IO ()+showRepoMOTD :: RepoPatch p => PutInfo -> Repository rt p wR wU wR -> IO () showRepoMOTD out (Repo loc _ _ _) = getMotd loc >>= out "MOTD" . BC.unpack -- Support routines to provide information used by the PutInfo operations above. -numPatches :: (RepoPatch p, ApplyState p ~ Tree) => Repository p wR wU wR -> IO Int+numPatches :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wR -> IO Int numPatches r = (lengthRL . newset2RL) `liftM` readRepo r
src/Darcs/UI/Commands/ShowTags.hs view
@@ -19,12 +19,14 @@ ( showTags ) where +import Prelude ()+import Darcs.Prelude+ import Control.Monad ( unless, join ) import Data.Maybe ( fromMaybe ) import System.IO ( stderr, hPutStrLn ) import Darcs.Patch.Set ( PatchSet(..) )-import Darcs.Patch.MaybeInternal ( MaybeInternal ) import Darcs.Repository ( readRepo, withRepositoryDirectory, RepoJob(..) ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, findRepository ) import Darcs.UI.Commands.Tag ( getTags )@@ -91,7 +93,7 @@ withRepositoryDirectory (useCache opts) repodir $ RepoJob $ \repo -> readRepo repo >>= printTags where- printTags :: MaybeInternal p => PatchSet p wW wZ -> IO ()+ printTags :: PatchSet rt p wW wZ -> IO () printTags = join . fmap (sequence_ . map process) . getTags process :: String -> IO () process t = normalize t t False >>= putStrLn
src/Darcs/UI/Commands/Tag.hs view
@@ -17,48 +17,46 @@ module Darcs.UI.Commands.Tag ( tag, getTags ) where -import Prelude hiding ( (^) )-import Control.Applicative ( (<$>) )+import Prelude ()+import Darcs.Prelude+ import Control.Monad ( when ) import Data.Maybe ( catMaybes ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository )-import Darcs.UI.Commands.Record ( getLog ) import Darcs.UI.Flags ( DarcsFlag(AskDeps), getDate, compression, verbosity, useCache, umask, getAuthor- , hasAuthor, diffAlgorithm )+ , hasAuthor ) import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags, parseFlags ) import qualified Darcs.UI.Options.All as O+import Darcs.UI.PatchHeader ( getLog ) import Darcs.Patch.PatchInfoAnd ( n2pia, hopefully ) import Darcs.Repository ( withRepoLock, Repository, RepoJob(..), readRepo, tentativelyAddPatch, finalizeRepositoryChanges, ) import Darcs.Patch- ( infopatch, adddeps, Patchy, PrimPatch, PrimOf- , RepoPatch+ ( Patchy, PrimPatch, PrimOf+ , IsRepoType, RepoPatch ) import Darcs.Patch.Apply ( ApplyState ) import Darcs.Patch.Info ( patchinfo, piTag ) import Darcs.Patch.Depends ( getUncovered ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, info )-import Darcs.Patch.MaybeInternal ( MaybeInternal(patchInternalChecker), InternalChecker(..) )-import Darcs.Patch.Named ( patchcontents )-import Darcs.Patch.Set ( PatchSet(..), emptyPatchSet, appendPSFL, newset2FL )+import Darcs.Patch.Named.Wrapped ( infopatch, adddeps, runInternalChecker, namedInternalChecker )+import Darcs.Patch.Set ( PatchSet(..), emptyPatchSet, appendPSFL, newset2FL, patchSetfMap ) import Darcs.Patch.Witnesses.Ordered ( FL(..), filterOutRLRL, (:>)(..) ) import Darcs.Patch.Witnesses.Sealed ( Sealed(..), mapSeal ) import Darcs.UI.SelectChanges- ( selectChanges- , WhichChanges(..)+ ( WhichChanges(..) , selectionContext , runSelection , PatchSelectionContext(allowSkipAll) ) import qualified Darcs.UI.SelectChanges as S-import Darcs.Repository.Util ( patchSetfMap ) import Darcs.Repository.Flags ( UpdateWorking(..), DryRun(NoDryRun) ) import Darcs.Util.Path ( AbsolutePath ) -import Storage.Hashed.Tree( Tree )+import Darcs.Util.Tree( Tree ) import System.IO ( hPutStr, stderr ) @@ -153,15 +151,15 @@ , commandParseOptions = onormalise tagOpts } -filterNonInternal :: MaybeInternal p => PatchSet p wX wY -> PatchSet p wX wY+filterNonInternal :: IsRepoType rt => PatchSet rt p wX wY -> PatchSet rt p wX wY filterNonInternal =- case patchInternalChecker of+ case namedInternalChecker of Nothing -> id- Just f -> \(PatchSet ps ts) -> PatchSet (filterOutRLRL (isInternal f . patchcontents . hopefully) ps) ts+ Just f -> \(PatchSet ts ps) -> PatchSet ts (filterOutRLRL (runInternalChecker f . hopefully) ps) tagCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () tagCmd _ opts args =- withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \(repository :: Repository p wR wU wR) -> do+ withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \(repository :: Repository rt p wR wU wR) -> do date <- getDate (hasPipe opts) the_author <- getAuthor (hasAuthor opts) (hasPipe opts) patches <- readRepo repository@@ -198,7 +196,7 @@ " already exists." return ("TAG " ++ name, comment) -getTags :: MaybeInternal p => PatchSet p wW wR -> IO [String]+getTags :: PatchSet rt p wW wR -> IO [String] getTags ps = catMaybes `fmap` patchSetfMap (return . piTag . info) ps -- This may be useful for developers, but users don't care about@@ -213,21 +211,20 @@ -- since the last tag, plus that tag itself. askAboutTagDepends- :: forall p wX wY . (RepoPatch p, ApplyState p ~ Tree)+ :: forall rt p wX wY . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => [DarcsFlag]- -> FL (PatchInfoAnd p) wX wY- -> IO (Sealed (FL (PatchInfoAnd p) wX))+ -> FL (PatchInfoAnd rt p) wX wY+ -> IO (Sealed (FL (PatchInfoAnd rt p) wX)) askAboutTagDepends flags ps = do let opts = S.PatchSelectionOptions { S.verbosity = verbosity flags , S.matchFlags = []- , S.diffAlgorithm = diffAlgorithm flags , S.interactive = True , S.selectDeps = O.PromptDeps , S.summary = O.NoSummary , S.withContext = O.NoContext }- (deps:>_) <- runSelection (selectChanges ps) $+ (deps:>_) <- runSelection ps $ ((selectionContext FirstReversed "depend on" opts Nothing Nothing) { allowSkipAll = False }) return $ Sealed deps
src/Darcs/UI/Commands/Test.hs view
@@ -20,7 +20,8 @@ test ) where -import Prelude hiding ( (^), init, catch )+import Prelude ()+import Darcs.Prelude hiding ( init ) import Control.Exception ( catch, IOException ) import Control.Monad( when )@@ -29,7 +30,7 @@ import System.Exit ( ExitCode(..), exitWith ) import System.IO ( hFlush, stdout ) -import Storage.Hashed.Tree( Tree )+import Darcs.Util.Tree( Tree ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts@@ -56,7 +57,7 @@ ) import Darcs.Patch.Witnesses.Ordered ( RL(..)- , (:<)(..)+ , (:>)(..) , (+<+) , reverseRL , splitAtRL@@ -77,11 +78,11 @@ , ShowPatch ) import Darcs.Patch ( RepoPatch- , Named- , description , apply+ , description , invert )+import Darcs.Patch.Named.Wrapped ( WrappedNamed ) import Darcs.Patch.Set ( newset2RL ) import Darcs.Util.Printer ( putDocLn , text@@ -89,14 +90,14 @@ import Darcs.Util.Path ( AbsolutePath ) import Darcs.Repository.ApplyPatches ( DefaultIO, runDefault ) import Darcs.Repository.Test ( getTest )-import Darcs.Repository.Lock+import Darcs.Util.Lock ( withTempDir , withPermDir ) testDescription :: String-testDescription = "Run regression test."+testDescription = "Run tests and search for the patch that introduced a bug." testHelp :: String testHelp =@@ -175,12 +176,12 @@ } -- | Functions defining a strategy for executing a test-type Strategy = forall p wX wY- . (RepoPatch p, ApplyMonad DefaultIO (ApplyState p), ApplyState p ~ Tree)+type Strategy = forall rt p wX wY+ . (RepoPatch p, ApplyMonad (ApplyState p) DefaultIO, ApplyState p ~ Tree) => [DarcsFlag] -> IO ExitCode -- ^ test command -> ExitCode- -> RL (Named p) wX wY+ -> RL (WrappedNamed rt p) wX wY -> IO () testCommand :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()@@ -225,7 +226,7 @@ -- | linear search (with --linear) trackLinear :: Strategy trackLinear _ _ ExitSuccess _ = putStrLn "Success!"-trackLinear opts testCmd (ExitFailure _) (p:<:ps) = do+trackLinear opts testCmd (ExitFailure _) (ps:<:p) = do let ip = invert p safeApply ip when (SetScriptsExecutable `elem` opts) $ setScriptsExecutablePatches ip@@ -244,11 +245,11 @@ trackBackoff opts testCmd (ExitFailure _) ps = trackNextBackoff opts testCmd 4 ps -trackNextBackoff :: (RepoPatch p, ApplyMonad DefaultIO (ApplyState p), ApplyState p ~ Tree)+trackNextBackoff :: (RepoPatch p, ApplyMonad (ApplyState p) DefaultIO, ApplyState p ~ Tree) => [DarcsFlag] -> IO ExitCode -> Int -- ^ number of patches to skip- -> RL (Named p) wY wZ -- ^ patches not yet skipped+ -> RL (WrappedNamed rt p) wY wZ -- ^ patches not yet skipped -> IO () trackNextBackoff _ _ _ NilRL = putStrLn "Noone passed the test!" trackNextBackoff opts testCmd n ahead@@ -257,7 +258,7 @@ putStrLn $ "Skipping " ++ show n ++ " patches..." hFlush stdout case splitAtRL n ahead of- ( skipped' :< ahead' ) -> do+ ( ahead' :> skipped' ) -> do unapplyRL skipped' when (SetScriptsExecutable `elem` opts) $ setScriptsExecutablePatches skipped' testResult <- testCmd@@ -276,10 +277,10 @@ trackBisect opts testCmd (ExitFailure _) ps = initialBisect opts testCmd ps -initialBisect :: (RepoPatch p, ApplyMonad DefaultIO (ApplyState p), ApplyState p ~ Tree)+initialBisect :: (RepoPatch p, ApplyMonad (ApplyState p) DefaultIO, ApplyState p ~ Tree) => [DarcsFlag] -> IO ExitCode- -> RL (Named p) wX wY+ -> RL (WrappedNamed rt p) wX wY -> IO () initialBisect opts testCmd ps = trackNextBisect opts currProg testCmd BisectRight (patchTreeFromRL ps)@@ -300,22 +301,22 @@ -- | Create Bisect PatchTree from the RL patchTreeFromRL :: (Patchy p) => RL p wX wY -> PatchTree p wX wY-patchTreeFromRL (l :<: NilRL) = Leaf l+patchTreeFromRL (NilRL :<: l) = Leaf l patchTreeFromRL xs = case splitAtRL (lengthRL xs `div` 2) xs of- (l :< r) -> Fork (patchTreeFromRL l) (patchTreeFromRL r)+ (r :> l) -> Fork (patchTreeFromRL l) (patchTreeFromRL r) -- | Convert PatchTree back to RL patchTree2RL :: (Patchy p) => PatchTree p wX wY -> RL p wX wY-patchTree2RL (Leaf p) = p :<: NilRL-patchTree2RL (Fork l r) = patchTree2RL l +<+ patchTree2RL r+patchTree2RL (Leaf p) = NilRL :<: p+patchTree2RL (Fork l r) = patchTree2RL r +<+ patchTree2RL l -- | Iterate the Patch Tree-trackNextBisect :: (RepoPatch p, ApplyMonad DefaultIO (ApplyState p), ApplyState p ~ Tree)+trackNextBisect :: (RepoPatch p, ApplyMonad (ApplyState p) DefaultIO, ApplyState p ~ Tree) => [DarcsFlag] -> BisectState -> IO ExitCode -- ^ test command -> BisectDir- -> PatchTree (Named p) wX wY+ -> PatchTree (WrappedNamed rt p) wX wY -> IO () trackNextBisect opts (dnow, dtotal) testCmd dir (Fork l r) = do putStr $ "Trying " ++ show dnow ++ "/" ++ show dtotal ++ " sequences...\n"@@ -335,7 +336,7 @@ jumpHalfOnRight :: (IsHunk p, Conflict p, PatchListFormat p, ShowPatch p, PatchInspect p,- Patchy p, ApplyMonad DefaultIO (ApplyState p))+ Patchy p, ApplyMonad (ApplyState p) DefaultIO) => [DarcsFlag] -> PatchTree p wX wY -> IO () jumpHalfOnRight opts l = do unapplyRL ps when (SetScriptsExecutable `elem` opts) $ setScriptsExecutablePatches ps@@ -343,21 +344,21 @@ jumpHalfOnLeft :: (IsHunk p, Conflict p, PatchListFormat p, ShowPatch p, PatchInspect p,- Patchy p, ApplyMonad DefaultIO (ApplyState p))+ Patchy p, ApplyMonad (ApplyState p) DefaultIO) => [DarcsFlag] -> PatchTree p wX wY -> IO () jumpHalfOnLeft opts r = do applyRL p when (SetScriptsExecutable `elem` opts) $ setScriptsExecutablePatches p where p = patchTree2RL r -applyRL :: (Invert p, ShowPatch p, Apply p, ApplyMonad DefaultIO (ApplyState p))+applyRL :: (Invert p, ShowPatch p, Apply p, ApplyMonad (ApplyState p) DefaultIO) => RL p wX wY -> IO () applyRL patches = sequence_ (mapFL safeApply (reverseRL patches)) -unapplyRL :: (Invert p, ShowPatch p, Apply p, ApplyMonad DefaultIO (ApplyState p))+unapplyRL :: (Invert p, ShowPatch p, Apply p, ApplyMonad (ApplyState p) DefaultIO) => RL p wX wY -> IO () unapplyRL patches = sequence_ (mapRL (safeApply . invert) patches) -safeApply :: (Invert p, ShowPatch p, Apply p, ApplyMonad DefaultIO (ApplyState p))+safeApply :: (Invert p, ShowPatch p, Apply p, ApplyMonad (ApplyState p) DefaultIO) => p wX wY -> IO () safeApply p = runDefault (apply p) `catch` \(msg :: IOException) -> fail $ "Bad patch:\n" ++ show msg
src/Darcs/UI/Commands/TransferMode.hs view
@@ -15,12 +15,12 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- -- The pragma above is only for pattern guards. module Darcs.UI.Commands.TransferMode ( transferMode ) where -import Prelude hiding ( catch )+import Prelude ()+import Darcs.Prelude+ import Control.Exception ( catch ) import System.IO ( stdout, hFlush ) @@ -33,6 +33,7 @@ import Darcs.Util.Progress ( setProgressMode ) import Darcs.Util.Global ( darcsdir ) import Darcs.Util.Path ( AbsolutePath )+import Darcs.Util.Ssh ( transferModeHeader ) import qualified Data.ByteString as B (hPut, readFile, length, ByteString) @@ -90,7 +91,7 @@ transferModeCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () transferModeCmd _ _ _ = do setProgressMode False- putStrLn "Hello user, I am darcs transfer mode"+ putStrLn transferModeHeader hFlush stdout withCurrentDirectory darcsdir transfer @@ -109,4 +110,3 @@ readfile :: String -> IO (Either String B.ByteString) readfile fn = (Right `fmap` B.readFile fn) `catch` (return . Left . prettyException)-
src/Darcs/UI/Commands/Unrecord.hs view
@@ -25,16 +25,19 @@ , matchingHead ) where -import Prelude hiding ( (^), catch )+import Prelude ()+import Darcs.Prelude +import Prelude hiding ( (^) )+ import Control.Exception ( catch, IOException )-import Control.Monad ( when )+import Control.Monad ( when, unless ) import Data.Maybe( isJust, mapMaybe ) import Data.List ( intercalate )-import Storage.Hashed.Tree( Tree )+import Darcs.Util.Tree( Tree ) import System.Exit ( exitSuccess ) -import Darcs.Patch ( RepoPatch, invert, commute, effect )+import Darcs.Patch ( IsRepoType, RepoPatch, invert, commute, effect ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Bundle ( makeBundleN, contextPatches, minContext ) import Darcs.Patch.Depends ( findCommonWithThem, newsetUnion )@@ -53,7 +56,7 @@ invalidateIndex, unrecordedChanges, identifyRepositoryFor ) import Darcs.Repository.Flags( UseIndex(..), ScanKnown(..), UpdateWorking(..), DryRun(NoDryRun) )-import Darcs.Repository.Lock( writeDocBinFile )+import Darcs.Util.Lock( writeDocBinFile ) import Darcs.Repository.Prefs ( getDefaultRepoPath ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, commandAlias , putVerbose, printDryRunMessageAndExit@@ -67,21 +70,21 @@ import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags, parseFlags ) import Darcs.UI.Options.All ( notInRemoteFlagName ) import qualified Darcs.UI.Options.All as O-import Darcs.UI.SelectChanges ( selectChanges, WhichChanges(..),+import Darcs.UI.SelectChanges ( WhichChanges(..), selectionContext, runSelection ) import qualified Darcs.UI.SelectChanges as S ( PatchSelectionOptions (..) ) import Darcs.Util.English ( presentParticiple )-import Darcs.Util.Printer ( text, putDoc )+import Darcs.Util.Printer ( text, putDoc, (<>), (<+>) ) import Darcs.Util.Progress ( debugMessage ) unrecordDescription :: String unrecordDescription =- "Remove recorded patches without changing the working copy."+ "Remove recorded patches without changing the working tree." unrecordHelp :: String unrecordHelp = unlines [ "Unrecord does the opposite of record: it deletes patches from"- , "the repository, without changing the working copy."+ , "the repository, without changing the working tree." , "Deleting patches from the repository makes active changes again" , "which you may record or revert later." , "Beware that you should not use this command if there is a"@@ -154,9 +157,9 @@ else matchingHead matchFlags allpatches let direction = if doReverse opts then Last else LastReversed context = selectionContext direction "unrecord" (patchSelOpts opts) Nothing Nothing- (_ :> to_unrecord) <- runSelection (selectChanges patches) context+ (_ :> to_unrecord) <- runSelection patches context when (nullFL to_unrecord) $ do- putStrLn "No patches selected!"+ putInfo opts "No patches selected!" exitSuccess putVerbose opts $ text "About to write out (potentially) modified patches..."@@ -165,10 +168,10 @@ _ <- tentativelyRemovePatches repository (compression opts) YesUpdateWorking to_unrecord finalizeRepositoryChanges repository YesUpdateWorking (compression opts)- putStrLn "Finished unrecording."+ putInfo opts "Finished unrecording." -getLastPatches :: RepoPatch p => [MatchFlag] -> PatchSet p Origin wR- -> (PatchSet p :> FL (PatchInfoAnd p)) Origin wR+getLastPatches :: (IsRepoType rt, RepoPatch p) => [MatchFlag] -> PatchSet rt p Origin wR+ -> (PatchSet rt p :> FL (PatchInfoAnd rt p)) Origin wR getLastPatches matchFlags ps = case matchFirstPatchset matchFlags ps of Sealed p1s -> findCommonWithThem ps p1s @@ -179,7 +182,7 @@ unpullHelp :: String unpullHelp = unlines [ "Unpull completely removes recorded patches from your local repository."- , "The changes will be undone in your working copy and the patches"+ , "The changes will be undone in your working tree and the patches" , "will not be shown in your changes list anymore. Beware that if the" , "patches are not still present in another repository you will lose" , "precious code by unpulling!"@@ -201,12 +204,12 @@ obliterateDescription :: String obliterateDescription =- "Delete selected patches from the repository. (UNSAFE!)"+ "Delete selected patches from the repository." obliterateHelp :: String obliterateHelp = unlines [ "Obliterate completely removes recorded patches from your local"- , "repository. The changes will be undone in your working copy and the"+ , "repository. The changes will be undone in your working tree and the" , "patches will not be shown in your changes list anymore. Beware that" , "you can lose precious code by obliterating!" , ""@@ -312,6 +315,7 @@ -> IO () genericObliterateCmd cmdname _ opts _ = let cacheOpt = useCache opts+ verbOpt = verbosity opts in withRepoLock (dryRun opts) cacheOpt YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do -- FIXME we may need to honour --ignore-times here, although this@@ -331,15 +335,15 @@ else matchingHead matchFlags allpatches nirs -> do (Sealed thems) <-- getNotInRemotePatches cacheOpt repository nirs+ getNotInRemotePatches verbOpt cacheOpt repository nirs return $ findCommonWithThem allpatches thems let direction = if doReverse opts then Last else LastReversed context = selectionContext direction cmdname (patchSelOpts opts) Nothing Nothing (kept :> removed) <-- runSelection (selectChanges removal_candidates) context+ runSelection removal_candidates context when (nullFL removed) $ do- putStrLn "No patches selected!"+ putInfo opts "No patches selected!" exitSuccess case commute (effect removed :> pend) of Nothing -> fail $ "Can't " ++ cmdname@@ -347,7 +351,7 @@ ++ "unrecorded change." Just (_ :> p_after_pending) -> do printDryRunMessageAndExit "obliterate"- (verbosity opts)+ verbOpt (hasSummary O.NoSummary opts) (dryRun opts) (hasXmlOutput opts)@@ -364,19 +368,21 @@ finalizeRepositoryChanges repository YesUpdateWorking (compression opts) debugMessage "Applying patches to working directory..."- _ <- applyToWorking repository (verbosity opts)+ _ <- applyToWorking repository verbOpt (invert p_after_pending) `catch` \(e :: IOException) -> fail $ "Couldn't undo patch in working dir.\n" ++ show e- putStrLn $ "Finished " ++ presentParticiple cmdname ++ "."+ putInfo opts $ "Finished" <+> text (presentParticiple cmdname) <> "." -- | Get the union of the set of patches in each specified location-getNotInRemotePatches :: (RepoPatch p, ApplyState p ~ Tree) => O.UseCache- -> Repository p wX wU wT -> [NotInRemoteLocation]- -> IO (SealedPatchSet p Origin)-getNotInRemotePatches cacheOpt repository nirs = do- putStrLn $ "Determining patches not in" ++ pluralExtra ++ ":\n" ++ names+getNotInRemotePatches :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => O.Verbosity -> O.UseCache+ -> Repository rt p wX wU wT -> [NotInRemoteLocation]+ -> IO (SealedPatchSet rt p Origin)+getNotInRemotePatches verbOpt cacheOpt repository nirs = do+ unless (verbOpt == O.Quiet) $+ putStrLn $ "Determining patches not in" ++ pluralExtra ++ ":\n" ++ names nirsPaths <- mapM getNotInRemotePath nirs newsetUnion `fmap` mapM readNir nirsPaths where@@ -401,23 +407,25 @@ -- | matchingHead returns the repository up to some tag. The tag t is the last -- tag such that there is a patch after t that is matched by the user's query.-matchingHead :: forall p wR. RepoPatch p => [MatchFlag] -> PatchSet p Origin wR- -> (PatchSet p :> FL (PatchInfoAnd p)) Origin wR+matchingHead :: forall rt p wR+ . (IsRepoType rt, RepoPatch p)+ => [MatchFlag] -> PatchSet rt p Origin wR+ -> (PatchSet rt p :> FL (PatchInfoAnd rt p)) Origin wR matchingHead matchFlags set = case mh set of (start :> patches) -> start :> reverseRL patches where- mh :: forall wX . PatchSet p Origin wX- -> (PatchSet p :> RL (PatchInfoAnd p)) Origin wX- mh s@(PatchSet x _)+ mh :: forall wX . PatchSet rt p Origin wX+ -> (PatchSet rt p :> RL (PatchInfoAnd rt p)) Origin wX+ mh s@(PatchSet _ x) | or (mapRL (matchAPatchread matchFlags) x) = contextPatches s- mh (PatchSet x (Tagged t _ ps :<: ts)) =- case mh (PatchSet (t :<: ps) ts) of- (start :> patches) -> start :> x +<+ patches+ mh (PatchSet (ts :<: Tagged t _ ps) x) =+ case mh (PatchSet ts (ps :<: t)) of+ (start :> patches) -> start :> patches +<+ x mh ps = ps :> NilRL savetoBundle :: (RepoPatch p, ApplyState p ~ Tree) => [DarcsFlag]- -> PatchSet p Origin wZ -> FL (PatchInfoAnd p) wZ wT -> IO ()+ -> PatchSet rt p Origin wZ -> FL (PatchInfoAnd rt p) wZ wT -> IO () savetoBundle opts kept removed@(x :>: _) = do let genFullBundle = makeBundleN Nothing kept (mapFL_FL hopefully removed) bundle <- if not (minimize opts)@@ -437,7 +445,6 @@ patchSelOpts flags = S.PatchSelectionOptions { S.verbosity = verbosity flags , S.matchFlags = parseFlags O.matchSeveralOrLast flags- , S.diffAlgorithm = diffAlgorithm flags , S.interactive = isInteractive True flags , S.selectDeps = selectDeps flags , S.summary = hasSummary O.NoSummary flags
src/Darcs/UI/Commands/Unrevert.hs view
@@ -20,11 +20,14 @@ module Darcs.UI.Commands.Unrevert ( unrevert, writeUnrevert ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( (^), catch ) import Control.Exception ( catch, IOException ) import System.Exit ( exitSuccess )-import Storage.Hashed.Tree( Tree )+import Darcs.Util.Tree( Tree ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository ) import Darcs.UI.Flags@@ -42,20 +45,20 @@ readRepo, readRecorded, applyToWorking, unrecordedChanges )-import Darcs.Patch ( RepoPatch, PrimOf, commute, namepatch, fromPrims )+import Darcs.Patch ( IsRepoType, RepoPatch, PrimOf, commute, fromPrims ) import Darcs.Patch.Apply( ApplyState )+import Darcs.Patch.Named.Wrapped ( namepatch ) import Darcs.Patch.Set ( Origin ) import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed) ) import Darcs.Patch.Witnesses.Ordered ( FL(..), (:>)(..), (+>+) ) import Darcs.UI.SelectChanges- ( selectChanges- , WhichChanges(First)+ ( WhichChanges(First) , runSelection , selectionContextPrim ) import qualified Darcs.UI.SelectChanges as S ( PatchSelectionOptions (..) ) import qualified Data.ByteString as B-import Darcs.Repository.Lock ( writeDocBinFile, removeFileMayNotExist )+import Darcs.Util.Lock ( writeDocBinFile, removeFileMayNotExist ) import Darcs.Patch.Depends ( mergeThem ) import Darcs.UI.External ( catchall ) import Darcs.Util.Prompt ( askUser )@@ -68,7 +71,7 @@ unrevertDescription :: String unrevertDescription =- "Undo the last revert (may fail if changes after the revert)."+ "Undo the last revert." unrevertHelp :: String unrevertHelp =@@ -121,7 +124,6 @@ patchSelOpts flags = S.PatchSelectionOptions { S.verbosity = verbosity flags , S.matchFlags = []- , S.diffAlgorithm = diffAlgorithm flags , S.interactive = isInteractive True flags , S.selectDeps = O.PromptDeps -- option not supported, use default , S.summary = O.NoSummary -- option not supported, use default@@ -162,7 +164,7 @@ ( UseIndex, ScanKnown, diffAlgorithm opts ) NilFL h_them let context = selectionContextPrim First "unrevert" (patchSelOpts opts) Nothing Nothing (Just recorded)- (p :> skipped) <- runSelection (selectChanges pw) context+ (p :> skipped) <- runSelection pw context tentativelyAddToPending repository YesUpdateWorking p withSignalsBlocked $ do finalizeRepositoryChanges repository YesUpdateWorking (compression opts)@@ -174,8 +176,8 @@ debugMessage "Finished unreverting." unrevertCmd _ _ _ = impossible -writeUnrevert :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> FL (PrimOf p) wX wY+writeUnrevert :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> FL (PrimOf p) wX wY -> Tree IO -> FL (PrimOf p) wR wX -> IO () writeUnrevert repository NilFL _ _ = removeFileMayNotExist $ unrevertUrl repository writeUnrevert repository ps recorded pend =@@ -190,10 +192,10 @@ np <- namepatch date "unrevert" "anon" [] (fromRepoPrims repository p') bundle <- makeBundleN (Just recorded) rep (np :>: NilFL) writeDocBinFile (unrevertUrl repository) bundle- where fromRepoPrims :: RepoPatch p => Repository p wR wU wT -> FL (PrimOf p) wR wY -> FL p wR wY+ where fromRepoPrims :: RepoPatch p => Repository rt p wR wU wT -> FL (PrimOf p) wR wY -> FL p wR wY fromRepoPrims _ = fromPrims -unrevertPatchBundle :: RepoPatch p => Repository p wR wU wT -> IO (SealedPatchSet p Origin)+unrevertPatchBundle :: RepoPatch p => Repository rt p wR wU wT -> IO (SealedPatchSet rt p Origin) unrevertPatchBundle repository = do pf <- B.readFile (unrevertUrl repository) `catchall` fail "There's nothing to unrevert!"
src/Darcs/UI/Commands/Util.hs view
@@ -16,9 +16,10 @@ -- Boston, MA 02110-1301, USA. {-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-} module Darcs.UI.Commands.Util ( announceFiles- , filterExistingFiles+ , filterExistingPaths , testTentativeAndMaybeExit , getUniqueRepositoryName , getUniqueDPatchName@@ -26,34 +27,37 @@ import Control.Monad ( unless ) +import Prelude ()+import Darcs.Prelude+ import System.Exit ( ExitCode(..), exitWith ) -import Storage.Hashed.Monad ( virtualTreeIO, exists )-import Storage.Hashed.Tree ( Tree )-import Storage.Hashed( floatPath, readPlainTree )+import Darcs.Util.Tree.Monad ( virtualTreeIO, exists )+import Darcs.Util.Tree ( Tree ) -import Darcs.Util.Path- ( SubPath, toFilePath, getUniquePathName ) import Darcs.Patch ( RepoPatch )-import Darcs.Repository ( Repository, readRecorded, readUnrecorded,- testTentative )-import Darcs.Repository.State ( applyTreeFilter, restrictBoring )-import Darcs.Repository.Flags ( LookForAdds (..) )+import Darcs.Repository ( Repository, readRecorded, testTentative )+import Darcs.Repository.State ( readUnrecordedFiltered ) import Darcs.Patch.Apply ( ApplyState ) import Darcs.Patch.Bundle ( patchFilename ) import Darcs.UI.Options.All- ( Verbosity, SetScriptsExecutable, TestChanges (..)- , RunTest(..), LeaveTestDir(..) )+ ( Verbosity(..), SetScriptsExecutable, TestChanges (..)+ , RunTest(..), LeaveTestDir(..), UseIndex, ScanKnown(..) ) import Darcs.Util.Exception ( clarifyErrors )+import Darcs.Util.Path+ ( SubPath, toFilePath, getUniquePathName, floatPath )+import Darcs.Util.Printer ( putDocLn, text, (<>), (<+>) ) import Darcs.Util.Prompt ( PromptConfig(..), promptChar )+import Darcs.Util.Text ( pathlist ) -announceFiles :: Maybe [SubPath] -> String -> IO ()-announceFiles Nothing _ = return ()-announceFiles (Just files) message = putStrLn $ message ++ " " ++- unwords (map show files) ++ ":\n"+announceFiles :: Verbosity -> Maybe [SubPath] -> String -> IO ()+announceFiles Quiet _ _ = return ()+announceFiles _ (Just subpaths) message = putDocLn $+ text message <> text ":" <+> pathlist (map toFilePath subpaths)+announceFiles _ _ _ = return () testTentativeAndMaybeExit :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT+ => Repository rt p wR wU wT -> Verbosity -> TestChanges -> SetScriptsExecutable@@ -74,29 +78,34 @@ yn <- promptChar (PromptConfig prompt "yn" [] (Just 'n') []) unless (yn == 'y') doExit -filterExistingFiles :: (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT- -> LookForAdds+-- | Given a repository and two common command options, classify the given list+-- of subpaths according to whether they exist in the pristine or working tree.+-- Paths which are neither in working nor pristine are reported and dropped.+-- The result is a pair of path lists: those that exist only in the working tree,+-- and those that exist in pristine or working.+filterExistingPaths :: (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> Verbosity+ -> UseIndex+ -> ScanKnown -> [SubPath]- -> IO [SubPath]-filterExistingFiles repo lfa files = do+ -> IO ([SubPath],[SubPath])+filterExistingPaths repo verb useidx scan paths = do pristine <- readRecorded repo- -- TODO this is slightly inefficient, since we should really somehow- -- extract the unrecorded state as a side-effect of unrecordedChanges- index <- readUnrecorded repo $ Just files- nonboring <- restrictBoring index- working <- applyTreeFilter nonboring `fmap` readPlainTree "."- let paths = map toFilePath files- check = virtualTreeIO $ mapM (exists . floatPath) paths- (in_working, _) <- check working+ working <- readUnrecordedFiltered repo useidx scan (Just paths)+ let filepaths = map toFilePath paths+ check = virtualTreeIO $ mapM (exists . floatPath) filepaths (in_pristine, _) <- check pristine- mapM_ maybe_warn $ zip3 paths in_working in_pristine- return [ path | (path, True) <- zip files (zipWith (||) in_working in_pristine) ]- where maybe_warn (file, False, False) =- putStrLn $ "WARNING: File '"++file++"' does not exist!"- maybe_warn (file, True, False) | lfa == YesLookForAdds =- putStrLn $ "WARNING: File '" ++ file ++ "' not in repository!"- maybe_warn _ = return ()+ (in_working, _) <- check working+ let paths_with_info = zip3 paths in_pristine in_working+ paths_in_neither = [ p | (p,False,False) <- paths_with_info ]+ paths_only_in_working = [ p | (p,False,True) <- paths_with_info ]+ paths_in_either = [ p | (p,inp,inw) <- paths_with_info, inp || inw ]+ or_not_added = if scan == ScanKnown then " or not added " else " "+ unless (verb == Quiet || null paths_in_neither) $ putDocLn $+ "Ignoring non-existing" <> or_not_added <> "paths:" <+>+ pathlist (map toFilePath paths_in_neither)+ return (paths_only_in_working, paths_in_either) getUniqueRepositoryName :: Bool -> FilePath -> IO FilePath getUniqueRepositoryName talkative name = getUniquePathName talkative buildMsg buildName
src/Darcs/UI/Commands/Util/Tree.hs view
@@ -24,22 +24,24 @@ , treeHasAnycase ) where +import Prelude ()+import Darcs.Prelude+ import Control.Monad ( forM ) import Control.Monad.State.Strict( gets ) import qualified Data.ByteString.Char8 as BSC import Data.Char ( toLower ) -import Storage.Hashed.Monad+import Darcs.Util.Tree.Monad ( withDirectory, fileExists, directoryExists , virtualTreeMonad, currentDirectory , TreeMonad )-import qualified Storage.Hashed.Monad as HS ( exists, tree )-import Storage.Hashed.Tree ( Tree, listImmediate, findTree )-import Storage.Hashed ( floatPath )+import qualified Darcs.Util.Tree.Monad as HS ( exists, tree )+import Darcs.Util.Tree ( Tree, listImmediate, findTree ) import Darcs.Util.Path- ( AnchoredPath(..), Name(..) )+ ( AnchoredPath(..), Name(..), floatPath ) treeHasAnycase :: (Functor m, Monad m) => Tree m
src/Darcs/UI/Commands/WhatsNew.hs view
@@ -24,18 +24,20 @@ , status ) where -import Prelude hiding ( (^), catch )+import Prelude ()+import Darcs.Prelude -import Control.Applicative ( (<$>) ) import Control.Monad ( void ) import Control.Monad.Reader ( runReaderT ) import Control.Monad.State ( evalStateT, liftIO )-import Storage.Hashed.Tree ( Tree )+import Darcs.Util.Tree ( Tree ) import System.Exit ( ExitCode (..), exitSuccess, exitWith )+import Data.List.Ordered ( nubSort ) import Darcs.Patch ( PrimOf, PrimPatch, RepoPatch , applyToTree, plainSummaryPrims, primIsHunk+ , listTouchedFiles, IsRepoType ) import Darcs.Patch.Apply ( ApplyState ) import Darcs.Patch.Choices ( patchChoicesLps, lpPatch )@@ -60,16 +62,17 @@ import Darcs.Patch.Witnesses.WZipper ( FZipper (..) ) import Darcs.Repository ( RepoJob (..), Repository- , listRegisteredFiles, readRecorded+ , listRegisteredFiles, readRecorded, readRepo , unrecordedChangesWithPatches, withRepository ) import Darcs.Repository.Diff ( treeDiff ) import Darcs.Repository.Prefs ( filetypeFunction )-import Darcs.Repository.Util ( getMovesPs, getReplaces )+import Darcs.Repository.State ( getMovesPs, getReplaces ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, amInRepository , commandAlias, nodefaults )+import Darcs.Repository.Resolution ( patchsetConflictResolutions ) import Darcs.UI.Commands.Util ( announceFiles ) import Darcs.UI.Flags ( DarcsFlag (Summary, LookForAdds, LookForMoves), diffAlgorithm, diffingOpts@@ -94,7 +97,7 @@ , skipOne, printSummary ) import qualified Darcs.UI.SelectChanges as S ( PatchSelectionOptions (..) )-import Darcs.Util.Path ( AbsolutePath, SubPath, toFilePath )+import Darcs.Util.Path ( AbsolutePath, SubPath, toFilePath, fp2fn ) import Darcs.Util.Printer ( putDocLn, renderString, RenderMode(..) , text, vcat@@ -120,7 +123,7 @@ ^ O.workingRepoDir ^ O.interactive -- False -whatsnewAdvancedOpts :: DarcsOption a (O.UseIndex -> Bool -> a)+whatsnewAdvancedOpts :: DarcsOption a (O.UseIndex -> O.IncludeBoring -> a) whatsnewAdvancedOpts = O.useIndex ^ O.includeBoring whatsnewOpts :: DarcsOption a@@ -137,7 +140,7 @@ -> O.Verbosity -> Bool -> O.UseIndex- -> Bool+ -> O.IncludeBoring -> O.UseCache -> Maybe String -> Bool@@ -150,7 +153,6 @@ patchSelOpts flags = S.PatchSelectionOptions { S.verbosity = verbosity flags , S.matchFlags = []- , S.diffAlgorithm = diffAlgorithm flags , S.interactive = isInteractive True flags , S.selectDeps = O.PromptDeps -- option not supported, use default , S.summary = hasSummary (defaultSummary flags) flags@@ -222,7 +224,7 @@ whatsnewCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () whatsnewCmd fps opts args =- withRepository (useCache opts) $ RepoJob $ \(repo :: Repository p wR wU wR) -> do+ withRepository (useCache opts) $ RepoJob $ \(repo :: Repository rt p wR wU wR) -> do files <- if null args then return Nothing else Just <$> fixSubPaths fps args@@ -256,12 +258,12 @@ -- Return the patches that create files/dirs that aren't yet added. unFreeLeft <$> treeDiff (diffAlgorithm opts) ftf noLookAddsTree lookAddsTree else return (Sealed NilFL)- announceFiles files "What's new in"+ announceFiles (verbosity opts) files "What's new in" exitOnNoChanges (unaddedNewPathsPs, noLookChanges) if maybeIsInteractive opts then runInteractive (interactiveHunks pristine) opts' pristine noLookChanges else do- printChanges opts' pristine noLookChanges+ printChanges repo opts' pristine noLookChanges printUnaddedPaths unaddedNewPathsPs where -- |Filter out hunk patches (leaving add patches) and return the tree@@ -278,7 +280,7 @@ printUnaddedPaths :: PrimPatch p => FL p wX wY -> IO () printUnaddedPaths NilFL = return () printUnaddedPaths ps =- putDocLn . lowercaseAs . renderString Encode . (plainSummaryPrims False) $ ps+ putDocLn . lowercaseAs . renderString Encode . (plainSummaryPrims False []) $ ps -- Make any add markers lowercase, to distinguish new-but-unadded files -- from those that are unrecorded, but added.@@ -287,25 +289,30 @@ lowercaseA x = x -- |Appropriately print changes, according to the passed flags.- printChanges :: (PatchListFormat p, IsHunk p, Patchy p, ShowPatch p, PrimDetails p,- ApplyState p ~ Tree) => [DarcsFlag] -> Tree IO -> FL p wX wY+ printChanges :: forall rt p wR wU wX wY. (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ => Repository rt p wR wU wR -> [DarcsFlag] -> Tree IO -> FL (PrimOf p) wX wY -> IO ()- printChanges opts' pristine changes- | Summary `elem` opts' = putDocLn $ plainSummaryPrims machineReadable changes+ printChanges repo opts' pristine changes+ | Summary `elem` opts' = do+ r <- readRepo repo+ Sealed res <- return $ patchsetConflictResolutions r+ let conflictFns = map fp2fn $ nubSort $ listTouchedFiles res+ putDocLn $ plainSummaryPrims machineReadable conflictFns changes | isUnified opts' == O.YesContext = contextualPrintPatch pristine changes | otherwise = printPatch changes where machineReadable = parseFlags O.machineReadable opts -- |return the unrecorded changes that affect an optional list of paths.- filteredUnrecordedChanges :: forall p wR wU wT. (RepoPatch p, ApplyState p ~ Tree,- ApplyState (PrimOf p) ~ Tree) => [DarcsFlag]- -> Repository p wR wU wT -> Maybe [SubPath]+ filteredUnrecordedChanges :: forall rt p wR wU wT. (RepoPatch p, ApplyState p ~ Tree,+ ApplyState (PrimOf p) ~ Tree)+ => [DarcsFlag]+ -> Repository rt p wR wU wT -> Maybe [SubPath] -> FL (PrimOf p) wR wT -- look-for-moves patches -> FL (PrimOf p) wT wT -- look-for-replaces patches -> IO (Sealed (FL (PrimOf p) wT)) filteredUnrecordedChanges opts' repo files movesPs replacesPs = let filePaths = map toFilePath <$> files in- choosePreTouching filePaths <$> unrecordedChangesWithPatches (diffingOpts opts') repo files movesPs replacesPs+ choosePreTouching filePaths <$> unrecordedChangesWithPatches movesPs replacesPs (diffingOpts opts') repo files -- | Runs the 'InteractiveSelectionM' code runInteractive :: (PatchListFormat p, IsHunk p, Patchy p, ShowPatch p,@@ -325,7 +332,8 @@ , choices = choices' } void $ runReaderT ps $- selectionContextPrim First "view" (patchSelOpts opts) (Just primSplitter)+ selectionContextPrim First "view" (patchSelOpts opts)+ (Just (primSplitter (diffAlgorithm opts))) Nothing (Just pristine) -- | The interactive part of @darcs whatsnew@
src/Darcs/UI/CommandsAux.hs view
@@ -24,6 +24,9 @@ , hasMaliciousPath ) where +import Prelude ()+import Darcs.Prelude+ import Control.Monad ( when ) import Darcs.UI.Flags ( DarcsFlag )
src/Darcs/UI/Defaults.hs view
@@ -1,5 +1,8 @@ module Darcs.UI.Defaults ( applyDefaults ) where +import Prelude ()+import Darcs.Prelude+ import Control.Monad.Writer import Data.Char ( isSpace ) import Data.Functor.Compose ( Compose(..) )@@ -8,8 +11,8 @@ import qualified Data.Map as M import System.Console.GetOpt import Text.Regex.Applicative- ( (<$>), (<*>), (*>), (<|>)- , match, pure, many, some+ ( (<|>)+ , match, many, some , psym, anySym, string ) import Darcs.UI.Flags ( DarcsFlag )
src/Darcs/UI/Email.hs view
@@ -5,6 +5,9 @@ , formatHeader ) where +import Prelude ()+import Darcs.Prelude+ import Data.Char ( digitToInt, isHexDigit, ord, intToDigit, isPrint, toUpper ) import Data.List ( isInfixOf ) import Darcs.Util.Printer
src/Darcs/UI/External.hs view
@@ -11,7 +11,6 @@ , execPipeIgnoreError , pipeDoc , pipeDocSSH- , maybeURLCmd , viewDoc , viewDocWith , haveSendmail@@ -27,7 +26,8 @@ , isUTF8Locale ) where -import Prelude hiding ( catch )+import Prelude ()+import Darcs.Prelude import Data.Maybe ( isJust, isNothing, maybeToList ) import Control.Monad ( unless, when, filterM, liftM2, void )@@ -58,14 +58,14 @@ ( try, finally, catch, IOException ) import System.IO.Error ( ioeGetErrorType ) import GHC.IO.Exception ( IOErrorType(ResourceVanished) )-import Data.Char ( toUpper, toLower )+import Data.Char ( toLower ) import Text.Regex #if defined (HAVE_MAPI) import Foreign.C ( withCString ) #endif #ifdef HAVE_MAPI import Foreign.Ptr ( nullPtr )-import Darcs.Repository.Lock ( canonFilename, writeDocBinFile )+import Darcs.Util.Lock ( canonFilename, writeDocBinFile ) #endif import Darcs.Util.SignalHandler ( catchNonSignal )@@ -83,12 +83,12 @@ ,take, concat, drop, isPrefixOf, singleton, append) import qualified Data.ByteString.Char8 as BC (unpack, pack) -import Darcs.Repository.Lock+import Darcs.Util.Lock ( withTemp , withNamedTemp , withOpenTemp )-import Darcs.Repository.Ssh ( getSSH, SSHCmd(..) )+import Darcs.Util.Ssh ( getSSH, SSHCmd(..) ) import Darcs.Util.CommandLine ( parseCmd, addUrlencoded ) import Darcs.Util.Exec ( execInteractive, exec, Redirect(..), withoutNonBlock ) import Darcs.Util.URL ( SshFilePath, sshUhost )@@ -121,12 +121,6 @@ #else darcsProgram = getProgName #endif--maybeURLCmd :: String -> String -> IO (Maybe String)-maybeURLCmd what url =- do let prot = map toUpper $ takeWhile (/= ':') url- fmap Just (getEnv ("DARCS_" ++ what ++ "_" ++ prot))- `catch` \(_ :: IOException) -> return Nothing pipeDoc :: RenderMode -> String -> [String] -> Doc -> IO ExitCode pipeDoc = pipeDocInternal (PipeToOther simplePrinters)
src/Darcs/UI/Flags.hs view
@@ -15,6 +15,7 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. +{-# LANGUAGE OverloadedStrings #-} module Darcs.UI.Flags ( -- TODO we want to stop exporting the constructors of DarcsFlag -- from here. First need to change all the relevant code over to the@@ -27,6 +28,7 @@ , editDescription , diffingOpts , diffOpts+ , scanKnown , externalMerge , wantGuiPause , isInteractive@@ -92,7 +94,8 @@ , applyAs ) where -import Prelude hiding ( (^) )+import Prelude ()+import Darcs.Prelude import Data.List ( nub, intercalate ) import Data.Maybe@@ -102,7 +105,6 @@ , catMaybes ) import Control.Monad ( unless )-import Control.Applicative( (<$>) ) import System.Directory ( doesDirectoryExist, createDirectory ) import System.FilePath.Posix ( (</>) ) @@ -119,7 +121,7 @@ ( askUser , askUserListItem )-import Darcs.Repository.Lock ( writeLocaleFile )+import Darcs.Util.Lock ( writeLocaleFile ) import Darcs.Repository.Prefs ( getPreflist , getGlobal@@ -138,8 +140,9 @@ , ioAbsolute , makeAbsoluteOrStd )-import Darcs.Util.Printer ( putDocLn, text, ($$) )+import Darcs.Util.Printer ( putDocLn, ePutDocLn, text, ($$), (<+>) ) import Darcs.Util.URL ( isValidLocalPath )+import Darcs.Util.Text ( pathlist ) type Config = [F.DarcsFlag] @@ -158,17 +161,18 @@ editDescription :: Config -> Bool editDescription = parseFlags O.editDescription --- | Non-trivial interaction between options.-diffOpts :: O.UseIndex -> O.LookForAdds -> Bool -> O.DiffAlgorithm -> (O.UseIndex, O.ScanKnown, O.DiffAlgorithm)+diffOpts :: O.UseIndex -> O.LookForAdds -> O.IncludeBoring -> O.DiffAlgorithm -> (O.UseIndex, O.ScanKnown, O.DiffAlgorithm) diffOpts use_index look_for_adds include_boring diff_alg = (use_index, scanKnown look_for_adds include_boring, diff_alg)- where- scanKnown O.NoLookForAdds _ = O.ScanKnown- scanKnown O.YesLookForAdds False = O.ScanAll- scanKnown O.YesLookForAdds True = O.ScanBoring +-- | Non-trivial interaction between options.+scanKnown :: O.LookForAdds -> O.IncludeBoring -> O.ScanKnown+scanKnown O.NoLookForAdds _ = O.ScanKnown+scanKnown O.YesLookForAdds O.NoIncludeBoring = O.ScanAll+scanKnown O.YesLookForAdds O.YesIncludeBoring = O.ScanBoring+ diffingOpts :: Config -> (O.UseIndex, O.ScanKnown, O.DiffAlgorithm)-diffingOpts flags = diffOpts (useIndex flags) (lookForAdds flags) False (diffAlgorithm flags)+diffingOpts flags = diffOpts (useIndex flags) (lookForAdds flags) O.NoIncludeBoring (diffAlgorithm flags) externalMerge :: Config -> O.ExternalMerge externalMerge = parseFlags O.useExternalMerge@@ -207,7 +211,9 @@ doHappyForwarding = parseFlags O.happyForwarding includeBoring :: Config -> Bool-includeBoring = parseFlags O.includeBoring+includeBoring cfg = case parseFlags O.includeBoring cfg of+ O.NoIncludeBoring -> False+ O.YesIncludeBoring -> True doAllowCaseOnly :: Config -> Bool doAllowCaseOnly = parseFlags O.allowCaseDifferingFilenames@@ -303,9 +309,14 @@ then toFilePath `fmap` withCurrentDirectory d (ioAbsolute f) else return f --- | @maybeFixSubPaths files@ tries to turn the file paths in its argument into--- @SubPath@s.+-- | @maybeFixSubPaths (repo_path, orig_path) file_paths@ tries to turn+-- @file_paths@ into 'SubPath's, taking into account the repository path and+-- the original path from which darcs was invoked. --+-- A 'SubPath' is a path /under/ (or inside) the repo path. This does /not/+-- mean it must exist as a file or directory, nor that the path has been added+-- to the repository; it merely means that it /could/ be added.+-- -- 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@@ -314,30 +325,24 @@ -- -- It is intended for validating file arguments to darcs commands. maybeFixSubPaths :: (AbsolutePath, AbsolutePath) -> [FilePath] -> IO [Maybe SubPath]-maybeFixSubPaths (r, o) fs = withCurrentDirectory o $ do+maybeFixSubPaths (r, o) fs = do fixedFs <- mapM fixit fs let bads = snd . unzip . filter (isNothing . fst) $ zip fixedFs fs- unless (null bads) . putStrLn $ "Ignoring non-repository paths: " ++- intercalate ", " bads+ unless (null bads) $+ ePutDocLn $ text "Ignoring non-repository paths:" <+> pathlist bads return fixedFs where- fixit p = do ap <- ioAbsolute p+ fixit p = do ap <- withCurrentDirectory o $ ioAbsolute p case makeSubPathOf r ap of Just sp -> return $ Just sp- Nothing -> withCurrentDirectory r $ do- absolutePathByRepodir <- ioAbsolute p+ Nothing -> do+ absolutePathByRepodir <- withCurrentDirectory r $ ioAbsolute p return $ makeSubPathOf r absolutePathByRepodir --- | @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' is a variant of 'maybeFixSubPaths' that throws out+-- non-repository paths and duplicates from the result. See there for details.+-- TODO: why filter out null paths from the input? why here and not in+-- 'maybeFixSubPaths'? fixSubPaths :: (AbsolutePath, AbsolutePath) -> [FilePath] -> IO [SubPath] fixSubPaths fps fs = nub . catMaybes <$> maybeFixSubPaths fps (filter (not . null) fs)
src/Darcs/UI/Message/Send.hs view
@@ -21,10 +21,12 @@ -- | Help text and UI messages for @darcs send@ module Darcs.UI.Message.Send where +import Prelude ()+import Darcs.Prelude+ import Darcs.Util.Path ( FilePathLike(..), toFilePath )-import Darcs.UI.Commands ( formatPath ) import Darcs.Repository.Flags ( DryRun(..) )-import Darcs.Util.Text ( sentence )+import Darcs.Util.Text ( sentence, quote ) import Darcs.Util.Printer cmdDescription :: String@@ -113,7 +115,7 @@ cannotSendToSelf = "Can't send to current repository! Did you mean send --context?" creatingPatch :: String -> Doc-creatingPatch repodir = "Creating patch to" <+> text (formatPath repodir) <> "..."+creatingPatch repodir = "Creating patch to" <+> text (quote repodir) <> "..." noWorkingSendmail :: Doc noWorkingSendmail = "No working sendmail instance on your machine!"
src/Darcs/UI/Options.hs view
@@ -7,6 +7,9 @@ , optDescr ) where +import Prelude ()+import Darcs.Prelude+ import Data.Functor.Compose import System.Console.GetOpt
src/Darcs/UI/Options/All.hs view
@@ -104,6 +104,7 @@ , UseIndex (..) -- re-export , ScanKnown (..) -- re-export , diffing+ , IncludeBoring (..) , includeBoring , allowProblematicFilenames , allowCaseDifferingFilenames@@ -234,6 +235,9 @@ , optimizePatchIndex ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( (^) ) import Data.Char ( isDigit ) import Data.List ( intercalate )@@ -265,6 +269,8 @@ , WantGuiPause (..) , WithPatchIndex (..) , WithWorkingDir (..)+ , PatchFormat (..)+ , IncludeBoring (..) ) import qualified Darcs.UI.Options.Flags as F ( DarcsFlag(..) )@@ -663,17 +669,17 @@ scanKnown :: PrimDarcsOption ScanKnown scanKnown = imap (Iso fw bw) $ lookforadds ^ includeBoring where- fw k ScanKnown = k NoLookForAdds False- fw k ScanAll = k YesLookForAdds False- fw k ScanBoring = k YesLookForAdds True+ fw k ScanKnown = k NoLookForAdds NoIncludeBoring+ fw k ScanAll = k YesLookForAdds NoIncludeBoring+ fw k ScanBoring = k YesLookForAdds YesIncludeBoring bw k NoLookForAdds _ = k ScanKnown- bw k YesLookForAdds False = k ScanAll- bw k YesLookForAdds True = k ScanBoring+ bw k YesLookForAdds NoIncludeBoring = k ScanAll+ bw k YesLookForAdds YesIncludeBoring = k ScanBoring -includeBoring :: PrimDarcsOption Bool-includeBoring = withDefault False- [ RawNoArg [] ["boring"] F.Boring True "don't skip boring files"- , RawNoArg [] ["no-boring"] F.SkipBoring False "skip boring files" ]+includeBoring :: PrimDarcsOption IncludeBoring+includeBoring = withDefault NoIncludeBoring+ [ RawNoArg [] ["boring"] F.Boring YesIncludeBoring "don't skip boring files"+ , RawNoArg [] ["no-boring"] F.SkipBoring NoIncludeBoring "skip boring files" ] allowProblematicFilenames :: DarcsOption a (Bool -> Bool -> a) allowProblematicFilenames = allowCaseDifferingFilenames ^ allowWindowsReservedFilenames@@ -1094,8 +1100,11 @@ -- | See above. machineReadable :: PrimDarcsOption Bool-machineReadable = singleNoArg [] ["machine-readable"] F.MachineReadable "give machine-readable output"+machineReadable = withDefault False [__machineReadable True] +__machineReadable :: RawDarcsOption+__machineReadable val = RawNoArg [] ["machine-readable"] F.MachineReadable val "give machine-readable output"+ -- ** clone partial :: PrimDarcsOption CloneKind@@ -1127,14 +1136,12 @@ , "Use --darcs-1 to get the effect that --hashed had previously." ] $ [ RawNoArg [] ["hashed"] F.Hashed () "deprecated, use --darcs-1 instead" ] -data PatchFormat = PatchFormat1 | PatchFormat2 deriving (Eq, Show)- patchFormat :: PrimDarcsOption PatchFormat patchFormat = withDefault PatchFormat2 [ RawNoArg [] ["darcs-2"] F.UseFormat2 PatchFormat2 "Standard darcs patch format" , RawNoArg [] ["darcs-1"] F.UseFormat1 PatchFormat1- "Older patch format (for compatibility)"]+ "Older patch format (for compatibility)" ] -- ** dist @@ -1148,13 +1155,14 @@ -- ** log -data ChangesFormat = HumanReadable | GenContext | GenXml | NumberPatches | CountPatches deriving (Eq, Show)+data ChangesFormat = HumanReadable | MachineReadable | GenContext | GenXml | NumberPatches | CountPatches deriving (Eq, Show) changesFormat :: PrimDarcsOption (Maybe ChangesFormat) changesFormat = withDefault Nothing [ RawNoArg [] ["context"] F.GenContext (Just GenContext) "give output suitable for get --context" , __xmloutput (Just GenXml) , __humanReadable (Just HumanReadable)+ , __machineReadable (Just MachineReadable) , RawNoArg [] ["number"] F.NumberPatches (Just NumberPatches) "number the changes" , RawNoArg [] ["count"] F.Count (Just CountPatches) "output count of changes" ]
src/Darcs/UI/Options/Core.hs view
@@ -32,8 +32,8 @@ -} module Darcs.UI.Options.Core where -import Prelude hiding ( (^) )-import Data.Monoid ( Monoid(..) )+import Prelude ()+import Darcs.Prelude import Darcs.UI.Options.Iso
src/Darcs/UI/Options/Flags.hs view
@@ -3,6 +3,9 @@ -- should import 'Darcs.UI.Flags' module Darcs.UI.Options.Flags ( DarcsFlag(..) ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Util.Path ( AbsolutePath, AbsolutePathOrStd ) -- | The 'DarcsFlag' type is a list of all flags that can ever be
src/Darcs/UI/Options/Iso.hs view
@@ -1,4 +1,8 @@ module Darcs.UI.Options.Iso where++import Prelude ()+import Darcs.Prelude+ -- * Isomorphisms -- | Lightweight type ismomorphisms (a.k.a. invertible functions). If
src/Darcs/UI/Options/Markdown.hs view
@@ -1,6 +1,9 @@ -- Support for @darcs help markdown@ module Darcs.UI.Options.Markdown ( optionsMarkdown ) where +import Prelude ()+import Darcs.Prelude+ import Data.Functor.Compose ( Compose(..) ) import System.Console.GetOpt ( OptDescr(..), ArgDescr(..) ) import Darcs.UI.Options.Util ( DarcsOptDescr )
src/Darcs/UI/Options/Matching.hs view
@@ -13,7 +13,7 @@ -} module Darcs.UI.Options.Matching ( MatchFlag(..) -- re-export- , matchOne+ , matchUpToOne , matchOneContext , matchOneNontag , matchSeveral@@ -22,12 +22,13 @@ , matchRange , matchSeveralOrRange , matchAny -- temporary, for toMatchFlags- , context -- temporary, for getContext ) where -import Prelude hiding ( last )+import Prelude ()+import Darcs.Prelude hiding ( last )+ import Data.Char ( isDigit )-import Data.Monoid ( (<>), mconcat )+import Data.Monoid ( (<>) ) import Darcs.Patch.Match ( MatchFlag(..) ) import qualified Darcs.UI.Options.Flags as F ( DarcsFlag(..) )@@ -40,8 +41,8 @@ -- * Combined matching options -matchOne :: MatchOption -- ^ amend, show files/contents, dist, annotate-matchOne = mconcat [match, patch, hash, tag, index]+matchUpToOne :: MatchOption -- ^ show files/contents, dist, annotate+matchUpToOne = mconcat [match, patch, hash, tag, index] -- | Used by: clone matchOneContext :: MatchOption
src/Darcs/UI/Options/Util.hs view
@@ -26,6 +26,9 @@ , makeAbsoluteOrStd ) where +import Prelude ()+import Darcs.Prelude+ import System.Console.GetOpt ( OptDescr(..), ArgDescr(..) ) import Data.Functor.Compose import Data.List ( intercalate )
+ src/Darcs/UI/PatchHeader.hs view
@@ -0,0 +1,295 @@+module Darcs.UI.PatchHeader+ ( getLog+ , getAuthor+ , updatePatchHeader, AskAboutDeps(..)+ , HijackT, HijackOptions(..)+ , runHijackT+ ) where++import Prelude ()+import Darcs.Prelude++import Darcs.Patch+ ( IsRepoType, RepoPatch, Patchy, PrimPatch, PrimOf, fromPrims+ , effect+ , summaryFL+ )+import Darcs.Patch.Apply ( ApplyState )+import Darcs.Patch.Info ( PatchInfo,+ piAuthor, piName, piLog, piDateString,+ patchinfo, isInverted, invertName,+ )+import Darcs.Patch.Named.Wrapped ( infopatch, getdeps, adddeps )+import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, n2pia, hopefully, info )+import Darcs.Patch.Prim ( canonizeFL )++import Darcs.Patch.Witnesses.Ordered ( FL(..), (+>+) )++import Darcs.Repository ( Repository )+import Darcs.Util.Lock+ ( readLocaleFile+ , writeLocaleFile+ , appendToFile+ )++import Darcs.UI.External ( editFile )+import Darcs.UI.Flags ( getEasyAuthor, promptAuthor, getDate )+import qualified Darcs.UI.Options.All as O+import qualified Darcs.UI.SelectChanges as S ( PatchSelectionOptions(..) )+import Darcs.UI.SelectChanges ( askAboutDepends )++import Darcs.Util.ByteString ( encodeLocale )+import qualified Darcs.Util.Diff as D ( DiffAlgorithm )+import Darcs.Util.English ( capitalize )+import Darcs.Util.Global ( darcsLastMessage )+import Darcs.Util.Path ( FilePathLike, toFilePath )+import Darcs.Util.Prompt ( PromptConfig(..), askUser, promptChar, promptYorn )+import Darcs.Util.Printer ( hPutDocLn, text, ($$), prefixLines, RenderMode(..) )+import qualified Darcs.Util.Ratified as Ratified ( hGetContents )++import Darcs.Util.Tree ( Tree )++import Control.Exception ( catch, IOException )+import Control.Monad ( when, void )+import Control.Monad.Trans ( liftIO )+import Control.Monad.Trans.State.Strict ( StateT(..), evalStateT, get, put )+import qualified Data.ByteString as B ( hPut )+import Data.List ( isPrefixOf )+import System.Exit ( exitSuccess )+import System.IO ( stdin )++data PName = FlagPatchName String | PriorPatchName String | NoPatchName++-- | Options for how to deal with the situation where we are somehow+-- modifying a patch that is not our own+data HijackOptions = IgnoreHijack -- ^ accept all hijack requests+ | RequestHijackPermission -- ^ prompt once, accepting subsequent hijacks if yes+ | AlwaysRequestHijackPermission -- ^ always prompt++-- | Transformer for interactions with a hijack warning state that we+-- need to thread through+type HijackT = StateT HijackOptions++-- | Get the patch name and long description from one of+--+-- * the configuration (flags, defaults, hard-coded)+--+-- * an existing log file+--+-- * stdin (e.g. a pipe)+--+-- * a text editor+--+-- It ensures the patch name is not empty nor starts with the prefix TAG.+--+-- The last result component is a possible path to a temporary file that should be removed later.+getLog :: forall prim wX wY . (Patchy prim, PrimPatch prim)+ => Maybe String -- ^ patchname option+ -> Bool -- ^ pipe option+ -> O.Logfile -- ^ logfile option+ -> Maybe O.AskLongComment -- ^ askLongComment option+ -> Maybe (String, [String]) -- ^ possibly an existing patch name and long description+ -> FL prim wX wY -- ^ changes to record+ -> IO (String, [String], Maybe String) -- ^ patch name, long description and possibly the path+ -- to the temporary file that should be removed later+getLog m_name has_pipe log_file ask_long m_old chs = go has_pipe log_file ask_long where+ go True _ _ = do+ p <- case patchname_specified of+ FlagPatchName p -> return p+ PriorPatchName p -> return p+ NoPatchName -> prompt_patchname False+ putStrLn "What is the log?"+ thelog <- lines `fmap` Ratified.hGetContents stdin+ return (p, thelog, Nothing)+ go _ (O.Logfile { O._logfile = Just f }) _ = do+ mlp <- lines `fmap` readLocaleFile f `catch` (\(_ :: IOException) -> return [])+ firstname <- case (patchname_specified, mlp) of+ (FlagPatchName p, []) -> return p+ (_, p:_) -> if badName p+ then prompt_patchname True+ else return p -- logfile trumps prior!+ (PriorPatchName p, []) -> return p+ (NoPatchName, []) -> prompt_patchname True+ append_info f firstname+ when (ask_long == Just O.YesEditLongComment) (void $ editFile f)+ (name, thelog) <- read_long_comment f firstname+ return (name, thelog, if O._rmlogfile log_file then Just $ toFilePath f else Nothing)+ go _ _ (Just O.YesEditLongComment) =+ case patchname_specified of+ FlagPatchName p -> actually_get_log p+ PriorPatchName p -> actually_get_log p+ NoPatchName -> actually_get_log ""+ go _ _ (Just O.NoEditLongComment) =+ case patchname_specified of+ FlagPatchName p -> return (p, default_log, Nothing) -- record (or amend) -m+ PriorPatchName p -> return (p, default_log, Nothing) -- amend+ NoPatchName -> do p <- prompt_patchname True -- record+ return (p, [], Nothing)+ go _ _ (Just O.PromptLongComment) =+ case patchname_specified of+ FlagPatchName p -> prompt_long_comment p -- record (or amend) -m+ PriorPatchName p -> prompt_long_comment p+ NoPatchName -> prompt_patchname True >>= prompt_long_comment+ go _ _ Nothing =+ case patchname_specified of+ FlagPatchName p -> return (p, default_log, Nothing) -- record (or amend) -m+ PriorPatchName "" -> actually_get_log ""+ PriorPatchName p -> return (p, default_log, Nothing)+ NoPatchName -> actually_get_log ""++ patchname_specified = case (m_name, m_old) of+ (Just name, _) | badName name -> NoPatchName+ | otherwise -> FlagPatchName name+ (Nothing, Just (name,_)) -> PriorPatchName name+ (Nothing, Nothing) -> NoPatchName++ badName "" = True+ badName n = "TAG" `isPrefixOf` n++ default_log = case m_old of+ Nothing -> []+ Just (_,l) -> l++ prompt_patchname retry =+ do n <- askUser "What is the patch name? "+ if badName n+ then if retry then prompt_patchname retry+ else fail "Bad patch name!"+ else return n++ prompt_long_comment oldname =+ do y <- promptYorn "Do you want to add a long comment?"+ if y then actually_get_log oldname+ else return (oldname, [], Nothing)++ actually_get_log p = do let logf = darcsLastMessage+ -- 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+ _ <- editFile logf+ (name,long) <- read_long_comment logf p+ if badName name+ then do putStrLn "WARNING: empty or incorrect patch name!"+ pn <- prompt_patchname True+ return (pn, long, Nothing)+ else return (name,long,Just logf)++ read_long_comment :: FilePathLike p => p -> String -> IO (String, [String])+ read_long_comment f oldname =+ do f' <- readLocaleFile f+ let t = filter (not.("#" `isPrefixOf`)) $ (lines.filter (/='\r')) f'+ case t of [] -> return (oldname, [])+ (n:ls) -> return (n, ls)++ append_info f oldname =+ do fc <- readLocaleFile f+ appendToFile f $ \h ->+ do case fc of+ _ | null (lines fc) -> B.hPut h (encodeLocale (oldname ++ "\n"))+ | last fc /= '\n' -> B.hPut h (encodeLocale "\n")+ | otherwise -> return ()+ hPutDocLn Encode h+ $ text "# Please enter the patch name in the first line, and"+ $$ text "# optionally, a long description in the following lines."+ $$ text "#"+ $$ text "# Lines starting with '#' will be ignored."+ $$ text "#"+ $$ text "#"+ $$ text "# Summary of selected changes:"+ $$ text "#"+ $$ prefixLines (text "#") (summaryFL chs)++-- |specify whether to ask about dependencies with respect to a particular repository, or not+data AskAboutDeps rt p wR wU wT = AskAboutDeps (Repository rt p wR wU wT) | NoAskAboutDeps++-- | Run a job that involves a hijack confirmation prompt.+--+-- See 'RequestHijackPermission' for initial values+runHijackT :: Monad m => HijackOptions -> HijackT m a -> m a+runHijackT = flip evalStateT++-- | Update the metadata for a patch.+-- This potentially involves a bit of interactivity, so we may return @Nothing@+-- if there is cause to abort what we're doing along the way+updatePatchHeader :: forall rt p wX wY wR wU wT . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => String -- ^ verb: command name+ -> AskAboutDeps rt p wR wU wT+ -> S.PatchSelectionOptions+ -> D.DiffAlgorithm+ -> Bool -- keepDate+ -> Bool -- selectAuthor+ -> Maybe String -- author+ -> Maybe String -- patchname+ -> Maybe O.AskLongComment+ -> PatchInfoAnd rt p wT wX+ -> FL (PrimOf p) wX wY+ -> HijackT IO (Maybe String, PatchInfoAnd rt p wT wY)+updatePatchHeader verb ask_deps pSelOpts da nKeepDate nSelectAuthor nAuthor nPatchname nAskLongComment oldp chs = do++ let newchs = canonizeFL da (effect oldp +>+ chs)++ let old_pdeps = getdeps $ hopefully oldp+ newdeps <-+ case ask_deps of+ AskAboutDeps repository -> liftIO $ askAboutDepends repository newchs pSelOpts old_pdeps+ NoAskAboutDeps -> return old_pdeps++ let old_pinf = info oldp+ prior = (piName old_pinf, piLog old_pinf)+ date <- if nKeepDate then return (piDateString old_pinf) else liftIO $ getDate False+ new_author <- getAuthor verb nSelectAuthor nAuthor old_pinf+ liftIO $ do+ (new_name, new_log, mlogf) <- getLog+ nPatchname False (O.Logfile Nothing False) nAskLongComment (Just prior) chs+ let maybe_invert = if isInverted old_pinf then invertName else id+ new_pinf <- maybe_invert `fmap` patchinfo date new_name new_author new_log+ let newp = n2pia (adddeps (infopatch new_pinf (fromPrims newchs)) newdeps)+ return (mlogf, newp)+++-- | @getAuthor@ tries to return the updated author for the patch.+-- There are two different scenarios:+--+-- * [explicit] Either we want to override the patch author, be it by+-- prompting the user (@select@) or having them pass it in from+-- the UI (@new_author@), or+--+-- * [implicit] We want to keep the original author, in which case we+-- also double-check that we are not inadvertently \"hijacking\"+-- somebody else's patch (if the patch author is not the same as the+-- repository author, we give them a chance to abort the whole+-- operation)+getAuthor :: String -- ^ verb: command name+ -> Bool -- ^ select: prompt for new auhor+ -> Maybe String -- ^ new author: explict new author+ -> PatchInfo -- ^ patch to update+ -> HijackT IO String+getAuthor _ True _ _ = do+ auth <- liftIO $ promptAuthor False True+ return auth+getAuthor _ False (Just new) _ =+ return new+getAuthor verb False Nothing pinfo = do+ whitelist <- liftIO $ getEasyAuthor+ hj <- get+ if orig `elem` whitelist || canIgnore hj+ then allowHijack+ else do+ hijackResp <- liftIO $ askAboutHijack hj+ case hijackResp of+ 'y' -> allowHijack+ 'a' -> put IgnoreHijack >> allowHijack+ _ -> liftIO exitSuccess+ where+ askAboutHijack hj = promptChar (PromptConfig msg opts [] Nothing [])+ where+ msg = "You're not " ++ orig ++"! " ++ capitalize verb ++ " anyway? "+ opts = case hj of+ AlwaysRequestHijackPermission -> "yn"+ _ -> "yna"+ canIgnore IgnoreHijack = True+ canIgnore RequestHijackPermission = False+ canIgnore AlwaysRequestHijackPermission = False+ allowHijack = return orig+ orig = piAuthor pinfo
src/Darcs/UI/PrintPatch.hs view
@@ -25,8 +25,11 @@ , showFriendly ) where -import Storage.Hashed.Monad( virtualTreeIO )-import Storage.Hashed.Tree( Tree )+import Prelude ()+import Darcs.Prelude++import Darcs.Util.Tree.Monad( virtualTreeIO )+import Darcs.Util.Tree( Tree ) import Darcs.Util.Printer.Color ( fancyPrinters ) import Darcs.Patch.Apply ( ApplyState )
− src/Darcs/UI/RemoteApply.hs
@@ -1,65 +0,0 @@--- | This module is used by the push and put commands to apply 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.UI.RemoteApply ( remoteApply ) where--import System.Exit ( ExitCode )--import Darcs.UI.Flags ( DarcsFlag, remoteDarcs, applyAs )-import Darcs.Util.Text ( breakCommand )-import Darcs.Util.URL ( isHttpUrl, isSshUrl, splitSshUrl, SshFilePath(..) )-import Darcs.UI.External- ( darcsProgram- , pipeDoc- , pipeDocSSH- , maybeURLCmd- )-import Darcs.UI.Options ( parseFlags )-import Darcs.UI.Options.All ( debug, compress )-import qualified Darcs.Repository.Ssh as Ssh ( remoteDarcs )-import Darcs.Util.Printer ( Doc, RenderMode(..) )--remoteApply :: [DarcsFlag] -> String -> Doc -> IO ExitCode-remoteApply opts repodir bundle- = case applyAs opts of- Nothing- | isSshUrl repodir -> applyViaSsh opts (splitSshUrl repodir) bundle- | isHttpUrl repodir -> applyViaUrl opts repodir bundle- | otherwise -> applyViaLocal opts repodir bundle- Just un -> if isSshUrl repodir- then applyViaSshAndSudo opts (splitSshUrl repodir) un bundle- else applyViaSudo un repodir bundle--applyViaSudo :: String -> String -> Doc -> IO ExitCode-applyViaSudo user repo bundle =- darcsProgram >>= \darcs ->- pipeDoc Standard "sudo" ["-u",user,darcs,"apply","--all","--repodir",repo] bundle--applyViaLocal :: [DarcsFlag] -> String -> Doc -> IO ExitCode-applyViaLocal opts repo bundle =- darcsProgram >>= \darcs ->- pipeDoc Standard darcs ("apply":"--all":"--repodir":repo:applyopts opts) bundle--applyViaUrl :: [DarcsFlag] -> String -> Doc -> IO ExitCode-applyViaUrl opts repo bundle =- do maybeapply <- maybeURLCmd "APPLY" repo- case maybeapply of- Nothing -> applyViaLocal opts repo bundle- Just apply ->- do let (cmd, args) = breakCommand apply- pipeDoc Standard cmd (args ++ [repo]) bundle--applyViaSsh :: [DarcsFlag] -> SshFilePath -> Doc -> IO ExitCode-applyViaSsh opts repo =- pipeDocSSH (parseFlags compress opts) Standard repo- [Ssh.remoteDarcs (remoteDarcs opts) ++" apply --all "++unwords (applyopts opts)++- " --repodir '"++sshRepo repo++"'"]--applyViaSshAndSudo :: [DarcsFlag] -> SshFilePath -> String -> Doc -> IO ExitCode-applyViaSshAndSudo opts repo username =- pipeDocSSH (parseFlags compress opts) Standard repo- ["sudo -u "++username++" "++Ssh.remoteDarcs (remoteDarcs opts)++- " apply --all --repodir '"++sshRepo repo++"'"]--applyopts :: [DarcsFlag] -> [String]-applyopts opts = if parseFlags debug opts then ["--debug"] else []
src/Darcs/UI/RunCommand.hs view
@@ -20,8 +20,9 @@ -- arguments and then running the command itself. module Darcs.UI.RunCommand ( runTheCommand ) where -import Prelude hiding ( (^) )-import Data.Functor ((<$>))+import Prelude ()+import Darcs.Prelude+ import Data.List ( intercalate ) import Control.Monad ( unless, when ) import System.Console.GetOpt( ArgOrder( Permute, RequireOrder ),@@ -59,7 +60,6 @@ , extractCommands , superName , subusage- , formatPath ) import Darcs.UI.Commands.GZCRCs ( doCRCWarnings ) import Darcs.UI.Commands.Clone ( makeRepoName, cloneToSSH )@@ -69,15 +69,16 @@ import Darcs.Repository.Test ( runPosthook, runPrehook ) import Darcs.Util.AtExit ( atexit ) import Darcs.Util.Download ( setDebugHTTP, disableHTTPPipelining )+import Darcs.Util.Exception ( die ) import Darcs.Util.Global ( setDebugMode, setTimingsMode ) import Darcs.Util.Path ( AbsolutePath, getCurrentDirectory, toPath, ioAbsoluteOrRemote, makeAbsolute ) import Darcs.Util.Printer ( text ) import Darcs.Util.Progress ( setProgressMode )-import Darcs.Util.Text ( chompTrailingNewline )+import Darcs.Util.Text ( chompTrailingNewline, quote ) runTheCommand :: [CommandControl] -> String -> [String] -> IO () runTheCommand commandControlList cmd args =- either fail rtc $ disambiguateCommands commandControlList cmd args+ either die rtc $ disambiguateCommands commandControlList cmd args where rtc (CommandOnly c, as) = runCommand Nothing c as rtc (SuperCommandOnly c, as) = runRawSupercommand c as@@ -86,7 +87,7 @@ runCommand :: Maybe (DarcsCommand pf1) -> DarcsCommand pf2 -> [String] -> IO () 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?"+ die "Are you sure you didn't mean --all rather than -all?" runCommand msuper cmd args = do old_wd <- getCurrentDirectory let options = commandOptions old_wd cmd@@ -111,18 +112,18 @@ file_args <- commandGetArgPossibilities cmd putStrLn $ intercalate "\n" $ getOptionsOptions options : file_args Just Disable ->- fail $ "Command "++commandName cmd++" disabled with --disable option!"+ die $ "Command "++commandName cmd++" disabled with --disable option!" Nothing -> case prereq_errors of- Left complaint -> fail $- "Unable to " ++ formatPath ("darcs " ++ superName msuper ++ commandName cmd) +++ Left complaint -> die $+ "Unable to " ++ quote ("darcs " ++ superName msuper ++ commandName cmd) ++ " here.\n\n" ++ complaint Right () -> case getopt_errs ++ flag_errors of [] -> do extra <- commandArgdefaults cmd flags old_wd orig_extra case extraArgumentsError extra cmd msuper of Nothing -> runWithHooks cmd old_wd flags extra- Just msg -> fail msg- es -> fail (intercalate "\n" es)+ Just msg -> die msg+ es -> die (intercalate "\n" es) fixupMsgs :: (a, b, [String]) -> (a, b, [String]) fixupMsgs (fs,as,es) = (fs,as,map (("command line: "++).chompTrailingNewline) es)@@ -174,7 +175,7 @@ reponame <- makeRepoName False flags repodir return $ makeAbsolute new_wd reponame _ -> return new_wd- _ -> fail "You must provide 'clone' with either one or two arguments."+ _ -> die "You must provide 'clone' with either one or two arguments." getPosthookDir new_wd _ _ _ = return new_wd @@ -210,7 +211,7 @@ runRawSupercommand :: DarcsCommand pf -> [String] -> IO () runRawSupercommand super [] =- fail $ "Command '"++ commandName super ++"' requires a subcommand!\n\n"+ die $ "Command '"++ commandName super ++"' requires a subcommand!\n\n" ++ subusage super runRawSupercommand super args = do cwd <- getCurrentDirectory@@ -223,8 +224,8 @@ putStrLn "--help" mapM_ (putStrLn . wrappedCommandName) (extractCommands $ getSubcommands super) Just Disable -> do- fail $ "Command " ++ commandName super +++ die $ "Command " ++ commandName super ++ " disabled with --disable option!"- Nothing -> fail $ case getopt_errs of+ Nothing -> die $ case getopt_errs of [] -> "Invalid subcommand!\n\n" ++ subusage super _ -> intercalate "\n" getopt_errs
src/Darcs/UI/SelectChanges.hs view
@@ -20,8 +20,7 @@ module Darcs.UI.SelectChanges ( -- * Working with changes- selectChanges- , WhichChanges(..)+ WhichChanges(..) , viewChanges , withSelectedPatchFromRepo , runSelection@@ -55,6 +54,9 @@ , askAboutDepends ) where +import Prelude ()+import Darcs.Prelude+ import Prelude hiding ( (^) ) import Control.Monad ( liftM, unless, when, (>=>) ) import Control.Monad.Identity ( Identity (..) )@@ -67,15 +69,14 @@ , modify, runStateT ) import Control.Monad.Trans ( liftIO )-import Data.Char ( toUpper ) import Data.List ( intercalate, union )-import Data.Maybe ( isJust, isNothing, catMaybes )+import Data.Maybe ( isJust, catMaybes ) import System.Exit ( exitSuccess ) import Darcs.Patch- ( Patchy, PrimPatch, RepoPatch, PrimOf+ ( Patchy, PrimPatch, IsRepoType, RepoPatch, PrimOf , commuteFLorComplain, invert- , listTouchedFiles, anonymous, fromPrims+ , listTouchedFiles, fromPrims ) import qualified Darcs.Patch ( thing, things, summary ) import Darcs.Patch.Apply ( ApplyState )@@ -95,6 +96,7 @@ import Darcs.Patch.Inspect ( PatchInspect ) import Darcs.Patch.Invert ( Invert ) import Darcs.Patch.Match ( haveNonrangeMatch, matchAPatch, matchAPatchread )+import Darcs.Patch.Named.Wrapped ( anonymous ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, info, n2pia ) import Darcs.Patch.Set ( PatchSet(..), newset2RL ) import Darcs.Patch.Show ( ShowPatch )@@ -119,15 +121,15 @@ import Darcs.Repository ( Repository, readRepo, readTentativeRepo ) import Darcs.UI.External ( editText ) import Darcs.UI.Options.All- ( Verbosity(..), Summary(..), DiffAlgorithm(..)+ ( Verbosity(..), Summary(..) , WithContext(..), SelectDeps(..), MatchFlag ) import Darcs.UI.PrintPatch ( printFriendly, printPatch , printPatchPager, showFriendly )-import Darcs.Util.English ( Noun (..), englishNum )+import Darcs.Util.English ( Noun (..), englishNum, capitalize ) import Darcs.Util.Printer ( prefix, putDocLn ) import Darcs.Util.Prompt ( PromptConfig (..), askUser, promptChar )-import Storage.Hashed.Tree ( Tree )+import Darcs.Util.Tree ( Tree ) -- | When asking about patches, we either ask about them in@@ -166,7 +168,6 @@ data PatchSelectionOptions = PatchSelectionOptions { verbosity :: Verbosity , matchFlags :: [MatchFlag]- , diffAlgorithm :: DiffAlgorithm , interactive :: Bool , selectDeps :: SelectDeps , summary :: Summary@@ -206,8 +207,8 @@ } -- | A 'PatchSelectionContext' for selecting full patches ('PatchInfoAnd' patches)-selectionContext :: (RepoPatch p) => WhichChanges -> String -> PatchSelectionOptions -> Maybe (Splitter (PatchInfoAnd p))- -> Maybe [FilePath] -> PatchSelectionContext (PatchInfoAnd p)+selectionContext :: (IsRepoType rt, RepoPatch p) => WhichChanges -> String -> PatchSelectionOptions -> Maybe (Splitter (PatchInfoAnd rt p))+ -> Maybe [FilePath] -> PatchSelectionContext (PatchInfoAnd rt p) selectionContext whch jn o spl fs = PSC { opts = o , splitter = spl@@ -220,8 +221,8 @@ } -- | A generic 'PatchSelectionContext'.-selectionContextGeneric :: (RepoPatch p, Invert q)- => (forall wX wY . q wX wY -> Sealed2 (PatchInfoAnd p))+selectionContextGeneric :: (IsRepoType rt, RepoPatch p, Invert q)+ => (forall wX wY . q wX wY -> Sealed2 (PatchInfoAnd rt p)) -> WhichChanges -> String -> PatchSelectionOptions@@ -265,13 +266,13 @@ triv = MatchCriterion { mcHasNonrange = False, mcFunction = \ _ _ -> True } -- | 'iswanted' selects patches according to the given match flags-iswanted :: forall p q- . (RepoPatch p, Invert q)- => (forall wX wY . q wX wY -> Sealed2 (PatchInfoAnd p))+iswanted :: forall rt p q+ . (IsRepoType rt, RepoPatch p, Invert q)+ => (forall wX wY . q wX wY -> Sealed2 (PatchInfoAnd rt p)) -> [MatchFlag] -> MatchCriterion q iswanted extract mflags = MatchCriterion- { mcHasNonrange = haveNonrangeMatch (PatchType :: PatchType p) mflags+ { mcHasNonrange = haveNonrangeMatch (PatchType :: PatchType rt p) mflags , mcFunction = isWantedMcFunction } where@@ -285,32 +286,31 @@ liftR = asks . runReader -- | runs a 'PatchSelection' action in the given 'PatchSelectionContext'.-runSelection :: (Patchy p) => PatchSelection p wX wY -> PatchSelectionContext p+runSelection :: forall p wX wY . (Patchy p, PatchInspect p, ShowPatch p, ApplyState p ~ Tree)+ => FL p wX wY+ -> PatchSelectionContext p -> IO ((FL p :> FL p) wX wY)-runSelection = runReaderT---- | Select patches from a @FL@.-selectChanges :: forall p wX wY . (Patchy p, PatchInspect p, ShowPatch p, ApplyState p ~ Tree) =>- FL p wX wY- -> PatchSelection p wX wY-selectChanges ps = do- whch <- asks whichChanges- case whch of+runSelection ps psc = runReaderT patchSelection psc+ where+ patchSelection = do+ case whichChanges psc of First -> normal ps Last -> normal ps FirstReversed -> reversed ps LastReversed -> reversed ps- where normal = sc1- reversed = return . invert >=> sc1 >=> return . invertC+ normal fl = sc1 fl+ reversed fl = do let ifl = invert fl+ choices_ <- sc1 ifl+ return $ invertC choices_ sc1 :: forall p wX wY . (Patchy p, PatchInspect p, ShowPatch p, ApplyState p ~ Tree) => FL p wX wY -> PatchSelection p wX wY-sc1 =- (liftR . patchesToConsider)- >=> realSelectChanges- >=> (\ps -> do whch <- asks whichChanges ; return $ selectedPatches whch ps)- >=> (liftR . canonizeAfterSplitter)+sc1 fl = do ps <- liftR (patchesToConsider fl)+ patchChoices_ <- realSelectChanges ps+ whch <- asks whichChanges+ let sps = selectedPatches whch patchChoices_+ liftR (canonizeAfterSplitter sps) -- | inverses the choices that have been made invertC :: (Patchy p) => (FL p :> FL p) wX wY -> (FL p :> FL p) wY wX@@ -324,7 +324,7 @@ repr Last (Sealed2 p) = Sealed2 p repr FirstReversed (Sealed2 p) = Sealed2 (invert p) --- | The equivalent of 'selectChanges' for the @darcs changes@ command+-- | The equivalent of 'runSelection' for the @darcs log@ command viewChanges :: (Patchy p, ShowPatch p, ApplyState p ~ Tree) => PatchSelectionOptions -> [Sealed2 p] -> IO () viewChanges ps_opts = textView ps_opts Nothing 0 [] @@ -353,11 +353,11 @@ -- | The function for selecting a patch to amend record. Read at your own risks. withSelectedPatchFromRepo ::- forall p wR wU wT. (RepoPatch p, ApplyState p ~ Tree)+ forall rt p wR wU wT. (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => String -- name of calling command (always "amend" as of now)- -> Repository p wR wU wT+ -> Repository rt p wR wU wT -> PatchSelectionOptions- -> (forall wA . (FL (PatchInfoAnd p) :> PatchInfoAnd p) wA wR -> IO ())+ -> (forall wA . (FL (PatchInfoAnd rt p) :> PatchInfoAnd rt p) wA wR -> IO ()) -> IO () withSelectedPatchFromRepo jn repository o job = do patchSet <- readRepo repository@@ -374,14 +374,14 @@ -- | 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 :: forall p wX wY wU. (RepoPatch p, ApplyState p ~ Tree)+wspfr :: forall rt p wX wY wU. (RepoPatch p, ApplyState p ~ Tree) => String- -> (forall wA wB . (PatchInfoAnd p) wA wB -> Bool)- -> RL (PatchInfoAnd p) wX wY- -> FL (WithSkipped (PatchInfoAnd p)) wY wU- -> IO (Maybe (FlippedSeal (FL (PatchInfoAnd p) :> PatchInfoAnd p) wU))+ -> (forall wA wB . (PatchInfoAnd rt p) wA wB -> Bool)+ -> RL (PatchInfoAnd rt p) wX wY+ -> FL (WithSkipped (PatchInfoAnd rt p)) wY wU+ -> IO (Maybe (FlippedSeal (FL (PatchInfoAnd rt p) :> PatchInfoAnd rt p) wU)) wspfr _ _ NilRL _ = return Nothing-wspfr jn matches remaining@(p:<:pps) skipped+wspfr jn matches remaining@(pps:<:p) skipped | not $ matches p = wspfr jn matches pps (WithSkipped SkippedAutomatically p :>: skipped) | otherwise =@@ -407,22 +407,21 @@ 'p' -> printPatchPager p >> repeatThis 'x' -> do putDocLn $ prefix " " $ Darcs.Patch.summary p repeatThis- 'q' -> do putStrLn $ jnCapital ++ " cancelled."+ 'q' -> do putStrLn $ (capitalize jn) ++ " cancelled." exitSuccess _ -> do putStrLn $ helpFor jn basicOptions advancedOptions repeatThis- where jnCapital = toUpper (head jn) : tail jn- repeatThis = wspfr jn matches (p:<:pps) skipped+ where repeatThis = wspfr jn matches (pps:<:p) skipped prompt' = "Shall I " ++ jn ++ " this patch?" nextPatch = wspfr jn matches pps (WithSkipped SkippedManually p:>:skipped)- previousPatch :: RL (PatchInfoAnd p) wX wQ- -> FL (WithSkipped (PatchInfoAnd p)) wQ wU- -> IO (Maybe (FlippedSeal (FL (PatchInfoAnd p) :> PatchInfoAnd p) wU))+ previousPatch :: RL (PatchInfoAnd rt p) wX wQ+ -> FL (WithSkipped (PatchInfoAnd rt p)) wQ wU+ -> IO (Maybe (FlippedSeal (FL (PatchInfoAnd rt p) :> PatchInfoAnd rt p) wU)) previousPatch remaining' NilFL = wspfr jn matches remaining' NilFL previousPatch remaining' (WithSkipped sk prev :>: skipped'') = case sk of- SkippedManually -> wspfr jn matches (prev :<: remaining') skipped''- SkippedAutomatically -> previousPatch (prev :<: remaining') skipped''+ SkippedManually -> wspfr jn matches (remaining' :<: prev) skipped''+ SkippedAutomatically -> previousPatch (remaining' :<: prev) skipped'' basicOptions = [[ KeyPress 'y' (jn ++ " this patch") , KeyPress 'n' ("don't " ++ jn ++ " it")@@ -441,11 +440,9 @@ -- After selecting with a splitter, the results may not be canonical canonizeAfterSplitter :: (FL p :> FL p) wX wY -> Reader (PatchSelectionContext p) ((FL p :> FL p) wX wY) canonizeAfterSplitter (x :> y) =- do o <- asks opts- mspl <- asks splitter- let canonizeIfNeeded = maybe (\_ b -> b) canonizeSplit mspl- da = diffAlgorithm o- return $ canonizeIfNeeded da x :> canonizeIfNeeded da y+ do mspl <- asks splitter+ let canonizeIfNeeded = maybe id canonizeSplit mspl+ return $ canonizeIfNeeded x :> canonizeIfNeeded y realSelectChanges :: forall p wX wY. (Patchy p, ShowPatch p, PatchInspect p, ApplyState p ~ Tree)@@ -512,10 +509,15 @@ FirstReversed -> TouchesFiles.selectNotTouching LastReversed -> TouchesFiles.deselectNotTouching everything = patchChoices ps- if isNothing fs && not (mcHasNonrange crit)- then return everything- else do notUnwanted <- deselectUnwanted everything- return $ deselectNotTouching fs notUnwanted+ -- first filter patches by matchers+ -- this often requires to read only metadata+ notUnwanted <- if not (mcHasNonrange crit)+ then return everything+ else deselectUnwanted everything+ -- if there are path restrictions, we need to read patch contents+ -- of what remains+ return $ if isJust fs then deselectNotTouching fs notUnwanted+ else notUnwanted -- | Returns the results of a patch selection user interaction selectedPatches :: Patchy p => WhichChanges -> PatchChoices p wY wZ@@ -733,8 +735,7 @@ -> InteractiveSelectionM p wX wY () splitCurrent s = do FZipper lps_done (lp:>:lps_todo) <- gets lps- o <- asks opts- case applySplitter s (diffAlgorithm o) (lpPatch lp) of+ case applySplitter s (lpPatch lp) of Nothing -> return () Just (text, parse) -> do@@ -858,9 +859,7 @@ reprCur = repr whichch (Sealed2 (lpPatch lp)) (basicOptions,advancedOptions) <- options singleFile theSlot <- liftChoices $ patchSlot' lp- let- the_default = getDefault (whichch == Last || whichch == FirstReversed) theSlot- jnCapital = toUpper (head jn) : tail jn+ let the_default = getDefault (whichch == Last || whichch == FirstReversed) theSlot yorn <- promptUser singleFile the_default let nextPatch = skipMundane >> showCur case yorn of@@ -893,7 +892,7 @@ skipAll return True 'q' -> liftIO $- do putStrLn $ jnCapital++" cancelled."+ do putStrLn $ capitalize jn ++ " cancelled." exitSuccess 'j' -> skipOne >> showCur >> return False 'k' -> backOne >> showCur >> return False@@ -909,7 +908,7 @@ aThing <- thing let (basicOptions, advancedOptions) = optionsLast jn aThing yorn <- liftIO . promptChar $- PromptConfig { pPrompt = "Do you want to "++jn++" these "++ theThings++"?"+ PromptConfig { pPrompt = "Do you want to "++capitalize jn++" these "++theThings++"?" , pBasicCharacters = "yglqk" , pAdvancedCharacters = "dan" , pDefault = Just 'y'@@ -1030,7 +1029,7 @@ boring :> interesting -> do justDone $ lengthFL boring + numSkipped- modify $ \isc -> isc {lps = FZipper (reverseFL boring +<+ reverseFL skipped +<+ lps_done) interesting}+ modify $ \isc -> isc {lps = FZipper (lps_done +<+ reverseFL skipped +<+ reverseFL boring) interesting} where show_skipped o jn n ps = do putStrLn $ _nevermind_ jn ++ _these_ n ++ "." when (verbosity o == Verbose) $@@ -1055,8 +1054,8 @@ getDefault False InFirst = 'y' getDefault False InLast = 'n' -askAboutDepends :: forall p wR wU wT wY . (RepoPatch p, ApplyState p ~ Tree)- => Repository p wR wU wT -> FL (PrimOf p) wT wY+askAboutDepends :: forall rt p wR wU wT wY . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> FL (PrimOf p) wT wY -> PatchSelectionOptions -> [PatchInfo] -> IO [PatchInfo] askAboutDepends repository pa' ps_opts olddeps = do@@ -1068,8 +1067,8 @@ -- FIXME: this code is completely unreadable FlippedSeal ps <- return ((case pps of- PatchSet x _ -> FlippedSeal (reverseRL x+>+(pa:>:NilFL)))- :: FlippedSeal (FL (PatchInfoAnd p)) wY)+ PatchSet _ x -> FlippedSeal (reverseRL x+>+(pa:>:NilFL)))+ :: FlippedSeal (FL (PatchInfoAnd rt p)) wY) let (pc, my_lps) = patchChoicesLps ps tas = case catMaybes (mapFL (\lp -> if pa `unsafeCompare` lpPatch lp || info (lpPatch lp) `elem` olddeps then Just (label lp) else Nothing) my_lps) of@@ -1077,7 +1076,7 @@ [] -> error "askAboutDepends: []" tgs -> tgs Sealed2 ps' <- return $ case getChoices (forceFirsts tas pc) of _ :> mc :> _ -> Sealed2 $ mapFL_FL lpPatch mc- (deps:>_) <- runSelection (selectChanges ps') $+ (deps:>_) <- runSelection ps' $ selectionContext FirstReversed "depend on" ps_opts { matchFlags = [], interactive = True } Nothing Nothing return $ olddeps `union` mapFL info deps {-
src/Darcs/UI/TheCommands.hs view
@@ -19,6 +19,7 @@ module Darcs.UI.TheCommands ( commandControlList ) where import Prelude ()+ import Darcs.UI.Commands.Add ( add ) import Darcs.UI.Commands.Amend ( amend, amendrecord ) import Darcs.UI.Commands.Annotate ( annotate )@@ -30,7 +31,7 @@ import Darcs.UI.Commands.GZCRCs ( gzcrcs ) import Darcs.UI.Commands.Init ( initialize ) import Darcs.UI.Commands.Log ( log, changes )-import Darcs.UI.Commands.Show ( showCommand, list, query )+import Darcs.UI.Commands.Show ( showCommand ) import Darcs.UI.Commands.MarkConflicts ( markconflicts ) import Darcs.UI.Commands.Move ( move, mv ) import Darcs.UI.Commands.Optimize ( optimize )@@ -58,45 +59,45 @@ -- are also listed here. commandControlList :: [CommandControl] commandControlList =- [ commandGroup "Changing and querying the working copy:"+ [ commandGroup "Most used/starting out:"+ , normalCommand initialize , normalCommand add- , normalCommand remove, hiddenCommand unadd, hiddenCommand rm+ , normalCommand whatsnew, hiddenCommand status+ , normalCommand record, hiddenCommand commit+ , normalCommand clone, hiddenCommand get, hiddenCommand put+ , normalCommand pull+ , normalCommand push+ , commandGroup "Preparing patches before recording:" , normalCommand move, hiddenCommand mv+ , normalCommand remove, hiddenCommand unadd, hiddenCommand rm , normalCommand replace+ , commandGroup "Querying the repository:"+ , normalCommand log, hiddenCommand changes+ , normalCommand annotate+ , normalCommand diffCommand+ , normalCommand showCommand+ , normalCommand test+ , commandGroup "Undoing and correcting:" , normalCommand revert , normalCommand unrevert- , normalCommand whatsnew, hiddenCommand status- , commandGroup "Copying changes between the working copy and the repository:"- , normalCommand record, hiddenCommand commit+ , normalCommand amend, hiddenCommand amendrecord+ , normalCommand rebase+ , normalCommand rollback , normalCommand unrecord- , normalCommand amend- , hiddenCommand amendrecord- , normalCommand markconflicts+ , normalCommand obliterate, hiddenCommand unpull , commandGroup "Direct modification of the repository:" , normalCommand tag , normalCommand setpref- , commandGroup "Querying the repository:"- , normalCommand diffCommand- , normalCommand log, hiddenCommand changes- , normalCommand annotate- , normalCommand dist- , normalCommand test- , normalCommand showCommand, hiddenCommand list, hiddenCommand query- , hiddenCommand transferMode- , commandGroup "Copying patches between repositories with working copy update:"- , normalCommand pull- , normalCommand fetch- , normalCommand obliterate, hiddenCommand unpull- , normalCommand rollback- , normalCommand push+ , commandGroup "Exchanging patches by e-mail:" , normalCommand send , normalCommand apply- , normalCommand clone, hiddenCommand get, hiddenCommand put- , commandGroup "Administrating repositories:"- , normalCommand initialize+ , commandGroup "Other commands:" , normalCommand optimize+ , normalCommand dist+ , normalCommand markconflicts , normalCommand repair, hiddenCommand check , normalCommand convert+ , normalCommand fetch , hiddenCommand gzcrcs- , normalCommand rebase+ , hiddenCommand transferMode ]
src/Darcs/UI/Usage.hs view
@@ -10,6 +10,10 @@ -- @ module Darcs.UI.Usage ( usageInfo ) where++import Prelude ()+import Darcs.Prelude+ import Data.Functor.Compose import System.Console.GetOpt( OptDescr(..), ArgDescr(..) ) import Darcs.UI.Options ( DarcsOptDescr )
src/Darcs/Util/AtExit.hs view
@@ -36,6 +36,8 @@ , withAtexit ) where +import Prelude ()+import Darcs.Prelude import Control.Concurrent.MVar import Control.Exception@@ -44,8 +46,6 @@ ) import System.IO.Unsafe (unsafePerformIO) import System.IO ( hPutStrLn, stderr, hPrint )-import Prelude hiding (catch)- atexitActions :: MVar (Maybe [IO ()]) atexitActions = unsafePerformIO (newMVar (Just []))
src/Darcs/Util/Bug.hs view
@@ -3,6 +3,9 @@ ( _bug, _bugDoc, _impossible, _fromJust ) where +import Prelude ()+import Darcs.Prelude+ import Data.Maybe(fromMaybe) import Darcs.Util.Printer ( Doc, errorDoc, text, ($$) )
src/Darcs/Util/ByteString.hs view
@@ -1,4 +1,3 @@-{-# OPTIONS_GHC -fno-warn-dodgy-imports #-} -- needed for GHC 7.0/7.2 {-# LANGUAGE BangPatterns, ForeignFunctionInterface, CPP, ScopedTypeVariables #-} -----------------------------------------------------------------------------@@ -30,6 +29,8 @@ gzWriteFilePSs, gzReadStdin, gzWriteHandle,+ FileSegment,+ readSegment, -- gzip handling isGZFile,@@ -60,10 +61,13 @@ decodeString ) where -import Prelude hiding ( catch )+import Prelude ()+import Darcs.Prelude+ import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as BC import qualified Data.ByteString.Internal as BI+import qualified Data.ByteString.Lazy.Char8 as BL8 import Data.ByteString (intercalate) import Data.ByteString.Internal (fromForeignPtr) @@ -71,7 +75,10 @@ #else import Control.Exception ( catch, SomeException ) #endif-import System.IO+import System.IO ( withFile, IOMode(ReadMode)+ , hSeek, SeekMode(SeekFromEnd,AbsoluteSeek)+ , openBinaryFile, hClose, Handle, hGetChar+ , stdin) import System.IO.Unsafe ( unsafePerformIO ) import Foreign.Storable ( peek )@@ -81,7 +88,7 @@ import Data.Bits ( rotateL ) import Data.Char ( ord, isSpace ) import Data.Word ( Word8 )-import Data.Int ( Int32 )+import Data.Int ( Int32, Int64 ) import qualified Data.Text as T ( pack, unpack ) import Data.Text.Encoding ( encodeUtf8, decodeUtf8With ) import Data.Text.Encoding.Error ( lenientDecode )@@ -110,6 +117,7 @@ import System.Mem( performGC ) import System.Posix.Files( fileSize, getSymbolicLinkStatus ) #endif+import qualified Bundled.Posix as Bundled ( getFileStatus, fileSize ) -- ----------------------------------------------------------------------------- -- obsolete debugging code@@ -395,6 +403,29 @@ compressed = BL.fromChunks [allStdin] in B.concat $ decompress compressed++-- | Pointer to a filesystem, possibly with start/end offsets. Supposed to be+-- fed to (uncurry mmapFileByteString) or similar.+type FileSegment = (FilePath, Maybe (Int64, Int))++-- | Read in a FileSegment into a Lazy ByteString. Implemented using mmap.+readSegment :: FileSegment -> IO BL.ByteString+readSegment (f,range) = do+ bs <- tryToRead+ `catch` (\(_::SomeException) -> do+ size <- Bundled.fileSize `fmap` Bundled.getFileStatus f+ if size == 0+ then return BC.empty+ else performGC >> tryToRead)+ return $ BL8.fromChunks [bs]+ where+ tryToRead =+ case range of+ Nothing -> B.readFile f+ Just (off, size) -> withFile f ReadMode $ \h -> do+ hSeek h AbsoluteSeek $ fromIntegral off+ B.hGet h size+{-# INLINE readSegment #-} -- ----------------------------------------------------------------------------- -- mmapFilePS
src/Darcs/Util/CommandLine.hs view
@@ -56,6 +56,9 @@ , addUrlencoded ) where +import Prelude ()+import Darcs.Prelude+ import Control.Arrow ( (***) ) import Data.Char ( ord, intToDigit, toUpper ) import Data.List ( find )
+ src/Darcs/Util/Compat.hs view
@@ -0,0 +1,141 @@+{-# LANGUAGE CPP, ForeignFunctionInterface #-}++module Darcs.Util.Compat+ ( stdoutIsAPipe+ , mkStdoutTemp+ , canonFilename+ , maybeRelink+ , atomicCreate+ , sloppyAtomicCreate+ ) where++import Prelude ()+import Darcs.Prelude++import Darcs.Util.File ( withCurrentDirectory )+#ifdef WIN32+import Data.Bits ( (.&.) )+import System.Random ( randomIO )+import Numeric ( showHex )+#else+#endif++import Control.Monad ( unless )+import Foreign.C.Types ( CInt(..) )+import Foreign.C.String ( CString, withCString+#ifndef WIN32+ , peekCString+#endif+ )++import Foreign.C.Error ( throwErrno, eEXIST, getErrno )+import System.Directory ( getCurrentDirectory )+import System.IO ( hFlush, stdout, stderr, hSetBuffering,+ BufferMode(NoBuffering) )+import System.IO.Error ( mkIOError, alreadyExistsErrorType )+import System.Posix.Files ( stdFileMode )+import System.Posix.IO ( openFd, closeFd, stdOutput, stdError,+ dupTo, defaultFileFlags, exclusive,+ OpenMode(WriteOnly) )+import System.Posix.Types ( Fd(..) )++import Darcs.Util.SignalHandler ( stdoutIsAPipe )++canonFilename :: FilePath -> IO FilePath+canonFilename f@(_:':':_) = return f -- absolute windows paths+canonFilename f@('/':_) = return f+canonFilename ('.':'/':f) = do cd <- getCurrentDirectory+ return $ cd ++ "/" ++ f+canonFilename f = case reverse $ dropWhile (/='/') $ reverse f of+ "" -> fmap (++('/':f)) getCurrentDirectory+ rd -> withCurrentDirectory rd $+ do fd <- getCurrentDirectory+ return $ fd ++ "/" ++ simplefilename+ where+ simplefilename = reverse $ takeWhile (/='/') $ reverse f++#ifdef WIN32+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 }++showHexLen :: (Integral a, Show a)+ => Int+ -> a+ -> String+showHexLen n x = let s = showHex x ""+ in replicate (n - length s) ' ' ++ s+#else+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+ else do str' <- peekCString cstr+ fname <- canonFilename str'+ return (Fd fd, fname)++foreign import ccall unsafe "static stdlib.h mkstemp"+ c_mkstemp :: CString -> IO CInt+#endif++mkStdoutTemp :: String -> IO String+mkStdoutTemp str = do (fd, fn) <- mkstempCore str+ hFlush stdout+ hFlush stderr+ _ <- dupTo fd stdOutput+ _ <- dupTo fd stdError+ hFlush stdout+ hFlush stderr+ hSetBuffering stdout NoBuffering+ hSetBuffering stderr NoBuffering+ return fn++++foreign import ccall unsafe "maybe_relink.h maybe_relink" maybe_relink+ :: CString -> CString -> CInt -> IO CInt++-- Checks whether src and dst are identical. If so, makes dst into a+-- link to src. Returns True if dst is a link to src (either because+-- we linked it or it already was). Safe against changes to src if+-- they are not in place, but not to dst.+maybeRelink :: String -> String -> IO Bool+maybeRelink src dst =+ withCString src $ \csrc ->+ withCString dst $ \cdst ->+ do rc <- maybe_relink csrc cdst 1+ case rc of+ 0 -> return True+ 1 -> return True+ -1 -> throwErrno ("Relinking " ++ dst)+ -2 -> return False+ -3 -> do putStrLn ("Relinking: race condition avoided on file " +++ dst)+ return False+ _ -> fail ("Unexpected situation when relinking " ++ dst)++sloppyAtomicCreate :: FilePath -> IO ()+sloppyAtomicCreate fp+ = do fd <- openFd fp WriteOnly (Just stdFileMode) flags+ closeFd fd+ where flags = defaultFileFlags { exclusive = True }++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+ ("atomicCreate in "++pwd)+ Nothing (Just fp)+ else throwErrno $ "atomicCreate "++fp++" in "++pwd++foreign import ccall unsafe "atomic_create.h atomic_create" c_atomic_create+ :: CString -> IO CInt
src/Darcs/Util/Crypt/SHA1.hs view
@@ -30,6 +30,9 @@ module Darcs.Util.Crypt.SHA1 ( sha1PS, SHA1(..), showAsHex, sha1Xor, zero ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Util.ByteString (unsafeWithInternals) import qualified Data.ByteString as B (ByteString, pack, length, concat) import Data.Binary ( Binary(..) )
src/Darcs/Util/Crypt/SHA256.hs view
@@ -1,5 +1,8 @@ module Darcs.Util.Crypt.SHA256 ( sha256sum ) where +import Prelude ()+import Darcs.Prelude+ import Crypto.Hash.SHA256 ( hash ) import Data.ByteString ( ByteString ) import Data.ByteString.Base16 ( encode )
src/Darcs/Util/DateMatcher.hs view
@@ -33,6 +33,9 @@ , getMatchers ) where +import Prelude ()+import Darcs.Prelude+ import Control.Exception ( catchJust ) import Data.Maybe ( isJust )
src/Darcs/Util/DateTime.hs view
@@ -12,6 +12,9 @@ , formatDateTime, fromClockTime, parseDateTime, startOfTime ) where +import Prelude ()+import Darcs.Prelude+ import qualified Data.Time.Calendar as Calendar ( fromGregorian ) import Data.Time.Clock ( UTCTime(UTCTime), UniversalTime(ModJulianDate)
src/Darcs/Util/Diff.hs view
@@ -3,6 +3,9 @@ , DiffAlgorithm(..) ) where +import Prelude ()+import Darcs.Prelude+ import qualified Darcs.Util.Diff.Myers as M ( getChanges ) import qualified Darcs.Util.Diff.Patience as P ( getChanges ) import qualified Data.ByteString as B ( ByteString )
src/Darcs/Util/Diff/Myers.hs view
@@ -60,6 +60,9 @@ , getSlice ) where +import Prelude ()+import Darcs.Prelude+ import Control.Monad import Data.Int import Control.Monad.ST
src/Darcs/Util/Diff/Patience.hs view
@@ -20,6 +20,9 @@ ( getChanges ) where +import Prelude ()+import Darcs.Prelude+ import Data.List ( sort ) import Data.Array.Unboxed import Data.Array.ST@@ -27,11 +30,7 @@ import qualified Data.Set as S import qualified Data.ByteString as B ( ByteString, elem ) import qualified Data.ByteString.Char8 as BC ( pack )-#ifdef USE_LOCAL_DATA_MAP_STRICT-import qualified Darcs.Data.Map.Strict as M-#else import qualified Data.Map.Strict as M-#endif ( Map, lookup, insertWith, empty, elems ) import qualified Data.Hashable as H ( hash ) import Darcs.Util.Diff.Myers (initP, aLen, PArray, getSlice)@@ -102,7 +101,7 @@ --Given a HunkMap, check collisions and return the line with an updated Map toHunk' :: HunkMap -> B.ByteString -> (Hunk, HunkMap) toHunk' lmap bs | oldbs == Nothing || null oldhunkpair = insert hash bs lmap- | otherwise = (fst $ head $ oldhunkpair, lmap)+ | otherwise = (fst $ head oldhunkpair, lmap) where hash = H.hash bs oldbs = M.lookup hash (getMap lmap) oldhunkpair = filter ((== bs) . snd) $ fromJust oldbs@@ -111,7 +110,7 @@ listToHunk [] hmap = ([], hmap) listToHunk (x:xs) hmap = let (y, hmap') = toHunk' hmap x (ys, hmap'') = listToHunk xs hmap'- in ((y:ys), hmap'')+ in (y:ys, hmap'') --listToHunk :: [B.ByteString] -> HunkMap -> ([Hunk], HunkMap) --listToHunk = listToHunk' []@@ -149,7 +148,7 @@ borings' = map BC.pack ["", "\n", " ", ")", "(", ","] byparagraph :: [Hunk] -> [[Hunk]]-byparagraph = reverse . (map reverse) . byparagraphAcc []+byparagraph = reverse . map reverse . byparagraphAcc [] where byparagraphAcc xs [] = xs byparagraphAcc [] (a:b:c:d) | a == nl && c == nl && b == hnull = case d of@@ -158,7 +157,7 @@ byparagraphAcc [] (a:as) = byparagraphAcc [[a]] as byparagraphAcc (x:xs) (a:b:c:d) | a == nl && c == nl && b == hnull = case d of- [] -> ((c:b:a:x):xs)+ [] -> (c:b:a:x):xs _ -> byparagraphAcc ([]:((c:b:a:x):xs)) d byparagraphAcc (x:xs) (a:as) = byparagraphAcc ((a:x):xs) as nl = -1 -- "\n" hunk@@ -189,27 +188,27 @@ ([a] -> Bool) -> Int -> [a] -> [a] -> [a] -> [(Int,[a],[a])] mkdiff b ny (l:ls) (x:xs) (y:ys) | l == x && l == y = mkdiff b (ny+1) ls xs ys-mkdiff boring ny (l:ls) xs ys =- if rmd == add- then mkdiff boring (ny+length add+1) ls restx resty- else if boring rmd && boring add- then case lcs rmd add of- [] -> prefixPostfixDiff ny rmd add ++- mkdiff boring (ny+length add+1) ls restx resty- ll -> mkdiff (const False) ny ll rmd add ++- mkdiff boring (ny+length add+1) ls restx resty- else prefixPostfixDiff ny rmd add ++- mkdiff boring (ny+length add+1) ls restx resty+mkdiff boring ny (l:ls) xs ys+ | rmd == add = mkdiff boring (ny+length add+1) ls restx resty+ | boring rmd && boring add =+ case lcs rmd add of+ [] -> prefixPostfixDiff ny rmd add +++ mkdiff boring (ny+length add+1) ls restx resty+ ll -> mkdiff (const False) ny ll rmd add +++ mkdiff boring (ny+length add+1) ls restx resty+ | otherwise = prefixPostfixDiff ny rmd add +++ mkdiff boring (ny+length add+1) ls restx resty where rmd = takeWhile (/= l) xs add = takeWhile (/= l) ys restx = drop (length rmd + 1) xs resty = drop (length add + 1) ys mkdiff _ _ [] [] [] = []-mkdiff boring ny [] rmd add =- if boring rmd && boring add- then case lcs rmd add of [] -> prefixPostfixDiff ny rmd add- ll -> mkdiff (const False) ny ll rmd add- else prefixPostfixDiff ny rmd add+mkdiff boring ny [] rmd add+ | boring rmd && boring add =+ case lcs rmd add of+ [] -> prefixPostfixDiff ny rmd add+ ll -> mkdiff (const False) ny ll rmd add+ | otherwise = prefixPostfixDiff ny rmd add prefixPostfixDiff :: Ord a => Int -> [a] -> [a] -> [(Int,[a],[a])] prefixPostfixDiff _ [] [] = []@@ -302,81 +301,79 @@ lcs (c1:c1s) (c2:c2s) | c1 == c2 = c1: lcs c1s c2s | otherwise =- reverse $ lcs_simple (reverse (c1:c1s)) (reverse (c2:c2s))+ reverse $ lcsSimple (reverse (c1:c1s)) (reverse (c2:c2s)) -lcs_simple :: Ord a => [a] -> [a] -> [a]-lcs_simple [] _ = []-lcs_simple _ [] = []-lcs_simple s1@(c1:c1s) s2@(c2:c2s)+lcsSimple :: Ord a => [a] -> [a] -> [a]+lcsSimple [] _ = []+lcsSimple _ [] = []+lcsSimple s1@(c1:c1s) s2@(c2:c2s) | c1 == c2 = c1: lcs c1s c2s- | otherwise = hunt $ prune_matches s1 $! find_matches s1 s2+ | otherwise = hunt $ pruneMatches s1 $! findMatches s1 s2 -prune_matches :: [a] -> [[Int]] -> [(a, [Int])]-prune_matches _ [] = []-prune_matches [] _ = []-prune_matches (_:cs) ([]:ms) = prune_matches cs ms-prune_matches (c:cs) (m:ms) = (c,m): prune_matches cs ms+pruneMatches :: [a] -> [[Int]] -> [(a, [Int])]+pruneMatches _ [] = []+pruneMatches [] _ = []+pruneMatches (_:cs) ([]:ms) = pruneMatches cs ms+pruneMatches (c:cs) (m:ms) = (c,m): pruneMatches cs ms type Threshold s a = STArray s Int (Int,[a]) hunt :: [(a, [Int])] -> [a] hunt [] = [] hunt csmatches =- runST ( do th <- empty_threshold (length csmatches) l- hunt_internal csmatches th- hunt_recover th (-1) l )+ runST ( do th <- emptyThreshold (length csmatches) l+ huntInternal csmatches th+ huntRecover th (-1) l ) where l = maximum (0 : concat (map snd csmatches)) -hunt_internal :: [(a, [Int])] -> Threshold s a -> ST s ()-hunt_internal [] _ = return ()-hunt_internal ((c,m):csms) th = do- hunt_one_char c m th- hunt_internal csms th+huntInternal :: [(a, [Int])] -> Threshold s a -> ST s ()+huntInternal [] _ = return ()+huntInternal ((c,m):csms) th = do+ huntOneChar c m th+ huntInternal csms th -hunt_one_char :: a -> [Int] -> Threshold s a -> ST s ()-hunt_one_char _ [] _ = return ()-hunt_one_char c (j:js) th = do- index_k <- my_bs j th+huntOneChar :: a -> [Int] -> Threshold s a -> ST s ()+huntOneChar _ [] _ = return ()+huntOneChar c (j:js) th = do+ index_k <- myBs j th case index_k of Nothing -> return () Just k -> do (_, rest) <- readArray th (k-1) writeArray th k (j, c:rest)- hunt_one_char c js th+ huntOneChar c js th -- This is O(n), which is stupid.-hunt_recover :: Threshold s a -> Int -> Int -> ST s [a]-hunt_recover th n limit =+huntRecover :: Threshold s a -> Int -> Int -> ST s [a]+huntRecover th n limit = do (_, th_max) <- getBounds th if n < 0- then hunt_recover th th_max limit- else if n == 0+ then huntRecover th th_max limit+ else if n == 0 || n > th_max then return []- else if n > th_max- then return []- else do (thn, sn) <- readArray th n- if thn <= limit- then return $ reverse sn- else hunt_recover th (n-1) limit+ else do (thn, sn) <- readArray th n+ if thn <= limit+ then return $ reverse sn+ else huntRecover th (n-1) limit -empty_threshold :: Int -> Int -> ST s (Threshold s a)-empty_threshold l th_max = do+emptyThreshold :: Int -> Int -> ST s (Threshold s a)+emptyThreshold l th_max = do th <- newArray (0,l) (th_max+1, []) writeArray th 0 (0, []) return th -my_bs :: Int -> Threshold s a -> ST s (Maybe Int)-my_bs j th = do bnds <- getBounds th- my_helper_bs j bnds th+myBs :: Int -> Threshold s a -> ST s (Maybe Int)+myBs j th = do bnds <- getBounds th+ myHelperBs j bnds th -my_helper_bs :: Int -> (Int,Int) -> Threshold s a ->+myHelperBs :: Int -> (Int,Int) -> Threshold s a -> ST s (Maybe Int)-my_helper_bs j (th_min,th_max) th =+myHelperBs j (th_min,th_max) th = if th_max - th_min > 1 then do (midth, _) <- readArray th th_middle if j > midth- then my_helper_bs j (th_middle,th_max) th- else my_helper_bs j (th_min,th_middle) th+ then myHelperBs j (th_middle,th_max) th+ else myHelperBs j (th_min,th_middle) th else do (minth, _) <- readArray th th_min (maxth, _) <- readArray th th_max@@ -388,32 +385,32 @@ -find_matches :: Ord a => [a] -> [a] -> [[Int]]-find_matches [] [] = []-find_matches [] (_:bs) = []: find_matches [] bs-find_matches _ [] = []-find_matches a b =- unzip_indexed $ sort $ find_sorted_matches indexeda indexedb [] []+findMatches :: Ord a => [a] -> [a] -> [[Int]]+findMatches [] [] = []+findMatches [] (_:bs) = []: findMatches [] bs+findMatches _ [] = []+findMatches a b =+ unzipIndexed $ sort $ findSortedMatches indexeda indexedb [] [] where indexeda = sort $ zip a [1..] indexedb = sort $ zip b [1..] -unzip_indexed :: [(Int,[a])] -> [[a]]-unzip_indexed s = unzip_indexed_helper 1 s- where unzip_indexed_helper _ [] = []- unzip_indexed_helper thisl ((l,c):rest)- | thisl == l = c: unzip_indexed_helper (l+1) rest- | otherwise = []: unzip_indexed_helper (thisl+1) ((l,c):rest)+unzipIndexed :: [(Int,[a])] -> [[a]]+unzipIndexed s = unzipIndexedHelper 1 s+ where unzipIndexedHelper _ [] = []+ unzipIndexedHelper thisl ((l,c):rest)+ | thisl == l = c: unzipIndexedHelper (l+1) rest+ | otherwise = []: unzipIndexedHelper (thisl+1) ((l,c):rest) -find_sorted_matches :: Ord a => [(a, Int)] -> [(a, Int)] -> [a] -> [Int]+findSortedMatches :: Ord a => [(a, Int)] -> [(a, Int)] -> [a] -> [Int] -> [(Int, [Int])]-find_sorted_matches [] _ _ _ = []-find_sorted_matches _ [] _ _ = []-find_sorted_matches ((a,na):as) ((b,nb):bs) aold aoldmatches+findSortedMatches [] _ _ _ = []+findSortedMatches _ [] _ _ = []+findSortedMatches ((a,na):as) ((b,nb):bs) aold aoldmatches | [a] == aold = (na, aoldmatches) :- find_sorted_matches as ((b,nb):bs) aold aoldmatches- | a > b = find_sorted_matches ((a,na):as) bs aold aoldmatches- | a < b = find_sorted_matches as ((b,nb):bs) aold aoldmatches+ findSortedMatches as ((b,nb):bs) aold aoldmatches+ | a > b = findSortedMatches ((a,na):as) bs aold aoldmatches+ | a < b = findSortedMatches as ((b,nb):bs) aold aoldmatches -- following line is inefficient if a line is repeated many times.-find_sorted_matches ((a,na):as) bs _ _ -- a == b- = (na, matches) : find_sorted_matches as bs [a] matches+findSortedMatches ((a,na):as) bs _ _ -- a == b+ = (na, matches) : findSortedMatches as bs [a] matches where matches = reverse $ map snd $ filter ((==a) . fst) bs
src/Darcs/Util/Download.hs view
@@ -22,6 +22,9 @@ , ConnectionError(..) ) where +import Prelude ( (^) )+import Darcs.Prelude+ import Control.Arrow ( (&&&) ) import Control.Concurrent ( forkIO ) import Control.Concurrent.Chan ( isEmptyChan, newChan, readChan, writeChan,
src/Darcs/Util/Download/Curl.hs view
@@ -1,9 +1,11 @@-{-# OPTIONS_GHC -fno-warn-dodgy-imports #-} -- needed for GHC 7.0/7.2 {-# LANGUAGE CPP, ForeignFunctionInterface #-} module Darcs.Util.Download.Curl where #ifdef HAVE_CURL++import Prelude ()+import Darcs.Prelude import Control.Exception ( bracket ) import Control.Monad ( when )
src/Darcs/Util/Download/HTTP.hs view
@@ -4,7 +4,8 @@ ( fetchUrl, postUrl, requestUrl, waitNextUrl ) where -import Prelude hiding ( catch )+import Prelude ()+import Darcs.Prelude import Darcs.Util.Global ( debugFail )
src/Darcs/Util/Download/Request.hs view
@@ -15,6 +15,9 @@ , ConnectionError(..) ) where +import Prelude ()+import Darcs.Prelude+ import Data.List ( delete ) import Data.Map ( Map ) import Foreign.C.Types ( CInt )
src/Darcs/Util/Encoding.hs view
@@ -26,6 +26,9 @@ ( encode,decode ) where +import Prelude ()+import Darcs.Prelude+ import Data.ByteString ( ByteString ) #ifdef WIN32
src/Darcs/Util/Encoding/Win32.hs view
@@ -26,6 +26,9 @@ ( encode, decode ) where +import Prelude ()+import Darcs.Prelude+ import Data.ByteString.Internal ( createAndTrim ) import qualified Data.ByteString as B ( ByteString, useAsCStringLen )
src/Darcs/Util/English.hs view
@@ -32,8 +32,12 @@ module Darcs.Util.English where -import Data.List (isSuffixOf, intercalate)+import Prelude ()+import Darcs.Prelude +import Data.Char (toUpper)+import Data.List (isSuffixOf)+ -- | > englishNum 0 (Noun "watch") "" == "watches" -- > englishNum 1 (Noun "watch") "" == "watch" -- > englishNum 2 (Noun "watch") "" == "watches"@@ -81,17 +85,24 @@ -- -- > orClauses ["foo", "bar", "baz"] == "foo, bar or baz" andClauses, orClauses :: [String] -> String-andClauses = intersperseLast ", " " and "-orClauses = intersperseLast ", " " or "+andClauses = itemize "and"+orClauses = itemize "or" --- | As 'intersperse', with a different separator for the last--- | interspersal.-intersperseLast :: String -> String -> [String] -> String-intersperseLast _ _ [] = ""-intersperseLast _ _ [clause] = clause-intersperseLast sep sepLast clauses =- intercalate sep (init clauses) ++ sepLast ++ last clauses+-- Should not be called with an empty list since this usually+-- prints an extra space. We allow it for compatibility.+itemize :: String -> [String] -> String+itemize _ [] = "" -- error "precondition in Darcs.Util.English.itemize"+itemize _ [x] = x+itemize sep [x,x'] = unwords [x, sep, x']+itemize sep (x:x':xs) = itemize' x x' xs where+ itemize' y y' [] = unwords [y ++ ",", sep, y']+ itemize' y y' (y'':ys) = unwords [y ++ ",", itemize' y' y'' ys] presentParticiple :: String -> String presentParticiple v | last v == 'e' = init v ++ "ing" | otherwise = v ++ "ing"++-- | Capitalize the first letter of a word+capitalize :: String -> String+capitalize [] = []+capitalize (x:xs) = toUpper x : xs
src/Darcs/Util/Environment.hs view
@@ -3,8 +3,8 @@ maybeGetEnv ) where --import Prelude hiding ( catch )+import Prelude ()+import Darcs.Prelude import System.Environment ( getEnv )
src/Darcs/Util/Exception.hs view
@@ -1,18 +1,22 @@-{-# Language MultiParamTypeClasses, DeriveDataTypeable #-}+{-# Language MultiParamTypeClasses #-} module Darcs.Util.Exception ( firstJustIO , catchall , clarifyErrors , prettyException , prettyError+ , die ) where -import Prelude hiding ( catch )+import Prelude ()+import Darcs.Prelude import Control.Exception ( SomeException, Exception(fromException), catch ) import Data.Maybe ( isJust ) +import System.Exit ( exitFailure )+import System.IO ( stderr, hPutStrLn ) import System.IO.Error ( isUserError, ioeGetErrorString , isDoesNotExistError, ioeGetFileName ) @@ -47,7 +51,7 @@ clarifyErrors :: IO a -> String -> IO a-clarifyErrors a e = a `catch` (\x -> fail $ unlines [prettyException x,e])+clarifyErrors a e = a `catch` (\x -> die $ unlines [prettyException x,e]) prettyException :: SomeException -> String@@ -62,3 +66,7 @@ prettyError :: IOError -> String prettyError e | isUserError e = ioeGetErrorString e | otherwise = show e++-- | Terminate the program with an error message.+die :: String -> IO a+die msg = hPutStrLn stderr msg >> exitFailure
src/Darcs/Util/Exec.hs view
@@ -37,6 +37,9 @@ , ExecException(..) ) where +import Prelude ()+import Darcs.Prelude+ #ifndef WIN32 import Control.Exception ( bracket ) import System.Posix.Env ( setEnv, getEnv, unsetEnv )
+ src/Darcs/Util/External.hs view
@@ -0,0 +1,216 @@+{-# LANGUAGE CPP #-}+module Darcs.Util.External+ ( cloneTree+ , cloneFile+ , fetchFilePS+ , fetchFileLazyPS+ , gzFetchFilePS+ , speculateFileOrUrl+ , copyFileOrUrl+ , Cachable(..)+ , backupByRenaming+ , backupByCopying+ ) where++import Control.Exception ( catch, IOException )++import System.Posix.Files+ ( getSymbolicLinkStatus+ , isRegularFile+ , isDirectory+ , createLink+ )+import System.Directory+ ( createDirectory+ , getDirectoryContents+ , doesDirectoryExist+ , doesFileExist+ , renameFile+ , renameDirectory+ , copyFile+ )++import System.FilePath.Posix ( (</>), normalise )+import System.IO.Error ( isDoesNotExistError )+import Control.Monad+ ( unless+ , when+ , zipWithM_+ )++import Darcs.Util.Global ( defaultRemoteDarcsCmd )+import Darcs.Util.Download+ ( copyUrl+ , copyUrlFirst+ , waitUrl+ , Cachable(..)+ )++import Darcs.Util.URL+ ( isValidLocalPath+ , isHttpUrl+ , isSshUrl+ , splitSshUrl+ )+import Darcs.Util.Exception ( catchall )+import Darcs.Util.Lock ( withTemp )+import Darcs.Util.Ssh ( copySSH )++import Darcs.Util.ByteString ( gzReadFilePS )+import qualified Data.ByteString as B (ByteString, readFile )+import qualified Data.ByteString.Lazy as BL++#ifdef HAVE_HTTP+import Network.Browser+ ( browse+ , request+ , setErrHandler+ , setOutHandler+ , setAllowRedirects+ )+import Network.HTTP+ ( RequestMethod(GET)+ , rspCode+ , rspBody+ , rspReason+ , mkRequest+ )+import Network.URI+ ( parseURI+ , uriScheme+ )+#endif+++copyFileOrUrl :: String -- ^ remote darcs executable+ -> FilePath -- ^ path representing the origin file or URL+ -> FilePath -- ^ destination path+ -> Cachable -- ^ tell whether file to copy is cachable+ -> IO ()+copyFileOrUrl _ fou out _ | isValidLocalPath fou = copyLocal fou out+copyFileOrUrl _ fou out cache | isHttpUrl fou = copyRemote fou out cache+copyFileOrUrl rd fou out _ | isSshUrl fou = copySSH rd (splitSshUrl fou) out+copyFileOrUrl _ fou _ _ = fail $ "unknown transport protocol: " ++ fou++copyLocal :: String -> FilePath -> IO ()+copyLocal fou out = createLink fou out `catchall` cloneFile fou out++cloneTree :: FilePath -> FilePath -> IO ()+cloneTree = cloneTreeExcept []++cloneTreeExcept :: [FilePath] -> FilePath -> FilePath -> IO ()+cloneTreeExcept except source dest =+ do fs <- getSymbolicLinkStatus source+ if isDirectory fs then do+ fps <- getDirectoryContents source+ let fps' = filter (`notElem` (".":"..":except)) fps+ mk_source fp = source </> fp+ mk_dest fp = dest </> fp+ zipWithM_ cloneSubTree (map mk_source fps') (map mk_dest fps')+ else fail ("cloneTreeExcept: Bad source " ++ source)+ `catch` \(_ :: IOException) -> fail ("cloneTreeExcept: Bad source " ++ source)++cloneSubTree :: FilePath -> FilePath -> IO ()+cloneSubTree source dest =+ do fs <- getSymbolicLinkStatus source+ if isDirectory fs then do+ createDirectory dest+ fps <- getDirectoryContents source+ let fps' = filter (`notElem` [".", ".."]) fps+ mk_source fp = source </> fp+ mk_dest fp = dest </> fp+ zipWithM_ cloneSubTree (map mk_source fps') (map mk_dest fps')+ else if isRegularFile fs then+ cloneFile source dest+ else fail ("cloneSubTree: Bad source "++ source)+ `catch` (\e -> unless (isDoesNotExistError e) $ ioError e)++cloneFile :: FilePath -> FilePath -> IO ()+cloneFile = copyFile++backupByRenaming :: FilePath -> IO ()+backupByRenaming = backupBy rename+ where rename x y = do+ isD <- doesDirectoryExist x+ if isD then renameDirectory x y else renameFile x y++backupByCopying :: FilePath -> IO ()+backupByCopying = backupBy copy+ where+ copy x y = do+ isD <- doesDirectoryExist x+ if isD then do createDirectory y+ cloneTree (normalise x) (normalise y)+ else copyFile x y++backupBy :: (FilePath -> FilePath -> IO ()) -> FilePath -> IO ()+backupBy backup f =+ do hasBF <- doesFileExist f+ hasBD <- doesDirectoryExist f+ when (hasBF || hasBD) $ helper 0+ where+ helper :: Int -> IO ()+ helper i = do existsF <- doesFileExist next+ existsD <- doesDirectoryExist next+ if existsF || existsD+ then helper (i + 1)+ else do putStrLn $ "Backing up " ++ f ++ "(" ++ suffix ++ ")"+ backup f next+ where next = f ++ suffix+ suffix = ".~" ++ show i ++ "~"++copyAndReadFile :: (FilePath -> IO a) -> String -> Cachable -> IO a+copyAndReadFile readfn fou _ | isValidLocalPath fou = readfn fou+copyAndReadFile readfn fou cache = withTemp $ \t -> do+ copyFileOrUrl defaultRemoteDarcsCmd fou t cache+ readfn t++-- | @fetchFile fileOrUrl cache@ returns the content of its argument (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.+--+-- We always use default remote darcs, since it is not fatal if the remote+-- darcs does not exist or is too old -- anything that supports transfer-mode+-- should do, and if not, we will fall back to SFTP or SCP.+fetchFilePS :: String -> Cachable -> IO B.ByteString+fetchFilePS = copyAndReadFile (B.readFile)++-- | @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. See+-- "fetchFilePS" for details.+fetchFileLazyPS :: String -> Cachable -> IO BL.ByteString+#ifdef HAVE_HTTP+fetchFileLazyPS x c = case parseURI x of+ Just x' | uriScheme x' == "http:" -> do+ rsp <- fmap snd . browse $ do+ setErrHandler . const $ return ()+ setOutHandler . const $ return ()+ setAllowRedirects True+ request $ mkRequest GET x'+ if rspCode rsp /= (2, 0, 0)+ then fail $ "fetchFileLazyPS: " ++ rspReason rsp+ else return $ rspBody rsp+ _ -> copyAndReadFile BL.readFile x c+#else+fetchFileLazyPS = copyAndReadFile BL.readFile+#endif++gzFetchFilePS :: String -> Cachable -> IO B.ByteString+gzFetchFilePS = copyAndReadFile gzReadFilePS++copyRemote :: String -> FilePath -> Cachable -> IO ()+copyRemote u v cache = copyUrlFirst u v cache >> waitUrl u++speculateFileOrUrl :: String -> FilePath -> IO ()+speculateFileOrUrl fou out | isHttpUrl fou = speculateRemote fou out+ | otherwise = return ()++speculateRemote :: String -> FilePath -> IO () -- speculations are always Cachable+#if defined(HAVE_CURL) || defined(HAVE_HTTP)+speculateRemote u v = copyUrl u v Cachable+#else+speculateRemote u _ = return ()+#endif++
src/Darcs/Util/File.hs view
@@ -14,7 +14,8 @@ , getRecursiveContentsFullPath ) where -import Prelude hiding ( catch )+import Prelude ( lookup )+import Darcs.Prelude import Control.Exception ( catch, bracket ) import Control.Monad ( when, unless, forM )
src/Darcs/Util/Global.hs view
@@ -50,18 +50,21 @@ , darcsLastMessage , darcsSendMessage , darcsSendMessageFinal+ , defaultRemoteDarcsCmd , isReachableSource , addReachableSource ) where +import Prelude ()+import Darcs.Prelude+ import Control.Monad ( when ) import Data.IORef ( modifyIORef, IORef, newIORef, readIORef, writeIORef ) import System.IO.Unsafe (unsafePerformIO) import System.IO ( hPutStrLn, hPutStr, stderr ) import System.Time ( calendarTimeToString, toCalendarTime, getClockTime ) import System.FilePath.Posix ( combine, (<.>) )-import Prelude hiding (catch) -- Write-once-read-many global variables make it easier to implement flags, such@@ -173,6 +176,9 @@ darcsdir :: String darcsdir = "_darcs"++defaultRemoteDarcsCmd :: String+defaultRemoteDarcsCmd = "darcs" darcsLastMessage :: String darcsLastMessage = combine darcsdir "patch_description.txt"
+ src/Darcs/Util/Hash.hs view
@@ -0,0 +1,63 @@+-- Copyright (C) 2009-2011 Petr Rockai+--+-- BSD3+{-# LANGUAGE DeriveDataTypeable #-}+module Darcs.Util.Hash( Hash(..)+ , encodeBase16, decodeBase16, sha256, rawHash+ , match ) where++import qualified Crypto.Hash.SHA256 as SHA256 ( hash )+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Char8 as BS8++import qualified Codec.Binary.Base16 as B16++import Data.Maybe( isJust, fromJust )+import Data.Char( toLower, toUpper )++import Data.Data( Data )+import Data.Typeable( Typeable )++data Hash = SHA256 !BS.ByteString+ | SHA1 !BS.ByteString+ | NoHash+ deriving (Show, Eq, Ord, Read, Typeable, Data)++base16 :: BS.ByteString -> BS.ByteString+debase16 :: BS.ByteString -> Maybe BS.ByteString++base16 = BS8.map toLower . B16.b16_enc+debase16 bs = case B16.b16_dec $ BS8.map toUpper bs of+ Right (s, _) -> Just s+ Left _ -> Nothing++-- | Produce a base16 (ascii-hex) encoded string from a hash. This can be+-- turned back into a Hash (see "decodeBase16". This is a loss-less process.+encodeBase16 :: Hash -> BS.ByteString+encodeBase16 (SHA256 bs) = base16 bs+encodeBase16 (SHA1 bs) = base16 bs+encodeBase16 NoHash = BS.empty++-- | Take a base16-encoded string and decode it as a "Hash". If the string is+-- malformed, yields NoHash.+decodeBase16 :: BS.ByteString -> Hash+decodeBase16 bs | BS.length bs == 64 && isJust (debase16 bs) = SHA256 (fromJust $ debase16 bs)+ | BS.length bs == 40 && isJust (debase16 bs) = SHA1 (fromJust $ debase16 bs)+ | otherwise = NoHash++-- | Compute a sha256 of a (lazy) ByteString. However, although this works+-- correctly for any bytestring, it is only efficient if the bytestring only+-- has a sigle chunk.+sha256 :: BL.ByteString -> Hash+sha256 bits = SHA256 $ SHA256.hash $ BS.concat $ BL.toChunks bits++rawHash :: Hash -> BS.ByteString+rawHash NoHash = error "Cannot obtain raw hash from NoHash."+rawHash (SHA1 s) = s+rawHash (SHA256 s) = s++match :: Hash -> Hash -> Bool+NoHash `match` _ = False+_ `match` NoHash = False+x `match` y = x == y
+ src/Darcs/Util/Index.hs view
@@ -0,0 +1,583 @@+-- Copyright (C) 2009-2011 Petr Rockai+-- (C) 2013 Jose Neder+-- BSD3+{-# LANGUAGE CPP, ScopedTypeVariables, MultiParamTypeClasses #-}++-- | This module contains plain tree indexing code. The index itself is a+-- CACHE: you should only ever use it as an optimisation and never as a primary+-- storage. In practice, this means that when we change index format, the+-- application is expected to throw the old index away and build a fresh+-- index. Please note that tracking index validity is out of scope for this+-- library: this is responsibility of your application. It is advisable that in+-- your validity tracking code, you also check for format validity (see+-- 'indexFormatValid') and scrap and re-create index when needed.+--+-- The index is a binary file that overlays a hashed tree over the working+-- copy. This means that every working file and directory has an entry in the+-- index, that contains its path and hash and validity data. The validity data+-- is a timestamp plus the file size. The file hashes are sha256's of the+-- file's content. It also contains the fileid to track moved files.+--+-- There are two entry types, a file entry and a directory entry. Both have a+-- common binary format (see 'Item'). The on-disk format is best described by+-- the section /Index format/ below.+--+-- For each file, the index has a copy of the file's last modification+-- timestamp taken at the instant when the hash has been computed. This means+-- that when file size and timestamp of a file in working copy matches those in+-- the index, we assume that the hash stored in the index for given file is+-- valid. These hashes are then exposed in the resulting 'Tree' object, and can+-- be leveraged by eg. 'diffTrees' to compare many files quickly.+--+-- You may have noticed that we also keep hashes of directories. These are+-- assumed to be valid whenever the complete subtree has been valid. At any+-- point, as soon as a size or timestamp mismatch is found, the working file in+-- question is opened, its hash (and timestamp and size) is recomputed and+-- updated in-place in the index file (everything lives at a fixed offset and+-- is fixed size, so this isn't an issue). This is also true of directories:+-- when a file in a directory changes hash, this triggers recomputation of all+-- of its parent directory hashes; moreover this is done efficiently -- each+-- directory is updated at most once during an update run.+--+-- /Index format/+--+-- The Index is organised into \"lines\" where each line describes a single+-- indexed item. Cf. 'Item'.+--+-- The first word on the index \"line\" is the length of the file path (which is+-- the only variable-length part of the line). Then comes the path itself, then+-- fixed-length hash (sha256) of the file in question, then three words, one for+-- size, one for "aux", which is used differently for directories and for files, and+-- one for the fileid (inode or fhandle) of the file.+--+-- With directories, this aux holds the offset of the next sibling line in the+-- index, so we can efficiently skip reading the whole subtree starting at a+-- given directory (by just seeking aux bytes forward). The lines are+-- pre-ordered with respect to directory structure -- the directory comes first+-- and after it come all its items. Cf. 'readIndex''.+--+-- For files, the aux field holds a timestamp.++module Darcs.Util.Index( readIndex, updateIndexFrom, indexFormatValid+ , updateIndex, listFileIDs, Index, filter+ , getFileID+ -- for testing+ , align+ , xlate32+ , xlate64 )+ where++import Prelude hiding ( lookup, readFile, writeFile, filter, (<$>) )+import Darcs.Util.ByteString ( readSegment )+import Darcs.Util.Tree+import Darcs.Util.Path hiding ( getCurrentDirectory )+import Data.Int( Int64, Int32 )++import Bundled.Posix( getFileStatusBS, modificationTime,+ getFileStatus, fileSize, fileExists, isDirectory )+import System.IO.MMap( mmapFileForeignPtr, mmapFileByteString, Mode(..) )+import System.IO( )+import System.Directory( doesFileExist, getCurrentDirectory, doesDirectoryExist )+#if mingw32_HOST_OS+import System.Directory( renameFile )+import System.FilePath( (<.>) )+#else+import System.Directory( removeFile )+#endif+import System.FilePath( (</>) )+import System.Posix.Types ( FileID )++import Control.Monad( when )+import Control.Exception( catch, SomeException )+import Control.Applicative( (<$>) )++import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as BSC+import Data.ByteString.Unsafe( unsafeHead, unsafeDrop )+import Data.ByteString.Internal( toForeignPtr, fromForeignPtr, memcpy+ , nullForeignPtr, c2w )++import Data.IORef( )+import Data.Maybe( fromJust, isJust, fromMaybe )+import Data.Bits( Bits )+#ifdef BIGENDIAN+import Data.Bits( (.&.), (.|.), shift, shiftL, rotateR )+#endif++import Foreign.Storable+import Foreign.ForeignPtr( ForeignPtr, withForeignPtr, castForeignPtr )+import Foreign.Ptr( Ptr, plusPtr )++import Darcs.Util.Hash( sha256, rawHash )+#ifdef WIN32+import System.Win32.File ( createFile, getFileInformationByHandle, BY_HANDLE_FILE_INFORMATION(..),+ fILE_SHARE_NONE, fILE_FLAG_BACKUP_SEMANTICS,+ gENERIC_NONE, oPEN_EXISTING, closeHandle )+#else+import System.PosixCompat ( fileID, getSymbolicLinkStatus )+#endif++--------------------------+-- Indexed trees+--++-- | Description of a a single indexed item. The structure itself does not+-- contain any data, just pointers to the underlying mmap (bytestring is a+-- pointer + offset + length).+--+-- The structure is recursive-ish (as opposed to flat-ish structure, which is+-- used by git...) It turns out that it's hard to efficiently read a flat index+-- with our internal data structures -- we need to turn the flat index into a+-- recursive Tree object, which is rather expensive... As a bonus, we can also+-- efficiently implement subtree queries this way (cf. 'readIndex').+data Item = Item { iBase :: !(Ptr ())+ , iHashAndDescriptor :: !BS.ByteString+ } deriving Show++size_magic :: Int+size_magic = 4 -- the magic word, first 4 bytes of the index++size_dsclen, size_hash, size_size, size_aux, size_fileid :: Int+size_size = 8 -- file/directory size (Int64)+size_aux = 8 -- aux (Int64)+size_fileid = 8 -- fileid (inode or fhandle FileID)+size_dsclen = 4 -- this many bytes store the length of the path+size_hash = 32 -- hash representation++off_size, off_aux, off_hash, off_dsc, off_dsclen, off_fileid :: Int+off_size = 0+off_aux = off_size + size_size+off_fileid = off_aux + size_aux+off_dsclen = off_fileid + size_fileid+off_hash = off_dsclen + size_dsclen+off_dsc = off_hash + size_hash++itemAllocSize :: AnchoredPath -> Int+itemAllocSize apath =+ align 4 $ size_hash + size_size + size_aux + size_fileid + size_dsclen + 2 + BS.length (flatten apath)++itemSize, itemNext :: Item -> Int+itemSize i = size_size + size_aux + size_fileid + size_dsclen + (BS.length $ iHashAndDescriptor i)+itemNext i = align 4 (itemSize i + 1)++iPath, iHash, iDescriptor :: Item -> BS.ByteString+iDescriptor = unsafeDrop size_hash . iHashAndDescriptor+iPath = unsafeDrop 1 . iDescriptor+iHash = BS.take size_hash . iHashAndDescriptor++iSize, iAux :: Item -> Ptr Int64+iSize i = plusPtr (iBase i) off_size+iAux i = plusPtr (iBase i) off_aux++iFileID :: Item -> Ptr FileID+iFileID i = plusPtr (iBase i) off_fileid++itemIsDir :: Item -> Bool+itemIsDir i = unsafeHead (iDescriptor i) == c2w 'D'++-- xlatePeek32 = fmap xlate32 . peek+xlatePeek64 :: (Storable a, Num a, Bits a) => Ptr a -> IO a+xlatePeek64 = fmap xlate64 . peek++-- xlatePoke32 ptr v = poke ptr (xlate32 v)+xlatePoke64 :: (Storable a, Num a, Bits a) => Ptr a -> a -> IO ()+xlatePoke64 ptr v = poke ptr (xlate64 v)++-- | Lay out the basic index item structure in memory. The memory location is+-- given by a ForeignPointer () and an offset. The path and type given are+-- written out, and a corresponding Item is given back. The remaining bits of+-- the item can be filled out using 'update'.+createItem :: ItemType -> AnchoredPath -> ForeignPtr () -> Int -> IO Item+createItem typ apath fp off =+ do let dsc = BS.concat [ BSC.singleton $ if typ == TreeType then 'D' else 'F'+ , flatten apath+ , BS.singleton 0 ]+ (dsc_fp, dsc_start, dsc_len) = toForeignPtr dsc+ withForeignPtr fp $ \p ->+ withForeignPtr dsc_fp $ \dsc_p ->+ do fileid <- fromMaybe 0 <$> getFileID apath+ pokeByteOff p (off + off_fileid) (xlate64 $ fromIntegral fileid :: Int64)+ pokeByteOff p (off + off_dsclen) (xlate32 $ fromIntegral dsc_len :: Int32)+ memcpy (plusPtr p $ off + off_dsc)+ (plusPtr dsc_p dsc_start)+ (fromIntegral dsc_len)+ peekItem fp off++-- | Read the on-disk representation into internal data structure.+--+-- See the module-level section /Index format/ for details on how the index+-- is structured.+peekItem :: ForeignPtr () -> Int -> IO Item+peekItem fp off =+ withForeignPtr fp $ \p -> do+ nl' :: Int32 <- xlate32 `fmap` peekByteOff p (off + off_dsclen)+ when (nl' <= 2) $ fail "Descriptor too short in peekItem!"+ let nl = fromIntegral nl'+ dsc = fromForeignPtr (castForeignPtr fp) (off + off_hash) (size_hash + nl - 1)+ return $! Item { iBase = plusPtr p off+ , iHashAndDescriptor = dsc }++-- | Update an existing item with new hash and optionally mtime (give Nothing+-- when updating directory entries).+updateItem :: Item -> Int64 -> Hash -> IO ()+updateItem item _ NoHash =+ fail $ "Index.update NoHash: " ++ BSC.unpack (iPath item)+updateItem item size hash =+ do xlatePoke64 (iSize item) size+ unsafePokeBS (iHash item) (rawHash hash)++updateFileID :: Item -> FileID -> IO ()+updateFileID item fileid = xlatePoke64 (iFileID item) $ fromIntegral fileid+updateAux :: Item -> Int64 -> IO ()+updateAux item aux = xlatePoke64 (iAux item) $ aux+updateTime :: forall a.(Enum a) => Item -> a -> IO ()+updateTime item mtime = updateAux item (fromIntegral $ fromEnum mtime)++iHash' :: Item -> Hash+iHash' i = SHA256 (iHash i)++-- | Gives a ForeignPtr to mmapped index, which can be used for reading and+-- updates. The req_size parameter, if non-0, expresses the requested size of+-- the index file. mmapIndex will grow the index if it is smaller than this.+mmapIndex :: forall a. FilePath -> Int -> IO (ForeignPtr a, Int)+mmapIndex indexpath req_size = do+ exist <- doesFileExist indexpath+ act_size <- fromIntegral `fmap` if exist then fileSize `fmap` getFileStatus indexpath+ else return 0+ let size = case req_size > 0 of+ True -> req_size+ False | act_size >= size_magic -> act_size - size_magic+ | otherwise -> 0+ case size of+ 0 -> return (castForeignPtr nullForeignPtr, size)+ _ -> do (x, _, _) <- mmapFileForeignPtr indexpath+ ReadWriteEx (Just (0, size + size_magic))+ return (x, size)++data IndexM m = Index { mmap :: (ForeignPtr ())+ , basedir :: FilePath+ , hashtree :: Tree m -> Hash+ , predicate :: AnchoredPath -> TreeItem m -> Bool }+ | EmptyIndex++type Index = IndexM IO++data State = State { dirlength :: !Int+ , path :: !AnchoredPath+ , start :: !Int }++data Result = Result { -- | marks if the item has changed since the last update to the index+ changed :: !Bool+ -- | next is the position of the next item, in bytes.+ , next :: !Int+ -- | treeitem is Nothing in case of the item doesn't exist in the tree+ -- or is filtered by a FilterTree. Or a TreeItem otherwise.+ , treeitem :: !(Maybe (TreeItem IO))+ -- | resitem is the item extracted.+ , resitem :: !Item }++data ResultF = ResultF { -- | nextF is the position of the next item, in bytes.+ nextF :: !Int+ -- | resitemF is the item extracted.+ , resitemF :: !Item+ -- | _fileIDs contains the fileids of the files and folders inside,+ -- in a folder item and its own fileid for file item).+ , _fileIDs :: [((AnchoredPath, ItemType), FileID)] }++readItem :: Index -> State -> IO Result+readItem index state = do+ item <- peekItem (mmap index) (start state)+ res' <- if itemIsDir item+ then readDir index state item+ else readFile index state item+ return res'++readDir :: Index -> State -> Item -> IO Result+readDir index state item =+ do following <- fromIntegral <$> xlatePeek64 (iAux item)+ st <- getFileStatusBS (iPath item)+ let exists = fileExists st && isDirectory st+ fileid <- fromIntegral <$> (xlatePeek64 $ iFileID item)+ fileid' <- fromMaybe fileid <$> (getFileID' $ BSC.unpack $ iPath item)+ when (fileid == 0) $ updateFileID item fileid'+ let name it dirlen = Name $ (BS.drop (dirlen + 1) $ iDescriptor it) -- FIXME MAGIC+ namelength = (BS.length $ iDescriptor item) - (dirlength state)+ myname = name item (dirlength state)+ substate = state { start = start state + itemNext item+ , path = path state `appendPath` myname+ , dirlength = if myname == Name (BSC.singleton '.')+ then dirlength state+ else dirlength state + namelength }++ want = exists && (predicate index) (path substate) (Stub undefined NoHash)+ oldhash = iHash' item++ subs off | off < following = do+ result <- readItem index $ substate { start = off }+ rest <- subs $ next result+ return $! (name (resitem result) $ dirlength substate, result) : rest+ subs coff | coff == following = return []+ | otherwise = fail $ "Offset mismatch at " ++ show coff +++ " (ends at " ++ show following ++ ")"++ inferiors <- if want then subs $ start substate+ else return []++ let we_changed = or [ changed x | (_, x) <- inferiors ] || nullleaf+ nullleaf = null inferiors && oldhash == nullsha+ nullsha = SHA256 (BS.replicate 32 0)+ tree' = makeTree [ (n, fromJust $ treeitem s) | (n, s) <- inferiors, isJust $ treeitem s ]+ treehash = if we_changed then hashtree index tree' else oldhash+ tree = tree' { treeHash = treehash }++ when (exists && we_changed) $ updateItem item 0 treehash+ return $ Result { changed = not exists || we_changed+ , next = following+ , treeitem = if want then Just $ SubTree tree+ else Nothing+ , resitem = item }++readFile :: Index -> State -> Item -> IO Result+readFile index state item =+ do st <- getFileStatusBS (iPath item)+ mtime <- fromIntegral <$> (xlatePeek64 $ iAux item)+ size <- xlatePeek64 $ iSize item+ fileid <- fromIntegral <$> (xlatePeek64 $ iFileID item)+ fileid' <- fromMaybe fileid <$> (getFileID' $ BSC.unpack $ iPath item)+ let mtime' = modificationTime st+ size' = fromIntegral $ fileSize st+ readblob = readSegment (basedir index </> BSC.unpack (iPath item), Nothing)+ exists = fileExists st && not (isDirectory st)+ we_changed = mtime /= mtime' || size /= size'+ hash = iHash' item+ when (exists && we_changed) $+ do hash' <- sha256 `fmap` readblob+ updateItem item size' hash'+ updateTime item mtime'+ when (fileid == 0) $ updateFileID item fileid'+ return $ Result { changed = not exists || we_changed+ , next = start state + itemNext item+ , treeitem = if exists then Just $ File $ Blob readblob hash else Nothing+ , resitem = item }++updateIndex :: Index -> IO (Tree IO)+updateIndex EmptyIndex = return emptyTree+updateIndex index =+ do let initial = State { start = size_magic+ , dirlength = 0+ , path = AnchoredPath [] }+ res <- readItem index initial+ case treeitem res of+ Just (SubTree tree) -> return $ filter (predicate index) tree+ _ -> fail "Unexpected failure in updateIndex!"++-- | Return a list containing all the file/folder names in an index, with+-- their respective ItemType and FileID.+listFileIDs :: Index -> IO ([((AnchoredPath, ItemType), FileID)])+listFileIDs EmptyIndex = return []+listFileIDs index =+ do let initial = State { start = size_magic+ , dirlength = 0+ , path = AnchoredPath [] }+ res <- readItemFileIDs index initial+ return $ _fileIDs res++readItemFileIDs :: Index -> State -> IO ResultF+readItemFileIDs index state = do+ item <- peekItem (mmap index) (start state)+ res' <- if itemIsDir item+ then readDirFileIDs index state item+ else readFileFileID index state item+ return res'++readDirFileIDs :: Index -> State -> Item -> IO ResultF+readDirFileIDs index state item =+ do fileid <- fromIntegral <$> (xlatePeek64 $ iFileID item)+ following <- fromIntegral <$> xlatePeek64 (iAux item)+ let name it dirlen = Name $ (BS.drop (dirlen + 1) $ iDescriptor it) -- FIXME MAGIC+ namelength = (BS.length $ iDescriptor item) - (dirlength state)+ myname = name item (dirlength state)+ substate = state { start = start state + itemNext item+ , path = path state `appendPath` myname+ , dirlength = if myname == Name (BSC.singleton '.')+ then dirlength state+ else dirlength state + namelength }+ subs off | off < following = do+ result <- readItemFileIDs index $ substate { start = off }+ rest <- subs $ nextF result+ return $! (name (resitemF result) $ dirlength substate, result) : rest+ subs coff | coff == following = return []+ | otherwise = fail $ "Offset mismatch at " ++ show coff +++ " (ends at " ++ show following ++ ")"+ inferiors <- subs $ start substate+ return $ ResultF { nextF = following+ , resitemF = item+ , _fileIDs = (((path substate, TreeType), fileid):concatMap (_fileIDs . snd) inferiors) }++readFileFileID :: Index -> State -> Item -> IO ResultF+readFileFileID _ state item =+ do fileid' <- fromIntegral <$> (xlatePeek64 $ iFileID item)+ let name it dirlen = Name $ (BS.drop (dirlen + 1) $ iDescriptor it)+ myname = name item (dirlength state)+ return $ ResultF { nextF = start state + itemNext item+ , resitemF = item+ , _fileIDs = [((path state `appendPath` myname, BlobType), fileid')] }+++-- | Read an index and build up a 'Tree' object from it, referring to current+-- working directory. The initial Index object returned by readIndex is not+-- directly useful. However, you can use 'Tree.filter' on it. Either way, to+-- obtain the actual Tree object, call update.+--+-- The usual use pattern is this:+--+-- > do (idx, update) <- readIndex+-- > tree <- update =<< filter predicate idx+--+-- The resulting tree will be fully expanded.+readIndex :: FilePath -> (Tree IO -> Hash) -> IO Index+readIndex indexpath ht = do+ (mmap_ptr, mmap_size) <- mmapIndex indexpath 0+ base <- getCurrentDirectory+ return $ if mmap_size == 0 then EmptyIndex+ else Index { mmap = mmap_ptr+ , basedir = base+ , hashtree = ht+ , predicate = \_ _ -> True }++formatIndex :: ForeignPtr () -> Tree IO -> Tree IO -> IO ()+formatIndex mmap_ptr old reference =+ do _ <- create (SubTree reference) (AnchoredPath []) size_magic+ unsafePokeBS magic (BSC.pack "HSI5")+ where magic = fromForeignPtr (castForeignPtr mmap_ptr) 0 4+ create (File _) path' off =+ do i <- createItem BlobType path' mmap_ptr off+ let flatpath = BSC.unpack $ flatten path'+ case find old path' of+ Nothing -> return ()+ -- TODO calling getFileStatus here is both slightly+ -- inefficient and slightly race-prone+ Just ti -> do st <- getFileStatus flatpath+ let hash = itemHash ti+ mtime = modificationTime st+ size = fileSize st+ updateItem i (fromIntegral size) hash+ updateTime i mtime+ return $ off + itemNext i+ create (SubTree s) path' off =+ do i <- createItem TreeType path' mmap_ptr off+ case find old path' of+ Nothing -> return ()+ Just ti | itemHash ti == NoHash -> return ()+ | otherwise -> updateItem i 0 $ itemHash ti+ let subs [] = return $ off + itemNext i+ subs ((name,x):xs) = do+ let path'' = path' `appendPath` name+ noff <- subs xs+ create x path'' noff+ lastOff <- subs (listImmediate s)+ xlatePoke64 (iAux i) (fromIntegral lastOff)+ return lastOff+ create (Stub _ _) path' _ =+ fail $ "Cannot create index from stubbed Tree at " ++ show path'++-- | Will add and remove files in index to make it match the 'Tree' object+-- given (it is an error for the 'Tree' to contain a file or directory that+-- does not exist in a plain form in current working directory).+updateIndexFrom :: FilePath -> (Tree IO -> Hash) -> Tree IO -> IO Index+updateIndexFrom indexpath hashtree' ref =+ do old_idx <- updateIndex =<< readIndex indexpath hashtree'+ reference <- expand ref+ let len_root = itemAllocSize anchoredRoot+ len = len_root + sum [ itemAllocSize p | (p, _) <- list reference ]+ exist <- doesFileExist indexpath+#if mingw32_HOST_OS+ when exist $ renameFile indexpath (indexpath <.> "old")+#else+ when exist $ removeFile indexpath -- to avoid clobbering oldidx+#endif+ (mmap_ptr, _) <- mmapIndex indexpath len+ formatIndex mmap_ptr old_idx reference+ readIndex indexpath hashtree'++-- | Check that a given file is an index file with a format we can handle. You+-- should remove and re-create the index whenever this is not true.+indexFormatValid :: FilePath -> IO Bool+indexFormatValid path' =+ do magic <- mmapFileByteString path' (Just (0, size_magic))+ return $ case BSC.unpack magic of+ "HSI5" -> True+ _ -> False+ `catch` \(_::SomeException) -> return False++instance FilterTree IndexM IO where+ filter _ EmptyIndex = EmptyIndex+ filter p index = index { predicate = \a b -> predicate index a b && p a b }+++-- | For a given file or folder path, get the corresponding fileID from the+-- filesystem.+getFileID :: AnchoredPath -> IO (Maybe FileID)+getFileID = getFileID' . anchorPath ""++getFileID' :: FilePath -> IO (Maybe FileID)+getFileID' fp = do file_exists <- doesFileExist fp+ dir_exists <- doesDirectoryExist fp+ if file_exists || dir_exists+#ifdef WIN32+ then do h <- createFile fp gENERIC_NONE fILE_SHARE_NONE Nothing oPEN_EXISTING fILE_FLAG_BACKUP_SEMANTICS Nothing+ fhnumber <- (Just . fromIntegral . bhfiFileIndex) <$> getFileInformationByHandle h+ closeHandle h+ return fhnumber+#else+ then (Just . fileID) <$> getSymbolicLinkStatus fp+#endif+ else return Nothing+++-- Wow, unsafe.+unsafePokeBS :: BSC.ByteString -> BSC.ByteString -> IO ()+unsafePokeBS to from =+ do let (fp_to, off_to, len_to) = toForeignPtr to+ (fp_from, off_from, len_from) = toForeignPtr from+ when (len_to /= len_from) $ fail $ "Length mismatch in unsafePokeBS: from = "+ ++ show len_from ++ " /= to = " ++ show len_to+ withForeignPtr fp_from $ \p_from ->+ withForeignPtr fp_to $ \p_to ->+ memcpy (plusPtr p_to off_to)+ (plusPtr p_from off_from)+ (fromIntegral len_to)++align :: Integral a => a -> a -> a+align boundary i = case i `rem` boundary of+ 0 -> i+ x -> i + boundary - x+{-# INLINE align #-}++xlate32 :: (Num a, Bits a) => a -> a+xlate64 :: (Num a, Bits a) => a -> a++#ifdef LITTLEENDIAN+xlate32 = id+xlate64 = id+#endif++#ifdef BIGENDIAN+bytemask :: (Num a, Bits a) => a+bytemask = 255++xlate32 a = ((a .&. (bytemask `shift` 0)) `shiftL` 24) .|.+ ((a .&. (bytemask `shift` 8)) `shiftL` 8) .|.+ ((a .&. (bytemask `shift` 16)) `rotateR` 8) .|.+ ((a .&. (bytemask `shift` 24)) `rotateR` 24)++xlate64 a = ((a .&. (bytemask `shift` 0)) `shiftL` 56) .|.+ ((a .&. (bytemask `shift` 8)) `shiftL` 40) .|.+ ((a .&. (bytemask `shift` 16)) `shiftL` 24) .|.+ ((a .&. (bytemask `shift` 24)) `shiftL` 8) .|.+ ((a .&. (bytemask `shift` 32)) `rotateR` 8) .|.+ ((a .&. (bytemask `shift` 40)) `rotateR` 24) .|.+ ((a .&. (bytemask `shift` 48)) `rotateR` 40) .|.+ ((a .&. (bytemask `shift` 56)) `rotateR` 56)+#endif+
src/Darcs/Util/IsoDate.hs view
@@ -36,6 +36,9 @@ , unsetTime, TimeInterval ) where +import Prelude ( (^) )+import Darcs.Prelude+ import Text.ParserCombinators.Parsec import System.Time import System.IO.Unsafe ( unsafePerformIO )
+ src/Darcs/Util/Lock.hs view
@@ -0,0 +1,384 @@+-- Copyright (C) 2003 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.++{-# LANGUAGE CPP #-}++module Darcs.Util.Lock+ ( withLock+ , withLockCanFail+ , environmentHelpLocks+ , withTemp+ , withOpenTemp+ , withStdoutTemp+ , withTempDir+ , withPermDir+ , withDelayedDir+ , withNamedTemp+ , writeToFile+ , appendToFile+ , writeBinFile+ , writeLocaleFile+ , writeDocBinFile+ , appendBinFile+ , appendDocBinFile+ , readBinFile+ , readLocaleFile+ , readDocBinFile+ , writeAtomicFilePS+ , gzWriteAtomicFilePS+ , gzWriteAtomicFilePSs+ , gzWriteDocFile+ , rmRecursive+ , removeFileMayNotExist+ , canonFilename+ , maybeRelink+ , worldReadableTemp+ , tempdirLoc+ , environmentHelpTmpdir+ , environmentHelpKeepTmpdir+ , addToErrorLoc+ ) where++import Data.List ( inits )+import Data.Maybe ( isJust, listToMaybe )+import System.Exit ( exitWith, ExitCode(..) )+import System.IO ( withBinaryFile, openBinaryTempFile,+ hClose, hPutStr, Handle,+ IOMode(WriteMode, AppendMode), hFlush, stdout )+import System.IO.Error+ ( isAlreadyExistsError+ , annotateIOError+ )+import Control.Exception+ ( IOException+ , bracket+ , throwIO+ , catch+ , try+ , SomeException+ )+import System.Directory ( removeFile, removeDirectory,+ doesFileExist, doesDirectoryExist,+ getDirectoryContents, createDirectory,+ getTemporaryDirectory,+ )+import System.FilePath.Posix ( splitDirectories )+import Control.Concurrent ( threadDelay )+import Control.Monad ( unless, when, liftM )++import Darcs.Util.URL ( isRelative )+import Darcs.Util.Environment ( maybeGetEnv )+import Darcs.Util.Exception+ ( firstJustIO+ , catchall+ )+import Darcs.Util.File ( withCurrentDirectory+ , doesDirectoryReallyExist, removeFileMayNotExist )+import Darcs.Util.Path ( AbsolutePath, FilePathLike, toFilePath,+ getCurrentDirectory, setCurrentDirectory )++import Darcs.Util.ByteString ( gzWriteFilePSs, decodeLocale, encodeLocale )+import qualified Data.ByteString as B (null, readFile, hPut, ByteString)+import qualified Data.ByteString.Char8 as BC (unpack)++import Darcs.Util.SignalHandler ( withSignalsBlocked )+import Darcs.Util.Printer ( Doc, hPutDoc, packedString, empty, renderPSs, RenderMode(..) )+import Darcs.Util.AtExit ( atexit )+import Darcs.Util.Global ( darcsdir )+import Darcs.Util.Workaround ( renameFile )+import Darcs.Util.Compat+ ( mkStdoutTemp+ , canonFilename+ , maybeRelink+ , atomicCreate+ , sloppyAtomicCreate+ )+import System.Posix.Files ( fileMode, getFileStatus, setFileMode )+import Darcs.Util.Prompt ( askUser )+#include "impossible.h"++withLock :: String -> IO a -> IO a+withLock s job = bracket (getlock s 30) releaseLock (\_ -> job)++releaseLock :: String -> IO ()+releaseLock = removeFileMayNotExist++-- | Tries to perform some task if it can obtain the lock,+-- Otherwise, just gives up without doing the task+withLockCanFail :: String -> IO a -> IO (Either () a)+withLockCanFail s job =+ bracket (takeLock s)+ (\l -> when l $ releaseLock s)+ (\l -> if l then liftM Right job+ else return $ Left ())++getlock :: String -> Int -> IO String+getlock l 0 = do yorn <- askUser $ "Couldn't get lock "++l++". Abort (yes or anything else)? "+ case yorn of+ ('y':_) -> exitWith $ ExitFailure 1+ _ -> getlock l 30+getlock lbad tl = do l <- canonFilename lbad+ gotit <- takeLock l+ if gotit then return l+ else do putStrLn $ "Waiting for lock "++l+ hFlush stdout -- for Windows+ threadDelay 2000000+ getlock l (tl - 1)+++takeLock :: FilePathLike p => p -> IO Bool+takeLock fp =+ do atomicCreate $ toFilePath fp+ return True+ `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 sloppyAtomicCreate fp+ return True+ `catch` \e -> if isAlreadyExistsError e+ then return False+ else do pwd <- getCurrentDirectory+ throwIO $ addToErrorLoc e+ ("takeFile "++fp++" in "++toFilePath pwd)++environmentHelpLocks :: ([String],[String])+environmentHelpLocks = (["DARCS_SLOPPY_LOCKS"],[+ "If on some filesystems you get an error of the kind:",+ "",+ " darcs: takeLock [...]: atomic_create [...]: unsupported operation",+ "",+ "you may want to try to export DARCS_SLOPPY_LOCKS=True."])++-- |'withTemp' safely creates an empty file (not open for writing) and+-- returns its name.+--+-- The temp file operations are rather similar to the locking operations, in+-- that they both should always try to clean up, so exitWith causes trouble.+withTemp :: (String -> IO a) -> IO a+withTemp = bracket get_empty_file removeFileMayNotExist+ where get_empty_file = do (f,h) <- openBinaryTempFile "." "darcs"+ hClose h+ return f++-- |'withOpenTemp' creates a temporary file, and opens it.+-- Both of them run their argument and then delete the file. Also,+-- both of them (to my knowledge) are not susceptible to race conditions on+-- the temporary file (as long as you never delete the temporary file; that+-- 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) :: 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 (mkStdoutTemp "stdout_") removeFileMayNotExist++tempdirLoc :: IO FilePath+tempdirLoc = liftM fromJust $+ firstJustIO [ liftM (Just . head . words) (readBinFile (darcsdir++"/prefs/tmpdir")) >>= chkdir,+ maybeGetEnv "DARCS_TMPDIR" >>= chkdir,+ getTemporaryDirectory >>= chkdir . Just,+ getCurrentDirectorySansDarcs,+ return $ Just "." -- always returns a Just+ ]+ where chkdir Nothing = return Nothing+ chkdir (Just d) = liftM (\e -> if e then Just (d++"/") else Nothing) $ doesDirectoryExist d++environmentHelpTmpdir :: ([String], [String])+environmentHelpTmpdir = (["DARCS_TMPDIR", "TMPDIR"], [+ "Darcs often creates temporary directories. For example, the `darcs",+ "diff` command creates two for the working trees to be diffed. By",+ "default temporary directories are created in /tmp, or if that doesn't",+ "exist, in _darcs (within the current repo). This can be overridden by",+ "specifying some other directory in the file _darcs/prefs/tmpdir or the",+ "environment variable $DARCS_TMPDIR or $TMPDIR."])++getCurrentDirectorySansDarcs :: IO (Maybe FilePath)+getCurrentDirectorySansDarcs = do+ c <- getCurrentDirectory+ return $ listToMaybe $ drop 5 $ reverse $ takeWhile no_darcs $ inits $ toFilePath c+ where no_darcs x = darcsdir `notElem` splitDirectories x++data WithDirKind = Perm | Temp | Delayed++-- | Creates a directory based on the path parameter;+-- if a relative path is given the dir is created in the darcs temp dir.+-- If an absolute path is given this dir will be created if it doesn't exist.+-- If it is specified as a temporary dir, it is deleted after finishing the job.+withDir :: WithDirKind -- specifies if and when directory will be deleted+ -> String -- path parameter+ -> (AbsolutePath -> IO a) -> IO a+withDir _ "" _ = bug "withDir called with empty directory name"+withDir kind absoluteOrRelativeName job = do+ absoluteName <- if isRelative absoluteOrRelativeName+ then fmap (++ absoluteOrRelativeName) tempdirLoc+ else return absoluteOrRelativeName+ formerdir <- getCurrentDirectory+ bracket (createDir absoluteName 0)+ (\dir -> do setCurrentDirectory formerdir+ k <- keepTempDir+ unless k $ case kind of+ Perm -> return ()+ Temp -> rmRecursive (toFilePath dir)+ Delayed -> atexit $ rmRecursive (toFilePath dir))+ job+ where newname name 0 = name+ newname name n = name ++ "-" ++ show n+ createDir :: FilePath -> Int -> IO AbsolutePath+ createDir name n+ = do createDirectory $ newname name n+ setCurrentDirectory $ newname name n+ getCurrentDirectory+ `catch` (\e -> if isAlreadyExistsError e+ then createDir name (n+1)+ else throwIO e)+ keepTempDir = isJust `fmap` maybeGetEnv "DARCS_KEEP_TMPDIR"++environmentHelpKeepTmpdir :: ([String], [String])+environmentHelpKeepTmpdir = (["DARCS_KEEP_TMPDIR"],[+ "If the environment variable DARCS_KEEP_TMPDIR is defined, darcs will",+ "not remove the temporary directories it creates. This is intended",+ "primarily for debugging Darcs itself, but it can also be useful, for",+ "example, to determine why your test preference (see `darcs setpref`)",+ "is failing when you run `darcs record`, but working when run manually."])++-- |'withPermDir' is like 'withTempDir', except that it doesn't+-- delete the directory afterwards.+withPermDir :: String -> (AbsolutePath -> IO a) -> IO a+withPermDir = withDir Perm++-- |'withTempDir' creates an empty directory and then removes it when it+-- is no longer needed. withTempDir creates a temporary directory. The+-- location of that directory is determined by the contents of+-- _darcs/prefs/tmpdir, if it exists, otherwise by @$DARCS_TMPDIR@, and if+-- that doesn't exist then whatever your operating system considers to be a+-- a temporary directory (e.g. @$TMPDIR@ under Unix, @$TEMP@ under+-- Windows).+--+-- If none of those exist it creates the temporary directory+-- in the current directory, unless the current directory is under a _darcs+-- directory, in which case the temporary directory in the parent of the highest+-- _darcs directory to avoid accidentally corrupting darcs's internals.+-- This should not fail, but if it does indeed fail, we go ahead and use the+-- current directory anyway. If @$DARCS_KEEP_TMPDIR@ variable is set+-- temporary directory is not removed, this can be useful for debugging.+withTempDir :: String -> (AbsolutePath -> IO a) -> IO a+withTempDir = withDir Temp++withDelayedDir :: String -> (AbsolutePath -> IO a) -> IO a+withDelayedDir = withDir Delayed++rmRecursive :: FilePath -> IO ()+rmRecursive d =+ do isd <- doesDirectoryReallyExist d+ if not isd+ then removeFile d+ else do conts <- actual_dir_contents+ withCurrentDirectory d $+ mapM_ rmRecursive conts+ removeDirectory d+ where actual_dir_contents = -- doesn't include . or ..+ do c <- getDirectoryContents d+ return $ filter (/=".") $ filter (/="..") c++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+ in do ok <- takeFile f_new+ if ok then return f_new+ else wrt (n+1)++withNamedTemp :: String -> (String -> IO a) -> IO a+withNamedTemp n = bracket get_empty_file removeFileMayNotExist+ where get_empty_file = worldReadableTemp n++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++appendBinFile :: FilePathLike p => p -> String -> IO ()+appendBinFile f s = appendToFile f $ \h -> hPutStr h s++appendDocBinFile :: FilePathLike p => p -> Doc -> IO ()+appendDocBinFile f d = appendToFile f $ \h -> hPutDoc Standard h d++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 Standard h d++writeAtomicFilePS :: FilePathLike p => p -> B.ByteString -> IO ()+writeAtomicFilePS f ps = writeToFile f $ \h -> B.hPut h ps++gzWriteAtomicFilePS :: FilePathLike p => p -> B.ByteString -> IO ()+gzWriteAtomicFilePS f ps = gzWriteAtomicFilePSs f [ps]++gzWriteAtomicFilePSs :: FilePathLike p => p -> [B.ByteString] -> IO ()+gzWriteAtomicFilePSs f pss =+ withSignalsBlocked $ withNamedTemp (toFilePath f) $ \newf -> do+ gzWriteFilePSs newf pss+ already_exists <- doesFileExist $ toFilePath f+ when already_exists $ do mode <- fileMode `fmap` getFileStatus (toFilePath f)+ setFileMode newf mode+ `catchall` return ()+ renameFile newf (toFilePath f)++gzWriteDocFile :: FilePathLike p => p -> Doc -> IO ()+gzWriteDocFile f d = gzWriteAtomicFilePSs f $ renderPSs Standard d++writeToFile :: FilePathLike p => p -> (Handle -> IO ()) -> IO ()+writeToFile f job =+ withSignalsBlocked $ withNamedTemp (toFilePath f) $ \newf -> do+ withBinaryFile newf WriteMode job+ already_exists <- doesFileExist (toFilePath f)+ when already_exists $ do mode <- fileMode `fmap` getFileStatus (toFilePath f)+ setFileMode newf mode+ `catchall` return ()+ renameFile newf (toFilePath f)++appendToFile :: FilePathLike p => p -> (Handle -> IO ()) -> IO ()+appendToFile f job = withSignalsBlocked $+ withBinaryFile (toFilePath f) AppendMode job+++addToErrorLoc :: IOException+ -> String+ -> IOException+addToErrorLoc ioe s = annotateIOError ioe s Nothing Nothing
src/Darcs/Util/Path.hs view
@@ -23,8 +23,7 @@ {-# LANGUAGE CPP #-} module Darcs.Util.Path- ( module Storage.Hashed.AnchoredPath- , FileName( )+ ( FileName( ) , fp2fn , fn2fp , fn2ps@@ -74,16 +73,28 @@ -- * Tree filtering. , filterFilePaths , filterPaths- ) where+ -- * AnchoredPaths: relative paths within a Tree. All paths are+ -- anchored at a certain root (this is usually the Tree root). They are+ -- represented by a list of Names (these are just strict bytestrings).+ , Name(..)+ , AnchoredPath(..)+ , anchoredRoot+ , appendPath+ , anchorPath+ , isPrefix+ , parent, parents, catPaths, flatten, makeName, appendToName+ -- * Unsafe AnchoredPath functions.+ , floatBS, floatPath, replacePrefixPath ) where -import Storage.Hashed.AnchoredPath+import Prelude ()+import Darcs.Prelude -import Control.Applicative ( (<$>) ) import Data.List ( isPrefixOf , isSuffixOf , stripPrefix , intersect+ , inits ) import Data.Char ( isSpace, chr, ord ) import Control.Exception ( tryJust, bracket_ )@@ -95,19 +106,20 @@ import System.Directory ( doesDirectoryExist, doesFileExist ) import qualified System.FilePath.Posix as FilePath ( normalise ) import qualified System.FilePath as NativeFilePath ( takeFileName, takeDirectory )-import System.FilePath ( splitDirectories )+import System.FilePath( (</>), splitDirectories, normalise, dropTrailingPathSeparator ) import System.Posix.Files ( isDirectory, getSymbolicLinkStatus ) import Darcs.Util.ByteString ( packStringToUTF8, unpackPSFromUTF8 )-import qualified Data.ByteString.Char8 as BC (unpack, pack)+import qualified Data.ByteString.Char8 as BC import qualified Data.ByteString as B (ByteString)-import Data.Binary +import Data.Binary import Darcs.Util.Global ( darcsdir ) import Darcs.Util.URL ( isAbsolute, isRelative, isSshNopath ) #include "impossible.h" + -- | FileName is an abstract type intended to facilitate the input and output of -- unicode filenames. newtype FileName = FN FilePath deriving ( Eq, Ord )@@ -261,10 +273,10 @@ class FilePathOrURL a where- toPath :: a -> String+ toPath :: a -> String class FilePathOrURL a => FilePathLike a where- toFilePath :: a -> FilePath+ toFilePath :: a -> FilePath -- | Paths which are relative to the local darcs repository and normalized. -- Note: These are understood not to have the dot in front.@@ -279,35 +291,35 @@ data AbsoluteOrRemotePath = AbsP AbsolutePath | RmtP String deriving (Eq, Ord) instance FilePathOrURL AbsolutePath where- toPath (AbsolutePath x) = x+ toPath (AbsolutePath x) = x instance FilePathOrURL SubPath where- toPath (SubPath x) = x+ toPath (SubPath x) = x instance CharLike c => FilePathOrURL [c] where- toPath = toFilePath+ toPath = toFilePath instance FilePathOrURL AbsoluteOrRemotePath where- toPath (AbsP a) = toPath a- toPath (RmtP r) = r+ toPath (AbsP a) = toPath a+ toPath (RmtP r) = r instance FilePathOrURL FileName where- toPath = fn2fp+ toPath = fn2fp instance FilePathLike FileName where- toFilePath = fn2fp+ toFilePath = fn2fp instance FilePathLike AbsolutePath where- toFilePath (AbsolutePath x) = x+ toFilePath (AbsolutePath x) = x instance FilePathLike SubPath where- toFilePath (SubPath x) = x+ toFilePath (SubPath x) = x class CharLike c where- toChar :: c -> Char- fromChar :: Char -> c+ toChar :: c -> Char+ fromChar :: Char -> c instance CharLike Char where- toChar = id- fromChar = id+ toChar = id+ fromChar = id instance CharLike c => FilePathLike [c] where- toFilePath = map toChar+ toFilePath = map toChar -- | Make the second path relative to the first, if possible makeSubPathOf :: AbsolutePath -> AbsolutePath -> Maybe SubPath@@ -552,4 +564,101 @@ -- | Transform a SubPath into an AnchoredPath. floatSubPath :: SubPath -> AnchoredPath floatSubPath = floatPath . fn2fp . sp2fn+ +-------------------------------+-- AnchoredPath utilities+-- +newtype Name = Name BC.ByteString deriving (Eq, Show, Ord)++-- | This is a type of "sane" file paths. These are always canonic in the sense+-- that there are no stray slashes, no ".." components and similar. They are+-- usually used to refer to a location within a Tree, but a relative filesystem+-- path works just as well. These are either constructed from individual name+-- components (using "appendPath", "catPaths" and "makeName"), or converted+-- from a FilePath ("floatPath" -- but take care when doing that) or .+newtype AnchoredPath = AnchoredPath [Name] deriving (Eq, Show, Ord)++-- | Check whether a path is a prefix of another path.+isPrefix :: AnchoredPath -> AnchoredPath -> Bool+(AnchoredPath a) `isPrefix` (AnchoredPath b) = a `isPrefixOf` b++-- | Append an element to the end of a path.+appendPath :: AnchoredPath -> Name -> AnchoredPath+appendPath (AnchoredPath p) n =+ case n of+ (Name s) | s == BC.empty -> AnchoredPath p+ | s == BC.pack "." -> AnchoredPath p+ | otherwise -> AnchoredPath $ p ++ [n]++-- | Catenate two paths together. Not very safe, but sometimes useful+-- (e.g. when you are representing paths relative to a different point than a+-- Tree root).+catPaths :: AnchoredPath -> AnchoredPath -> AnchoredPath+catPaths (AnchoredPath p) (AnchoredPath n) = AnchoredPath $ p ++ n++-- | Get parent (path) of a given path. foo/bar/baz -> foo/bar+parent :: AnchoredPath -> AnchoredPath+parent (AnchoredPath x) = AnchoredPath (init x)++-- | List all parents of a given path. foo/bar/baz -> [foo, foo/bar]+parents :: AnchoredPath -> [AnchoredPath]+parents (AnchoredPath x) = map AnchoredPath . init . inits $ x++-- | Take a "root" directory and an anchored path and produce a full+-- 'FilePath'. Moreover, you can use @anchorPath \"\"@ to get a relative+-- 'FilePath'.+anchorPath :: FilePath -> AnchoredPath -> FilePath+anchorPath dir p = dir </> BC.unpack (flatten p)+{-# INLINE anchorPath #-}++-- | Unsafe. Only ever use on bytestrings that came from flatten on a+-- pre-existing AnchoredPath.+floatBS :: BC.ByteString -> AnchoredPath+floatBS = AnchoredPath . map Name . takeWhile (not . BC.null) . BC.split '/'++flatten :: AnchoredPath -> BC.ByteString+flatten (AnchoredPath []) = BC.singleton '.'+flatten (AnchoredPath p) = BC.intercalate (BC.singleton '/')+ [ n | (Name n) <- p ]++makeName :: String -> Name+makeName ".." = error ".. is not a valid AnchoredPath component name"+makeName n | '/' `elem` n = error "/ may not occur in a valid AnchoredPath component name"+ | otherwise = Name $ BC.pack n++-- | Take a relative FilePath and turn it into an AnchoredPath. The operation+-- is (relatively) unsafe. Basically, by using floatPath, you are testifying+-- that the argument is a path relative to some common root -- i.e. the root of+-- the associated "Tree" object. Also, there are certain invariants about+-- AnchoredPath that this function tries hard to preserve, but probably cannot+-- guarantee (i.e. this is a best-effort thing). You should sanitize any+-- FilePaths before you declare them "good" by converting into AnchoredPath+-- (using this function).+floatPath :: FilePath -> AnchoredPath+floatPath = make . splitDirectories . normalise . dropTrailingPathSeparator+ where make ["."] = AnchoredPath []+ make x = AnchoredPath $ map (Name . BC.pack) x+++anchoredRoot :: AnchoredPath+anchoredRoot = AnchoredPath []++-- | Take a prefix path, the changed prefix path, and a path to change.+-- Assumes the prefix path is a valid prefix. If prefix is wrong return+-- AnchoredPath [].+replacePrefixPath :: AnchoredPath -> AnchoredPath -> AnchoredPath -> AnchoredPath+replacePrefixPath (AnchoredPath []) b c = catPaths b c+replacePrefixPath (AnchoredPath (r:p)) b (AnchoredPath (r':p'))+ | r == r' = replacePrefixPath (AnchoredPath p) b (AnchoredPath p')+ | otherwise = AnchoredPath []+replacePrefixPath _ _ _ = AnchoredPath []++-- | Append a ByteString to the last Name of an AnchoredPath.+appendToName :: AnchoredPath -> String -> AnchoredPath+appendToName (AnchoredPath p) s = AnchoredPath (init p++[Name finalname])+ where suffix = BC.pack s+ finalname | suffix `elem` (BC.tails lastname) = lastname+ | otherwise = BC.append lastname suffix+ lastname = case last p of+ Name name -> name
src/Darcs/Util/Printer.hs view
@@ -36,33 +36,61 @@ -- This code was made generic in the element type by Juliusz Chroboczek. module Darcs.Util.Printer (- Printable(..), Doc(Doc,unDoc), Printers, Printers'(..), Printer, Color(..)+ -- * 'Doc' type and structural combinators+ Doc(Doc,unDoc)+ , empty, (<>), (<?>), (<+>), ($$), vcat, vsep, hcat, hsep+ , minus, newline, plus, space, backslash, lparen, rparen+ , parens+ -- * Constructing 'Doc's+ , text+ , hiddenText+ , invisibleText+ , wrapText, quoted+ , userchunk, packedString+ , prefix+ , hiddenPrefix+ , insertBeforeLastline+ , prefixLines+ , invisiblePS, userchunkPS+ -- * Rendering , RenderMode(..)+ , renderString, renderStringWith, renderPS, renderPSWith+ , renderPSs, renderPSsWith+ -- * Printers+ , Printers+ , Printers'(..)+ , Printer+ , simplePrinters, invisiblePrinter, simplePrinter+ -- * Printables+ , Printable(..)+ , doc+ , printable, invisiblePrintable, hiddenPrintable, userchunkPrintable+ -- * Constructing colored 'Doc's+ , Color(..)+ , blueText, redText, greenText, magentaText, cyanText+ , colorText+ , lineColor+ -- * IO , hPutDoc, hPutDocLn, putDoc, putDocLn , hPutDocWith, hPutDocLnWith, putDocWith, putDocLnWith , hPutDocCompr , debugDocLn- , renderString, renderStringWith, renderPS, renderPSWith- , renderPSs, renderPSsWith, lineColor- , prefix, insertBeforeLastline, colorText, invisibleText- , prefixLines- , hiddenText, hiddenPrefix, userchunk, text- , printable, wrapText- , blueText, redText, greenText, magentaText, cyanText- , unsafeText, unsafeBoth, unsafeBothText, unsafeChar- , invisiblePS, packedString, unsafePackedString, userchunkPS- , simplePrinters, invisiblePrinter, simplePrinter- , doc, empty, (<>), (<?>), (<+>), ($$), vcat, vsep, hcat- , minus, newline, plus, space, backslash, lparen, rparen- , parens+ , ePutDocLn , errorDoc+ -- * Unsafe constructors+ , unsafeText, unsafeBoth, unsafeBothText, unsafeChar+ , unsafePackedString ) where +import Prelude ()+import Darcs.Prelude+ import Control.Exception ( throwIO, ErrorCall(..) ) import Data.String ( IsString(..) ) import Data.List (intersperse)+import Data.Monoid ( (<>) ) import GHC.Stack ( currentCallStack )-import System.IO (Handle, stdout, hPutStr)+import System.IO (Handle, stdout, stderr, hPutStr) import System.IO.Unsafe ( unsafePerformIO ) import qualified Data.ByteString as B (ByteString, hPut, concat) import qualified Data.ByteString.Char8 as BC (unpack, pack, singleton)@@ -76,36 +104,47 @@ | PS !B.ByteString | Both !String !B.ByteString --- | 'spaceP' is the 'Printable' representation of a space.+-- | 'Printable' representation of a space spaceP :: Printable spaceP = Both " " (BC.singleton ' ') --- | 'newlineP' is the 'Printable' representation of a newline.+-- | 'Printable' representation of a newline. newlineP :: Printable newlineP = S "\n" --- | Minimal 'Doc's representing the common characters 'space', 'newline'--- 'minus', 'plus', and 'backslash'.-space, newline, plus, minus, backslash :: Doc-space = unsafeBoth " " (BC.singleton ' ')-newline = unsafeChar '\n'-minus = unsafeBoth "-" (BC.singleton '-')-plus = unsafeBoth "+" (BC.singleton '+')+-- | A 'Doc' representing a space (\" \")+space :: Doc+space = unsafeBoth " " (BC.singleton ' ')++-- | A 'Doc' representing a newline+newline :: Doc+newline = unsafeChar '\n'++-- | A 'Doc' representing a \"-\"+minus :: Doc+minus = unsafeBoth "-" (BC.singleton '-')++-- | A 'Doc' representing a \"+\"+plus :: Doc+plus = unsafeBoth "+" (BC.singleton '+')++-- | A 'Doc' representing a \"\\\"+backslash :: Doc backslash = unsafeBoth "\\" (BC.singleton '\\') --- | 'lparen' is the 'Doc' that represents @\"(\"@+-- | A 'Doc' that represents @\"(\"@ lparen :: Doc lparen = unsafeBoth "(" (BC.singleton '(') --- | 'rparen' is the 'Doc' that represents @\")\"@+-- | A 'Doc' that represents @\")\"@ rparen :: Doc rparen = unsafeBoth ")" (BC.singleton ')') --- | @'parens' doc@ returns a 'Doc' with the content of @doc@ put within--- a pair of parenthesis.+-- | prop> parens d = lparen <> d <> rparen parens :: Doc -> Doc parens d = lparen <> d <> rparen +-- | Fail with a stack trace and the given 'Doc' as error message. errorDoc :: Doc -> a errorDoc x = unsafePerformIO $ do stack <- currentCallStack@@ -129,6 +168,14 @@ putDocLn :: Doc -> IO () putDocLn = hPutDocLn Encode stdout +-- | 'eputDocLn' puts a doc, followed by a newline to stderr using+-- 'simplePrinters'. Like putDocLn, it encodes with the user's locale.+-- This function is the recommended way to output messages that should+-- be visible to users on the console, but cannot (or should not) be+-- silenced even when --quiet is in effect.+ePutDocLn :: Doc -> IO ()+ePutDocLn = hPutDocLn Encode stderr+ -- | 'hputDocWith' puts a doc on the given handle using the given printer. hPutDocWith :: Printers -> RenderMode -> Handle -> Doc -> IO () hPutDocWith prs target h d = hPrintPrintables target h (renderWith (prs h) d)@@ -142,7 +189,7 @@ hPutDoc :: RenderMode -> Handle -> Doc -> IO () hPutDoc = hPutDocWith simplePrinters --- 'hputDocLn' puts a doc, followed by a newline on the given handle using+-- | 'hputDocLn' puts a doc, followed by a newline on the given handle using -- 'simplePrinters'. hPutDocLn :: RenderMode -> Handle -> Doc -> IO () hPutDocLn = hPutDocLnWith simplePrinters@@ -155,11 +202,11 @@ debugDocLn :: Doc -> IO () debugDocLn = debugMessage . renderString Standard --- | @'hPrintPrintables' h@ prints a list of 'Printable's to the handle h+-- | @'hPrintPrintables' h@ prints a list of 'Printable's to the handle @h@ hPrintPrintables :: RenderMode -> Handle -> [Printable] -> IO () hPrintPrintables target h = mapM_ (hPrintPrintable target h) --- | @hPrintPrintable h@ prints a 'Printable' to the handle h.+-- | @'hPrintPrintable' h@ prints a 'Printable' to the handle @h@. hPrintPrintable :: RenderMode -> Handle -> Printable -> IO () hPrintPrintable Standard h (S ps) = hPutStr h ps hPrintPrintable Encode h (S ps) = B.hPut h (encodeLocale ps)@@ -168,10 +215,12 @@ hPrintPrintable Standard h (Both _ ps) = B.hPut h ps hPrintPrintable Encode h (Both _ ps) = B.hPut h ps --- | a 'Doc' is a bit of enriched text. 'Doc's get concatanated using--- '<>', which is right-associative.+-- | A 'Doc' is a bit of enriched text. 'Doc's are concatenated using+-- '<>' from class 'Monoid', which is right-associative. newtype Doc = Doc { unDoc :: St -> Document } +-- | Together with the language extension OverloadedStrings, this allows to+-- use string literals where a 'Doc' is expected. instance IsString Doc where fromString = text @@ -330,6 +379,8 @@ invisiblePS = invisiblePrintable . PS -- | 'userchunkPS' creates a 'Doc' representing a user chunk from a 'B.ByteString'.+--+-- Rrrright. And what, please is that supposed to mean? userchunkPS :: B.ByteString -> Doc userchunkPS = userchunkPrintable . PS @@ -357,7 +408,6 @@ userchunk :: String -> Doc userchunk = userchunkPrintable . S --- | 'blueText' creates a 'Doc' containing blue text from a @String@ blueText, redText, greenText, magentaText, cyanText :: String -> Doc blueText = colorText Blue redText = colorText Red@@ -378,14 +428,23 @@ add_to_line (l:ls) new | length l + length new > n = new:l:ls add_to_line (l:ls) new = (l ++ " " ++ new):ls --- | 'printable x' creates a 'Doc' from any 'Printable'.-printable, invisiblePrintable, hiddenPrintable, userchunkPrintable :: Printable -> Doc+-- | Creates a 'Doc' from any 'Printable'.+printable :: Printable -> Doc printable x = Doc $ \st -> defP (printers st) x st mkColorPrintable :: Color -> Printable -> Doc mkColorPrintable c x = Doc $ \st -> colorP (printers st) c x st++-- | Creates an invisible 'Doc' from any 'Printable'.+invisiblePrintable :: Printable -> Doc invisiblePrintable x = Doc $ \st -> invisibleP (printers st) x st++-- | Creates a hidden 'Doc' from any 'Printable'.+hiddenPrintable :: Printable -> Doc hiddenPrintable x = Doc $ \st -> hiddenP (printers st) x st++-- | Creates... WTF is a userchunk???+userchunkPrintable :: Printable -> Doc userchunkPrintable x = Doc $ \st -> userchunkP (printers st) x st -- | 'simplePrinters' is a 'Printers' which uses the set 'simplePriners\'' on any@@ -417,26 +476,25 @@ invisiblePrinter :: Printer invisiblePrinter _ = unDoc empty -infixr 6 <>+infixr 6 `append` infixr 6 <+> infixr 5 $$ --- | The empty 'Doc'.+-- | The empty 'Doc' empty :: Doc empty = Doc $ const Empty+ doc :: ([Printable] -> [Printable]) -> Doc doc f = Doc $ const $ Document f --- | '(<>)' is the concatenation operator for 'Doc's-(<>) :: Doc -> Doc -> Doc--- | @a '<?>' b@ is @a <> b@ if @a@ is not empty, else empty.-(<?>) :: Doc -> Doc -> Doc--- | @a '<+>' b@ is @a@ followed by a space, then @b@.-(<+>) :: Doc -> Doc -> Doc--- | @a '$$' b@ is @a@ above @b@.-($$) :: Doc -> Doc -> Doc--- a then b-Doc a <> Doc b =+-- | 'mappend' ('<>') is concatenation, 'mempty' is the 'empty' 'Doc'+instance Monoid Doc where+ mempty = empty+ mappend = append++-- | Concatenation of two 'Doc's+append :: Doc -> Doc -> Doc+Doc a `append` Doc b = Doc $ \st -> case a st of Empty -> b st Document af ->@@ -444,7 +502,8 @@ Empty -> s Document bf -> bf s) --- empty if a empty, else a then b+-- | @a '<?>' b@ is @a '<>' b@ if @a@ is not empty, else empty+(<?>) :: Doc -> Doc -> Doc Doc a <?> Doc b = Doc $ \st -> case a st of Empty -> Empty@@ -452,7 +511,8 @@ Empty -> s Document bf -> bf s) --- a then space then b+-- | @a '<+>' b@ is @a@ followed by a space, then @b@+(<+>) :: Doc -> Doc -> Doc Doc a <+> Doc b = Doc $ \st -> case a st of Empty -> b st@@ -461,7 +521,8 @@ Document bf -> spaceP:bf s) --- a above b+-- | @a '$$' b@ is @a@ above @b@+($$) :: Doc -> Doc -> Doc Doc a $$ Doc b = Doc $ \st -> case a st of Empty -> b st@@ -472,17 +533,29 @@ where pf = currentPrefix st sf = lineColorS $ printers st --- | 'vcat' piles vertically a list of 'Doc's.+-- | Pile 'Doc's vertically vcat :: [Doc] -> Doc vcat [] = empty vcat ds = foldr1 ($$) ds --- | 'vsep' piles vertically a list of 'Doc's leaving a blank line between each.+-- | Pile 'Doc's vertically, with a blank line in between vsep :: [Doc] -> Doc vsep [] = empty vsep ds = foldr1 ($$) $ intersperse (text "") ds --- | 'hcat' concatenates (horizontally) a list of 'Doc's+-- | Concatenate 'Doc's horizontally hcat :: [Doc] -> Doc-hcat [] = empty-hcat ds = foldr1 (<>) ds+hcat = mconcat++-- | Concatenate 'Doc's horizontally with a space as separator+hsep :: [Doc] -> Doc+hsep = foldr (<+>) empty++-- | Quote a string for screen output+quoted :: String -> Doc+quoted s = text "\"" <> text (escape s) <> text "\""+ where+ escape "" = ""+ escape (c:cs) = if c `elem` ['\\', '"']+ then '\\' : c : escape cs+ else c : escape cs
src/Darcs/Util/Printer/Color.hs view
@@ -4,6 +4,9 @@ , environmentHelpColor, environmentHelpEscape, environmentHelpEscapeWhite ) where +import Prelude ()+import Darcs.Prelude+ import Darcs.Util.Printer ( Printer, Printers, Printers'(..), Printable(..), Color(..), RenderMode(..) , invisiblePrinter, (<>), (<?>), Doc(Doc,unDoc), unsafeBothText, simplePrinter, hcat@@ -11,7 +14,6 @@ , renderStringWith, prefix ) -import Prelude hiding ( catch ) import Control.Monad ( liftM ) import Control.Exception ( catch, IOException ) import Debug.Trace ( trace )
src/Darcs/Util/Progress.hs view
@@ -29,6 +29,9 @@ ) where +import Prelude ()+import Darcs.Prelude hiding ( lookup )+ import Prelude hiding (lookup) import Control.Arrow ( second )
src/Darcs/Util/Prompt.hs view
@@ -10,7 +10,8 @@ ) where -import Prelude hiding ( catch )+import Prelude ()+import Darcs.Prelude import Control.Monad ( void )
src/Darcs/Util/Show.hs view
@@ -2,6 +2,9 @@ ( appPrec, BSWrapper(..) ) where +import Prelude ()+import Darcs.Prelude+ import qualified Data.ByteString as B appPrec :: Int
src/Darcs/Util/SignalHandler.hs view
@@ -23,7 +23,8 @@ tryNonSignal, stdoutIsAPipe ) where -import Prelude hiding ( catch )+import Prelude ()+import Darcs.Prelude import System.IO.Error ( isUserError, ioeGetErrorString, ioeGetFileName ) import System.Exit ( exitWith, ExitCode ( ExitFailure ) )
src/Darcs/Util/Ssh.hs view
@@ -20,19 +20,47 @@ SshSettings(..) , defaultSsh , windows+ , copySSH+ , SSHCmd(..)+ , getSSH+ , environmentHelpSsh+ , environmentHelpScp+ , environmentHelpSshPort+ , transferModeHeader ) where +import Prelude ()+import Darcs.Prelude+import Prelude hiding ( lookup ) -import Control.Applicative ( (<$>), (<*>) )-import Control.Exception ( catch, catchJust, SomeException )+import System.Environment ( getEnv )+import System.Exit ( ExitCode(..) )++import Control.Concurrent.MVar ( MVar, newMVar, withMVar, modifyMVar, modifyMVar_ )+import Control.Exception ( throwIO, catch, catchJust, SomeException )+import Control.Monad ( unless, (>=>) )++import qualified Data.ByteString as B (ByteString, hGet, writeFile )++import Data.Map ( Map, empty, insert, lookup )++import System.IO ( Handle, hSetBinaryMode, hPutStrLn, hGetLine, hFlush )+import System.IO.Unsafe ( unsafePerformIO )+import System.Process ( runInteractiveProcess, readProcessWithExitCode )++import Darcs.Util.SignalHandler ( catchNonSignal )+import Darcs.Util.URL ( SshFilePath, sshFilePathOf, sshUhost, sshRepo, sshFile )+import Darcs.Util.Text ( breakCommand )+import Darcs.Util.Exception ( prettyException, catchall )+import Darcs.Util.Exec ( readInteractiveProcess, ExecException(..), Redirect(AsIs) )+import Darcs.Util.Progress ( withoutProgress, debugMessage, debugFail )++import qualified Darcs.Util.Ratified as Ratified ( hGetContents )+ import Data.IORef ( IORef, newIORef, readIORef ) import Data.List ( isPrefixOf ) import System.Info ( os )-import System.IO.Unsafe (unsafePerformIO) import System.IO.Error ( ioeGetErrorType, isDoesNotExistErrorType )-import System.Process ( readProcessWithExitCode )-import System.Environment ( getEnv )-import Prelude hiding (catch) import Darcs.Util.Global ( whenDebugMode ) @@ -48,7 +76,7 @@ _defaultSsh :: IORef SshSettings _defaultSsh = unsafePerformIO $ newIORef =<< detectSsh-+{-# NOINLINE _defaultSsh #-} -- | Expected properties: --@@ -89,3 +117,212 @@ defaultSsh :: SshSettings defaultSsh = unsafePerformIO $ readIORef _defaultSsh+{-# NOINLINE defaultSsh #-}++-- | A re-usable connection to a remote darcs in transfer-mode.+-- It contains the three standard handles.+data Connection = C+ { inp :: !Handle+ , out :: !Handle+ , err :: !Handle+ }++-- | Identifier (key) for a connection.+type RepoId = (String, String) -- (user@host,repodir)++-- | Global mutable variable that contains open connections,+-- identified by the repoid part of the ssh file name.+-- Only one thread can use a connection at a time, which is why+-- we stuff them behind their own 'MVar's.+--+-- We distinguish between a failed connection (represented by a+-- 'Nothing' entry in the map) and one that was never established+-- (the repoid is not in the map). Once a connection fails,+-- either when trying to establish it or during usage, it will not+-- be tried again.+sshConnections :: MVar (Map RepoId (Maybe (MVar Connection)))+sshConnections = unsafePerformIO $ newMVar empty+{-# NOINLINE sshConnections #-}++-- | Wait for an existing connection to become available or, if none+-- is available, try to create a new one and cache it.+getSshConnection :: String -- ^ remote darcs command+ -> SshFilePath -- ^ destination+ -> IO (Maybe (MVar Connection)) -- ^ wrapper for the action+getSshConnection rdarcs sshfp = modifyMVar sshConnections $ \cmap -> do+ let key = repoid sshfp+ case lookup key cmap of+ Nothing -> do+ -- we have not yet tried with this key, do it now+ mc <- newSshConnection rdarcs sshfp+ case mc of+ Nothing ->+ -- failed, remember it, so we don't try again+ return (insert key Nothing cmap, Nothing)+ Just c -> do+ -- success, remember and use+ v <- newMVar c+ return (insert key (Just v) cmap, Just v)+ Just Nothing ->+ -- we have tried to connect before, don't do it again+ return (cmap, Nothing)+ Just (Just v) ->+ -- we do have a connection, return an action that+ -- waits until it is available+ return (cmap, Just v)++-- | Try to create a new ssh connection to a remote darcs that runs the+-- transfer-mode command. This is tried only once per repoid.+newSshConnection :: String -> SshFilePath -> IO (Maybe Connection)+newSshConnection rdarcs sshfp = do+ (sshcmd,sshargs_) <- getSSH SSH+ debugMessage $ "Starting new ssh connection to " ++ sshUhost sshfp+ let sshargs = sshargs_ ++ [sshUhost sshfp, rdarcs,+ "transfer-mode","--repodir",sshRepo sshfp]+ debugMessage $ unwords (sshcmd : sshargs)+ (i,o,e,_) <- runInteractiveProcess sshcmd sshargs Nothing Nothing+ do+ hSetBinaryMode i True+ hSetBinaryMode o True+ l <- hGetLine o+ unless (l == transferModeHeader) $+ debugFail "Couldn't start darcs transfer-mode on server"+ return $ Just C { inp = i, out = o, err = e }+ `catchNonSignal` \exn -> do+ debugMessage $ "Failed to start ssh connection: " ++ prettyException exn+ debugMessage $ unlines+ [ "NOTE: the server may be running a version of darcs prior to 2.0.0."+ , ""+ , "Installing darcs 2 on the server will speed up ssh-based commands."+ ]+ return Nothing++-- | Mark any connection associated with the given ssh file path+-- as failed, so it won't be tried again.+dropSshConnection :: RepoId -> IO ()+dropSshConnection key = do+ debugMessage $ "Dropping ssh failed connection to " ++ fst key ++ ":" ++ snd key+ modifyMVar_ sshConnections (return . insert key Nothing)++repoid :: SshFilePath -> RepoId+repoid sshfp = (sshUhost sshfp, sshRepo sshfp)++grabSSH :: SshFilePath -> Connection -> IO B.ByteString+grabSSH src c = do+ debugMessage $ "grabSSH src=" ++ sshFilePathOf src+ let failwith e = do dropSshConnection (repoid src)+ -- hGetContents is ok here because we're+ -- only grabbing stderr, and we're also+ -- about to throw the contents.+ eee <- Ratified.hGetContents (err c)+ debugFail $ e ++ " grabbing ssh file " +++ sshFilePathOf src ++"\n" ++ eee+ file = sshFile src+ hPutStrLn (inp c) $ "get " ++ file+ hFlush (inp c)+ l2 <- hGetLine (out c)+ if l2 == "got "++file+ then do showlen <- hGetLine (out c)+ case reads showlen of+ [(len,"")] -> B.hGet (out c) len+ _ -> failwith "Couldn't get length"+ else if l2 == "error "++file+ then do e <- hGetLine (out c)+ case reads e of+ (msg,_):_ -> debugFail $ "Error reading file remotely:\n"++msg+ [] -> failwith "An error occurred"+ else failwith "Error"++copySSH :: String -> SshFilePath -> FilePath -> IO ()+copySSH rdarcs src dest = do+ debugMessage $ "copySSH file: " ++ sshFilePathOf src+ -- TODO why do we disable progress reporting here?+ withoutProgress $ do+ mc <- getSshConnection rdarcs src+ case mc of+ Just v -> withMVar v (grabSSH src >=> B.writeFile dest)+ Nothing -> do+ -- remote 'darcs transfer-mode' does not work => use scp+ let u = escape_dollar $ sshFilePathOf src+ (scpcmd, args) <- getSSH SCP+ let scp_args = filter (/="-q") args ++ [u, dest]+ (r, scp_err) <- readInteractiveProcess scpcmd scp_args+ unless (r == ExitSuccess) $+ throwIO $ ExecException scpcmd scp_args (AsIs,AsIs,AsIs) scp_err+ where+ -- '$' in filenames is troublesome for scp, for some reason.+ escape_dollar :: String -> String+ escape_dollar = concatMap tr+ where+ tr '$' = "\\$"+ tr c = [c]++transferModeHeader :: String+transferModeHeader = "Hello user, I am darcs transfer mode"++-- ---------------------------------------------------------------------+-- older ssh helper functions+-- ---------------------------------------------------------------------++data SSHCmd = SSH+ | SCP+ | SFTP+++fromSshCmd :: SshSettings+ -> SSHCmd+ -> String+fromSshCmd s SSH = ssh s+fromSshCmd s SCP = scp s+fromSshCmd s SFTP = sftp s+++-- | Return the command and arguments needed to run an ssh command+-- First try the appropriate darcs environment variable and SSH_PORT+-- defaulting to "ssh" and no specified port.+getSSH :: SSHCmd+ -> IO (String, [String])+getSSH cmd = do+ port <- (portFlag cmd `fmap` getEnv "SSH_PORT") `catchall` return []+ let (sshcmd, ssh_args) = breakCommand command+ return (sshcmd, ssh_args ++ port)+ where+ command = fromSshCmd defaultSsh cmd+ portFlag SSH x = ["-p", x]+ portFlag SCP x = ["-P", x]+ portFlag SFTP x = ["-oPort=" ++ x]+++environmentHelpSsh :: ([String], [String])+environmentHelpSsh = (["DARCS_SSH"], [+ "Repositories of the form [user@]host:[dir] are taken to be remote",+ "repositories, which Darcs accesses with the external program ssh(1).",+ "",+ "The environment variable $DARCS_SSH can be used to specify an",+ "alternative SSH client. Arguments may be included, separated by",+ "whitespace. The value is not interpreted by a shell, so shell",+ "constructs cannot be used; in particular, it is not possible for the",+ "program name to contain whitespace by using quoting or escaping."])+++environmentHelpScp :: ([String], [String])+environmentHelpScp = (["DARCS_SCP", "DARCS_SFTP"], [+ "When reading from a remote repository, Darcs will attempt to run",+ "`darcs transfer-mode` on the remote host. This will fail if the",+ "remote host only has Darcs 1 installed, doesn't have Darcs installed",+ "at all, or only allows SFTP.",+ "",+ "If transfer-mode fails, Darcs will fall back on scp(1) and sftp(1).",+ "The commands invoked can be customized with the environment variables",+ "$DARCS_SCP and $DARCS_SFTP respectively, which behave like $DARCS_SSH.",+ "If the remote end allows only sftp, try setting DARCS_SCP=sftp."])+++environmentHelpSshPort :: ([String], [String])+environmentHelpSshPort = (["SSH_PORT"], [+ "If this environment variable is set, it will be used as the port",+ "number for all SSH calls made by Darcs (when accessing remote",+ "repositories over SSH). This is useful if your SSH server does not",+ "run on the default port, and your SSH client does not support",+ "ssh_config(5). OpenSSH users will probably prefer to put something",+ "like `Host *.example.net Port 443` into their ~/.ssh/config file."])
src/Darcs/Util/Text.hs view
@@ -11,12 +11,17 @@ , chompTrailingNewline -- * Text processing , breakCommand+ , quote+ , pathlist ) where +import Prelude ()+import Darcs.Prelude+ import Control.Arrow ( first ) import Data.List ( intercalate ) -import Darcs.Util.Printer ( Doc, (<>) )+import Darcs.Util.Printer ( Doc, (<>), renderString, RenderMode(..), quoted, hsep ) sentence :: Doc -> Doc sentence = (<> ".")@@ -48,3 +53,14 @@ chompTrailingNewline :: String -> String chompTrailingNewline "" = "" chompTrailingNewline s = if last s == '\n' then init s else s++-- | Quote a string for screen output.+quote :: String -> String+quote = renderString Encode . quoted++-- | Format a list of 'FilePath's as quoted text. It deliberately refuses to+-- use English.andClauses but rather separates the quoted strings only with a+-- space, because this makes it usable for copy and paste e.g. as arguments to+-- another shell command.+pathlist :: [FilePath] -> Doc+pathlist paths = hsep (map quoted paths)
+ src/Darcs/Util/Tree.hs view
@@ -0,0 +1,470 @@+-- Copyright (C) 2009-2011 Petr Rockai+--+-- BSD3+{-# LANGUAGE ScopedTypeVariables, MultiParamTypeClasses, FlexibleInstances, BangPatterns #-}+{-# LANGUAGE CPP #-}++-- | The abstract representation of a Tree and useful abstract utilities to+-- handle those.+module Darcs.Util.Tree+ ( Tree, Blob(..), TreeItem(..), ItemType(..), Hash(..)+ , makeTree, makeTreeWithHash, emptyTree, emptyBlob, makeBlob, makeBlobBS++ -- * Unfolding stubbed (lazy) Trees.+ --+ -- | By default, Tree obtained by a read function is stubbed: it will+ -- contain Stub items that need to be executed in order to access the+ -- respective subtrees. 'expand' will produce an unstubbed Tree.+ , expandUpdate, expand, expandPath, checkExpand++ -- * Tree access and lookup.+ , items, list, listImmediate, treeHash+ , lookup, find, findFile, findTree, itemHash, itemType+ , zipCommonFiles, zipFiles, zipTrees, diffTrees++ -- * Files (Blobs).+ , readBlob++ -- * Filtering trees.+ , FilterTree(..), restrict++ -- * Manipulating trees.+ , modifyTree, updateTree, partiallyUpdateTree, updateSubtrees, overlay+ , addMissingHashes ) where++import Control.Exception( catch, IOException )+import Prelude hiding( lookup, filter, all, (<$>) )+import Darcs.Util.Path+import Darcs.Util.Hash++import qualified Data.ByteString.Lazy.Char8 as BL+import qualified Data.ByteString.Char8 as BS+import qualified Data.Map as M++import Data.Maybe( catMaybes, isNothing )+import Data.Either( lefts, rights )+import Data.List( union, sort )+import Control.Monad( filterM )+import Control.Applicative( (<$>) )++#include "impossible.h"++--------------------------------+-- Tree, Blob and friends+--++data Blob m = Blob !(m BL.ByteString) !Hash+data TreeItem m = File !(Blob m)+ | SubTree !(Tree m)+ | Stub !(m (Tree m)) !Hash++data ItemType = TreeType | BlobType deriving (Show, Eq, Ord)++-- | Abstraction of a filesystem tree.+-- Please note that the Tree returned by the respective read operations will+-- have TreeStub items in it. To obtain a Tree without such stubs, call+-- expand on it, eg.:+--+-- > tree <- readDarcsPristine "." >>= expand+--+-- When a Tree is expanded, it becomes \"final\". All stubs are forced and the+-- Tree can be traversed purely. Access to actual file contents stays in IO+-- though.+--+-- A Tree may have a Hash associated with it. A pair of Tree's is identical+-- whenever their hashes are (the reverse need not hold, since not all Trees+-- come equipped with a hash).+data Tree m = Tree { items :: M.Map Name (TreeItem m)+ -- | Get hash of a Tree. This is guaranteed to uniquely+ -- identify the Tree (including any blob content), as far as+ -- cryptographic hashes are concerned. Sha256 is recommended.+ , treeHash :: !Hash }++listImmediate :: Tree m -> [(Name, TreeItem m)]+listImmediate = M.toList . items++-- | Get a hash of a TreeItem. May be Nothing.+itemHash :: TreeItem m -> Hash+itemHash (File (Blob _ h)) = h+itemHash (SubTree t) = treeHash t+itemHash (Stub _ h) = h++itemType :: TreeItem m -> ItemType+itemType (File _) = BlobType+itemType (SubTree _) = TreeType+itemType (Stub _ _) = TreeType++emptyTree :: (Monad m) => Tree m+emptyTree = Tree { items = M.empty+ , treeHash = NoHash }++emptyBlob :: (Monad m) => Blob m+emptyBlob = Blob (return BL.empty) NoHash++makeBlob :: (Monad m) => BL.ByteString -> Blob m+makeBlob str = Blob (return str) (sha256 str)++makeBlobBS :: (Monad m) => BS.ByteString -> Blob m+makeBlobBS s' = let s = BL.fromChunks [s'] in Blob (return s) (sha256 s)++makeTree :: (Monad m) => [(Name,TreeItem m)] -> Tree m+makeTree l = Tree { items = M.fromList l+ , treeHash = NoHash }++makeTreeWithHash :: (Monad m) => [(Name,TreeItem m)] -> Hash -> Tree m+makeTreeWithHash l h = Tree { items = M.fromList l+ , treeHash = h }++-----------------------------------+-- Tree access and lookup+--++-- | Look up a 'Tree' item (an immediate subtree or blob).+lookup :: Tree m -> Name -> Maybe (TreeItem m)+lookup t n = M.lookup n (items t)++find' :: TreeItem m -> AnchoredPath -> Maybe (TreeItem m)+find' t (AnchoredPath []) = Just t+find' (SubTree t) (AnchoredPath (d : rest)) =+ case lookup t d of+ Just sub -> find' sub (AnchoredPath rest)+ Nothing -> Nothing+find' _ _ = Nothing++-- | Find a 'TreeItem' by its path. Gives 'Nothing' if the path is invalid.+find :: Tree m -> AnchoredPath -> Maybe (TreeItem m)+find = find' . SubTree++-- | Find a 'Blob' by its path. Gives 'Nothing' if the path is invalid, or does+-- not point to a Blob.+findFile :: Tree m -> AnchoredPath -> Maybe (Blob m)+findFile t p = case find t p of+ Just (File x) -> Just x+ _ -> Nothing++-- | Find a 'Tree' by its path. Gives 'Nothing' if the path is invalid, or does+-- not point to a Tree.+findTree :: Tree m -> AnchoredPath -> Maybe (Tree m)+findTree t p = case find t p of+ Just (SubTree x) -> Just x+ _ -> Nothing++-- | List all contents of a 'Tree'.+list :: Tree m -> [(AnchoredPath, TreeItem m)]+list t_ = paths t_ (AnchoredPath [])+ where paths t p = [ (appendPath p n, i)+ | (n,i) <- listImmediate t ] +++ concat [ paths subt (appendPath p subn)+ | (subn, SubTree subt) <- listImmediate t ]++expandUpdate :: (Monad m) => (AnchoredPath -> Tree m -> m (Tree m)) -> Tree m -> m (Tree m)+expandUpdate update t_ = go (AnchoredPath []) t_+ where go path t = do+ let subtree (name, sub) = do tree <- go (path `appendPath` name) =<< unstub sub+ return (name, SubTree tree)+ expanded <- mapM subtree [ x | x@(_, item) <- listImmediate t, isSub item ]+ let orig_map = M.filter (not . isSub) (items t)+ expanded_map = M.fromList expanded+ tree = t { items = M.union orig_map expanded_map }+ update path tree++-- | Expand a stubbed Tree into a one with no stubs in it. You might want to+-- filter the tree before expanding to save IO. This is the basic+-- implementation, which may be overriden by some Tree instances (this is+-- especially true of the Index case).+expand :: (Monad m) => Tree m -> m (Tree m)+expand = expandUpdate $ const return++-- | Unfold a path in a (stubbed) Tree, such that the leaf node of the path is+-- reachable without crossing any stubs. Moreover, the leaf ought not be a Stub+-- in the resulting Tree. A non-existent path is expanded as far as it can be.+expandPath :: (Monad m) => Tree m -> AnchoredPath -> m (Tree m)+expandPath t (AnchoredPath []) = return t+expandPath t (AnchoredPath (n:rest)) =+ case lookup t n of+ (Just item) | isSub item -> amend t n rest =<< unstub item+ _ -> return t -- fail $ "Descent error in expandPath: " ++ show path_+ where+ amend t' name rest' sub = do+ sub' <- expandPath sub (AnchoredPath rest')+ let tree = t' { items = M.insert name (SubTree sub') (items t') }+ return tree++-- | Check the disk version of a Tree: expands it, and checks each+-- hash. Returns either the expanded tree or a list of AnchoredPaths+-- where there are problems. The first argument is the hashing function+-- used to create the tree.+checkExpand :: (TreeItem IO -> IO Hash) -> Tree IO+ -> IO (Either [(AnchoredPath, Hash, Maybe Hash)] (Tree IO))+checkExpand hashFunc t = go (AnchoredPath []) t+ where+ go path t_ = do+ let+ subtree (name, sub) =+ do let here = path `appendPath` name+ sub' <- (Just <$> unstub sub) `catch` \(_ :: IOException) -> return Nothing+ case sub' of+ Nothing -> return $ Left [(here, treeHash t_, Nothing)]+ Just sub'' -> do+ treeOrTrouble <- go (path `appendPath` name) sub''+ return $ case treeOrTrouble of+ Left problems -> Left problems+ Right tree -> Right (name, SubTree tree)+ badBlob (_, f@(File (Blob _ h))) =+ fmap (/= h) (hashFunc f `catch` (\(_ :: IOException) -> return NoHash))+ badBlob _ = return False+ render (name, f@(File (Blob _ h))) = do+ h' <- (Just <$> hashFunc f) `catch` \(_ :: IOException) -> return Nothing+ return (path `appendPath` name, h, h')+ render (name, _) = return (path `appendPath` name, NoHash, Nothing)+ subs <- mapM subtree [ x | x@(_, item) <- listImmediate t_, isSub item ]+ badBlobs <- filterM badBlob (listImmediate t) >>= mapM render+ let problems = badBlobs ++ concat (lefts subs)+ if null problems+ then do+ let orig_map = M.filter (not . isSub) (items t)+ expanded_map = M.fromList $ rights subs+ tree = t_ {items = orig_map `M.union` expanded_map}+ h' <- hashFunc (SubTree t_)+ if h' `match` treeHash t_+ then return $ Right tree+ else return $ Left [(path, treeHash t_, Just h')]+ else return $ Left problems++class (Monad m) => FilterTree a m where+ -- | Given @pred tree@, produce a 'Tree' that only has items for which+ -- @pred@ returns @True@.+ -- The tree might contain stubs. When expanded, these will be subject to+ -- filtering as well.+ filter :: (AnchoredPath -> TreeItem m -> Bool) -> a m -> a m++instance (Monad m) => FilterTree Tree m where+ filter predicate t_ = filter' t_ (AnchoredPath [])+ where filter' t path = t { items = M.mapMaybeWithKey (wibble path) $ items t }+ wibble path name item =+ let npath = path `appendPath` name in+ if predicate npath item+ then Just $ filterSub npath item+ else Nothing+ filterSub npath (SubTree t) = SubTree $ filter' t npath+ filterSub npath (Stub stub h) =+ Stub (do x <- stub+ return $ filter' x npath) h+ filterSub _ x = x++-- | Given two Trees, a @guide@ and a @tree@, produces a new Tree that is a+-- identical to @tree@, but only has those items that are present in both+-- @tree@ and @guide@. The @guide@ Tree may not contain any stubs.+restrict :: (FilterTree t m, Monad n) => Tree n -> t m -> t m+restrict guide tree = filter accept tree+ where accept path item =+ case (find guide path, item) of+ (Just (SubTree _), SubTree _) -> True+ (Just (SubTree _), Stub _ _) -> True+ (Just (File _), File _) -> True+ (Just (Stub _ _), _) ->+ bug "*sulk* Go away, you, you precondition violator!"+ (_, _) -> False++-- | Read a Blob into a Lazy ByteString. Might be backed by an mmap, use with+-- care.+readBlob :: Blob m -> m BL.ByteString+readBlob (Blob r _) = r++-- | For every pair of corresponding blobs from the two supplied trees,+-- evaluate the supplied function and accumulate the results in a list. Hint:+-- to get IO actions through, just use sequence on the resulting list.+-- NB. This won't expand any stubs.+zipCommonFiles :: (AnchoredPath -> Blob m -> Blob m -> a) -> Tree m -> Tree m -> [a]+zipCommonFiles f a b = catMaybes [ flip (f p) x `fmap` findFile a p+ | (p, File x) <- list b ]++-- | For each file in each of the two supplied trees, evaluate the supplied+-- function (supplying the corresponding file from the other tree, or Nothing)+-- and accumulate the results in a list. Hint: to get IO actions through, just+-- use sequence on the resulting list. NB. This won't expand any stubs.+zipFiles :: (AnchoredPath -> Maybe (Blob m) -> Maybe (Blob m) -> a)+ -> Tree m -> Tree m -> [a]+zipFiles f a b = [ f p (findFile a p) (findFile b p)+ | p <- paths a `sortedUnion` paths b ]+ where paths t = sort [ p | (p, File _) <- list t ]++zipTrees :: (AnchoredPath -> Maybe (TreeItem m) -> Maybe (TreeItem m) -> a)+ -> Tree m -> Tree m -> [a]+zipTrees f a b = [ f p (find a p) (find b p)+ | p <- reverse (paths a `sortedUnion` paths b) ]+ where paths t = sort [ p | (p, _) <- list t ]++-- | Helper function for taking the union of AnchoredPath lists that+-- are already sorted. This function does not check the precondition+-- so use it carefully.+sortedUnion :: [AnchoredPath] -> [AnchoredPath] -> [AnchoredPath]+sortedUnion [] ys = ys+sortedUnion xs [] = xs+sortedUnion a@(x:xs) b@(y:ys) = case compare x y of+ LT -> x : sortedUnion xs b+ EQ -> x : sortedUnion xs ys+ GT -> y : sortedUnion a ys++-- | Cautiously extracts differing subtrees from a pair of Trees. It will never+-- do any unneccessary expanding. Tree hashes are used to cut the comparison as+-- high up the Tree branches as possible. The result is a pair of trees that do+-- not share any identical subtrees. They are derived from the first and second+-- parameters respectively and they are always fully expanded. It might be+-- advantageous to feed the result into 'zipFiles' or 'zipTrees'.+diffTrees :: forall m. (Functor m, Monad m) => Tree m -> Tree m -> m (Tree m, Tree m)+diffTrees left right =+ if treeHash left `match` treeHash right+ then return (emptyTree, emptyTree)+ else diff left right+ where isFile (File _) = True+ isFile _ = False+ notFile = not . isFile+ isEmpty = null . listImmediate+ subtree :: TreeItem m -> m (Tree m)+ subtree (Stub x _) = x+ subtree (SubTree x) = return x+ subtree (File _) = bug "diffTrees tried to descend a File as a subtree"+ maybeUnfold (Stub x _) = SubTree `fmap` (x >>= expand)+ maybeUnfold (SubTree x) = SubTree `fmap` expand x+ maybeUnfold i = return i+ immediateN t = [ n | (n, _) <- listImmediate t ]+ diff left' right' = do+ is <- sequence [+ case (lookup left' n, lookup right' n) of+ (Just l, Nothing) -> do+ l' <- maybeUnfold l+ return (n, Just l', Nothing)+ (Nothing, Just r) -> do+ r' <- maybeUnfold r+ return (n, Nothing, Just r')+ (Just l, Just r)+ | itemHash l `match` itemHash r ->+ return (n, Nothing, Nothing)+ | notFile l && notFile r ->+ do x <- subtree l+ y <- subtree r+ (x', y') <- diffTrees x y+ if isEmpty x' && isEmpty y'+ then return (n, Nothing, Nothing)+ else return (n, Just $ SubTree x', Just $ SubTree y')+ | isFile l && isFile r ->+ return (n, Just l, Just r)+ | otherwise ->+ do l' <- maybeUnfold l+ r' <- maybeUnfold r+ return (n, Just l', Just r')+ _ -> bug "n lookups failed"+ | n <- immediateN left' `union` immediateN right' ]+ let is_l = [ (n, l) | (n, Just l, _) <- is ]+ is_r = [ (n, r) | (n, _, Just r) <- is ]+ return (makeTree is_l, makeTree is_r)++-- | Modify a Tree (by replacing, or removing or adding items).+modifyTree :: (Monad m) => Tree m -> AnchoredPath -> Maybe (TreeItem m) -> Tree m+modifyTree t_ p_ i_ = snd $ go t_ p_ i_+ where fix t unmod items' = (unmod, t { items = (countmap items':: Int) `seq` items'+ , treeHash = if unmod then treeHash t else NoHash })++ go t (AnchoredPath []) (Just (SubTree sub)) = (treeHash t `match` treeHash sub, sub)++ go t (AnchoredPath [n]) (Just item) = fix t unmod items'+ where !items' = M.insert n item (items t)+ !unmod = itemHash item `match` case lookup t n of+ Nothing -> NoHash+ Just i -> itemHash i++ go t (AnchoredPath [n]) Nothing = fix t unmod items'+ where !items' = M.delete n (items t)+ !unmod = isNothing $ lookup t n++ go t path@(AnchoredPath (n:r)) item = fix t unmod items'+ where subtree s = go s (AnchoredPath r) item+ !items' = M.insert n sub (items t)+ !sub = snd sub'+ !unmod = fst sub'+ !sub' = case lookup t n of+ Just (SubTree s) -> let (mod', sub'') = subtree s in (mod', SubTree sub'')+ Just (Stub s _) -> (False, Stub (do x <- s+ return $! snd $! subtree x) NoHash)+ Nothing -> (False, SubTree $! snd $! subtree emptyTree)+ _ -> bug $ "Modify tree at " ++ show path++ go _ (AnchoredPath []) (Just (Stub _ _)) =+ bug $ "descending in modifyTree, case = (Just (Stub _ _)), path = " ++ show p_+ go _ (AnchoredPath []) (Just (File _)) =+ bug $ "descending in modifyTree, case = (Just (File _)), path = " ++ show p_+ go _ (AnchoredPath []) Nothing =+ bug $ "descending in modifyTree, case = Nothing, path = " ++ show p_++countmap :: forall a k. M.Map k a -> Int+countmap = M.fold (\_ i -> i + 1) 0++updateSubtrees :: (Tree m -> Tree m) -> Tree m -> Tree m+updateSubtrees fun t =+ fun $ t { items = M.mapWithKey (curry $ snd . update) $ items t+ , treeHash = NoHash }+ where update (k, SubTree s) = (k, SubTree $ updateSubtrees fun s)+ update (k, File f) = (k, File f)+ update (_, Stub _ _) = bug "Stubs not supported in updateTreePostorder"++-- | Does /not/ expand the tree.+updateTree :: (Functor m, Monad m) => (TreeItem m -> m (TreeItem m)) -> Tree m -> m (Tree m)+updateTree fun t = partiallyUpdateTree fun (\_ _ -> True) t++-- | Does /not/ expand the tree.+partiallyUpdateTree :: (Functor m, Monad m) => (TreeItem m -> m (TreeItem m))+ -> (AnchoredPath -> TreeItem m -> Bool) -> Tree m -> m (Tree m)+partiallyUpdateTree fun predi t' = go (AnchoredPath []) t'+ where go path t = do+ items' <- M.fromList <$> mapM (maybeupdate path) (listImmediate t)+ SubTree t'' <- fun . SubTree $ t { items = items'+ , treeHash = NoHash }+ return t''+ maybeupdate path (k, item) = if predi (path `appendPath` k) item+ then update (path `appendPath` k) (k, item)+ else return (k, item)+ update path (k, SubTree tree) = (\new -> (k, SubTree new)) <$> go path tree+ update _ (k, item) = (\new -> (k, new)) <$> fun item++-- | Lay one tree over another. The resulting Tree will look like the base (1st+-- parameter) Tree, although any items also present in the overlay Tree will be+-- taken from the overlay. It is not allowed to overlay a different kind of an+-- object, nor it is allowed for the overlay to add new objects to base. This+-- means that the overlay Tree should be a subset of the base Tree (although+-- any extraneous items will be ignored by the implementation).+overlay :: (Functor m, Monad m) => Tree m -> Tree m -> Tree m+overlay base over = Tree { items = M.fromList immediate+ , treeHash = NoHash }+ where immediate = [ (n, get n) | (n, _) <- listImmediate base ]+ get n = case (M.lookup n $ items base, M.lookup n $ items over) of+ (Just (File _), Just f@(File _)) -> f+ (Just (SubTree b), Just (SubTree o)) -> SubTree $ overlay b o+ (Just (Stub b _), Just (SubTree o)) -> Stub (flip overlay o `fmap` b) NoHash+ (Just (SubTree b), Just (Stub o _)) -> Stub (overlay b `fmap` o) NoHash+ (Just (Stub b _), Just (Stub o _)) -> Stub (do o' <- o+ b' <- b+ return $ overlay b' o') NoHash+ (Just x, _) -> x+ (_, _) -> bug $ "Unexpected case in overlay at get " ++ show n ++ "."++addMissingHashes :: (Monad m, Functor m) => (TreeItem m -> m Hash) -> Tree m -> m (Tree m)+addMissingHashes make = updateTree update -- use partiallyUpdateTree here+ where update (SubTree t) = make (SubTree t) >>= \x -> return $ SubTree (t { treeHash = x })+ update (File blob@(Blob con NoHash)) =+ do hash <- make $ File blob+ return $ File (Blob con hash)+ update (Stub s NoHash) = update . SubTree =<< s+ update x = return x++------ Private utilities shared among multiple functions. --------++unstub :: (Monad m) => TreeItem m -> m (Tree m)+unstub (Stub s _) = s+unstub (SubTree s) = return s+unstub _ = return emptyTree++isSub :: TreeItem m -> Bool+isSub (File _) = False+isSub _ = True+
+ src/Darcs/Util/Tree/Hashed.hs view
@@ -0,0 +1,245 @@+-- Copyright (C) 2009-2011 Petr Rockai+--+-- BSD3+{-# LANGUAGE ScopedTypeVariables, BangPatterns #-}++-- | A few darcs-specific utility functions. These are used for reading and+-- writing darcs and darcs-compatible hashed trees.+module Darcs.Util.Tree.Hashed+ ( -- * Obtaining Trees.+ --+ -- | Please note that Trees obtained this way will contain Stub+ -- items. These need to be executed (they are IO actions) in order to be+ -- accessed. Use 'expand' to do this. However, many operations are+ -- perfectly fine to be used on a stubbed Tree (and it is often more+ -- efficient to do everything that can be done before expanding a Tree).+ readDarcsHashed+ -- * Writing trees.+ , writeDarcsHashed+ -- * Interact with hashed tree+ , hashedTreeIO+ -- * Other+ , readDarcsHashedDir+ , readDarcsHashedNosize+ , darcsAddMissingHashes+ , darcsLocation+ , darcsTreeHash+ , decodeDarcsHash+ , decodeDarcsSize+ , darcsUpdateHashes+ ) where++import Prelude hiding ( lookup, (<$>) )+import System.FilePath ( (</>) )++import System.Directory( doesFileExist )+import Codec.Compression.GZip( decompress, compress )+import Control.Applicative( (<$>) )++import qualified Data.ByteString.Char8 as BS8+import qualified Data.ByteString.Lazy.Char8 as BL8+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString as BS++import Data.List( sortBy )+import Data.Maybe( fromJust, isJust )+import Control.Monad.State.Strict++import Darcs.Util.Path+import Darcs.Util.ByteString ( FileSegment, readSegment )+import Darcs.Util.Hash+import Darcs.Util.Tree+import Darcs.Util.Tree.Monad++---------------------------------------------------------------------+-- Utilities for coping with the darcs directory format.+--++darcsEncodeWhiteBS :: BS8.ByteString -> BS8.ByteString+darcsEncodeWhiteBS = BS8.pack . encodeWhite . BS8.unpack++decodeDarcsHash :: BS8.ByteString -> Hash+decodeDarcsHash bs = case BS8.split '-' bs of+ [s, h] | BS8.length s == 10 -> decodeBase16 h+ _ -> decodeBase16 bs++decodeDarcsSize :: BS8.ByteString -> Maybe Int+decodeDarcsSize bs = case BS8.split '-' bs of+ [s, _] | BS8.length s == 10 ->+ case reads (BS8.unpack s) of+ [(x, _)] -> Just x+ _ -> Nothing+ _ -> Nothing++darcsLocation :: FilePath -> (Maybe Int, Hash) -> FileSegment+darcsLocation dir (s,h) = case hash of+ "" -> error "darcsLocation: invalid hash"+ _ -> (dir </> prefix s ++ hash, Nothing)+ where prefix Nothing = ""+ prefix (Just s') = formatSize s' ++ "-"+ formatSize s' = let n = show s' in replicate (10 - length n) '0' ++ n+ hash = BS8.unpack (encodeBase16 h)++----------------------------------------------+-- Darcs directory format.+--++darcsFormatDir :: Tree m -> Maybe BL8.ByteString+darcsFormatDir t = BL8.fromChunks . concat <$>+ mapM string (sortBy cmp $ listImmediate t)+ where cmp (Name a, _) (Name b, _) = compare a b+ string (Name name, item) =+ do header <- case item of+ File _ -> Just $ BS8.pack "file:\n"+ _ -> Just $ BS8.pack "directory:\n"+ hash <- case itemHash item of+ NoHash -> Nothing+ x -> Just $ encodeBase16 x+ return [ header+ , darcsEncodeWhiteBS name+ , BS8.singleton '\n'+ , hash, BS8.singleton '\n' ]++darcsParseDir :: BL8.ByteString -> [(ItemType, Name, Maybe Int, Hash)]+darcsParseDir content = parse (BL8.split '\n' content)+ where+ parse (t:n:h':r) = (header t,+ Name $ BS8.pack $ decodeWhite (BL8.unpack n),+ decodeDarcsSize hash,+ decodeDarcsHash hash) : parse r+ where hash = BS8.concat $ BL8.toChunks h'+ parse _ = []+ header x+ | x == BL8.pack "file:" = BlobType+ | x == BL8.pack "directory:" = TreeType+ | otherwise = error $ "Error parsing darcs hashed dir: " ++ BL8.unpack x++----------------------------------------+-- Utilities.+--++-- | Compute a darcs-compatible hash value for a tree-like structure.+darcsTreeHash :: Tree m -> Hash+darcsTreeHash t = case darcsFormatDir t of+ Nothing -> NoHash+ Just x -> sha256 x++-- The following two are mostly for experimental use in Packed.++darcsUpdateDirHashes :: Tree m -> Tree m+darcsUpdateDirHashes = updateSubtrees update+ where update t = t { treeHash = darcsTreeHash t }++darcsUpdateHashes :: (Monad m, Functor m) => Tree m -> m (Tree m)+darcsUpdateHashes = updateTree update+ where update (SubTree t) = return . SubTree $ t { treeHash = darcsTreeHash t }+ update (File blob@(Blob con _)) =+ do hash <- sha256 <$> readBlob blob+ return $ File (Blob con hash)+ update stub = return stub++darcsHash :: (Monad m, Functor m) => TreeItem m -> m Hash+darcsHash (SubTree t) = return $ darcsTreeHash t+darcsHash (File blob) = sha256 <$> readBlob blob+darcsHash _ = return NoHash++darcsAddMissingHashes :: (Monad m, Functor m) => Tree m -> m (Tree m)+darcsAddMissingHashes = addMissingHashes darcsHash++-------------------------------------------+-- Reading darcs pristine data+--++-- | Read and parse a darcs-style hashed directory listing from a given @dir@+-- and with a given @hash@.+readDarcsHashedDir :: FilePath -> (Maybe Int, Hash) -> IO [(ItemType, Name, Maybe Int, Hash)]+readDarcsHashedDir dir h = do+ exist <- doesFileExist $ fst (darcsLocation dir h)+ unless exist $ fail $ "error opening " ++ fst (darcsLocation dir h)+ compressed <- readSegment $ darcsLocation dir h+ let content = decompress compressed+ return $ if BL8.null compressed+ then []+ else darcsParseDir content++-- | Read in a darcs-style hashed tree. This is mainly useful for reading+-- \"pristine.hashed\". You need to provide the root hash you are interested in+-- (found in _darcs/hashed_inventory).+readDarcsHashed' :: Bool -> FilePath -> (Maybe Int, Hash) -> IO (Tree IO)+readDarcsHashed' _ _ (_, NoHash) = fail "Cannot readDarcsHashed NoHash"+readDarcsHashed' sizefail dir root@(_, hash) = do+ items' <- readDarcsHashedDir dir root+ subs <- sequence [+ do when (sizefail && isJust s) $+ fail ("Unexpectedly encountered size-prefixed hash in " ++ dir)+ case tp of+ BlobType -> return (d, File $+ Blob (readBlob' (s, h)) h)+ TreeType ->+ do let t = readDarcsHashed dir (s, h)+ return (d, Stub t h)+ | (tp, d, s, h) <- items' ]+ return $ makeTreeWithHash subs hash+ where readBlob' = fmap decompress . readSegment . darcsLocation dir++readDarcsHashed :: FilePath -> (Maybe Int, Hash) -> IO (Tree IO)+readDarcsHashed = readDarcsHashed' False++readDarcsHashedNosize :: FilePath -> Hash -> IO (Tree IO)+readDarcsHashedNosize dir hash = readDarcsHashed' True dir (Nothing, hash)++----------------------------------------------------+-- Writing darcs-style hashed trees.+--++-- | Write a Tree into a darcs-style hashed directory.+writeDarcsHashed :: Tree IO -> FilePath -> IO Hash+writeDarcsHashed tree' dir =+ do t <- darcsUpdateDirHashes <$> expand tree'+ sequence_ [ dump =<< readBlob b | (_, File b) <- list t ]+ let dirs = darcsFormatDir t : [ darcsFormatDir d | (_, SubTree d) <- list t ]+ _ <- mapM (dump . fromJust) dirs+ return $ darcsTreeHash t+ where dump bits =+ do let name = dir </> BS8.unpack (encodeBase16 $ sha256 bits)+ exist <- doesFileExist name+ unless exist $ BL.writeFile name (compress bits)++-- | Create a hashed file from a 'FilePath' and content. In case the file exists+-- it is kept untouched and is assumed to have the right content. XXX Corrupt+-- files should be probably renamed out of the way automatically or something+-- (probably when they are being read though).+fsCreateHashedFile :: FilePath -> BL8.ByteString -> TreeIO ()+fsCreateHashedFile fn content =+ liftIO $ do+ exist <- doesFileExist fn+ unless exist $ BL.writeFile fn content++-- | Run a 'TreeIO' @action@ in a hashed setting. The @initial@ tree is assumed+-- to be fully available from the @directory@, and any changes will be written+-- out to same. Please note that actual filesystem files are never removed.+hashedTreeIO :: TreeIO a -- ^ action+ -> Tree IO -- ^ initial+ -> FilePath -- ^ directory+ -> IO (a, Tree IO)+hashedTreeIO action t dir =+ runTreeMonad action $ initialState t darcsHash updateItem+ where updateItem _ (File b) = File <$> updateFile b+ updateItem _ (SubTree s) = SubTree <$> updateSub s+ updateItem _ x = return x++ updateFile b@(Blob _ !h) = do+ content <- liftIO $ readBlob b+ let fn = dir </> BS8.unpack (encodeBase16 h)+ nblob = Blob (decompress <$> rblob) h+ rblob = BL.fromChunks . return <$> BS.readFile fn+ newcontent = compress content+ fsCreateHashedFile fn newcontent+ return nblob+ updateSub s = do+ let !hash = treeHash s+ Just dirdata = darcsFormatDir s+ fn = dir </> BS8.unpack (encodeBase16 hash)+ fsCreateHashedFile fn (compress dirdata)+ return s+
+ src/Darcs/Util/Tree/Monad.hs view
@@ -0,0 +1,280 @@+-- Copyright (C) 2009-2011 Petr Rockai+--+-- BSD3+{-# LANGUAGE ScopedTypeVariables, TypeSynonymInstances, UndecidableInstances, FlexibleInstances #-}++-- | An experimental monadic interface to Tree mutation. The main idea is to+-- simulate IO-ish manipulation of real filesystem (that's the state part of+-- the monad), and to keep memory usage down by reasonably often dumping the+-- intermediate data to disk and forgetting it. The monad interface itself is+-- generic, and a number of actual implementations can be used. This module+-- provides just 'virtualTreeIO' that never writes any changes, but may trigger+-- filesystem reads as appropriate.+module Darcs.Util.Tree.Monad+ ( virtualTreeIO, virtualTreeMonad+ , readFile, writeFile, createDirectory, rename, copy, unlink+ , fileExists, directoryExists, exists, withDirectory+ , currentDirectory+ , tree, TreeState, TreeMonad, TreeIO, runTreeMonad+ , initialState, replaceItem+ , findM, findFileM, findTreeM+ , TreeRO, TreeRW+ ) where++import Prelude hiding ( readFile, writeFile, (<$>) )++import Darcs.Util.Path+import Darcs.Util.Tree++import Control.Applicative( (<$>) )++import Data.List( sortBy )+import Data.Int( Int64 )+import Data.Maybe( isNothing, isJust )++import qualified Data.ByteString.Lazy.Char8 as BL+import Control.Monad.RWS.Strict+import qualified Data.Map as M++type Changed = M.Map AnchoredPath (Int64, Int64) -- size, age++-- | Internal state of the 'TreeIO' monad. Keeps track of the current Tree+-- content, unsync'd changes and a current working directory (of the monad).+data TreeState m = TreeState { tree :: !(Tree m)+ , changed :: !Changed+ , changesize :: !Int64+ , maxage :: !Int64+ , updateHash :: TreeItem m -> m Hash+ , update :: AnchoredPath -> TreeItem m -> TreeMonad m (TreeItem m) }++-- | A 'TreeIO' monad. A sort of like IO but it keeps a 'TreeState' around as well,+-- which is a sort of virtual filesystem. Depending on how you obtained your+-- 'TreeIO', the actions in your virtual filesystem get somehow reflected in the+-- actual real filesystem. For 'virtualTreeIO', nothing happens in real+-- filesystem, however with 'plainTreeIO', the plain tree will be updated every+-- now and then, and with 'hashedTreeIO' a darcs-style hashed tree will get+-- updated.+type TreeMonad m = RWST AnchoredPath () (TreeState m) m+type TreeIO = TreeMonad IO++class (Functor m, Monad m) => TreeRO m where+ currentDirectory :: m AnchoredPath+ withDirectory :: AnchoredPath -> m a -> m a+ expandTo :: AnchoredPath -> m AnchoredPath+ -- | Grab content of a file in the current Tree at the given path.+ readFile :: AnchoredPath -> m BL.ByteString+ -- | Check for existence of a node (file or directory, doesn't matter).+ exists :: AnchoredPath -> m Bool+ -- | Check for existence of a directory.+ directoryExists ::AnchoredPath -> m Bool+ -- | Check for existence of a file.+ fileExists :: AnchoredPath -> m Bool++class TreeRO m => TreeRW m where+ -- | Change content of a file at a given path. The change will be+ -- eventually flushed to disk, but might be buffered for some time.+ writeFile :: AnchoredPath -> BL.ByteString -> m ()+ createDirectory :: AnchoredPath -> m ()+ unlink :: AnchoredPath -> m ()+ rename :: AnchoredPath -> AnchoredPath -> m ()+ copy :: AnchoredPath -> AnchoredPath -> m ()++initialState :: Tree m -> (TreeItem m -> m Hash)+ -> (AnchoredPath -> TreeItem m -> TreeMonad m (TreeItem m)) -> TreeState m+initialState t uh u = TreeState { tree = t+ , changed = M.empty+ , changesize = 0+ , updateHash = uh+ , maxage = 0+ , update = u }++flush :: (Functor m, Monad m) => TreeMonad m ()+flush = do changed' <- map fst . M.toList <$> gets changed+ dirs' <- gets tree >>= \t -> return [ path | (path, SubTree _) <- list t ]+ modify $ \st -> st { changed = M.empty, changesize = 0 }+ forM_ (changed' ++ dirs' ++ [AnchoredPath []]) flushItem++runTreeMonad' :: (Functor m, Monad m) => TreeMonad m a -> TreeState m -> m (a, Tree m)+runTreeMonad' action initial = do+ (out, final, _) <- runRWST action (AnchoredPath []) initial+ return (out, tree final)++runTreeMonad :: (Functor m, Monad m) => TreeMonad m a -> TreeState m -> m (a, Tree m)+runTreeMonad action initial = do+ let action' = do x <- action+ flush+ return x+ runTreeMonad' action' initial++-- | Run a TreeIO action without storing any changes. This is useful for+-- running monadic tree mutations for obtaining the resulting Tree (as opposed+-- to their effect of writing a modified tree to disk). The actions can do both+-- read and write -- reads are passed through to the actual filesystem, but the+-- writes are held in memory in a form of modified Tree.+virtualTreeMonad :: (Functor m, Monad m) => TreeMonad m a -> Tree m -> m (a, Tree m)+virtualTreeMonad action t = runTreeMonad' action $+ initialState t (\_ -> return NoHash) (\_ x -> return x)++virtualTreeIO :: TreeIO a -> Tree IO -> IO (a, Tree IO)+virtualTreeIO = virtualTreeMonad++-- | Modifies an item in the current Tree. This action keeps an account of the+-- modified data, in changed and changesize, for subsequent flush+-- operations. Any modifications (as in "modifyTree") are allowed.+modifyItem :: (Functor m, Monad m)+ => AnchoredPath -> Maybe (TreeItem m) -> TreeMonad m ()+modifyItem path item = do+ path' <- (`catPaths` path) `fmap` currentDirectory+ age <- gets maxage+ changed' <- gets changed+ let getsize (Just (File b)) = lift (BL.length `fmap` readBlob b)+ getsize _ = return 0+ size <- getsize item+ let change = case M.lookup path' changed' of+ Nothing -> size+ Just (oldsize, _) -> size - oldsize++ modify $ \st -> st { tree = modifyTree (tree st) path' item+ , changed = M.insert path' (size, age) (changed st)+ , maxage = age + 1+ , changesize = changesize st + change }++renameChanged :: (Functor m, Monad m)+ => AnchoredPath -> AnchoredPath -> TreeMonad m ()+renameChanged from to = modify $ \st -> st { changed = rename' $ changed st }+ where rename' = M.fromList . map renameone . M.toList+ renameone (x, d) | from `isPrefix` x = (to `catPaths` relative from x, d)+ | otherwise = (x, d)+ relative (AnchoredPath from') (AnchoredPath x) = AnchoredPath $ drop (length from') x++-- | Replace an item with a new version without modifying the content of the+-- tree. This does not do any change tracking. Ought to be only used from a+-- 'sync' implementation for a particular storage format. The presumed use-case+-- is that an existing in-memory Blob is replaced with a one referring to an+-- on-disk file.+replaceItem :: (Functor m, Monad m)+ => AnchoredPath -> Maybe (TreeItem m) -> TreeMonad m ()+replaceItem path item = do+ path' <- (`catPaths` path) `fmap` currentDirectory+ modify $ \st -> st { tree = modifyTree (tree st) path' item }++flushItem :: forall m. (Monad m, Functor m) => AnchoredPath -> TreeMonad m ()+flushItem path =+ do current <- gets tree+ case find current path of+ Nothing -> return () -- vanished, do nothing+ Just x -> do y <- fixHash x+ new <- gets update >>= ($ y) . ($ path)+ replaceItem path (Just new)+ where fixHash :: TreeItem m -> TreeMonad m (TreeItem m)+ fixHash f@(File (Blob con NoHash)) = do+ hash <- gets updateHash >>= \x -> lift $ x f+ return $ File $ Blob con hash+ fixHash (SubTree s) | treeHash s == NoHash =+ gets updateHash >>= \f -> SubTree <$> lift (addMissingHashes f s)+ fixHash x = return x+++-- | If buffers are becoming large, sync, otherwise do nothing.+flushSome :: (Monad m, Functor m) => TreeMonad m ()+flushSome = do x <- gets changesize+ when (x > megs 100) $ do+ remaining <- go =<< sortBy age . M.toList <$> gets changed+ modify $ \s -> s { changed = M.fromList remaining }+ where go [] = return []+ go ((path, (size, _)):chs) = do+ x <- (\s -> s - size) <$> gets changesize+ flushItem path+ modify $ \s -> s { changesize = x }+ if x > megs 50 then go chs+ else return chs+ megs = (* (1024 * 1024))+ age (_, (_, a)) (_, (_, b)) = compare a b++instance (Functor m, Monad m) => TreeRO (TreeMonad m) where+ expandTo p =+ do t <- gets tree+ p' <- (`catPaths` p) `fmap` ask+ t' <- lift $ expandPath t p'+ modify $ \st -> st { tree = t' }+ return p'++ fileExists p =+ do p' <- expandTo p+ (isJust . (`findFile` p')) `fmap` gets tree++ directoryExists p =+ do p' <- expandTo p+ (isJust . (`findTree` p')) `fmap` gets tree++ exists p =+ do p' <- expandTo p+ (isJust . (`find` p')) `fmap` gets tree++ readFile p =+ do p' <- expandTo p+ t <- gets tree+ let f = findFile t p'+ case f of+ Nothing -> fail $ "No such file " ++ show p'+ Just x -> lift (readBlob x)++ currentDirectory = ask+ withDirectory dir act = do+ dir' <- expandTo dir+ local (const dir') act++instance (Functor m, Monad m) => TreeRW (TreeMonad m) where+ writeFile p con =+ do _ <- expandTo p+ modifyItem p (Just blob)+ flushSome+ where blob = File $ Blob (return con) hash+ hash = NoHash -- we would like to say "sha256 con" here, but due+ -- to strictness of Hash in Blob, this would often+ -- lead to unnecessary computation which would then+ -- be discarded anyway; we rely on the sync+ -- implementation to fix up any NoHash occurrences++ createDirectory p =+ do _ <- expandTo p+ modifyItem p $ Just $ SubTree emptyTree++ unlink p =+ do _ <- expandTo p+ modifyItem p Nothing++ rename from to =+ do from' <- expandTo from+ to' <- expandTo to+ tr <- gets tree+ let item = find tr from'+ found_to = find tr to'+ unless (isNothing found_to) $+ fail $ "Error renaming: destination " ++ show to ++ " exists."+ unless (isNothing item) $ do+ modifyItem from Nothing+ modifyItem to item+ renameChanged from to++ copy from to =+ do from' <- expandTo from+ _ <- expandTo to+ tr <- gets tree+ let item = find tr from'+ unless (isNothing item) $ modifyItem to item++findM' :: forall m a. (Monad m, Functor m)+ => (Tree m -> AnchoredPath -> a) -> Tree m -> AnchoredPath -> m a+findM' what t path = fst <$> virtualTreeMonad (look path) t+ where look :: AnchoredPath -> TreeMonad m a+ look = expandTo >=> \p' -> flip what p' <$> gets tree++findM :: (Monad m, Functor m) => Tree m -> AnchoredPath -> m (Maybe (TreeItem m))+findM = findM' find++findTreeM :: (Monad m, Functor m) => Tree m -> AnchoredPath -> m (Maybe (Tree m))+findTreeM = findM' findTree++findFileM :: (Monad m, Functor m) => Tree m -> AnchoredPath -> m (Maybe (Blob m))+findFileM = findM' findFile
+ src/Darcs/Util/Tree/Plain.hs view
@@ -0,0 +1,76 @@+-- Copyright (C) 2009-2011 Petr Rockai+--+-- BSD3+-- | The plain format implementation resides in this module. The plain format+-- does not use any hashing and basically just wraps a normal filesystem tree+-- in the hashed-storage API.+--+-- NB. The 'read' function on Blobs coming from a plain tree is susceptible to+-- file content changes. Since we use mmap in 'read', this will break+-- referential transparency and produce unexpected results. Please always make+-- sure that all parallel access to the underlying filesystem tree never+-- mutates files. Unlink + recreate is fine though (in other words, the+-- 'writePlainTree' implemented in this module is safe in this respect).+module Darcs.Util.Tree.Plain+ ( -- * Obtaining Trees.+ --+ -- | Please note that Trees obtained this way will contain Stub+ -- items. These need to be executed (they are IO actions) in order to be+ -- accessed. Use 'expand' to do this. However, many operations are+ -- perfectly fine to be used on a stubbed Tree (and it is often more+ -- efficient to do everything that can be done before expanding a Tree).+ readPlainTree++ -- * Writing trees.+ , writePlainTree+ ) where++import Data.Maybe( catMaybes )+import qualified Data.ByteString.Char8 as BS8+import qualified Data.ByteString.Lazy as BL+import System.FilePath( (</>) )+import System.Directory( getDirectoryContents+ , createDirectoryIfMissing )+import Bundled.Posix( getFileStatus, isDirectory, isRegularFile, FileStatus )++import Darcs.Util.Path+import Darcs.Util.File ( withCurrentDirectory )+import Darcs.Util.ByteString ( readSegment )+import Darcs.Util.Hash( Hash( NoHash) )+import Darcs.Util.Tree( Tree(), TreeItem(..)+ , Blob(..), makeTree+ , list, readBlob, expand )++readPlainDir :: FilePath -> IO [(FilePath, FileStatus)]+readPlainDir dir =+ withCurrentDirectory dir $ do+ items <- getDirectoryContents "."+ sequence [ do st <- getFileStatus s+ return (s, st)+ | s <- items, s `notElem` [ ".", ".." ] ]++readPlainTree :: FilePath -> IO (Tree IO)+readPlainTree dir = do+ items <- readPlainDir dir+ let subs = catMaybes [+ let name = Name (BS8.pack name')+ in case status of+ _ | isDirectory status -> Just (name, Stub (readPlainTree (dir </> name')) NoHash)+ _ | isRegularFile status -> Just (name, File $ Blob (readBlob' name) NoHash)+ _ -> Nothing+ | (name', status) <- items ]+ return $ makeTree subs+ where readBlob' (Name name) = readSegment (dir </> BS8.unpack name, Nothing)++-- | Write out /full/ tree to a plain directory structure. If you instead want+-- to make incremental updates, refer to "Darcs.Util.Tree.Monad".+writePlainTree :: Tree IO -> FilePath -> IO ()+writePlainTree t dir = do+ createDirectoryIfMissing True dir+ expand t >>= mapM_ write . list+ where write (p, File b) = write' p b+ write (p, SubTree _) =+ createDirectoryIfMissing True (anchorPath dir p)+ write _ = return ()+ write' p b = readBlob b >>= BL.writeFile (anchorPath dir p)+
src/Darcs/Util/URL.hs view
@@ -45,17 +45,24 @@ unless a username is provided. Perhaps ssh-paths should use @\"ssh:\/\/user\@host\/path\"@-syntax instead?++ TODO: This whole module should be re-written using a regex matching library!+ The way we do this here is error-prone and inefficient. -} module Darcs.Util.URL ( isValidLocalPath, isHttpUrl, isSshUrl, isRelative, isAbsolute,- isSshNopath, SshFilePath, sshRepo, sshUhost, sshFile, urlOf, splitSshUrl+ isSshNopath, SshFilePath, sshRepo, sshUhost, sshFile, sshFilePathOf, splitSshUrl ) where -import Darcs.Util.Global(darcsdir)+import Prelude ()+import Darcs.Prelude++import Darcs.Util.Global ( darcsdir ) import Data.List ( isPrefixOf, isInfixOf ) import Data.Char ( isSpace )-import qualified System.FilePath as FP (isRelative, isAbsolute, isValid)+import qualified System.FilePath as FP ( isRelative, isAbsolute, isValid )+import System.FilePath ( (</>) ) #include "impossible.h" @@ -91,7 +98,8 @@ ':':x@(_:_:_) -> ':' `notElem` x _ -> False --- | Gives the (user, host, dir) out of an ssh url+-- | Given an ssh URL or file path, split it into+-- user@host, repodir, and the file (with any _darcs/ prefix removed) splitSshUrl :: String -> SshFilePath splitSshUrl s | "ssh://" `isPrefixOf` s = let s' = drop (length "ssh://") $ dropWhile isSpace s@@ -106,7 +114,6 @@ , sshRepo = dir , sshFile = file } - cleanrepourl :: String -> (String, String) cleanrepourl zzz | dd `isPrefixOf` zzz = ([], drop (length dd) zzz) where dd = darcsdir++"/"@@ -119,8 +126,8 @@ cleanrepodir sep = cleanrepourl . drop 1 . dropWhile (/= sep) data SshFilePath = SshFP { sshUhost :: String- , sshRepo :: String- , sshFile :: String}+ , sshRepo :: String+ , sshFile :: String } -urlOf :: SshFilePath -> String-urlOf (SshFP uhost dir file) = uhost ++ ":" ++ dir ++ "/" ++ darcsdir ++ "/" ++ file+sshFilePathOf :: SshFilePath -> String+sshFilePathOf (SshFP uhost dir file) = uhost ++ ":" ++ (dir </> darcsdir </> file)
src/Darcs/Util/Workaround.hs view
@@ -25,7 +25,8 @@ , sigPIPE ) where -import Prelude hiding ( catch )+import Prelude ()+import Darcs.Prelude #ifdef WIN32
src/win32/System/Posix.hs view
@@ -1,4 +1,3 @@-{-# OPTIONS_GHC -fno-warn-dodgy-imports #-} -- needed for GHC 7.0/7.2 {-# LANGUAGE ForeignFunctionInterface #-} module System.Posix ( sleep ) where
src/win32/System/Posix/Files.hsc view
@@ -1,4 +1,3 @@-{-# OPTIONS_GHC -fno-warn-dodgy-imports #-} -- needed for GHC 7.0/7.2 {-# LANGUAGE CPP, ForeignFunctionInterface #-} module System.Posix.Files( isNamedPipe, isDirectory, isRegularFile, isSymbolicLink , getFdStatus, getFileStatus, getSymbolicLinkStatus
− tests/add-formerly-pl.sh
@@ -1,58 +0,0 @@-#!/usr/bin/env bash--# Some tests for 'darcs add'--. lib--rm -rf temp1 temp2--# set up the repository-mkdir temp1-cd temp1-darcs init--# Make sure that messages about directories call them directories-mkdir foo.d-mkdir oof.d-darcs add foo.d-darcs add oof.d-not darcs add -v foo.d 2>&1 | grep -i directory-# Try adding the same directory when it's already in the repo-not darcs add -v foo.d oof.d 2>&1 | grep -i directories--# Make sure that messages about files call them files-touch bar-touch baz-darcs add bar-darcs add baz-not darcs add -v bar 2>&1 | grep -i "following file is"-not darcs add -v bar baz 2>&1 | grep -i "following files are"--# Make sure that messages about both files and directories say so-not darcs add -v bar foo.d 2>&1 | grep -i 'files and directories'---# Make sure that parent directories are added for files-mkdir -p a.d/aa.d/aaa.d-mkdir -p b.d/bb.d-touch a.d/aa.d/aaa.d/baz-touch a.d/aa.d/aaa.d/bar-darcs add -v a.d/aa.d/aaa.d/bar a.d/aa.d/aaa.d/baz b.d/bb.d 2> log-test ! -s log # no output--# Make sure that darcs doesn\'t complains about duplicate adds when adding parent dirs.-mkdir c.d-touch c.d/baz-darcs add -v c.d/baz c.d 2> log-test ! -s log # no output--# Make sure that add output looks good when adding files in subdir-mkdir d.d-touch d.d/foo-darcs add -rv d.d | grep 'd.d/foo'--# 'adding a non-existent dir and file gives the expected message-not darcs add -v notadir/notafile 2>&1 | grep -i 'does not exist'--cd ..-rm -rf temp1
tests/add.sh view
@@ -17,3 +17,99 @@ rm -rf temp1 +# add in subdir++darcs init temp1+cd temp1++mkdir dir+echo zig > dir/foo+darcs add dir dir/foo+darcs record -am add_foo+cd ..+rm -rf temp1++# addrm++darcs init temp1+cd temp1+touch foo+darcs add foo+darcs record -a -m add_foo -A x+darcs remove foo+darcs record -a -m del_foo -A x+cd ..+rm -rf temp1++# issue184: recording files in directories that haven't explicity been added++darcs init temp1+cd temp1++mkdir new+mkdir new/dir+touch new/dir/t.t+darcs add new/dir/t.t+darcs record -am test new/dir/t.t > log+not grep "don't want to record" log+cd ..++rm -rf temp1++# Make sure that parent directories are added for files+darcs init temp1+cd temp1++mkdir -p a.d/aa.d/aaa.d+mkdir -p b.d/bb.d+touch a.d/aa.d/aaa.d/baz+touch a.d/aa.d/aaa.d/bar+darcs add -v a.d/aa.d/aaa.d/bar a.d/aa.d/aaa.d/baz b.d/bb.d 2> log+test ! -s log # no output++# Make sure that darcs doesn\'t complains about duplicate adds when adding parent dirs.+mkdir c.d+touch c.d/baz+darcs add -v c.d/baz c.d 2> log+test ! -s log # no output++# Make sure that add output looks good when adding files in subdir+mkdir d.d+touch d.d/foo+darcs add -rv d.d | grep 'd.d/foo'++# 'adding a non-existent dir and file gives the expected message+not darcs add -v notadir/notafile 2>&1 | grep -i 'does not exist'++cd ..+rm -rf temp1++# test for darcs add behaviour on missing files.++darcs init temp1+cd temp1++empty='test ! -s'+nonempty='test -s'++rm -f foo+darcs add foo >stdout 2>stderr && exit 1 || true+$empty stdout+$nonempty stderr++>foo+darcs add foo >stdout 2>stderr+$nonempty stdout # confirmation message of added file+$empty stderr++darcs add foo >stdout 2>stderr && exit 1 || true+$empty stdout+$nonempty stderr++rm foo+darcs add foo >stdout 2>stderr && exit 1 || true+$empty stdout+$nonempty stderr++cd ..+rm -rf temp1
− tests/add_in_subdir.sh
@@ -1,22 +0,0 @@-#!/usr/bin/env bash--. lib--rm -rf temp1 temp2-mkdir temp1-cd temp1-darcs init-mkdir dir-echo zig > dir/foo-darcs add dir dir/foo-darcs record -a -m add_foo-# Create second repo inside the first-darcs init --repodir=temp2-cd temp2-darcs pull -a ../../temp1-darcs changes -s | grep "A ./dir/foo"-# no differences-diff ../../temp1/dir/foo dir/foo-cd ..--rm -rf temp1
− tests/addexitval.sh
@@ -1,33 +0,0 @@-#!/usr/bin/env bash--. ./lib--rm -rf tmp-mkdir tmp-cd tmp-darcs init--empty='test ! -s'-nonempty='test -s'--rm -f foo-darcs add foo >stdout 2>stderr && exit 1 || true-$empty stdout-$nonempty stderr-->foo-darcs add foo >stdout 2>stderr-$nonempty stdout # print confirmation message of added file-$empty stderr--darcs add foo >stdout 2>stderr && exit 1 || true-$empty stdout-$nonempty stderr--rm foo-darcs add foo >stdout 2>stderr && exit 1 || true-$empty stdout-$nonempty stderr--cd ..-rm -rf tmp
− tests/addmv.sh
@@ -1,20 +0,0 @@-#!/usr/bin/env bash-. ./lib--rm -rf temp1 temp2-mkdir temp1 temp2-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-cd ../temp2-darcs init-darcs pull -v -a ../temp1-cd ..-rm -rf temp1 temp2-
− tests/addrace.sh
@@ -1,18 +0,0 @@-#!/usr/bin/env bash-. ./lib--rm -rf temp1 temp2-mkdir temp1 temp2-cd temp1-darcs init-echo zig > foo-darcs add foo-#sleep 1-darcs record -a -m add_foo -A x-cd ../temp2-darcs init-darcs pull -a ../temp1-cd ..-cmp temp1/foo temp2/foo-rm -rf temp1 temp2-
− tests/addrm.sh
@@ -1,18 +0,0 @@-#!/usr/bin/env bash-. ./lib--rm -rf temp1 temp2-mkdir temp1 temp2-cd temp1-darcs init-touch foo-darcs add foo-darcs record -a -m add_foo -A x-darcs remove foo-darcs record -a -m del_foo -A x-cd ../temp2-darcs init-darcs pull --all ../temp1-cd ..-rm -rf temp1 temp2-
− tests/amend-cancelling.sh
@@ -1,19 +0,0 @@-#!/usr/bin/env bash-. ./lib--# This checks for a possible bug in patch selection where the no available-# patches case is hit.--rm -rf temp1-mkdir temp1-cd temp1-darcs init-touch A-darcs add A-darcs record -am A-echo 'l1' >> A-darcs record -am l1-darcs amend -a --patch 'A'--cd ..-rm -rf temp1
− tests/amend-record-back-up.sh
@@ -1,39 +0,0 @@-#!/usr/bin/env bash-## Copyright (C) 2011 Ganesh Sittampalam <ganesh@earth.li>-##-## Permission is hereby granted, free of charge, to any person-## obtaining a copy of this software and associated documentation-## files (the "Software"), to deal in the Software without-## restriction, including without limitation the rights to use, copy,-## modify, merge, publish, distribute, sublicense, and/or sell copies-## of the Software, and to permit persons to whom the Software is-## furnished to do so, subject to the following conditions:-##-## The above copyright notice and this permission notice shall be-## included in all copies or substantial portions of the Software.-##-## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,-## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF-## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND-## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS-## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN-## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN-## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE-## SOFTWARE.----. lib # Load some portability helpers.-darcs init --repo amend--cd amend-echo 'file1' > file1-darcs record -lam 'file1'--echo 'file2' > file2-darcs record -lam 'file2'--echo 'file2:amended' > file2-echo 'nkya' | darcs amend--darcs changes -p 'file2' -v | grep amended
− tests/amend-unrecord.sh
@@ -1,57 +0,0 @@-#!/usr/bin/env bash-## Test for amend --unrecord-##-## Copyright (C) 2012 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.--. lib # Load some portability helpers.-darcs init --repo R # Create our test repo.--cd R--(echo x ; echo y) > foo-darcs add foo-darcs rec -am "add foo"--(echo 1 ; echo x ; echo y ; echo 2) > foo-darcs rec -am "insert 1 and 2"--(echo yyny) | darcs amend --unrecord-(echo x ; echo y ; echo 2) > foo.expected-darcs show contents foo | diff -q foo.expected ---(echo yenyy) | DARCS_EDITOR="sed -i -e s/2/2j/" darcs amend --unrecord-(echo x ; echo y ; echo 2j) > foo.expected-darcs show contents foo | diff -q foo.expected ---echo 'ugh' > bar-darcs add bar-# use amend to check it's still a short form for amend-record-# if we make amend-unrecord visible rather than hidden that would change-echo y | darcs amend -a-darcs show contents bar | diff -q bar ---# test that amend --unrecord --all and specifying files works-echo y | darcs amend --unrecord -a foo-(echo x ; echo y) > foo.expected-darcs show contents foo | diff -q foo.expected --darcs show contents bar | diff -q bar -
tests/amend.sh view
@@ -74,5 +74,73 @@ cd .. - rm -rf temp1 temp2+++# This checks for a possible bug in patch selection where the no available+# patches case is hit.++darcs init temp1+cd temp1+touch A+darcs record -lam A+echo 'l1' >> A+darcs record -am l1+darcs amend -a --patch 'A'++cd ..+rm -rf temp1++## Copyright (C) 2011 Ganesh Sittampalam <ganesh@earth.li>++darcs init temp1+cd temp1++echo 'file1' > file1+darcs record -lam 'file1'++echo 'file2' > file2+darcs record -lam 'file2'++echo 'file2:amended' > file2+echo 'nkya' | darcs amend++darcs log -p 'file2' -v | grep amended+cd ..+rm -rf temp1++## Test for amend --unrecord+## Copyright (C) 2012 Ganesh Sittampalam++darcs init temp1+cd temp1++echo -e "x\ny" > foo+darcs rec -lam "add foo"++echo -e "1\nx\ny\n2" > foo+darcs rec -am "insert 1 and 2"++echo yyny | darcs amend --unrecord+echo -e "x\ny\n2" > foo.expected+darcs show contents foo | diff -q foo.expected -++echo yenyy | DARCS_EDITOR="sed -i -e s/2/2j/" darcs amend --unrecord+echo -e "x\ny\n2j" > foo.expected+darcs show contents foo | diff -q foo.expected -++echo 'ugh' > bar+darcs add bar+# use amend to check it's still a short form for amend-record+# if we make amend-unrecord visible rather than hidden that would change+echo y | darcs amend -a+darcs show contents bar | diff -q bar -++# test that amend --unrecord --all and specifying files works+echo y | darcs amend --unrecord -a foo+echo -e "x\ny" > foo.expected+darcs show contents foo | diff -q foo.expected -+darcs show contents bar | diff -q bar -++cd ..+rm -rf temp1
+ tests/apply_skip_conflicts.sh view
@@ -0,0 +1,59 @@+#!/usr/bin/env bash+## Test that apply --skip-conflicts filters the conflicts+## appropriately.+##+## 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.++. lib # Load some portability helpers.+rm -rf R S # Another script may have left a mess.++mkdir R+cd R+darcs init+echo 'foo' > foo+echo 'bar' > bar+darcs rec -lam 'Add foo and bar'+cd ..++darcs get R S++cd R+echo 'foo2' > foo+darcs rec -lam 'Change foo (2)'+echo 'bar2' > bar+darcs rec -lam 'Change bar (2)'+cd ..++cd S+echo 'foo3' > foo+darcs rec -lam 'Change foo (3)'+cd ..++cd R+darcs send -a ../S -o ../S/applyme.dpatch+cd ..++cd S+darcs apply --skip-conflicts applyme.dpatch+test `darcs changes --count` -eq 3+cd ..
tests/ask_deps.sh view
@@ -18,13 +18,13 @@ touch a darcs add a echo q | darcs rec -am a0-darcs ann -p a0+darcs log -p a0 -v --machine | cat echo 1 > a echo q | darcs rec -am a1-darcs ann -p a1+darcs log -p a1 -v --machine | cat echo 2 > a echo q | darcs rec -am a2-darcs ann -p a2+darcs log -p a2 -v --machine | cat # add some patches for file 'b' # expect no dependency questions for file 'b',@@ -36,19 +36,21 @@ darcs add b # test 0 echo nnnY | tr '[A-Z]' '[a-z]' | darcs rec -am b0-darcs ann -p b0+darcs log -p b0 -v --machine | cat # test 1 echo 1 > b echo nnyY | tr '[A-Z]' '[a-z]' | darcs rec -am b1-darcs ann -p b1 | grep '^\[a0'+darcs log -p b1 -v --machine | cat+darcs log -p b1 -v --machine | grep '\[a0' # test 2 echo 2 > b echo nyY | tr '[A-Z]' '[a-z]' | darcs rec -am b2-darcs ann -p b2 | grep '^\[a1'+darcs log -p b2 -v --machine | grep '\[a1' # test 3 echo 3 > b echo yY | tr '[A-Z]' '[a-z]' | darcs rec -am b3-darcs ann -p b3 | grep '^\[a2'+darcs log -p b3 -v --machine | cat+darcs log -p b3 -v --machine | grep '\[a2' cd .. rm -rf temp
− tests/bad_pending_after_pull.sh
@@ -1,51 +0,0 @@-#!/usr/bin/env bash--. ./lib--rm -fr temp1 temp2--mkdir temp1-cd temp1-darcs init--echo abc > A-darcs add A-echo def > B1-darcs add B1-# darcs record -all --name patch1 # this way it doesn't trigger the bug-for i in 1 2 3 4 5 6 7 8 9 11; do echo y; done | darcs record --name patch1--darcs mv B1 B2-darcs record --all --name patch2-cd ..--mkdir temp2-cd temp2-darcs init-darcs pull --all ../temp1-not darcs whatsnew-cd ..--rm -fr temp1 temp2--# issue494: note that in this test, we deliberately select filenames-# with a backwards sorting order-mkdir temp1-cd temp1-darcs init-echo abc > b-darcs add b-darcs record --all -m patch1-darcs mv b a-echo def > a-darcs record --all -m patch2-cd ..--mkdir temp2-cd temp2-darcs init-darcs pull --all ../temp1-not darcs whatsnew-cd ..--rm -fr temp1 temp2
− tests/changes-duplicate.sh
@@ -1,49 +0,0 @@-#!/usr/bin/env bash-## Test for patch index automation - <SYNOPSIS: Patch index should-## always be in sync with repo after execution of any command.>-##-## Copyright (C) 2012 BSRK Aditya-##-## 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.--#pragma repo-format darcs-2--. lib-grep darcs-2 $HOME/.darcs/defaults || exit 200--rm -rf R S-darcs init --repo R-darcs init --repo S--cd R-touch f-darcs record -lam 'p1'--cd ../S-touch f-darcs record -lam 'p2'-darcs send -ao p2.dpatch ../R--cd ../R-darcs apply -a ../S/p2.dpatch -darcs changes --verbose-darcs changes --verbose | grep -q 'duplicate'-darcs changes f --verbose | not grep -q 'duplicate'
− tests/changes.sh
@@ -1,71 +0,0 @@-#!/usr/bin/env bash-. ./lib--# Some tests for 'darcs changes -a'--rm -rf temp1-mkdir temp1-cd temp1-darcs init--date >> date.t-darcs add date.t--darcs record -A 'Mark Stosberg <a@b.com>' -a -m foo date.t--####--darcs changes date.t > out # trivial case first-cat out-grep foo out--darcs changes --last=1 date.t > out-cat out-grep foo out--darcs changes --last 1 --summary date.t > out-cat out-grep foo out--darcs changes --last=1 --xml > out-cat out-grep '<a@b.com>' out # check that --xml encodes < and >--###--# Add 6 records and try again-for i in 0 1 2 3 4 5; do- date >> date.t- darcs record -a -m "foo record num $i" date.t-done--darcs changes date.t > out-cat out-grep foo out--darcs changes --last=1 date.t > out-cat out-grep foo out--darcs changes --last 1 --summary date.t > out-cat out-grep foo out--###--darcs changes --context --from-patch='num 1' --to-patch 'num 4' > out-cat out-grep 'num 4' out-grep 'num 3' out-grep 'num 2' out-grep 'num 1' out--###--date >> second_file.t-darcs add second_file.t--darcs record -a -m adding_second_file second_file.t--cd ..-rm -rf temp1
− tests/changes_send_context.sh
@@ -1,18 +0,0 @@-#!/usr/bin/env bash-. ./lib--# RT#544 using context created with 8-bit chars;-rm -rf temp1--mkdir temp1--cd temp1-darcs init-touch foo-darcs record -la -m 'add\212 foo' | grep 'Finished record'-darcs changes --context >context-date > foo-darcs record -a -m 'date foo' | grep 'Finished record'-darcs send -a -o patch --context context . | grep 'Wrote patch to'-cd ..-rm -rf temp1
− tests/changes_with_move.sh
@@ -1,34 +0,0 @@-#!/usr/bin/env bash--# Some tests for the output of changes when combined with move.--. lib--darcs init-date > foo-darcs add foo-darcs record -m 'add foo' -a-mkdir d-darcs add d-darcs record -m 'add d' -a-darcs mv foo d-darcs record -m 'mv foo to d' -a-darcs mv d directory-darcs record -m 'mv d to directory' -a-echo 'How beauteous mankind is' > directory/foo-darcs record -m 'modify directory/foo' -a-darcs changes directory/foo > log-grep 'add foo' log-grep 'mv foo to d' log-echo 'O brave new world' > directory/foo-# darcs should also take unrecorded moves into account-darcs mv directory/foo directory/bar-darcs changes directory/foo > log-grep 'mv foo to d' log-echo 'That has such people in it' > directory/foo-darcs add directory/foo-darcs record -m 'mv foo then add new foo' -a-darcs annotate directory/bar | tee log-grep 'O brave new world' log-grep "mv foo then add new foo" log-not grep "unknown" log
+ tests/conflict-reporting.sh view
@@ -0,0 +1,97 @@+#!/bin/sh -e+##+## General tests for the conflict UI:+## - conflict reporting on pull etc ("We have conflicts in the following files:")+## - conflict reporting in summaries of changes ("M! foo.txt")+## - conflict marking in files+##+## Copyright (C) 2016 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.++#pragma repo-format darcs-1,darcs-2++. lib++rm -rf R1 R2+mkdir R1+cd R1+darcs init+cat > file1 <<EOF+line1+EOF+darcs add file1+cat > file2 <<EOF+line1+line2+line3+EOF+darcs add file2+darcs rec -am "initial patch"++cd ..+darcs get R1 R2+cd R2++cat > file1 <<EOF+line1A+EOF+cat > file2 <<EOF+line1A+line2+line3+EOF++darcs rec -am "patch 1"++cd ../R1++cat > file1 <<EOF+line1B+EOF+cat > file2 <<EOF+line1+line2+line3B+EOF++darcs rec -am "patch 2"+darcs pull -a ../R2 > pull-output+grep "We have conflicts in the following files" pull-output+grep "file1" pull-output+not grep "file2" pull-output++cat > file1-expected <<EOF+v v v v v v v+line1+=============+line1A+*************+line1B+^ ^ ^ ^ ^ ^ ^+EOF++diff -u file1-expected file1++darcs changes --summary -p "patch 1" > changes-output+grep "M! ./file1 -1 +1" changes-output+grep "M ./file2 -1 +1" changes-output+
− tests/dist-v.sh
@@ -1,18 +0,0 @@-#!/usr/bin/env bash-. ./lib--# tests for "darcs dist"--not () { "$@" && exit 1 || :; }--rm -rf temp1-mkdir temp1-cd temp1-darcs init-# needs fixed on FreeBSD-darcs dist -v 2> log-not grep error log-cd ..--rm -rf temp1-
− tests/double-unrevert.sh
@@ -1,29 +0,0 @@-#!/usr/bin/env bash-. ./lib--# This demonstrates a bug that happens if you revert followed by-# a partial unrevert and a full unrevert. It requires that-# the second unrevert is working with patches who's contents need-# to be modified by the commute in the first unrevert.--rm -rf temp1-mkdir temp1-cd temp1-darcs init-echo line1 >> A-echo line2 >> A-echo line3 >> A-echo line4 >> A-echo line5 >> A-echo line6 >> A-darcs add A-darcs record -am A-sed 's/line2/Line2/' A > A1; rm A; mv A1 A-sed '4d' A > A1; rm A; mv A1 A-sed 's/line6/Line6/' A > A1; rm A; mv A1 A-darcs revert -a-echo nyny | darcs unrev-darcs unrev -a--cd ..-rm -rf temp1
− tests/failing-haskell_policy.sh
@@ -1,41 +0,0 @@-#!/usr/bin/env bash-## This is a pseudo-test that runs tweaked hlint on the source code.-##-## Copyright (C) 2009 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--explain() {- echo >&2- echo "## It seems that hlint has found errors. This usually means that you" >&2- echo "## have used a forbidden function. See contrib/darcs-errors.hlint for" >&2- echo "## explanation. Please also disregard any possible parse errors." >&2-}--trap explain ERR--hlint >& /dev/null || exit 200 # skip if there's no hlint--wd="`pwd`"-cd ..-hlint --hint=contrib/darcs-errors.hlint src
tests/failing-index-argument.sh view
@@ -36,11 +36,10 @@ darcs add a echo "test" >> a darcs record -a -m "record a" a-darcs annotate --index 1 darcs changes --index 1 darcs diff --index 1+darcs dist --index 1 darcs show contents --index 1 a #### failing tests darcs show files --index 1 a-darcs dist --index 1
− tests/failing-issue1013_either_dependency.sh
@@ -1,53 +0,0 @@-#!/usr/bin/env bash-. ./lib-export DARCS_EMAIL=test-rm -rf tmp_d1 tmp_d2 tmp_d-# Preparations:-# Set up two repos, each with a patch (d1 and d2 respectively) with both-# an individual change and a change that is identical in both repos,-# and thus auto-merge, i.e., they don't conflict in darcs2. Pull them-# together and record a patch (problem) on top of the auto-merged change,-# so that it depends on EITHER of two patches.--mkdir tmp_d1; cd tmp_d1-darcs init --darcs-2-echo a > a-echo b > b-echo c > c-darcs rec -alm init-echo a-independent > a-echo c-common > c-darcs rec -am d1-cd ..-darcs get --to-patch init tmp_d1 tmp_d2-cd tmp_d2-echo b-independent > b-echo c-common > c-darcs rec -am d2-darcs pull -a ../tmp_d1-# no conflicts -- c-common is identical-echo c-problem > c-darcs rec -am problem-cd ..--# I want to pull the 'problem' patch, but expect darcs to get confused-# because it doesn't know how to select one of the two dependent patches.--darcs get --to-patch init tmp_d2 tmp_d-cd tmp_d-echo n/n/y |tr / \\012 |darcs pull ../tmp_d2-darcs cha--# This is weird, we got d2 though we said No. I would have expected-# darcs to skip the 'problem' patch in this case.--# Try to pull d1 and unpull d2.--darcs pull -a ../tmp_d1-exit 1 # darcs hangs here (2.0.2+77)!-echo n/y/d |tr / \\012 |darcs obl -p d2--# The obliterate fails with: patches to commute_to_end does not commutex (1)--cd ..-rm -rf tmp_d1 tmp_d2 tmp_d
− tests/failing-issue1196_whatsnew_falsely_lists_all_changes.sh
@@ -1,21 +0,0 @@-#!/usr/bin/env bash-. ./lib--not () { "$@" && exit 1 || :; }--rm -rf temp1-mkdir temp1-cd temp1--darcs init-touch aargh-darcs add aargh-echo utrecht > aargh--darcs wh foo foo/../foo/. > out-cat out-not grep utrecht out--cd ..-rm -rf temp1-
tests/failing-issue1316-2.sh view
@@ -38,7 +38,7 @@ darcs rec -am 'Y' rm Y-echo 'y' | darcs amend-rec -a+echo 'y' | darcs amend -a # the bug is that addfile Y shows up in pending # if pending is empty as it should be, then since
− tests/failing-issue1332_add_r_boring.sh
@@ -1,45 +0,0 @@-#!/usr/bin/env bash-## Test for issue1332 - add -r ignores --boring-##-## 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-touch core f-# this is already known to work-darcs add --boring core f-darcs whatsnew > log-grep 'addfile ./f' log-grep 'addfile ./core' log-rm _darcs/patches/pending-touch _darcs/index_invalid--# this fails for issue1332-darcs add -r --boring .-darcs whatsnew > log-grep 'addfile ./f' log-grep 'addfile ./core' log
tests/failing-issue1401_bug_in_get_extra.sh view
@@ -30,7 +30,7 @@ . lib ## This bug only affects darcs-2 repositories.-fgrep darcs-2 ~/.darcs/defaults &>/dev/null || exit 0+fgrep darcs-2 ~/.darcs/defaults &>/dev/null || exit 200 rm -rf d e # Another script may have left a mess. darcs initialize --repodir d/
− tests/failing-issue1609_ot_convergence.sh
@@ -1,104 +0,0 @@-#!/usr/bin/env bash-## Test for issue1609 - standard test for the TP2 property-## in the Operational Transformation literature.-##-## According to Wikipedia:-## For every three concurrent operations op1,op2 and op3 defined on the same-## document state, the transformation function T satisfies CP2/TP2 property-## if and only if:-## T(op_3, op_1 \circ T(op_2,op_1)) = T(op_3, op_2 \circ T(op_1,op_2)).-##-## Copyright (C) 2009 Eric Kow <kowey@darcs.net>-## Copyright (C) 2009 Pascal Molli <momo54@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.--. lib # Load some portability helpers.-rm -rf S1 S2 S3 # Another script may have left a mess.-darcs init --repo S1 # Create our test repos.--cd S1-cat > f << END-1-2-3-4-5-END-darcs add f-darcs record -am init-cd ..--darcs get S1 S2-darcs get S1 S3--cd S1-cat > f << END-1-2-X-3-4-5-END-darcs record -am 'insert X before line 3'-cd ..--cd S2-cat > f << END-1-2-4-5-END-darcs record -am 'delete line 3'-cd ..--cd S3-cat > f << END-1-2-3-Y-4-5-END-darcs record -am 'insert Y after line 3'-cd ..--darcs pull --allow-conflicts -a --repo S1 S2-darcs pull --allow-conflicts -a --repo S2 S1--darcs pull --allow-conflicts -a --repo S1 S3-darcs pull --allow-conflicts -a --repo S2 S3--darcs pull --allow-conflicts -a --repo S3 S1--# TP2 is just fine for conflict resolution itself...-diff S1/f S2/f # no difference-diff S2/f S3/f # no difference--# But what about the conflict marking?-darcs mark-conflicts --repo S1-darcs mark-conflicts --repo S2-darcs mark-conflicts --repo S3-diff S1/f S2/f # no difference-diff S2/f S3/f # no difference
− tests/failing-issue1632_changes_nonexisting.sh
@@ -1,40 +0,0 @@-#!/usr/bin/env bash-## Test for issue1632 - 'darcs changes d/f' should not list any changes,-## where d is part of the repo and f is a non-existent file.-##-## Copyright (C) 2009 Ben Franksen-##-## 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-darcs record -lam 'added directory d'-# darcs should not list any changes here:-darcs changes non-existent-file > log-not grep 'added directory d' log-# ...and neither here:-darcs changes d/non-existent-file > log-not grep 'added directory d' log-cd ..
− tests/failing-issue1702-optimize-relink-vs-cache.sh
@@ -1,77 +0,0 @@-#!/usr/bin/env bash-## Test for issue1702 - an optimize --relink does not relink the files-## in ~/.darcs/cache.-##-## 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.--. lib # Load some portability helpers.-rm -rf R S # Another script may have left a mess.-darcs init --repo R # Create our test repos.--## Create a patch.-echo 'Example content.' > R/f-darcs record -lam 'Add f.' --repodir R--## Get a hard link into the cache.-darcs get R S--## Are hard links available?-x=(R/_darcs/patches/*-*)-x=${x#R/_darcs/patches/}-if [[ ! R/_darcs/patches/$x -ef ~/.darcs/cache/patches/$x ]]-then- echo This test requires filesystem support for hard links.- echo This test requires the hashed (or darcs-2) repo format.- exit 200-fi--## IMPORTANT! In bash [[ ]] is neither a builtin nor a command; it is-## a keyword. This means it can fail without tripping ./lib's set -e.-## This is why all invocations below have the form [[ ... ]] || false.--## Confirm that all three are hard linked.-ls -lids {~/.darcs/cache,[RS]/_darcs}/patches/$x # debugging-[[ R/_darcs/patches/$x -ef ~/.darcs/cache/patches/$x ]] || false-[[ S/_darcs/patches/$x -ef ~/.darcs/cache/patches/$x ]] || false-[[ R/_darcs/patches/$x -ef S/_darcs/patches/$x ]] || false--## Break all hard links.-rm -rf S-cp -r R S-rm -rf R-cp -r S R--## Confirm that there are no hard links.-ls -lids {~/.darcs/cache,[RS]/_darcs}/patches/$x # debugging-[[ ! R/_darcs/patches/$x -ef ~/.darcs/cache/patches/$x ]] || false-[[ ! S/_darcs/patches/$x -ef ~/.darcs/cache/patches/$x ]] || false-[[ ! R/_darcs/patches/$x -ef S/_darcs/patches/$x ]] || false--## Optimize *should* hard-link all three together.-darcs optimize relink --repodir R --sibling S--## Confirm that all three are hard linked.-ls -lids {~/.darcs/cache,[RS]/_darcs}/patches/$x # debugging-[[ R/_darcs/patches/$x -ef ~/.darcs/cache/patches/$x ]] || false-[[ S/_darcs/patches/$x -ef ~/.darcs/cache/patches/$x ]] || false-[[ R/_darcs/patches/$x -ef S/_darcs/patches/$x ]] || false
tests/failing-issue1829-inconsistent-conflictor.sh view
@@ -26,8 +26,9 @@ ## SOFTWARE. . lib -rm -rf r1 r2+grep darcs-2 $HOME/.darcs/defaults || exit 200 +rm -rf r1 r2 mkdir r1 cd r1 darcs init
− tests/failing-issue1928-file-dir-replace.sh
@@ -1,36 +0,0 @@-#!/usr/bin/env bash-## Test for issue1913 - test for directory diffing-##-## Copyright (C) 2010 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.--. 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-touch foo-darcs rec -l -a -m "foo file patch"-rm foo-mkdir foo-darcs rec -l -a -m "foo dir patch"-cd ..
− tests/failing-issue2017-missing-tag.sh
@@ -1,85 +0,0 @@-#!/usr/bin/env bash-## Test for issue2017 - apply should gracefully handle tag missing-## from context (complain, not crash)-##-## 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.-darcs init --repo R # Create our test repos.--cd R-echo 'Example content.' > f-darcs record -lam 'Add f'-cd ..--# variant 0 - this passes trivially-darcs get R R0-darcs get R0 S0-darcs tag 's' --repo S0-darcs get S0 T0-cd T0-echo 'More content.' > f-darcs record -lam 'Modify f'-darcs send -o foo.dpatch -a-cd ..-not darcs apply --repo R0 T0/foo.dpatch > log 2>&1-not grep bug log-grep missing log--# variant 1 - tag in shared context-darcs get R R1-darcs tag '1' --repo R1-darcs get R1 S1-darcs tag 's1' --repo S1-darcs get S1 T1-cd T1-echo 'More content.' > f-darcs record -lam 'Modify f'-darcs send -o foo.dpatch -a-cd ..-# sanity check: should be able to cherry pick-darcs get R1 R1b-cd R1b-[ `darcs changes --count` -eq 2 ]-darcs pull ../T1 --match 'touch f' --all-[ `darcs changes --count` -eq 3 ]-cd ..-# the test: can't apply this due to incorrect context-not darcs apply --repo R1 T1/foo.dpatch > log 2>&1-not grep 'bug' log-grep missing log--# variant 2 - tag created after the fact-darcs get R R2-darcs get R2 S2-darcs tag 's2' --repo S2-darcs get S2 T2-cd T2-echo 'More content.' > f-darcs record -lam 'Modify f'-darcs send -o foo.dpatch -a-cd ..-darcs tag '2' --repo R2 # only tag after-not darcs apply --repo R2 T2/foo.dpatch > log 2>&1-not grep 'bug' log-grep missing log
tests/failing-issue2047_duplicate_conflictor_recommute_fail.sh view
@@ -43,6 +43,8 @@ # # See issue2047 on the BTS for more information. +. ./lib+ rm -rf R1* R2 R3* darcs init --repo R1
− tests/failing-issue2086-index-permissions.sh
@@ -1,38 +0,0 @@-#!/usr/bin/env bash-## Test for issue2086 - _darcs/index permissions are not preserved-##-## Copyright (C) 2011 Eric Kow-##-## Permission is hereby granted, free of charge, to any person-## obtaining a copy of this software and associated documentation-## files (the "Software"), to deal in the Software without-## restriction, including without limitation the rights to use, copy,-## modify, merge, publish, distribute, sublicense, and/or sell copies-## of the Software, and to permit persons to whom the Software is-## furnished to do so, subject to the following conditions:-##-## The above copyright notice and this permission notice shall be-## included in all copies or substantial portions of the Software.-##-## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,-## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF-## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND-## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS-## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN-## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN-## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE-## SOFTWARE.--. lib # Load some portability helpers.-darcs init --repo R # Create our test repos.--cd R-echo 'Example content.' > f-darcs record -lam 'Add f' --umask 022-ls -l _darcs/index | grep '^.rw..--...'-chmod g+w _darcs/index-ls -l _darcs/index | grep '^.rw.rw....'-echo 'Example content 2.' > f-darcs record -lam 'Tweak f'-ls -l _darcs/index | grep '^.rw.rw....'-cd ..
− tests/failing-issue2138-whatsnew-s.sh
@@ -1,59 +0,0 @@-#!/usr/bin/env bash-## Test for issue2138 - whatsnew --summary does not show conflicts-##-## Copyright (C) 2012 Lele Gaifax-##-## 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--darcs init --repo R--cd R-echo 'Example content.' > f-darcs record -lam 'Add f'-cd ..--darcs get R S--# Create a deliberate conflict-cd R-echo "Conflict on side R" >> f-darcs record -am 'CR'--cd ../S-echo "Conflict on side S" >> f-darcs record -am 'CS'--darcs pull -a ../R--darcs whatsnew > out-cat out-grep "side R" out | wc -l | grep 1-grep "side S" out | wc -l | grep 1--darcs whatsnew --summary > out-grep "^M!" out | wc -l | grep 1--cd ..-rm -rf R S
tests/failing-issue2186-apply--reply-conflict.sh view
@@ -40,7 +40,7 @@ echo 'Example content.' >file1 darcs add file1 darcs rec -a --name patch1-darcs send --dont-edit-description --to noreply@example.net --sendmail-command '../dummy-sendmail %<' -a ../S+darcs send --dont-edit-description --to noreply@example.net --mail --sendmail-command '../dummy-sendmail %<' -a ../S mv message patch-set1 # Create the same file in the target repository to trigger a conflict.@@ -51,5 +51,5 @@ # Apply the patch set. darcs should email a report. If it does our dummy-sendmail script will # create file "message".-darcs apply --reply noreply@example.net --sendmail-command '../dummy-sendmail %<' ../R/patch-set1 || :;-[ -f ./message ] || exit 1+darcs apply --reply noreply@example.net --mail --sendmail-command '../dummy-sendmail %<' ../R/patch-set1 || true;+test -f ./message
tests/failing-issue2186-apply--reply-ok.sh view
@@ -40,11 +40,11 @@ echo 'Example content.' >file1 darcs add file1 darcs rec -a --name patch1-darcs send --dont-edit-description --to noreply@example.net --sendmail-command '../dummy-sendmail %<' -a ../S+darcs send --dont-edit-description --to noreply@example.net --mail --sendmail-command '../dummy-sendmail %<' -a ../S mv message patch-set1 # Apply the patch set. darcs should email a report. If it does our dummy-sendmail script will # create file "message". cd ../S-darcs apply --reply noreply@example.net --sendmail-command '../dummy-sendmail %<' ../R/patch-set1-[ -f ./message ] || exit 1+darcs apply --reply noreply@example.net --mail --sendmail-command '../dummy-sendmail %<' ../R/patch-set1+test -f ./message
tests/failing-issue2219-no-working.sh view
@@ -48,22 +48,23 @@ darcs pull ../R1b -a # NB: not sure if this test expresses what I really intend DIFFCOUNT=$(darcs diff | wc -l)-test [ $DIFFCOUNT -eq 0 ]+test $DIFFCOUNT -eq 0 -# 2011-12-27: unclear on what should happen here+# add should not work mkdir S1 cd S1 darcs init --no-working-dir echo "bonjour" > a-darcs add a-darcs record -a -m 'a file' a+not darcs add a cd .. +# move and remove should not work+darcs clone R1 R1m --no-working-dir+cd R1m+not darcs remove a+not darcs move a b+ # --no-working-dir --working-dir flags should trump each other in the # same fashion as the rest of darcs-#-## 2011-12-27: fails-darcs get --no-working-dir --with-working-dir R1 R3-test -e R3/a-darcs get --with-working-dir --no-working-dir R1 R4-test ! -e R4+not darcs clone --no-working-dir --with-working-dir R1 R3+not darcs clone --with-working-dir --no-working-dir R1 R4
− tests/failing-issue2238-unadded-files-showing-added.sh
@@ -1,37 +0,0 @@-#!/usr/bin/env bash-## Test for issue2238 - passing -l twice to whatsnew reports unadded files as-## already added.-##-## Copyright (C) 2012 Owen Stephens-##-## Permission is hereby granted, free of charge, to any person-## obtaining a copy of this software and associated documentation-## files (the "Software"), to deal in the Software without-## restriction, including without limitation the rights to use, copy,-## modify, merge, publish, distribute, sublicense, and/or sell copies-## of the Software, and to permit persons to whom the Software is-## furnished to do so, subject to the following conditions:-##-## The above copyright notice and this permission notice shall be-## included in all copies or substantial portions of the Software.-##-## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,-## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF-## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND-## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS-## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN-## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN-## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE-## SOFTWARE.--. lib--rm -rf R-darcs init --repo R-cd R--touch foo--darcs wh -l | grep 'a \./foo'-darcs wh -ll | grep 'a \./foo'-darcs wh -ll | not grep 'A \./foo'
− tests/failing-issue2242-rollback-mv.sh
@@ -1,46 +0,0 @@-#!/usr/bin/env bash-## Test for issue2242 - rollback of a mv patch generates bogus changes-##-## Copyright (C) 2012 Owen Stephens-##-## Permission is hereby granted, free of charge, to any person-## obtaining a copy of this software and associated documentation-## files (the "Software"), to deal in the Software without-## restriction, including without limitation the rights to use, copy,-## modify, merge, publish, distribute, sublicense, and/or sell copies-## of the Software, and to permit persons to whom the Software is-## furnished to do so, subject to the following conditions:-##-## The above copyright notice and this permission notice shall be-## included in all copies or substantial portions of the Software.-##-## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,-## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF-## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND-## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS-## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN-## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN-## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE-## SOFTWARE.--. lib--rm -rf R-darcs init --repo R-cd R--# Setup dir with empty file in it-mkdir A-touch A/foo-darcs rec -alm 'Add A'--# Mv dir and add content to file-darcs mv A B-echo -e 'line1\nline2' > B/foo-darcs rec -alm 'Move A -> B and change foo'--# Rollback everything in the move/change patch-echo ynya | darcs roll--# We shouldn't see any rm'd dirs/files (just a move and line removal hunk)-darcs wh | not grep rm
tests/failing-issue2293-laziness-amend.sh view
@@ -32,4 +32,4 @@ echo 'baz' > bar -echo yyyy | darcs amend-rec+echo yyyy | darcs amend
− tests/failing-issue2308-changes-missing-amend-edit.sh
@@ -1,54 +0,0 @@-#!/usr/bin/env bash-## Test for issue2308 changes aren't listed when using amend --edit-##-## Copyright (C) 2013 Owen Stephens-##-## Permission is hereby granted, free of charge, to any person-## obtaining a copy of this software and associated documentation-## files (the "Software"), to deal in the Software without-## restriction, including without limitation the rights to use, copy,-## modify, merge, publish, distribute, sublicense, and/or sell copies-## of the Software, and to permit persons to whom the Software is-## furnished to do so, subject to the following conditions:-##-## The above copyright notice and this permission notice shall be-## included in all copies or substantial portions of the Software.-##-## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,-## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF-## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND-## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS-## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN-## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN-## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE-## SOFTWARE.--. lib # Load some portability helpers.--rm -rf R--darcs init --repo R--cd R-echo -e 'a\nb\nc' > file-darcs rec -alm 'Add file'--cat > expected <<EOF- * Add file-Shall I amend this patch? [yNjk...], or ? for more options: Add file-***END OF DESCRIPTION***--Place the long patch description above the ***END OF DESCRIPTION*** marker.-The first line of this file will be the patch name.---This patch contains the following changes:--EOF--export DARCS_EDITOR=cat-# Ignore the patch date/author line (telling tail to start at line 2) and the-# "finished amending patch:" lines-echo y | darcs amend --edit | tail -n+2 | head -n-3 > actual--not diff expected actual
− tests/failing-issue2380-rename-to-deleted-file.sh
@@ -1,47 +0,0 @@-#!/usr/bin/env bash-## Test for issue2380 - darcs won't rename a file to a file that has been-## deleted in the working dir.-##-## Copyright (C) 2014 Owen Stephens-##-## Permission is hereby granted, free of charge, to any person-## obtaining a copy of this software and associated documentation-## files (the "Software"), to deal in the Software without-## restriction, including without limitation the rights to use, copy,-## modify, merge, publish, distribute, sublicense, and/or sell copies-## of the Software, and to permit persons to whom the Software is-## furnished to do so, subject to the following conditions:-##-## The above copyright notice and this permission notice shall be-## included in all copies or substantial portions of the Software.-##-## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,-## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF-## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND-## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS-## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN-## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN-## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE-rm -rf R && darcs init --repo R--. lib--cd R-echo foostuff > foo-echo otherstuff > other-darcs rec -alm 'Add foo and other'--rm foo-darcs mv other foo--# foo should exist, with the correct contents, and other should not-[[ -e foo && ! -e other && $(<foo) == "otherstuff" ]]--echo -e 'hunk ./foo 1\n-foostuff\nrmfile ./foo\nmove ./other ./foo' > expected-darcs wh > actual--diff expected actual--darcs rev -a--[[ -e foo && -e other && $(<foo) == "foostuff" && $(<other) == "otherstuff" ]]
+ tests/failing-issue2480-display-unicode-in-patch-content.sh view
@@ -0,0 +1,9 @@+#!/usr/bin/env bash++. lib+darcs init R+cd R+touch äöüßÄÖÜ+darcs record -lam'added äöüßÄÖÜ'+#darcs log -s | grep './äöüßÄÖÜ'+DARCS_DONT_ESCAPE_8BIT=1 darcs log -s | grep './äöüßÄÖÜ'
− tests/failing-issue612_repo_not_writable.sh
@@ -1,35 +0,0 @@-#!/usr/bin/env bash--# Test that darcs fails appropriately when the target repo inventory file is not writable.-# See issue612--. lib--abort_windows--rm -rf temp1 temp2-mkdir temp1-cd temp1-darcs init-touch t.t-darcs add t.t-darcs record -am "initial add"-if [ -e _darcs/inventories ]; then- chmod 0555 _darcs/inventories/*- chmod 0555 _darcs/inventories-fi-if [ -e _darcs/inventory ]; then- chmod 0555 _darcs/inventory-fi-cd ..--darcs get temp1 temp2-cd temp2-# this block may fail so we'd better make sure we clean up after-# ourselves to avoid a permissions mess for other tests-trap "cd ..; chmod -R 0755 temp1; rm -rf temp1 temp2" EXIT-echo new >> t.t-darcs record -am "new patch"-not darcs push -a ../temp1 2> log-grep failed log-
− tests/failing-issue68_broken_pipe.sh
@@ -1,27 +0,0 @@-#!/usr/bin/env bash--# For issue68, 'don't report "resource vanished" when stdout pipe is broken.'--. ./lib--rm -rf temp1 # Another script may have left a mess.--darcs init --repodir temp1--cd temp1--date > f-darcs add f-darcs rec -am firstp-for (( i=0 ; i < 500; i=i+1 )); do- echo $i >> f;- darcs rec -am p$i-done--darcs changes 2> err | head--touch correcterr--diff correcterr err--cd ..
− tests/failing-look_for_moves.sh
@@ -1,39 +0,0 @@-#!/usr/bin/env bash--. ./lib--rm -rf temp1 temp2-mkdir temp1 temp2-cd temp1--# # exchange of dirs with contents and exchange filenames inside--darcs init-mkdir dir dir2-touch dir/foo dir2/foo dir2/foo2-darcs record -a -m add_files_and_dirs -A x --look-for-adds-mv dir dir.tmp-mv dir2 dir-mv dir.tmp dir2-mv dir/foo dir/foo.tmp-mv dir/foo2 dir/foo-mv dir/foo.tmp dir/foo2-darcs wh --summary --look-for-moves > log 2>&1-cat > log.expected <<EOF- ./dir -> ./dir.tmp~- ./dir2 -> ./dir- ./dir.tmp~ -> ./dir2- ./dir/foo -> ./dir/foo.tmp~- ./dir/foo2 -> ./dir/foo- ./dir/foo.tmp~ -> ./dir/foo2-EOF-diff -u log log.expected-rm log log.expected-darcs record -a -m move_dirs -A x --look-for-moves-darcs wh --look-for-moves --look-for-adds >log 2>&1-grep -vE "(^ *$|^\+|No changes!)" log-rm -rf *---cd ..-rm -rf temp1
tests/failing-merging_newlines.sh view
@@ -33,7 +33,6 @@ echo "in tmp2" >> one.txt darcs whatsnew -s | grep M darcs record -A bar -am "add extra line"-darcs annotate -p . -u darcs push -av > log cat log not grep -i conflicts log
− tests/get-http-packed-detect.sh
@@ -1,40 +0,0 @@-#!/usr/bin/env bash-# 2011, by Petr Rockai, Guillaume Hoffmann, public domain--# Tets that darcs get --verbose reports getting a pack when there is one,-# and does not report when there is none or when --no-packs is passed.--#pragma repo-format darcs-1,darcs-2--. lib--rm -rf S--if grep darcs-1 .darcs/defaults; then-format=hashed-elif grep darcs-2 .darcs/defaults; then-format=darcs-2-else format=ERROR; fi--gunzip -c $TESTDATA/many-files--${format}.tgz | tar xf ---cd many*--darcs optimize http-test -e _darcs/packs/basic.tar.gz-test -e _darcs/packs/patches.tar.gz-cd ..--serve_http # sets baseurl--# check that default behaviour is to get packs-darcs get $baseurl/many-files--${format} S --verbose |grep "Cloning packed basic repository"--# check that it does really not get packs when --no-packs is passed-rm -rf S-darcs get $baseurl/many-files--${format} S --no-packs --verbose |not grep "Cloning packed basic repository"--# check that it does not clam getting packs when there are not-rm -rf S-rm -rf many-files--${format}/_darcs/packs/-darcs get $baseurl/many-files--${format} S --verbose |not grep "Cloning packed basic repository"
− tests/get-http-packed.sh
@@ -1,29 +0,0 @@-#!/usr/bin/env bash-# Written in 2010 by Petr Rockai, placed in public domain--#pragma repo-format darcs-1,darcs-2--. lib--rm -rf S--if grep darcs-1 .darcs/defaults; then-format=hashed-elif grep darcs-2 .darcs/defaults; then-format=darcs-2-else format=ERROR; fi--gunzip -c $TESTDATA/many-files--${format}.tgz | tar xf ---cd many*--darcs optimize http-test -e _darcs/packs/basic.tar.gz-test -e _darcs/packs/patches.tar.gz-cd ..--serve_http # sets baseurl-darcs get --packs $baseurl/many-files--${format} S-cd S-rm _darcs/prefs/sources # avoid any further contact with the original repository-darcs check
− tests/get-http.sh
@@ -1,41 +0,0 @@-#!/usr/bin/env bash-# Written in 2010 by Petr Rockai, placed in public domain--# This file is included as part of the Darcs test distribution,-# which is licensed to you under the following terms:-##-## 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-cd R-darcs init-echo a > a-darcs rec -lam a-cd ..--serve_http # sets baseurl-darcs get $baseurl/R S-cd S-darcs pull ../R | tee log-grep "No remote" log-darcs check
tests/git_import_delete_empty_directories.sh view
@@ -109,4 +109,37 @@ } wcDiff gitsource darcsmirror+darcs check --repodir=darcsmirror # ensure repo is consistent +# ensure dir is deleted after a file move out of it (a common case)+# and that a dir is created when moving to it (also a common case)++git init gitsource2+cd gitsource2+mkdir -p dir1/+echo "i want to move" > dir1/f+echo "me too!!" > g+echo "12345" > h+echo "67890" > i+git add .+git commit -m "initial commit"+mv dir1/f f+git add --all .+git commit -m "move dir1/f to f"+mkdir dir2+mv g dir2/g+git add --all .+git commit -m "move g to dir2/"+# the following commit could be problematic with darcs import,+# with the addfile hunk ending up between both move hunks.+mkdir dir3+mv h dir3/h+mv i dir3/i+git add --all .+git commit -m "move h and i to dir2/"+git clean -fd+cd ..+(cd gitsource2 && git fast-export --all -M) | darcs convert import darcsmirror2 # -M to generate file moves data++wcDiff gitsource2 darcsmirror2+darcs check --repodir=darcsmirror2 # ensure repo is consistent
+ tests/git_rename_and_copy_files.sh view
@@ -0,0 +1,63 @@+#!/usr/bin/env bash+## ensure empty directories get deleted when importing from git+##+## Copyright (C) 2014 Owen Stephens+## 2016 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++! read -r -d '' DATA <<'EOF'+blob+mark :1+data 8+testing++reset refs/heads/master+commit refs/heads/master+mark :2+author CommiterName <me@example.org> 1307452813 +0100+committer CommiterName <me@example.org> 1307452813 +0100+data 6+add a+M 100644 :1 a++commit refs/heads/master+mark :3+author CommiterName <me@example.org> 1307452821 +0100+committer CommiterName <me@example.org> 1307452821 +0100+data 19+Copy, rename, copy+from :2+C "a" "c"+R "a" "b"+C b d+C b a path/with spaces++EOF++rm -rf R+echo "$DATA" | darcs convert import R+cd R++[[ -e c && -e b && -e d && ! -e a && -e "a path/with spaces" ]]+[[ $(darcs log --count) -eq 2 ]]
+ tests/git_rename_dir.sh view
@@ -0,0 +1,62 @@+#!/usr/bin/env bash+## ensure empty directories get deleted when importing from git+##+## Copyright (C) 2014 Owen Stephens+## 2016 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++! read -r -d '' DATA <<'EOF'+blob+mark :1+data 8+testing++reset refs/heads/master+commit refs/heads/master+mark :2+author CommiterName <me@example.org> 1307452813 +0100+committer CommiterName <me@example.org> 1307452813 +0100+data 10+add dir/a+M 100644 :1 dir/a++commit refs/heads/master+mark :3+author CommiterName <me@example.org> 1307452821 +0100+committer CommiterName <me@example.org> 1307452821 +0100+data 24+copy, rename, copy dirs+from :2+C "dir" "dir2"+R "dir" "dir3/dir4"+C "dir3/dir4" "dir4"++EOF++rm -rf R+echo "$DATA" | darcs convert import R+cd R+[[ -e dir2 && -e dir3 && -e dir4 && (! -e dir) && -e dir2/a && -e dir3/dir4/a+ && -e dir4/a ]]+[[ $(darcs log --count) -eq 2 ]]
+ tests/hijack.sh view
@@ -0,0 +1,112 @@+#!/usr/bin/env bash++# Testing patch hijack interactions++. lib++rm -rf temp1+# set up the repository+mkdir temp1+cd temp1+darcs init+cd ..++# create some simple patches by somebody else+cd temp1+touch 1 2 3 4 5+darcs add *+darcs record -a --author you 1 -m 'patch1a'+darcs record -a --author you 2 -m 'patch2a'+darcs record -a --author you 3 -m 'patch3a'+darcs record -a --author you 4 -m 'patch4a'+darcs record -a --author you 5 -m 'patch5a'+cd ..++# try amending a patch+cd temp1+echo yn | darcs amend -p patch5 -m 'patch5b' | grep "Amend anyway"+darcs log | grep patch5+darcs log | not grep patch5b+echo yy | darcs amend -p patch5 -m 'patch5b' | grep "Amend anyway"+darcs log | grep patch5b+cd ..++# try some unsuspending+cd temp1+# ...hijack one+# abort everywhere: need selectchanges for smarter behaviour+echo yyn | darcs rebase suspend --all+darcs log | grep 'patch1a' # nothing suspended+darcs log | grep 'patch5b' # nothing suspended+not darcs rebase unsuspend --all # not in progress+# ...hijack all+echo ya | darcs rebase suspend --all+darcs log | not grep 'patch1a'+darcs log | not grep 'patch5b'+darcs rebase unsuspend --all+darcs log | grep 'patch1a'+darcs log | grep 'patch5b'+cd ..++# make some conflicting patches in another repo+mkdir temp2+cd temp2+darcs init+touch 1 2 3 4 5+darcs add *+darcs record -a --author thirdperson 1 -m 'patch1c'+darcs record -a --author thirdperson 2 -m 'patch2c'+darcs record -a --author thirdperson 3 -m 'patch3c'+darcs record -a --author thirdperson 4 -m 'patch4c'+darcs record -a --author thirdperson 5 -m 'patch5c'+cd ..++# try suspending via rebase pull+cd temp1+# first 'y' is for a "repositories seem to be unrelated" prompt+echo yayyn | darcs rebase pull ../temp2 --all+darcs log | grep 'patch1a' # nothing suspended or pulled+darcs log | grep 'patch5b' # nothing suspended or pulled+not darcs rebase unsuspend --all # not in progress++echo yayyyyyy | darcs rebase pull ../temp2 --all+darcs log | not grep 'patch1a'+darcs log | not grep 'patch5b'++darcs obliterate -a -p 'patch1c'+darcs obliterate -a -p 'patch2c'+darcs obliterate -a -p 'patch3c'+darcs obliterate -a -p 'patch4c'+darcs obliterate -a -p 'patch5c'++darcs rebase unsuspend --all+darcs log | grep 'patch1a'+darcs log | grep 'patch5b'+cd ..++# try suspending via rebase apply++cd temp2+darcs obliterate -a -o c.dpatch+cd ..++cd temp1+echo ayyn | darcs rebase apply ../temp2/c.dpatch --all+darcs log | grep 'patch1a' # nothing suspended or pulled+darcs log | grep 'patch5b' # nothing suspended or pulled+not darcs rebase unsuspend --all # not in progress++echo ayyyyyy | darcs rebase apply ../temp2/c.dpatch --all+darcs log | not grep 'patch1a'+darcs log | not grep 'patch5b'++darcs obliterate -a -p 'patch1c'+darcs obliterate -a -p 'patch2c'+darcs obliterate -a -p 'patch3c'+darcs obliterate -a -p 'patch4c'+darcs obliterate -a -p 'patch5c'++darcs rebase unsuspend --all+darcs log | grep 'patch1a'+darcs log | grep 'patch5b'+cd ..
− tests/illegal_mv.sh
@@ -1,21 +0,0 @@-#!/usr/bin/env bash-. ./lib--rm -rf temp-mkdir temp-cd temp--darcs initialize-echo text > afile.txt-darcs add afile.txt-darcs record --author me --all --no-test --name init-mkdir d-echo The following mv should fail, since d isnt in the repo.-if darcs mv afile.txt d/afile.txt; then-false-fi--# Now clean up.-cd ..-rm -rf temp-
− tests/impossible_unrevert.sh
@@ -1,33 +0,0 @@-#!/usr/bin/env bash-. ./lib--rm -rf temp-mkdir temp-cd temp-darcs init-echo ALL ignore-times > _darcs/prefs/defaults-echo a > foo-darcs add foo-darcs record -a -m aa -A test-echo b > foo-echo yy | darcs revert -a-echo ydyy | darcs unrecord-# since the unrevert is impossible, we should fail if it succeeds...-echo yy | darcs unrevert && exit 1 || true--# now let's try a possible unrevert, just for fun...-echo b >> foo-darcs record -a -m bb -A test-echo f/b | tr / \\012 > foo-darcs record -a -m 'aaa becomes f' -A test-date >> foo-echo yy | darcs revert -a-echo ydyy | darcs unpull-# Now add the date back on at the end:-echo yy | darcs unrevert-echo 'M ./foo +1' > correct_summary-darcs whatsnew --dont-look-for-adds --summary > actual_summary-diff -c correct_summary actual_summary--cd ..-rm -rf temp
− tests/issue1012_unrecord_remove.sh
@@ -1,18 +0,0 @@-#!/usr/bin/env bash-. ./lib-rm -rf temp1-mkdir temp1-cd temp1-darcs --version-darcs init-echo temp1 >File.hs-darcs add File.hs-darcs record File.hs -a -m "add File"-rm File.hs-darcs record -a -m "rm File"-darcs changes-darcs unrecord -p "rm File" -a-darcs changes-darcs record -a -m "re-rm File"-cd ..-rm -rf temp1
− tests/issue1111-pull-intersection.sh
@@ -1,66 +0,0 @@-#!/usr/bin/env bash-# This file is included as part of the Darcs test distribution,-# which is licensed to you under the following terms:-#-## 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-# This test script is in the public domain.-# This file is included as part of the Darcs test distribution,-# which is licensed to you under the following terms:-#---rm -rf temp1 temp2 temp3--mkdir temp1-cd temp1-darcs initialize-echo A > A-darcs add A-darcs record -a -m Aismyname-echo B > B-darcs add B-darcs record -a -m Bismyname--cd ..-darcs get temp1 temp2--cd temp2-darcs obliterate --last 1 -a-echo C > C-darcs add C-darcs record -a -m Cismyname-cd ..--mkdir temp3-cd temp3-darcs init-darcs pull -a -v --intersection ../temp1 ../temp2--darcs changes > out-cat out-grep Aismyname out-not grep Bismyname out-not grep Cismyname out-cd ..--rm -rf temp1 temp2 temp3
tests/issue1139-diff-last.sh view
@@ -9,7 +9,6 @@ echo text > foo darcs add foo darcs rec -am 'add foo'-darcs annotate -p . echo newtext > foo darcs record -am 'modify foo'
+ tests/issue1196_whatsnew_falsely_lists_all_changes.sh view
@@ -0,0 +1,15 @@+#!/usr/bin/env bash+. ./lib++rm -rf temp1+mkdir temp1+cd temp1+darcs init+echo utrecht > aargh+darcs add aargh++not darcs wh foo foo/../foo/.++cd ..+rm -rf temp1+
+ tests/issue1332_add_r_boring.sh view
@@ -0,0 +1,45 @@+#!/usr/bin/env bash+## Test for issue1332 - add -r ignores --boring+##+## 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+touch core f+# this is already known to work+darcs add --boring core f+darcs whatsnew > log+grep 'addfile ./f' log+grep 'addfile ./core' log+rm _darcs/patches/pending+touch _darcs/index_invalid++# this fails for issue1332+darcs add -r --boring .+darcs whatsnew > log+grep 'addfile ./f' log+grep 'addfile ./core' log
− tests/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.--. 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/issue142_record-log.sh
@@ -1,34 +0,0 @@-#!/usr/bin/env bash-## Test for issue142 - darcs record --logfile foo should not-## let you record a patch even when 'foo' is a missing file-##-## 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-touch f g-touch log-darcs record -alm f --logfile log f-not darcs record -alm g --logfile missing g
− tests/issue1599-automatically-expire-unused-caches.sh
@@ -1,55 +0,0 @@-#!/usr/bin/env bash-## Test for issue1599 - 'Automatically expire unused caches'-##-## 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.-. lib--rm -rf R S log && mkdir R-cd R-darcs init-echo a > a-darcs rec -lam a-echo b > b-darcs rec -lam b-echo c > c-darcs rec -lam c-cd ..--serve_http # sets baseurl-darcs get --lazy $baseurl/R S-rm S/_darcs/prefs/sources-if [ -z "$http_proxy" ]; then- echo "repo:http://10.1.2.3/S" >> S/_darcs/prefs/sources-fi-echo "repo:$baseurl/dummyRepo" >> S/_darcs/prefs/sources-echo "repo:~/test1599/S" >> S/_darcs/prefs/sources-echo "repo:$baseurl/R" >> S/_darcs/prefs/sources-export DARCS_CONNECTION_TIMEOUT=1 && darcs changes --repo S --debug --verbose --no-cache 2>&1 | tee log-if [ -z "$http_proxy" ]; then- c=`grep -c "URL.waitUrl http://10.1.2.3/S" log`- [ $c -eq 1 ]-fi-c1=`grep -c "URL.waitUrl $baseurl/dummyRepo" log`-[ $c1 -eq 2 ]-c2=`grep -c "~/test1599/S" log`-[ $c2 -eq 1 ]
+ tests/issue1702-optimize-relink-vs-cache.sh view
@@ -0,0 +1,77 @@+#!/usr/bin/env bash+## Test for issue1702 - an optimize --relink does not relink the files+## in ~/.darcs/cache.+##+## 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.++. lib # Load some portability helpers.+rm -rf R S # Another script may have left a mess.+darcs init --repo R # Create our test repos.++## Create a patch.+echo 'Example content.' > R/f+darcs record -lam 'Add f.' --repodir R++## Get a hard link into the cache.+darcs get R S++## Are hard links available?+x=(R/_darcs/patches/*-*)+x=${x#R/_darcs/patches/}+if [[ ! R/_darcs/patches/$x -ef ~/.darcs/cache/patches/$x ]]+then+ echo This test requires filesystem support for hard links.+ echo This test requires the hashed or darcs-2 repo format.+ exit 200+fi++## IMPORTANT! In bash [[ ]] is neither a builtin nor a command; it is+## a keyword. This means it can fail without tripping ./lib's set -e.+## This is why all invocations below have the form [[ ... ]] || false.++## Confirm that all three are hard linked.+ls -lids {~/.darcs/cache,[RS]/_darcs}/patches/$x # debugging+[[ R/_darcs/patches/$x -ef ~/.darcs/cache/patches/$x ]] || false+[[ S/_darcs/patches/$x -ef ~/.darcs/cache/patches/$x ]] || false+[[ R/_darcs/patches/$x -ef S/_darcs/patches/$x ]] || false++## Break all hard links.+rm -rf S+cp -r R S+rm -rf R+cp -r S R++## Confirm that there are no hard links.+ls -lids {~/.darcs/cache,[RS]/_darcs}/patches/$x # debugging+[[ ! R/_darcs/patches/$x -ef ~/.darcs/cache/patches/$x ]] || false+[[ ! S/_darcs/patches/$x -ef ~/.darcs/cache/patches/$x ]] || false+[[ ! R/_darcs/patches/$x -ef S/_darcs/patches/$x ]] || false++## Optimize *should* hard-link all three together.+darcs optimize relink --repodir R --sibling S++## Confirm that all three are hard linked.+ls -lids {~/.darcs/cache,[RS]/_darcs}/patches/$x # debugging+[[ R/_darcs/patches/$x -ef ~/.darcs/cache/patches/$x ]] || false+[[ S/_darcs/patches/$x -ef ~/.darcs/cache/patches/$x ]] || false+[[ R/_darcs/patches/$x -ef S/_darcs/patches/$x ]] || false
− tests/issue1765_recursive-remove-on-root.sh
@@ -1,36 +0,0 @@-#!/usr/bin/env bash-## Test for issueNNNN - <SYNOPSIS: WHAT IS THE BUG? THIS SYNOPSIS-## SHOULD BE ONE OR TWO SENTENCES.>-##-## Copyright (C) YEAR AUTHOR-##-## 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-mkdir d e # Change the working tree.-echo 'Example content.' > d/f-darcs record -lam 'Add d/f and e.'-darcs remove * -r
tests/issue1825-remove-pending.sh view
@@ -35,9 +35,5 @@ 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?+not darcs remove blahblah darcs revert -a a/b
− tests/issue1845-record-removed.sh
@@ -1,16 +0,0 @@-#!/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 ny | darcs record f 2>&1 | tee log-not grep "None of the files" log-cd ..
− tests/issue184_add.sh
@@ -1,19 +0,0 @@-#!/usr/bin/env bash--. lib--# For issue184: recording files in directories that haven't explicity been added.--rm -rf temp1-mkdir temp1-cd temp1-darcs init-mkdir new-mkdir new/dir-touch new/dir/t.t-darcs add new/dir/t.t-darcs record -am test new/dir/t.t > log-not grep "don't want to record" log-cd ..--rm -rf temp1
− tests/issue1888-changes-context.sh
@@ -1,43 +0,0 @@-#!/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/issue1923-cache-warning.sh
@@ -1,53 +0,0 @@-#!/usr/bin/env bash-## Test for issue1599 - 'Automatically expire unused caches'-##-## 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.-. lib--darcs init --repo R-cd R-echo a > a-darcs rec -lam a-cd ..--serve_http-cat <<SOURCES > fake-sources-repo:$baseurl/dummyRepo-repo:/some/bogus/local/path-repo:$baseurl/R-SOURCES-darcs get --lazy R S1 && cp fake-sources S1/_darcs/prefs/sources-darcs get --lazy R S2 && cp fake-sources S2/_darcs/prefs/sources--# make sure we do warn about things that are under your control-darcs changes --verbose --repo S1 --no-cache 2>&1 | tee log-local-c1=`grep -c "$baseurl/dummyRepo" log-local`-[ $c1 -eq 1 ]-c2=`grep -c "/some/bogus/local/path" log-local`-[ $c2 -eq 1 ]--# now what about things that aren't?-darcs changes --verbose --repo $baseurl/S2 --no-cache 2>&1 | tee log-remote-c1=`grep -c "$baseurl/dummyRepo" log-remote`-[ $c1 -eq 1 ] # always under your control-not grep -c "/some/bogus/local/path" log-remote
+ tests/issue1928-file-dir-replace.sh view
@@ -0,0 +1,36 @@+#!/usr/bin/env bash+## Test for issue1928 - removing a file and adding a directory with the same name+##+## Copyright (C) 2010 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.++. 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+touch foo+darcs rec -l -a -m "foo file patch"+rm foo+mkdir foo+darcs rec -l -a -m "foo dir patch"+cd ..
tests/issue1932-colon-breaks-add.sh view
@@ -25,6 +25,8 @@ . lib # Load some portability helpers. +abort_windows # Windows doesn't support ':' in filenames at all+ rm -rf R # Another script may have left a mess. mkdir -p R # Create our test repo. @@ -36,16 +38,7 @@ # Colon in repo name is an indication of special case - remote repo. # Colon in the file could be there under unix and requires no special treatment. -# Repo name with ':' is either scp repo or http repo.-# Let's check scp repo first.-( darcs get user@invalid:path || true ) > log 2>&1-[ -n "$(fgrep 'ssh: Could not resolve hostname invalid: Name or service not known' log)" ]--# HTTP repo-( darcs get http://www.bogus.domain.so.it.will.surely.fail.com || true ) 2>&1 | tee log-egrep 'CouldNotResolveHost|host lookup failure' log--abort_windows # Windows doesn't support ':' in filenames at all+# remote repos are tested by tests/network/issue1932-remote.sh # All following files should not be added unless "--reserved-ok" is specified, # but should be added with "--reserved-ok" just fine
+ tests/issue2017-missing-tag.sh view
@@ -0,0 +1,85 @@+#!/usr/bin/env bash+## Test for issue2017 - apply should gracefully handle tag missing+## from context (complain, not crash)+##+## 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.+darcs init --repo R # Create our test repos.++cd R+echo 'Example content.' > f+darcs record -lam 'Add f'+cd ..++# variant 0 - this passes trivially+darcs get R R0+darcs get R0 S0+darcs tag 's' --repo S0+darcs get S0 T0+cd T0+echo 'More content.' > f+darcs record -lam 'Modify f'+darcs send -o foo.dpatch -a+cd ..+not darcs apply --repo R0 T0/foo.dpatch > log 2>&1+not grep bug log+grep missing log++# variant 1 - tag in shared context+darcs get R R1+darcs tag '1' --repo R1+darcs get R1 S1+darcs tag 's1' --repo S1+darcs get S1 T1+cd T1+echo 'More content.' > f+darcs record -lam 'Modify f'+darcs send -o foo.dpatch -a+cd ..+# sanity check: should be able to cherry pick+darcs get R1 R1b+cd R1b+[ `darcs changes --count` -eq 2 ]+darcs pull ../T1 --match 'touch f' --all+[ `darcs changes --count` -eq 3 ]+cd ..+# the test: can't apply this due to incorrect context+not darcs apply --repo R1 T1/foo.dpatch > log 2>&1+not grep 'bug' log+grep missing log++# variant 2 - tag created after the fact+darcs get R R2+darcs get R2 S2+darcs tag 's2' --repo S2+darcs get S2 T2+cd T2+echo 'More content.' > f+darcs record -lam 'Modify f'+darcs send -o foo.dpatch -a+cd ..+darcs tag '2' --repo R2 # only tag after+not darcs apply --repo R2 T2/foo.dpatch > log 2>&1+not grep 'bug' log+grep missing log
+ tests/issue2086-index-permissions.sh view
@@ -0,0 +1,40 @@+#!/usr/bin/env bash+## Test for issue2086 - _darcs/index permissions are not preserved+##+## Copyright (C) 2011 Eric Kow+##+## Permission is hereby granted, free of charge, to any person+## obtaining a copy of this software and associated documentation+## files (the "Software"), to deal in the Software without+## restriction, including without limitation the rights to use, copy,+## modify, merge, publish, distribute, sublicense, and/or sell copies+## of the Software, and to permit persons to whom the Software is+## furnished to do so, subject to the following conditions:+##+## The above copyright notice and this permission notice shall be+## included in all copies or substantial portions of the Software.+##+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS+## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN+## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+## SOFTWARE.++. lib # Load some portability helpers.+darcs init --repo R # Create our test repos.++abort_windows # umasks don't really work there++cd R+echo 'Example content.' > f+darcs record -lam 'Add f' --umask 022+ls -l _darcs/index | grep '^.rw..--...'+chmod g+w _darcs/index+ls -l _darcs/index | grep '^.rw.rw....'+echo 'Example content 2.' > f+darcs record -lam 'Tweak f' --umask 002+ls -l _darcs/index | grep '^.rw.rw....'+cd ..
− tests/issue2136-changes_created_as_for_multiple_files.sh
@@ -1,86 +0,0 @@-#!/usr/bin/env bash--## Ensure changes --xml reports correct original filenames for multiple files.-##-## Copyright (C) 2012 Owen Stephens-##-## Permission is hereby granted, free of charge, to any person-## obtaining a copy of this software and associated documentation-## files (the "Software"), to deal in the Software without-## restriction, including without limitation the rights to use, copy,-## modify, merge, publish, distribute, sublicense, and/or sell copies-## of the Software, and to permit persons to whom the Software is-## furnished to do so, subject to the following conditions:-##-## The above copyright notice and this permission notice shall be-## included in all copies or substantial portions of the Software.-##-## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,-## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF-## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND-## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS-## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN-## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN-## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE-## SOFTWARE.--. lib--rm -rf R--darcs init --repo R--cd R--mkdir tldir--touch tldir/f1-darcs rec -alm 'Add tldir/f1'-echo foo >> tldir/f1-darcs rec -am 'Modify tldir/f1'-darcs move tldir/f1 tldir/f2-darcs rec -am 'Move tldir/f1 -> tldir/f2'--touch f3-darcs rec -alm 'Add f3'-darcs move f3 f4-darcs rec -am 'Move f3 -> f4'-darcs move f4 f5-darcs rec -am 'Move f4 -> f5'-touch f6-darcs rec -alm 'Add non-changing file f6'--mkdir tldir/d1-darcs rec -alm 'Add tldir/d1'-darcs move tldir/d1 tldir/d2-darcs rec -am 'Move tldir/d1 -> tldir/d2'--mkdir d3-darcs rec -alm 'Add d3'-darcs move d3 d4-darcs rec -am 'Move d3 -> d4'-darcs move d4 d5-darcs rec -am 'Move d4 -> d5'--# Ensure all original names are reported, both forwards, and reversed.-xmlChanges=$(darcs changes --xml tldir/f2 f5 tldir/d2 d5 f6)-xmlChangesRev=$(darcs changes --reverse --xml tldir/f2 f5 tldir/d2 d5 f6)--# xmlChanges needs to be quoted everywhere, otherwise this hack to retrieve the-# 2 following lines won't work.-checkRename () {- echo "$1" | grep "<created_as current_name='\./$2' original_name='\./$3'>" -C2 | tail -1 | grep "<name>$4</name>"-}--checkInXML () {- checkRename "$1" "d5" "d3" "Add d3"- checkRename "$1" "f5" "f3" "Add f3"- checkRename "$1" "tldir/d2" "tldir/d1" "Add tldir/d1"- checkRename "$1" "tldir/f2" "tldir/f1" "Add tldir/f1"-}--checkInXML "$xmlChanges"-checkInXML "$xmlChangesRev"--# But don't mention unchanged files.-echo "$xmlChanges" | not grep "<created_as[^>]*'\./f6'"
+ tests/issue2136-log_created_as_for_multiple_files.sh view
@@ -0,0 +1,86 @@+#!/usr/bin/env bash++## Ensure log --xml reports correct original filenames for multiple files.+##+## Copyright (C) 2012 Owen Stephens+##+## Permission is hereby granted, free of charge, to any person+## obtaining a copy of this software and associated documentation+## files (the "Software"), to deal in the Software without+## restriction, including without limitation the rights to use, copy,+## modify, merge, publish, distribute, sublicense, and/or sell copies+## of the Software, and to permit persons to whom the Software is+## furnished to do so, subject to the following conditions:+##+## The above copyright notice and this permission notice shall be+## included in all copies or substantial portions of the Software.+##+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS+## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN+## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+## SOFTWARE.++. lib++rm -rf R++darcs init --repo R++cd R++mkdir tldir++touch tldir/f1+darcs rec -alm 'Add tldir/f1'+echo foo >> tldir/f1+darcs rec -am 'Modify tldir/f1'+darcs move tldir/f1 tldir/f2+darcs rec -am 'Move tldir/f1 -> tldir/f2'++touch f3+darcs rec -alm 'Add f3'+darcs move f3 f4+darcs rec -am 'Move f3 -> f4'+darcs move f4 f5+darcs rec -am 'Move f4 -> f5'+touch f6+darcs rec -alm 'Add non-changing file f6'++mkdir tldir/d1+darcs rec -alm 'Add tldir/d1'+darcs move tldir/d1 tldir/d2+darcs rec -am 'Move tldir/d1 -> tldir/d2'++mkdir d3+darcs rec -alm 'Add d3'+darcs move d3 d4+darcs rec -am 'Move d3 -> d4'+darcs move d4 d5+darcs rec -am 'Move d4 -> d5'++# Ensure all original names are reported, both forwards, and reversed.+xmlLog=$(darcs log --xml tldir/f2 f5 tldir/d2 d5 f6)+xmlLogRev=$(darcs log --reverse --xml tldir/f2 f5 tldir/d2 d5 f6)++# xmlLog needs to be quoted everywhere, otherwise this hack to retrieve the+# 2 following lines won't work.+checkRename () {+ echo "$1" | grep "<created_as current_name='\./$2' original_name='\./$3'>" -C2 | tail -1 | grep "<name>$4</name>"+}++checkInXML () {+ checkRename "$1" "d5" "d3" "Add d3"+ checkRename "$1" "f5" "f3" "Add f3"+ checkRename "$1" "tldir/d2" "tldir/d1" "Add tldir/d1"+ checkRename "$1" "tldir/f2" "tldir/f1" "Add tldir/f1"+}++checkInXML "$xmlLog"+checkInXML "$xmlLogRev"++# But don't mention unchanged files.+echo "$xmlLog" | not grep "<created_as[^>]*'\./f6'"
+ tests/issue2138-whatsnew-s.sh view
@@ -0,0 +1,59 @@+#!/usr/bin/env bash+## Test for issue2138 - whatsnew --summary does not show conflicts+##+## Copyright (C) 2012 Lele Gaifax+##+## 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++darcs init --repo R++cd R+echo 'Example content.' > f+darcs record -lam 'Add f'+cd ..++darcs get R S++# Create a deliberate conflict+cd R+echo "Conflict on side R" >> f+darcs record -am 'CR'++cd ../S+echo "Conflict on side S" >> f+darcs record -am 'CS'++darcs pull -a ../R++darcs whatsnew > out+cat out+grep "side R" out | wc -l | grep 1+grep "side S" out | wc -l | grep 1++darcs whatsnew --summary > out+grep "^M!" out | wc -l | grep 1++cd ..+rm -rf R S
− tests/issue2139-mv-to-dir.sh
@@ -1,54 +0,0 @@-#!/usr/bin/env bash-## Test for issue2139 - darcs should accept to mv to the-## current working directory-##-## Copyright (C) 2012 Eric Kow-##-## Permission is hereby granted, free of charge, to any person-## obtaining a copy of this software and associated documentation-## files (the "Software"), to deal in the Software without-## restriction, including without limitation the rights to use, copy,-## modify, merge, publish, distribute, sublicense, and/or sell copies-## of the Software, and to permit persons to whom the Software is-## furnished to do so, subject to the following conditions:-##-## The above copyright notice and this permission notice shall be-## included in all copies or substantial portions of the Software.-##-## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,-## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF-## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND-## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS-## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN-## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN-## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE-## SOFTWARE.--. lib # Load some portability helpers.--darcs init --repo R # Create our test repos.--cd R--# move dir to root-mkdir a a/a2 a/a3-darcs record -lam 'Some directories (a)'-darcs mv a/a2 .-test -d a2 -cd a-darcs mv a3 ..-not test -d a3-cd ..-test -d a3--# move dir to non-root dir-mkdir b b2 b3-darcs record -lam 'Some directories (b)'-darcs mv b2 b-test -d b/b2-cd b-darcs mv ../b3 .-test -d b3-cd ..--cd ..
tests/issue2209-look_for_replaces.sh view
@@ -25,11 +25,9 @@ . lib -rm -rf R-mkdir R+darcs init R cd R # simple full complete replace (record)-darcs init echo "foo" > file darcs record -al -m "add file" echo "bar_longer" > file # replace by token of different length@@ -321,3 +319,47 @@ EOF diff -u log log.expected rm -rf *++# 2++cd ..+darcs init S+cd S+echo 'Example content' > f+echo 'This is golden content , super interesting content' >> f++echo 'Example content' > g+echo 'This is golden content , super interesting content' >> g+darcs record -lam 'Add f, g'++sed -i "s/content/matter/g" f+sed -i "s/content/topic/g" g+darcs whatsnew --look-for-replace | grep replace+# replace same token differently in different files is OK++# 3++cd ..+darcs init T+cd T+echo 'Example content' > f+echo 'This is golden content , super interesting content' >> f++cp f g+cp f e++darcs record -lam 'Add e, f, g'++# change the file in the middle++echo 'Example issue' > f+echo 'This is golden matter , super interesting matter' >> f # create inconsistent replace in f++sed -i "s/content/stuff/g" e # consistent replace in e+sed -i "s/content/topic/g" g # consistent replace in g++darcs whatsnew --look-for-replace > out++grep "^replace ./e" out # 1 replace in e+not grep "^replace ./f" out # 0 replace in f+grep "^replace ./g" out # 1 replace in g
+ tests/issue2242-rollback-mv.sh view
@@ -0,0 +1,46 @@+#!/usr/bin/env bash+## Test for issue2242 - rollback of a mv patch generates bogus changes+##+## Copyright (C) 2012 Owen Stephens+##+## Permission is hereby granted, free of charge, to any person+## obtaining a copy of this software and associated documentation+## files (the "Software"), to deal in the Software without+## restriction, including without limitation the rights to use, copy,+## modify, merge, publish, distribute, sublicense, and/or sell copies+## of the Software, and to permit persons to whom the Software is+## furnished to do so, subject to the following conditions:+##+## The above copyright notice and this permission notice shall be+## included in all copies or substantial portions of the Software.+##+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS+## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN+## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+## SOFTWARE.++. lib++rm -rf R+darcs init --repo R+cd R++# Setup dir with empty file in it+mkdir A+touch A/foo+darcs rec -alm 'Add A'++# Mv dir and add content to file+darcs mv A B+echo -e 'line1\nline2' > B/foo+darcs rec -alm 'Move A -> B and change foo'++# Rollback everything in the move/change patch+echo ynya | darcs roll++# We shouldn't see any rm'd dirs/files (just a move and line removal hunk)+darcs wh | not grep rm
− tests/issue2270-changes-interactive-only-to-files.sh
@@ -1,139 +0,0 @@-#!/usr/bin/env bash-## Test for issue2270 - -## darcs changes should only show changes to relevant files-##-## Copyright (C) 2013 Sebastian Fischer-##-## 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-mkdir R-cd R-darcs init--echo irrelevant > irrelevant-darcs add irrelevant-darcs record -am "recorded irrelevant file"--echo relevant-initial > relevant-echo other > other-darcs add relevant other-darcs record -am "recorded relevant and other file"---# --interactive --only-to-files should only show relevant-(echo y | darcs changes -i --only-to-files relevant > out) || true-cat out-not grep "addfile ./irrelevant" out-grep "addfile ./relevant" out-not grep "addfile ./other" out--# --verbose --only-to-files should only show relevant-(darcs changes -v --only-to-files relevant > out) || true-cat out-not grep "addfile ./irrelevant" out-grep "addfile ./relevant" out-not grep "addfile ./other" out--# --interactive should only show relevant and other-(echo yy | darcs changes -i relevant > out) || true-cat out-not grep "addfile ./irrelevant" out-grep "addfile ./relevant" out-grep "addfile ./other" out--# --verbose should only show relevant and other-(darcs changes -v relevant > out) || true-cat out-not grep "addfile ./irrelevant" out-grep "addfile ./relevant" out-grep "addfile ./other" out--# --interactive --only-to-files should show old name of moved-darcs move relevant renamed-relevant-darcs record -am "renamed relevant file"--(echo yy | darcs changes -i --only-to-files renamed-relevant > out) || true-cat out-grep "addfile ./relevant" out-not grep "addfile ./irrelevant" out-not grep "addfile ./other" out--# --verbose --only-to-files should show old name of moved-(darcs changes -v --only-to-files renamed-relevant > out) || true-cat out-grep "addfile ./relevant" out-not grep "addfile ./irrelevant" out-not grep "addfile ./other" out--# --interactive should show old name of moved-(echo yy | darcs changes -i renamed-relevant > out) || true-cat out-grep "addfile ./relevant" out-not grep "addfile ./irrelevant" out-grep "addfile ./other" out--# --verbose should show old name of moved-(darcs changes -v renamed-relevant > out) || true-cat out-grep "addfile ./relevant" out-not grep "addfile ./irrelevant" out-grep "addfile ./other" out--# adding a new file called 'relevant' :-# changes should now relate to that file and not to the original one-echo relevant-new > relevant-darcs add relevant-echo other-new > other-darcs record -am "recorded new add of relevant"--(echo yy | darcs changes -i --only-to-files relevant > out) || true-cat out-grep "addfile ./relevant" out-grep 'relevant-new' out-not grep 'other-new' out-not grep 'relevant-initial' out--(echo yy | darcs changes -v --only-to-files relevant > out) || true-cat out-grep "addfile ./relevant" out-grep 'relevant-new' out-not grep 'other-new' out-not grep 'relevant-initial' out--(echo yy | darcs changes -i relevant > out) || true-cat out-grep "addfile ./relevant" out-grep 'relevant-new' out-grep 'other-new' out-not grep 'relevant-initial' out--(echo yy | darcs changes -v relevant > out) || true-cat out-grep "addfile ./relevant" out-grep 'relevant-new' out-grep 'other-new' out-not grep 'relevant-initial' out--cd ..-rm -rf R
+ tests/issue2270-log-interactive-only-to-files.sh view
@@ -0,0 +1,139 @@+#!/usr/bin/env bash+## Test for issue2270 - +## darcs log should only show changes to relevant files+##+## Copyright (C) 2013 Sebastian Fischer+##+## 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+mkdir R+cd R+darcs init++echo irrelevant > irrelevant+darcs add irrelevant+darcs record -am "recorded irrelevant file"++echo relevant-initial > relevant+echo other > other+darcs add relevant other+darcs record -am "recorded relevant and other file"+++# --interactive --only-to-files should only show relevant+(echo y | darcs log -i --only-to-files relevant > out) || true+cat out+not grep "addfile ./irrelevant" out+grep "addfile ./relevant" out+not grep "addfile ./other" out++# --verbose --only-to-files should only show relevant+(darcs log -v --only-to-files relevant > out) || true+cat out+not grep "addfile ./irrelevant" out+grep "addfile ./relevant" out+not grep "addfile ./other" out++# --interactive should only show relevant and other+(echo yy | darcs log -i relevant > out) || true+cat out+not grep "addfile ./irrelevant" out+grep "addfile ./relevant" out+grep "addfile ./other" out++# --verbose should only show relevant and other+(darcs log -v relevant > out) || true+cat out+not grep "addfile ./irrelevant" out+grep "addfile ./relevant" out+grep "addfile ./other" out++# --interactive --only-to-files should show old name of moved+darcs move relevant renamed-relevant+darcs record -am "renamed relevant file"++(echo yy | darcs log -i --only-to-files renamed-relevant > out) || true+cat out+grep "addfile ./relevant" out+not grep "addfile ./irrelevant" out+not grep "addfile ./other" out++# --verbose --only-to-files should show old name of moved+(darcs log -v --only-to-files renamed-relevant > out) || true+cat out+grep "addfile ./relevant" out+not grep "addfile ./irrelevant" out+not grep "addfile ./other" out++# --interactive should show old name of moved+(echo yy | darcs log -i renamed-relevant > out) || true+cat out+grep "addfile ./relevant" out+not grep "addfile ./irrelevant" out+grep "addfile ./other" out++# --verbose should show old name of moved+(darcs log -v renamed-relevant > out) || true+cat out+grep "addfile ./relevant" out+not grep "addfile ./irrelevant" out+grep "addfile ./other" out++# adding a new file called 'relevant' :+# changes should now relate to that file and not to the original one+echo relevant-new > relevant+darcs add relevant+echo other-new > other+darcs record -am "recorded new add of relevant"++(echo yy | darcs log -i --only-to-files relevant > out) || true+cat out+grep "addfile ./relevant" out+grep 'relevant-new' out+not grep 'other-new' out+not grep 'relevant-initial' out++(echo yy | darcs log -v --only-to-files relevant > out) || true+cat out+grep "addfile ./relevant" out+grep 'relevant-new' out+not grep 'other-new' out+not grep 'relevant-initial' out++(echo yy | darcs log -i relevant > out) || true+cat out+grep "addfile ./relevant" out+grep 'relevant-new' out+grep 'other-new' out+not grep 'relevant-initial' out++(echo yy | darcs log -v relevant > out) || true+cat out+grep "addfile ./relevant" out+grep 'relevant-new' out+grep 'other-new' out+not grep 'relevant-initial' out++cd ..+rm -rf R
+ tests/issue2378-moving-directory-to-file.sh view
@@ -0,0 +1,14 @@+darcs initialize +mkdir d+darcs add d+echo sometext > d/f+darcs add d/f+darcs record -am'added d/f' --skip-long-comment+darcs move d/f .+darcs record -am'moved d/f to .' --skip-long-comment+rmdir d+darcs record -am'removed d' --skip-long-comment+darcs move f d+darcs record -am'moved f to d' --skip-long-comment+darcs obliterate --last=3 --all+darcs whatsnew -l
+ tests/issue2380-rename-to-deleted-file.sh view
@@ -0,0 +1,47 @@+#!/usr/bin/env bash+## Test for issue2380 - darcs won't rename a file to a file that has been+## deleted in the working dir.+##+## Copyright (C) 2014 Owen Stephens+##+## Permission is hereby granted, free of charge, to any person+## obtaining a copy of this software and associated documentation+## files (the "Software"), to deal in the Software without+## restriction, including without limitation the rights to use, copy,+## modify, merge, publish, distribute, sublicense, and/or sell copies+## of the Software, and to permit persons to whom the Software is+## furnished to do so, subject to the following conditions:+##+## The above copyright notice and this permission notice shall be+## included in all copies or substantial portions of the Software.+##+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS+## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN+## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+rm -rf R && darcs init --repo R++. lib++cd R+echo foostuff > foo+echo otherstuff > other+darcs rec -alm 'Add foo and other'++rm foo+darcs mv other foo++# foo should exist, with the correct contents, and other should not+[[ -e foo && ! -e other && $(<foo) == "otherstuff" ]]++echo -e 'hunk ./foo 1\n-foostuff\nrmfile ./foo\nmove ./other ./foo' > expected+darcs wh > actual++diff expected actual++darcs rev -a++[[ -e foo && -e other && $(<foo) == "foostuff" && $(<other) == "otherstuff" ]]
− tests/issue2388-optimize-fails-no-changes.sh
@@ -1,30 +0,0 @@-#!/usr/bin/env bash-## Test for issue2388 - optimize fails if no patches have been recorded-##-## Copyright (C) 2014 Owen Stephens-##-## Permission is hereby granted, free of charge, to any person-## obtaining a copy of this software and associated documentation-## files (the "Software"), to deal in the Software without-## restriction, including without limitation the rights to use, copy,-## modify, merge, publish, distribute, sublicense, and/or sell copies-## of the Software, and to permit persons to whom the Software is-## furnished to do so, subject to the following conditions:-##-## The above copyright notice and this permission notice shall be-## included in all copies or substantial portions of the Software.-##-## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,-## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF-## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND-## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS-## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN-## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN-## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE-## SOFTWARE.--. lib-darcs init --repo R-cd R--darcs optimize clean
− tests/issue244_changes.sh
@@ -1,18 +0,0 @@-#!/usr/bin/env bash-. ./lib--# Issue244-# darcs changes should be able to pull up the history for a file-# using its moved and not-yet recorded new name--rm -rf temp1-mkdir temp1-cd temp1-darcs init-touch b-darcs add b-darcs record -am 11-darcs mv b c-darcs changes c | grep 11-cd ..-rm -rf temp1
+ tests/issue2479-mv-list-files.sh view
@@ -0,0 +1,14 @@+#!/usr/bin/env bash+## Test for issueNNNN - Bad error message in 'darcs move' if the repo root dir (".")+## is given among multiple sources for the move.++. lib # Load some portability helpers.++mkdir R+cd R+darcs init+echo "test" > b+darcs add b+darcs record -a -m 'added b'+mkdir a+not darcs move . ./b a 2>&1 | not grep -i bug
+ tests/issue2494-output-of-record-with-file-arguments.sh view
@@ -0,0 +1,79 @@+#!/usr/bin/env bash++# Load some portability helpers+. lib++echo added >> ../all_paths+echo not-added >> ../all_paths+echo recorded >> ../all_paths+echo removed >> ../all_paths+echo not-existing >> ../all_paths+echo /not-repo-path >> ../all_paths++check_report() {+ yes="$1"+ log="$2"+ pre="$3"+ shift+ shift+ shift+ paths="$@"+ for arg in "$@"; do+ grep $yes "$pre.\+$arg" "$log"+ done+}++check_report_yes() {+ check_report '' "$@"+}++check_report_no() {+ check_report '-v' "$@"+}++# Create and populate test repo+darcs init --repo R+cd R+touch recorded removed+darcs add recorded removed+darcs record -am'two files'+touch added not-added+rm removed+darcs add added++# Now do the tests+echo q | darcs record added not-added recorded removed not-existing /not-repo-path > ../record.out 2>&1++check_report_yes ../record.out 'non-repository' /not-repo-path+check_report_no ../record.out 'non-repository' added not-added recorded removed not-existing++check_report_yes ../record.out 'non-existing' not-added not-existing+check_report_no ../record.out 'non-existing' added recorded removed /not-repo-path++check_report_yes ../record.out 'not.\+in.\+repository' added+check_report_no ../record.out 'not.\+in.\+repository' not-added recorded removed not-existing /not-repo-path++check_report_yes ../record.out 'Recording' added recorded removed+check_report_no ../record.out 'Recording' not-added not-existing /not-repo-path++echo q | darcs record -l added not-added recorded removed not-existing /not-repo-path > ../record-l.out 2>&1++check_report_yes ../record-l.out 'non-repository' /not-repo-path+check_report_no ../record-l.out 'non-repository' added not-added recorded removed not-existing++check_report_yes ../record-l.out 'non-existing' not-existing+check_report_no ../record-l.out 'non-existing' added not-added recorded removed /not-repo-path++check_report_no ../record-l.out 'not.\+in.\+repository' added not-added recorded removed not-existing /not-repo-path++check_report_yes ../record-l.out 'Recording' added not-added recorded removed+check_report_no ../record-l.out 'Recording' not-existing /not-repo-path++echo q | darcs record -l -q added not-added recorded removed not-existing /not-repo-path > ../record-l-q.stdout++not grep 'paths' ../record-l-q.stdout+check_report_no ../record-l-q.stdout 'Recording' added not-added recorded removed not-existing /not-repo-path++echo q | darcs record -q -a -m'patchname' added not-added recorded removed not-existing /not-repo-path > ../record-q-a.stdout++not grep '.' ../record-q-a.stdout
+ tests/issue612_repo_not_writable.sh view
@@ -0,0 +1,35 @@+#!/usr/bin/env bash++# Test that darcs fails appropriately when the target repo inventory file is not writable.+# See issue612++. lib++abort_windows++rm -rf temp1 temp2+mkdir temp1+cd temp1+darcs init+touch t.t+darcs add t.t+darcs record -am "initial add"+if [ -e _darcs/inventories ]; then+ chmod 0555 _darcs/inventories/*+ chmod 0555 _darcs/inventories+fi+if [ -e _darcs/inventory ]; then+ chmod 0555 _darcs/inventory+fi+cd ..++darcs get temp1 temp2+cd temp2+# this block may fail so we'd better make sure we clean up after+# ourselves to avoid a permissions mess for other tests+trap "cd ..; chmod -R 0755 temp1; rm -rf temp1 temp2" EXIT+echo new >> t.t+darcs record -am "new patch"+not darcs push -a ../temp1 2> log+grep failed log+
+ tests/issue68_broken_pipe.sh view
@@ -0,0 +1,25 @@+#!/usr/bin/env bash++# For issue68, 'don't report "resource vanished" when stdout pipe is broken.'++. ./lib++rm -rf temp1 # Another script may have left a mess.++darcs init --repodir temp1++cd temp1++for i in {1..500}+do+ echo $i >> f+done++darcs changes 2> err | head+darcs rec -alm 'Add big f'++# We recorded a big file add, so asking for the first n lines of the patch+# would trigger this bug.+darcs changes -v 2> err | head++[[ ! -s err ]]
tests/lib view
@@ -84,6 +84,13 @@ test -e "$1/light.pid" && kill `cat "$1/light.pid"` || true } +check_remote_http() {+ if ! curl -fI "$1"; then+ echo Cannot reach "$1"+ exit 200+ fi+}+ skip-formats() { for f in "$@"; do grep $f $HOME/.darcs/defaults && exit 200 || true; done }
+ tests/log-duplicate.sh view
@@ -0,0 +1,46 @@+#!/usr/bin/env bash+## Copyright (C) 2012 BSRK Aditya+##+## 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.++#pragma repo-format darcs-2++. lib+grep darcs-2 $HOME/.darcs/defaults || exit 200++rm -rf R S+darcs init --repo R+darcs init --repo S++cd R+touch f+darcs record -lam 'p1'++cd ../S+touch f+darcs record -lam 'p2'+darcs send -ao p2.dpatch ../R++cd ../R+darcs apply -a ../S/p2.dpatch +darcs log --verbose+darcs log --verbose | grep -q 'duplicate'+darcs log f --verbose | not grep -q 'duplicate'
+ tests/log.sh view
@@ -0,0 +1,161 @@+#!/usr/bin/env bash+. ./lib++# Some tests for 'darcs log -a'++rm -rf temp1+darcs init temp1+cd temp1++date >> date.t+darcs record -A 'Mark Stosberg <a@b.com>' -lam foo++####++darcs log date.t > out # trivial case first+cat out+grep foo out++darcs log --last=1 date.t > out+cat out+grep foo out++darcs log --last 1 --summary date.t > out+cat out+grep foo out++darcs log --last=1 --xml > out+cat out+grep '<a@b.com>' out # check that --xml encodes < and >++###++# Add 6 records and try again+for i in 0 1 2 3 4 5; do+ date >> date.t+ darcs record -a -m "foo record num $i" date.t+done++darcs log date.t > out+cat out+grep foo out++darcs log --last=1 date.t > out+cat out+grep foo out++darcs log --last 1 --summary date.t > out+cat out+grep foo out++###++darcs log --context --from-patch='num 1' --to-patch 'num 4' > out+cat out+grep 'num 4' out+grep 'num 3' out+grep 'num 2' out+grep 'num 1' out++cd ..+rm -rf temp1+++# Some tests for the output of log when combined with move.++darcs init temp1+cd temp1+date > foo+darcs record -lam 'add foo'+mkdir d+darcs record -lam 'add d'+darcs mv foo d+darcs record -m 'mv foo to d' -a+darcs mv d directory+darcs record -m 'mv d to directory' -a+echo 'How beauteous mankind is' > directory/foo+darcs record -m 'modify directory/foo' -a+darcs log directory/foo > log+grep 'add foo' log+grep 'mv foo to d' log+echo 'O brave new world' > directory/foo+# darcs should also take unrecorded moves into account+darcs mv directory/foo directory/bar+darcs log directory/foo > log+grep 'mv foo to d' log+echo 'That has such people in it' > directory/foo+darcs add directory/foo+darcs record -m 'mv foo then add new foo' -a+darcs annotate directory/bar | tee log+grep 'O brave new world' log+grep "mv foo then add new foo" log+not grep "unknown" log+cd ..+rm -rf temp1++# Issue244+# darcs changes should be able to pull up the history for a file+# using its moved and not-yet recorded new name++darcs init temp1+cd temp1+touch b+darcs record -lam 11+darcs mv b c+darcs log c | grep 11+cd ..+rm -rf temp1++## issue1337 - darcs log shows unrelated patches+## Asking "darcs log" about an unrecorded file d/f will list the+## patch that creates the parent directory d/ (instead of no patches).++darcs init 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 log --count --match 'touch d/f')"++cd ..+rm -rf temp1++## issue1632 - 'darcs changes d/f' should not list any changes,+## where d is part of the repo and f is a non-existent file.++darcs init temp1+cd temp1++mkdir d+darcs record -lam 'added directory d'+# darcs should not list any changes here:+darcs changes non-existent-file > log+not grep 'added directory d' log+# ...and neither here:+darcs changes d/non-existent-file > log+not grep 'added directory d' log+cd ..+rm -rf temp1++## issue1888 - changes --context is broken when topmost patch+## is a clean tag.++darcs init temp1+cd temp1++echo a > a ; darcs rec -lam "patch_a"+darcs log --context | grep patch_a++darcs tag -m "tag_a"+darcs log --context | not grep patch_a+darcs log --context | grep tag_a++echo b > a; darcs rec -lam "patch_b"+darcs log --context | not grep patch_a+darcs log --context | grep tag_a+darcs log --context | grep patch_b+cd ..+rm -rf temp1++
+ tests/log_send_context.sh view
@@ -0,0 +1,18 @@+#!/usr/bin/env bash+. ./lib++# RT#544 using context created with 8-bit chars;+rm -rf temp1++mkdir temp1++cd temp1+darcs init+touch foo+darcs record -la -m 'add\212 foo' | grep 'Finished record'+darcs log --context >context+date > foo+darcs record -a -m 'date foo' | grep 'Finished record'+darcs send -a -o patch --context context . | grep 'Wrote patch to'+cd ..+rm -rf temp1
tests/look_for_moves.sh view
@@ -10,7 +10,7 @@ darcs init touch foo-darcs record -a -m add_file -A x --look-for-adds+darcs record -lam add_file mv foo foo2 darcs wh --summary --look-for-moves >log 2>&1 cat > log.expected <<EOF@@ -18,7 +18,7 @@ EOF diff -u log log.expected rm log log.expected-darcs record -a -m move_file -A x --look-for-moves+darcs record -am move_file --look-for-moves darcs wh --look-for-moves --look-for-adds >log 2>&1 grep -vE "(^ *$|^\+|No changes!)" log rm -rf *@@ -27,7 +27,7 @@ darcs init mkdir foo-darcs record -a -m add_dir -A x --look-for-adds+darcs record -lam add_dir mv foo foo2 darcs wh --summary --look-for-moves >log 2>&1 cat > log.expected <<EOF@@ -35,7 +35,7 @@ EOF diff -u log log.expected rm log log.expected-darcs record -a -m move_file -A x --look-for-moves+darcs record -am move_dir --look-for-moves darcs wh --look-for-adds >log 2>&1 grep -vE "(^ *$|^\+|No changes!)" log rm -rf *@@ -44,7 +44,7 @@ darcs init touch foo-darcs record -a -m add_file -A x --look-for-adds+darcs record -lam add_file mv foo foo2 touch foo darcs wh --summary --look-for-moves >log 2>&1@@ -58,7 +58,7 @@ ./foo -> ./foo2 a ./foo EOF-darcs record -a -m move_file_add_file -A x --look-for-moves --look-for-adds+darcs record -am move_file_add_file --look-for-moves --look-for-adds grep -vE "(^ *$|^\+|No changes!)" log rm -rf * @@ -66,25 +66,25 @@ darcs init touch foo-darcs record -a -m add_file -A x --look-for-adds+darcs record -lam add_file mv foo foo2-echo 'yyy' | darcs amend-record -p add_file -A x --look-for-moves+echo 'yyy' | darcs amend-record -p add_file --look-for-moves darcs wh --look-for-moves --look-for-adds >log 2>&1 grep -vE "(^ *$|^\+|No changes!)" log-darcs annotate --patch add_file | grep "] addfile ./foo2"+darcs log -v --machine -p add_file | grep "] addfile ./foo2" rm -rf * # add, move, add same name and amend-record darcs init touch foo-darcs record -a -m add_file -A x --look-for-adds+darcs record -lam add_file mv foo foo2 touch foo-echo 'yyyy' | darcs amend-record -p add_file -A x --look-for-moves --look-for-adds+echo 'yyyy' | darcs amend-record -p add_file --look-for-moves --look-for-adds darcs wh --look-for-moves --look-for-adds >log 2>&1 grep -vE "(^ *$|^\+|No changes!)" log-darcs annotate --patch add_file >log 2>&1+darcs log -v --patch add_file >log 2>&1 grep "addfile ./foo" log grep "addfile ./foo2" log rm -rf *@@ -93,11 +93,11 @@ darcs init touch foo-darcs record -a -m add_file -A x --look-for-adds+darcs record -lam add_file mv foo foo2-echo 'yyy' | darcs amend-record -p add_file -A x --look-for-moves+echo 'yyy' | darcs amend-record -p add_file --look-for-moves mv foo2 foo-echo 'yyy' | darcs amend-record -p add_file -A x --look-for-moves+echo 'yyy' | darcs amend-record -p add_file --look-for-moves darcs wh --look-for-moves --look-for-adds >log 2>&1 grep -vE "(^ *$|^\+|No changes!)" log rm -rf *@@ -108,7 +108,7 @@ touch foo # created before dir to get a lower inode mkdir dir mv foo dir-darcs record -a -m add_files -A x --look-for-adds+darcs record -lam add_files mv dir dir2 darcs wh --summary --look-for-moves > log 2>&1 cat > log.expected <<EOF@@ -116,7 +116,7 @@ EOF diff -u log log.expected rm log log.expected-darcs record -a -m move_dir -A x --look-for-moves+darcs record -am move_dir --look-for-moves darcs wh --look-for-moves --look-for-adds >log 2>&1 grep -vE "(^ *$|^\+|No changes!)" log rm -rf *@@ -125,30 +125,35 @@ darcs init touch foo foo2-darcs record -lam add_file -A x+darcs record -lam add_file mv foo foo.tmp mv foo2 foo mv foo.tmp foo2 not darcs wh --look-for-moves rm -rf * -# dir swapping -- ignored+# dir swapping -- dir moves are ignored but inner files moves are considered darcs init mkdir dir dir2 touch dir/foo dir2/foo2-darcs record -a -m add_files_and_dirs -A x --look-for-adds+darcs record -lam add_files_and_dirs mv dir dir.tmp mv dir2 dir mv dir.tmp dir2-not darcs wh --summary --look-for-moves+darcs wh --summary --look-for-moves > log 2>&1+cat > log.expected <<EOF+ ./dir/foo -> ./dir2/foo+ ./dir2/foo2 -> ./dir/foo2+EOF+diff -u log log.expected rm -rf * # darcs mv before a plain mv darcs init touch foo-darcs record -a -m add_files_and_dirs -A x --look-for-adds+darcs record -lam add_files_and_dirs darcs mv foo foo2 mv foo2 foo3 darcs wh --summary --look-for-moves > log 2>&1@@ -157,7 +162,7 @@ EOF diff -u log log.expected rm log log.expected-darcs record -a -m move_dirs -A x --look-for-moves+darcs record -a -m move_dirs --look-for-moves darcs wh --look-for-moves --look-for-adds >log 2>&1 grep -vE "(^ *$|^\+|No changes!)" log rm -rf *@@ -166,7 +171,7 @@ darcs init touch foo-darcs record -a -m add_files_and_dirs -A x --look-for-adds+darcs record -lam add_files_and_dirs mv foo foo~ darcs wh --summary --look-for-moves > log 2>&1 cat > log.expected <<EOF
tests/merge_three_patches.sh view
@@ -42,8 +42,8 @@ cd tempA darcs pull ../tempB-darcs annotate -p B-darcs annotate -p resolution+darcs log -v --max-count 1 -p B | cat+darcs log -v --max-count 1 -p resolution | cat cd .. cmp tempA/foo tempB/foo
tests/merging_newlines.sh view
@@ -31,7 +31,6 @@ echo "in tmp2" >> one.txt darcs whatsnew -s | grep M darcs record -A bar -am "add extra line"-darcs annotate -p . -u darcs push -av > log cat log not grep -i conflicts log
− tests/mv-formerly-pl.sh
@@ -1,123 +0,0 @@-#!/usr/bin/env bash--# Some tests for 'darcs mv'--. lib--rm -rf temp-mkdir temp-cd temp-darcs init--###--echo adding a directory with more than one .. in it should work.-mkdir foo.d-mkdir foo.d/second-mkdir foo.d/second/third-mkdir foo.d/other---touch ./foo.d/other/date.t-darcs add -r foo.d--cd foo.d/second/third--darcs mv ../../other/date.t ../../other/date_moved.t--cd ../../..-echo darcs refuses to move to an existing file-touch ping-touch pong-darcs add ping pong--not darcs mv ping pong 2>&1 | grep "already exists"--# case sensitivity series-# ------------------------# these are tests designed to check out darcs behave wrt to renames-# where the case of the file becomes important--# are we on a case sensitive file system?-touch is_it_cs-rm -f IS_IT_CS--if test -e is_it_cs; then- echo This is a case-sensitive file system.-else- echo This is NOT a case-sensitive file system.-fi--# if the new file already exists - we don't allow it-# basically the same test as mv ping pong, except we do mv ping PING-# and both ping and PING exist on the filesystem-echo "case sensitivity - simply don't allow mv if new file exists"-touch 'cs-n-1'; touch 'CS-N-1';-touch 'cs-y-1'; touch 'CS-Y-1';-darcs add cs-n-1 cs-y-1--if test -e is_it_cs; then- # regardless of case-ok, we do NOT want this mv at all- not darcs mv cs-n-1 CS-Y-1 2>&1 | grep "already exists"-- not darcs mv --case-ok cs-n-1 CS-Y-1 2>&1 | grep "already exists"-fi--# if the new file does not already exist - we allow it-echo "case sensitivity - the new file does *not* exist"-touch 'cs-n-2';-touch 'cs-y-2';-darcs add cs-n-2 cs-y-2-# these mv's should be allowed regardless of flag or filesystem-darcs mv cs-n-2 CS-N-2-darcs mv --case-ok cs-y-2 CS-Y-2--# parasites - do not accidentally overwrite a file just because it has a-# similar name and points to the same inode. We want to check if a file if the-# same NAME already exists - we shouldn't care about what the actual file is!-echo "case sensitivity - inode check";-touch 'cs-n-3';-touch 'cs-y-3';-darcs add cs-n-3 cs-y-3--if ln cs-n-3 CS-N-3; then # checking if we support hard links- ln cs-y-3 CS-Y-3- # regardless of case-ok, we do NOT want this mv at all- not darcs mv cs-n-3 CS-N-3 2>&1 | grep "already exists"-- not darcs mv --case-ok cs-y-3 CS-Y-3 2>&1 | grep "already exists"-fi--# parasites - we don't allow weird stuff like mv foo bar/foo just because-# we opened up some crazy exception based on foo's name-echo 'refuses to move to an existing file with same name, different path'-touch 'cs-n-4'; touch 'foo.d/cs-n-4';-touch 'cs-y-4'; touch 'foo.d/cs-y-4';-darcs add cs-n-4-# regardless of case-ok, we do NOT want this mv at all-not darcs mv cs-n-4 foo.d/cs-n-4 2>&1 | grep "already exists"--not darcs mv --case-ok cs-y-4 foo.d/cs-y-4 2>&1 | grep "unadded"--# ----------------------------# end case sensitivity series--touch abs_path.t-darcs add abs_path.t-REPO_ABS=`pwd`-darcs mv "$REPO_ABS/abs_path.t" abs_path_new.t-darcs mv abs_path_new.t "$REPO_ABS/abs_path.t"---# issue608-- touch 'gonna_be_deleted';- darcs add gonna_be_deleted- darcs record -am 'added doomed file'- rm gonna_be_deleted- darcs record -am 'deleted file'- touch 'new_file';- darcs add new_file- darcs mv new_file gonna_be_deleted--
− tests/mv-test-suite.sh
@@ -1,28 +0,0 @@-#!/usr/bin/env bash--. ./lib--rm -rf temp1-mkdir temp1-cd temp1-darcs init--date > foo-darcs record -a -m add_foo -A x --look-for-adds--# add a test file-echo 'test ! -e foo' > test.sh-darcs add test.sh-darcs record -a -m add_test--darcs setpref test 'ls && bash test.sh'-darcs record -a -m settest -A x --no-test--darcs mv foo bar-darcs record --debug -a -m mvfoo -A x--darcs check--cd ..-rm -rf temp1-
tests/mv.sh view
@@ -1,10 +1,9 @@ #!/usr/bin/env bash . ./lib -rm -rf temp-mkdir temp-cd temp-darcs init+darcs init temp1+cd temp1+ echo hi world > temp.c darcs add temp.c darcs record --all -A test --name=hi@@ -32,22 +31,247 @@ grep 'Cannot rename a file or directory onto itself' stderr cd ..+rm -rf temp1 -rm -rf temp-mkdir temp-cd temp-darcs init+darcs init temp1+cd temp1+ echo hi world > a-darcs add a-darcs record --all -m lower+darcs record -lam lower cd ..-darcs get temp temp1-cd temp+darcs clone temp1 temp2+cd temp1 darcs mv a A echo goodbye > A darcs record --all -m 'to upper'-cd ../temp1+cd ../temp2 darcs pull -a cd ..-rm -rf temp temp1+rm -rf temp1 temp2++# Part 2++darcs init temp1+cd temp1++echo adding a directory with more than one .. in it should work.+mkdir foo.d+mkdir foo.d/second+mkdir foo.d/second/third+mkdir foo.d/other++touch ./foo.d/other/date.t+darcs add -r foo.d++cd foo.d/second/third++darcs mv ../../other/date.t ../../other/date_moved.t++cd ../../..+echo darcs refuses to move to an existing file+touch ping pong+darcs add ping pong++not darcs mv ping pong 2>&1 | grep "already exists"++# case sensitivity series+# -----------------------+# these are tests designed to check out darcs behave wrt to renames+# where the case of the file becomes important++# are we on a case sensitive file system?+touch is_it_cs+rm -f IS_IT_CS++if test -e is_it_cs; then+ echo This is a case-sensitive file system.+else+ echo This is NOT a case-sensitive file system.+fi++# if the new file already exists - we don't allow it+# basically the same test as mv ping pong, except we do mv ping PING+# and both ping and PING exist on the filesystem+echo "case sensitivity - simply don't allow mv if new file exists"+touch 'cs-n-1'; touch 'CS-N-1';+touch 'cs-y-1'; touch 'CS-Y-1';+darcs add cs-n-1 cs-y-1++if test -e is_it_cs; then+ # regardless of case-ok, we do NOT want this mv at all+ not darcs mv cs-n-1 CS-Y-1 2>&1 | grep "already exists"++ not darcs mv --case-ok cs-n-1 CS-Y-1 2>&1 | grep "already exists"+fi++# if the new file does not already exist - we allow it+echo "case sensitivity - the new file does *not* exist"+touch 'cs-n-2';+touch 'cs-y-2';+darcs add cs-n-2 cs-y-2+# these mv's should be allowed regardless of flag or filesystem+darcs mv cs-n-2 CS-N-2+darcs mv --case-ok cs-y-2 CS-Y-2++# parasites - do not accidentally overwrite a file just because it has a+# similar name and points to the same inode. We want to check if a file if the+# same NAME already exists - we shouldn't care about what the actual file is!+echo "case sensitivity - inode check";+touch 'cs-n-3';+touch 'cs-y-3';+darcs add cs-n-3 cs-y-3++if ln cs-n-3 CS-N-3; then # checking if we support hard links+ ln cs-y-3 CS-Y-3+ # regardless of case-ok, we do NOT want this mv at all+ not darcs mv cs-n-3 CS-N-3 2>&1 | grep "already exists"++ not darcs mv --case-ok cs-y-3 CS-Y-3 2>&1 | grep "already exists"+fi++# parasites - we don't allow weird stuff like mv foo bar/foo just because+# we opened up some crazy exception based on foo's name+echo 'refuses to move to an existing file with same name, different path'+touch 'cs-n-4'; touch 'foo.d/cs-n-4';+touch 'cs-y-4'; touch 'foo.d/cs-y-4';+darcs add cs-n-4+# regardless of case-ok, we do NOT want this mv at all+not darcs mv cs-n-4 foo.d/cs-n-4 2>&1 | grep "already exists"++not darcs mv --case-ok cs-y-4 foo.d/cs-y-4 2>&1 | grep "unadded"++# ---------------------------+# end case sensitivity series++touch abs_path.t+darcs add abs_path.t+REPO_ABS=`pwd`+darcs mv "$REPO_ABS/abs_path.t" abs_path_new.t+darcs mv abs_path_new.t "$REPO_ABS/abs_path.t"++# issue608++touch 'gonna_be_deleted';+darcs add gonna_be_deleted+darcs record -am 'added doomed file'+rm gonna_be_deleted+darcs record -am 'deleted file'+touch 'new_file';+darcs add new_file+darcs mv new_file gonna_be_deleted++cd ..+rm -rf temp1++# mv and test suite++darcs init temp1+cd temp1++date > foo+darcs record -lam add_foo++echo 'test ! -e foo' > test.sh # "foo should not exist"+darcs record -lam add_test++darcs setpref test 'ls && bash test.sh'+darcs record -a -m settest --no-test++darcs mv foo bar+darcs record --debug -a -m mvfoo++cd ..+rm -rf temp1++# mv then ad++darcs init temp1+cd temp1++touch fee fi fo fum+darcs record -lam add+darcs mv fee foo+touch fee+darcs add fee+darcs record -a -m newfee+darcs mv fi fib+darcs record -a -m mvfi+date > fi+darcs add fi +darcs record -a -m newfi++cd ..+rm -rf temp1++# illegal mv++darcs init temp1+cd temp1++echo text > afile.txt+darcs record -lam init+mkdir d+not darcs mv afile.txt d/afile.txt # should fail, since d not in repo++cd ..+rm -rf temp1++# swapping files++darcs init temp1+cd temp1++touch foo bar+darcs record -lam add_foo_bar+darcs mv foo zig+darcs mv bar foo+darcs mv zig bar+darcs record -a -m swap_foo_bar+cd ..+rm -rf temp1+++## issue2139 - darcs should accept to mv to the current working directory+## Copyright (C) 2012 Eric Kow++darcs init temp1+cd temp1++# move dir to root+mkdir a a/a2 a/a3+darcs record -lam 'Some directories (a)'+darcs mv a/a2 .+test -d a2 +cd a+darcs mv a3 ..+not test -d a3+cd ..+test -d a3++# move dir to non-root dir+mkdir b b2 b3+darcs record -lam 'Some directories (b)'+darcs mv b2 b+test -d b/b2+cd b+darcs mv ../b3 .+test -d b3+cd ..++cd ..+rm -rf temp1++# issue1740 - darcs mv on directories should work after the fact++darcs init temp1+cd temp1++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"+cd ..+rm -rf temp1
− tests/mv_then_add.sh
@@ -1,25 +0,0 @@-#!/usr/bin/env bash-. ./lib--rm -rf temp-mkdir temp-cd temp--darcs init-touch fee fi fo fum-darcs add f*-darcs record --author me --all --no-test --name add-darcs mv fee foo-touch fee-darcs add fee-darcs record --author me --all --no-test --name newfee-darcs mv fi fib-darcs record --author me --all --no-test --name mvfi-date > fi-darcs add fi -darcs record --author me --all --no-test --name newfi--cd ..--rm -rf temp-
+ tests/network/clone-http-packed-detect.sh view
@@ -0,0 +1,40 @@+#!/usr/bin/env bash+# 2011, by Petr Rockai, Guillaume Hoffmann, public domain++# Tests that darcs clone --verbose reports getting a pack when there is one,+# and does not report when there is none or when --no-packs is passed.++#pragma repo-format darcs-1,darcs-2++. lib++rm -rf S++if grep darcs-1 .darcs/defaults; then+format=hashed+elif grep darcs-2 .darcs/defaults; then+format=darcs-2+else format=ERROR; fi++gunzip -c $TESTDATA/many-files--${format}.tgz | tar xf -++cd many*++darcs optimize http+test -e _darcs/packs/basic.tar.gz+test -e _darcs/packs/patches.tar.gz+cd ..++serve_http # sets baseurl++# check that default behaviour is to get packs+darcs clone $baseurl/many-files--${format} S --verbose |grep "Cloning packed basic repository"++# check that it does really not get packs when --no-packs is passed+rm -rf S+darcs clone $baseurl/many-files--${format} S --no-packs --verbose |not grep "Cloning packed basic repository"++# check that it does not clam getting packs when there are not+rm -rf S+rm -rf many-files--${format}/_darcs/packs/+darcs clone $baseurl/many-files--${format} S --verbose |not grep "Cloning packed basic repository"
+ tests/network/clone-http-packed.sh view
@@ -0,0 +1,29 @@+#!/usr/bin/env bash+# Written in 2010 by Petr Rockai, placed in public domain++#pragma repo-format darcs-1,darcs-2++. lib++rm -rf S++if grep darcs-1 .darcs/defaults; then+format=hashed+elif grep darcs-2 .darcs/defaults; then+format=darcs-2+else format=ERROR; fi++gunzip -c $TESTDATA/many-files--${format}.tgz | tar xf -++cd many*++darcs optimize http+test -e _darcs/packs/basic.tar.gz+test -e _darcs/packs/patches.tar.gz+cd ..++serve_http # sets baseurl+darcs clone --packs $baseurl/many-files--${format} S+cd S+rm _darcs/prefs/sources # avoid any further contact with the original repository+darcs check
+ tests/network/clone-http.sh view
@@ -0,0 +1,41 @@+#!/usr/bin/env bash+# Written in 2010 by Petr Rockai, placed in public domain++# This file is included as part of the Darcs test distribution,+# which is licensed to you under the following terms:+##+## 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+cd R+darcs init+echo a > a+darcs rec -lam a+cd ..++serve_http # sets baseurl+darcs clone $baseurl/R S+cd S+darcs pull ../R | tee log+grep "No remote" log+darcs check
tests/network/clone.sh view
@@ -1,6 +1,9 @@ #!/usr/bin/env bash-set -ev +. lib++check_remote_http http://hub.darcs.net/kowey/tabular+ rm -rf temp temp2 temp3 #"$DARCS" clone http://hub.darcs.net/kowey/tabular temp@@ -15,5 +18,3 @@ cd .. diff -u temp2/_darcs/hashed_inventory temp3/_darcs/hashed_inventory--rm -rf temp temp2 temp3
tests/network/issue1503_prefer_local_caches_to_remote_one.sh view
@@ -25,6 +25,8 @@ . lib +check_remote_http http://darcs.net/testing/repo1+ rm -rf S T darcs clone --lazy http://darcs.net/testing/repo1 S darcs tag --repo S -m 2
+ tests/network/issue1599-automatically-expire-unused-caches.sh view
@@ -0,0 +1,55 @@+#!/usr/bin/env bash+## Test for issue1599 - 'Automatically expire unused caches'+##+## 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.+. lib++rm -rf R S log && mkdir R+cd R+darcs init+echo a > a+darcs rec -lam a+echo b > b+darcs rec -lam b+echo c > c+darcs rec -lam c+cd ..++serve_http # sets baseurl+darcs clone --lazy $baseurl/R S+rm S/_darcs/prefs/sources+if [ -z "$http_proxy" ]; then+ echo "repo:http://10.1.2.3/S" >> S/_darcs/prefs/sources+fi+echo "repo:$baseurl/dummyRepo" >> S/_darcs/prefs/sources+echo "repo:~/test1599/S" >> S/_darcs/prefs/sources+echo "repo:$baseurl/R" >> S/_darcs/prefs/sources+export DARCS_CONNECTION_TIMEOUT=1 && darcs log --repo S --debug --verbose --no-cache 2>&1 | tee log+if [ -z "$http_proxy" ]; then+ c=`grep -c "URL.waitUrl http://10.1.2.3/S" log`+ [ $c -eq 1 ]+fi+c1=`grep -c "URL.waitUrl $baseurl/dummyRepo" log`+[ $c1 -eq 2 ]+c2=`grep -c "~/test1599/S" log`+[ $c2 -eq 1 ]
+ tests/network/issue1923-cache-warning.sh view
@@ -0,0 +1,53 @@+#!/usr/bin/env bash+## Test for issue1599 - 'Automatically expire unused caches'+##+## 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.+. lib++darcs init --repo R+cd R+echo a > a+darcs rec -lam a+cd ..++serve_http+cat <<SOURCES > fake-sources+repo:$baseurl/dummyRepo+repo:/some/bogus/local/path+repo:$baseurl/R+SOURCES+darcs clone --lazy R S1 && cp fake-sources S1/_darcs/prefs/sources+darcs clone --lazy R S2 && cp fake-sources S2/_darcs/prefs/sources++# make sure we do warn about things that are under your control+darcs log --verbose --repo S1 --no-cache 2>&1 | tee log-local+c1=`grep -c "$baseurl/dummyRepo" log-local`+[ $c1 -eq 1 ]+c2=`grep -c "/some/bogus/local/path" log-local`+[ $c2 -eq 1 ]++# now what about things that aren't?+darcs log --verbose --repo $baseurl/S2 --no-cache 2>&1 | tee log-remote+c1=`grep -c "$baseurl/dummyRepo" log-remote`+[ $c1 -eq 1 ] # always under your control+not grep -c "/some/bogus/local/path" log-remote
+ tests/network/issue1932-remote.sh view
@@ -0,0 +1,41 @@+#!/usr/bin/env bash+## Test for issue1932 - "darcs add -qr ." should not break on files with colons+##+## Copyright(C) 2010 Dmitry Astapov+##+## 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.++# Colons could be in repo names and in file name.+# Colon in repo name is an indication of special case - remote repo.+# Colon in the file could be there under unix and requires no special treatment.++# Repo name with ':' is either scp repo or http repo.+# Let's check scp repo first.+( darcs clone user@invalid:path || true ) > log 2>&1+[ -n "$(fgrep 'ssh: Could not resolve hostname invalid: Name or service not known' log)" ]++# HTTP repo+( darcs clone http://www.bogus.domain.so.it.will.surely.fail.com || true ) 2>&1 | tee log+egrep 'CouldNotResolveHost|host lookup failure' log++# local repos are tested by tests/issue1932-colon-breaks-add.sh
tests/network/issue2090-transfer-mode.sh view
@@ -23,10 +23,13 @@ ## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ## SOFTWARE. -echo 'Comment this line out and run the script by hand'; exit 200+# echo 'Comment this line out and run the script by hand'; exit 200 -. $(dirname $0)/../lib-. $(dirname $0)/sshlib+# . $(dirname $0)/../lib+# . $(dirname $0)/sshlib++. lib+. sshlib # Clean up after previous remote runs ${SSH} ${REMOTE} "\
tests/network/lazy-clone.sh view
@@ -1,6 +1,9 @@ #!/usr/bin/env bash-set -ev +. lib++check_remote_http http://hub.darcs.net/kowey/tabular+ rm -rf temp temp2 temp3 darcs clone --lazy http://hub.darcs.net/kowey/tabular temp@@ -13,7 +16,7 @@ test ! -f _darcs/patches/0000005705-178beaf653578703e32346b4d68c8ee2f84aeef548633b2dafe3a5974d763bf2 -darcs annotate -p 'Initial version'+darcs log -p 'Initial version' -v | cat test -f _darcs/patches/0000005705-178beaf653578703e32346b4d68c8ee2f84aeef548633b2dafe3a5974d763bf2
tests/network/log.sh view
@@ -2,6 +2,8 @@ . lib +check_remote_http http://darcs.net+ # Demonstrates issue385 and others darcs log --repo=http://darcs.net GNUmakefile --last 300 @@ -10,8 +12,11 @@ # no _darcs should remain test ! -d _darcs # go to a directory where we have no write access+# (I dearly hope nobody tries to run the tests as root!) cd / # and try again (with less patches to fetch) darcs log --repo=http://darcs.net GNUmakefile --last 3 # an absolute path should give an error not darcs log --repo=http://darcs.net /GNUmakefile --last 3+# also test that it works without any filename arguments+darcs log --repo=http://darcs.net --last 1
+ tests/network/show_tags-remote.sh view
@@ -0,0 +1,46 @@+#!/usr/bin/env bash+## Test for show tags --repo+##+## 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.+darcs init --repo R # Create our test repos.+darcs init --repo S++cd R+echo 'Example content.' > f+darcs record -lam 'Add f'+darcs tag 't0'+darcs tag 't1'+darcs show tags | grep t0+cd ..+serve_http # sets baseurl++cd S+darcs log --repo ../R+darcs show tags --repo ../R | grep t0+darcs show tags --repo $baseurl/R | grep t0+cd ..++darcs show tags --repo R | grep t0+darcs show tags --repo $baseurl/R | grep t0
tests/network/ssh.sh view
@@ -1,31 +1,36 @@ #!/bin/bash-echo 'Comment this line out and run the script by hand'; exit 200+# echo 'Comment this line out and run the script by hand'; exit 200 -. $(dirname $0)/../lib-. $(dirname $0)/sshlib+# . $(dirname $0)/../lib+# . $(dirname $0)/sshlib +. lib+. sshlib+ # ================ Setting up remote repositories ===============-${SSH} ${REMOTE} "\-rm -rf ${REMOTE_DIR}; \-mkdir ${REMOTE_DIR}; \-cd ${REMOTE_DIR}; \-\-mkdir testrepo; cd testrepo; \-darcs init; \-echo moi > _darcs/prefs/author; \-touch a; darcs add a; darcs record a --ignore-times -am 'add file a'; \-echo 'first line' > a; darcs record a --ignore-times -am 'add first line to a'; \-cd ..; \-\-darcs clone testrepo testrepo-pull; \-cd testrepo-pull; \-echo moi > _darcs/prefs/author; \-touch b; darcs add b; darcs record b --ignore-times -am 'add file b'; \-echo 'other line' > b; darcs record b --ignore-times -am 'add other line to b'; \-cd ..; \-\-darcs clone testrepo testrepo-push; \-darcs clone testrepo testrepo-send; \+${SSH} ${REMOTE} "+rm -rf ${REMOTE_DIR}+mkdir ${REMOTE_DIR}+cd ${REMOTE_DIR}++mkdir testrepo; cd testrepo+darcs init+echo moi > _darcs/prefs/author+touch a; darcs add a+darcs record --skip-long-comment a --ignore-times -am 'add file a'+echo 'first line' > a+darcs record --skip-long-comment a --ignore-times -am 'add first line to a'+cd ..++darcs clone testrepo testrepo-pull+cd testrepo-pull+echo moi > _darcs/prefs/author+touch b; darcs add b; darcs record --skip-long-comment b --ignore-times -am 'add file b'+echo 'other line' > b; darcs record --skip-long-comment b --ignore-times -am 'add other line to b'+cd ..++darcs clone testrepo testrepo-push+darcs clone testrepo testrepo-send " # ================ Settings ===============@@ -63,9 +68,10 @@ darcs clone ${DARCS_SSH_FLAGS} testrepo testrepo-push cd testrepo-push echo moi > _darcs/prefs/author-echo "second line" >> a; darcs record a --ignore-times -am "add second line to a"+echo "second line" >> a+darcs record --skip-long-comment a --ignore-times -am "add second line to a" touch c; darcs add c-darcs record --ignore-times -am "add file c" c+darcs record --skip-long-comment --ignore-times -am "add file c" c echo yyy | darcs push ${DARCS_SSH_FLAGS} ${REMOTE}:${REMOTE_DIR}/testrepo-push # check that the file c got pushed over ${SSH} ${REMOTE} "[ -f ${REMOTE_DIR}/testrepo-push/c ]"@@ -88,11 +94,11 @@ cd testrepo echo moi > _darcs/prefs/author echo 'change for remote' > a-darcs record --ignore-times -am 'change for remote'+darcs record --skip-long-comment --ignore-times -am 'change for remote' darcs push -a darcs ob --last 1 -a echo 'change for local' > a-darcs record --ignore-times -am 'change for local'+darcs record --skip-long-comment --ignore-times -am 'change for local' darcs push -a > log 2>&1 || : grep -q 'conflicts options to apply' log
− tests/obliterate-add.sh
@@ -1,19 +0,0 @@-#!/usr/bin/env bash--. lib--rm -rf temp1-mkdir temp1-cd temp1-darcs init-echo foo > foo-darcs add foo--darcs record -a -m 'addfoo'--darcs obliterate -a--not darcs whatsnew--cd ..-rm -rf temp1
− tests/obliterate-formerly-pl.sh
@@ -1,32 +0,0 @@-#!/usr/bin/env bash-# Some tests for 'darcs obliterate'--. lib--rm -rf temp1--# set up the repository-mkdir temp1-cd temp1-darcs init-cd ..--cd temp1-touch a.txt-darcs add a.txt-darcs record -a -m 'adding a' a.txt-touch b.txt-darcs add b.txt-darcs record -a -m 'adding b' b.txt-# extra confirmation for --all-echo an | darcs obliterate -p add | grep -i "really obliterate"-# --last=1-echo nyy | darcs obliterate --last 1 | grep -i adding-# automatically getting dependencies-date >> a.txt-darcs record -a -m 'modifying a' a.txt-echo ny | darcs obliterate -p 'adding a' > log-grep -i "modifying a" log-grep -i "No patches selected" log-cd ..-rm -rf temp1
tests/obliterate.sh view
@@ -1,29 +1,24 @@ #!/usr/bin/env bash . ./lib -rm -rf tempA-mkdir tempA-cd tempA-darcs initialize-echo hello world > foo-darcs add foo-darcs record -a -m hellofoo+# Part 1 +rm -rf temp1+darcs initialize temp1+cd temp1++echo hello world > foo+darcs record -l -a -m hellofoo echo goodbye world >> foo darcs record -a -m goodbyefoo- darcs replace world bar foo echo Hi there foo > bar-darcs add bar-darcs record -a -m addbar-+darcs record -l -a -m addbar darcs mv bar baz darcs replace bar baz foo darcs record -a -m bar2baz- echo Do not love the baz, or anything in the baz. >> foo darcs record -a -m nolove- darcs mv baz world darcs replace baz world foo darcs record -a -m baz2world@@ -47,5 +42,65 @@ grep 'love' foo && exit 1 || true cd ..-rm -rf tempA+rm -rf temp1 +# Part 2++darcs init temp1+cd temp1++touch a.txt+darcs add a.txt+darcs record -a -m 'adding a' a.txt+touch b.txt+darcs add b.txt+darcs record -a -m 'adding b' b.txt+# extra confirmation for --all+echo an | darcs obliterate -p add | grep -i "really obliterate"+# --last=1+echo nyy | darcs obliterate --last 1 | grep -i adding+# automatically getting dependencies+date >> a.txt+darcs record -a -m 'modifying a' a.txt+echo ny | darcs obliterate -p 'adding a' > log+grep -i "modifying a" log+grep -i "No patches selected" log+cd ..+rm -rf temp1++# Part 3++darcs init temp1+cd temp1++echo foo > foo+darcs record -l -a -m 'addfoo'++darcs obliterate -a++not darcs whatsnew++cd ..+rm -rf temp1++# Part 4++darcs init temp1+cd temp1++cat > f <<EOF+one+two+three+EOF+darcs rec -Ax -alm init+cp f g+cat > f <<EOF+three+one+EOF+darcs rec -Ax -am foo+echo yy | darcs unpull -p foo+cmp f g+cd ..+rm -rf temp1
tests/optimize.sh view
@@ -14,3 +14,11 @@ cd .. rm -rf temp1++## issue2388 - optimize fails if no patches have been recorded++darcs init temp1+cd temp1+darcs optimize clean+cd ..+rm -rf temp1
tests/patch-index-creation.sh view
@@ -36,27 +36,27 @@ darcs init --repo R cd R-darcs show patch-index-status | grep 'Patch Index is not yet created.' # init+darcs show patch-index | grep 'Patch Index is not yet created.' # init cd .. rm -rf R darcs init --repo R --with-patch-index cd R-darcs show patch-index-status | grep 'Patch Index is in sync with repo.' # init --patch-index+darcs show patch-index | grep 'Patch Index is in sync with repo.' # init --patch-index cd .. darcs clone R S cd S-darcs show patch-index-status | grep 'Patch Index is not yet created.' # clone+darcs show patch-index | grep 'Patch Index is not yet created.' # clone cd .. rm -rf S darcs clone R S --with-patch-index cd S-darcs show patch-index-status | grep 'Patch Index is in sync with repo.' # clone --patch-index+darcs show patch-index | grep 'Patch Index is in sync with repo.' # clone --patch-index cd .. @@ -64,7 +64,7 @@ gunzip -c $TESTDATA/simple-v1.tgz | tar xf - echo 'I understand the consequences of my action' | darcs convert darcs-2 repo repo2 cd repo2-darcs show patch-index-status | grep 'Patch Index is not yet created.' # convert+darcs show patch-index | grep 'Patch Index is not yet created.' # convert cd .. rm -rf repo repo2@@ -72,7 +72,7 @@ gunzip -c $TESTDATA/simple-v1.tgz | tar xf - echo 'I understand the consequences of my action' | darcs convert darcs-2 repo repo2 --with-patch-index cd repo2-darcs show patch-index-status | grep 'Patch Index is in sync with repo.' # convert --patch-index+darcs show patch-index | grep 'Patch Index is in sync with repo.' # convert --patch-index cd ../R @@ -81,19 +81,19 @@ darcs record -lam "Change d/f" rm -rf _darcs/patch_index darcs annotate d/f-darcs show patch-index-status | grep 'Patch Index is in sync with repo.' # annotate+darcs show patch-index | grep 'Patch Index is in sync with repo.' # annotate rm -rf _darcs/patch_index darcs annotate d/f --no-patch-index-darcs show patch-index-status | grep 'Patch Index is not yet created.' # annotate --no-patch-index+darcs show patch-index | grep 'Patch Index is not yet created.' # annotate --no-patch-index rm -rf _darcs/patch_index darcs log -a d/f-darcs show patch-index-status | grep 'Patch Index is in sync with repo.' # log -a file+darcs show patch-index | grep 'Patch Index is in sync with repo.' # log -a file rm -rf _darcs/patch_index darcs log -a d/f --no-patch-index-darcs show patch-index-status | grep 'Patch Index is not yet created.' # log -a file --no-patch-index+darcs show patch-index | grep 'Patch Index is not yet created.' # log -a file --no-patch-index cd .. rm -rf R S repo repo2
tests/patch-index-enabled-and-disabled.sh view
@@ -48,14 +48,14 @@ darcs changes f | grep 'older darcs' # Now, check: Did the patch-index self heal?-darcs show patch-index-status | grep 'Patch Index is in sync with repo.'+darcs show patch-index | grep 'Patch Index is in sync with repo.' # Another check, just to be sure. echo 'Example content updated again.' > f darcs record -lam 'Another update by newer darcs' # Still good?-darcs show patch-index-status | grep 'Patch Index is in sync with repo.'+darcs show patch-index | grep 'Patch Index is in sync with repo.' # Clean up. cd ..
tests/patch-index-rename.sh view
@@ -34,5 +34,5 @@ darcs mv file1 file2 darcs rec -am "move" -darcs show patch-index-test+darcs show patch-index --verbose
tests/patch-index-spans.sh view
@@ -31,7 +31,7 @@ mkdir d e # Change the working tree. echo 'Example content.' > d/f darcs record -lam 'Add d/f and e.'-darcs show patch-index-all > pi+darcs show patch-index --verbose > pi grep -F "./d -> 1#./d" pi grep -F "./d/f -> 1#./d/f" pi@@ -42,7 +42,7 @@ darcs mv d/f e/ darcs record -am 'Move d/f to e/f.'-darcs show patch-index-all > pi+darcs show patch-index --verbose > pi grep -F "./d -> 1#./d" pi grep -F "./d/f -> 1#./d/f" pi@@ -55,7 +55,7 @@ echo 'File 2' > d/f darcs record -lam 'Add d/f'-darcs show patch-index-all > pi+darcs show patch-index --verbose > pi grep -F "./d -> 1#./d" pi grep -F "./d/f -> 2#./d/f" pi
tests/patch-index-sync.sh view
@@ -29,42 +29,42 @@ darcs init --repo R --with-patch-index darcs init --repo S --with-patch-index cd R-darcs show patch-index-status | grep 'Patch Index is in sync with repo.' # test init+darcs show patch-index | grep 'Patch Index is in sync with repo.' # test init mkdir d e echo 'Example content.' > d/f darcs record -lam 'Add d/f and e.'-darcs show patch-index-status | grep 'Patch Index is in sync with repo.' # test record+darcs show patch-index | grep 'Patch Index is in sync with repo.' # test record echo 'New line.' >> d/f echo "y" | darcs amend-record -p 'Add d/f and e.' -a-darcs show patch-index-status | grep 'Patch Index is in sync with repo.' # test amend-record+darcs show patch-index | grep 'Patch Index is in sync with repo.' # test amend-record darcs push -a ../S cd ../S-darcs show patch-index-status | grep 'Patch Index is in sync with repo.' # test push+darcs show patch-index | grep 'Patch Index is in sync with repo.' # test push echo 'Changed Content' > d/f darcs record -am 'Change d/f'-darcs show patch-index-status | grep 'Patch Index is in sync with repo.' # test record 2+darcs show patch-index | grep 'Patch Index is in sync with repo.' # test record 2 darcs send -ao test.dpatch ../R cd .. darcs clone R T --with-patch-index cd T-darcs show patch-index-status | grep 'Patch Index is in sync with repo.' # test clone+darcs show patch-index | grep 'Patch Index is in sync with repo.' # test clone darcs pull -a ../S-darcs show patch-index-status | grep 'Patch Index is in sync with repo.' # test pull+darcs show patch-index | grep 'Patch Index is in sync with repo.' # test pull darcs obliterate -p 'Add d/f and e.' -a-darcs show patch-index-status | grep 'Patch Index is in sync with repo.' # test obliterate+darcs show patch-index | grep 'Patch Index is in sync with repo.' # test obliterate cd ../R darcs apply -a ../S/test.dpatch-darcs show patch-index-status | grep 'Patch Index is in sync with repo.' # test apply+darcs show patch-index | grep 'Patch Index is in sync with repo.' # test apply darcs unrecord -a -p 'Change d/f'-darcs show patch-index-status | grep 'Patch Index is in sync with repo.' # test unrecord+darcs show patch-index | grep 'Patch Index is in sync with repo.' # test unrecord darcs tag -m 'tag R'-darcs show patch-index-status | grep 'Patch Index is in sync with repo.' # test tag+darcs show patch-index | grep 'Patch Index is in sync with repo.' # test tag cd .. rm -rf R S T
− tests/pull-union.sh
@@ -1,42 +0,0 @@-#!/usr/bin/env bash--. lib--# This test script is in the public domain.---rm -rf temp1 temp2 temp3--mkdir temp1-cd temp1-darcs initialize-echo A > A-darcs add A-darcs record -a -m A-echo B > B-darcs add B-darcs record -a -m B--cd ..-darcs get temp1 temp2--cd temp2-darcs obliterate --last 1 -a-echo C > C-darcs add C-darcs record -a -m C-cd ..--mkdir temp3-cd temp3-darcs init-darcs pull -a -v ../temp1 ../temp2--darcs changes > out-cat out-grep A out-grep B out-grep C out-cd ..--rm -rf temp1 temp2 temp3
tests/pull.sh view
@@ -4,23 +4,21 @@ rm -rf temp1 temp2 -mkdir temp1+darcs init temp1 cd temp1-darcs init cd ..-mkdir temp2+darcs init temp2 cd temp2-darcs init mkdir one cd one mkdir two cd two-echo darcs pull should work relative to the current directory+# darcs pull should work relative to the current directory darcs pull -a ../../../temp1 | grep -i 'No remote patches to pull in' -echo -- darcs pull should pull into repo specified with --repo+# darcs pull should pull into repo specified with --repo cd ../.. # now in temp2 darcs add one; darcs record --name uno --all@@ -42,7 +40,6 @@ chmod a-rwx ./temp1/one # remove all permissions not darcs pull --repodir ./temp1 -a 2> err chmod u+rwx temp1/one # restore permission- cat err grep 'permission denied' err rm -rf temp1/one fi@@ -55,11 +52,9 @@ #return special message when you try to pull from yourself DIR="`pwd`" not darcs pull --debug -a "$DIR" 2> out-cat out grep 'Can.t pull from current repository' out not darcs pull --debug -a . 2> out-cat out grep 'Can.t pull from current repository' out # and do not update the default repo to be the current di@@ -73,7 +68,6 @@ grep 'Can.t pull from current repository' err not darcs pull --debug ../* 2> out-cat out not grep 'Can.t pull from current repository' out cd .. # now outside of any repo @@ -121,7 +115,6 @@ cd ../temp2 mkdir newdir darcs pull -a --set-default ../temp1 &> out2-cat out grep Backing out2 grep 'Finished pulling' out2 grep newdir out2@@ -130,26 +123,200 @@ rm -rf temp1 temp2 -# A test for issue662, which triggered:+# issue662, which triggered: # darcs failed: Error applying hunk to file ./t.t # Error applying patch to the working directory. -rm -rf tmp;-darcs init --darcs-1 --repodir=tmp-touch tmp/t.t-cd tmp-darcs add t.t-echo 'content'>t.t-darcs record -am 'initial add' --ignore-echo 'content: remote change'>t.t+darcs init temp1+cd temp1++touch t.t+echo 'content'> t.t+darcs record -lam 'initial add'+echo 'content: remote change'> t.t darcs record -am 'remote change' --ignore-darcs get . tmp2-cd tmp2-darcs obliterate --last 1 --all;+cd ..+darcs clone temp1 temp2+cd temp2+darcs obliterate --last 1 --all echo 'content: local change'> t.t-darcs pull -a ../+darcs pull -a ../temp1 darcs w -s darcs revert -a+cd ..+rm -rf temp1 temp2 -cd ../..-rm -rf tmp+# pull with conflicts++darcs initialize temp1+cd temp1+echo foo > bar+darcs record -lam addbar++cd ..+darcs clone temp1 temp2+cd temp1+date > bar+darcs record -a -m datebar+cd ../temp2+echo aack >> bar+darcs record -a -m aackbar+darcs pull -a+darcs check++cd ..+rm -rf temp1 temp2++# pull --union++rm -rf temp1 temp2 temp3++darcs init temp1+cd temp1+echo A > A+darcs record -lam A+echo B > B+darcs record -lam B++cd ..+darcs clone temp1 temp2++cd temp2+darcs obliterate --last 1 -a+echo C > C+darcs record -lam C+cd ..++darcs init temp3+cd temp3+darcs pull -a ../temp1 ../temp2++darcs log > out+grep A out+grep B out+grep C out++cd ..+rm -rf temp1 temp2 temp3++# pull --intersection++darcs init temp1+cd temp1+echo A > A+darcs record -lam Aismyname+echo B > B+darcs record -lam Bismyname++cd ..+darcs clone temp1 temp2+cd temp2+darcs obliterate --last 1 -a+echo C > C+darcs record -lam Cismyname++cd ..+darcs init temp3+cd temp3+darcs pull -a --intersection ../temp1 ../temp2+darcs log > out+grep Aismyname out+not grep Bismyname out+not grep Cismyname out++cd ..+rm -rf temp1 temp2 temp3+++# pull --skip-conflicts+rm -rf R S+darcs init R+cd R+echo 'foo' > foo+echo 'bar' > bar+darcs rec -lam 'Add foo and bar'+cd ..++darcs clone R S++cd R+echo 'foo2' > foo+darcs rec -lam 'Change foo (2)'+echo 'bar2' > bar+darcs rec -lam 'Change bar (2)'+cd ..++cd S+echo 'foo3' > foo+darcs rec -lam 'Change foo (3)'+darcs pull --skip-conflicts -a ../R+test `darcs log --count` -eq 3+cd ..++cd S+darcs pull -a ../R+test `darcs log --count` -eq 4+cd ..+rm -rf R S++# bad pending after pull++rm -fr temp1 temp2++darcs init temp1+cd temp1+echo abc > A+echo def > B1+darcs record -lam patch1+darcs mv B1 B2+darcs record -am patch2+cd ..++darcs init temp2+cd temp2+darcs pull -a ../temp1+not darcs whatsnew+cd ..++rm -fr temp1 temp2++# issue494: note that in this test, we deliberately select filenames+# with a backwards sorting order+darcs init temp1+cd temp1+echo abc > b+darcs record -lam patch1+darcs mv b a+echo def > a+darcs record -am patch2+cd ..++darcs init temp2+cd temp2+darcs pull --all ../temp1+not darcs whatsnew+cd ..++rm -fr temp1 temp2++# pull binary++rm -rf temp1 temp2++darcs init temp1+cd temp1+printf "%01048576d" 0 > foo+darcs record -l -a -m xx+rm foo+darcs record -a -m yy+cd ..++darcs init temp2+cd temp2+echo yny | darcs pull --set-default ../temp1+rm foo+darcs pull -a+cd ..++rm -rf temp1 temp2+
− tests/pull_binary.sh
@@ -1,50 +0,0 @@-#!/usr/bin/env bash--# This test script, originally written by David Roundy and Ian Lynagh is in-# the public domain.-#-# This file is included as part of the Darcs test distribution,-# which is licensed to you under the following terms:-#-## 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 temp1 temp2--mkdir temp1-cd temp1-darcs init-printf "%01048576d" 0 > foo-darcs record -l -a -A author -m xx-rm foo-darcs record -a -A author -m yy-cd ..--mkdir temp2-cd temp2-darcs init-echo yny | darcs pull --set-default ../temp1-rm foo-darcs pull -a-cd ..--rm -rf temp1 temp2
− tests/pull_compl.sh
@@ -1,145 +0,0 @@-#!/usr/bin/env bash--## Public domain 2007 Kevin Quick-##-## This file is included as part of the Darcs test distribution,-## which is licensed to you under the following terms:-##-## 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 temp1 temp2 temp3 temp4 temp5--mkdir temp1-cd temp1-cat > foo <<EOF-line1-line2-line3-line4-line5-line6-line7-EOF-darcs initialize-darcs add foo-darcs record -a -m addfoo--cd ..-darcs get temp1 temp4--chgrec () {- set -ev- sed -e "$1" foo > foo.tmp- mv foo.tmp foo- darcs record -a --ignore-times -m "$2"-}--cd temp1-chgrec 's/line2/line2\nline2.1\nline2.2/' inssub2-chgrec 's/line4/Line 4/' Line4--darcs changes | grep ' \*'-echo done with changes on temp1 > /dev/null--cd ..-darcs get temp1 temp2-darcs get temp1 temp3-cd temp1--chgrec 's/line1/line0\nline1/' line0-chgrec 's/Line 4/LINE FOUR/' LINE4-chgrec 's/line7/line7\nLastLine/' LastLine-chgrec 's/LINE FOUR/LINE FOUR\nline4.1/' line4.1--darcs changes | grep ' \*'-echo done with changes on temp1 > /dev/null--cd ../temp3-darcs pull -p LastLine -av-chgrec 's/line1$/FirstLine/' FirstLine--cd ../temp4--darcs changes | grep ' \*'-echo done with changes on temp4 > /dev/null--darcs pull ../temp1 --dry-run | grep ' \*'-darcs pull ../temp1 --dry-run | grep ' \*' > p1.out-cat > p1.req <<EOF- * inssub2- * Line4- * line0- * LINE4- * LastLine- * line4.1-EOF-diff p1.req p1.out--darcs pull ../temp1 --dry-run --complement | grep ' \*' > p2.out-diff p1.out p2.out--darcs pull --dry-run --complement ../temp1 ../temp2 | grep ' \*' > p3.out-cat > p3.req <<EOF- * line0- * LastLine-EOF-diff p3.req p3.out--darcs pull --dry-run --complement ../temp1 ../temp3 | grep ' \*' > p4.out-cat > p4.req <<EOF- * line0-EOF-diff p3.req p3.out--darcs pull --dry-run --complement ../temp1 ../temp2 ../temp3 | grep ' \*' > p5.out-diff p4.out p5.out--darcs pull --dry-run --complement ../temp1 ../temp2 ../temp3 ../temp2 ../temp2 ../temp3 ../temp3 ../temp2 | grep ' \*' > p6.out-diff p4.out p6.out--darcs pull --dry-run --complement ../temp3 ../temp2 | grep ' \*' > p7.out-cat > p7.req <<EOF- * LastLine-EOF--darcs pull --dry-run --complement ../temp2 ../temp3 > p8.out-grep "No remote patches to pull in!" p8.out--# because duplicates are stripped before performing action,-# this is the same as: darcs pull ../temp1-darcs pull --dry-run --complement ../temp1 ../temp1 > fooout-cat fooout-grep ' \*' fooout > p9.out-diff p1.req p9.out--# so the "null" pull must be tested this way:-darcs get ../temp1 ../temp5-darcs pull --dry-run --complement ../temp1 ../temp5 > p9.out-grep "No remote patches to pull in!" p9.out--darcs pull -av --complement ../temp1 ../temp3-darcs check--cd ..-rm -rf temp1 temp2 temp3 temp4 temp5
+ tests/pull_complement.sh view
@@ -0,0 +1,145 @@+#!/usr/bin/env bash++## Public domain 2007 Kevin Quick+##+## This file is included as part of the Darcs test distribution,+## which is licensed to you under the following terms:+##+## 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 temp1 temp2 temp3 temp4 temp5++mkdir temp1+cd temp1+cat > foo <<EOF+line1+line2+line3+line4+line5+line6+line7+EOF+darcs initialize+darcs add foo+darcs record -a -m addfoo++cd ..+darcs get temp1 temp4++chgrec () {+ set -ev+ sed -e "$1" foo > foo.tmp+ mv foo.tmp foo+ darcs record -a --ignore-times -m "$2"+}++cd temp1+chgrec 's/line2/line2\nline2.1\nline2.2/' inssub2+chgrec 's/line4/Line 4/' Line4++darcs changes | grep ' \*'+echo done with changes on temp1 > /dev/null++cd ..+darcs get temp1 temp2+darcs get temp1 temp3+cd temp1++chgrec 's/line1/line0\nline1/' line0+chgrec 's/Line 4/LINE FOUR/' LINE4+chgrec 's/line7/line7\nLastLine/' LastLine+chgrec 's/LINE FOUR/LINE FOUR\nline4.1/' line4.1++darcs changes | grep ' \*'+echo done with changes on temp1 > /dev/null++cd ../temp3+darcs pull -p LastLine -av+chgrec 's/line1$/FirstLine/' FirstLine++cd ../temp4++darcs changes | grep ' \*'+echo done with changes on temp4 > /dev/null++darcs pull ../temp1 --dry-run | grep ' \*'+darcs pull ../temp1 --dry-run | grep ' \*' > p1.out+cat > p1.req <<EOF+ * inssub2+ * Line4+ * line0+ * LINE4+ * LastLine+ * line4.1+EOF+diff p1.req p1.out++darcs pull ../temp1 --dry-run --complement | grep ' \*' > p2.out+diff p1.out p2.out++darcs pull --dry-run --complement ../temp1 ../temp2 | grep ' \*' > p3.out+cat > p3.req <<EOF+ * line0+ * LastLine+EOF+diff p3.req p3.out++darcs pull --dry-run --complement ../temp1 ../temp3 | grep ' \*' > p4.out+cat > p4.req <<EOF+ * line0+EOF+diff p3.req p3.out++darcs pull --dry-run --complement ../temp1 ../temp2 ../temp3 | grep ' \*' > p5.out+diff p4.out p5.out++darcs pull --dry-run --complement ../temp1 ../temp2 ../temp3 ../temp2 ../temp2 ../temp3 ../temp3 ../temp2 | grep ' \*' > p6.out+diff p4.out p6.out++darcs pull --dry-run --complement ../temp3 ../temp2 | grep ' \*' > p7.out+cat > p7.req <<EOF+ * LastLine+EOF++darcs pull --dry-run --complement ../temp2 ../temp3 > p8.out+grep "No remote patches to pull in!" p8.out++# because duplicates are stripped before performing action,+# this is the same as: darcs pull ../temp1+darcs pull --dry-run --complement ../temp1 ../temp1 > fooout+cat fooout+grep ' \*' fooout > p9.out+diff p1.req p9.out++# so the "null" pull must be tested this way:+darcs get ../temp1 ../temp5+darcs pull --dry-run --complement ../temp1 ../temp5 > p9.out+grep "No remote patches to pull in!" p9.out++darcs pull -av --complement ../temp1 ../temp3+darcs check++cd ..+rm -rf temp1 temp2 temp3 temp4 temp5
− tests/pull_conflicts.sh
@@ -1,57 +0,0 @@-#!/usr/bin/env bash-## Test that pull --skip-conflicts filters the conflicts-## appropriately.-##-## 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.--. lib # Load some portability helpers.-rm -rf R S # Another script may have left a mess.--mkdir R-cd R-darcs init-echo 'foo' > foo-echo 'bar' > bar-darcs rec -lam 'Add foo and bar'-cd ..--darcs get R S--cd R-echo 'foo2' > foo-darcs rec -lam 'Change foo (2)'-echo 'bar2' > bar-darcs rec -lam 'Change bar (2)'-cd ..--cd S-echo 'foo3' > foo-darcs rec -lam 'Change foo (3)'-darcs pull --skip-conflicts -a ../R-test `darcs changes --count` -eq 3-cd ..--cd S-darcs pull -a ../R-test `darcs changes --count` -eq 4-cd ..
− tests/pull_two.sh
@@ -1,34 +0,0 @@-#!/usr/bin/env bash--# This test script, originally written by David Roundy is in the public-# domain.--. ./lib--rm -rf temp1 temp2--mkdir temp1-cd temp1-echo foo > bar-darcs initialize-echo record author me > _darcs/prefs/defaults-darcs add bar-darcs record -a -m addbar--cd ..-darcs get temp1 temp2-cd temp1-date > bar-darcs record -a -m datebar--cd ../temp1-echo aack >> bar-darcs record -a -m aackbar--cd ../temp2--darcs pull -av-darcs check--cd ..-rm -rf temp1 temp2
− tests/push-dont-prompt-deps.sh
@@ -1,32 +0,0 @@-#!/usr/bin/env bash--. ./lib--# Check that the right patches get pushed using --dont-prompt-for-dependencies--rm -rf temp1-rm -rf temp2-mkdir temp2-mkdir temp1-cd temp2-darcs init-cd ..-cd temp1-darcs init-echo foo > f-darcs record -Ax -alm foo1-echo bar > b-darcs rec -Ax -alm bar1-echo foo2 > f-darcs record -Ax -alm foo2-echo bar2 > b-darcs record -Ax -alm bar2-echo yy | 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-not grep bar toto-cd ..-rm -rf temp1 temp2
tests/push-dry-run.sh view
@@ -4,16 +4,11 @@ # For issue855: wish: avoid taking lock if using --dry-run chmod -R u+w temp2 || : rm -rf temp1 temp2-mkdir temp1-cd temp1-darcs init-cd ..-mkdir temp2+darcs init temp1+darcs init temp2 cd temp2-darcs init touch x-darcs add x-darcs record -am "test"+darcs record -lam test cd .. chmod -R u-w temp2 cd temp2
− tests/push-formerly-pl.sh
@@ -1,57 +0,0 @@-#!/usr/bin/env bash--# Some tests for 'darcs push'--. lib--slash() {-if echo $OS | grep -q -i windows; then- echo -n \\-else- echo -n /-fi-}--DIR="`pwd`"--rm -rf temp1 temp2-mkdir temp1-cd temp1-darcs init-cd ..-mkdir temp2-cd temp2-darcs init-cd ..--# push without a repo gives an error-cd temp1-not darcs push -p 123 2> log-grep -i 'missing argument' log-cd ..--mkdir -p temp2/one/two-cd temp2/one/two-# darcs push should work relative to the current directory-darcs push -a ../../../temp1 | grep -i 'No recorded local patches to push'-cd ../../../--# darcs push should push into repo specified with --repo-cd temp2-darcs add one-darcs record --name uno --all-cd ..--darcs push --repodir temp2 --all temp1 | grep -i 'Finished apply'--cd temp1-# Before trying to pull from self, defaultrepo does not exist-test ! -e _darcs/prefs/defaultrepo-# return special message when you try to push to yourself-not darcs push -a "$DIR`slash`temp1" 2> log-grep -i "cannot push from repository to itself" log-# and don't update the default repo to be the current dir-test ! -e _darcs/prefs/defaultrepo-cd ..--rm -rf temp1 temp2
tests/push.sh view
@@ -1,21 +1,101 @@ #!/usr/bin/env bash-. ./lib -rm -rf temp temp_0-mkdir temp-cd temp+# Some tests for 'darcs push'++. lib++slash() {+if echo $OS | grep -q -i windows; then+ echo -n \\+else+ echo -n /+fi+}++DIR="`pwd`"++rm -rf temp1 temp2+mkdir temp1+cd temp1 darcs init-echo tester > _darcs/prefs/author-date > bla-darcs add bla-darcs record -a --name=11 cd ..-darcs get temp-cd temp-date > bla2-darcs add bla2-darcs record -a --name=22-darcs push -a ../temp_0+mkdir temp2+cd temp2+darcs init cd .. -rm -rf temp temp_0+# push without a repo gives an error+cd temp1+not darcs push -p 123 2> log+grep -i 'missing argument' log+cd ..++mkdir -p temp2/one/two+cd temp2/one/two+# darcs push should work relative to the current directory+darcs push -a ../../../temp1 | grep -i 'No recorded local patches to push'+cd ../../../++# darcs push should push into repo specified with --repo+cd temp2+darcs add one+darcs record --name uno --all+cd ..++darcs push --repodir temp2 --all temp1 | grep -i 'Finished apply'++cd temp1+# Before trying to pull from self, defaultrepo does not exist+test ! -e _darcs/prefs/defaultrepo+# return special message when you try to push to yourself+not darcs push -a "$DIR`slash`temp1" 2> log+grep -i "cannot push from repository to itself" log+# and don't update the default repo to be the current dir+test ! -e _darcs/prefs/defaultrepo+cd ..++rm -rf temp1 temp2++# Check that the right patches get pushed using --dont-prompt-for-dependencies++rm -rf temp1 temp2+darcs init temp2+darcs init temp1++cd temp1+echo foo > f+darcs record -alm foo1+echo bar > b+darcs rec -alm bar1+echo foo2 > f+darcs record -alm foo2+echo bar2 > b+darcs record -alm bar2+echo yy | 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+not grep bar toto+cd ..+rm -rf temp1 temp2++# For issue257: push => incorrect return code when couldn't get lock++rm -rf temp1 temp2+darcs init temp1+cd temp1+echo foo > foo.c+darcs rec -alm init+cd ..+darcs clone temp1 temp2+cd temp2+echo server >> foo.c+darcs rec -alm server+cd ../temp1+echo client >> foo.c+darcs rec -alm client+not darcs push -a ../temp2+cd ..+rm -rf temp1 temp2
− tests/push_conflicts.sh
@@ -1,59 +0,0 @@-#!/usr/bin/env bash-## Test that apply --skip-conflicts filters the conflicts-## appropriately.-##-## 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.--. lib # Load some portability helpers.-rm -rf R S # Another script may have left a mess.--mkdir R-cd R-darcs init-echo 'foo' > foo-echo 'bar' > bar-darcs rec -lam 'Add foo and bar'-cd ..--darcs get R S--cd R-echo 'foo2' > foo-darcs rec -lam 'Change foo (2)'-echo 'bar2' > bar-darcs rec -lam 'Change bar (2)'-cd ..--cd S-echo 'foo3' > foo-darcs rec -lam 'Change foo (3)'-cd ..--cd R-darcs send -a ../S -o ../S/applyme.dpatch-cd ..--cd S-darcs apply --skip-conflicts applyme.dpatch-test `darcs changes --count` -eq 3-cd ..
− tests/push_lock.sh
@@ -1,26 +0,0 @@-#!/usr/bin/env bash--# For issue257: push => incorrect return code when couldn't get lock--. ./lib--rm -rf tempc-mkdir tempc-cd tempc-darcs init-echo foo > foo.c-darcs rec -Ax -alm init-cd ..-rm -rf temps-darcs get tempc temps-cd temps-echo server >> foo.c-darcs rec -Ax -alm server-cd ../tempc-echo client >> foo.c-darcs rec -Ax -alm client-if darcs push -a ../temps; then- false-fi-cd ..-rm -rf tempc temps
− tests/query_manifest.sh
@@ -1,91 +0,0 @@-#!/usr/bin/env bash-. ./lib--check_manifest () {- : > files.tmp- echo . > dirs.tmp- echo . > files-dirs.tmp- for x in $1 ; do- echo "./$x" >> files.tmp- echo "./$x" >> files-dirs.tmp- done- for x in $2 ; do- echo "./$x" >> dirs.tmp- echo "./$x" >> files-dirs.tmp- done- 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 | 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-mkdir temp-cd temp-darcs init--check_manifest "" "" "--no-pending"-check_manifest "" "" "--pending"-touch a b-darcs add a-check_manifest "" "" "--no-pending"-check_manifest "a" "" "--pending"-darcs add b-mkdir c-check_manifest "" "" "--no-pending"-check_manifest "a b" "" "--pending"-darcs add c-touch c/1 c/2-check_manifest "" "" "--no-pending"-check_manifest "a b" "c" "--pending"-darcs add c/1 c/2-check_manifest "" "" "--no-pending"-check_manifest "a b c/1 c/2" "c" "--pending"-mkdir d-touch d/3 d/4-darcs add d/3 d/4-check_manifest "" "" "--no-pending"-check_manifest "a b c/1 c/2 d/3 d/4" "c d" "--pending"-darcs record -A test --all --name "patch 1" --skip-long-comment-check_manifest "a b c/1 c/2 d/3 d/4" "c d" "--no-pending"-check_manifest "a b c/1 c/2 d/3 d/4" "c d" "--pending"--darcs mv d e-check_manifest "a b c/1 c/2 d/3 d/4" "c d" "--no-pending"-check_manifest "a b c/1 c/2 e/3 e/4" "c e" "--pending"-rm c/1-check_manifest "a b c/1 c/2 d/3 d/4" "c d" "--no-pending"-check_manifest "a b c/1 c/2 e/3 e/4" "c e" "--pending"-darcs remove c/1-check_manifest "a b c/1 c/2 d/3 d/4" "c d" "--no-pending"-check_manifest "a b c/2 e/3 e/4" "c e" "--pending"-darcs mv c/2 c/1-check_manifest "a b c/1 c/2 d/3 d/4" "c d" "--no-pending"-check_manifest "a b c/1 e/3 e/4" "c e" "--pending"-darcs record -A test --all --name "patch 2" --skip-long-comment-check_manifest "a b c/1 e/3 e/4" "c e" "--no-pending"-check_manifest "a b c/1 e/3 e/4" "c e" "--pending"--darcs remove c/1-check_manifest "a b c/1 e/3 e/4" "c e" "--no-pending"-check_manifest "a b e/3 e/4" "c e" "--pending"-darcs remove c-check_manifest "a b c/1 e/3 e/4" "c e" "--no-pending"-check_manifest "a b e/3 e/4" "e" "--pending"-darcs record -A test --all --name "patch 3" --skip-long-comment-check_manifest "a b e/3 e/4" "e" "--no-pending"-check_manifest "a b e/3 e/4" "e" "--pending"--darcs mv b b2-darcs mv b2 b3-check_manifest "a b e/3 e/4" "e" "--no-pending"-check_manifest "a b3 e/3 e/4" "e" "--pending"-darcs record -A test --all --name "patch 3" --skip-long-comment-check_manifest "a b3 e/3 e/4" "e" "--no-pending"-check_manifest "a b3 e/3 e/4" "e" "--pending"--cd ..-rm -rf temp
tests/rebase-nochanges.sh view
@@ -54,6 +54,7 @@ * add wobble Making no changes: this is a dry run.+Rebase in progress: 1 suspended patches EOF darcs unpull --dry-run | tail -n+5 | grep -v tester | diff -u expected -
+ tests/rebase-warns-lost-deps.sh view
@@ -0,0 +1,54 @@+#!/usr/bin/env bash+##+## Check that lost explicit dependencies are reported on during rebase+##+## Copyright (C) 2015 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.++. lib # Load some portability helpers.++rm -rf t1+mkdir t1+cd t1+darcs init++echo 'A' > A+darcs add A+darcs rec -am"patch A" --ignore-times++echo 'B' > B+darcs add B+darcs rec -am"patch B" --ignore-times++echo 'C' > C+darcs add C+echo 'yyy' | darcs rec -am"patch C" --ignore-times --ask-deps++echo 'ydy' | darcs rebase suspend++darcs unpull -a -p 'patch A'++echo 'ydy' | darcs rebase unsuspend > unsuspend-output.txt++grep "following explicit dependency" unsuspend-output.txt+grep "patch A" unsuspend-output.txt+not grep "patch B" unsuspend-output.txt
− tests/record-interactive.sh
@@ -1,27 +0,0 @@-#!/usr/bin/env bash--. lib---rm -rf temp1-mkdir temp1-cd temp1-darcs init--touch foo-darcs add foo-darcs record -a -m addfoo--darcs replace one two foo-darcs replace three four foo-darcs replace five six foo--echo sa | darcs record -m cancelled--darcs whatsnew--darcs changes > ch-not grep cancelled ch--cd ..-rm -rf temp1
− tests/record-misc.sh
@@ -1,61 +0,0 @@-#!/usr/bin/env bash--# Some tests for 'darcs record '--. lib--rm -rf temp1-mkdir temp1-cd temp1-darcs init--# issue308 - no patches and no deps for record should abort-darcs record -am foo --ask-deps | grep -i "Ok, if you don't want to record anything, that's fine!"--# RT#476 - --ask-deps works when there are no patches-if echo $OS | grep -i windows; then- echo This test does not work on Windows-else- touch t.f- darcs add t.f- darcs record -am add- echo a | darcs record -am foo --ask-deps | grep -i 'finished recording'-fi--# RT#231 - special message is given for nonexistent directories-not darcs record -am foo not_there.txt > log-grep -i 'not exist' log--# RT#231 - a nonexistent file before an existing file is handled correctly-touch b.t-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-darcs add date.t-darcs record -a -m foo "$DIR/date.t" | grep -i 'finished recording'--# issue396 - record -l ""-touch 'notnull.t'-darcs record -am foo -l "" notnull.t | grep -i 'finished recording'--# basic record-date >> date.t-darcs record -a -m basic_record date.t | grep -i 'finished recording'--# testing --logfile-date >> date.t-echo "second record\n" >> log.txt-darcs record -a -m 'second record' --logfile=log.txt date.t | grep -i 'finished recording'--# refuse empty patch name-export DARCS_EDITOR="cat -n"-date >> date.t-echo "patchname" | darcs record -a -m "" | grep WARNING-date >> date.t-darcs record -a -m "some name"--cd ..-rm -rf temp1
+ tests/record.sh view
@@ -0,0 +1,211 @@+#!/usr/bin/env bash++# Some tests for 'darcs record '++. lib++rm -rf temp1+mkdir temp1+cd temp1+darcs init++# issue308 - no patches and no deps for record should abort+darcs record -am foo --ask-deps | grep -i "Ok, if you don't want to record anything, that's fine!"++# RT#476 - --ask-deps works when there are no patches+if echo $OS | grep -i windows; then+ echo This test does not work on Windows+else+ touch t.f+ darcs add t.f+ darcs record -am add+ echo a | darcs record -am foo --ask-deps | grep -i 'finished recording'+fi++# RT#231 - special message is given for nonexistent directories+not darcs record -am foo not_there.txt > log 2>&1+grep -i 'non-existing' log++# RT#231 - a nonexistent file before an existing file is handled correctly+# test disabled, see tests/issue2494-output-of-record-with-file-arguments.sh+# which contains an updated test+# touch b.t+# 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+darcs add date.t+darcs record -a -m foo "$DIR/date.t" | grep -i 'finished recording'++# issue396 - record -l ""+touch 'notnull.t'+darcs record -am foo -l "" notnull.t | grep -i 'finished recording'++# basic record+date >> date.t+darcs record -a -m basic_record date.t | grep -i 'finished recording'++# testing --logfile+date >> date.t+echo "second record\n" >> log.txt+darcs record -a -m 'second record' --logfile=log.txt date.t | grep -i 'finished recording'++# refuse empty patch name+export DARCS_EDITOR="cat -n"+date >> date.t+echo "patchname" | darcs record -a -m "" | grep WARNING+date >> date.t+darcs record -a -m "some name"++cd ..+rm -rf temp1+++# record race++rm -rf foo1 foo2+mkdir foo1 foo2+cd foo1+darcs init+echo zig > foo+darcs add foo+sleep 1+darcs record -a -m add_foo -A x+#sleep 1+echo zag >> foo+darcs record --ignore-time -a -m mod_foo -A x+cd ../foo2+darcs init+darcs pull -a ../foo1+cd ..+cmp foo1/foo foo2/foo+rm -rf foo1 foo2++# record interactive+++rm -rf temp1+mkdir temp1+cd temp1+darcs init++touch foo+darcs add foo+darcs record -a -m addfoo++darcs replace one two foo+darcs replace three four foo+darcs replace five six foo++echo sa | darcs record -m cancelled++darcs whatsnew++darcs changes > ch+not grep cancelled ch++cd ..+rm -rf temp1+++# Some tests for 'darcs rec --edit-long-comment'++rm -rf temp1++export DARCS_EDITOR="cat -n"+# editor: space in command+mkdir temp1+cd temp1+darcs init+touch file.t+darcs add file.t+echo y | darcs record --edit-long-comment -a -m foo file.t | grep '# Please enter'+cd ..+rm -rf temp1++# editor: space in path+mkdir temp2\ dir+cd temp2\ dir+darcs init+touch file.t+darcs add file.t+echo y | darcs record --edit-long-comment -a -m foo file.t | grep '# Please enter'+cd ..+rm -rf temp2\ dir++# make sure summaries are coalesced+mkdir temp3+cd temp3+darcs init+cat > file <<EOF+1+2+3+4+EOF+darcs add file+darcs rec -a -m "init" file+cat > file <<EOF+A+2+3+B+EOF+echo y | darcs record --edit-long-comment -a -m edit | grep -c "./file" | grep 1+cd ..+rm -rf temp2\ dir++export DARCS_EDITOR='grep "# Please enter"'+# editor: quoting in command+mkdir temp1+cd temp1+darcs init+touch file.t+darcs add file.t+echo y | darcs record --edit-long-comment -a -m foo file.t | grep '# Please enter'+cd ..+rm -rf temp1++export DARCS_EDITOR='echo'+# editor: evil filename+darcs init temp1+cd temp1+touch file.t+darcs add file.t+touch '; test-command'+echo > test-command << FOO+#!/bin/sh+echo EVIL+FOO+chmod u+x test-command+echo y | darcs record --logfile='; test-command' --edit-long-comment -a -m foo file.t > log+not grep EVIL log+cd ..+rm -rf temp1++## Test for issue142 - darcs record --logfile foo should not++darcs init temp1+cd temp1+touch f g+touch log+darcs record -alm f --logfile log f+not darcs record -alm g --logfile missing g+cd ..+rm -rf temp1++## Test for issue1845 - darcs record f, for f a removed file should work+## Public domain - 2010 Petr Rockai++darcs init temp1+cd temp1++echo 'Example content.' > f+darcs rec -lam "first"+rm -f f+echo ny | darcs record f 2>&1 | tee log+not grep "None of the files" log+cd ..+rm -rf temp1
− tests/record_editor.sh
@@ -1,79 +0,0 @@-#!/usr/bin/env bash--# Some tests for 'darcs rec --edit-long-comment'--. lib--rm -rf temp1--export DARCS_EDITOR="cat -n"-# editor: space in command-mkdir temp1-cd temp1-darcs init-touch file.t-darcs add file.t-echo y | darcs record --edit-long-comment -a -m foo file.t | grep '# Please enter'-cd ..-rm -rf temp1--# editor: space in path-mkdir temp2\ dir-cd temp2\ dir-darcs init-touch file.t-darcs add file.t-echo y | darcs record --edit-long-comment -a -m foo file.t | grep '# Please enter'-cd ..-rm -rf temp2\ dir--# make sure summaries are coalesced-mkdir temp3-cd temp3-darcs init-cat > file <<EOF-1-2-3-4-EOF-darcs add file-darcs rec -a -m "init" file-cat > file <<EOF-A-2-3-B-EOF-echo y | darcs record --edit-long-comment -a -m edit | grep -c "./file" | grep 1-cd ..-rm -rf temp2\ dir--export DARCS_EDITOR='grep "# Please enter"'-# editor: quoting in command-mkdir temp1-cd temp1-darcs init-touch file.t-darcs add file.t-echo y | darcs record --edit-long-comment -a -m foo file.t | grep '# Please enter'-cd ..-rm -rf temp1--export DARCS_EDITOR='echo'-# editor: evil filename-mkdir temp1-cd temp1-darcs init-touch file.t-darcs add file.t-touch '; test-command'-echo > test-command << FOO-#!/bin/sh-echo EVIL-FOO-chmod u+x test-command-echo y | darcs record --logfile='; test-command' --edit-long-comment -a -m foo file.t > log-not grep EVIL log-cd ..-rm -rf temp1
− tests/recordrace.sh
@@ -1,21 +0,0 @@-#!/usr/bin/env bash-. ./lib--rm -rf foo1 foo2-mkdir foo1 foo2-cd foo1-darcs init-echo zig > foo-darcs add foo-sleep 1-darcs record -a -m add_foo -A x-#sleep 1-echo zag >> foo-darcs record --ignore-time -a -m mod_foo -A x-cd ../foo2-darcs init-darcs pull -a ../foo1-cd ..-cmp foo1/foo foo2/foo-rm -rf foo1 foo2-
tests/remove.sh view
@@ -48,3 +48,17 @@ not grep -i d/f/1.txt after.lst cd ..+rm -rf temp1++# issue1765 recursive remove on root++darcs init temp1+cd temp1++mkdir d e # Change the working tree.+echo 'Example content.' > d/f+darcs record -lam 'Add d/f and e.'+darcs remove * -r++cd ..+rm -rf temp1
tests/replace.sh view
@@ -59,3 +59,36 @@ cd .. rm -rf temp++# replace after pending add++mkdir temp1+cd temp1+darcs init++echo a b a b a b > A+darcs add A+if darcs replace a c A | grep Skipping; then+ exit 1+fi+cd ..++rm -fr temp1++# replace after pending mv++mkdir temp1+cd temp1+darcs init++echo a b a b a b > A+darcs add A+darcs record --all --name=p1+darcs mv A B+if darcs replace a c B | grep Skipping; then+ exit 1+fi+cd ..++rm -fr temp1+
− tests/replace_after_pending_add.sh
@@ -1,21 +0,0 @@-#!/usr/bin/env bash--. ./lib--rm -fr temp1--mkdir temp1-cd temp1-darcs --version--darcs init--echo a b a b a b > A-darcs add A-if darcs replace a c A | grep Skipping; then- exit 1-fi-cd ..--rm -fr temp1-
− tests/replace_after_pending_mv.sh
@@ -1,21 +0,0 @@-#!/usr/bin/env bash--. ./lib--rm -fr temp1--mkdir temp1-cd temp1-darcs init--echo a b a b a b > A-darcs add A-darcs record --all --name=p1-darcs mv A B-if darcs replace a c B | grep Skipping; then- exit 1-fi-cd ..--rm -fr temp1-
+ tests/show_files.sh view
@@ -0,0 +1,91 @@+#!/usr/bin/env bash+. ./lib++check_manifest () {+ : > files.tmp+ echo . > dirs.tmp+ echo . > files-dirs.tmp+ for x in $1 ; do+ echo "./$x" >> files.tmp+ echo "./$x" >> files-dirs.tmp+ done+ for x in $2 ; do+ echo "./$x" >> dirs.tmp+ echo "./$x" >> files-dirs.tmp+ done+ darcs show files $3 --files --no-directories > darcsraw-files.tmp+ darcs show files $3 --no-files --directories > darcsraw-dirs.tmp+ darcs show files $3 --files --directories > darcsraw-files-dirs.tmp+ for x in files dirs files-dirs ; do+ 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+mkdir temp+cd temp+darcs init++check_manifest "" "" "--no-pending"+check_manifest "" "" "--pending"+touch a b+darcs add a+check_manifest "" "" "--no-pending"+check_manifest "a" "" "--pending"+darcs add b+mkdir c+check_manifest "" "" "--no-pending"+check_manifest "a b" "" "--pending"+darcs add c+touch c/1 c/2+check_manifest "" "" "--no-pending"+check_manifest "a b" "c" "--pending"+darcs add c/1 c/2+check_manifest "" "" "--no-pending"+check_manifest "a b c/1 c/2" "c" "--pending"+mkdir d+touch d/3 d/4+darcs add d/3 d/4+check_manifest "" "" "--no-pending"+check_manifest "a b c/1 c/2 d/3 d/4" "c d" "--pending"+darcs record -A test --all --name "patch 1" --skip-long-comment+check_manifest "a b c/1 c/2 d/3 d/4" "c d" "--no-pending"+check_manifest "a b c/1 c/2 d/3 d/4" "c d" "--pending"++darcs mv d e+check_manifest "a b c/1 c/2 d/3 d/4" "c d" "--no-pending"+check_manifest "a b c/1 c/2 e/3 e/4" "c e" "--pending"+rm c/1+check_manifest "a b c/1 c/2 d/3 d/4" "c d" "--no-pending"+check_manifest "a b c/1 c/2 e/3 e/4" "c e" "--pending"+darcs remove c/1+check_manifest "a b c/1 c/2 d/3 d/4" "c d" "--no-pending"+check_manifest "a b c/2 e/3 e/4" "c e" "--pending"+darcs mv c/2 c/1+check_manifest "a b c/1 c/2 d/3 d/4" "c d" "--no-pending"+check_manifest "a b c/1 e/3 e/4" "c e" "--pending"+darcs record -A test --all --name "patch 2" --skip-long-comment+check_manifest "a b c/1 e/3 e/4" "c e" "--no-pending"+check_manifest "a b c/1 e/3 e/4" "c e" "--pending"++darcs remove c/1+check_manifest "a b c/1 e/3 e/4" "c e" "--no-pending"+check_manifest "a b e/3 e/4" "c e" "--pending"+darcs remove c+check_manifest "a b c/1 e/3 e/4" "c e" "--no-pending"+check_manifest "a b e/3 e/4" "e" "--pending"+darcs record -A test --all --name "patch 3" --skip-long-comment+check_manifest "a b e/3 e/4" "e" "--no-pending"+check_manifest "a b e/3 e/4" "e" "--pending"++darcs mv b b2+darcs mv b2 b3+check_manifest "a b e/3 e/4" "e" "--no-pending"+check_manifest "a b3 e/3 e/4" "e" "--pending"+darcs record -A test --all --name "patch 3" --skip-long-comment+check_manifest "a b3 e/3 e/4" "e" "--no-pending"+check_manifest "a b3 e/3 e/4" "e" "--pending"++cd ..+rm -rf temp
− tests/show_tags-remote.sh
@@ -1,46 +0,0 @@-#!/usr/bin/env bash-## Test for show tags --repo-##-## 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.-darcs init --repo R # Create our test repos.-darcs init --repo S--cd R-echo 'Example content.' > f-darcs record -lam 'Add f'-darcs tag 't0'-darcs tag 't1'-darcs show tags | grep t0-cd ..-serve_http # sets baseurl--cd S-darcs changes --repo ../R-darcs show tags --repo ../R | grep t0-darcs show tags --repo $baseurl/R | grep t0-cd ..--darcs show tags --repo R | grep t0-darcs show tags --repo $baseurl/R | grep t0
− tests/tricky_unrecord.sh
@@ -1,27 +0,0 @@-#!/usr/bin/env bash-. ./lib--rm -rf temp-mkdir temp-cd temp-darcs init-date > temp.c-darcs add temp.c-darcs record --all -A test --name=hi--mkdir d-darcs add d-darcs mv temp.c d/-darcs record --all -A test --name=mvetc-darcs show contents d/temp.c | cmp d/temp.c ---echo y/d/y | tr / \\012 | darcs unrecord-darcs whatsnew-# darcs show contents d/temp.c | cmp d/temp.c ---darcs record --all -A test --name=again-darcs show contents d/temp.c | cmp d/temp.c ---cd ..-rm -rf temp-
− tests/unpull-formerly-pl.sh
@@ -1,51 +0,0 @@-#!/usr/bin/env bash-. ./lib--rm -rf tempA-mkdir tempA-cd tempA-darcs initialize-echo hello world > foo-darcs add foo-darcs record -a -m hellofoo--echo goodbye world >> foo-darcs record -a -m goodbyefoo--darcs replace world bar foo-echo Hi there foo > bar-darcs add bar-darcs record -a -m addbar--darcs mv bar baz-darcs replace bar baz foo-darcs record -a -m bar2baz--echo Do not love the baz, or anything in the baz. >> foo-darcs record -a -m nolove--darcs mv baz world-darcs replace baz world foo-darcs record -a -m baz2world--not darcs whatsnew--grep 'love the world' foo--echo yy | darcs unpull -p baz2world--not darcs whatsnew--grep 'love the baz' foo--echo yy | darcs unpull -p bar2baz--grep 'love the bar' foo--echo yy | darcs unpull -p nolove--grep 'love' foo && exit 1 || true--cd ..-rm -rf tempA-
− tests/unpull.sh
@@ -1,23 +0,0 @@-#!/usr/bin/env bash-. ./lib--rm -rf temp1-mkdir temp1-cd temp1-darcs init-cat > f <<EOF-one-two-three-EOF-darcs rec -Ax -alm init-cp f g-cat > f <<EOF-three-one-EOF-darcs rec -Ax -am foo-echo yy | darcs unpull -p foo-cmp f g-cd ..-rm -rf temp1
− tests/unrecord-add.sh
@@ -1,23 +0,0 @@-#!/usr/bin/env bash-. ./lib--rm -rf temp1-mkdir temp1-cd temp1-darcs init-echo foo > foo-darcs add foo-darcs whatsnew > correct-cat correct--darcs record -a -m 'addfoo'--darcs unrecord -a--darcs whatsnew > unrecorded-cat unrecorded--diff -u correct unrecorded--cd ..-rm -rf temp1
− tests/unrecord-dont-prompt.sh
@@ -1,28 +0,0 @@-#!/usr/bin/env bash--. ./lib--# Check that the right patches get unrecorded using --dont-prompt-for-dependencies--rm -rf temp1-mkdir temp1-cd temp1-darcs init-echo foo > f-darcs record -Ax -alm foo1-echo bar > b-darcs rec -Ax -alm bar1-echo foo2 > f-darcs record -Ax -alm foo2-echo bar2 > b-darcs record -Ax -alm bar2-darcs unrec --no-deps -p foo1-darcs changes -p foo --count | grep 2-#foo1 is depended upon, we don't unpull it-echo yy | darcs unrec --dont-prompt-for-dependencies -p foo1-#on the previous line, we don't get asked about foo2.-darcs changes -p foo --count | grep 0-#yet, it is unrecorded.-darcs changes -p bar --count | grep 2-cd ..-rm -rf temp1
− tests/unrecord-remove.sh
@@ -1,28 +0,0 @@-#!/usr/bin/env bash-. ./lib--rm -rf temp1-mkdir temp1-cd temp1-darcs init-echo foo > foo-darcs add foo--darcs record -a -m 'addfoo'--darcs remove foo--darcs whatsnew > correct-cat correct--darcs record -a -m 'rmfoo'--darcs unrecord -a --last 1--darcs whatsnew > unrecorded-cat unrecorded--diff -u correct unrecorded--cd ..-rm -rf temp1
− tests/unrecord-setpref.sh
@@ -1,24 +0,0 @@-#!/usr/bin/env bash-. ./lib--rm -rf temp1-mkdir temp1-cd temp1-darcs init--darcs setpref boringfile foobar--darcs whatsnew > correct-cat correct--darcs record -a -m 'boringfoobar'--darcs unrecord -a--darcs whatsnew > unrecorded-cat unrecorded--diff -u correct unrecorded--cd ..-rm -rf temp1
+ tests/unrecord.sh view
@@ -0,0 +1,126 @@+#!/usr/bin/env bash+. ./lib++# unrecord remove++rm -rf temp1+darcs init temp1+cd temp1++echo foo > foo+darcs record -lam 'addfoo'+darcs remove foo+darcs whatsnew > correct+darcs record -a -m 'rmfoo'+darcs unrecord -a --last 1+darcs whatsnew > unrecorded+diff -u correct unrecorded++cd ..+rm -rf temp1++# unrecord setpref++darcs init temp1+cd temp1++darcs setpref boringfile foobar++darcs whatsnew > correct+cat correct++darcs record -a -m 'boringfoobar'+darcs unrecord -a++darcs whatsnew > unrecorded+cat unrecorded++diff -u correct unrecorded++cd ..+rm -rf temp1++# unrecord add++darcs init temp1+cd temp1++echo foo > foo+darcs add foo+darcs whatsnew > correct+cat correct++darcs record -a -m 'addfoo'++darcs unrecord -a++darcs whatsnew > unrecorded+cat unrecorded++diff -u correct unrecorded++cd ..+rm -rf temp1++# tricky unrecord++darcs init temp1+cd temp1++date > temp.c+darcs record -lam hi++mkdir d+darcs add d+darcs mv temp.c d/+darcs record -am mvetc+darcs show contents d/temp.c | cmp d/temp.c -++echo y/d/y | tr / \\012 | darcs unrecord+darcs whatsnew++darcs record -a -m again+darcs show contents d/temp.c | cmp d/temp.c -++cd ..+rm -rf temp1++# Check that the right patches get unrecorded using --dont-prompt-for-dependencies++darcs init temp1+cd temp1++echo foo > f+darcs record -Ax -alm foo1+echo bar > b+darcs rec -Ax -alm bar1+echo foo2 > f+darcs record -Ax -alm foo2+echo bar2 > b+darcs record -Ax -alm bar2+darcs unrec --no-deps -p foo1+darcs changes -p foo --count | grep 2+#foo1 is depended upon, we don't unpull it+echo yy | darcs unrec --dont-prompt-for-dependencies -p foo1+#on the previous line, we don't get asked about foo2.+darcs changes -p foo --count | grep 0+#yet, it is unrecorded.+darcs changes -p bar --count | grep 2+cd ..+rm -rf temp1+++# issue1012: rm/record/unrecord/record => inconsistent repository++darcs init temp1+cd temp1++echo temp1 >File.hs+darcs add File.hs+darcs record File.hs -a -m "add File"+rm File.hs+darcs record -a -m "rm File"+darcs unrecord -p "rm File" -a+darcs record -a -m "re-rm File"+cd ..+rm -rf temp1
− tests/unrevert-add.sh
@@ -1,26 +0,0 @@-#!/usr/bin/env bash--. lib--rm -rf temp1-mkdir temp1-cd temp1-darcs init-echo foo > foo-darcs add foo-darcs whatsnew > correct-cat correct--darcs revert -a--not darcs whatsnew--darcs unrevert -a--darcs whatsnew > unrecorded-cat unrecorded--diff -u correct unrecorded--cd ..-rm -rf temp1
− tests/unrevert-replace-moved.sh
@@ -1,36 +0,0 @@-#!/usr/bin/env bash--. lib--rm -rf temp1-mkdir temp1-cd temp1-darcs init-echo hello world > foo-darcs add foo--darcs record -a -m 'addfoo'--darcs replace hello goodbye foo--darcs revert -a--not darcs whatsnew--darcs mv foo bar--echo hello my good friends >> bar--darcs unrevert -a--darcs whatsnew > unrecorded-cat unrecorded--grep 'bar .* hello goodbye' unrecorded--cat bar-grep 'goodbye world' bar-grep 'goodbye my good friends' bar--cd ..-rm -rf temp1
tests/unrevert.sh view
@@ -2,12 +2,11 @@ . ./lib rm -rf temp1-mkdir temp1+darcs init temp1 cd temp1-darcs init+ echo hello world > foo-darcs add foo-darcs record -a -m add -A x+darcs record -lam add echo goodbye world >> foo cp foo bar darcs revert -a@@ -19,3 +18,117 @@ cd .. rm -rf temp1 +# unrevert replace moved++darcs init temp1+cd temp1++echo hello world > foo+darcs record -lam 'addfoo'+darcs replace hello goodbye foo+darcs revert -a++not darcs whatsnew++darcs mv foo bar++echo hello my good friends >> bar++darcs unrevert -a++darcs whatsnew > unrecorded+cat unrecorded++grep 'bar .* hello goodbye' unrecorded++cat bar+grep 'goodbye world' bar+grep 'goodbye my good friends' bar++cd ..+rm -rf temp1++# unrevert cancel+# From issue366 bug report++darcs init temp1+cd temp1++touch a b +darcs record -lam init+echo plim >> a+echo plim >> b+echo yyyy | darcs revert+echo ploum >> a +echo nyyy | darcs unrevert++cd ..+rm -rf temp1++# unrevert add++darcs init temp1+cd temp1++echo foo > foo+darcs add foo+darcs whatsnew > correct+cat correct++darcs revert -a++not darcs whatsnew++darcs unrevert -a++darcs whatsnew > unrecorded+cat unrecorded++diff -u correct unrecorded++cd ..+rm -rf temp1++# double unrevert+# This demonstrates a bug that happens if you revert followed by+# a partial unrevert and a full unrevert. It requires that+# the second unrevert is working with patches who's contents need+# to be modified by the commute in the first unrevert.++darcs init temp1+cd temp1++echo line1 >> A+echo line2 >> A+echo line3 >> A+echo line4 >> A+echo line5 >> A+echo line6 >> A+darcs add A+darcs record -am A+sed 's/line2/Line2/' A > A1; rm A; mv A1 A+sed '4d' A > A1; rm A; mv A1 A+sed 's/line6/Line6/' A > A1; rm A; mv A1 A+darcs revert -a+echo nyny | darcs unrev+darcs unrev -a++cd ..+rm -rf temp1++# impossible unrevert++darcs init temp1+cd temp1++echo ALL ignore-times > _darcs/prefs/defaults+echo a > foo+darcs record -lam aa+echo b > foo+echo yy | darcs revert -a+echo ydyy | darcs unrecord+# since the unrevert is impossible, we should fail if it succeeds...+echo yy | darcs unrevert && exit 1 || true++cd ..+rm -rf temp1
− tests/unrevert_cancel.sh
@@ -1,22 +0,0 @@-#!/usr/bin/env bash--# From issue366 bug report--. ./lib--rm -rf temp1-mkdir temp1-cd temp1-darcs init-touch a -touch b-darcs add *-darcs record -A moi -am init-echo plim >> a-echo plim >> b-echo yyyy | darcs revert-echo ploum >> a -echo nyyy | darcs unrevert--cd ..-rm -rf temp1
− tests/what_sl.sh
@@ -1,19 +0,0 @@-#!/usr/bin/env bash--. ./lib--rm -rf temp1-mkdir temp1-cd temp1-darcs init-touch foo-darcs add foo-darcs rec -m t1 -a -A tester-echo 1 >> foo-darcs what -s | grep -v No\ changes-darcs what -l | grep -v No\ changes-darcs what -sl | grep -v No\ changes--cd ..-rm -rf temp1-
− tests/whatsnew-file.sh
@@ -1,61 +0,0 @@-#!/usr/bin/env bash--. lib--# Some tests for 'darcs whatsnew '--rm -rf temp1--mkdir temp1-cd temp1--darcs init-date > foo-mkdir bar-echo hello world > bar/baz--darcs record -la -m "add foo"--echo goodbye world >> bar/baz--# goodbye should show up precisely once--darcs wh > out-cat out-grep goodbye out | wc -l | grep 1--darcs wh bar bar/baz > out-cat out-grep goodbye out | wc -l | grep 1--darcs mv foo bar-echo not date > bar/foo--darcs wh bar bar/baz > out-cat out-grep date out | wc -l | grep 1--darcs wh foo > out-cat out-grep date out | wc -l | grep 1--darcs wh foo foo foo > out-cat out-grep date out | wc -l | grep 1--darcs wh foo ./foo ../temp1/foo > out-cat out-grep date out | wc -l | grep 1--darcs wh foo bar/../foo > out-cat out-grep date out | wc -l | grep 1--# This one fails actually, but it's not my fault. Filed as issue1196.-#darcs wh foo foo/../foo/. > out-#cat out-#grep date out | wc -l | grep 1--cd ..--rm -rf temp1
− tests/whatsnew-interactive.sh
@@ -1,46 +0,0 @@-#!/usr/bin/env bash-## -## This tests the basic fascilities of `whatsnew --interactive`-##-## Copyright (C) 2014 Daniil Frumin-##-## 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 wn-i--darcs init --repo wn-i-cd wn-i--echo lolz > foo-darcs add foo--echo n | darcs whatsnew -i > what-grep "Will not ask whether to view 1" what-rm what--echo yxgq | darcs whatsnew -i > what2-grep "M ./foo +1" what2-addfileCount=`grep -c "addfile" what2`-if [ "$addfileCount" -ne 3 ]; then- exit 1-fi;
− tests/whatsnew-pending.sh
@@ -1,38 +0,0 @@-#!/usr/bin/env bash-## Ensure that darcs whatsnew <paths> only lists relevant bits.-## Public Domain, 2010, Petr Rockai-##-## This file is included as part of the Darcs test distribution,-## which is licensed to you under the following terms:-##-## 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 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 ..
tests/whatsnew.sh view
@@ -4,7 +4,7 @@ # Some tests for 'darcs whatsnew ' -rm -rf temp1 temp2+rm -rf temp1 mkdir temp1 cd temp1@@ -51,3 +51,116 @@ cd .. rm -rf temp1+++## Part 2+## This tests the basic fascilities of `whatsnew --interactive`+## Copyright (C) 2014 Daniil Frumin++rm -rf wn-i++darcs init --repo wn-i+cd wn-i++echo lolz > foo+darcs add foo++echo n | darcs whatsnew -i > what+grep "Will not ask whether to view 1" what+rm what++echo yxgq | darcs whatsnew -i > what2+grep "M ./foo +1" what2+addfileCount=`grep -c "addfile" what2`+if [ "$addfileCount" -ne 3 ]; then+ exit 1+fi;+cd ..++## Part 3+## Ensure that darcs whatsnew <paths> only lists relevant bits.+## Public Domain, 2010, Petr Rockai++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 ..++## Part 4+# Some tests for 'darcs whatsnew '++rm -rf temp1++darcs init temp1+cd temp1++date > foo+mkdir bar+echo hello world > bar/baz++darcs record -la -m "add foo"++echo goodbye world >> bar/baz++# goodbye should show up precisely once++darcs wh > out+cat out+grep goodbye out | wc -l | grep 1++darcs wh bar bar/baz > out+cat out+grep goodbye out | wc -l | grep 1++darcs mv foo bar+echo not date > bar/foo++darcs wh bar bar/baz > out+cat out+grep date out | wc -l | grep 1++darcs wh foo > out+cat out+grep date out | wc -l | grep 1++darcs wh foo foo foo > out+cat out+grep date out | wc -l | grep 1++darcs wh foo ./foo ../temp1/foo > out+cat out+grep date out | wc -l | grep 1++darcs wh foo bar/../foo > out+cat out+grep date out | wc -l | grep 1++# This one fails actually, but it's not my fault. Filed as issue1196.+#darcs wh foo foo/../foo/. > out+#cat out+#grep date out | wc -l | grep 1++cd ..+rm -rf temp1++## Part 5++darcs init temp1+cd temp1+touch foo+darcs add foo+darcs rec -m t1 -a -A tester+echo 1 >> foo+darcs what -s | grep -v No\ changes+darcs what -l | grep -v No\ changes+darcs what -sl | grep -v No\ changes++cd ..+rm -rf temp1+